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

# Bybit Whitelist Onboarding

> Register destination addresses in Bybit withdrawal whitelist

# 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`): `131098` → `TRANSFER_ERROR`;
  `131083` / `131093` → `ADDRESS_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

| Symptom                                                                               | Cause                                                                          | Operator action                                                                                 |
| ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------- |
| Submit returns retCode `131098` after registration                                    | Cooldown not elapsed                                                           | Wait; do not re-register (re-registration restarts the 24h clock).                              |
| `get_whitelisted_addresses` does not return the entry                                 | Wrong scope key (read-only does not see whitelist)                             | Use a Withdraw-scope key for `--include-whitelist` probe.                                       |
| `get_whitelisted_addresses` returns the entry but submit fails with `131083`/`131093` | Network/chain mismatch between QTG template and Bybit entry                    | Confirm `NETWORK_TO_CHAIN` mapping vs UI selection; UI `ETH` must match QTG `ethereum`/`erc20`. |
| Submit fails with field-validation error mentioning tag                               | Tag present for non-tag asset, or missing for tag asset                        | Edit (or delete + re-register) the entry. **Editing also triggers a fresh 24h cooldown.**       |
| Submit succeeds but funds never arrive at destination                                 | Memo/tag wrong, or wrong chain (e.g. ERC-20 sent to a TRC-20-only destination) | Funds typically unrecoverable from external venue; treat as ops incident.                       |

## 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.
