Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 41 additions & 15 deletions src/apps/deposit/components/SendAsset/SendAsset.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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';
Expand All @@ -27,7 +31,8 @@ const SendAsset = () => {
const { address } = useAppKitAccount();
const { chainId } = useAppKitNetworkCore();
const { walletProvider } = useAppKitProvider<Provider>('eip155');
const [amount, setAmount] = useState('');
const [amount, setAmount] = useState<string>('');
const [nftType, setNftType] = useState<string>('');
const walletAddress = useWalletAddress();
const dispatch = useAppDispatch();
const selectedAsset = useAppSelector(
Expand All @@ -37,16 +42,29 @@ const SendAsset = () => {
const [error, setError] = useState<string | null>(null);
const [message, setMessage] = useState<string | null>(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 {
Expand Down Expand Up @@ -141,9 +159,11 @@ const SendAsset = () => {
/>
<div className="flex flex-col gap-2 items-center">
<p className="text-base">
{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'}
</p>
{assetType === 'token' && (
<input
Expand All @@ -156,14 +176,20 @@ const SendAsset = () => {
/>
)}
</div>
{Number(amount) > 0 && (
{Number(amount) > 0 && nftType !== 'ERC1155' ? (
<button
type="button"
onClick={handleSendTx}
className="w-fit h-fit self-center px-4 py-2 mt-4 rounded-md bg-purple_medium hover:bg-purple_light"
>
Start transfer
</button>
) : (
<p className="text-base">
Sorry, we do not currently support ERC1155 transfers. You can
manually transfer this NFT to your PillarX wallet address:{' '}
{walletAddress}
</p>
)}
</div>
)}
Expand Down
149 changes: 106 additions & 43 deletions src/apps/deposit/utils/blockchain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -391,65 +447,72 @@ export const transferNft = async (
): Promise<string> => {
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) {
Expand Down
Loading