signPermitQuote
The signPermitQuote
method signs a permit quote using EIP-2612 permit signatures. This enables gasless approvals for ERC20 tokens that implement the permit extension, allowing for more efficient transactions without requiring separate approval transactions.
Usage
const signedQuote = await meeClient.signPermitQuote({
fusionQuote: permitQuote
});
Parameters
parameters
An object containing the parameters for signing a permit quote.
type SignPermitQuoteParams = {
/**
* The fusion quote to sign, obtained from getPermitQuote
*/
fusionQuote: GetPermitQuotePayload;
/**
* Optional account to use for signing (defaults to client.account)
*/
account?: MultichainSmartAccount;
}
/**
* The payload from getPermitQuote containing quote and trigger information
*/
type GetPermitQuotePayload = {
quote: GetQuotePayload;
trigger: Trigger;
}
/**
* Trigger information
*/
type Trigger = {
/**
* Chain ID where the token exists
* @example 1 // Ethereum Mainnet
*/
chainId: number;
/**
* Address of the token to use for payment
* Must support ERC20Permit (EIP-2612)
* @example "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" // USDC
*/
tokenAddress: Address;
/**
* Amount of tokens to use, in the token's smallest unit
* @example 1000000n // 1 USDC (6 decimals)
*/
amount: bigint;
}
Return Value
Returns a Promise that resolves to a SignPermitQuotePayload
object containing the quote details with the added permit signature.
type SignPermitQuotePayload = GetQuotePayload & {
/**
* The signature of the quote, prefixed with '0x02' and concatenated with
* the encoded permit parameters and signature components
*/
signature: Hex;
}
Examples
Basic Example
import { createMeeClient, toMultichainNexusAccount, mcUSDC } from "@biconomy/abstractjs";
import { http, zeroAddress } from "viem";
import { mainnet, optimism } from "viem/chains";
// Setup multichain account
const mcNexus = await toMultichainNexusAccount({
chains: [mainnet, optimism],
signer: eoaAccount,
transports: [http(), http()]
});
// Create MEE client
const meeClient = await createMeeClient({ account: mcNexus });
// Token information (using a token that supports EIP-2612 permits)
const tokenAddress = mcUSDC.addressOn(mainnet.id);
// Create trigger information
const trigger = {
chainId: mainnet.id,
tokenAddress: tokenAddress,
amount: 1000000n // 1 USDC (6 decimals)
};
// Fee token information
const feeToken = {
address: tokenAddress,
chainId: mainnet.id
};
try {
// 1. Get a permit quote
const permitQuote = await meeClient.getPermitQuote({
instructions: [
mcNexus.build({
type: "default",
data: {
calls: [
{
to: zeroAddress,
gasLimit: 50000n,
value: 0n
}
],
chainId: optimism.id
}
})
],
feeToken,
trigger
});
console.log("Permit quote received:");
console.log(`Quote ID: ${permitQuote.quote.id}`);
// 2. Sign the permit quote
const signedQuote = await meeClient.signPermitQuote({
fusionQuote: permitQuote
});
console.log("Quote signed with signature:", signedQuote.signature);
// The signedQuote can now be executed with executeSignedQuote
} catch (error) {
console.error("Error during permit quote signing:", error);
}
Complete Flow Example with Execution
import { createMeeClient, toMultichainNexusAccount, mcUSDC } from "@biconomy/abstractjs";
import { http, generatePrivateKey, privateKeyToAccount } from "viem";
import { mainnet } from "viem/chains";
// Setup multichain account
const mcNexus = await toMultichainNexusAccount({
chains: [mainnet],
signer: eoaAccount,
transports: [http()]
});
// Create MEE client
const meeClient = await createMeeClient({ account: mcNexus });
// Create a recipient account
const recipientAccount = privateKeyToAccount(generatePrivateKey());
// Token address that supports EIP-2612 permit
const tokenAddress = mcUSDC.addressOn(mainnet.id);
// Create trigger for a minimal transfer (for testing)
const trigger = {
chainId: mainnet.id,
tokenAddress: tokenAddress,
amount: 1n
};
// Fee token (same as trigger token in this example)
const feeToken = {
address: tokenAddress,
chainId: mainnet.id
};
// Get smart account information
const recipient = mcNexus.addressOn(mainnet.id, true);
const sender = mcNexus.signer.address;
try {
// 1. Get the fusion quote
const permitQuote = await meeClient.getPermitQuote({
trigger,
instructions: [
// Transfer from EOA to smart account
mcNexus.build({
type: "transferFrom",
data: { ...trigger, recipient, sender }
}),
// Transfer from smart account to recipient
mcNexus.build({
type: "transfer",
data: {
...trigger,
recipient: recipientAccount.address
}
})
],
feeToken
});
// Calculate total amount including fees
const fusionQuote = {
quote: permitQuote.quote,
trigger: {
...trigger,
amount: BigInt(trigger.amount) + BigInt(permitQuote.quote.paymentInfo.tokenWeiAmount)
}
};
// 2. Sign the permit quote
const signedQuote = await meeClient.signPermitQuote({ fusionQuote });
// 3. Execute the signed quote
const { hash } = await meeClient.executeSignedQuote({ signedQuote });
console.log(`Quote execution submitted with hash: ${hash}`);
// 4. Wait for the transaction to be confirmed
const receipt = await meeClient.waitForSupertransactionReceipt({ hash });
console.log("Transaction confirmed!");
console.log(`Explorer links: ${receipt.explorerLinks.join(', ')}`);
// 5. Verify the recipient received the tokens
const recipientBalance = await getBalance(
mcNexus.deploymentOn(mainnet.id, true).publicClient,
recipientAccount.address,
tokenAddress
);
console.log(`Recipient balance: ${recipientBalance}`);
} catch (error) {
console.error("Error during permit flow:", error);
}
Using Intent and Transaction Instructions
import { createMeeClient, toMultichainNexusAccount, mcUSDC } from "@biconomy/abstractjs";
import { http, zeroAddress } from "viem";
import { mainnet, optimism } from "viem/chains";
// Setup multichain account
const mcNexus = await toMultichainNexusAccount({
chains: [mainnet, optimism],
signer: eoaAccount,
transports: [http(), http()]
});
// Create MEE client
const meeClient = await createMeeClient({ account: mcNexus });
// Token address
const tokenAddress = mcUSDC.addressOn(mainnet.id);
// Create trigger
const trigger = {
chainId: mainnet.id,
tokenAddress: tokenAddress,
amount: 1n // Minimal amount for test
};
// Fee token
const feeToken = {
address: tokenAddress,
chainId: mainnet.id
};
// Get permit quote for an intent and a transaction
try {
const fusionQuote = await meeClient.getPermitQuote({
trigger,
instructions: [
// Intent to bridge USDC to the target chain
mcNexus.build({
type: "intent",
data: {
amount: 1n,
mcToken: mcUSDC,
toChain: optimism
}
}),
// A simple transaction on the target chain
mcNexus.build({
type: "default",
data: {
calls: [
{
to: zeroAddress,
gasLimit: 50000n,
value: 0n
}
],
chainId: optimism.id
}
})
],
feeToken
});
// Sign the fusion quote with the permit
const signedQuote = await meeClient.signPermitQuote({ fusionQuote });
console.log("Permit quote signed for intent and transaction");
console.log(`Number of user operations: ${signedQuote.userOps.length}`);
console.log(`Quote ID: ${signedQuote.id}`);
console.log(`Signature: ${signedQuote.signature.slice(0, 66)}...`);
} catch (error) {
console.error("Error signing permit quote:", error);
}
Error Handling
The signPermitQuote
method may throw errors during the signing process:
try {
const signedQuote = await meeClient.signPermitQuote({
fusionQuote: permitQuote
});
// Process the signed quote
} catch (error) {
console.error("Failed to sign permit quote:", error);
if (error.message.includes("does not support ERC20Permit")) {
console.log("The specified token does not support ERC20Permit (EIP-2612). Use signOnChainQuote instead.");
} else if (error.message.includes("user rejected")) {
console.log("The signature request was rejected by the user.");
} else if (error.message.includes("domain separator")) {
console.log("Error with token contract domain separator. Verify the token implements permit correctly.");
} else {
console.log("An unexpected error occurred. Please try again later.");
}
}
Related Methods
getPermitQuote
- Get a permit quote to be signedexecuteSignedQuote
- Execute a signed quotesignOnChainQuote
- Sign a quote for tokens that don't support ERC20Permit
When to Use
Use signPermitQuote
when:
- You're working with tokens that support ERC20Permit (EIP-2612)
- You've obtained a permit quote with
getPermitQuote
and need to sign it - You want to execute transactions without requiring separate approval transactions
- You want to create a more efficient and user-friendly transaction flow with fewer on-chain steps