HD wallet architecture
Custodial deposit addresses are derived from BIP-32 hierarchical deterministic wallets, one per asset family. The master seed lives inside an HSM partition; only the public extended key (xpub) is available to the deposit-address service.
Derivation paths follow the established standards so an external audit can reproduce any address from the xpub alone:
- EVM (ETH, BSC, Polygon, Arbitrum, Base) — BIP-44
m/44'/60'/0'/0/<user_index>. - Native BTC — BIP-84
m/84'/0'/0'/0/<user_index>for native SegWit (bc1q…) addresses. - Solana — SLIP-0010 ed25519
m/44'/501'/<user_index>'/0'.
The user_index is a stable integer assigned at signup and reused across asset families, which keeps audit-trail reasoning simple. Deposit reuse is allowed: an address never expires, but rotation is available for users who want it.
Deposit address generation
The first time you visit Wallet → Deposit for a chain we have not yet provisioned, the address service derives the next address from the xpub, stores the (user_id, chain, address, index) tuple, and returns it. Subsequent visits return the cached row — addresses are deterministic.
Every newly minted address is registered with the chain watcher before being shown to you. There is no race where a deposit could arrive at an unwatched address.
GET /v1/wallet/addresses?chain=ethereum&asset=USDT
200 OK
{
"chain": "ethereum",
"asset": "USDT",
"address": "0x7a3e...c91f",
"memo": null,
"min_confirmations": 12
}Chains that require a memo or tag (Stellar, TON) return one alongside the address. Depositing without the memo will result in a delayed credit while we run manual reconciliation.
Confirmation thresholds
Funds are visible as pending from the moment the watcher sees the first inclusion, and become spendable only after the chain's confirmation threshold is reached. The defaults:
| Chain | Assets | Confirms | Median time |
|---|---|---|---|
| Bitcoin | BTC | 3 | ~30 min |
| Ethereum | ETH, ERC-20 | 12 | ~3 min |
| BNB Smart Chain | BNB, BEP-20 | 12 | ~36s |
| Polygon | MATIC, ERC-20 | 128 | ~4 min |
| Arbitrum One | ETH, ERC-20 | 20 | ~5s |
| Solana | SOL, SPL | 32 | ~13s |
During chain instability (reorg storm, network split, oracle outage) we may temporarily double the required confirmations. The active threshold is exposed on the deposit row so your UI can render it truthfully.
Withdrawal lifecycle
A withdrawal is a finite-state object. The transitions are durable — every change is written to the ledger before any side effect, and replays of the transition log will reach the same terminal state.
requested │ validation passes ▼ gating ── address screening, KYC, 2FA, velocity │ all gates pass ▼ queued ── waiting for operator review (if above auto-cap) │ ▼ signing ── hot-wallet signer produces the tx │ ▼ broadcasted ── tx submitted to the chain │ inclusion + confirmations ▼ confirmed ── terminal success
From any state up to signing a withdrawal can transition to cancelled (by you) or rejected (by an operator). After broadcasted the transaction is irreversible; we can stop submitting replacements but the chain is authoritative.
A second terminal state, failed, is reachable only if the broadcast itself errored (insufficient gas, nonce collision, rejected by the bundler). Failed withdrawals always re-credit the held balance.
Withdrawal gates
Every withdrawal walks through the same gate chain. Order is fixed and execution short-circuits on the first failure.
- KYC tier check. The requested amount must fit within the requesting tier's daily cap. Above the cap, the user is shown the path to the next tier.
- Daily & velocity caps. Per-asset 24-hour rolling caps. Sudden velocity above the user's historical baseline triggers a soft hold for operator review.
- Address screening. Every destination is screened against TRM Labs and Chainalysis risk lists. A high-risk hit blocks the withdrawal and notifies the compliance queue.
- 2FA challenge. TOTP or passkey challenge bound to the withdrawal payload. The challenge cannot be reused across requests.
- Operator approval (above auto-cap). Withdrawals above the per-asset auto-cap are queued in the operator console. See Operations.
Sweeping deposits to hot wallet
Deposit addresses accumulate balances as users top up. To keep the hot wallet funded for withdrawals while limiting the keys that touch the network, a sweeper consolidates deposits into the hot wallet at a defined cadence.
- EVM sweeps run on a 4-hour cadence per chain, batched and signed from the deposit signer partition. Gas is paid from a small EOA-funded float to avoid pulling deposit balances down through gas leaks.
- BTC sweeps are explicit, daily, and tagged by the deposit cohort to keep change outputs clean and audit-friendly.
- Solana sweeps follow the EVM cadence but use rent-exempt minimums on derived token accounts to avoid account-closure griefing.
Sweep transactions are reconciled hourly with the ledger; any drift above 1 basis point against the recorded user balances opens an incident, as described in Operations.

