diff --git a/package-lock.json b/package-lock.json index 08020fe83..1db36736f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -66,6 +66,7 @@ "crypto-browserify": "^3.12.0", "dayjs": "^1.11.3", "ethers": "^5.7.0", + "ethers-v6": "npm:ethers@^6.10.0", "fetch": "^1.1.0", "formik": "^2.2.9", "graphql": "^16.5.0", @@ -22979,6 +22980,64 @@ "@ethersproject/wordlists": "5.7.0" } }, + "node_modules/ethers-v6": { + "name": "ethers", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.10.0.tgz", + "integrity": "sha512-nMNwYHzs6V1FR3Y4cdfxSQmNgZsRj1RiTU25JwvnJLmyzw9z3SKxNc2XKDuiXXo/v9ds5Mp9m6HBabgYQQ26tA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@adraffy/ens-normalize": "1.10.0", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "18.15.13", + "aes-js": "4.0.0-beta.5", + "tslib": "2.4.0", + "ws": "8.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ethers-v6/node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==" + }, + "node_modules/ethers-v6/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/ethers-v6/node_modules/ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/ethjs-unit": { "version": "0.1.6", "license": "MIT", diff --git a/package.json b/package.json index 1fca92c78..4582e9244 100644 --- a/package.json +++ b/package.json @@ -102,6 +102,7 @@ "crypto-browserify": "^3.12.0", "dayjs": "^1.11.3", "ethers": "^5.7.0", + "ethers-v6": "npm:ethers@^6.10.0", "fetch": "^1.1.0", "formik": "^2.2.9", "graphql": "^16.5.0", diff --git a/src/components/modal/components/VoidProduct.tsx b/src/components/modal/components/VoidProduct.tsx index 50d766bd9..79955aeea 100644 --- a/src/components/modal/components/VoidProduct.tsx +++ b/src/components/modal/components/VoidProduct.tsx @@ -1,10 +1,11 @@ import { TransactionResponse } from "@bosonprotocol/common"; import { CoreSDK, + CreateListingButton, hooks, + PremintButton, Provider, - subgraph, - VoidButton + subgraph } from "@bosonprotocol/react-kit"; import { extractUserFriendlyError, @@ -12,16 +13,22 @@ import { } from "@bosonprotocol/react-kit"; import * as Sentry from "@sentry/browser"; import { useConfigContext } from "components/config/ConfigContext"; -import { BigNumberish } from "ethers"; +import { BigNumber, BigNumberish } from "ethers"; import { getOfferDetails } from "lib/utils/offer/getOfferDetails"; import { poll } from "lib/utils/promises"; import { useCallback, useState } from "react"; import toast from "react-hot-toast"; import styled from "styled-components"; +import { CONFIG } from "../../../lib/config"; import { colors } from "../../../lib/styles/colors"; import { Offer } from "../../../lib/types/offer"; -import { useSigner } from "../../../lib/utils/hooks/connection/connection"; +import { + useAccount, + useChainId, + useSigner, + useSignerV6 +} from "../../../lib/utils/hooks/connection/connection"; import { useAddPendingTransaction } from "../../../lib/utils/hooks/transactions/usePendingTransactions"; import { useCoreSDK } from "../../../lib/utils/useCoreSdk"; import { Break } from "../../detail/Detail.style"; @@ -207,8 +214,15 @@ export default function VoidProduct({ const addPendingTransaction = useAddPendingTransaction(); const [isLoading, setIsLoading] = useState(false); const signer = useSigner(); + const { data: signerV6 } = useSignerV6(); const { hideModal } = useModal(); const { isMetaTx } = hooks.useMetaTx(coreSdk); + const tokenId = + offerId && offer?.range + ? BigNumber.from(offerId).shl(128).add(offer.range.start).toString() + : undefined; + const connectedChainId = useChainId(); + const { account } = useAccount(); const handleFinish = useCallback(() => { hideModal(); @@ -340,7 +354,7 @@ export default function VoidProduct({ {offer && ( - { console.error("onError", error); const hasUserRejectedTx = getHasUserRejectedTx(error); @@ -397,6 +413,89 @@ export default function VoidProduct({ onSuccess={handleSuccess} /> + {tokenId && ( + + { + console.error("onError", error); + const hasUserRejectedTx = getHasUserRejectedTx(error); + if (hasUserRejectedTx) { + showModal("TRANSACTION_FAILED"); + } else { + Sentry.captureException(error); + showModal("TRANSACTION_FAILED", { + errorMessage: "Something went wrong", + detailedErrorMessage: await extractUserFriendlyError( + error, + { + txResponse, + provider: signer?.provider as Provider + } + ) + }); + } + }} + onPendingSignature={() => { + showModal( + "WAITING_FOR_CONFIRMATION", + undefined, + "auto", + undefined, + { + xs: "400px" + } + ); + }} + onPendingTransaction={(hash, isMetaTx) => { + showModal("TRANSACTION_SUBMITTED", { + action: "Void", + txHash: hash + }); + addPendingTransaction({ + type: subgraph.EventType.OFFER_VOIDED, + hash, + isMetaTx, + accountType: "Seller", + offer: { + id: offer.id + } + }); + }} + onSuccess={handleSuccess} + /> + + )} )} {offers && !!offers.length && ( diff --git a/src/components/seller/products/SellerProductsTable.tsx b/src/components/seller/products/SellerProductsTable.tsx index 5053aad9d..5f6b4ade0 100644 --- a/src/components/seller/products/SellerProductsTable.tsx +++ b/src/components/seller/products/SellerProductsTable.tsx @@ -594,8 +594,7 @@ export default function SellerProductsTable({ ), action: !( - variantStatus === OffersKit.OfferState.VOIDED || - variant?.quantityAvailable === "0" + variantStatus === OffersKit.OfferState.VOIDED ) && ( - Void + Premint ) @@ -762,10 +761,7 @@ export default function SellerProductsTable({ ); })(), action: (() => { - const withVoidButton = !( - status === OffersKit.OfferState.VOIDED || - offer?.quantityAvailable === "0" - ); + const withVoidButton = !(status === OffersKit.OfferState.VOIDED); return ( { @@ -808,7 +804,7 @@ export default function SellerProductsTable({ showModal( modalTypes.VOID_PRODUCT, { - title: "Void Confirmation", + title: "Premint Confirmation", offerId: offer.id, offer, refetch @@ -819,7 +815,7 @@ export default function SellerProductsTable({ } }} > - Void + Premint ) } diff --git a/src/lib/utils/hooks/connection/connection.ts b/src/lib/utils/hooks/connection/connection.ts index 2c06a7d8c..540b32bae 100644 --- a/src/lib/utils/hooks/connection/connection.ts +++ b/src/lib/utils/hooks/connection/connection.ts @@ -3,8 +3,10 @@ import { hooks, useUser } from "@bosonprotocol/react-kit"; import { useWeb3React } from "@web3-react/core"; +import { providers } from "ethers"; +import { BrowserProvider } from "ethers-v6"; import { useMemo } from "react"; -import { useMutation } from "react-query"; +import { useMutation, useQuery } from "react-query"; export function useProvider() { const { provider } = useWeb3React(); @@ -25,6 +27,35 @@ export function useSigner() { return signer; } +export function useSignerV6() { + const providerV5 = useProvider(); + const url = providerV5?.connection?.url; + return useQuery(["signer", url], async () => { + const providerV6 = providerV5toV6(providerV5); + return await providerV6?.getSigner(); + }); +} + +function providerV5toV6( + providerV5: providers.Web3Provider +): BrowserProvider | undefined { + return providerV5 + ? new BrowserProvider({ + request: async (request: { + method: string; + params?: Array | Record; + }) => { + const params = request.params + ? Array.isArray(request.params) + ? (request.params as Array) + : Array.from(request.params.values()) + : []; + return providerV5.send(request.method, params); + } + }) + : undefined; +} + export function useAccount() { const { account } = useWeb3React(); const { user } = useUser();