// Set up target address and function signature abi
const targetContractAddress = "<your target contract address>";
const abi = [
"function example()",
"function exampleFeeCapped(uint256)",
"function increment()",
"function incrementFeeCapped(uint256)",
"function incrementWithPermit(address,uint256,uint256,uint8,bytes32,bytes32)",
"function incrementWithPermitFeeCapped(address,uint256,uint256,uint8,bytes32,bytes32,uint256)"
];
const [address] = await window.ethereum!.request({
method: "eth_requestAccounts",
});
// Generate payload using front-end provider such as MetaMask
const client = createWalletClient({
account: address,
chain,
transport: custom(window.ethereum!),
});
// Address of the token to pay fees
const feeToken = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
const chainId = await client.getChainId();
// Helper function to check if token supports permit
async function checkPermitSupport(tokenContract) {
try {
const hasPermit = await tokenContract.permit.staticCall(
ethers.ZeroAddress, ethers.ZeroAddress, 0, 0, 0, "0x00", "0x00"
).catch(() => false);
return hasPermit !== false;
} catch {
return false;
}
}
// Helper function to generate permit signature
async function signPermit(signer, token, amount, spender, deadline, chainId) {
const domain = {
name: await token.name(),
version: "1",
chainId: chainId,
verifyingContract: token.target
};
const types = {
Permit: [
{ name: "owner", type: "address" },
{ name: "spender", type: "address" },
{ name: "value", type: "uint256" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" }
]
};
const nonce = await token.nonces(signer.address);
const values = {
owner: signer.address,
spender: spender,
value: amount,
nonce: nonce,
deadline: deadline
};
const signature = await signer.signTypedData(domain, types, values);
return ethers.Signature.from(signature);
}
// Encode function data
const data = encodeFunctionData({
abi: abi,
functionName: "example",
});
// -----------------------------------------------------------------
// Alternative example using Gelato Fee Oracle
// Retrieve the estimate fee from the Gelato
const fee = await relay.getEstimatedFee(
BigInt(chainId),
feeToken,
gasLimit,
false,
);
const maxFee = fee * 2 // You can use 2x or 3x to set your maxFee
// Encode function data
const dataMaxFee = encodeFunctionData({
abi: abi,
functionName: "exampleFeeCapped",
args: [maxFee]
});
// -----------------------------------------------------------------
// For ERC-20 tokens with permit support (enhanced user experience)
const tokenContract = new ethers.Contract(tokenAddress, tokenABI, provider);
const supportsPermit = await checkPermitSupport(tokenContract);
if (supportsPermit) {
const signer = await provider.getSigner();
const amount = ethers.parseUnits("100", 6); // 100 USDC
const deadline = Math.floor(Date.now() / 1000) + 3600; // 1 hour from now
// Generate permit signature
const sig = await signPermit(
signer,
tokenContract,
amount,
targetContractAddress,
deadline,
chainId
);
const { v, r, s } = sig;
// Encode function data with permit
const dataWithPermit = encodeFunctionData({
abi: abi,
functionName: "incrementWithPermit",
args: [tokenAddress, amount, deadline, v, r, s]
});
// With fee cap using permit
const dataWithPermitFeeCapped = encodeFunctionData({
abi: abi,
functionName: "incrementWithPermitFeeCapped",
args: [tokenAddress, amount, deadline, v, r, s, maxFee]
});
}