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

# Funding methods

> Fund a quoted Circle Gateway deposit address with ERC-3009, EIP-2612, or a direct USDC transfer.

After you [create a Circle Gateway deposit address](/programmable-addresses/circle-gateway-deposits#step-1-create-a-quoted-deposit-address), use one of these methods to fund it.

| Method | What the user signs | Source-chain gas |
| - | - | - |
| ERC-3009 | `TransferWithAuthorization` typed data | Submitted by Eco's service |
| EIP-2612 | `Permit` typed data | Submitted by Eco's service |
| Direct transfer | An ERC-20 transfer transaction | Paid by the sender |

Gasless funding does not imply a fee-free deposit. The returned quote determines the deposit amount and deadline.

## Prerequisites

* The returned `vaultAddress`, `amount`, and quote `deadline`
* The source chain's USDC contract and the user's wallet
* Sufficient USDC for the quoted amount
* For the TypeScript example, `viem` and a wallet client connected to Base

Circle Gateway submit endpoints do not require an API key. No requests-per-second quota is published; handle `429` with backoff and honor `Retry-After` when present. The linked endpoint pages contain complete request, success, and error examples.

## ERC-3009 transferWithAuthorization

Sign `TransferWithAuthorization` with the returned address as `to`. Use a fresh random `bytes32` nonce. Verify the token's EIP-712 domain for the selected chain; a domain from another USDC deployment can produce an invalid signature.

This example is scoped to **Base mainnet USDC**. It reads the token's name onchain and uses USDC's domain version `2`. Pass the actual create-response fields to the exported function. It signs and submits an authorization when called; use a wallet client for the user who is funding the deposit.

```typescript theme={null}
import {
  createPublicClient, erc20Abi, http, toHex,
  type Address, type WalletClient,
} from 'viem';
import { base } from 'viem/chains';

const USDC = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
const publicClient = createPublicClient({ chain: base, transport: http() });

export async function fundWithErc3009(
  walletClient: WalletClient,
  owner: Address,
  deposit: { vaultAddress: Address; amount: string; deadline: number },
): Promise<unknown> {
  try {
    if (await walletClient.getChainId() !== base.id) {
      throw new Error('Connect the wallet to Base mainnet');
    }
    const now = Math.floor(Date.now() / 1000);
    const validBefore = Math.min(deposit.deadline, now + 3600);
    if (validBefore <= now + 60) {
      throw new Error('Request a fresh deposit quote before signing');
    }
    const name = await publicClient.readContract({
      address: USDC, abi: erc20Abi, functionName: 'name',
    });
    const nonce = toHex(crypto.getRandomValues(new Uint8Array(32)));
    const authorization = {
      from: owner,
      to: deposit.vaultAddress,
      value: deposit.amount,
      validAfter: '0',
      validBefore: String(validBefore),
      nonce,
    };
    const signature = await walletClient.signTypedData({
      account: owner,
      domain: { name, version: '2', chainId: base.id, verifyingContract: USDC },
      types: {
        TransferWithAuthorization: [
          { name: 'from', type: 'address' },
          { name: 'to', type: 'address' },
          { name: 'value', type: 'uint256' },
          { name: 'validAfter', type: 'uint256' },
          { name: 'validBefore', type: 'uint256' },
          { name: 'nonce', type: 'bytes32' },
        ],
      },
      primaryType: 'TransferWithAuthorization',
      message: {
        ...authorization,
        value: BigInt(authorization.value),
        validAfter: 0n,
        validBefore: BigInt(validBefore),
      },
    });
    if (Math.floor(Date.now() / 1000) >= validBefore - 60) {
      throw new Error('Authorization is too close to expiry; do not submit it');
    }
    const payload = {
      chainId: base.id,
      target: { depositAddress: deposit.vaultAddress },
      authorization,
      signature,
    };
    // Persist this exact payload securely before sending, so an uncertain
    // response can be retried without collecting a second authorization.
    const response = await fetch(
      'https://api.eco.com/v1/circle-gateway/deposit-addresses/submit/erc-3009',
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload),
        signal: AbortSignal.timeout(30_000),
      },
    );
    const text = await response.text();
    if (!response.ok) throw new Error(`HTTP ${response.status}: ${text}`);
    return JSON.parse(text);
  } catch (error) {
    throw new Error('ERC-3009 deposit submission failed; inspect any existing job before signing again', {
      cause: error,
    });
  }
}
```

The first accepted submission returns `202` with a gasless job; retrying the same signed payload returns the existing job with `200`. Save the job ID. See the [ERC-3009 endpoint](/api-reference/v1/gateway-submit-erc-3009) for validation errors and the response schema.

The comment about persistence marks an application responsibility: wire in durable storage before using this function in a production payment flow. If the HTTP result is uncertain, recover the saved payload and retry it rather than calling the signing function again.

## EIP-2612 Permit

Use the [EIP-2612 endpoint](/api-reference/v1/gateway-submit-erc-2612) with a token deployment that supports the required permit flow.

| Signed field | Value |
| - | - |
| `owner` | The funding wallet |
| `spender` | The returned `vaultAddress` |
| `value` | The quoted amount in base units |
| `nonce` | The token contract's current `nonces(owner)` value |
| `deadline` | A future Unix timestamp compatible with the quote deadline |

Sign the token's EIP-712 `Permit` type using its chain-specific domain. Submit `{ chainId, target: { depositAddress }, permit, signature }`. Serialize `value` and `nonce` as decimal strings and `deadline` as a number, as required by the request schema.

A permit can be mined before the subsequent token movement completes. Inspect the job and transaction hashes before creating another permit after a failure.

## Direct transfer

Send the quoted amount of source-chain USDC to `vaultAddress` using the token's ERC-20 `transfer` function. The sender signs that transaction and pays gas. Estimate the transfer's gas before submitting and check its receipt.

The service monitors the address and processes it when its USDC balance reaches the quote amount. A successful source transfer does not confirm the destination deposit.

## Polling

Follow [deposit and job tracking](/programmable-addresses/circle-gateway-deposits#step-3-poll-for-completion). Preserve the source chain, address, signed payload, job ID, and transaction hashes with the deposit record.

* For validation errors, check the target binding, amount, token domain, signer, nonce, and deadlines.
* If a job is `failed` or `partial`, inspect its per-chain results before retrying.
* If the deposit is `PUBLISHED`, confirm the delivery intent or recipient's Gateway balance before marking the payment complete.

## Next steps

* [Circle Gateway deposit lifecycle](/programmable-addresses/circle-gateway-deposits)
* [Gasless deposit recipe](/cookbook/gasless-gateway-deposit)
