diff --git a/packages/mask/src/plugin-infra/host.ts b/packages/mask/src/plugin-infra/host.ts index 46f26970bfa8..90afe4505400 100644 --- a/packages/mask/src/plugin-infra/host.ts +++ b/packages/mask/src/plugin-infra/host.ts @@ -29,6 +29,8 @@ export function createSharedContext(pluginID: string, signal: AbortSignal): Plug send: WalletRPC.sendPayload, + fetch: Services.Helper.r2d2Fetch, + openPopupWindow: Services.Helper.openPopupWindow, closePopupWindow: Services.Helper.removePopupWindow, diff --git a/packages/mask/src/plugins/Avatar/Application/NFTListPage.tsx b/packages/mask/src/plugins/Avatar/Application/NFTListPage.tsx index cc997bf84e38..1b6a438cda81 100644 --- a/packages/mask/src/plugins/Avatar/Application/NFTListPage.tsx +++ b/packages/mask/src/plugins/Avatar/Application/NFTListPage.tsx @@ -1,6 +1,6 @@ import { useImageChecker } from '@masknet/shared' import { makeStyles } from '@masknet/theme' -import type { NonFungibleToken } from '@masknet/web3-shared-base' +import { createNonFungibleToken, NonFungibleToken } from '@masknet/web3-shared-base' import { ChainId, createERC721Token, SchemaType } from '@masknet/web3-shared-evm' import { Box, Skeleton } from '@mui/material' import { useState } from 'react' @@ -54,7 +54,9 @@ export function NFTListPage(props: NFTListPageProps) { const { classes } = useStyles() const { onSelect, chainId, tokenInfo, tokens, children } = props const [selectedToken, setSelectedToken] = useState | undefined>( - tokenInfo ? createERC721Token(chainId, tokenInfo.address, tokenInfo.tokenId) : undefined, + tokenInfo + ? createNonFungibleToken(chainId, tokenInfo.address, SchemaType.ERC721, tokenInfo.tokenId) + : undefined, ) const onChange = (token: NonFungibleToken) => { diff --git a/packages/mask/src/plugins/Avatar/SNSAdaptor/AddNFT.tsx b/packages/mask/src/plugins/Avatar/SNSAdaptor/AddNFT.tsx index a51c17e97ec8..983c58817e6b 100644 --- a/packages/mask/src/plugins/Avatar/SNSAdaptor/AddNFT.tsx +++ b/packages/mask/src/plugins/Avatar/SNSAdaptor/AddNFT.tsx @@ -61,13 +61,13 @@ export function AddNFT(props: AddNFTProps) { return } - const token = await connection?.getNonFungibleToken(address, tokenId, { chainId, account }) + const token = await connection?.getNonFungibleToken(address, tokenId, undefined, { chainId, account }) if (token) { if (chainId && token && token.contract?.chainId !== chainId) { setMessage('chain does not match.') return } - if (!token || !isSameAddress(token?.metadata?.owner, account ?? _account)) { + if (!token || !isSameAddress(token?.contract?.owner, account ?? _account)) { setMessage(t('nft_owner_hint')) return } diff --git a/packages/mask/src/plugins/Avatar/hooks/useTokenOwner.ts b/packages/mask/src/plugins/Avatar/hooks/useTokenOwner.ts index 895c87d5aedd..ca870736e84a 100644 --- a/packages/mask/src/plugins/Avatar/hooks/useTokenOwner.ts +++ b/packages/mask/src/plugins/Avatar/hooks/useTokenOwner.ts @@ -1,18 +1,18 @@ import { useAsyncRetry } from 'react-use' +import type { ChainId } from '@masknet/web3-shared-evm' import { isSameAddress, NetworkPluginID } from '@masknet/web3-shared-base' +import { useWeb3Connection, useAccount } from '@masknet/plugin-infra/web3' import { activatedSocialNetworkUI } from '../../../social-network' import { PluginNFTAvatarRPC } from '../messages' import { usePersonas } from './usePersonas' -import { useWeb3Connection, useAccount } from '@masknet/plugin-infra/web3' -import type { ChainId } from '@masknet/web3-shared-evm' export function useTokenOwner(address: string, tokenId: string, chainId?: ChainId) { const connection = useWeb3Connection(NetworkPluginID.PLUGIN_EVM, { chainId }) const account = useAccount(NetworkPluginID.PLUGIN_EVM) return useAsyncRetry(async () => { if (!address || !tokenId || !connection) return - const token = await connection.getNonFungibleToken(address, tokenId, { chainId, account }) - return { owner: token?.metadata?.owner, name: token?.contract?.name, symbol: token?.contract?.symbol } + const token = await connection.getNonFungibleToken(address, tokenId, undefined, { chainId, account }) + return { owner: token?.contract?.owner, name: token?.contract?.name, symbol: token?.contract?.symbol } }, [connection, tokenId, address, account, chainId]) } diff --git a/packages/mask/src/plugins/MaskBox/hooks/useContext.ts b/packages/mask/src/plugins/MaskBox/hooks/useContext.ts index 074d9539e9ee..9c289d81d4ab 100644 --- a/packages/mask/src/plugins/MaskBox/hooks/useContext.ts +++ b/packages/mask/src/plugins/MaskBox/hooks/useContext.ts @@ -8,7 +8,14 @@ import { omit, clamp, first, uniq } from 'lodash-unified' import BigNumber from 'bignumber.js' import { createContainer } from 'unstated-next' import { unreachable } from '@dimensiondev/kit' -import { ChainId, useTokenConstants, useMaskBoxConstants, ZERO_ADDRESS, isZeroAddress } from '@masknet/web3-shared-evm' +import { + ChainId, + useTokenConstants, + useMaskBoxConstants, + ZERO_ADDRESS, + isZeroAddress, + SchemaType, +} from '@masknet/web3-shared-evm' import type { NonPayableTx } from '@masknet/web3-contracts/types/types' import { BoxInfo, BoxState } from '../type' import { useMaskBoxInfo } from './useMaskBoxInfo' @@ -239,6 +246,8 @@ function useContext(initialState?: { boxId: string; hashRoot: string }) { const { value: contractDetailed } = useNonFungibleTokenContract( NetworkPluginID.PLUGIN_EVM, maskBoxInfo?.nft_address ?? '', + undefined, + SchemaType.ERC721, { account }, ) // #endregion diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/NftRedPacketHistoryItem.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/NftRedPacketHistoryItem.tsx index 08745ed7b14e..cc212b38c080 100644 --- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/NftRedPacketHistoryItem.tsx +++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/NftRedPacketHistoryItem.tsx @@ -159,6 +159,8 @@ export const NftRedPacketHistoryItem: FC = memo( const { value: contractDetailed } = useNonFungibleTokenContract( NetworkPluginID.PLUGIN_EVM, history.token_contract.address, + undefined, + undefined, { account }, ) const { closeDialog: closeApplicationBoardDialog } = useRemoteControlledDialog( diff --git a/packages/mask/src/plugins/RedPacket/SNSAdaptor/SelectNftTokenDialog.tsx b/packages/mask/src/plugins/RedPacket/SNSAdaptor/SelectNftTokenDialog.tsx index 895423b727be..da2934f3c6c1 100644 --- a/packages/mask/src/plugins/RedPacket/SNSAdaptor/SelectNftTokenDialog.tsx +++ b/packages/mask/src/plugins/RedPacket/SNSAdaptor/SelectNftTokenDialog.tsx @@ -402,11 +402,16 @@ export function SelectNftTokenDialog(props: SelectNftTokenDialogProps) { // #region fetch token detail const onSearch = useCallback(async () => { setLoadingToken(true) - const _tokenDetailed = (await connection?.getNonFungibleToken(contract?.address ?? '', tokenId, { - account, - chainId, - })) as NonFungibleToken - setTokenDetailed(_tokenDetailed?.metadata?.owner ? { ..._tokenDetailed, index: 0 } : undefined) + const _tokenDetailed = (await connection?.getNonFungibleToken( + contract?.address ?? '', + tokenId, + SchemaType.ERC721, + { + account, + chainId, + }, + )) as NonFungibleToken + setTokenDetailed(_tokenDetailed?.contract?.owner ? { ..._tokenDetailed, index: 0 } : undefined) setSearched(true) setLoadingToken(false) }, [connection, contract, tokenId, chainId, account]) @@ -420,7 +425,7 @@ export function SelectNftTokenDialog(props: SelectNftTokenDialogProps) { if (tokenDetailedOwnerList.length > 0) setTokenDetailed(undefined) }, [tokenDetailedOwnerList.length]) - const isOwner = isSameAddress(account, tokenDetailed?.metadata?.owner) || tokenDetailedSelectedList.length > 0 + const isOwner = isSameAddress(account, tokenDetailed?.contract?.owner) || tokenDetailedSelectedList.length > 0 const isAdded = existTokenDetailedList.map((t) => t.tokenId).includes(tokenDetailed?.tokenId ?? '') // #endregion diff --git a/packages/mask/src/plugins/Tips/components/AddDialog.tsx b/packages/mask/src/plugins/Tips/components/AddDialog.tsx index 6d69d539857d..e6c7086c8c7c 100644 --- a/packages/mask/src/plugins/Tips/components/AddDialog.tsx +++ b/packages/mask/src/plugins/Tips/components/AddDialog.tsx @@ -9,7 +9,7 @@ import { useERC721TokenContract } from '@masknet/plugin-infra/web3-evm' import { ImageIcon, InjectedDialog, InjectedDialogProps } from '@masknet/shared' import { makeStyles } from '@masknet/theme' import { isSameAddress, NetworkPluginID, NonFungibleToken } from '@masknet/web3-shared-base' -import type { ChainId, SchemaType } from '@masknet/web3-shared-evm' +import { ChainId, SchemaType } from '@masknet/web3-shared-evm' import { Button, DialogContent, FormControl, TextField, Typography } from '@mui/material' import { FC, useCallback, useEffect, useMemo, useState } from 'react' import { useAsyncFn } from 'react-use' @@ -89,10 +89,15 @@ export const AddDialog: FC = ({ onAdd, onClose, ...rest }) => { setMessage(t.tip_add_collectibles_error()) return } - const erc721TokenDetailed = await web3Connection?.getNonFungibleToken(contractAddress, tokenId, { - chainId, - account, - }) + const erc721TokenDetailed = await web3Connection?.getNonFungibleToken( + contractAddress, + tokenId, + SchemaType.ERC721, + { + chainId, + account, + }, + ) if (!erc721TokenDetailed) { setMessage(t.tip_add_collectibles_error()) diff --git a/packages/mask/src/plugins/Tips/contexts/Tip/TipTaskProvider.tsx b/packages/mask/src/plugins/Tips/contexts/Tip/TipTaskProvider.tsx index cd589968c597..e8839982d81f 100644 --- a/packages/mask/src/plugins/Tips/contexts/Tip/TipTaskProvider.tsx +++ b/packages/mask/src/plugins/Tips/contexts/Tip/TipTaskProvider.tsx @@ -1,6 +1,6 @@ import { useChainId, useFungibleToken, useNonFungibleTokenContract, useAccount } from '@masknet/plugin-infra/web3' import { NetworkPluginID } from '@masknet/web3-shared-base' -import type { GasConfig } from '@masknet/web3-shared-evm' +import { GasConfig, SchemaType } from '@masknet/web3-shared-evm' import { FC, useContext, useEffect, useMemo, useState } from 'react' import { useSubscription } from 'use-subscription' import { getStorage } from '../../storage' @@ -29,9 +29,15 @@ export const TipTaskProvider: FC> = ({ children, const [erc721TokenId, setErc721TokenId] = useState(null) const storedTokens = useSubscription(getStorage().addedTokens.subscription) - const { value: erc721Contract } = useNonFungibleTokenContract(NetworkPluginID.PLUGIN_EVM, erc721Address, { - account, - }) + const { value: erc721Contract } = useNonFungibleTokenContract( + NetworkPluginID.PLUGIN_EVM, + erc721Address, + undefined, + SchemaType.ERC721, + { + account, + }, + ) useEffect(() => { setTipType(TipType.Token) diff --git a/packages/mask/src/plugins/Tips/contexts/Tip/useNftTip.ts b/packages/mask/src/plugins/Tips/contexts/Tip/useNftTip.ts index 510c245819cf..ad381cc9a58c 100644 --- a/packages/mask/src/plugins/Tips/contexts/Tip/useNftTip.ts +++ b/packages/mask/src/plugins/Tips/contexts/Tip/useNftTip.ts @@ -14,7 +14,7 @@ export function useNftTip(recipient: string, tokenId: string | null, contractAdd const sendTip = useCallback(async () => { if (!tokenId || !contractAddress) return const hash = await transferCallback(tokenId, recipient) - const tokenDetailed = await connection?.getNonFungibleToken(contractAddress ?? '', tokenId, { + const tokenDetailed = await connection?.getNonFungibleToken(contractAddress ?? '', tokenId, undefined, { chainId, account, }) diff --git a/packages/mask/src/plugins/Wallet/SNSAdaptor/SelectNftContractDialog.tsx b/packages/mask/src/plugins/Wallet/SNSAdaptor/SelectNftContractDialog.tsx index 5daa6bd588c3..9224cea9f936 100644 --- a/packages/mask/src/plugins/Wallet/SNSAdaptor/SelectNftContractDialog.tsx +++ b/packages/mask/src/plugins/Wallet/SNSAdaptor/SelectNftContractDialog.tsx @@ -235,6 +235,8 @@ function SearchResultBox(props: SearchResultBoxProps) { const { value: contractDetailed = null, loading } = useNonFungibleTokenContract( NetworkPluginID.PLUGIN_EVM, keyword, + undefined, + undefined, { account }, ) return ( diff --git a/packages/plugin-infra/src/types.ts b/packages/plugin-infra/src/types.ts index 9dd37631c17f..7841b3d3d2af 100644 --- a/packages/plugin-infra/src/types.ts +++ b/packages/plugin-infra/src/types.ts @@ -151,6 +151,8 @@ export namespace Plugin.Shared { }, ): Promise + fetch: (input: RequestInfo, init?: RequestInit | undefined) => Promise + /** Open popup window */ openPopupWindow(route?: PopupRoutes, params?: Record): Promise /** Close popup window */ diff --git a/packages/plugin-infra/src/web3-state/Connection.ts b/packages/plugin-infra/src/web3-state/Connection.ts index ed810495f75f..4552d26eea87 100644 --- a/packages/plugin-infra/src/web3-state/Connection.ts +++ b/packages/plugin-infra/src/web3-state/Connection.ts @@ -1,5 +1,6 @@ import type { Subscription } from 'use-subscription' import type { Connection, ConnectionOptions, ConnectionState as Web3ConnectionState } from '@masknet/web3-shared-base' +import type { Plugin } from '../types' export class ConnectionState< ChainId, @@ -35,10 +36,14 @@ export class ConnectionState< > { constructor( + private context: Plugin.Shared.SharedContext, protected createConnection: ( - chainId?: ChainId, - account?: string, - providerType?: ProviderType, + context: Plugin.Shared.SharedContext, + options?: { + chainId?: ChainId + account?: string + providerType?: ProviderType + }, ) => Connection< ChainId, SchemaType, @@ -71,10 +76,10 @@ export class ConnectionState< } async getConnection(options?: Web3ConnectionOptions) { - return this.createConnection( - options?.chainId ?? this.subscription.chainId?.getCurrentValue(), - options?.account ?? this.subscription.account?.getCurrentValue(), - options?.providerType ?? this.subscription.providerType?.getCurrentValue(), - ) + return this.createConnection(this.context, { + chainId: options?.chainId ?? this.subscription.chainId?.getCurrentValue(), + account: options?.account ?? this.subscription.account?.getCurrentValue(), + providerType: options?.providerType ?? this.subscription.providerType?.getCurrentValue(), + }) } } diff --git a/packages/plugin-infra/src/web3/EVM/useNonFungibleOwnerTokens.ts b/packages/plugin-infra/src/web3/EVM/useNonFungibleOwnerTokens.ts index b4dd9a53dc58..ebd69d83d904 100644 --- a/packages/plugin-infra/src/web3/EVM/useNonFungibleOwnerTokens.ts +++ b/packages/plugin-infra/src/web3/EVM/useNonFungibleOwnerTokens.ts @@ -1,6 +1,6 @@ -import type { ChainId, SchemaType } from '@masknet/web3-shared-evm' -import { useAsyncRetry } from 'react-use' import { useRef } from 'react' +import { useAsyncRetry } from 'react-use' +import { ChainId, SchemaType } from '@masknet/web3-shared-evm' import { NonFungibleToken, NetworkPluginID } from '@masknet/web3-shared-base' import { useWeb3Connection } from '../useWeb3Connection' @@ -49,7 +49,7 @@ export function useNonFungibleOwnerTokens( allListRef.current = ( await Promise.all( listOfPairs?.map((x) => - connection.getNonFungibleToken(x[0], x[1], { + connection.getNonFungibleToken(x[0], x[1], SchemaType.ERC721, { account: ownerAccount, }), ) ?? [], diff --git a/packages/plugin-infra/src/web3/useNonFungibleToken.ts b/packages/plugin-infra/src/web3/useNonFungibleToken.ts index d4bbe0802745..63e74e489081 100644 --- a/packages/plugin-infra/src/web3/useNonFungibleToken.ts +++ b/packages/plugin-infra/src/web3/useNonFungibleToken.ts @@ -8,7 +8,8 @@ import { useAccount } from './useAccount' export function useNonFungibleToken( pluginID?: T, address?: string, - id?: string, + tokenId?: string, + schemaType?: Web3Helper.SchemaTypeScope, options?: Web3Helper.Web3ConnectionOptionsScope, ) { const chainId = useChainId(pluginID, options?.chainId) @@ -16,11 +17,11 @@ export function useNonFungibleToken | undefined>(async () => { - if (!address || !id || !connection) return - return connection.getNonFungibleToken(address, id, { + if (!address || !tokenId || !connection) return + return connection.getNonFungibleToken(address, tokenId, schemaType, { ...options, account: options?.account ?? account, chainId: options?.chainId ?? chainId, }) - }, [address, id, connection, chainId, account]) + }, [address, tokenId, schemaType, connection, chainId, account]) } diff --git a/packages/plugin-infra/src/web3/useNonFungibleTokenContract.ts b/packages/plugin-infra/src/web3/useNonFungibleTokenContract.ts index 056dfea6c1b8..6f50ce629317 100644 --- a/packages/plugin-infra/src/web3/useNonFungibleTokenContract.ts +++ b/packages/plugin-infra/src/web3/useNonFungibleTokenContract.ts @@ -6,7 +6,9 @@ import { useWeb3Connection } from './useWeb3Connection' export function useNonFungibleTokenContract( pluginID: T, address: string, - options: Web3Helper.Web3ConnectionOptionsScope, + tokenId?: string, + schemaType?: Web3Helper.SchemaTypeScope, + options?: Web3Helper.Web3ConnectionOptionsScope, ) { const connection = useWeb3Connection(pluginID, options) @@ -14,6 +16,6 @@ export function useNonFungibleTokenContract, Web3Helper.SchemaTypeScope> | undefined >(async () => { if (!connection || !address || !options) return - return connection.getNonFungibleTokenContract?.(address, options) - }, [address, connection, JSON.stringify(options)]) + return connection.getNonFungibleTokenContract?.(address, tokenId, schemaType, options) + }, [address, tokenId, schemaType, connection, JSON.stringify(options)]) } diff --git a/packages/plugin-infra/src/web3/useNonFungibleTokens.ts b/packages/plugin-infra/src/web3/useNonFungibleTokens.ts index 9df978e81314..b378e9e72fcc 100644 --- a/packages/plugin-infra/src/web3/useNonFungibleTokens.ts +++ b/packages/plugin-infra/src/web3/useNonFungibleTokens.ts @@ -14,6 +14,8 @@ export function useNonFungibleTokens>>(async () => { if (!connection) return EMPTY_LIST - return Promise.all(listOfPairs?.map((x) => connection.getNonFungibleToken(x[0], x[1], options)) ?? []) + return Promise.all( + listOfPairs?.map((x) => connection.getNonFungibleToken(x[0], x[1], undefined, options)) ?? [], + ) }, [connection, listOfPairs?.join()]) } diff --git a/packages/plugins/EVM/src/state/Connection.ts b/packages/plugins/EVM/src/state/Connection.ts index fe6c3e74ad99..5bcd52f74150 100644 --- a/packages/plugins/EVM/src/state/Connection.ts +++ b/packages/plugins/EVM/src/state/Connection.ts @@ -30,13 +30,13 @@ export class Connection extends ConnectionState< Web3Provider > { constructor( - private context: Plugin.Shared.SharedContext, + context: Plugin.Shared.SharedContext, subscription: { account?: Subscription chainId?: Subscription providerType?: Subscription }, ) { - super(createConnection, subscription) + super(context, createConnection, subscription) } } diff --git a/packages/plugins/EVM/src/state/Connection/connection.ts b/packages/plugins/EVM/src/state/Connection/connection.ts index 1439a44d6982..4205d8256c77 100644 --- a/packages/plugins/EVM/src/state/Connection/connection.ts +++ b/packages/plugins/EVM/src/state/Connection/connection.ts @@ -6,11 +6,14 @@ import type { ERC20 } from '@masknet/web3-contracts/types/ERC20' import type { ERC20Bytes32 } from '@masknet/web3-contracts/types/ERC20Bytes32' import type { ERC165 } from '@masknet/web3-contracts/types/ERC165' import type { ERC721 } from '@masknet/web3-contracts/types/ERC721' +import type { ERC1155 } from '@masknet/web3-contracts/types/ERC1155' import type { BalanceChecker } from '@masknet/web3-contracts/types/BalanceChecker' import ERC20ABI from '@masknet/web3-contracts/abis/ERC20.json' import ERC20Bytes32ABI from '@masknet/web3-contracts/abis/ERC20Bytes32.json' import ERC721ABI from '@masknet/web3-contracts/abis/ERC721.json' +import ERC1155ABI from '@masknet/web3-contracts/abis/ERC1155.json' import BalanceCheckerABI from '@masknet/web3-contracts/abis/BalanceChecker.json' +import type { Plugin } from '@masknet/plugin-infra' import { ChainId, EthereumMethodType, @@ -25,16 +28,16 @@ import { sendTransaction, createERC20Token, parseStringOrBytes32, - createERC721Token, - createERC721Contract, - createERC721Metadata, - createERC721Collection, createWeb3Provider, getEthereumConstants, } from '@masknet/web3-shared-evm' import { Account, ConnectionOptions, + createNonFungibleToken, + createNonFungibleTokenCollection, + createNonFungibleTokenContract, + createNonFungibleTokenMetadata, currySameAddress, FungibleToken, isSameAddress, @@ -43,12 +46,12 @@ import { NonFungibleTokenContract, TransactionStatusType, } from '@masknet/web3-shared-base' +import type { BaseContract } from '@masknet/web3-contracts/types/types' import { createContext, dispatch } from './composer' import { Providers } from './provider' -import type { EVM_Connection, EVM_Web3ConnectionOptions } from './types' +import type { ERC1155Metadata, ERC721Metadata, EVM_Connection, EVM_Web3ConnectionOptions } from './types' import { getReceiptStatus } from './utils' import { Web3StateSettings } from '../../settings' -import type { BaseContract } from '@masknet/web3-contracts/types/types' const EMPTY_STRING = () => Promise.resolve('') const ZERO = () => Promise.resolve(0) @@ -77,8 +80,26 @@ function isNativeTokenAddress(chainId: ChainId, address: string) { return isSameAddress(address, getTokenConstants(chainId).NATIVE_TOKEN_ADDRESS) } +async function fetchJSON( + input: RequestInfo, + init?: RequestInit | undefined, + options?: { + fetch?: (input: RequestInfo, init?: RequestInit | undefined) => Promise + }, +) { + const fetch_ = options?.fetch ?? globalThis.fetch + const response = await fetch_(input, init) + if (!response.ok) throw new Error('Failed to fetch.') + return response.json() as Promise +} + class Connection implements EVM_Connection { - constructor(private chainId: ChainId, private account: string, private providerType: ProviderType) {} + constructor( + private chainId: ChainId, + private account: string, + private providerType: ProviderType, + private context?: Plugin.Shared.SharedContext, + ) {} // Hijack RPC requests and process them with koa like middleware private get hijackedRequest() { @@ -194,7 +215,7 @@ class Connection implements EVM_Connection { } // ERC20 - const contract = await this.getWeb3Contract(address, ERC20ABI as AbiItem[], options) + const contract = await this.getERC20Contract(address, options) const tx = contract?.methods.transfer(recipient, toHex(amount)) return sendTransaction(contract, tx, options?.overrides) } @@ -202,11 +223,22 @@ class Connection implements EVM_Connection { address: string, recipient: string, tokenId: string, - amount: string, + amount?: string, + schema?: SchemaType, options?: EVM_Web3ConnectionOptions, ): Promise { + const account = options?.account ?? this.account + const actualSchemaType = schema ?? (await this.getSchemaType(address, options)) + + // ERC1155 + if (actualSchemaType === SchemaType.ERC1155) { + const contract = await this.getERC1155Contract(address, options) + const tx = contract?.methods.safeTransferFrom(account, recipient, tokenId, amount, '0x') + return sendTransaction(contract, tx, options?.overrides) + } + // ERC721 - const contract = await this.getWeb3Contract(address, ERC721ABI as AbiItem[], options) + const contract = await this.getERC721Contract(address, options) const tx = contract?.methods.transferFrom(options?.account ?? this.account, recipient, tokenId) return sendTransaction(contract, tx, options?.overrides) } @@ -244,37 +276,133 @@ class Connection implements EVM_Connection { return } + async getNonFungibleTokenMetadata( + address: string, + tokenId?: string, + schema?: SchemaType, + options?: EVM_Web3ConnectionOptions, + ) { + const actualChainId = options?.chainId ?? this.chainId + const actualSchemaType = schema ?? (await this.getSchemaType(address, options)) + + // ERC1155 + if (actualSchemaType === SchemaType.ERC1155) { + const contract = await this.getERC1155Contract(address, options) + const uri = await contract?.methods.uri(tokenId).call() + if (!uri) throw new Error('Failed to read metadata uri.') + const response = await fetchJSON(uri, undefined, { + fetch: this.context?.fetch, + }) + return createNonFungibleTokenMetadata( + actualChainId, + response.name, + '', + response.description, + undefined, + response.image, + response.image, + ) + } + + // ERC721 + const contract = await this.getERC721Contract(address, options) + const uri = await contract?.methods.tokenURI(tokenId).call() + if (!uri) throw new Error('Failed to read metadata uri.') + const response = await fetchJSON(uri, undefined, { + fetch: this.context?.fetch, + }) + return createNonFungibleTokenMetadata( + actualChainId, + response.name, + '', + response.description, + undefined, + response.image, + response.image, + ) + } async getNonFungibleTokenContract( address: string, + tokenId?: string, + schema?: SchemaType, options?: EVM_Web3ConnectionOptions, ): Promise> { + const actualAccount = options?.account ?? this.account + const actualChainId = options?.chainId ?? this.chainId + const actualSchemaType = schema ?? (await this.getSchemaType(address, options)) + + // ERC1155 + if (actualSchemaType === SchemaType.ERC1155) { + const contract = await this.getERC1155Contract(address, options) + const results = await Promise.allSettled([ + // @ts-ignore some contracts implement it + contract?.methods.name().call() ?? EMPTY_STRING, + // @ts-ignore some contracts implement it + contract?.methods.symbol().call() ?? EMPTY_STRING, + // @ts-ignore some contracts implement it + contract?.methods.balanceOf(actualAccount, tokenId).call() ?? ZERO, + ]) + + const [name, symbol, balance] = results.map((result) => + result.status === 'fulfilled' ? result.value : '', + ) as string[] + const balance_ = balance as unknown as number + const metadata = tokenId ? await this.getNonFungibleTokenMetadata(address, tokenId, schema, options) : null + + return createNonFungibleTokenContract( + actualChainId, + SchemaType.ERC1155, + address, + metadata?.name ?? name ?? 'Unknown Token', + metadata?.symbol ?? symbol ?? 'UNKNOWN', + tokenId && balance_ > 0 ? actualAccount : undefined, + tokenId ? balance_ : 0, + ) + } + // ERC721 - const contract = await this.getWeb3Contract(address, ERC721ABI as AbiItem[], options) + const contract = await this.getERC721Contract(address, options) const results = await Promise.allSettled([ contract?.methods.name().call() ?? EMPTY_STRING, contract?.methods.symbol().call() ?? EMPTY_STRING, - contract?.methods.balanceOf(options?.account ?? '').call() ?? EMPTY_STRING, + contract?.methods.balanceOf(actualAccount).call() ?? EMPTY_STRING, ]) + const [name, symbol, balance] = results.map((result) => result.status === 'fulfilled' ? result.value : '', ) as string[] - return createERC721Contract( - options?.chainId ?? this.chainId, + const balance_ = balance as unknown as number + + return createNonFungibleTokenContract( + actualChainId, + SchemaType.ERC721, address, name ?? 'Unknown Token', symbol ?? 'UNKNOWN', - balance as unknown as number, + actualAccount, + balance_, ) } async getNonFungibleTokenCollection( address: string, + tokenId?: string, + schema?: SchemaType, options?: EVM_Web3ConnectionOptions, ): Promise> { + const actualAccount = options?.account ?? this.account + const actualChainId = options?.chainId ?? this.chainId + const actualSchemaType = schema ?? (await this.getSchemaType(address, options)) + + // ERC1155 + if (actualSchemaType === SchemaType.ERC1155) { + throw new Error('Not implemented') + } + // ERC721 - const contract = await this.getWeb3Contract(address, ERC721ABI as AbiItem[], options) + const contract = await this.getERC721Contract(address, options) const results = await Promise.allSettled([contract?.methods.name().call() ?? EMPTY_STRING]) const [name] = results.map((result) => (result.status === 'fulfilled' ? result.value : '')) as string[] - return createERC721Collection(options?.chainId ?? this.chainId, address, name ?? 'Unknown Token', '') + return createNonFungibleTokenCollection(actualChainId, address, name ?? 'Unknown Token', '') } async switchChain(options?: EVM_Web3ConnectionOptions): Promise { await Providers[options?.providerType ?? this.providerType].switchChain(options?.chainId) @@ -285,19 +413,35 @@ class Connection implements EVM_Connection { return this.getBalance(options?.account ?? this.account, options) } async getFungibleTokenBalance(address: string, options?: EVM_Web3ConnectionOptions): Promise { + const actualAccount = options?.account ?? this.account + const actualChainId = options?.chainId ?? this.chainId + // Native - if (!address || isNativeTokenAddress(options?.chainId ?? this.chainId, address)) - return this.getNativeTokenBalance(options) + if (!address || isNativeTokenAddress(actualChainId, address)) return this.getNativeTokenBalance(options) // ERC20 - const contract = await this.getWeb3Contract(address, ERC20ABI as AbiItem[], options) - return contract?.methods.balanceOf(options?.account ?? this.account).call() ?? '0' - } - async getNonFungibleTokenBalance(address: string, options?: EVM_Web3ConnectionOptions): Promise { - const contract = await this.getWeb3Contract(address, ERC721ABI as AbiItem[], options) - return contract?.methods.balanceOf(options?.account ?? this.account).call() ?? '0' + const contract = await this.getERC20Contract(address, options) + return contract?.methods.balanceOf(actualAccount).call() ?? '0' } + async getNonFungibleTokenBalance( + address: string, + tokenId?: string, + schema?: SchemaType, + options?: EVM_Web3ConnectionOptions, + ): Promise { + const actualAccount = options?.account ?? this.account + const actualSchemaType = schema ?? (await this.getSchemaType(address, options)) + + // ERC721 + if (actualSchemaType === SchemaType.ERC721) { + const contract = await this.getERC721Contract(address, options) + return contract?.methods.balanceOf(actualAccount).call() ?? '0' + } + // ERC1155 + const contract = await this.getERC1155Contract(address, options) + return contract?.methods?.balanceOf(actualAccount, tokenId).call() ?? '0' + } async getFungibleTokensBalance( listOfAddress: string[], options?: EVM_Web3ConnectionOptions, @@ -372,7 +516,7 @@ class Connection implements EVM_Connection { return this.getNativeToken(options) // ERC20 - const contract = await this.getWeb3Contract(address, ERC20ABI as AbiItem[], options) + const contract = await this.getERC20Contract(address, options) const bytes32Contract = await this.getWeb3Contract(address, ERC20Bytes32ABI as AbiItem[], options) const results = await Promise.allSettled([ contract?.methods.name().call() ?? EMPTY_STRING, @@ -395,35 +539,24 @@ class Connection implements EVM_Connection { async getNonFungibleToken( address: string, - id: string, + tokenId: string, + schema?: SchemaType, options?: EVM_Web3ConnectionOptions, ): Promise> { const chainId = options?.chainId ?? this.chainId - // ERC721 - const contract = await this.getWeb3Contract(address, ERC721ABI as AbiItem[], options) - const results = await Promise.allSettled([ - contract?.methods.name().call() ?? EMPTY_STRING, - contract?.methods.symbol().call() ?? EMPTY_STRING, - contract?.methods.ownerOf(id).call() ?? EMPTY_STRING, - contract?.methods.balanceOf(options?.account ?? '').call() ?? EMPTY_STRING, - ]) - const [name, symbol, owner, balance] = results.map((result) => - result.status === 'fulfilled' ? result.value : '', - ) as string[] + const actualSchema = schema ?? (await this.getSchemaType(address, options)) + const contract = await this.getNonFungibleTokenContract(address, tokenId, schema, options) + const collection = await this.getNonFungibleTokenCollection(address, tokenId, schema, options) + const metadata = await this.getNonFungibleTokenMetadata(address, tokenId, schema, options) - return createERC721Token( + return createNonFungibleToken( chainId, address, - id, - createERC721Metadata(chainId, name ?? 'Unknown Token', symbol ?? 'UNKNOWN', owner), - createERC721Contract( - chainId, - address, - name ?? 'Unknown Token', - symbol ?? 'UNKNOWN', - balance as unknown as number, - ), - createERC721Collection(chainId, name ?? 'Unknown Token', ''), + actualSchema ?? SchemaType.ERC721, + tokenId, + metadata, + contract, + collection, ) } @@ -436,6 +569,18 @@ class Connection implements EVM_Connection { return createContract(web3, address, ABI) } + async getERC20Contract(address: string, options?: EVM_Web3ConnectionOptions) { + return this.getWeb3Contract(address, ERC20ABI as AbiItem[], options) + } + + async getERC721Contract(address: string, options?: EVM_Web3ConnectionOptions) { + return this.getWeb3Contract(address, ERC721ABI as AbiItem[], options) + } + + async getERC1155Contract(address: string, options?: EVM_Web3ConnectionOptions) { + return this.getWeb3Contract(address, ERC1155ABI as AbiItem[], options) + } + async getAccount(options?: EVM_Web3ConnectionOptions) { const accounts = await this.hijackedRequest( { @@ -685,6 +830,15 @@ class Connection implements EVM_Connection { * @param providerType * @returns */ -export function createConnection(chainId = ChainId.Mainnet, account = '', providerType = ProviderType.MaskWallet) { - return new Connection(chainId, account, providerType) +export function createConnection( + context?: Plugin.Shared.SharedContext, + options?: { + chainId?: ChainId + account?: string + providerType?: ProviderType + }, +) { + const { chainId = ChainId.Mainnet, account = '', providerType = ProviderType.MaskWallet } = options ?? {} + + return new Connection(chainId, account, providerType, context) } diff --git a/packages/plugins/EVM/src/state/Connection/types.ts b/packages/plugins/EVM/src/state/Connection/types.ts index b32c6d885b64..a74a1c8d21a2 100644 --- a/packages/plugins/EVM/src/state/Connection/types.ts +++ b/packages/plugins/EVM/src/state/Connection/types.ts @@ -21,6 +21,19 @@ export interface EVM_Connection extends Web3Helper.Web3Connection } +export interface ERC721Metadata { + name: string + description: string + image: string +} + +export interface ERC1155Metadata { + name: string + decimals: number + description: string + image: string +} + export interface Context { readonly account: string readonly chainId: ChainId diff --git a/packages/plugins/EVM/src/state/TransactionFormatter.ts b/packages/plugins/EVM/src/state/TransactionFormatter.ts index 58216b8316d4..96ef364831e0 100644 --- a/packages/plugins/EVM/src/state/TransactionFormatter.ts +++ b/packages/plugins/EVM/src/state/TransactionFormatter.ts @@ -32,6 +32,7 @@ import { ITODescriptor } from './TransactionFormatter/descriptors/ITO' import { RedPacketDescriptor } from './TransactionFormatter/descriptors/RedPacket' import { ERC20Descriptor } from './TransactionFormatter/descriptors/ERC20' import { SwapDescriptor } from './TransactionFormatter/descriptors/Swap' + export class TransactionFormatter extends TransactionFormatterState { private coder = ABICoder as unknown as ABICoder.AbiCoder private connection = createConnection() diff --git a/packages/plugins/Flow/src/state/Connection.ts b/packages/plugins/Flow/src/state/Connection.ts index c444bde252e9..7ad463e42491 100644 --- a/packages/plugins/Flow/src/state/Connection.ts +++ b/packages/plugins/Flow/src/state/Connection.ts @@ -34,13 +34,13 @@ export class Connection extends ConnectionState< Web3Provider > { constructor( - private context: Plugin.Shared.SharedContext, + context: Plugin.Shared.SharedContext, subscription: { account?: Subscription chainId?: Subscription providerType?: Subscription }, ) { - super(createConnection, subscription) + super(context, createConnection, subscription) } } diff --git a/packages/plugins/Flow/src/state/Connection/connection.ts b/packages/plugins/Flow/src/state/Connection/connection.ts index 7f1aa32cbba7..151127f6b082 100644 --- a/packages/plugins/Flow/src/state/Connection/connection.ts +++ b/packages/plugins/Flow/src/state/Connection/connection.ts @@ -9,15 +9,22 @@ import { NonFungibleToken, NonFungibleTokenCollection, NonFungibleTokenContract, + NonFungibleTokenMetadata, TransactionStatusType, } from '@masknet/web3-shared-base' import { toHex } from '@masknet/shared-base' import { Providers } from './provider' import type { FlowWeb3Connection as BaseConnection, FlowConnectionOptions } from './types' import { Web3StateSettings } from '../../settings' +import type { Plugin } from '@masknet/plugin-infra' class Connection implements BaseConnection { - constructor(private chainId: ChainId, private account: string, private providerType: ProviderType) {} + constructor( + private chainId: ChainId, + private account: string, + private providerType: ProviderType, + private context?: Plugin.Shared.SharedContext, + ) {} private _getWeb3Provider(options?: FlowConnectionOptions) { return Providers[options?.providerType ?? this.providerType] @@ -56,12 +63,16 @@ class Connection implements BaseConnection { } getNonFungibleTokenContract( address: string, + tokenId?: string, + schemaType?: SchemaType, options?: FlowConnectionOptions, ): Promise> { throw new Error('Method not implemented.') } getNonFungibleTokenCollection( address: string, + tokenId?: string, + schemaType?: SchemaType, options?: FlowConnectionOptions, ): Promise> { throw new Error('Method not implemented.') @@ -89,6 +100,7 @@ class Connection implements BaseConnection { recipient: string, tokenId: string, amount: string, + schemaType?: SchemaType, options?: FlowConnectionOptions, ): Promise { throw new Error('Method not implemented.') @@ -102,7 +114,12 @@ class Connection implements BaseConnection { getFungibleTokenBalance(address: string, options?: FlowConnectionOptions): Promise { throw new Error('Method not implemented.') } - getNonFungibleTokenBalance(address: string, options?: FlowConnectionOptions): Promise { + getNonFungibleTokenBalance( + address: string, + tokenId?: string, + schemaType?: SchemaType, + options?: FlowConnectionOptions, + ): Promise { throw new Error('Method not implemented.') } getFungibleTokensBalance( @@ -126,11 +143,20 @@ class Connection implements BaseConnection { } getNonFungibleToken( address: string, - id: string, + tokenId: string, + schemaType?: SchemaType, options?: FlowConnectionOptions, ): Promise> { throw new Error('Method not implemented.') } + getNonFungibleTokenMetadata( + address: string, + tokenId: string, + schemaType?: SchemaType, + options?: FlowConnectionOptions, + ): Promise> { + throw new Error('Method not implemented.') + } async getAccount(options?: FlowConnectionOptions): Promise { const web3 = await this.getWeb3(options) @@ -239,6 +265,15 @@ class Connection implements BaseConnection { * @param providerType * @returns */ -export function createConnection(chainId = ChainId.Mainnet, account = '', providerType = ProviderType.Blocto) { - return new Connection(chainId, account, providerType) +export function createConnection( + context: Plugin.Shared.SharedContext, + options?: { + chainId?: ChainId + account?: string + providerType?: ProviderType + }, +) { + const { chainId = ChainId.Mainnet, account = '', providerType = ProviderType.Blocto } = options ?? {} + + return new Connection(chainId, account, providerType, context) } diff --git a/packages/plugins/Solana/src/state/Connection.ts b/packages/plugins/Solana/src/state/Connection.ts index 48fb57df3dac..eb3c156f202d 100644 --- a/packages/plugins/Solana/src/state/Connection.ts +++ b/packages/plugins/Solana/src/state/Connection.ts @@ -30,13 +30,13 @@ export class Connection extends ConnectionState< Web3Provider > { constructor( - private context: Plugin.Shared.SharedContext, + context: Plugin.Shared.SharedContext, subscription: { account?: Subscription chainId?: Subscription providerType?: Subscription }, ) { - super(createConnection, subscription) + super(context, createConnection, subscription) } } diff --git a/packages/plugins/Solana/src/state/Connection/connection.ts b/packages/plugins/Solana/src/state/Connection/connection.ts index 45df9e8bd495..b09f631d067f 100644 --- a/packages/plugins/Solana/src/state/Connection/connection.ts +++ b/packages/plugins/Solana/src/state/Connection/connection.ts @@ -15,14 +15,21 @@ import { NonFungibleToken, NonFungibleTokenCollection, NonFungibleTokenContract, + NonFungibleTokenMetadata, TransactionStatusType, } from '@masknet/web3-shared-base' import { Web3StateSettings } from '../../settings' +import type { Plugin } from '@masknet/plugin-infra' class Connection implements BaseConnection { private connections: Map = new Map() - constructor(private chainId: ChainId, private account: string, private providerType: ProviderType) {} + constructor( + private chainId: ChainId, + private account: string, + private providerType: ProviderType, + private context?: Plugin.Shared.SharedContext, + ) {} private _getWeb3Provider(options?: SolanaWeb3ConnectionOptions) { return Providers[options?.providerType ?? this.providerType] @@ -69,6 +76,7 @@ class Connection implements BaseConnection { tokenId: string, amount: string, recipient: string, + schemaType?: SchemaType, options?: SolanaWeb3ConnectionOptions, ): Promise { throw new Error('Method not implemented.') @@ -81,12 +89,16 @@ class Connection implements BaseConnection { } getNonFungibleTokenContract( address: string, + tokenId?: string, + schemaType?: SchemaType, options?: SolanaWeb3ConnectionOptions, ): Promise> { throw new Error('Method not implemented.') } getNonFungibleTokenCollection( address: string, + tokenId?: string, + schemaType?: SchemaType, options?: SolanaWeb3ConnectionOptions, ): Promise> { throw new Error('Method not implemented.') @@ -103,7 +115,12 @@ class Connection implements BaseConnection { getFungibleTokenBalance(address: string, options?: SolanaWeb3ConnectionOptions): Promise { throw new Error('Method not implemented.') } - getNonFungibleTokenBalance(address: string, options?: SolanaWeb3ConnectionOptions): Promise { + getNonFungibleTokenBalance( + address: string, + tokenId?: string, + schemaType?: SchemaType, + options?: SolanaWeb3ConnectionOptions, + ): Promise { throw new Error('Method not implemented.') } getFungibleTokensBalance( @@ -222,11 +239,20 @@ class Connection implements BaseConnection { } getNonFungibleToken( address: string, - id: string, + tokenId: string, + schemaType?: SchemaType, options?: SolanaWeb3ConnectionOptions, ): Promise> { throw new Error('Method not implemented.') } + getNonFungibleTokenMetadata( + address: string, + tokenId: string, + schemaType?: SchemaType, + options?: SolanaWeb3ConnectionOptions, + ): Promise> { + throw new Error('Method not implemented.') + } replaceRequest( hash: string, config: Transaction, @@ -243,6 +269,15 @@ class Connection implements BaseConnection { } } -export function createConnection(chainId = ChainId.Mainnet, account = '', providerType = ProviderType.Phantom) { - return new Connection(chainId, account, providerType) +export function createConnection( + context: Plugin.Shared.SharedContext, + options?: { + chainId?: ChainId + account?: string + providerType?: ProviderType + }, +) { + const { chainId = ChainId.Mainnet, account = '', providerType = ProviderType.Phantom } = options ?? {} + + return new Connection(chainId, account, providerType, context) } diff --git a/packages/shared/src/UI/components/NFTCardStyledAssetPlayer/index.tsx b/packages/shared/src/UI/components/NFTCardStyledAssetPlayer/index.tsx index 16748f8c70c8..3ef832f1c824 100644 --- a/packages/shared/src/UI/components/NFTCardStyledAssetPlayer/index.tsx +++ b/packages/shared/src/UI/components/NFTCardStyledAssetPlayer/index.tsx @@ -3,7 +3,6 @@ import { CircularProgress, useTheme } from '@mui/material' import { makeStyles, useStylesExtends } from '@masknet/theme' import { AssetPlayer } from '../AssetPlayer' import { useNonFungibleToken, Web3Helper } from '@masknet/plugin-infra/web3' -import type { ChainId } from '@masknet/web3-shared-evm' import { NetworkPluginID } from '@masknet/web3-shared-base' import { useImageChecker } from '../../../hooks' @@ -64,9 +63,15 @@ export function NFTCardStyledAssetPlayer(props: Props) { } = props const classes = useStylesExtends(useStyles(), props) const theme = useTheme() - const { value: tokenDetailed } = useNonFungibleToken(NetworkPluginID.PLUGIN_EVM, contractAddress, tokenId, { - chainId: chainId as ChainId, - }) + const { value: tokenDetailed } = useNonFungibleToken<'all'>( + NetworkPluginID.PLUGIN_EVM, + contractAddress, + tokenId, + undefined, + { + chainId, + }, + ) const { value: isImageToken } = useImageChecker( url || tokenDetailed?.metadata?.imageURL || tokenDetailed?.metadata?.mediaURL, ) diff --git a/packages/web3-contracts/abis/ERC1155.json b/packages/web3-contracts/abis/ERC1155.json index 6e166b582309..a4d1d60057fc 100644 --- a/packages/web3-contracts/abis/ERC1155.json +++ b/packages/web3-contracts/abis/ERC1155.json @@ -1,4 +1,278 @@ [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "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": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + } + ], + "name": "TransferBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "TransferSingle", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "value", + "type": "string" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "URI", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "accounts", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + } + ], + "name": "balanceOfBatch", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "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": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "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": [ { @@ -17,5 +291,24 @@ ], "stateMutability": "view", "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "uri", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" } ] diff --git a/packages/web3-contracts/types/ArtBlocksMinterContract.d.ts b/packages/web3-contracts/types/ArtBlocksMinterContract.d.ts index 1e8e21c01f4e..ec93f8bd5410 100644 --- a/packages/web3-contracts/types/ArtBlocksMinterContract.d.ts +++ b/packages/web3-contracts/types/ArtBlocksMinterContract.d.ts @@ -21,28 +21,49 @@ interface EventOptions { topics?: string[] } +export type PricePerTokenInWeiUpdated = ContractEventLog<{ + _projectId: string + _pricePerTokenInWei: string + 0: string + 1: string +}> +export type ProjectCurrencyInfoUpdated = ContractEventLog<{ + _projectId: string + _currencyAddress: string + _currencySymbol: string + 0: string + 1: string + 2: string +}> +export type PurchaseToDisabledUpdated = ContractEventLog<{ + _projectId: string + _purchaseToDisabled: boolean + 0: string + 1: boolean +}> + export interface ArtBlocksMinterContract extends BaseContract { constructor(jsonInterface: any[], address?: string, options?: ContractOptions): ArtBlocksMinterContract clone(): ArtBlocksMinterContract methods: { - artblocksContract(): NonPayableTransactionObject - - artistSetBonusContractAddress( - _projectId: number | string | BN, - _bonusContractAddress: string, - ): NonPayableTransactionObject - - artistToggleBonus(_projectId: number | string | BN): NonPayableTransactionObject + contractMintable(arg0: number | string | BN): NonPayableTransactionObject - checkYourAllowanceOfProjectERC20(_projectId: number | string | BN): NonPayableTransactionObject + genArt721CoreAddress(): NonPayableTransactionObject - contractFilterProject(arg0: number | string | BN): NonPayableTransactionObject + getPriceInfo(_projectId: number | string | BN): NonPayableTransactionObject<{ + isConfigured: boolean + tokenPriceInWei: string + currencySymbol: string + currencyAddress: string + 0: boolean + 1: string + 2: string + 3: string + }> - getYourBalanceOfProjectERC20(_projectId: number | string | BN): NonPayableTransactionObject + minterFilterAddress(): NonPayableTransactionObject - projectIdToBonus(arg0: number | string | BN): NonPayableTransactionObject - - projectIdToBonusContractAddress(arg0: number | string | BN): NonPayableTransactionObject + minterType(): NonPayableTransactionObject projectMaxHasBeenInvoked(arg0: number | string | BN): NonPayableTransactionObject @@ -54,6 +75,10 @@ export interface ArtBlocksMinterContract extends BaseContract { purchase(_projectId: number | string | BN): PayableTransactionObject + purchaseTo(_to: string, _projectId: number | string | BN): PayableTransactionObject + + purchaseToDisabled(arg0: number | string | BN): NonPayableTransactionObject + setProjectMaxInvocations(_projectId: number | string | BN): NonPayableTransactionObject setProjectMintLimit( @@ -61,9 +86,34 @@ export interface ArtBlocksMinterContract extends BaseContract { _limit: number | string | BN, ): NonPayableTransactionObject - toggleContractFilter(_projectId: number | string | BN): NonPayableTransactionObject + toggleContractMintable(_projectId: number | string | BN): NonPayableTransactionObject + + togglePurchaseToDisabled(_projectId: number | string | BN): NonPayableTransactionObject + + updatePricePerTokenInWei( + _projectId: number | string | BN, + _pricePerTokenInWei: number | string | BN, + ): NonPayableTransactionObject } events: { + PricePerTokenInWeiUpdated(cb?: Callback): EventEmitter + PricePerTokenInWeiUpdated(options?: EventOptions, cb?: Callback): EventEmitter + + ProjectCurrencyInfoUpdated(cb?: Callback): EventEmitter + ProjectCurrencyInfoUpdated(options?: EventOptions, cb?: Callback): EventEmitter + + PurchaseToDisabledUpdated(cb?: Callback): EventEmitter + PurchaseToDisabledUpdated(options?: EventOptions, cb?: Callback): EventEmitter + allEvents(options?: EventOptions, cb?: Callback): EventEmitter } + + once(event: 'PricePerTokenInWeiUpdated', cb: Callback): void + once(event: 'PricePerTokenInWeiUpdated', options: EventOptions, cb: Callback): void + + once(event: 'ProjectCurrencyInfoUpdated', cb: Callback): void + once(event: 'ProjectCurrencyInfoUpdated', options: EventOptions, cb: Callback): void + + once(event: 'PurchaseToDisabledUpdated', cb: Callback): void + once(event: 'PurchaseToDisabledUpdated', options: EventOptions, cb: Callback): void } diff --git a/packages/web3-contracts/types/ERC1155.d.ts b/packages/web3-contracts/types/ERC1155.d.ts index baebd723ba5f..0087afd683c8 100644 --- a/packages/web3-contracts/types/ERC1155.d.ts +++ b/packages/web3-contracts/types/ERC1155.d.ts @@ -21,13 +21,102 @@ interface EventOptions { topics?: string[] } +export type ApprovalForAll = ContractEventLog<{ + account: string + operator: string + approved: boolean + 0: string + 1: string + 2: boolean +}> +export type TransferBatch = ContractEventLog<{ + operator: string + from: string + to: string + ids: string[] + values: string[] + 0: string + 1: string + 2: string + 3: string[] + 4: string[] +}> +export type TransferSingle = ContractEventLog<{ + operator: string + from: string + to: string + id: string + value: string + 0: string + 1: string + 2: string + 3: string + 4: string +}> +export type URI = ContractEventLog<{ + value: string + id: string + 0: string + 1: string +}> + export interface ERC1155 extends BaseContract { constructor(jsonInterface: any[], address?: string, options?: ContractOptions): ERC1155 clone(): ERC1155 methods: { + balanceOf(account: string, id: number | string | BN): NonPayableTransactionObject + + balanceOfBatch(accounts: string[], ids: (number | string | BN)[]): NonPayableTransactionObject + + isApprovedForAll(account: string, operator: string): NonPayableTransactionObject + + safeBatchTransferFrom( + from: string, + to: string, + ids: (number | string | BN)[], + amounts: (number | string | BN)[], + data: string | number[], + ): NonPayableTransactionObject + + safeTransferFrom( + from: string, + to: string, + id: number | string | BN, + amount: number | string | BN, + data: string | number[], + ): NonPayableTransactionObject + + setApprovalForAll(operator: string, approved: boolean): NonPayableTransactionObject + supportsInterface(interfaceId: string | number[]): NonPayableTransactionObject + + uri(id: number | string | BN): NonPayableTransactionObject } events: { + ApprovalForAll(cb?: Callback): EventEmitter + ApprovalForAll(options?: EventOptions, cb?: Callback): EventEmitter + + TransferBatch(cb?: Callback): EventEmitter + TransferBatch(options?: EventOptions, cb?: Callback): EventEmitter + + TransferSingle(cb?: Callback): EventEmitter + TransferSingle(options?: EventOptions, cb?: Callback): EventEmitter + + URI(cb?: Callback): EventEmitter + URI(options?: EventOptions, cb?: Callback): EventEmitter + allEvents(options?: EventOptions, cb?: Callback): EventEmitter } + + once(event: 'ApprovalForAll', cb: Callback): void + once(event: 'ApprovalForAll', options: EventOptions, cb: Callback): void + + once(event: 'TransferBatch', cb: Callback): void + once(event: 'TransferBatch', options: EventOptions, cb: Callback): void + + once(event: 'TransferSingle', cb: Callback): void + once(event: 'TransferSingle', options: EventOptions, cb: Callback): void + + once(event: 'URI', cb: Callback): void + once(event: 'URI', options: EventOptions, cb: Callback): void } diff --git a/packages/web3-providers/src/NFTScan/index.ts b/packages/web3-providers/src/NFTScan/index.ts index 68a9b323629c..8a6ca32ac0ff 100644 --- a/packages/web3-providers/src/NFTScan/index.ts +++ b/packages/web3-providers/src/NFTScan/index.ts @@ -1,14 +1,13 @@ import urlcat from 'urlcat' -import { createPageable, HubOptions } from '@masknet/web3-shared-base' import { - ChainId, - createERC721Contract, - createERC721Token, - resolveIPFSLinkFromURL, - SchemaType, - createERC721Metadata, - createERC721Collection, -} from '@masknet/web3-shared-evm' + createNonFungibleToken, + createNonFungibleTokenCollection, + createNonFungibleTokenContract, + createNonFungibleTokenMetadata, + createPageable, + HubOptions, +} from '@masknet/web3-shared-base' +import { ChainId, resolveIPFSLinkFromURL, SchemaType } from '@masknet/web3-shared-evm' import addSeconds from 'date-fns/addSeconds' import isBefore from 'date-fns/isBefore' import getUnixTime from 'date-fns/getUnixTime' @@ -60,11 +59,12 @@ function createERC721TokenAsset(asset: NFTScanAsset) { JSON.parse(asset.nft_json ?? '{}').image ?? asset.nft_content_uri ?? payload.image ?? '', ) - return createERC721Token( + return createNonFungibleToken( ChainId.Mainnet, asset.trade_contract ?? asset.nft_contract_address, + SchemaType.ERC721, asset.nft_asset_id, - createERC721Metadata( + createNonFungibleTokenMetadata( ChainId.Mainnet, name, asset.trade_symbol, @@ -73,15 +73,14 @@ function createERC721TokenAsset(asset: NFTScanAsset) { undefined, mediaURL, ), - createERC721Contract( + createNonFungibleTokenContract( ChainId.Mainnet, + SchemaType.ERC721, asset.trade_contract ?? asset.nft_contract_address, name, asset.trade_symbol, - undefined, - mediaURL, ), - createERC721Collection( + createNonFungibleTokenCollection( ChainId.Mainnet, name, name, diff --git a/packages/web3-providers/src/opensea/index.ts b/packages/web3-providers/src/opensea/index.ts index e4fc7d4fc704..616901430609 100644 --- a/packages/web3-providers/src/opensea/index.ts +++ b/packages/web3-providers/src/opensea/index.ts @@ -15,14 +15,9 @@ import { scale10, TokenType, HubOptions, + createNonFungibleTokenContract, } from '@masknet/web3-shared-base' -import { - ChainId, - SchemaType, - createERC721Contract, - createNativeToken, - createERC20Token, -} from '@masknet/web3-shared-evm' +import { ChainId, SchemaType, createNativeToken, createERC20Token } from '@masknet/web3-shared-evm' import type { NonFungibleTokenAPI } from '../types' import type { OpenSeaAssetContract, @@ -305,13 +300,12 @@ export class OpenSeaAPI implements NonFungibleTokenAPI.Provider { const imageURL = resolveIPFSLinkFromURL(asset?.meta?.image?.url.ORIGINAL ?? asset?.meta?.image?.url.PREVIEW ?? '') - return createERC721Token( + return createNonFungibleToken( ChainId.Mainnet, tokenAddress, + SchemaType.ERC721, tokenId, - createERC721Metadata( - ChainId.Mainnet, - asset?.meta?.name ?? '', - '', - first(asset?.owners), - '', - imageURL, - imageURL, - ), - createERC721Contract(ChainId.Mainnet, tokenAddress, asset?.meta?.name ?? '', ''), - createERC721Collection(ChainId.Mainnet, asset?.meta?.name ?? '', '', '', ''), + createNonFungibleTokenMetadata(ChainId.Mainnet, asset?.meta?.name ?? '', '', '', undefined, imageURL, imageURL), + createNonFungibleTokenContract(ChainId.Mainnet, SchemaType.ERC721, tokenAddress, asset?.meta?.name ?? '', ''), + createNonFungibleTokenCollection(ChainId.Mainnet, asset?.meta?.name ?? '', '', '', ''), ) } @@ -193,7 +182,7 @@ export class RaribleAPI implements NonFungibleTokenAPI.Provider createERC721TokenFromAsset(asset.contract, asset.tokenId, asset)) - .filter((x) => x.metadata?.owner?.toLowerCase() === from.toLowerCase()) + .filter((x) => x.contract?.owner?.toLowerCase() === from.toLowerCase()) .map((x) => ({ ...x, provideBy: 'Rarible' })) ?? [] return createPageable(items, indicator, asset.continuation) } diff --git a/packages/web3-shared/base/src/specs/index.ts b/packages/web3-shared/base/src/specs/index.ts index a8ae8b83bdf0..49d5813bf6f0 100644 --- a/packages/web3-shared/base/src/specs/index.ts +++ b/packages/web3-shared/base/src/specs/index.ts @@ -218,6 +218,7 @@ export interface NonFungibleTokenContract { symbol: string address: string schema: SchemaType + owner?: string balance?: number logoURL?: string iconURL?: string @@ -227,7 +228,7 @@ export interface NonFungibleTokenMetadata { chainId: ChainId name: string symbol: string - owner?: string + decimals?: number, description?: string /** preview image url */ imageURL?: string @@ -576,17 +577,28 @@ export interface Connection< /** Get an non-fungible token. */ getNonFungibleToken( address: string, - id: string, + tokenId: string, + schemaType?: SchemaType, options?: Web3ConnectionOptions, ): Promise> + getNonFungibleTokenMetadata( + address: string, + tokenId: string, + schemaType?: SchemaType, + options?: Web3ConnectionOptions, + ): Promise> /** Get an non-fungible token contract. */ getNonFungibleTokenContract( address: string, + tokenId?: string, + schemaType?: SchemaType, options?: Web3ConnectionOptions, ): Promise> /** Get an non-fungible token collection. */ getNonFungibleTokenCollection( address: string, + tokenId?: string, + schemaType?: SchemaType, options?: Web3ConnectionOptions, ): Promise> /** Get native fungible token balance. */ @@ -594,7 +606,7 @@ export interface Connection< /** Get fungible token balance */ getFungibleTokenBalance(address: string, options?: Web3ConnectionOptions): Promise /** Get non-fungible token balance */ - getNonFungibleTokenBalance(address: string, options?: Web3ConnectionOptions): Promise + getNonFungibleTokenBalance(address: string, tokenId?: string, schemaType?: SchemaType, options?: Web3ConnectionOptions): Promise /** Get fungible token balance */ getFungibleTokensBalance(listOfAddress: string[], options?: Web3ConnectionOptions): Promise> /** Get non-fungible token balance */ @@ -649,6 +661,7 @@ export interface Connection< recipient: string, tokenId: string, amount: string, + schemaType?: SchemaType, options?: Web3ConnectionOptions, ): Promise /** Sign a transaction */ diff --git a/packages/web3-shared/base/src/utils/token.ts b/packages/web3-shared/base/src/utils/token.ts index ceaab6eb1581..324b9dc55949 100644 --- a/packages/web3-shared/base/src/utils/token.ts +++ b/packages/web3-shared/base/src/utils/token.ts @@ -55,14 +55,16 @@ export function createNonFungibleTokenContract( address: string, name: string, symbol: string, + owner?: string, balance?: number, ): NonFungibleTokenContract { return { chainId, + schema, name, symbol, address, - schema, + owner, balance, } } @@ -87,8 +89,8 @@ export function createNonFungibleTokenCollection( } export function createNonFungibleToken( chainId: ChainId, - schema: SchemaType, address: string, + schema: SchemaType, tokenId: string, metadata?: NonFungibleToken['metadata'], contract?: NonFungibleToken['contract'], diff --git a/packages/web3-shared/evm/utils/token.ts b/packages/web3-shared/evm/utils/token.ts index ecaf8b330dd2..afe3428d92ad 100644 --- a/packages/web3-shared/evm/utils/token.ts +++ b/packages/web3-shared/evm/utils/token.ts @@ -1,12 +1,4 @@ -import { - createFungibleToken, - createFungibleTokensFromConstants, - createNonFungibleToken, - NonFungibleToken, - NonFungibleTokenCollection, - NonFungibleTokenContract, - NonFungibleTokenMetadata, -} from '@masknet/web3-shared-base' +import { createFungibleToken, createFungibleTokensFromConstants } from '@masknet/web3-shared-base' import Token from '@masknet/web3-constants/evm/token.json' import { chainResolver } from '.' import { CHAIN_DESCRIPTORS, getTokenConstants, ZERO_ADDRESS } from '../constants' @@ -52,86 +44,3 @@ export function createERC20Token( } export const createERC20Tokens = createFungibleTokensFromConstants(getEnumAsArray(ChainId), SchemaType.ERC20, Token) - -export function createERC721Token( - chainId: ChainId, - address: string, - tokenId: string, - metadata?: NonFungibleTokenMetadata, - contract?: NonFungibleTokenContract, - collection?: NonFungibleTokenCollection, -) { - return createNonFungibleToken(chainId, SchemaType.ERC721, address, tokenId, metadata, contract, collection) -} - -export function createERC721Contract( - chainId: ChainId, - address: string, - name: string, - symbol: string, - balance?: number, - logoURL?: string, -): NonFungibleTokenContract { - return { - chainId, - schema: SchemaType.ERC721, - address, - name, - symbol, - balance, - logoURL, - } -} - -export function createERC721Metadata( - chainId: ChainId, - name: string, - symbol: string, - owner?: string, - description?: string, - imageURL?: string, - mediaURL?: string, - mediaType?: string, -): NonFungibleTokenMetadata { - return { - chainId, - name, - symbol, - owner, - description, - imageURL, - mediaURL, - mediaType, - } -} - -export function createERC721Collection( - chainId: ChainId, - name: string, - slug: string, - description?: string, - iconURL?: string, - verified?: boolean, - createdAt?: number, -): NonFungibleTokenCollection { - return { - chainId, - name, - slug, - description, - iconURL, - verified, - createdAt, - } -} - -export function createERC1155Token( - chainId: ChainId, - address: string, - tokenId: string, - metadata?: NonFungibleToken['metadata'], - contract?: NonFungibleToken['contract'], - collection?: NonFungibleToken['collection'], -) { - return createNonFungibleToken(chainId, SchemaType.ERC1155, address, tokenId, metadata, contract, collection) -}