Skip to main content

Bybit Whitelist Onboarding (Risk 2 SOP)

When to run: Before any new (asset, chain, destination address, tag) tuple is referenced by a Bybit movement template. Why it’s manual: Bybit V5 does not expose an address-book write API. Whitelist entries are added only via the Bybit web UI, and every new entry triggers a 24-hour cooldown during which the address cannot receive withdrawals. The 24h timer is enforced server-side; there is no operator override.
Source: Bybit V5 docs /v5/asset/withdraw/query-address only — no create companion. Verified 2026-05-12.

1. Pre-onboarding checklist

Before touching the Bybit UI:
  • Destination address is already an active QTG allowed_addresses row for the target venue (so on-chain executor preflight will accept it later).
  • Operator has logged Bybit address-book entries that already exist via bybit_readonly_smoke --include-whitelist (requires Withdraw-scope key) — confirm the new entry is genuinely new and not a duplicate.
  • Memo/tag policy decided per asset:
    • XRP / EOS / XLM: tag REQUIRED. Mismatched tag = funds permanently misrouted on Bybit’s side.
    • ERC-20 / TRC-20 / BEP-20 stables: tag must be empty (Bybit rejects with field validation error if present).
  • Network code in the QTG template matches the Bybit chain code (e.g. QTG ethereum → Bybit ETH, QTG tron → Bybit TRX). Mapping table: src/qtg/infrastructure/executors/cex/adapters/bybit/bybit_transfer_constants.py:NETWORK_TO_CHAIN.

2. UI registration steps

  1. Sign in to Bybit web UI (mainnet or testnet — drill differs from live, don’t cross them).
  2. Navigate: Assets → Withdraw → Address Book → Add Address.
  3. Fill:
    • Coin: the asset symbol (USDT, XRP, etc.). Bybit forces case-uppercase.
    • Network: the chain code matching NETWORK_TO_CHAIN[<qtg_network>]. Do not select “internal transfer” / “UID” — QTG observe path requires on-chain txID.
    • Address: the destination wallet/exchange deposit address. Paste, do not retype.
    • Tag/Memo: per the asset rule above.
    • Address Type (if asked): always pick On-chain (not Universal, not Internal). QTG filter (ADDRESS_TYPE_ON_CHAIN = 1) drops the other two.
    • Label: a short operator-readable name, format qtg-<env>-<dest_venue>-<asset>-<chain> (e.g. qtg-prod-upbit-usdt-trx). Bybit truncates to ~32 chars.
  4. Complete Bybit’s 2FA + email confirmation (typically email link + Google Authenticator code).
  5. Record the registration timestamp (UTC) in the drill log.

3. 24-hour cooldown window

  • After successful UI registration, Bybit marks the entry as pending for 24h.
  • GET /v5/asset/withdraw/query-address will return the entry but a POST /v5/asset/withdraw/create to that address will fail with retCode 131098 (new-address 24h cooldown).
  • QTG mapping (bybit_transfer_constants.py:ERROR_CODE_MAP): 131098TRANSFER_ERROR; 131083 / 131093ADDRESS_NOT_WHITELISTED, meaning the address is absent from the address book. The node terminates FAILED in both cases — the dispatcher does not retry.
  • 131086 is a risk-limit rejection. It is neither a whitelist nor a cooldown signal, and waiting does not clear it. Do not diagnose it with this page.
  • During the window, run bybit_readonly_smoke --include-whitelist --asset <coin> to verify the entry is visible. The entry being visible ≠ entry being usable; only the 24h timer makes it usable.

4. Post-cooldown verification

After 24h has elapsed:
  • bybit_readonly_smoke --include-whitelist --asset <coin> returns the entry in get_whitelisted_addresses summary.
  • Optional sanity: a tiny test movement (bybit_tiny_withdrawal_drill plan) submits without 131098.

5. Common failure modes

6. Hard rules

  • Never edit a whitelist entry to “fix” an address. Editing restarts the 24h cooldown and during that window the entry is unusable — this leaves any in-flight movement template referencing the entry stuck in FAILED. Delete and re-register instead, knowing the new entry also has 24h cooldown.
  • Never use Bybit’s “Internal Transfer” address type for QTG movements. Internal transfers produce no on-chain txID, and QTG’s deposit_observe matches by txID — the deposit-side node would hang indefinitely.
  • Whitelist scope is per-API-key-pair if 2FA is bound. A whitelist entry registered under one Bybit account is not visible to another account, even within the same legal entity.