PaymentRunTradeStatus
Owner context: partners (value object) — projected from cex-wallets CexAccountTradeStatus | Entity: PaymentRunTrade (value object embedded in PaymentRun) | Field: status | Values: 5
Status of a single CEX spot trade executed as part of a PaymentRun (partner payout). Unlike the other state machines documented so far, this is not a state machine the partners context owns — it is a read-only projection of CexAccountTradeStatus in cex-wallets. The values are the same strings; the entity in partners never mutates them, it just mirrors the CEX trade's status whenever PaymentRun.updateTrades() is called.
If you need to document or change the transition semantics, the authoritative source is CexAccountTradeStatus (to be documented), not this value object.
States
| State | Terminal | Description | Entry condition |
|---|---|---|---|
open | No | Trade created locally, not yet submitted to the CEX | PaymentRunTradesCreator creates the trade record |
pending | No | Submitted to the CEX; awaiting fill | CEX accepted the order; reported back on polling |
close | Yes (sink) | Trade filled and closed successfully | CEX reports fill |
failed | Yes (sink) | Trade rejected or errored at the CEX | CEX reports failure |
canceled | Yes (sink) | Trade canceled (manually or by CEX) | Cancellation confirmed |
There are no helper methods beyond the per-state predicates (isOpen(), isPending(), isClose(), isFailed(), isCanceled()). No isFinal(), no canTransition(). See PaymentRunTradeStatus.ts.
Transitions
The arrows show what is observed in practice from the CEX, not what PaymentRunTradeStatus enforces — it enforces nothing. The source state machine in cex-wallets is where transitions are validated.
Transition Table
PaymentRunTradeStatus has no transition matrix and no guards. The projection is refreshed by PaymentRun.updateTrades() using PaymentRun.mapCexTradesToPaymentRunTrades(), which builds PaymentRunTrade value objects from the current CexAccountTrade records on every call.
| Observed transition | Driven by (in cex-wallets) | How it reaches partners |
|---|---|---|
open → pending | CexAccountSpotTradeCreator submits the order to the CEX | PaymentRunTradesSynchronizer reloads trades by motive and calls PaymentRun.updateTrades() |
pending → close | CEX fills the order | Same sync path |
pending → failed | CEX rejects the order | Same sync path |
pending → canceled | Manual cancel or CEX cancel | Same sync path |
open → {canceled, failed} | Pre-submission cancel/error | Same sync path |
No domain events are emitted by transitions of PaymentRunTradeStatus in the partners context. Events of interest live on the CEX side (CexAccountTrade*DomainEvent, to be catalogued) or at the PaymentRun aggregate level (PaymentRunWithdrawInitiatedDomainEvent, PaymentRunInsufficientFundsForTradesDomainEvent).
Invariants
- Read-only in
partners—PaymentRunTradeis a value object. No method mutates itsstatus. The only way the value changes is by replacing the wholetradesarray on the parentPaymentRunviaupdateTrades(). - Source of truth is
CexAccountTradeStatus— the enum values are type-aliased fromcex-wallets(the internal tuple is literally namedCexAccountTradeStatuses, PaymentRunTradeStatus.ts:1). Any divergence between the two enums is a bug. - True sinks are
close,failed,canceled— once the CEX reports one of these, the trade does not move. This is enforced byCexAccountTradeStatusincex-wallets, not here. openvspendingis the submission boundary —openmeans "record exists locally, order not yet sent";pendingmeans "CEX has accepted the order and has not filled it yet". Treat them as both "not settled" for reporting, but onlypendingguarantees the order is in the CEX's book.closevscanceledis the settlement semantics —closemeans the trade was filled (successful settlement, coins moved on the CEX);canceledmeans the order was withdrawn without filling (nothing happened).- No event emissions tied to status changes — if you need to react to a
close, subscribe to the CEX trade event (incex-wallets) or poll viaPaymentRunTradesSynchronizer. Do not expect aPaymentRunTradeStatusChangedDomainEvent.
PaymentRunTrade vs Due vs PaymentRun
These three are often confused; the distinctions matter:
| Value object / entity | Context | Status field | Lifecycle |
|---|---|---|---|
Due (entity) | partners | DueStatus (open → settling → paid) | One commission owed to one partner, created from a ShiftConfirmedDomainEvent |
PaymentRun (aggregate root) | partners | PaymentRunStatus (created → rated → withdrawing → paid) | Container that groups many Dues for one payout cycle |
PaymentRunTrade (value object) | partners | PaymentRunTradeStatus (this doc) | One spot trade executed to rebalance funds before the payout; projection of CexAccountTrade |
CexAccountTrade (entity) | cex-wallets | CexAccountTradeStatus | The real trade record — source of truth for this projection |
Advancing a PaymentRun from rated to paid requires all its PaymentRunTrades to reach close and the resulting blockchain transfer to confirm. The trade-level state does not directly drive the PaymentRun state — PaymentRunTransferCompletionChecker owns that coordination.
Code Pointers
- Projection value object: packages/partners/domain/valueObjects/PaymentRunTradeStatus.ts
- Embedding value object: packages/partners/domain/valueObjects/PaymentRunTrade.ts
- Aggregate that owns the projection: packages/partners/domain/entities/PaymentRun.ts (
mapCexTradesToPaymentRunTrades,updateTrades) - Trade creation (open → pending): packages/partners/application/services/PaymentRunTradesCreator.ts
- Sync from
cex-wallets: packages/partners/application/services/PaymentRunTradesSynchronizer.ts - Completion check (drives
PaymentRun → paid): packages/partners/application/services/PaymentRunTransferCompletionChecker.ts - Authoritative source: packages/cex-wallets/domain/valueObjects/CexAccountTradeStatus.ts
Related
- Owner context:
partners - Upstream context and source of truth:
cex-wallets—CexAccountTradeStatus(to be documented) - Sibling status in the same aggregate:
DueStatus(to be documented),PaymentRunStatus(to be documented) - Cross-context flow that consumes this projection:
payment-run(to be documented) - Upstream flow that creates the
Dues this payout aggregates:shift-lifecycle
