This file covers the Phoenix perpetuals instructions implemented by the toolkit and the evidence needed to interpret their outcomes. SDK-only and experimental behavior is separated from dated live or simulation observations.
TL;DR
- Identity is market plus exact price ticks and order sequence; u64 values must never pass through
Number. - A confirmed transaction can post nothing or cancel nothing. Inspect program results.
- IoC needs
minBaseLotsToFill: 0; the evaluated SDK default otherwise acts as fill-or-kill. - Resting reduce-only orders do not shrink as exposure shrinks; matching after zero exposure remains unverified.
- Cancel batches must satisfy packet size and compute limits, not merely the builder's ID count.
1. Instructions and packets
The toolkit supports limit, post-only, IoC, exact-ID cancellation, and cancel-all instruction paths through the experimental signer. The owner supplies the account identity and the position authority signs normal trading instructions. Permission-account delegated variants are different instructions.
Limit packets use explicit ticks/lots, self-trade behavior, cancellation behavior, and flags. Post-only with sliding disabled fails if it crosses. IoC uses ImmediateOrCancelOrderPacket, minBaseLotsToFill: 0, a price bound, and an explicit last-valid-slot window. The evaluated Rise 0.5.28 default used submitted lots as minimum fill, effectively imposing fill-or-kill. Set the field deliberately.
CancelProvide allows removal of the resting self-order so the incoming order can continue. Abort can fail on self-liquidity. cancelExisting is false in the toolkit's ordinary placement path to avoid implicit cancellation on a margin shortfall. These fields express packet behavior; their selection is visible to the caller.
OrderFlags.ReduceOnly is bit 128. Conditional/stop builders exist upstream but are not an implemented toolkit trading workflow.
An IoC past lastValidSlot produced a successful transaction with zero fill in simulation on 2026-09-24. A blockhash still being valid does not extend that IoC's physical fill window.
2. Exact identity and intent IDs
Resting identity is (symbol, priceInTicks, orderSequenceNumber). All values are retained exactly. Bid sequences are inverted u64 values at least 2^63, while ask sequences are below it. Both price and sequence matter: the same sequence under another price or market is another identity.
The account snapshot has no persistent client-order ownership tag. clientOrderId is u128 event metadata, not an ID for matching resting state. The toolkit keeps direct exact IDs rather than a numeric surrogate registry.
An application intent ID identifies one signer request and carries a caller-provided namespace/prefix. It is separate from on-book identity. See signing-and-sidecar.md §2 for replay semantics.
3. Placement return data and fill reconciliation
Successful Phoenix program return data is 64 bytes: eight little-endian u64 fields for price ticks, sequence, quote-in, base-in, quote-out, base-out, quote-posted, and base-posted. The directions are from the matching engine's perspective: taker buy fills use base-out and quote-in; sells use base-in and quote-out. This direction was checked by simulation and transaction interpretation on 2026-09-24.
Reject wrong length, wrong program origin, contradictory direction fields, and a posted size with invalid identity. Zero posted and zero filled can mean empty IoC or an order rejection inside an otherwise successful transaction. “Confirmed” is therefore transport/chain evidence, not a FILLED label.
The pure resolver combines return-data fill with a signed position delta from a read at or after the transaction slot. On disagreement it takes the conservative common evidence. Without valid return data, a delta from precisely the transaction slot supplies stronger attribution than a later delta that may include other transactions. Lack of evidence remains unconfirmed fill.
Reason names TooManyLimitOrders, PostOnlyCross, InvalidOrderPacket, TiFInvalid, and OutsideExecutionPriceBand are from public Rust orderbook event source. Do not attribute them to a TypeScript SDK enum that does not contain them. Their live envelope was not comprehensively recorded.
4. Reduce-only
Simulation on 2026-09-24 showed an oversized reduce-only IoC trimmed to the position, and an oversized reduce-only limit posting at the position size. Reduce-only with no position or with an increasing direction failed rather than creating a protective order. A landed failed transaction can charge a fee.
Live observations on 2026-09-24 showed that an already resting reduce-only quantity did not shrink after partial reduction or remain capped to the new displayed position. It remained in the book after the position became flat. Its later match behavior is not verified; do not claim that it cannot reverse exposure.
planReduceOnly and reducibleLots require the current signed position. Cancel or explicitly review obsolete resting orders around a full close. Post-only reduce-only was not verified in the evaluated implementation.
5. Cancellation and batching
A successful cancellation transaction can be a no-op on a cold account, or can report OrderNotFound for an ID while the transaction itself succeeds. The outcome helper therefore uses effective-cancel evidence and exact not-found IDs. Do not count an unknown/failed/no-op cancel as removal.
Public SDK source defines 64 resting limit orders per trader, per market, per side. The rejection event form at the limit remains unverified live. This is a venue ceiling; a lower application target is not a package default.
Solana serialized transactions are limited to 1232 bytes. The toolkit's cancel batch ceiling is 30 IDs, a conservative implementation bound based on the evaluated transaction layout, not the network's universal ID limit. Even below it, check final serialized bytes and the selected compute limit before signing. Extra instructions or accounts can change the size.
Cancel failure due to packet size or compute exhaustion is atomic for that transaction. The batcher may split only a known failed attempt; it must not duplicate an unknown transaction. Builder capacity alone does not prove network acceptance.
6. Pending memory and unknown outcomes
Own-write memory records accepted slots, known state-change/fill slots, exact placed/cancelled echoes, and unresolved intent locks. A resting unknown locks its exact level; an IoC unknown locks its symbol until transaction or expiry evidence establishes resolution.
A confirmed transaction is resolved by its matching signature and parsed program outcome. A signed resting unknown remains locked until transaction resolution; elapsed time alone does not establish whether its order is live. A no-response lock can release only using configured slot/time windows plus a read that started after the physical execution window. Reads begun before that window cannot prove absence after it. A failed transaction releases the unknown intent without inventing a fill.
Experimental band-rejection memory records a paid rejection for an exact placement while the same band remains applicable. It is a client heuristic; band-edge IoC-miss and side-capacity heuristics are not implemented as universal venue behavior.
Pitfalls
| What breaks | Why | Correct approach |
|---|---|---|
| Exact bid cannot be cancelled | Rounded u64 | String/bigint identity end to end |
| Empty IoC counted filled | Confirmation confused with return data | Inspect fill lots and slot-qualified delta |
| Cancel counts increase with no book change | Cold/no-found no-op | Check effective cancellation |
| IoC unexpectedly requires all liquidity | SDK minimum-fill default | Set zero explicitly |
| Batch disappears without execution | Oversized network packet | Measure bytes before signing |
| Resting reduce-only assumed harmless at zero | Quantity did not shrink | Fresh exposure and explicit handling |
Open questions / not verified
- Resting reduce-only matching at zero/opposite exposure and post-only reduce-only.
- Post-only market packet acceptance, live capacity-event form, and CU cost per extra cancellation ID.
- Band-edge inclusivity and clamped-IoC fill behavior.
Sources
Orders; Rise public source (Rise 0.5.28 packet builders, scale-order limit, and Rust matching-engine/orderbook event source); Transactions. Fill/cancel/reduce-only observations and simulations: 2026-09-24. Packet-size measurement: offline 2026-09-25. The cancel batch ceiling is a toolkit bound.
© markpaper authors. Licensed under CC BY 4.0: when publishing or adapting this material, credit “markpaper — Phoenix knowledge base” and link to the original and the license.