From f1c1b19b5937cd5c7277654acf818e73786f835f Mon Sep 17 00:00:00 2001 From: Tin Date: Mon, 4 Aug 2025 09:43:03 +0700 Subject: [PATCH 1/2] fix: incorrect chain ID in contract functions --- package-lock.json | 13 +++ package.json | 1 + .../ContractFunctions/ContractFunctions.tsx | 37 ++++---- src/components/ContractFunctions/index.tsx | 13 ++- src/utils/constants.ts | 91 +++++++++++-------- 5 files changed, 98 insertions(+), 57 deletions(-) diff --git a/package-lock.json b/package-lock.json index d418e76..2005f36 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "jose": "^6.0.11", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-error-boundary": "^6.0.0", "tailwind-merge": "^3.2.0", "tailwindcss": "^4.1.5", "viem": "^2.29.0", @@ -8513,6 +8514,18 @@ "react": "^18.3.1" } }, + "node_modules/react-error-boundary": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.0.0.tgz", + "integrity": "sha512-gdlJjD7NWr0IfkPlaREN2d9uUZUlksrfOx7SX62VRerwXbMY6ftGCIZua1VG1aXFNOimhISsTq+Owp725b9SiA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, "node_modules/react-refresh": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", diff --git a/package.json b/package.json index d901d56..1fbd6c1 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "jose": "^6.0.11", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-error-boundary": "^6.0.0", "tailwind-merge": "^3.2.0", "tailwindcss": "^4.1.5", "viem": "^2.29.0", diff --git a/src/components/ContractFunctions/ContractFunctions.tsx b/src/components/ContractFunctions/ContractFunctions.tsx index 95d7c8e..35285fe 100644 --- a/src/components/ContractFunctions/ContractFunctions.tsx +++ b/src/components/ContractFunctions/ContractFunctions.tsx @@ -1,7 +1,9 @@ import { multicall } from "@wagmi/core"; +import { useMemo, useState } from "react"; import { encodeFunctionData, formatEther, parseAbi } from "viem"; import { useAccount, + useChainId, useConfig, useEstimateGas, useReadContract, @@ -9,12 +11,10 @@ import { } from "wagmi"; import { useLogging } from "../../hooks/useLogging"; import { - DEFAULT_CHAIN, MOCK_ERC20_CONTRACT, MOCK_ERC721_CONTRACT, } from "../../utils/constants"; import { Button } from "../common/Button"; -import { useMemo, useState } from "react"; export const ContractFunctions = () => { const { setLog } = useLogging(); @@ -27,15 +27,16 @@ export const ContractFunctions = () => { const setLoading = (key: string, loading: boolean) => { setLoadingStates((prev) => ({ ...prev, [key]: loading })); }; + const chainId = useChainId(); const data = useMemo( () => encodeFunctionData({ - abi: parseAbi(MOCK_ERC20_CONTRACT(DEFAULT_CHAIN.id).abi), + abi: parseAbi(MOCK_ERC20_CONTRACT(chainId).abi), functionName: "transfer", args: [address, "10"], }), - [address] + [address, chainId] ); const { refetch: refetchGasEstimate } = useEstimateGas({ @@ -49,14 +50,14 @@ export const ContractFunctions = () => { const { writeContractAsync } = useWriteContract(); const { refetch: refetchErc20Balance } = useReadContract({ - address: MOCK_ERC20_CONTRACT(DEFAULT_CHAIN.id).address, - abi: parseAbi(MOCK_ERC20_CONTRACT(DEFAULT_CHAIN.id).abi), + address: MOCK_ERC20_CONTRACT(chainId).address, + abi: parseAbi(MOCK_ERC20_CONTRACT(chainId).abi), functionName: "balanceOf", args: [address], }); const { refetch: refetchErc721Balance } = useReadContract({ - address: MOCK_ERC721_CONTRACT(DEFAULT_CHAIN.id).address, - abi: parseAbi(MOCK_ERC721_CONTRACT(DEFAULT_CHAIN.id).abi), + address: MOCK_ERC721_CONTRACT(chainId).address, + abi: parseAbi(MOCK_ERC721_CONTRACT(chainId).abi), functionName: "balanceOf", args: [address], }); @@ -73,8 +74,8 @@ export const ContractFunctions = () => { const mintMockTokenFn = async () => { try { const result = await writeContractAsync({ - address: MOCK_ERC20_CONTRACT(DEFAULT_CHAIN.id).address, - abi: parseAbi(MOCK_ERC20_CONTRACT(DEFAULT_CHAIN.id).abi), + address: MOCK_ERC20_CONTRACT(chainId).address, + abi: parseAbi(MOCK_ERC20_CONTRACT(chainId).abi), functionName: "mint", args: [address, "10"], }); @@ -87,8 +88,8 @@ export const ContractFunctions = () => { const transferTokenFn = async () => { try { const result = await writeContractAsync({ - address: MOCK_ERC20_CONTRACT(DEFAULT_CHAIN.id).address, - abi: parseAbi(MOCK_ERC20_CONTRACT(DEFAULT_CHAIN.id).abi), + address: MOCK_ERC20_CONTRACT(chainId).address, + abi: parseAbi(MOCK_ERC20_CONTRACT(chainId).abi), functionName: "transfer", args: [address, "10"], }); @@ -114,8 +115,8 @@ export const ContractFunctions = () => { const mintMockERC721Fn = async () => { try { const result = await writeContractAsync({ - address: MOCK_ERC721_CONTRACT(DEFAULT_CHAIN.id).address, - abi: parseAbi(MOCK_ERC721_CONTRACT(DEFAULT_CHAIN.id).abi), + address: MOCK_ERC721_CONTRACT(chainId).address, + abi: parseAbi(MOCK_ERC721_CONTRACT(chainId).abi), functionName: "safeMint", args: [address], }); @@ -143,14 +144,14 @@ export const ContractFunctions = () => { const result = await multicall(config, { contracts: [ { - address: MOCK_ERC20_CONTRACT(DEFAULT_CHAIN.id).address, - abi: parseAbi(MOCK_ERC20_CONTRACT(DEFAULT_CHAIN.id).abi), + address: MOCK_ERC20_CONTRACT(chainId).address, + abi: parseAbi(MOCK_ERC20_CONTRACT(chainId).abi), functionName: "balanceOf", args: [address!], }, { - address: MOCK_ERC721_CONTRACT(DEFAULT_CHAIN.id).address, - abi: parseAbi(MOCK_ERC721_CONTRACT(DEFAULT_CHAIN.id).abi), + address: MOCK_ERC721_CONTRACT(chainId).address, + abi: parseAbi(MOCK_ERC721_CONTRACT(chainId).abi), functionName: "balanceOf", args: [address!], }, diff --git a/src/components/ContractFunctions/index.tsx b/src/components/ContractFunctions/index.tsx index 6784fae..3da10a3 100644 --- a/src/components/ContractFunctions/index.tsx +++ b/src/components/ContractFunctions/index.tsx @@ -1,3 +1,4 @@ +import { ErrorBoundary } from "react-error-boundary"; import { AccountFunctions } from "./AccountFunctions"; import { ContractFunctions } from "./ContractFunctions"; import { SigningFunctions } from "./SigningFunctions"; @@ -7,7 +8,17 @@ export const BlockchainFunctions = () => {
- + { + return ( +
+ Blockchain Functions Error: {error?.message} +
+ ); + }} + > + +
); }; diff --git a/src/utils/constants.ts b/src/utils/constants.ts index edef294..d7fa3cf 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -1,8 +1,10 @@ +import { BUILD_ENV as AIRKIT_BUILD_ENV } from "@mocanetwork/airkit"; import { type Address, type Chain, defineChain } from "viem"; import { baseSepolia, soneiumMinato } from "viem/chains"; -import { BUILD_ENV as AIRKIT_BUILD_ENV } from "@mocanetwork/airkit"; -export const mocaTestnet: Chain & { contracts: { multicall3: { address: `0x${string}`; blockCreated: number } } } = defineChain({ +export const mocaTestnet: Chain & { + contracts: { multicall3: { address: `0x${string}`; blockCreated: number } }; +} = defineChain({ id: 5151, name: "Moca Testnet", nativeCurrency: { @@ -28,51 +30,64 @@ export const mocaTestnet: Chain & { contracts: { multicall3: { address: `0x${str } as const); export const BUILD_ENV = AIRKIT_BUILD_ENV.SANDBOX; -export const DEFAULT_CHAIN = mocaTestnet; const ERC20_ADDRESSES: { [chainId: number]: Address } = { [baseSepolia.id]: "0xa807429271f7001ED6e5eB40e2029B7ecbA9445f", [soneiumMinato.id]: "0x15a2e33bf32563C796b5f85e77236e20C6D1b956", - [mocaTestnet.id]: "0xF025335C838738ba426ded3ABc8Ce36B871F5c0C" + [mocaTestnet.id]: "0xF025335C838738ba426ded3ABc8Ce36B871F5c0C", }; const ERC721_ADDRESSES: { [chainId: number]: Address } = { [baseSepolia.id]: "0xc9061eEC6abEB13DC7815e47FFfe1b9a40A8088b", [soneiumMinato.id]: "0x51aeA0a26D84eCa3ADE38078b6eFDa5e04702607", - [mocaTestnet.id]: "0x6c513255C62D9036aFd14dA3633C4f2e4239adD1" + [mocaTestnet.id]: "0x6c513255C62D9036aFd14dA3633C4f2e4239adD1", +}; + +export const MOCK_ERC20_CONTRACT = (chainId: number) => { + const erc20Address = ERC20_ADDRESSES[chainId]; + + if (!erc20Address) { + throw new Error(`No erc20 address found for chainId: ${chainId}`); + } + + return { + address: erc20Address, + abi: [ + "function balanceOf(address account) view returns (uint256)", + "function symbol() view returns (string)", + "function mint(address to, uint256 amount)", + "function transfer(address to, uint256 value)", + "error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed)", + "error ERC20InvalidSender(address sender)", + "error ERC20InvalidReceiver(address receiver)", + "error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed)", + "error ERC20InvalidApprover(address approver)", + "error ERC20InvalidSpender(address spender)", + ], + }; }; -export const MOCK_ERC20_CONTRACT = (chainId: number) => ({ - address: ERC20_ADDRESSES[chainId], - abi: [ - "function balanceOf(address account) view returns (uint256)", - "function symbol() view returns (string)", - "function mint(address to, uint256 amount)", - "function transfer(address to, uint256 value)", - "error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed)", - "error ERC20InvalidSender(address sender)", - "error ERC20InvalidReceiver(address receiver)", - "error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed)", - "error ERC20InvalidApprover(address approver)", - "error ERC20InvalidSpender(address spender)", - ], - chainId: DEFAULT_CHAIN.id, -}); +export const MOCK_ERC721_CONTRACT = (chainId: number) => { + const erc721Address = ERC721_ADDRESSES[chainId]; -export const MOCK_ERC721_CONTRACT = (chainId: number) => ({ - address: ERC721_ADDRESSES[chainId], - abi: [ - "function balanceOf(address account) view returns (uint256)", - "function safeMint(address to)", - "function transferFrom(address from, address to, uint256 tokenId)", - "error ERC721InvalidOwner(address owner)", - "error ERC721NonexistentToken(uint256 tokenId)", - "error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner)", - "error ERC721InvalidSender(address sender)", - "error ERC721InvalidReceiver(address receiver)", - "error ERC721InsufficientApproval(address operator, uint256 tokenId)", - "error ERC721InvalidApprover(address approver)", - "error ERC721InvalidOperator(address operator)", - ], - chainId: DEFAULT_CHAIN.id, -}); + if (!erc721Address) { + throw new Error(`No erc721 address found for chainId: ${chainId}`); + } + + return { + address: erc721Address, + abi: [ + "function balanceOf(address account) view returns (uint256)", + "function safeMint(address to)", + "function transferFrom(address from, address to, uint256 tokenId)", + "error ERC721InvalidOwner(address owner)", + "error ERC721NonexistentToken(uint256 tokenId)", + "error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner)", + "error ERC721InvalidSender(address sender)", + "error ERC721InvalidReceiver(address receiver)", + "error ERC721InsufficientApproval(address operator, uint256 tokenId)", + "error ERC721InvalidApprover(address approver)", + "error ERC721InvalidOperator(address operator)", + ], + }; +}; From 018b1d5b634f9d46104e98005ec7f58aef9423ce Mon Sep 17 00:00:00 2001 From: Tin Date: Mon, 4 Aug 2025 10:04:25 +0700 Subject: [PATCH 2/2] fix: wrong amount in minting/transfering --- src/components/ContractFunctions/ContractFunctions.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/ContractFunctions/ContractFunctions.tsx b/src/components/ContractFunctions/ContractFunctions.tsx index 35285fe..26c0b2d 100644 --- a/src/components/ContractFunctions/ContractFunctions.tsx +++ b/src/components/ContractFunctions/ContractFunctions.tsx @@ -1,6 +1,6 @@ import { multicall } from "@wagmi/core"; import { useMemo, useState } from "react"; -import { encodeFunctionData, formatEther, parseAbi } from "viem"; +import { encodeFunctionData, formatEther, parseAbi, parseUnits } from "viem"; import { useAccount, useChainId, @@ -34,7 +34,7 @@ export const ContractFunctions = () => { encodeFunctionData({ abi: parseAbi(MOCK_ERC20_CONTRACT(chainId).abi), functionName: "transfer", - args: [address, "10"], + args: [address, parseUnits("10", 18)], }), [address, chainId] ); @@ -77,7 +77,7 @@ export const ContractFunctions = () => { address: MOCK_ERC20_CONTRACT(chainId).address, abi: parseAbi(MOCK_ERC20_CONTRACT(chainId).abi), functionName: "mint", - args: [address, "10"], + args: [address, parseUnits("10", 18)], }); setLog(`Minted token: ${result}`, "info"); } catch (error) { @@ -91,7 +91,7 @@ export const ContractFunctions = () => { address: MOCK_ERC20_CONTRACT(chainId).address, abi: parseAbi(MOCK_ERC20_CONTRACT(chainId).abi), functionName: "transfer", - args: [address, "10"], + args: [address, parseUnits("10", 18)], }); setLog(`Transferred token: ${result}`, "info"); } catch (error) {