> ## Documentation Index
> Fetch the complete documentation index at: https://developers.circle.com/llms.txt
> Use this file to discover all available pages before exploring further.

# How-to: Grant USDC allowance to Permit2

> Grant a USDC token allowance to the Permit2 contract using a Circle Wallets developer-controlled wallet or an EIP-1193 Ethereum wallet

For Transactions V2 on EVM blockchains, there is a dependency on the `Permit2`
contract to enable allowance management. To get the benefits of Transactions V2,
you must grant a USDC token allowance to the `Permit2` contract.

This guide shows two examples of how to grant a USDC token allowance to the
`Permit2` contract. The
[`Permit2` documentation](https://docs.uniswap.org/contracts/permit2/overview)
provides additional examples of how to grant this allowance.

## Prerequisites

The examples on this page show how to grant a USDC token allowance to the
`Permit2` contract using a
[Circle Wallets developer-controlled wallet](/wallets/dev-controlled) or a
generic EIP-1193 Ethereum wallet. Before you begin, ensure you have:

* If you are following the Circle Wallets example:
  * A Circle Developer Account
  * A
    [developer-controlled wallet](/wallets/dev-controlled/create-your-first-wallet)

* **Node.js** and **npm** installed on your development machine

* A project set up as described in the below section

### Set up your project

1. Initialize a new Node.js project and install dependencies:

   ```shell theme={null}
   npm init -y
   npm pkg set type=module
   npm install viem dotenv
   ```

2. In the project root, create a `.env` file and add the following variables:

   ```shell theme={null}
   USDC_CONTRACT_ADDRESS=<USDC_CONTRACT_ADDRESS>
   PERMIT2_CONTRACT_ADDRESS=<PERMIT2_CONTRACT_ADDRESS>
   APPROVAL_AMOUNT=<APPROVAL_AMOUNT>
   WALLET_ADDRESS=<WALLET_ADDRESS>
   ```

   The `PERMIT2_CONTRACT_ADDRESS` is the same across all EVM blockchains
   (`0x000000000022D473030F116dDEE9F6B43aC78BA3`), but you should verify it with
   the blockchain explorer on the chain you are using. You can find the
   `USDC_CONTRACT_ADDRESS` on the
   [USDC contract address page](/stablecoins/usdc-contract-addresses).

   <Note>
     The USDC token has 6 decimals. To approve \$100 USDC, set `APPROVAL_AMOUNT` to
     `100000000` (100 \* 10<sup>6</sup>).
   </Note>

   If you are following the Circle Wallets example, you will also need to add
   the following variables:

   ```shell theme={null}
   CIRCLE_WALLET_ID=<CIRCLE_WALLET_ID>
   CIRCLE_WALLETS_API_KEY=<CIRCLE_WALLETS_API_KEY>
   ENTITY_SECRET=<ENTITY_SECRET>
   ```

   If you are following the EIP-1193 Ethereum wallet example, or your Circle
   Wallet is on the generic `EVM` / `EVM-TESTNET` chain, you will also need to
   add the following variable:

   ```shell theme={null}
   RPC_URL=<RPC_URL>
   ```

3. Create an `index.js` file. You'll add code step by step in the following
   sections.

### 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](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
using a Circle Wallets developer-controlled wallet or an EIP-1193 Ethereum
wallet.

<Tabs>
  <Tab title="Circle Wallets">
    <Note>
      **Note:** This example is for a Circle Wallets developer-controlled wallet on
      specific EVM blockchains (for example, `ETH`, `ETH-SEPOLIA`, `MATIC`,
      `MATIC-AMOY`, etc.). If your Circle Wallet is on the generic `EVM` /
      `EVM-TESTNET` chain, which is likely the case if you are migrating from
      Transactions V1 to V2, you can use the example in the "Circle Wallets (generic
      EVM)" tab.
    </Note>

    ```javascript theme={null}
    import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets";
    import { randomUUID } from "crypto";
    import dotenv from "dotenv";

    dotenv.config();

    /**
     * Approves a specified amount of USDC for the Permit2 contract using
     * a developer-controlled wallet managed by Circle.
     *
     * 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 approveUSDCWithCircleWallets
     * @returns {Promise<object>} The transaction response data returned by Circle's API.
     */
    export async function approveUSDCWithCircleWallets() {
      const client = initiateDeveloperControlledWalletsClient({
        apiKey: process.env.CIRCLE_WALLETS_API_KEY,
        entitySecret: process.env.ENTITY_SECRET,
      });

      const response = await client.createContractExecutionTransaction({
        walletId: process.env.CIRCLE_WALLET_ID,
        contractAddress: process.env.USDC_CONTRACT_ADDRESS,
        abiFunctionSignature: "approve(address,uint256)",
        abiParameters: [
          process.env.PERMIT2_CONTRACT_ADDRESS,
          process.env.APPROVAL_AMOUNT,
        ],
        idempotencyKey: randomUUID(),
        fee: { type: "level", config: { feeLevel: "MEDIUM" } },
      });

      return response.data;
    }

    /* -------- Example usage with Circle ---------

    // For auth and wallet creation, see: https://developers.circle.com/interactive-quickstarts/dev-controlled-wallets
    const response = await approveUSDCWithCircleWallets();
    console.log('Response:', response);

    ---------------------------------- */
    ```
  </Tab>

  <Tab title="Circle Wallets (generic EVM)">
    <Note>
      **Note:** This example is for a Circle Wallets developer-controlled wallet on
      generic EVM blockchains (for example, `EVM`, `EVM-TESTNET`). If you are getting
      started with Transactions V2 instead of migrating from Transactions V1, Circle
      recommends that you use create chain-specific Circle Wallets (for example,
      `ETH`, `ETH-SEPOLIA`, `MATIC`, `MATIC-AMOY`) instead of a generic EVM wallet and
      follow the example in the "Circle Wallets" tab instead.
    </Note>

    ```javascript theme={null}
    import { createPublicClient, http, encodeFunctionData, erc20Abi } from "viem";
    import { sepolia } from "viem/chains";
    import dotenv from "dotenv";

    dotenv.config();

    /**
     * Signs a transaction using a developer-controlled wallet managed by Circle.
     *
     * For EVM chains, accepts a transaction object in JSON format.
     * The transaction object will be automatically stringified if needed.
     *
     * @async
     * @function signTransaction
     * @param {object} transaction - Transaction object for EVM chains
     * @returns {Promise<object>} The signature response data returned by Circle's API
     *
     * @example
     * const signature = await signTransaction({
     *   to: '0x...',
     *   value: '0x0',
     *   data: '0x...',
     *   nonce: 1,
     *   gasLimit: '0x5208',
     *   maxFeePerGas: '0x...',
     *   maxPriorityFeePerGas: '0x...',
     *   chainId: 1
     * });
     */
    export async function signTransaction(transaction) {
      const client = initiateDeveloperControlledWalletsClient({
        apiKey: process.env.CIRCLE_WALLETS_API_KEY,
        entitySecret: process.env.ENTITY_SECRET,
      });

      const transactionJson = JSON.stringify(
        transaction,
        (_, value) => (typeof value === "bigint" ? value.toString() : value),
        2,
      );

      const response = await client.signTransaction({
        walletId: process.env.CIRCLE_WALLET_ID,
        transaction: transactionJson,
      });

      return response.data;
    }

    /**
     * Composes a raw unsigned EIP-1559 transaction for approving USDC for the Permit2 contract.
     * This transaction can then be signed using Circle Wallets' raw transaction signing API.
     *
     * @async
     * @function composeUSDCApprovalTransaction
     * @returns {Promise<object>} The unsigned EIP-1559 transaction object.
     */
    export async function composeUSDCApprovalTransaction() {
      const publicClient = createPublicClient({
        chain: sepolia,
        transport: http(process.env.RPC_URL),
      });

      // Step 1: Encode the function call data
      const data = encodeFunctionData({
        abi: erc20Abi,
        functionName: "approve",
        args: [
          process.env.PERMIT2_CONTRACT_ADDRESS,
          BigInt(process.env.APPROVAL_AMOUNT),
        ],
      });

      // Step 2: Estimate gas fees (viem handles the calculation)
      const fees = await publicClient.estimateFeesPerGas();

      // Step 3: Prepare the transaction request (viem auto-fills nonce, gas, etc.)
      const transaction = await publicClient.prepareTransactionRequest({
        account: process.env.SENDER_WALLET_ADDRESS,
        to: process.env.USDC_CONTRACT_ADDRESS,
        value: 0n,
        data,
        ...fees,
      });

      return transaction;
    }

    /**
     * Broadcasts an signed transaction to the network.
     *
     * @async
     * @function broadcastSignedTransaction
     * @param {string} signedTransaction - The signed transaction as a hex string (with 0x prefix)
     * @returns {Promise<string>} The transaction hash
     */
    export async function broadcastSignedTransaction(signedTransaction) {
      const publicClient = createPublicClient({
        chain: sepolia, // use the correct chain for your wallet
        transport: http(process.env.RPC_URL),
      });

      // Send the raw signed transaction
      const txHash = await publicClient.sendRawTransaction({
        serializedTransaction: signedTransaction,
      });

      return txHash;
    }

    /* -------- Example usage with Circle ---------

    // For auth and wallet creation, see: https://developers.circle.com/interactive-quickstarts/dev-controlled-wallets
    const tx = await composeUSDCApprovalTransaction();
    const signature = await signTransaction(tx);
    const txHash = await broadcastSignedTransaction(signature.signedTransaction);

    ---------------------------------- */
    ```
  </Tab>

  <Tab title="EIP-1193 Ethereum Wallet">
    ```javascript theme={null}
    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,
          BigInt(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);

    ---------------------------------- */
    ```
  </Tab>
</Tabs>
