CEX Lane (Exchange-to-Exchange Transfer)
What is a CEX Lane?
A CEX (Centralized Exchange) Lane is a 3-step pipeline for sending coins from Exchange A to Exchange B. Think of it like a bank wire transfer:- Withdrawal request: “Please send $1,000 from Bank A to Bank B” (withdrawal action)
- Withdrawal confirmation: Bank A says “Transfer processed, reference TXN-1234” (withdrawal observe)
- Deposit confirmation: Bank B confirms “$1,000 received” (deposit observe)
3-Node Flow
Data propagation between nodes (provider_context)
Each node’s output becomes the next node’s input — like passing a relay baton:Node 1: Withdrawal Action
The executor responsible for submitting a withdrawal request. Registered under the keyexec.cex.withdrawal_action.
What preflight checks
In code:What submit does
Calls the exchange API to actually execute the withdrawal:exchange_withdrawal_id in the return value is the key piece of data — it gets propagated to the next node (Withdrawal Observe).
Error handling
Node 2: Withdrawal Observe
The executor that periodically checks the withdrawal status. Registered under the keyexec.cex.withdrawal_observe.
Preflight
First gates on complete gross fee-quote evidence, then verifies thatexchange_withdrawal_id is present in the provider_context:
Observe (core logic)
Queries the exchange API for withdrawal status and branches based on the result:Why txid only appears at COMPLETED: the blockchain transaction ID (txid) is not finalized until the exchange has actually completed the on-chain transfer. Before that point, the transaction is still being processed internally by the exchange, so no txid is available yet.
Recover
recover() simply calls observe() again:
Node 3: Deposit Observe
The executor that confirms the deposit has been credited at the destination exchange. Registered under the keyexec.cex.deposit_observe.
Why txid matching is necessary
Deposit states
CREDITED vs ACCEPTED: different exchanges use different names for a completed deposit (
CREDITED, ACCEPTED, DEPOSIT_ACCEPTED, etc.). The code handles all of these variants because exchange response formats differ.Live capacity reads
Before creating a movement, callers can queryGET /v3/withdrawal-capacities for one configured venue, asset, and
provider-native network. The response keeps three concerns separate:
- current chain deposit/withdraw status and provider policy;
- the configured account’s live withdrawable balance;
- provider quotas and QTG’s pending reservation overlay.
no-store, and read-only. It creates no movement and grants no
approval to withdraw.
The same fail-closed rule applies to missing provider policy: an unknown
minimum, maximum, precision, or fee never becomes zero or unlimited. OKX fee
evidence comes from current fee/feeCcy and burningFeeRate fields, not the
deprecated minFee/maxFee pair.
Fee-aware balance holds
Approval separates the net destination amount from the gross same-asset source debit. An exacton_top quote reserves amount plus fee; an exact
deducted_from_amount quote reserves the requested amount. Unknown or
provider-defined charge semantics cannot approve a CEX movement.
The quote is stored on the withdrawal node and used by the existing outflow cap
and balance-reservation ledger; approved_source_debit stores gross separately
from net approved_amount. Positive on-top manual approval requires operator
max_source_debit, while auto-approval rechecks and reserves its policy budget
with gross. Immediately before submit, QTG reads the fee
again and requires the fee, gross debit, identity fields, and active reservation
to match the approval evidence exactly while the pool row is locked. A mismatch
fails before provider I/O; it is never permission to retry an earlier submit.
Unknown-gross legacy CEX approvals quarantine the same asset across transports
for every active cap/policy window. Legacy in-flight rows park for manual review,
and cancellation cannot release a hold while a side effect may be in flight.
Error Handling and Route Availability
Error scenario overview
CEX Lane full error flow
Related Docs
- Executor Protocol — The common interface all Executors implement
- Bridge Lane — On-chain bridge family (CCTP, CCIP, LayerZero)
- State Machine — State transitions and recovery logic