Skip to main content
Fast Deposits for Circle Gateway lets users fund a Circle Gateway balance in seconds from any Eco-supported chain, regardless of the source chain’s finality. Eco fills each deposit and settles the source-chain side in the background, so you ship no custom contracts and never take custody of user funds. The user can deposit as a regular transfer to an address or gaslessly.
For the full REST endpoint reference, see Gateway API under the API Reference tab.
Three properties define the system:
  • Non-custodial: Eco never holds user funds. USDC moves from the user’s wallet into a dedicated source-chain vault that can only fund the quoted deposit intent. If the intent is never fulfilled, funds return to the refundRecipient (defaults to the depositor).
  • Permissionless: Any wallet or application can request a deposit vault via the API. No whitelisting, no KYC gate.
  • Per-intent vaults: Each vault is created for every intent and carries a quote deadline. The vault address is derived deterministically from the quoted intent.

Supported chains

Source chainsDestination
Base, Optimism, ArbitrumGateway on Polygon
Any Gateway-enabled chain can be added. Reach out if you need a chain that isn’t listed.

Environment

Base URL: https://api.eco.com

How it works

Each POST creates a quoted Routes intent and returns its vault address. When the vault’s USDC balance reaches the quoted amount, Eco publishes the intent and a solver fulfills it on Polygon by calling Gateway’s depositFor for the recipient. The solver is repaid from the vault once fulfillment is proven.

Quickstart

1

Create the deposit vault

POST /circle-gateway/v2/depositAddresses with the source chain, amount, recipient, and depositor.
2

Fund it, gasless or direct

Collect an ERC-3009 signature and call POST /circle-gateway/v1/gasless/transferWithAuthorization, or just do a vanilla ERC-20 transfer. Send at least the quoted amount before the deadline.
3

Poll for completion

Hit GET /circle-gateway/v2/depositAddresses/{vaultAddress} until state is PUBLISHED. For gasless transfers you can also poll GET /circle-gateway/v1/gasless/jobs/{id}.

Step 1: Create a quoted deposit vault

curl -X POST https://api.eco.com/circle-gateway/v2/depositAddresses \
  -H "Content-Type: application/json" \
  -d '{
    "sourceChainId": 8453,
    "amount": "1000000",
    "recipient": "0xRecipientOnGateway",
    "depositor": "0xYourWalletAddress"
  }'
FieldTypeRequiredDescription
sourceChainIdnumberYesSource chain (one of the supported chains above)
amountstringYesSource-chain USDC amount in base units (6 decimals)
recipientstringYesRecipient credited on Gateway (Polygon)
depositorstringYesAddress that will fund the vault
refundRecipientstringNoAddress that receives expiry refunds. Defaults to depositor
Response (201 Created):
{
  "data": {
    "vaultAddress": "0x...",
    "amount": "1000000",
    "deadline": 1798915200
  }
}
deadline is the quote expiry in Unix seconds. Fund the vault before it passes. Repeating the same request while the quote is pending and unexpired returns the same vaultAddress; a different amount (or an expired quote) produces a new vault.

Step 2: Fund the vault

Three ways, pick one:
  • ERC-3009 transferWithAuthorization (recommended for gasless UX). Single transaction, no user gas. The authorization value must cover the quoted amount.
  • EIP-2612 Permit. Two transactions (permit() and the pull into the vault), no user gas.
  • Direct ERC-20 transfer. Sender pays gas.
Have the user sign an EIP-712 TransferWithAuthorization over the USDC contract with to set to the vault address, then post it:
curl -X POST https://api.eco.com/circle-gateway/v1/gasless/transferWithAuthorization \
  -H "Content-Type: application/json" \
  -d '{
    "chainId": 8453,
    "from": "0xSignerAddress",
    "to": "0xVaultAddress",
    "value": "1000000",
    "validAfter": "0",
    "validBefore": "1776098178",
    "nonce": "0x<random-32-bytes>",
    "signature": "0x<65-byte-eip712-signature>"
  }'
Response (202 Accepted):
{ "data": { "id": "<job-id>", "status": "PENDING" } }
See Funding methods for signing code and field-by-field details.

Step 3: Poll for completion

Poll the vault status (note the required sourceChainId query parameter):
curl "https://api.eco.com/circle-gateway/v2/depositAddresses/0xVaultAddress?sourceChainId=8453"
Response (200 OK):
{
  "data": {
    "vaultAddress": "0x...",
    "amount": "1000000",
    "deadline": 1798915200,
    "state": "PUBLISHED",
    "intentHash": "0x...",
    "sourceChainId": 8453
  }
}
StateMeaning
PENDINGVault created, waiting for funds
FUNDING_DETECTEDVault balance reached the quoted amount
PUBLISHEDDeposit intent published; solver fulfillment follows in seconds
EXPIRED_UNFUNDEDQuote deadline passed before the vault was funded
FAILEDPublishing failed; funds follow the refund path
REFUNDED_BY_USERDepositor reclaimed the funds
RECOVERY_PUBLISHEDRecovery service published a recovery intent for the funds
For gasless transfers you can additionally poll GET /circle-gateway/v1/gasless/jobs/{id}, which transitions PENDING → (PERMIT_SENT for Permit only) → COMPLETED or FAILED and includes transferTxHash when complete. The intentHash comes from the vault status endpoint above. To confirm the deposit reached Gateway, query Circle Gateway’s balance API for the recipient address.

FAQ

Poll GET /circle-gateway/v2/depositAddresses/{vaultAddress}?sourceChainId={id} until state is PUBLISHED, then confirm the credited balance by querying Circle Gateway’s balance API for the recipient address.
Nothing. FAILED means the service couldn’t initiate the transfer (e.g. signature invalid, balance dropped, deadline expired). USDC never left the user’s wallet. The signature just expires. Submit a new one to retry.
The deposit isn’t triggered until the vault balance reaches the quoted amount, so the vault stays PENDING. Top it up before the deadline. If the deadline passes first, the quote moves to EXPIRED_UNFUNDED and any funds in the vault are recoverable to the refundRecipient.
The vault status tells you which path the funds took: FAILED means publishing didn’t complete, REFUNDED_BY_USER means the depositor reclaimed the funds, and RECOVERY_PUBLISHED means an independent recovery service stepped in. In every non-PUBLISHED outcome, funds return to the refundRecipient. Refund and recovery are permissionless, so you (or anyone) can also drive them yourself.
On Base Sepolia → Polygon Amoy we typically see 20–40 seconds from submission to Gateway balance update. Time scales with source-chain finality.
ERC-3009 transferWithAuthorization, single transaction, cleaner UX, USDC-native. Use Permit if the token you’re working with supports permit() but not ERC-3009.
Any Gateway-enabled chain. Today: Base, Optimism, Arbitrum. Ask if you need a chain added.
While a quote is pending and unexpired, repeating the same request returns the same vaultAddress. A different amount, a different recipient, or an expired quote produces a new vault. Always send users the address from the latest response.

Security

  • Each quote maps to a dedicated vault derived deterministically from the intent it funds. No shared contract state.
  • USDC in a vault can only fund the quoted deposit intent or be refunded to the refundRecipient. It cannot be redirected.
  • Refunds and recovery are driven by an independent, permissionless service. Funds cannot be stuck.
  • Full audit reports (Cantina) are available on request.
  • Eco never holds or touches user funds.