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

# Fund an Account

> Move tokens from the user's wallet into their product account, with the user or your gas sponsor paying for the transaction.

A new product account is empty. Before the user can deposit into a vault, post collateral or buy an asset, move tokens from their wallet into the account with the product's `transfer` endpoint and `action: "DEPOSIT"`.

This works the same way in Earn, Credit and Tokenized Assets. Perpetual Trading doesn't use a product account: you fund it with a [USDC deposit to Hyperliquid](/v2/api-reference/perpetual-trading/deposit-usdc-to-perpetual-trading-account) instead.

| Product | Endpoint |
| - | - |
| Earn | [`POST /v2/earn/transfer`](/v2/api-reference/earn/transfer-tokens-tofrom-account) |
| Credit | [`POST /v2/credit/transfer`](/v2/api-reference/credit/transfer-tokens-tofrom-credit-account) |
| Tokenized Assets | [`POST /v2/tokenized_assets/transfer`](/v2/api-reference/tokenized-assets/transfer-tokens-tofrom-account) |

## Request

Move 100 USDC from the user's wallet into their Earn Account on Base:

<CodeGroup>
  ```python Python theme={"system"}
  import httpx

  response = httpx.post(
      "https://api.compasslabs.ai/v2/earn/transfer",
      json={
          "owner": "0xYourWalletAddress",
          "chain": "base",
          "token": "USDC",
          "amount": "100",
          "action": "DEPOSIT",
      },
      headers={"x-api-key": "YOUR_API_KEY"},
  )
  result = response.json()

  # result["transaction"]: unsigned token transfer from the wallet to the account
  # The owner signs and sends it
  ```

  ```typescript TypeScript theme={"system"}
  const response = await fetch(
    "https://api.compasslabs.ai/v2/earn/transfer",
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-api-key": "YOUR_API_KEY",
      },
      body: JSON.stringify({
        owner: "0xYourWalletAddress",
        chain: "base",
        token: "USDC",
        amount: "100",
        action: "DEPOSIT",
      }),
    }
  );
  const result = await response.json();

  // result.transaction: unsigned token transfer from the wallet to the account
  // The owner signs and sends it
  ```
</CodeGroup>

| Field | Description |
| - | - |
| `owner` | The user's wallet. Tokens move from this wallet into the account it owns. |
| `chain` | The chain the account is on. Tokenized Assets defaults to `ethereum`. |
| `token` | A token symbol such as `USDC`, or a token address. |
| `amount` | The amount in token units, not wei: `"100"` means 100 USDC. Send it as a string to keep full precision. |
| `action` | `DEPOSIT` moves tokens into the account. `WITHDRAW` moves them out (see [Withdraw Funds](/v2/account-lifecycle/withdraw-funds)). |
| `gas_sponsorship` | `false` (the default): the user signs and pays gas. `true`: your sponsor wallet pays, see [Sponsored deposits](#sponsored-deposits). |

## Response

With `gas_sponsorship: false`, `transaction` is a standard token transfer from the user's wallet to their account, and `eip_712` is `null`. The user signs and sends the transaction.

```json theme={"system"}
{
  "transaction": {
    "chainId": "0x2105",
    "from": "0xYourWalletAddress",
    "to": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "data": "0xa9059cbb...",
    "value": "0x0",
    "gas": "0xc350",
    "nonce": "0x8",
    "maxFeePerGas": "0x1c9c380",
    "maxPriorityFeePerGas": "0x186a0"
  },
  "eip_712": null
}
```

Here `to` is the USDC contract on Base, and `data` encodes a transfer of 100 USDC to the account. The API checks the wallet first: if it holds less than `amount`, the call returns `400 Insufficient balance`.

<Note>
  USDC on BNB Smart Chain has 18 decimals instead of 6. Amounts are always in token units, so `"100"` still means 100 USDC there.
</Note>

## Sponsored deposits

With gas sponsorship, the user only signs messages. Your sponsor wallet sends the transactions and pays the gas. Each token needs a one-time approval first, so that later transfers can be authorized by signature alone.

<Steps>
  <Step title="Once per token: approve the token">
    Call [`POST /v2/gas_sponsorship/approve_transfer`](/v2/api-reference/gas-sponsorship/approve-token-transfer) with `gas_sponsorship: true`. The user signs the returned `eip_712`, and you relay it as in step 3.

    ```json theme={"system"}
    {
      "owner": "0xYourWalletAddress",
      "chain": "base",
      "token": "USDC",
      "gas_sponsorship": true
    }
    ```

    This works for tokens that support EIP-2612 permits, like USDC. For tokens that don't, such as USDT and WETH, send `gas_sponsorship: false` instead: the user sends the approval transaction and pays its gas. If the allowance is already in place, the call returns `400 Token allowance already set` and you can skip this step.
  </Step>

  <Step title="Request the deposit">
    Call `transfer` with `gas_sponsorship: true` and `spender` set to your sponsor wallet. Instead of a transaction, the response has `eip_712`: a transfer authorization for the user to sign, valid for 30 minutes.

    ```json theme={"system"}
    {
      "owner": "0xYourWalletAddress",
      "chain": "base",
      "token": "USDC",
      "amount": "100",
      "action": "DEPOSIT",
      "gas_sponsorship": true,
      "spender": "0xYourSponsorWallet"
    }
    ```
  </Step>

  <Step title="Sign and relay">
    The user signs `eip_712` as EIP-712 typed data (for example with `eth_signTypedData_v4`). Send the signature and the unchanged `eip_712` to [`POST /v2/gas_sponsorship/prepare`](/v2/api-reference/gas-sponsorship/prepare-gas-sponsored-transaction). It returns a transaction for your sponsor wallet to sign and send.

    ```json theme={"system"}
    {
      "owner": "0xYourWalletAddress",
      "chain": "base",
      "product": "earn",
      "eip_712": { "...": "the eip_712 object from step 2" },
      "signature": "0xUserSignature",
      "sender": "0xYourSponsorWallet"
    }
    ```
  </Step>
</Steps>

<Warning>
  Two fields have to line up:

  * **`sender` in `prepare` must be the `spender` from `transfer`.** The signature only works for the wallet it names.
  * **`product` in `prepare` picks the account that receives the tokens:** `earn`, `credit` or `tokenized_assets`. It defaults to `earn`, so a Credit or Tokenized Assets deposit sent without it lands in the user's Earn Account.
</Warning>

If you leave out `spender`, it defaults to the product account itself. That's only for [bundles](/v2/Products/Bundling), where the account pulls the tokens as one step of a larger transaction. Sponsored flows need the owner to be a regular wallet (EOA), since the API checks the signature against the owner's address. See [Gas Sponsorship](/v2/Products/gas-sponsorship) for more.

## Next

<Card title="Balances and Positions" icon="arrow-right" href="/v2/account-lifecycle/balances-and-positions">
  Read what the account holds and how it's performing.
</Card>
