Circle Docs

How-to: Grant USDC Allowance to Permit2

When using the StableFX API to deliver the funds onchain for an FX trade, you must grant an allowance to the Permit2 contract. This allows the Permit2 contract to transfer the USDC from your wallet to the FxEscrow contract. This guide shows an example of how to grant a USDC token allowance to the Permit2 contract. The Permit2 documentation provides additional examples of how to grant this allowance.

Before you begin, ensure you have:

  • Node.js and npm installed on your development machine

    • A project initialized with npm init -y
    • viem and dotenv installed in your project
    • A .env file in your project root with the following variables defined:
      • USDC_CONTRACT_ADDRESS
      • PERMIT2_CONTRACT_ADDRESS
      • APPROVAL_AMOUNT
      • WALLET_ADDRESS
      • RPC_URL

The following steps show how to grant a USDC token allowance to the Permit2 contract using an EIP-1193 Ethereum wallet.

Step 1. Grant a USDC token allowance to the Permit2 contract

The following example code shows the process for granting a USDC token allowance to the Permit2 contract using an EIP-1193 Ethereum wallet.

JavaScript
import {
  createWalletClient,
  createPublicClient,
  http,
  custom,
  erc20Abi,
} from "viem";
import { sepolia } from "viem/chains";
import dotenv from "dotenv";

dotenv.config();

/**
 * Approves a specified amount of USDC for the Permit2 contract using
 * a custom wallet with an EIP-1193 provider (like MetaMask).
 *
 * This function sends an on-chain transaction to call the ERC-20 `approve`
 * method on the USDC contract, allowing the Permit2 contract to spend
 * the specified amount on behalf of the wallet.
 *
 * @async
 * @function approveUSDCWithEIP1193Wallet
 * @param {any} [provider] - EIP-1193 provider.
 * @returns {Promise<object>} The transaction hash and receipt.
 */
export async function approveUSDCWithEIP1193Wallet(provider) {
  const publicClient = createPublicClient({
    chain: sepolia,
    transport: http(process.env.RPC_URL),
  });

  const walletClient = createWalletClient({
    account: process.env.WALLET_ADDRESS,
    chain: sepolia,
    transport: custom(provider),
  });

  const hash = await walletClient.writeContract({
    address: process.env.USDC_CONTRACT_ADDRESS,
    abi: erc20Abi,
    functionName: "approve",
    args: [
      process.env.PERMIT2_CONTRACT_ADDRESS,
      BitInt(process.env.APPROVAL_AMOUNT),
    ],
  });

  const receipt = await publicClient.waitForTransactionReceipt({ hash });
  return { hash, receipt };
}

/* -------- Example usage with EIP-1193 wallet ---------

// Refer to https://viem.sh/docs/clients/transports/custom and your wallet provider's documentation for the provider object. 
const {hash, receipt} = await approveUSDCWithEIP1193Wallet({provider: window.ethereum});
console.log('Hash:', hash);
console.log('Receipt:', receipt);

---------------------------------- */
Did this page help you?
© 2023-2025 Circle Technology Services, LLC. All rights reserved.