Skip to content

Scrypt DTO drift vs vendor AsyncAPI spec #3741

@TaprootFreak

Description

@TaprootFreak

Background

During the work on #3738 a side-by-side audit of src/integration/exchange/dto/scrypt.dto.ts (+ scrypt.service.ts, scrypt-websocket-connection.ts, exchange-tx.mapper.ts) against the freshly checked-in vendor AsyncAPI spec at src/integration/exchange/docs/scrypt-asyncapi.yaml (see #3737) surfaced a handful of drifts. None of them are regressions introduced by #3738 — most predate it — but several have non-cosmetic risk and should be tracked.

For each item below: verify against the live server payload before changing, because the spec may be incomplete or out of date in a few places (vendor confirmed this is a recurring issue with their docs).

Must verify, then fix

1. ScryptTransactionStatus likely has wrong values

  • Code (scrypt.dto.ts:8-12): Completed | Failed | Rejected
  • Spec (scrypt-asyncapi.yaml:3542-3544, example :1303): PendingApproval | Approved | Rejected
  • Impact if spec is correct: mapScryptStatus (exchange-tx.mapper.ts:111-121) returns 'pending' for every successful withdrawal because Approved falls through to default. Also affects scrypt.service.ts:161 success detection.
  • Verify first: tap incoming BalanceTransaction events on PRD and log the actual Status values seen.

2. ScryptOrderStatus missing Replaced

  • Spec (:2927) lists Replaced (and possibly Removed, Cancelled with double-l) on OrderEvent.OrdStatus. Our enum (scrypt.dto.ts:92-101) doesn't include it.
  • Impact: The exhaustive switch in ScryptService.checkTrade (scrypt.service.ts:341-415) silently returns undefined for unmodeled statuses → the caller treats that as "not done" → order ticks indefinitely.
  • Fix: Add REPLACED = 'Replaced' and a handler (probably: refresh the in-memory report, then re-check).

3. ExecType.CancelRejected / ReplaceRejected not surfaced

  • Spec (:3024) enumerates 18 ExecType values. We don't model an enum and don't branch on ExecType.
  • Impact: After a failed cancel, cancelOrder (scrypt.service.ts:475-494) returns false and editOrder (:522-527) only checks OrdStatus === REJECTED. A CancelRejected ExecType with a still-New OrdStatus is misread as "cancel succeeded, order remains active."
  • Fix: Model ScryptExecType enum and explicitly handle the reject variants.

4. OrderCancelRequest / OrderCancelReplaceRequest missing required TransactTime

  • Spec (:3184, :3210) marks TransactTime as required.
  • Code (scrypt.service.ts:478-483 and :506-512) doesn't include it.
  • Impact: Server may reject (verify on DEV first; could be that the spec is stricter than the live server).
  • Fix: Add TransactTime: new Date().toISOString() to both payloads.

Should fix (smaller correctness or hygiene)

5. ScryptTimeInForce enum missing inbound-only values

  • Spec (:2950) has Day, ImmediateOrCancel, GoodTillDate on OrderEvent.TimeInForce. We only model GoodTillCancel | FillAndKill | FillOrKill and only ever send the first.
  • Impact: Cosmetic unless we ever subscribe to Order events.

6. OrdType.RFQ not modeled

  • Spec (:3035, :3145) lists RFQ. We only send Market | Limit.
  • Impact: If we ever receive an ExecutionReport with OrdType: RFQ it deserializes as a raw string we never branch on. Today: theoretical; only matters once RFQ trading is wired.

7. ScryptBalanceTransaction schema diverges

  • Code (scrypt.dto.ts:25-38) declares Fee, TxHash, RejectReason, RejectText, Timestamp, TransactTime.
  • Spec BalanceTransactionEvent (:3512-3548, additionalProperties: false) defines only Timestamp, ClReqID, TransactionID, Revision, TransactionType, MarketAccount, Quantity, Currency, Status, LastUpdateTime.
  • Impact: We read phantom fields (TransactTime in scrypt.service.ts:212, Fee, TxHash, RejectReason, RejectText in the mapper and getWithdrawalStatus). Either the spec is incomplete (likely) or these reads silently produce undefined.
  • Verify: log a raw BalanceTransaction frame on PRD.

8. Revision is required by spec and ignored by us

  • Spec (:3527) requires Revision: integer on every BalanceTransactionEvent.
  • Code overwrites the in-memory map by ClReqID (scrypt.service.ts:86, 94) without comparing revisions. Older messages arriving after newer ones (e.g. after a reconnect + replay) can clobber state.

9. MarketDataSnapshot.Status is ignored

  • Spec (:2433, enum :2445): Online | Offline. Required.
  • Code (fetchOrderBook, scrypt.service.ts:572-586) doesn't check Status before returning prices → a stale Offline snapshot is treated as live.

10. SecurityEvent.UpdateAction ignored

  • Spec (:2757-2760): UpdateAction: Update | Remove. Our ScryptSecurity DTO doesn't model it; getSecurity/getTradePair (scrypt.service.ts:551-578) treats every cached entry as live → a Remove push would never evict a stale security.

11. BalanceTransactionEvent.LastUpdateTime required, unread

  • Spec (:3514): required. We expose externalUpdated via optional Timestamp (exchange-tx.mapper.ts:90). Switch to LastUpdateTime.

Nice to have (future surface)

  • ExecutionReport.QuoteID, RFQID + TradeEvent.QuoteID, RFQID (:3085-3102, :3334, :3340) — needed for RFQ correlation when we eventually use that path.
  • QuoteEvent.TradedPx (:3404, "all-in price that includes fees") — would let us bypass the embedded-commission cap logic for RFQ orders.
  • MarketDataSnapshotSubscription.Depth, Throttle, PriceIncrement (:2402-2421) — we always pull full top-of-book; could subscribe with Depth: "1" to reduce traffic.
  • HelloEvent (:2374-2389) — log the session ID for support tickets.

What looks correct

  • HMAC payload + base64-url translation + headers (scrypt-websocket-connection.ts:244-254) match spec :99-113 exactly.
  • ISO-8601 timestamp with microsecond zero-pad (.000000Z) matches the spec example precisely.
  • placeOrder request shape covers all spec-required NewOrderSingleData fields.
  • NewDepositRequest TxHashes array shape matches spec.
  • All numeric quantities sent as strings.
  • subscribe / unsubscribe / page operation names, pagination via next token.

Acceptance criteria

  • For each Must verify, then fix item: a quick PRD log capture to confirm the actual server payload, then DTO + handler fix
  • For Should fix items: fixed unless verification shows the spec is wrong (then leave a code comment with the discrepancy)
  • Tests for the new enum members / handlers
  • Linked to this issue from the resulting PR(s)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions