> ## 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.

> ## Agent Instructions
> Before reading these docs, install the relevant Circle Skill to get pre-loaded context — skills are LLM-optimized and cover architecture decisions, correct flows, and common pitfalls. In Claude Code: run /plugin marketplace add circlefin/skills then /plugin install circle-skills@circle. With Vercel Skills CLI: run npx skills add circlefin/skills. Available skills: use-usdc https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-usdc/SKILL.md (balances, transfers, approvals on EVM and Solana); EURC has no skill — see https://developers.circle.com/stablecoins/what-is-eurc.md; bridge-stablecoin https://github.com/circlefin/skills/blob/master/plugins/circle/skills/bridge-stablecoin/SKILL.md (crosschain transfers via CCTP, USDC, EURC, and third-party assets); use-arc https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-arc/SKILL.md (Arc chain config, contract deployment, USDC bridging — Arc docs: https://docs.arc.io/llms.txt); use-circle-wallets https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-circle-wallets/SKILL.md (choose wallet type); use-developer-controlled-wallets https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-developer-controlled-wallets/SKILL.md (custodial wallets for payouts and treasury); use-user-controlled-wallets https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-user-controlled-wallets/SKILL.md (embedded wallets with Web2 login); use-modular-wallets https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-modular-wallets/SKILL.md (smart contract wallets with passkeys and ERC-4337); use-gateway https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-gateway/SKILL.md (unified USDC balance, nanopayments); use-smart-contract-platform https://github.com/circlefin/skills/blob/master/plugins/circle/skills/use-smart-contract-platform/SKILL.md (deploy, interact, monitor contracts). Pair any skill with the Circle MCP server for live SDK signatures, contract addresses, and chain IDs: {"mcpServers":{"circle":{"url":"https://api.circle.com/v1/codegen/mcp"}}}.
> Read the product overview first. Each product has an overview explaining when and why to use it. Read it before quickstarts.
> Pick the right wallet type. Developer-controlled (you hold keys), user-controlled (end user holds keys via PIN/social login), or modular (smart contract wallets with passkeys). See https://developers.circle.com/wallets.md.
> Use CCTP V2 for crosschain transfers. CCTP natively transfers USDC and EURC via burn-and-mint, and supports permissionless wrapping of third-party assets via lock-and-mint. V1 is legacy; always use V2 unless V1 is specified or the chain requires it (Noble, Sui, and Aptos still require V1). Migration guide: https://developers.circle.com/cctp/migration-from-v1-to-v2.md.
> Use Bridge Kit for frontend bridging. Wraps CCTP with a simpler SDK for user-facing apps. Use CCTP directly for backend transfers.
> Gas Station vs Paymaster. Gas Station sponsors gas for Circle Wallet transactions. Paymaster lets users pay gas in USDC. Different use cases — read both overviews.
> Gateway Nanopayments for sub-cent payments. Gasless USDC micropayments down to $0.000001 via x402 and batched settlement. For pay-per-request APIs, AI agent payments, streaming.
> Look up USDC addresses per chain. Never hardcode — use https://developers.circle.com/stablecoins/usdc-contract-addresses.md.
> Prefer SDKs over raw API calls. Node.js and Python SDKs handle auth, retries, and errors.
> API key required. Bearer token in Authorization header. Testnet and mainnet use separate keys and may use different base URLs depending on the product.
> Set up webhooks when available. Most operations are async. Webhooks deliver transaction confirmations and state changes.
> When calling list endpoints, paginate using pageSize and pageAfter until no nextPageAfter cursor is returned—stopping at the first page silently misses records.
> Building an AI agent? Start with the Agent Stack—Circle CLI, agent wallets, and nanopayments built for autonomous use cases: https://developers.circle.com/agent-stack.md.

# CoreDepositWallet contract interface

The `CoreDepositWallet` contract on HyperEVM allows you to deposit USDC from
HyperEVM to HyperCore. This topic describes the contract interface and the
available deposit functions.

<Warning>
  To move USDC from HyperEVM to HyperCore, always call one of the
  `CoreDepositWallet` deposit functions (`deposit`, `depositFor`, or
  `depositWithAuth`). Only USDC is supported.

  Sending USDC or any tokens directly to the `CoreDepositWallet` contract address
  doesn't trigger a deposit on HyperCore. The funds are permanently stuck.
</Warning>

## Deposit functions

The `CoreDepositWallet` provides three entry points for depositing USDC from
HyperEVM into HyperCore. All deposits credit a user's balance on HyperCore, on
either the perps or spot DEX.

### `deposit` function

The `deposit` function transfers USDC from the caller's address and credits the
same address on HyperCore, after the caller has approved the `CoreDepositWallet`
to spend their tokens.

**Signature:**

```solidity theme={null}
deposit(uint256 amount, uint32 destinationDex);
```

**Parameters:**

| Parameter | Value |
| - | - |
| `amount` | The USDC amount being deposited from HyperEVM to HyperCore |
| `destinationDex` | The HyperCore destination `dex` index. Accepted values are:<br />- `0` → default perps DEX<br />- `type(uint32).max` → spot DEX |

**Token pull:** Uses `transferFrom(msg.sender, address(this), amount)` →
requires prior ERC-20 approve from the `msg.sender` to the core deposit wallet.

**Who is credited:** The `msg.sender` of the transaction on HyperEVM is credited
on HyperCore.

**Examples:**

ERC-20 Approval:

```shell theme={null}
# approve the CoreDepositWallet to spend 100 USDC from the sender
cast send <USDC_ADDRESS> "approve(address,uint256)" <CORE_DEPOSIT_WALLET_ADDRESS> 100000000 \
  --private-key $PRIVATE_KEY \
  --rpc-url $RPC_URL
```

Depositing to the perps DEX:

```shell theme={null}
# Deposit 100 USDC to the perps DEX (destinationDex = 0)
cast send <CORE_DEPOSIT_WALLET_ADDRESS> "deposit(uint256,uint32)" 100000000 0 \
  --private-key $PRIVATE_KEY \
  --rpc-url $RPC_URL
```

Depositing to the spot DEX:

```shell theme={null}
# Deposit 100 USDC to the spot DEX (destinationDex = uint32.max)
cast send <CORE_DEPOSIT_WALLET_ADDRESS> "deposit(uint256,uint32)" 100000000 4294967295 \
  --private-key $PRIVATE_KEY \
  --rpc-url $RPC_URL
```

<Note>
  **Note:** If the destination DEX value is not supported (spot or perps), the
  deposit is credited to the sender's spot balance.
</Note>

### `depositFor` function

The `depositFor` function transfers USDC from the caller but credits a specified
recipient address on HyperCore. This allows deposits on behalf of another user.

**Signature:**

```solidity theme={null}
depositFor(address recipient, uint256 amount, uint32 destinationId);
```

**Parameters:**

| Parameter | Value |
| - | - |
| `recipient` | The recipient address on HyperCore |
| `amount` | The USDC amount being deposited from HyperEVM to HyperCore |
| `destinationId` | The HyperCore destination `dex` index. Accepted values are:<br />- `0` → default perps DEX<br />- `type(uint32).max` → spot DEX |

**Token pull:** Uses `transferFrom(msg.sender, address(this), amount)` →
requires prior ERC-20 approve from the `msg.sender` to the core deposit wallet.

**Who is credited:** The recipient address passed to the function is credited on
HyperCore.

**Examples:**

ERC-20 Approval:

```shell theme={null}
# approve the CoreDepositWallet to spend 100 USDC from the sender
cast send <USDC_ADDRESS> "approve(address,uint256)" <CORE_DEPOSIT_WALLET_ADDRESS> 100000000 \
  --private-key $PRIVATE_KEY \
  --rpc-url $RPC_URL
```

Depositing to the perps DEX:

```shell theme={null}
# Deposit 100 USDC to the perps DEX (destinationDex = 0)
cast send <CORE_DEPOSIT_WALLET_ADDRESS> "depositFor(address,uint256,uint32)" <RECIPIENT_ADDRESS> 100000000 0 \
  --private-key $PRIVATE_KEY \
  --rpc-url $RPC_URL
```

Depositing to the spot DEX:

```shell theme={null}
# Deposit 100 USDC to the spot DEX (destinationDex = uint32.max)
cast send <CORE_DEPOSIT_WALLET_ADDRESS> "depositFor(address,uint256,uint32)" <RECIPIENT_ADDRESS> 100000000 4294967295 \
  --private-key $PRIVATE_KEY \
  --rpc-url $RPC_URL
```

<Note>
  **Note:** If the destination DEX value is not supported (spot or perps), the
  deposit is credited to the recipient's spot balance.
</Note>

### `depositWithAuth` function

The `depositWithAuth` function allows depositing USDC using a pre-signed
ERC-3009 authorization. This enables a deposit where the token transfer is
authorized offchain and executed onchain without requiring a prior approve call.

**Signature:**

```solidity theme={null}
depositWithAuth(uint256 amount, uint256 authValidAfter, uint256 authValidBefore, bytes32 authNonce, uint8 v, bytes32 r, bytes32 s, uint32 destinationDex);
```

**Parameters:**

* `amount`: The USDC amount being deposited from HyperEVM to HyperCore
* `authValidAfter`, `authValidBefore`, `authNonce`, `v`, `r`, `s`:
  EIP-3009-style authorization fields for `receiveWithAuthorization`
* `destinationDex`: The HyperCore destination `dex` index. Accepted values are:
  * `0` → default perps DEX
  * `type(uint32).max` → spot DEX

**Token pull:** Calls `token.receiveWithAuthorization(...)`, no prior approve
needed.

**Who is credited:** The `msg.sender` which has to match the `from` address from
the `receiveWithAuthorization` is credited on HyperCore.

**receiveWithAuthorization details:**

* **ERC:** ERC-3009
* **Function Signature:** `ReceiveWithAuthorization`
* **Parameters:**

| Parameter | Value |
| - | - |
| `from` | The payer's address (`authorizer`) has to match the `msg.sender` of the `depositWithAuth` function |
| `to` | The `CoreDepositWallet` address (payee) |
| `value` | The auth amount |
| `validAfter` | The time after which this is valid (Unix time) |
| `validBefore` | The time before which this is valid (Unix time) |
| `nonce` | Unique nonce |
| `v` | v of the signature |
| `r` | r of the signature |
| `s` | s of the signature |

**Example:**

The example below illustrates how to generate an ERC-3009 authorization:

```javascript theme={null}
#!/usr/bin/env node

const ethers = require("ethers");
const PRIVATE_KEY = process.env.PRIVATE_KEY;
const wallet = new ethers.Wallet(PRIVATE_KEY);
const provider = new ethers.JsonRpcProvider(
  process.env.RPC_URL || "https://rpc.hyperliquid-testnet.xyz/evm",
);

const usdcAddress = "<USDC_CONTRACT_ADDRESS>";
const coreDepositWallet = "<CORE_DEPOSIT_WALLET_ADDRESS>";

const EIP712_PREFIX = "0x1901";

const amount = ethers.parseUnits("100", 6); // 100 USDC
const nonce = ethers.hexlify(ethers.randomBytes(32));
const validAfter = 0;
const validBefore = Math.floor(Date.now() / 1000) + 3600; // valid for 1 hour

// Minimal ABI for DOMAIN_SEPARATOR
const USDC_ABI = [
  {
    inputs: [],
    name: "DOMAIN_SEPARATOR",
    outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }],
    stateMutability: "view",
    type: "function",
  },
];

async function getDomainSeparator(usdc) {
  try {
    return await usdc.DOMAIN_SEPARATOR();
  } catch {
    const domain = {
      name: "USD Coin",
      version: "2",
      chainId: await provider.getNetwork().then((n) => n.chainId),
      verifyingContract: await usdc.getAddress(),
    };
    return ethers.TypedDataEncoder.hashDomain(domain);
  }
}

async function main() {
  const usdc = new ethers.Contract(usdcAddress, USDC_ABI, provider);
  const domainSeparator = await getDomainSeparator(usdc);

  const structHash = ethers.keccak256(
    ethers.AbiCoder.defaultAbiCoder().encode(
      [
        "bytes32",
        "address",
        "address",
        "uint256",
        "uint256",
        "uint256",
        "bytes32",
      ],
      [
        ethers.keccak256(
          ethers.toUtf8Bytes(
            "ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)",
          ),
        ),
        wallet.address,
        coreDepositWallet,
        amount,
        validAfter,
        validBefore,
        nonce,
      ],
    ),
  );

  const digest = ethers.keccak256(
    ethers.concat([EIP712_PREFIX, domainSeparator, structHash]),
  );
  const signer = new ethers.SigningKey(PRIVATE_KEY);
  const sig = signer.sign(digest);

  console.log("Authorization parameters:");
  console.log({
    amount: amount.toString(),
    validAfter,
    validBefore,
    nonce,
    v: sig.v,
    r: sig.r,
    s: sig.s,
  });
}

main().catch(console.error);
```

The example below illustrates how to call the `depositWithAuth` function with
the authorization data:

```shell theme={null}
cast send <CORE_DEPOSIT_WALLET_ADDRESS> \
  "depositWithAuth(uint256,uint256,uint256,bytes32,uint8,bytes32,bytes32,uint32)" \
  <AMOUNT> 0 1735660000 0x<NONCE> <V> 0x<R> 0x<S> <DEST_DEX_ID> \
  --private-key $PRIVATE_KEY \
  --rpc-url $RPC_URL
```

<Note>
  **Note:** If the destination DEX value is not supported (spot or `perp`), the
  deposit is credited to the `authorizer's` spot balance.
</Note>
