The Problem with Simple Monotonic Nonces
A strict monotonic nonce (nonce N+1 must follow nonce N) creates a head-of-line blocking problem for HFT strategies:- You send order A with nonce 100
- You send order B with nonce 101 in parallel
- Order B arrives at the engine before A (network jitter)
- Engine rejects B: nonce 101 is greater than expected 100? Wait — actually in a strict scheme, the engine must process them in order. If B arrives first, either it is buffered (adds latency) or rejected (forces retry).
Rolling 20-Window Design
Vela maintains a rolling window of the 20 most recent nonces per user. The window acts as a sliding acceptance range:Validation Rules
| Condition | Result | Error |
|---|---|---|
new_nonce > min(window) and not in window | Accepted | — |
new_nonce <= min(window) | Rejected | InvalidNonce |
new_nonce already in window | Rejected | DuplicateNonce |
Replay Attack Prevention
The window prevents replay attacks:- A nonce below
min(window)is permanently rejected — an attacker cannot re-submit a captured request. - A nonce in the window is rejected as a duplicate — even if the original is still being processed.
- The window minimum only moves forward — there is no way to roll it back.
How to Use: Timestamp-Based Nonces
Timestamp-based nonces are recommended. They are always increasing across restarts, human-readable for debugging, and virtually guarantee uniqueness:Code Example: Concurrent Dispatch
TypeScript Example
Window State After Burst
After sending 20 orders with nonces[1000, 1001, ..., 1019], the window holds all 20. Sending nonce 1020 evicts 1000 (the minimum), leaving [1001, ..., 1019, 1020]. The window always holds exactly 20 entries once full.
If you need to send a 21st order before any acknowledgments return, you must wait for at least one window slot to open — either by receiving a response (the nonce is now confirmed consumed) or by accepting that nonce 1000 was evicted and 1020 is safe to send.
In practice, network round-trip time means responses start arriving before you exhaust 20 slots.