CexTransferStatus
Owner context: cex-wallets | Entity: none (DTO value object) | Field: status on CexDepositInfo / CexWithdrawInfo | Values: 4 (5 accepted on input)
Normalized representation of a CEX-reported transfer status (deposit or withdrawal) as it reaches the cex-wallets boundary. Unlike the other state machines in this catalog, there is no CexTransfer entity and no writer that mutates this value — the status is read from the CEX API by the adapter (CctxCexSpotAccountAdapter) and returned inside DTOs (CexDepositInfo, CexWithdrawInfo) from CexSpotAccountPort.
The value object's job is twofold:
- Vocabulary normalization — absorb the variety of status strings returned by different CEX APIs (via CCXT) and expose a fixed set of four domain values. The input
verifyingis coerced topendingto handle exchanges that surface a KYT/compliance step; any other unknown value throws. - Bridge translation — convert the normalized CEX status to the neighboring state machines that actually drive work:
TransactionStatus(viatoTransactionStatus()) andTransferStatus(viatoTransferStatus()).
Treat CexTransferStatus as a translation layer, not an owned lifecycle.
States
| State | Terminal | Description | Entry condition |
|---|---|---|---|
pending | No | The CEX has accepted the operation but has not completed it | CEX returns pending or verifying |
ok | Yes (sink) | The CEX reports the transfer as fully processed | CEX returns ok |
failed | Yes (sink) | The CEX reports the transfer as failed | CEX returns failed |
canceled | Yes (sink) | The CEX reports the transfer as canceled | CEX returns canceled |
There is a fifth input value, verifying, which fromString() accepts and coerces to pending at construction (CexTransferStatus.ts:22-24). There is no way to observe a stored verifying — the value object holds pending internally.
Helpers: isPending(), isConfirmed() (tests for ok), isFailed(), isCancelled(), and isSettled() which returns true for ok || failed (note: not canceled, matching the definition in TransactionStatus).
Transitions
All transitions happen inside the CEX. The adapter re-reads the value on every poll; the value object has no updateStatus or canTransition method.
"Transitions" in practice
There is no transition matrix because this system does not transition the value — it observes it.
| Observation | Producer | How it enters the system |
|---|---|---|
| Deposit status | CexAccountDepositFinder polling the CEX | Returned as CexDepositInfo.status from CexSpotAccountPort.deposits() |
| Withdrawal status | CexAccountWithdrawFinder polling the CEX | Returned as CexWithdrawInfo.status from CexSpotAccountPort.withdrawals() |
| Internal sub-account transfer status | CexAccountTransferSender | Used internally; emits CexAccountTransferSentDomainEvent when dispatched |
No domain event carries a CexTransferStatus. When a deposit is confirmed, the context emits CexAccountDepositConfirmedDomainEvent, whose semantics are already "deposit is ok" — the raw status string does not travel on the event bus.
Bridge translations
The two mapper methods are the load-bearing part of this value object. They wire the CEX domain into the neighboring state machines.
toTransactionStatus()
Used when a Transaction on a custodial wallet in wallets is being driven by a CEX operation — today this does not happen in the main flow, but the method exists for alignment and is exercised by the adapter where the wallet side is backed by a CEX.
CexTransferStatus | → TransactionStatus |
|---|---|
pending | pending |
ok | confirmed |
failed | failed |
canceled | cancelled (note the double-l spelling in TransactionStatus) |
toTransferStatus()
Used by ShiftTransferUpdater when the owning Transfer's sender is a CEX. The updater polls the CEX via CexAccountWithdrawFinder, receives a CexWithdrawInfo, calls toTransferStatus(), and uses the result to advance the shift's Transfer.
CexTransferStatus | → TransferStatus |
|---|---|
pending | sending |
ok | confirmed |
failed | failed |
canceled | canceled |
This mapping is the other half of the shift-lifecycle "pull" boundary documented in shift-lifecycle: when the transfer's sender is binance / htx / kucoin, this is how the external status becomes our status.
Invariants
- There is no
CexTransferentity. This is a value object on DTOs returned by the adapter; no repository persists it on its own. If you need to check a CEX operation's status, call the adapter — do not look for a database row. - The
cancelledstatic vs thecanceledstored string.CexTransferStatus.cancelled(static, British spelling) constructs a value with stored string'canceled'(American) (CexTransferStatus.ts:10).toString()returns'canceled'. Code comparing against a string literal must use the American spelling. verifyingis input-only. The enum tuple['pending', 'ok', 'failed', 'canceled', 'verifying']allowsverifyinginfromString, but the constructor storespendinginstead. You will never seevalue === 'verifying'after construction.isSettled()excludescanceled. MatchesTransactionStatus.isSettled(): settled means the CEX took an on-chain action (succeeded or failed). A cancellation means no action happened.- No transition validation. The CEX is the authority. Our code does not assert that a transfer observed as
okwas previously observed aspending; consecutive reads may skip straight to a terminal. okis the only success path. For both mappers,okis the only input that maps to a confirmed state downstream. Any other terminal (failed,canceled) is surfaced to the shift or transaction as failure.
Code Pointers
- Value object: packages/cex-wallets/domain/valueObjects/CexTransferStatus.ts
- Port DTOs: packages/cex-wallets/domain/ports/CexSpotAccountPort.ts (
CexDepositInfo,CexWithdrawInfo) - Adapter (produces the status from CEX responses): packages/cex-wallets/infrastructure/adapters/CctxCexSpotAccountAdapter.ts
- Deposit polling: packages/cex-wallets/application/services/transfers/CexAccountDepositFinder.ts
- Withdrawal polling: packages/cex-wallets/application/services/transfers/CexAccountWithdrawFinder.ts
- Withdrawal dispatch: packages/cex-wallets/application/services/transfers/CexMainAccountWithdrawer.ts
- Internal transfer: packages/cex-wallets/application/services/transfers/CexAccountTransferSender.ts
- Cross-context consumer: packages/shifts/application/services/transfers/ShiftTransferUpdater.ts (uses
toTransferStatus()) - Deposit-confirmed event (carries no status literal): packages/cex-wallets/domain/events/CexAccountDepositConfirmedDomainEvent.ts
- Transfer-sent event: packages/cex-wallets/domain/events/CexAccountTransferSentDomainEvent.ts
- Deposit → funds transfer subscriber: apps/wallets-worker/application/eventSubscribers/cex-wallets/CexAccountTransferFundsOnDepositConfirmed.ts
Related
- Owner context:
cex-wallets - Bridged targets:
TransactionStatus,TransferStatus - Sibling value object (same context):
CexAccountTradeStatus - Flow that consumes the
toTransferStatus()bridge:shift-lifecycle(steps 5–7)
