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

# Deposit tokens on-chain

> Deposit an eligible token straight to the Arcus BridgeVault as perp collateral: address checks, the deposit steps, confirming the credit, and reclaiming a failed deposit

This guide deposits an eligible token, such as WETH (wrap native ETH first), on-chain as perp collateral. The token isn't converted to USDG. For how token collateral and the automatic USDG loan work, see [Multi-Asset Collateral](https://docs.arcus.xyz/concepts/perpetuals/multi-asset-collateral.md). For runnable Python covering the deposit and everything after it, see [Use Multi-Asset Collateral](https://docs.arcus.xyz/guides/multi-asset-collateral.md).

> [!WARNING]
>
> **This moves real funds on mainnet.** Send a small test deposit first and confirm it is credited (see [Step 5](#step-5-confirm-the-credit)) before you send more.

> [!NOTE]
>
> **What this guide covers.** Depositing **WETH** (wrap native ETH first) or a **Stock Token** to the `BridgeVault` on mainnet or testnet. The steps are the same for both; only native ETH needs wrapping first. Check that the vault accepts a token (step 3) before depositing it. There is no on-chain USDG deposit path documented for mainnet: deposit USDG through the [app](https://docs.arcus.xyz/concepts/onboarding.md#depositing-through-fun). On testnet, to deposit USDG on-chain, see [Fund a testnet account](https://docs.arcus.xyz/guides/fund-testnet-account.md).

## Deposit contract addresses can change

> [!WARNING]
>
> **Arcus deposit contract addresses can change.** Read them from the docs and the API before **every** deposit. Never hard-code an address, and never reuse one you saved earlier or copied from somewhere else. A deposit sent to an old or wrong address may not be credited to your account.
>
> * **Deposit contract (`BridgeVault`):** the `verifyingContract` for your environment in the per-environment `chainId` / `verifyingContract` table on [`POST /v1/withdraw`](https://docs.arcus.xyz/api-reference/exchange/submit-withdrawal).
> * **Token contract:** `contractId` for the asset on [`GET /v1/spotAssets`](https://docs.arcus.xyz/api-reference/public/get-spot-collateral-assets). This is the token you send, not the deposit contract.
> * **Chain ID:** the `chainId` in the same `POST /v1/withdraw` table, also listed under [Environments](https://docs.arcus.xyz/concepts/spot-integration.md#environments). Check that your wallet or RPC is on that chain.

## Before you start

* **The token on Robinhood Chain**, plus a little native ETH for gas. Buy tokens on [Arcus spot](https://docs.arcus.xyz/concepts/spot-overview.md).
* **A subaccount to deposit into** (`accountIndex`). You don't need an API key or an order first.

## Step 1: Read the current addresses

Read the `BridgeVault` address, chain ID and token `contractId` as described in [Deposit contract addresses can change](#deposit-contract-addresses-can-change). Do this for every deposit.

## Step 2: Hold the exact token

Use the token at `contractId` on `GET /v1/spotAssets`. Any other token for the same asset isn't credited. For ETH that is **WETH** on Robinhood Chain. Native ETH isn't accepted, so wrap it first by calling `deposit()` on the WETH contract.

## Step 3: Check the vault accepts the token

Call the `BridgeVault`'s `depositConfig(token)` view. It returns `(allowed, minAmount)`:

* `allowed` must be `true`. An asset can appear on `GET /v1/spotAssets` before the vault accepts it.
* Your `amount` must be at least `minAmount`, in the token's smallest unit. Minimums are set per token and can change, so read it before each deposit.

## Step 4: Approve and deposit

1. **Approve the `BridgeVault`** to pull the amount from your wallet (`approve(BridgeVault, amount)` on the token). Token deposits go to the `BridgeVault` directly, not through a USDG deposit proxy.
2. **Call `initiateDeposit(owner, accountIndex, token, amount)`** on the `BridgeVault`. `owner` must be the wallet that sends the transaction, and `amount` is in the token's smallest unit (read the token's `decimals()`). Keep the returned `inboxIndex`; it is also emitted in the `DepositInitiated` event, and you need it to reclaim a deposit that is never credited.

Size the amount in whole multiples of the asset's `stepSize` so you can withdraw the full credited size later. [Step 2 of Use Multi-Asset Collateral](https://docs.arcus.xyz/guides/multi-asset-collateral.md#step-2-wrap-approve-and-deposit) has the code.

## Step 5: Confirm the credit

Arcus credits the token after the transaction is picked up from the chain.

* Poll [`GET /v1/accountTransferUpdates`](https://docs.arcus.xyz/api-reference/public/get-account-transfer-updates) (or the [`accountTransferUpdates`](https://docs.arcus.xyz/api-reference/channels#accounttransferupdates) channel) for the `DEPOSIT` row. It carries `spotAssetId` and the size in token units as `amount`, and ends `APPLIED` once the token is credited, or with a `REJECTED_*` status if Arcus refused it.
* Read [`GET /v1/account`](https://docs.arcus.xyz/api-reference/public/get-account). The token shows in `spotPositions`, your USDG balance is unchanged, and `borrowHeadroom` rises by about the token's value × `ltvInitial`. There is no separate "enable as collateral" step.
* A credited deposit to a subaccount you have never used activates it: `GET /v1/account` for that `accountIndex` goes from `404` to `200`. A rejected deposit doesn't.

[Step 3 of Use Multi-Asset Collateral](https://docs.arcus.xyz/guides/multi-asset-collateral.md#step-3-confirm-the-credit) has the polling code and an example response.

## If a deposit fails

A token deposit has two steps. You send it on-chain, and then Arcus credits it to your account. Either step can fail:

| What happens | Why | What you see |
| - | - | - |
| The deposit transaction reverts | The token isn't accepted for deposit yet (`DepositNotAllowed`), the amount is under the minimum (`DepositBelowMinimum`), or `owner` isn't the sending wallet (`NotOwner`) | Nothing leaves your wallet. |
| The deposit is rejected | It would breach `userSupplyCap` or `globalSupplyCap` (`REJECTED_SUPPLY_CAP_EXCEEDED`), or the exchange-wide deposit cap (`REJECTED_GLOBAL_DEPOSIT_CAP_EXCEEDED`); or the asset is disabled (`REJECTED_COLLATERAL_DISABLED`) | A `REJECTED_*` status on [`GET /v1/accountTransferUpdates`](https://docs.arcus.xyz/api-reference/public/get-account-transfer-updates) and the [`accountTransferUpdates`](https://docs.arcus.xyz/api-reference/channels#accounttransferupdates) channel. You can reclaim it right away. |
| The deposit is never credited | The token isn't one Arcus accepts as collateral (not the `contractId` from `GET /v1/spotAssets`), or the deposit expired before it was credited | No credit and no transfer row. You can reclaim it once it expires. Read the expiry period with the vault's `depositExpiryPeriod()` view. |
| Native ETH sent, nothing credited as collateral | Native ETH isn't a collateral token | Wrap it to WETH and deposit the WETH. ETH sent through the app's main Deposit flow is converted to USDG instead. |

### Reclaiming a deposit

A token that isn't credited stays held by the `BridgeVault`, not lost. To reclaim it, first read `BridgeVault.pendingDepositByIndex(index)`. It returns the pending deposit's record:

* `amount`: non-zero while the deposit is still reclaimable; `0` once it has been credited or refunded.
* `expiresAt`: `cancelDeposit` succeeds only after this time. A rejected deposit is expired early, so its `expiresAt` has already passed.
* `cancelRecipient`: the address that receives the refund, and the only address that can call `cancelDeposit` for a deposit made with `initiateDeposit`. For a token you deposited with `initiateDeposit` on the `BridgeVault`, it is your wallet.

Then call `cancelDeposit(index)` on the `BridgeVault` from that wallet. The tokens go back to the wallet, and `pendingDepositByIndex(index)` returns an `amount` of `0`. Calling `cancelDeposit` again reverts with `PendingDepositNotFound`. A deposit routed through a deposit proxy is cancelled through that proxy instead.

Which index to use:

* **Rejected deposit** (a `REJECTED_*` row): `rootchainQueueIndex` from the rejected row. It is the same number as the deposit's `inboxIndex`. You can reclaim it right away.
* **Never-credited deposit** (no row): the `inboxIndex` from [step 4](#step-4-approve-and-deposit). Reclaim it once `expiresAt` has passed.

## Next steps

* [Use Multi-Asset Collateral](https://docs.arcus.xyz/guides/multi-asset-collateral.md): trade on the loan, repay it and withdraw the token, with code.
* [Multi-Asset Collateral](https://docs.arcus.xyz/concepts/perpetuals/multi-asset-collateral.md): eligible assets, LTVs, interest and liquidation.
* [Onboarding & deposits](https://docs.arcus.xyz/concepts/onboarding.md): depositing USDG through the app.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.