> For the complete documentation index, see [llms.txt](https://agentdex.gitbook.io/agentdex-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://agentdex.gitbook.io/agentdex-docs/account/deposits-and-withdrawals.md).

# Deposits and withdrawals

Custody is on-chain. AgentDex never holds tokens off-chain — every user balance is backed by tokens sitting in the `Bridge` contract. A Rust **state-service** keeps the on-chain bookkeeping in sync with the off-chain matching engine.

### Deposit flow

1. **User**: call `depositToken(token, amount)` on the bridge from your wallet. The bridge transfers the ERC-20, increments your on-chain `available` field, and emits a `Deposit(user, token, amount, nonce)` event.
2. **State-service**: observes the event, resolves your wallet → account ID, persists a bridge-transfer record, and sends the matching engine a `DepositMessage`.
3. **Engine**: credits the off-chain balance and emits a `DepositSuccessMessage` back.
4. **State-service**: marks the bridge transfer complete; you can trade.

Typical end-to-end time is small-number-of-L1-confirmations plus a sub-second engine handshake — usually well under a minute on a healthy chain. The deposit is visible on the bridge contract immediately; the off-chain credit is gated by L1 confirmations the state-service waits for (configured per network).

### Withdrawal flow

{% hint style="info" %}
This flow is intentionally three-phase so the off-chain engine and on-chain custody never disagree.
{% endhint %}

Withdrawals are three-phase by design, so the off-chain engine and on-chain custody never disagree.

#### Phase 1 — Request

```solidity
requestWithdrawal(token, amount)
```

* Creates a `Withdrawal` record with status `PENDING` and the current `block.timestamp`.
* Increments `claimed`; requires `available >= claimed`.
* Emits `WithdrawalRequested(id, user, token, amount, requestedAt)`.

State-service observes the event and sends the engine `WithdrawalHoldAndChargeMessage` — "freeze this amount and deduct from off-chain balance".

#### Phase 2a — Normal finalisation

After the engine confirms the hold:

* **ACCEPT** → `_transferOut`: tokens move from the bridge to the user's wallet.
* **CANCEL** → no transfer; the contract decrements `claimed` and frees the slot.

This happens via a batched call from the Treasury operator:

```solidity
batchFinalizeWithdrawals2(acceptIds[], cancelIds[])
```

#### Phase 2b — Force withdrawal (escape hatch)

{% hint style="warning" %}
Every call to `updateAvailableBalance` refreshes `updatedAt`, resetting the timelock. Force withdrawal only becomes available if the L2 has been completely silent for the full window.
{% endhint %}

If the off-chain engine never responds, you can force the withdrawal directly on-chain:

```solidity
forceWithdrawal(id)
```

* The timelock is `max(requestedAt, updatedAt) + timelockDuration`, where `timelockDuration` is between **1 and 14 days** (default **7 days**).
* Every call to `updateAvailableBalance` (a routine engine→bridge sync) refreshes `updatedAt`, **resetting** the timelock. So force withdrawal only becomes available if the L2 has been completely silent for the full window.
* Once the timelock elapses, calling `forceWithdrawal(id)` decrements `available` and transfers the tokens.

This is the structural guarantee that you can always recover your collateral if the off-chain operator becomes unresponsive.

### Edge cases

| Situation                                                                        | Outcome                                                                                                                       |
| -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| User calls `cancelWithdrawal`, then Treasury later calls `finalize(..., ACCEPT)` | Treated as **redeposit**: status `REDEPOSITED`, new `Deposit` event, tokens stay on the contract, off-chain balance restored. |
| Withdrawal request that the engine rejects (e.g. insufficient off-chain balance) | Status `CANCELLED`, tokens stay on the contract, on-chain `claimed` is decremented.                                           |
| Engine confirms but the finalisation tx fails                                    | State-service retries; user funds remain held in `claimed` until success or cancel.                                           |
| Network reorg removes the original `Deposit` event                               | State-service detects the reorg via block-hash comparison and rolls back the credit.                                          |

### Balance synchronisation

The engine periodically reconciles authoritative balances back to the bridge:

```solidity
updateAvailableBalance(batch, token, updates[])
```

`updates[]` carries absolute (not delta) `available` values per user. The on-chain `available` after this call is the source of truth for force-withdrawal calculations.

Two important properties of this call:

1. The value is **absolute** — the contract overwrites, never accumulates.
2. The call refreshes the **`updatedAt`** timestamp used by force withdrawal. Frequent calls (the normal case) keep the escape-hatch window from opening.

If you see the bridge contract's `updatedAt` not progressing while your withdrawal sits, it means the engine has stopped syncing. The force-withdrawal timer is now ticking.

### Supported tokens

The bridge has an explicit allow-list; only tokens flipped on by the Owner are accepted by `depositToken`. The current list lives on the bridge contract (`supportedTokens` mapping).

#### Current networks

| Network            | Status            |
| ------------------ | ----------------- |
| Production mainnet | Deployed.         |
| Arbitrum           | Planned.          |
| Ethereum mainnet   | Not yet deployed. |

For deployed addresses, see 20-smart-contracts.md.

#### Native token

The bridge contract has a `receive()` path but no native-token credit logic. **Don't send ETH (or the chain's native gas token) directly to the bridge** — it will not be credited as a deposit.

{% hint style="danger" %}
Don't send ETH (or the chain's native gas token) directly to the bridge — it will not be credited as a deposit.
{% endhint %}

### Known limitations

The current implementation has explicit gaps, which we surface honestly:

* **Deflationary or fee-on-transfer tokens**: not supported. Deposit accounting assumes the transferred amount equals the credited amount.
* **WithdrawalCancelled events**: emitted by the contract but not yet acted on by state-service.
* **Out-of-order finalization messages**: if `WithdrawalFinalized` arrives before the engine has registered the withdrawal in its pending map, the message is dropped.
* **Native-token deposit**: the contract accepts via `receive()` but doesn't credit. Don't send ETH; use the supported ERC-20 path.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://agentdex.gitbook.io/agentdex-docs/account/deposits-and-withdrawals.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
