diff --git a/src/apps/deposit/components/SendAsset/SendAsset.tsx b/src/apps/deposit/components/SendAsset/SendAsset.tsx index bfcdfd58..9ef5579c 100644 --- a/src/apps/deposit/components/SendAsset/SendAsset.tsx +++ b/src/apps/deposit/components/SendAsset/SendAsset.tsx @@ -5,7 +5,7 @@ import { useAppKitNetworkCore, useAppKitProvider, } from '@reown/appkit/react'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { IoMdReturnLeft } from 'react-icons/io'; // hooks @@ -18,7 +18,11 @@ import { setDepositStep } from '../../reducer/depositSlice'; import { AddedAssets, BalanceInfo } from '../../types/types'; // utils -import { transferNft, transferTokens } from '../../utils/blockchain'; +import { + checkContractType, + transferNft, + transferTokens, +} from '../../utils/blockchain'; // components import Asset from '../Asset/Asset'; @@ -27,7 +31,8 @@ const SendAsset = () => { const { address } = useAppKitAccount(); const { chainId } = useAppKitNetworkCore(); const { walletProvider } = useAppKitProvider('eip155'); - const [amount, setAmount] = useState(''); + const [amount, setAmount] = useState(''); + const [nftType, setNftType] = useState(''); const walletAddress = useWalletAddress(); const dispatch = useAppDispatch(); const selectedAsset = useAppSelector( @@ -37,16 +42,29 @@ const SendAsset = () => { const [error, setError] = useState(null); const [message, setMessage] = useState(null); - if (!selectedAsset || !address || !walletAddress) return null; - const assetType = - selectedAsset.type === 'AddedAsset' && selectedAsset.assetType === 'nft' - ? 'nft' - : 'token'; + (selectedAsset && 'name' in selectedAsset) || + selectedAsset?.assetType === 'token' + ? 'token' + : 'nft'; + + useEffect(() => { + if (assetType === 'nft') { + setAmount('1'); + const checkNftType = async () => { + const type = await checkContractType( + Number(chainId), + selectedAsset as AddedAssets + ); + setNftType(type); + }; + + checkNftType(); + } + }, [assetType, chainId, selectedAsset]); + + if (!selectedAsset || !address || !walletAddress) return null; - if (assetType === 'nft') { - setAmount('1'); - } const handleSendTx = async () => { if (assetType === 'token') { try { @@ -141,9 +159,11 @@ const SendAsset = () => { />

- {assetType === 'token' - ? 'How much would you like to deposit in your PillarX Wallet?' - : 'Transfer the amount of 1 NFT'} + {assetType === 'token' && + 'How much would you like to deposit in your PillarX Wallet?'} + {assetType === 'nft' && + nftType === 'ERC721' && + 'Transfer this NFT'}

{assetType === 'token' && ( { /> )}
- {Number(amount) > 0 && ( + {Number(amount) > 0 && nftType !== 'ERC1155' ? ( + ) : ( +

+ Sorry, we do not currently support ERC1155 transfers. You can + manually transfer this NFT to your PillarX wallet address:{' '} + {walletAddress} +

)} )} diff --git a/src/apps/deposit/utils/blockchain.tsx b/src/apps/deposit/utils/blockchain.tsx index 2dcc0ee0..4ff71952 100644 --- a/src/apps/deposit/utils/blockchain.tsx +++ b/src/apps/deposit/utils/blockchain.tsx @@ -382,6 +382,62 @@ export const transferTokens = async ( } }; +export const checkContractType = async ( + chainId: number, + selectedAsset: AddedAssets +) => { + const chain = getNetworkViem(chainId); + const chainUrl = chainMapping[chain.name.toLowerCase() as Network] || null; + + if (!chainUrl) { + throw new Error(`Unsupported chain: ${chain.name}`); + } + + const ERC721_INTERFACE_ID = '0x80ac58cd'; + const ERC1155_INTERFACE_ID = '0xd9b67a26'; + + const supportsInterfaceABI = [ + { + name: 'supportsInterface', + type: 'function', + stateMutability: 'view', + inputs: [{ name: 'interfaceId', type: 'bytes4' }], + outputs: [{ name: '', type: 'bool' }], + }, + ]; + + try { + const publicClient = createPublicClient({ + chain, + transport: http(chainUrl), + }); + + const isERC721 = await publicClient.readContract({ + address: selectedAsset.tokenAddress as `0x${string}`, + abi: supportsInterfaceABI, + functionName: 'supportsInterface', + args: [ERC721_INTERFACE_ID], + }); + + const isERC1155 = await publicClient.readContract({ + address: selectedAsset.tokenAddress as `0x${string}`, + abi: supportsInterfaceABI, + functionName: 'supportsInterface', + args: [ERC1155_INTERFACE_ID], + }); + + if (isERC721) { + return 'ERC721'; + } + if (isERC1155) { + return 'ERC1155'; + } + } catch (error) { + console.error('Error checking the ERC type:', error); + } + return '0'; +}; + export const transferNft = async ( chainId: number, walletProvider: Provider, @@ -391,65 +447,72 @@ export const transferNft = async ( ): Promise => { try { const walletClient = createWalletClient({ - chain: getNetworkViem(Number(chainId)), + chain: getNetworkViem(chainId), transport: custom(walletProvider), }); - const isNft = - selectedAsset.type === 'AddedAsset' && selectedAsset.assetType === 'nft'; - - if (!isNft) { - return ';'; - } + const isNft = !( + (selectedAsset && 'name' in selectedAsset) || + selectedAsset?.assetType === 'token' + ); if (isNft) { - // Encode the function data - const calldataERC721 = encodeFunctionData({ - abi: ERC721_ABI, - functionName: 'safeTransferFrom', - args: [accountAddress, pillarXAddress, selectedAsset.tokenId], - }); - - const calldataERC1155 = encodeFunctionData({ - abi: ERC1155_ABI, - functionName: 'safeTransferFrom', - args: [ - accountAddress, - pillarXAddress, - selectedAsset.tokenId, - '1', - '0x', - ], - }); - - // Try ERC721 transfer - try { - const txHash = await walletClient.sendTransaction({ - account: accountAddress as `0x${string}`, - to: selectedAsset.tokenAddress as `0x${string}`, - value: BigInt('0'), - data: calldataERC721, - }); + const contractType = await checkContractType(chainId, selectedAsset); - return txHash; - } catch (errorERC721) { - console.error(`ERC721 transfer failed: ${errorERC721}`); - - // Fallback to ERC1155 transfer + if (contractType === 'ERC721') { + // Try ERC721 transfer try { + // Encode the function data + const calldataERC721 = encodeFunctionData({ + abi: ERC721_ABI, + functionName: 'transferFrom', + args: [accountAddress, pillarXAddress, selectedAsset.tokenId], + }); + const txHash = await walletClient.sendTransaction({ account: accountAddress as `0x${string}`, to: selectedAsset.tokenAddress as `0x${string}`, value: BigInt('0'), - data: calldataERC1155, + data: calldataERC721, }); return txHash; - } catch (errorERC1155) { - console.error(`ERC1155 transfer also failed: ${errorERC1155}`); - return 'Error transferring NFT: both ERC721 and ERC1155 failed.'; + } catch (errorERC721) { + console.warn(`ERC721 transfer failed: ${errorERC721}`); } } + + if (contractType === 'ERC1155') { + return 'ERC1155 deposit not supported.'; + // // TODO when ERC1155 is supported + // // Try ERC1155 transfer + // try { + // // Encode the function data + // const calldataERC1155 = encodeFunctionData({ + // abi: ERC1155_ABI, + // functionName: 'safeTransferFrom', + // args: [ + // accountAddress, + // pillarXAddress, + // selectedAsset.tokenId, + // '1', + // '0x', + // ], + // }); + + // const txHash = await walletClient.sendTransaction({ + // account: accountAddress as `0x${string}`, + // to: selectedAsset.tokenAddress as `0x${string}`, + // value: BigInt('0'), + // data: calldataERC1155, + // }); + + // return txHash; + // } catch (errorERC1155) { + // console.error(`ERC1155 transfer also failed: ${errorERC1155}`); + // return 'Error transferring NFT: both ERC721 and ERC1155 failed.'; + // } + } } return 'Selected asset is not an NFT. Transfer aborted.'; } catch (error) {