From 52b7b4e6f9d0156d89a7f44f834841de160d2ef6 Mon Sep 17 00:00:00 2001 From: Juanma Hidalgo Date: Wed, 30 Oct 2024 13:02:16 +0100 Subject: [PATCH] Feat/transak update (#2312) * feat: add support for new contract on Trasak flow * fix: update the TradeService import * feat: update contractId * feat: bump decentraland-transactions * feat: add support for old flow of MarketplaceV2 and CollectionStore * chore: remove console.logs * fix: add missing order in metadata type * feat: update contract addresses * feat: add all contractIds for different flows * feat: fix the order sagas spec file * test: fix tests * fix: fix build * feat: add contractIds for ETH contracts * fix: rollback marketplace-server pointing to localhost * feat: bump dcl-dapps * feat: remove marketplaceServerURL --- webapp/package-lock.json | 27 +- webapp/package.json | 2 +- .../BuyNFTButtons/BuyNFTButtons.container.tsx | 2 +- .../BuyNFTButtons/BuyNFTButtons.tsx | 6 +- .../BuyNFTModal/BuyNFTModal.containter.ts | 3 +- .../BuyPage/BuyNFTModal/BuyNFTModal.tsx | 2 +- .../BuyWithCardExplanationModal.container.ts | 3 +- .../BuyWithCardExplanationModal.tsx | 4 +- .../BuyWithCardExplanationModal.types.ts | 2 + .../Modals/SellModal/SellModal.container.ts | 4 +- .../components/Modals/SellModal/SellModal.tsx | 9 +- .../Modals/SellModal/SellModal.types.ts | 10 +- .../Authorization/Authorization.container.ts | 4 +- .../Authorization/Authorization.tsx | 27 +- .../Authorization/Authorization.types.ts | 6 +- webapp/src/contracts/ERC721Collection.json | 1624 +++++++++++++++++ webapp/src/modules/asset/sagas.spec.ts | 13 - webapp/src/modules/asset/sagas.ts | 2 +- webapp/src/modules/asset/utils.ts | 6 +- webapp/src/modules/item/sagas.spec.ts | 4 +- webapp/src/modules/order/actions.ts | 2 +- webapp/src/modules/order/sagas.spec.ts | 9 +- webapp/src/modules/order/sagas.ts | 4 +- webapp/src/modules/sagas.ts | 2 +- webapp/src/modules/transak/actions.ts | 3 +- webapp/src/modules/transak/sagas.ts | 126 +- webapp/src/modules/transak/utils.ts | 15 + .../vendor/decentraland/analytics/api.ts | 4 +- .../vendor/decentraland/rankings/api.ts | 4 +- 29 files changed, 1853 insertions(+), 76 deletions(-) create mode 100644 webapp/src/contracts/ERC721Collection.json create mode 100644 webapp/src/modules/transak/utils.ts diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 726ad42f1a..361e4559a1 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -27,7 +27,7 @@ "dcl-catalyst-commons": "^9.0.1", "decentraland-connect": "^6.3.1", "decentraland-crypto-fetch": "^1.0.3", - "decentraland-dapps": "^23.12.0", + "decentraland-dapps": "^23.13.0", "decentraland-transactions": "^2.17.0", "decentraland-ui": "^6.11.0", "ethers": "^5.6.8", @@ -5910,15 +5910,16 @@ } }, "node_modules/@transak/transak-sdk": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@transak/transak-sdk/-/transak-sdk-1.4.1.tgz", - "integrity": "sha512-/BKzb9orz1xDxa41oOPW+4KpjSHNEXgtaFazX/aIjQbr7LLbRqfXC/IHzpPmjR9OmFm8pFhV2Y86Rg0aZt5ZUA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@transak/transak-sdk/-/transak-sdk-3.1.3.tgz", + "integrity": "sha512-Ea4/OY5Rz/vZLWt2mgwoJDaeUScNbK1AI/yDfhJrNHG0cmalX3SSQOMswY1uX/GAL+6VNSLAybJg9FRx5I1N/A==", "dependencies": { "events": "^3.3.0", - "query-string": "^8.1.0" + "pako": "^2.1.0", + "query-string": "^8.2.0" }, "engines": { - "node": ">=10.0.0" + "node": ">=18.0.0" } }, "node_modules/@transak/transak-sdk/node_modules/decode-uri-component": { @@ -9694,9 +9695,9 @@ } }, "node_modules/decentraland-dapps": { - "version": "23.12.0", - "resolved": "https://registry.npmjs.org/decentraland-dapps/-/decentraland-dapps-23.12.0.tgz", - "integrity": "sha512-b9rbyUuTSw8Ft80xT3gYPf88aM3dcQgk+vB/c9LL+RWOTSzTXIIBC7IGDamrIf6cobP6l9H+sTtWqmUHjqjP9Q==", + "version": "23.13.0", + "resolved": "https://registry.npmjs.org/decentraland-dapps/-/decentraland-dapps-23.13.0.tgz", + "integrity": "sha512-NPnbWwg5sMW/w5TZ7QUct43nBaHI5N1U1lzz7t4Oi/0fJwUJ+09ZeDdCzA/Q7hGfSxg8xfroIBV3VTWD7QpvVQ==", "dependencies": { "@0xsequence/multicall": "^0.25.1", "@0xsequence/relayer": "^0.25.1", @@ -9704,7 +9705,7 @@ "@dcl/schemas": "^14.0.0", "@dcl/single-sign-on-client": "^0.1.0", "@dcl/ui-env": "^1.5.0", - "@transak/transak-sdk": "^1.0.31", + "@transak/transak-sdk": "^3.1.3", "@types/flat": "0.0.28", "@types/segment-analytics": "^0.0.38", "@well-known-components/fetch-component": "^2.0.1", @@ -9746,9 +9747,9 @@ "license": "MIT" }, "node_modules/decentraland-dapps/node_modules/@dcl/schemas": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@dcl/schemas/-/schemas-14.0.0.tgz", - "integrity": "sha512-w3P/5g/gkYcBUB6+eN1PYdQBaiS8nLh7ldGfUI6GuiHQKvPIC3wS26Dx5i2ukwHrds2koOaqhFdLzHLVDDitQg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@dcl/schemas/-/schemas-14.1.0.tgz", + "integrity": "sha512-KE7679WNJqlrXH088pvXh2a6YLtDL+PvZYXY8ZXShhEJrnxjUhXGYH1LJckPLy4Tb7GEJBwaCcfYlVVVMrDLeA==", "dependencies": { "ajv": "^8.11.0", "ajv-errors": "^3.0.0", diff --git a/webapp/package.json b/webapp/package.json index 78067243d0..208300b80c 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -22,7 +22,7 @@ "dcl-catalyst-commons": "^9.0.1", "decentraland-connect": "^6.3.1", "decentraland-crypto-fetch": "^1.0.3", - "decentraland-dapps": "^23.12.0", + "decentraland-dapps": "^23.13.0", "decentraland-transactions": "^2.17.0", "decentraland-ui": "^6.11.0", "ethers": "^5.6.8", diff --git a/webapp/src/components/AssetPage/SaleActionBox/BuyNFTButtons/BuyNFTButtons.container.tsx b/webapp/src/components/AssetPage/SaleActionBox/BuyNFTButtons/BuyNFTButtons.container.tsx index ed22bf2f15..3ee017df89 100644 --- a/webapp/src/components/AssetPage/SaleActionBox/BuyNFTButtons/BuyNFTButtons.container.tsx +++ b/webapp/src/components/AssetPage/SaleActionBox/BuyNFTButtons/BuyNFTButtons.container.tsx @@ -18,7 +18,7 @@ const mapState = (state: RootState): MapStateProps => ({ }) const mapDispatch = (dispatch: MapDispatch, ownProps: OwnProps): MapDispatchProps => ({ - onExecuteOrderWithCard: nft => dispatch(executeOrderWithCardRequest(nft)), + onExecuteOrderWithCard: (nft, order?: Order) => dispatch(executeOrderWithCardRequest(nft, order)), onBuyWithCrypto: (asset: Asset, order?: Order | null) => ownProps.assetType === AssetType.NFT ? dispatch( diff --git a/webapp/src/components/AssetPage/SaleActionBox/BuyNFTButtons/BuyNFTButtons.tsx b/webapp/src/components/AssetPage/SaleActionBox/BuyNFTButtons/BuyNFTButtons.tsx index b3e30baae0..06c8000066 100644 --- a/webapp/src/components/AssetPage/SaleActionBox/BuyNFTButtons/BuyNFTButtons.tsx +++ b/webapp/src/components/AssetPage/SaleActionBox/BuyNFTButtons/BuyNFTButtons.tsx @@ -35,9 +35,9 @@ const BuyNFTButtons = ({ const history = useHistory() const handleBuyWithCard = useCallback( - (asset: Asset) => { + (asset: Asset, order?: Order) => { analytics.track(events.CLICK_GO_TO_BUY_NFT_WITH_CARD) - !isNFT(asset) ? onBuyItemWithCard(asset) : onExecuteOrderWithCard(asset) + !isNFT(asset) ? onBuyItemWithCard(asset) : onExecuteOrderWithCard(asset, order) }, [analytics, onBuyItemWithCard, onExecuteOrderWithCard] ) @@ -65,7 +65,7 @@ const BuyNFTButtons = ({ return ( <> handleBuyWithCrypto(asset, order)} /> - handleBuyWithCard(asset)} /> + handleBuyWithCard(asset, order || undefined)} /> ) }} diff --git a/webapp/src/components/BuyPage/BuyNFTModal/BuyNFTModal.containter.ts b/webapp/src/components/BuyPage/BuyNFTModal/BuyNFTModal.containter.ts index 8d0a338a3b..0054fcc325 100644 --- a/webapp/src/components/BuyPage/BuyNFTModal/BuyNFTModal.containter.ts +++ b/webapp/src/components/BuyPage/BuyNFTModal/BuyNFTModal.containter.ts @@ -1,4 +1,5 @@ import { connect } from 'react-redux' +import { Order } from '@dcl/schemas' import { FETCH_AUTHORIZATIONS_REQUEST } from 'decentraland-dapps/dist/modules/authorization/actions' import { getLoading as getLoadingAuthorizations } from 'decentraland-dapps/dist/modules/authorization/selectors' import { isLoadingType } from 'decentraland-dapps/dist/modules/loading/selectors' @@ -23,7 +24,7 @@ const mapState = (state: RootState): MapStateProps => ({ const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({ onExecuteOrder: (order, nft, fingerprint, silent) => dispatch(executeOrderRequest(order, nft, fingerprint, silent)), - onExecuteOrderWithCard: nft => dispatch(executeOrderWithCardRequest(nft)), + onExecuteOrderWithCard: (nft, order?: Order) => dispatch(executeOrderWithCardRequest(nft, order)), onClearOrderErrors: () => dispatch(clearOrderErrors()) }) diff --git a/webapp/src/components/BuyPage/BuyNFTModal/BuyNFTModal.tsx b/webapp/src/components/BuyPage/BuyNFTModal/BuyNFTModal.tsx index 77ce292b4f..bc8bc8a29c 100644 --- a/webapp/src/components/BuyPage/BuyNFTModal/BuyNFTModal.tsx +++ b/webapp/src/components/BuyPage/BuyNFTModal/BuyNFTModal.tsx @@ -57,7 +57,7 @@ const BuyNFTModal = (props: Props) => { (alreadyAuthorized: boolean = true) => { if (isBuyWithCardPage) { analytics.track(events.CLICK_BUY_NFT_WITH_CARD) - return onExecuteOrderWithCard(nft) + return onExecuteOrderWithCard(nft, order || undefined) } !!order && onExecuteOrder(order, nft, fingerprint, !alreadyAuthorized) diff --git a/webapp/src/components/Modals/BuyWithCardExplanationModal/BuyWithCardExplanationModal.container.ts b/webapp/src/components/Modals/BuyWithCardExplanationModal/BuyWithCardExplanationModal.container.ts index 334a32c092..5e6cca1b26 100644 --- a/webapp/src/components/Modals/BuyWithCardExplanationModal/BuyWithCardExplanationModal.container.ts +++ b/webapp/src/components/Modals/BuyWithCardExplanationModal/BuyWithCardExplanationModal.container.ts @@ -1,11 +1,12 @@ import { connect } from 'react-redux' +import { Order } from '@dcl/schemas' import { Asset } from '../../../modules/asset/types' import { openTransak } from '../../../modules/transak/actions' import BuyWithCardExplanationModal from './BuyWithCardExplanationModal' import { MapDispatch, MapDispatchProps } from './BuyWithCardExplanationModal.types' const mapDispatch = (dispatch: MapDispatch): MapDispatchProps => ({ - onContinue: (asset: Asset) => dispatch(openTransak(asset)) + onContinue: (asset: Asset, order?: Order) => dispatch(openTransak(asset, order)) }) export default connect(null, mapDispatch)(BuyWithCardExplanationModal) diff --git a/webapp/src/components/Modals/BuyWithCardExplanationModal/BuyWithCardExplanationModal.tsx b/webapp/src/components/Modals/BuyWithCardExplanationModal/BuyWithCardExplanationModal.tsx index 3e2838f1b8..0f6dce0ed6 100644 --- a/webapp/src/components/Modals/BuyWithCardExplanationModal/BuyWithCardExplanationModal.tsx +++ b/webapp/src/components/Modals/BuyWithCardExplanationModal/BuyWithCardExplanationModal.tsx @@ -6,12 +6,12 @@ import * as events from '../../../utils/events' import { Props } from './BuyWithCardExplanationModal.types' import styles from './BuyWithCardExplanationModal.module.css' -const BuyWithCardExplanationModal = ({ metadata: { asset }, onContinue, onClose }: Props) => { +const BuyWithCardExplanationModal = ({ metadata: { asset, order }, onContinue, onClose }: Props) => { const analytics = getAnalytics() const handleContinue = useCallback(() => { analytics.track(events.CONTINUE_BUY_WITH_CARD_MODAL) - onContinue(asset) + onContinue(asset, order) }, [analytics, asset, onContinue]) const handleGoBack = useCallback(() => { diff --git a/webapp/src/components/Modals/BuyWithCardExplanationModal/BuyWithCardExplanationModal.types.ts b/webapp/src/components/Modals/BuyWithCardExplanationModal/BuyWithCardExplanationModal.types.ts index 7d88ff28ee..e875f41f2c 100644 --- a/webapp/src/components/Modals/BuyWithCardExplanationModal/BuyWithCardExplanationModal.types.ts +++ b/webapp/src/components/Modals/BuyWithCardExplanationModal/BuyWithCardExplanationModal.types.ts @@ -1,10 +1,12 @@ import { Dispatch } from 'redux' +import { Order } from '@dcl/schemas' import { ModalProps } from 'decentraland-dapps/dist/providers/ModalProvider/ModalProvider.types' import { Asset } from '../../../modules/asset/types' import { openTransak, OpenTransakAction } from '../../../modules/transak/actions' export type Metadata = { asset: Asset + order?: Order } export type Props = Omit & { diff --git a/webapp/src/components/Modals/SellModal/SellModal.container.ts b/webapp/src/components/Modals/SellModal/SellModal.container.ts index 3c337fd534..125540e038 100644 --- a/webapp/src/components/Modals/SellModal/SellModal.container.ts +++ b/webapp/src/components/Modals/SellModal/SellModal.container.ts @@ -9,6 +9,7 @@ import { Authorization } from 'decentraland-dapps/dist/modules/authorization/typ import { isLoadingType } from 'decentraland-dapps/dist/modules/loading/selectors' import { upsertContracts } from '../../../modules/contract/actions' import { getContract } from '../../../modules/contract/selectors' +import { getIsOffchainPublicNFTOrdersEnabled } from '../../../modules/features/selectors' import { createOrderRequest, CREATE_ORDER_REQUEST, cancelOrderRequest, CANCEL_ORDER_REQUEST } from '../../../modules/order/actions' import { getLoading as getLoadingOrders } from '../../../modules/order/selectors' import { RootState } from '../../../modules/reducer' @@ -26,7 +27,8 @@ const mapState = (state: RootState): MapStateProps => { authorizations: getAuthorizations(state), isCreatingOrder: isLoadingType(getLoadingOrders(state), CREATE_ORDER_REQUEST), isAuthorizing: isLoadingType(getLoading(state), GRANT_TOKEN_REQUEST) || isLoadingType(getLoading(state), REVOKE_TOKEN_REQUEST), - isCancelling: isLoadingType(getLoading(state), CANCEL_ORDER_REQUEST) + isCancelling: isLoadingType(getLoading(state), CANCEL_ORDER_REQUEST), + isOffchainPublicNFTOrdersEnabled: getIsOffchainPublicNFTOrdersEnabled(state) } } diff --git a/webapp/src/components/Modals/SellModal/SellModal.tsx b/webapp/src/components/Modals/SellModal/SellModal.tsx index d7b1091c1d..7e96828f3f 100644 --- a/webapp/src/components/Modals/SellModal/SellModal.tsx +++ b/webapp/src/components/Modals/SellModal/SellModal.tsx @@ -10,7 +10,7 @@ import { toFixedMANAValue } from 'decentraland-dapps/dist/lib/mana' import { AuthorizationType, Authorization as Authorizations } from 'decentraland-dapps/dist/modules/authorization/types' import { hasAuthorization } from 'decentraland-dapps/dist/modules/authorization/utils' import { T, t } from 'decentraland-dapps/dist/modules/translation/utils' -import { ContractName } from 'decentraland-transactions' +import { ContractName, getContract as getDecentralandContract } from 'decentraland-transactions' import { Button, Field, Loader, Mana, Message, ModalNavigation } from 'decentraland-ui' import { useAuthorization } from '../../../lib/authorization' import { formatWeiMANA, parseMANANumber } from '../../../lib/mana' @@ -43,6 +43,7 @@ const SellModal = ({ error, onFetchAuthorizations, isCancelling, + isOffchainPublicNFTOrdersEnabled, onCancelOrder }: Props) => { const { orderService } = VendorFactory.build(nft.vendor) @@ -83,9 +84,13 @@ const SellModal = ({ network: nft.network }) + const offchainOrdersContract = isOffchainPublicNFTOrdersEnabled + ? getDecentralandContract(ContractName.OffChainMarketplace, nft.chainId) + : null + const authorization: Authorizations = { address: wallet?.address || '', - authorizedAddress: marketplace!.address, + authorizedAddress: !!offchainOrdersContract && isOffchainPublicNFTOrdersEnabled ? offchainOrdersContract.address : marketplace!.address, contractAddress: nft.contractAddress, contractName: (nft.category === NFTCategory.WEARABLE || nft.category === NFTCategory.EMOTE) && nft.network === Network.MATIC diff --git a/webapp/src/components/Modals/SellModal/SellModal.types.ts b/webapp/src/components/Modals/SellModal/SellModal.types.ts index 2be6678d2e..71fde20ea6 100644 --- a/webapp/src/components/Modals/SellModal/SellModal.types.ts +++ b/webapp/src/components/Modals/SellModal/SellModal.types.ts @@ -24,6 +24,7 @@ export type Props = Omit & { authorizations: Authorization[] isCreatingOrder: boolean isAuthorizing: boolean + isOffchainPublicNFTOrdersEnabled: boolean onFetchAuthorizations: typeof fetchAuthorizationsRequest onUpsertContracts: typeof upsertContracts onCancelOrder: typeof cancelOrderRequest @@ -34,7 +35,14 @@ export type OwnProps = Pick export type MapStateProps = Pick< Props, - 'authorizations' | 'wallet' | 'isCreatingOrder' | 'error' | 'getContract' | 'isAuthorizing' | 'isCancelling' + | 'authorizations' + | 'wallet' + | 'isCreatingOrder' + | 'error' + | 'getContract' + | 'isAuthorizing' + | 'isCancelling' + | 'isOffchainPublicNFTOrdersEnabled' > export type MapDispatchProps = Pick diff --git a/webapp/src/components/SettingsPage/Authorization/Authorization.container.ts b/webapp/src/components/SettingsPage/Authorization/Authorization.container.ts index 53e99c40ac..58ddf41327 100644 --- a/webapp/src/components/SettingsPage/Authorization/Authorization.container.ts +++ b/webapp/src/components/SettingsPage/Authorization/Authorization.container.ts @@ -10,6 +10,7 @@ import { import { getData as getAuthorizations, getLoading } from 'decentraland-dapps/dist/modules/authorization/selectors' import { areEqual } from 'decentraland-dapps/dist/modules/authorization/utils' import { getContract } from '../../../modules/contract/selectors' +import { getIsOffchainPublicNFTOrdersEnabled } from '../../../modules/features/selectors' import { RootState } from '../../../modules/reducer' import { getPendingAuthorizationTransactions } from '../../../modules/transaction/selectors' import { hasTransactionPending } from '../../../modules/transaction/utils' @@ -35,7 +36,8 @@ const mapState = (state: RootState, { authorization }: OwnProps): MapStateProps authorizations, pendingTransactions, isLoading: isLoading || hasTransactionPending(pendingTransactions, authorizedAddress, contractAddress), - getContract: (query: Partial) => getContract(state, query) + getContract: (query: Partial) => getContract(state, query), + isOffchainPublicNFTOrdersEnabled: getIsOffchainPublicNFTOrdersEnabled(state) } } diff --git a/webapp/src/components/SettingsPage/Authorization/Authorization.tsx b/webapp/src/components/SettingsPage/Authorization/Authorization.tsx index 5c3aa976fd..13e046386e 100644 --- a/webapp/src/components/SettingsPage/Authorization/Authorization.tsx +++ b/webapp/src/components/SettingsPage/Authorization/Authorization.tsx @@ -6,6 +6,7 @@ import { ChainCheck, TransactionLink } from 'decentraland-dapps/dist/containers' import { getChainConfiguration } from 'decentraland-dapps/dist/lib/chainConfiguration' import { AuthorizationType } from 'decentraland-dapps/dist/modules/authorization/types' import { t, T } from 'decentraland-dapps/dist/modules/translation/utils' +import { ContractName, getContract as getDecentralandContract } from 'decentraland-transactions' import { Form, Radio, Loader, Popup, RadioProps } from 'decentraland-ui' import { isAuthorized } from '../../../lib/authorization' import { locations } from '../../../modules/routing/locations' @@ -13,7 +14,16 @@ import { Props } from './Authorization.types' import './Authorization.css' const Authorization = (props: Props) => { - const { authorization, authorizations, shouldUpdateSpendingCap, isLoading, onGrant, onRevoke, getContract } = props + const { + authorization, + authorizations, + shouldUpdateSpendingCap, + isLoading, + isOffchainPublicNFTOrdersEnabled, + onGrant, + onRevoke, + getContract + } = props const handleOnChange = useCallback( (isChecked: boolean) => { @@ -39,7 +49,18 @@ const Authorization = (props: Props) => { const { contractAddress, authorizedAddress } = authorization - const contract = getContract({ address: authorizedAddress }) + let contract + let name: string = '' + if (isOffchainPublicNFTOrdersEnabled) { + contract = getDecentralandContract(ContractName.OffChainMarketplace, authorization.chainId) + name = contract.name + } else { + contract = getContract({ address: authorizedAddress }) + if (contract) { + name = contract.label || contract.name + } + } + const token = getContract({ address: contractAddress }) if (!contract || !token) { @@ -76,7 +97,7 @@ const Authorization = (props: Props) => { values={{ contract_link: ( - {contract.label || contract.name} + {name} ), symbol: token.name, diff --git a/webapp/src/components/SettingsPage/Authorization/Authorization.types.ts b/webapp/src/components/SettingsPage/Authorization/Authorization.types.ts index 53686a911e..2f389ae010 100644 --- a/webapp/src/components/SettingsPage/Authorization/Authorization.types.ts +++ b/webapp/src/components/SettingsPage/Authorization/Authorization.types.ts @@ -15,6 +15,7 @@ export type Props = { authorizations: Authorization[] pendingTransactions: Transaction[] isLoading: boolean + isOffchainPublicNFTOrdersEnabled: boolean shouldUpdateSpendingCap?: boolean getContract: (query: Partial) => ReturnType onGrant: typeof grantTokenRequest @@ -22,6 +23,9 @@ export type Props = { } export type OwnProps = Pick -export type MapStateProps = Pick +export type MapStateProps = Pick< + Props, + 'authorizations' | 'pendingTransactions' | 'isLoading' | 'getContract' | 'isOffchainPublicNFTOrdersEnabled' +> export type MapDispatchProps = Pick export type MapDispatch = Dispatch diff --git a/webapp/src/contracts/ERC721Collection.json b/webapp/src/contracts/ERC721Collection.json new file mode 100644 index 0000000000..d6eb2daad8 --- /dev/null +++ b/webapp/src/contracts/ERC721Collection.json @@ -0,0 +1,1624 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "_itemId", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "string", + "name": "rarity", + "type": "string" + }, + { + "internalType": "uint256", + "name": "maxSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "price", + "type": "uint256" + }, + { + "internalType": "address", + "name": "beneficiary", + "type": "address" + }, + { + "internalType": "string", + "name": "metadata", + "type": "string" + }, + { + "internalType": "string", + "name": "contentHash", + "type": "string" + } + ], + "indexed": false, + "internalType": "struct ERC721BaseCollectionV2.Item", + "name": "_item", + "type": "tuple" + } + ], + "name": "AddItem", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "_oldBaseURI", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "_newBaseURI", + "type": "string" + } + ], + "name": "BaseURI", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "Complete", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_previousCreator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_newCreator", + "type": "address" + } + ], + "name": "CreatorshipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_beneficiary", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "_tokenId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "_itemId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_issuedId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "_caller", + "type": "address" + } + ], + "name": "Issue", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "userAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "relayerAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "functionSignature", + "type": "bytes" + } + ], + "name": "MetaTransactionExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "_itemId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "_contentHash", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "_metadata", + "type": "string" + } + ], + "name": "RescueItem", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "_previousValue", + "type": "bool" + }, + { + "indexed": false, + "internalType": "bool", + "name": "_newValue", + "type": "bool" + } + ], + "name": "SetApproved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "_previousValue", + "type": "bool" + }, + { + "indexed": false, + "internalType": "bool", + "name": "_newValue", + "type": "bool" + } + ], + "name": "SetEditable", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_manager", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "_value", + "type": "bool" + } + ], + "name": "SetGlobalManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_minter", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "_value", + "type": "bool" + } + ], + "name": "SetGlobalMinter", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "_itemId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "_manager", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "_value", + "type": "bool" + } + ], + "name": "SetItemManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "_itemId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "_minter", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "SetItemMinter", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "_itemId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_price", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "_beneficiary", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "_metadata", + "type": "string" + } + ], + "name": "UpdateItemData", + "type": "event" + }, + { + "inputs": [], + "name": "COLLECTION_HASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ISSUED_ID_BITS", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ITEM_ID_BITS", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_ISSUED_ID", + "outputs": [ + { + "internalType": "uint216", + "name": "", + "type": "uint216" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_ITEM_ID", + "outputs": [ + { + "internalType": "uint40", + "name": "", + "type": "uint40" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "string", + "name": "rarity", + "type": "string" + }, + { + "internalType": "uint256", + "name": "price", + "type": "uint256" + }, + { + "internalType": "address", + "name": "beneficiary", + "type": "address" + }, + { + "internalType": "string", + "name": "metadata", + "type": "string" + } + ], + "internalType": "struct ERC721BaseCollectionV2.ItemParam[]", + "name": "_items", + "type": "tuple[]" + } + ], + "name": "addItems", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "baseURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "_tokenIds", + "type": "uint256[]" + } + ], + "name": "batchTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "completeCollection", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "createdAt", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "creator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_id", + "type": "uint256" + } + ], + "name": "decodeTokenId", + "outputs": [ + { + "internalType": "uint256", + "name": "itemId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "issuedId", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "domainSeparator", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "_itemIds", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "_prices", + "type": "uint256[]" + }, + { + "internalType": "address[]", + "name": "_beneficiaries", + "type": "address[]" + }, + { + "internalType": "string[]", + "name": "_metadatas", + "type": "string[]" + } + ], + "name": "editItemsData", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_itemId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_issuedId", + "type": "uint256" + } + ], + "name": "encodeTokenId", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "userAddress", + "type": "address" + }, + { + "internalType": "bytes", + "name": "functionSignature", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "sigR", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "sigS", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "sigV", + "type": "uint8" + } + ], + "name": "executeMetaTransaction", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getChainId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "getNonce", + "outputs": [ + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "globalManagers", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "globalMinters", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "initImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "string", + "name": "_symbol", + "type": "string" + }, + { + "internalType": "string", + "name": "_baseURI", + "type": "string" + }, + { + "internalType": "address", + "name": "_creator", + "type": "address" + }, + { + "internalType": "bool", + "name": "_shouldComplete", + "type": "bool" + }, + { + "internalType": "bool", + "name": "_isApproved", + "type": "bool" + }, + { + "internalType": "contract IRarities", + "name": "_rarities", + "type": "address" + }, + { + "components": [ + { + "internalType": "string", + "name": "rarity", + "type": "string" + }, + { + "internalType": "uint256", + "name": "price", + "type": "uint256" + }, + { + "internalType": "address", + "name": "beneficiary", + "type": "address" + }, + { + "internalType": "string", + "name": "metadata", + "type": "string" + } + ], + "internalType": "struct ERC721BaseCollectionV2.ItemParam[]", + "name": "_items", + "type": "tuple[]" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isApproved", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isCompleted", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isEditable", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isInitialized", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isMintingAllowed", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_beneficiaries", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_itemIds", + "type": "uint256[]" + } + ], + "name": "issueTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "itemManagers", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "itemMinters", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "items", + "outputs": [ + { + "internalType": "string", + "name": "rarity", + "type": "string" + }, + { + "internalType": "uint256", + "name": "maxSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "price", + "type": "uint256" + }, + { + "internalType": "address", + "name": "beneficiary", + "type": "address" + }, + { + "internalType": "string", + "name": "metadata", + "type": "string" + }, + { + "internalType": "string", + "name": "contentHash", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "itemsCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rarities", + "outputs": [ + { + "internalType": "contract IRarities", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "_itemIds", + "type": "uint256[]" + }, + { + "internalType": "string[]", + "name": "_contentHashes", + "type": "string[]" + }, + { + "internalType": "string[]", + "name": "_metadatas", + "type": "string[]" + } + ], + "name": "rescueItems", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "_tokenIds", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "safeBatchTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_value", + "type": "bool" + } + ], + "name": "setApproved", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_baseURI", + "type": "string" + } + ], + "name": "setBaseURI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_value", + "type": "bool" + } + ], + "name": "setEditable", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "_itemIds", + "type": "uint256[]" + }, + { + "internalType": "address[]", + "name": "_managers", + "type": "address[]" + }, + { + "internalType": "bool[]", + "name": "_values", + "type": "bool[]" + } + ], + "name": "setItemsManagers", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "_itemIds", + "type": "uint256[]" + }, + { + "internalType": "address[]", + "name": "_minters", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_values", + "type": "uint256[]" + } + ], + "name": "setItemsMinters", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_managers", + "type": "address[]" + }, + { + "internalType": "bool[]", + "name": "_values", + "type": "bool[]" + } + ], + "name": "setManagers", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_minters", + "type": "address[]" + }, + { + "internalType": "bool[]", + "name": "_values", + "type": "bool[]" + } + ], + "name": "setMinters", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenOfOwnerByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newCreator", + "type": "address" + } + ], + "name": "transferCreatorship", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/webapp/src/modules/asset/sagas.spec.ts b/webapp/src/modules/asset/sagas.spec.ts index 357dfaad9a..0efe612175 100644 --- a/webapp/src/modules/asset/sagas.spec.ts +++ b/webapp/src/modules/asset/sagas.spec.ts @@ -95,19 +95,6 @@ describe('when handling the set purchase action', () => { }) }) }) - - describe('and the tx hash has not yet been setted', () => { - it('should not dispatch a push to the history with the location of the buy status page', () => { - return expectSaga(assetSaga) - .provide([[getContext('history'), { location: { pathname: locations.browse() } }]]) - - .dispatch(setPurchase({ ...mockNFTPurchase, txHash: null })) - .run({ silenceTimeout: true }) - .then(({ effects }) => { - expect(effects.put).toBeUndefined() - }) - }) - }) }) describe.each([PurchaseStatus.FAILED, PurchaseStatus.CANCELLED, PurchaseStatus.REFUNDED])( diff --git a/webapp/src/modules/asset/sagas.ts b/webapp/src/modules/asset/sagas.ts index 937dfde3f9..3c30f7e7d2 100644 --- a/webapp/src/modules/asset/sagas.ts +++ b/webapp/src/modules/asset/sagas.ts @@ -45,7 +45,7 @@ function* handleSetAssetPurchaseWithCard(action: SetPurchaseAction) { const statusPagePathname = locations.buyStatusPage(assetType, contractAddress, assetId) const shouldRedirect = [new URL(`${window.origin}${buyWithCardPathname}`).pathname, statusPagePathname].includes(pathname) - if (shouldRedirect && [PurchaseStatus.PENDING, PurchaseStatus.COMPLETE].includes(status)) { + if ([PurchaseStatus.PENDING, PurchaseStatus.COMPLETE].includes(status)) { history.push(statusPagePathname) } diff --git a/webapp/src/modules/asset/utils.ts b/webapp/src/modules/asset/utils.ts index ffff8e5556..2474545ea1 100644 --- a/webapp/src/modules/asset/utils.ts +++ b/webapp/src/modules/asset/utils.ts @@ -99,18 +99,18 @@ export function isWearableOrEmote(asset: Asset): boolean { return categories.includes(asset.category) } -export function* buyAssetWithCard(asset: Asset) { +export function* buyAssetWithCard(asset: Asset, order?: Order) { const buyNftsWithCardExplanationPopupKey = (yield call( [localStorage, 'getItem'], BUY_NFTS_WITH_CARD_EXPLANATION_POPUP_KEY )) as ReturnType if (buyNftsWithCardExplanationPopupKey === 'true') { - yield put(openTransak(asset)) + yield put(openTransak(asset, order)) return } - yield put(openModal('BuyWithCardExplanationModal', { asset })) + yield put(openModal('BuyWithCardExplanationModal', { asset, order })) const { close } = (yield race({ continue: take(SET_PURCHASE), diff --git a/webapp/src/modules/item/sagas.spec.ts b/webapp/src/modules/item/sagas.spec.ts index 9287c47000..da8b13c7e0 100644 --- a/webapp/src/modules/item/sagas.spec.ts +++ b/webapp/src/modules/item/sagas.spec.ts @@ -220,7 +220,7 @@ describe('when handling the buy items with card action', () => { it('should open Transak widget', () => { return expectSaga(itemSaga, getIdentity) .provide([[call([localStorage, 'getItem'], BUY_NFTS_WITH_CARD_EXPLANATION_POPUP_KEY), null]]) - .put(openModal('BuyWithCardExplanationModal', { asset: item })) + .put(openModal('BuyWithCardExplanationModal', { asset: item, order: undefined })) .dispatch(buyItemWithCardRequest(item)) .dispatch(closeModal('BuyWithCardExplanationModal')) .run({ silenceTimeout: true }) @@ -234,7 +234,7 @@ describe('when handling the buy items with card action', () => { it('should not set the item in the local storage to show the modal again later', () => { return expectSaga(itemSaga, getIdentity) .provide([[call([localStorage, 'getItem'], BUY_NFTS_WITH_CARD_EXPLANATION_POPUP_KEY), null]]) - .put(openModal('BuyWithCardExplanationModal', { asset: item })) + .put(openModal('BuyWithCardExplanationModal', { asset: item, order: undefined })) .dispatch(buyItemWithCardRequest(item)) .dispatch(closeModal('BuyWithCardExplanationModal')) .run({ silenceTimeout: true }) diff --git a/webapp/src/modules/order/actions.ts b/webapp/src/modules/order/actions.ts index b3e2ff68eb..383fe00d1e 100644 --- a/webapp/src/modules/order/actions.ts +++ b/webapp/src/modules/order/actions.ts @@ -76,7 +76,7 @@ export const EXECUTE_ORDER_WITH_CARD_REQUEST = '[Request] Execute Order With Car export const EXECUTE_ORDER_WITH_CARD_SUCCESS = '[Success] Execute Order With Card' export const EXECUTE_ORDER_WITH_CARD_FAILURE = '[Failure] Execute Order With Card' -export const executeOrderWithCardRequest = (nft: NFT) => action(EXECUTE_ORDER_WITH_CARD_REQUEST, { nft }) +export const executeOrderWithCardRequest = (nft: NFT, order?: Order) => action(EXECUTE_ORDER_WITH_CARD_REQUEST, { nft, order }) export const executeOrderWithCardSuccess = (purchase: NFTPurchase, nft: NFT, txHash: string) => action(EXECUTE_ORDER_WITH_CARD_SUCCESS, { diff --git a/webapp/src/modules/order/sagas.spec.ts b/webapp/src/modules/order/sagas.spec.ts index f18deaaf87..fb5a42f862 100644 --- a/webapp/src/modules/order/sagas.spec.ts +++ b/webapp/src/modules/order/sagas.spec.ts @@ -233,7 +233,10 @@ describe('when handling the execute order with card action', () => { describe('when the explanation modal has already been shown', () => { it('should open Transak widget', () => { return expectSaga(orderSaga, tradeService) - .provide([[call([localStorage, 'getItem'], BUY_NFTS_WITH_CARD_EXPLANATION_POPUP_KEY), 'true']]) + .provide([ + [call([localStorage, 'getItem'], BUY_NFTS_WITH_CARD_EXPLANATION_POPUP_KEY), 'true'], + [select(getIsOffchainPublicNFTOrdersEnabled), true] + ]) .put(openTransak(nft)) .dispatch(executeOrderWithCardRequest(nft)) .run({ silenceTimeout: true }) @@ -247,7 +250,7 @@ describe('when handling the execute order with card action', () => { it('should not set nft in the local storage to show the modal again later', () => { return expectSaga(orderSaga, tradeService) .provide([[call([localStorage, 'getItem'], BUY_NFTS_WITH_CARD_EXPLANATION_POPUP_KEY), null]]) - .put(openModal('BuyWithCardExplanationModal', { asset: nft })) + .put(openModal('BuyWithCardExplanationModal', { asset: nft, order: undefined })) .dispatch(executeOrderWithCardRequest(nft)) .dispatch(closeModal('BuyWithCardExplanationModal')) .run({ silenceTimeout: true }) @@ -266,7 +269,7 @@ describe('when handling the execute order with card action', () => { it('should dispatch an action signaling the failure of the action handling', () => { return expectSaga(orderSaga, tradeService) - .provide([[call(buyAssetWithCard, nft), Promise.reject(new Error(errorMessage))]]) + .provide([[call(buyAssetWithCard, nft, undefined), Promise.reject(new Error(errorMessage))]]) .put(executeOrderWithCardFailure(errorMessage)) .dispatch(executeOrderWithCardRequest(nft)) .run({ silenceTimeout: true }) diff --git a/webapp/src/modules/order/sagas.ts b/webapp/src/modules/order/sagas.ts index 3cfaf26162..6bb3f106ab 100644 --- a/webapp/src/modules/order/sagas.ts +++ b/webapp/src/modules/order/sagas.ts @@ -174,10 +174,10 @@ export function* orderSaga(tradeService: TradeService) { } function* handleExecuteOrderWithCardRequest(action: ExecuteOrderWithCardRequestAction) { - const { nft } = action.payload + const { nft, order } = action.payload try { - yield call(buyAssetWithCard, nft) + yield call(buyAssetWithCard, nft, order) } catch (error) { yield put(executeOrderWithCardFailure(isErrorWithMessage(error) ? error.message : t('global.unknown_error'))) } diff --git a/webapp/src/modules/sagas.ts b/webapp/src/modules/sagas.ts index a6ef2d04ab..a373ff3777 100644 --- a/webapp/src/modules/sagas.ts +++ b/webapp/src/modules/sagas.ts @@ -72,7 +72,7 @@ const gatewaySaga = createGatewaySaga({ widgetBaseUrl: config.get('MOON_PAY_WIDGET_URL') }, [NetworkGatewayType.TRANSAK]: { - apiBaseUrl: config.get('TRANSAK_API_URL'), + apiBaseUrl: config.get('MARKETPLACE_SERVER_URL'), key: config.get('TRANSAK_KEY'), env: config.get('TRANSAK_ENV'), pollingDelay: +config.get('TRANSAK_POLLING_DELAY'), diff --git a/webapp/src/modules/transak/actions.ts b/webapp/src/modules/transak/actions.ts index ea90d91966..a5718cc607 100644 --- a/webapp/src/modules/transak/actions.ts +++ b/webapp/src/modules/transak/actions.ts @@ -1,7 +1,8 @@ import { action } from 'typesafe-actions' +import { Order } from '@dcl/schemas' import { Asset } from '../asset/types' // Open Transak export const OPEN_TRANSAK = 'Open Transak' -export const openTransak = (asset: Asset) => action(OPEN_TRANSAK, { asset }) +export const openTransak = (asset: Asset, order?: Order) => action(OPEN_TRANSAK, { asset, order }) export type OpenTransakAction = ReturnType diff --git a/webapp/src/modules/transak/sagas.ts b/webapp/src/modules/transak/sagas.ts index ad33332400..3e0904cab0 100644 --- a/webapp/src/modules/transak/sagas.ts +++ b/webapp/src/modules/transak/sagas.ts @@ -1,23 +1,61 @@ -import { put, select, takeEvery } from 'redux-saga/effects' -import { Network } from '@dcl/schemas' +import { ethers } from 'ethers' +import { call, put, select, takeEvery } from 'redux-saga/effects' +import { ChainId, Network, Rarity, Trade } from '@dcl/schemas' import { isMobile } from 'decentraland-dapps/dist/lib/utils' import { Transak } from 'decentraland-dapps/dist/modules/gateway/transak' -import { ProductsAvailed, TradeType } from 'decentraland-dapps/dist/modules/gateway/transak/types' import { TransakConfig } from 'decentraland-dapps/dist/modules/gateway/types' import { closeAllModals } from 'decentraland-dapps/dist/modules/modal/actions' +import { TradeService } from 'decentraland-dapps/dist/modules/trades/TradeService' import { getAddress } from 'decentraland-dapps/dist/modules/wallet/selectors' +import { ContractName, getContract, getContractName } from 'decentraland-transactions' import { config } from '../../config' -import { isNFT } from '../asset/utils' +import { API_SIGNER } from '../../lib/api' +import { getOnChainTrade } from '../../utils/trades' +import { getAssetImage, isNFT } from '../asset/utils' +import { MARKETPLACE_SERVER_URL } from '../vendor/decentraland' +import { getWallet } from '../wallet/selectors' import { OPEN_TRANSAK, OpenTransakAction } from './actions' +import { encodeTokenId } from './utils' export function* transakSaga() { yield takeEvery(OPEN_TRANSAK, handleOpenTransak) } +const MarketplaceV3ContractIds: Pick>>, Network.MATIC | Network.ETHEREUM> = { + [Network.MATIC]: { + [ChainId.MATIC_AMOY]: '670660ed2bbeb54123b28728', + [ChainId.MATIC_MAINNET]: '6717e6cd2fb1688e111c1a80' + }, + [Network.ETHEREUM]: { + [ChainId.ETHEREUM_MAINNET]: '672100492fb1688e111c2bd4', + [ChainId.ETHEREUM_SEPOLIA]: '671a23e92bbeb54123b3b692' + } +} +const MarketplaceV2ContractIds: Pick>>, Network.MATIC | Network.ETHEREUM> = { + [Network.MATIC]: { + [ChainId.MATIC_AMOY]: '670e86dd2bbeb54123b3a2a3', + [ChainId.MATIC_MAINNET]: '6717e6dac00223b9cc8e51cd' + }, + [Network.ETHEREUM]: { + [ChainId.ETHEREUM_MAINNET]: '672100572fb1688e111c2bdb', + [ChainId.ETHEREUM_SEPOLIA]: '671f9815945ac8890fbae4c6' + } +} +const TransakMulticallContracts: Pick>>, Network.MATIC | Network.ETHEREUM> = { + [Network.MATIC]: { + [ChainId.MATIC_AMOY]: '0xCB9bD5aCD627e8FcCf9EB8d4ba72AEb1Cd8Ff5EF', + [ChainId.MATIC_MAINNET]: '0x4A598B7eC77b1562AD0dF7dc64a162695cE4c78A' + }, + [Network.ETHEREUM]: { + [ChainId.ETHEREUM_MAINNET]: '0xab88cd272863b197b48762ea283f24a13f6586dd', + [ChainId.ETHEREUM_SEPOLIA]: '0xD84aC4716A082B1F7eCDe9301aA91A7c4B62ECd7' + } +} + function* handleOpenTransak(action: OpenTransakAction) { - const { asset } = action.payload + const { asset, order } = action.payload const transakConfig: TransakConfig = { - apiBaseUrl: config.get('TRANSAK_API_URL'), + apiBaseUrl: config.get('MARKETPLACE_SERVER_URL'), key: config.get('TRANSAK_KEY'), env: config.get('TRANSAK_ENV'), pollingDelay: +config.get('TRANSAK_POLLING_DELAY'), @@ -26,16 +64,78 @@ function* handleOpenTransak(action: OpenTransakAction) { appCluster: config.get('TRANSAK_PUSHER_APP_CLUSTER') } } - const tokenId = isNFT(asset) ? asset.tokenId : asset.itemId + + const wallet = (yield select(getWallet)) as ReturnType + if (!wallet) { + return + } + + const tradeId = isNFT(asset) ? order?.tradeId : asset.tradeId + let calldata: string = '' + let contractId + + const transakMulticallContract = TransakMulticallContracts[asset.network]?.[asset.chainId] + if (!transakMulticallContract) { + throw new Error(`Transak multicall contract not found for network ${asset.network} and chainId ${asset.chainId}`) + } + + if (tradeId && wallet?.address) { + contractId = MarketplaceV3ContractIds[asset.network]?.[asset.chainId] + if (!contractId) { + throw new Error(`Marketplace contract not found for network ${asset.network} and chainId ${asset.chainId}`) + } + const tradeService = new TradeService(API_SIGNER, MARKETPLACE_SERVER_URL, () => undefined) + const trade: Trade = yield call([tradeService, 'fetchTrade'], tradeId) + const { abi } = getContract(ContractName.OffChainMarketplace, asset.chainId) + const MarketplaveV3Interface = new ethers.utils.Interface(abi) + calldata = MarketplaveV3Interface.encodeFunctionData('accept', [[getOnChainTrade(trade, transakMulticallContract)]]) + } else if (order && isNFT(asset)) { + contractId = MarketplaceV2ContractIds[asset.network]?.[asset.chainId] + if (!contractId) { + throw new Error(`Marketplace contract not found for network ${asset.network} and chainId ${asset.chainId}`) + } + const contractName = getContractName(order.marketplaceAddress) + const contract = getContract(contractName, order.chainId) + const MarketplaceV2Interface = new ethers.utils.Interface(contract.abi) + calldata = MarketplaceV2Interface.encodeFunctionData('executeOrder', [[asset.contractAddress, asset.tokenId, order.price]]) + } else if (!isNFT(asset)) { + contractId = asset.chainId === ChainId.MATIC_AMOY ? '670e8b512bbeb54123b3a2b4' : '6717e6e62fb1688e111c1a87' // CollectionStore contractId + const contract = getContract(ContractName.CollectionStore, asset.chainId) + const CollectionStoreInterface = new ethers.utils.Interface(contract.abi) + calldata = CollectionStoreInterface.encodeFunctionData('buy', [ + [[asset.contractAddress, [asset.itemId], [asset.price], [transakMulticallContract]]] + ]) + } + + let tokenId: string = '' + if (!isNFT(asset)) { + const raritySupply = Rarity.getMaxSupply(asset.rarity) + const available = asset.available + const nextIssueId = raritySupply - available + 1 + tokenId = encodeTokenId(parseInt(asset.itemId), nextIssueId).toString() + } else { + tokenId = asset.tokenId + } + const customizationOptions = { - contractAddress: asset.contractAddress, - tradeType: isNFT(asset) ? TradeType.SECONDARY : TradeType.PRIMARY, - tokenId, - productsAvailed: ProductsAvailed.BUY, + calldata, + cryptoCurrencyCode: 'MANA', isNFT: true, - widgetWidth: isMobile() ? undefined : '450px' // To avoid fixing the width of the widget in mobile + estimatedGasLimit: 70_000, + widgetWidth: isMobile() ? undefined : '450px', // To avoid fixing the width of the widget in mobile + contractId, + nftData: [ + { + imageURL: getAssetImage(asset), + nftName: asset.name, + collectionAddress: asset.contractAddress, + tokenID: [`${tokenId}`], + price: [+ethers.utils.formatEther((isNFT(asset) ? order?.price : asset.price) || 0)], + quantity: 1, + nftType: 'ERC721' + } + ] } - const address: string | undefined = (yield select(getAddress)) as ReturnType yield put(closeAllModals()) diff --git a/webapp/src/modules/transak/utils.ts b/webapp/src/modules/transak/utils.ts new file mode 100644 index 0000000000..85128fc784 --- /dev/null +++ b/webapp/src/modules/transak/utils.ts @@ -0,0 +1,15 @@ +export function encodeTokenId(itemId: number, issuedId: number): bigint { + const MAX_ITEM_ID = BigInt('0xFFFFFFFFFF') // 40 bits max value + const MAX_ISSUED_ID = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF') // 216 bits max value + + if (BigInt(itemId) > MAX_ITEM_ID) { + throw new Error('encodeTokenId: INVALID_ITEM_ID') + } + + if (BigInt(issuedId) > MAX_ISSUED_ID) { + throw new Error('encodeTokenId: INVALID_ISSUED_ID') + } + + // Shift the itemId left by 216 bits and OR it with issuedId + return (BigInt(itemId) << BigInt(216)) | BigInt(issuedId) +} diff --git a/webapp/src/modules/vendor/decentraland/analytics/api.ts b/webapp/src/modules/vendor/decentraland/analytics/api.ts index b1ef5ec0a7..44ca6212b4 100644 --- a/webapp/src/modules/vendor/decentraland/analytics/api.ts +++ b/webapp/src/modules/vendor/decentraland/analytics/api.ts @@ -1,6 +1,6 @@ import { BaseAPI } from 'decentraland-dapps/dist/lib/api' import { AnalyticsTimeframe, AnalyticsVolumeData } from '../../../analytics/types' -import { NFT_SERVER_URL } from '../nft' +import { MARKETPLACE_SERVER_URL } from '../marketplace/api' import { retryParams } from '../utils' class AnalyticsAPI extends BaseAPI { @@ -8,4 +8,4 @@ class AnalyticsAPI extends BaseAPI { this.request('get', `/volume/${timeframe}`) as Promise<{ data: AnalyticsVolumeData }> } -export const analyticsAPI = new AnalyticsAPI(NFT_SERVER_URL, retryParams) +export const analyticsAPI = new AnalyticsAPI(MARKETPLACE_SERVER_URL, retryParams) diff --git a/webapp/src/modules/vendor/decentraland/rankings/api.ts b/webapp/src/modules/vendor/decentraland/rankings/api.ts index e7e9a760e2..ea8b64c52b 100644 --- a/webapp/src/modules/vendor/decentraland/rankings/api.ts +++ b/webapp/src/modules/vendor/decentraland/rankings/api.ts @@ -1,6 +1,6 @@ import { BaseAPI } from 'decentraland-dapps/dist/lib/api' import { AnalyticsTimeframe, RankingEntities, RankingEntity, RankingsFilters } from '../../../analytics/types' -import { NFT_SERVER_URL } from '../nft' +import { MARKETPLACE_SERVER_URL } from '../marketplace/api' import { retryParams } from '../utils' const DEFAULT_REQUEST_SIZE = 5 @@ -30,4 +30,4 @@ class RankingsAPI extends BaseAPI { } } -export const rankingsAPI = new RankingsAPI(NFT_SERVER_URL, retryParams) +export const rankingsAPI = new RankingsAPI(MARKETPLACE_SERVER_URL, retryParams)