order-engine is Jupiter’s RFQ fill program
(MIT), audited by Offside Labs in November 2024. It is vendored unchanged except for its program id, and CI
rebuilds it from the pinned upstream commit plus the one patch and fails on any other difference.
What the program does
One instruction,fill(input_amount, output_amount, expire_at). The taker and the maker must both sign.
It checks expire_at against the cluster clock, then moves both legs by CPI: the taker’s input to the
maker and the maker’s output to the taker, through SPL Token or Token-2022. It refuses a Token-2022 mint
that charges a transfer fee. It holds no state and charges nothing.
Panofx’s protocol fee and the keeper’s gas repayment are two more transfer_checked instructions in the
same transaction. The maker and the taker each sign the whole message, so neither can be removed or
changed without breaking their signatures.
The canonical transaction
The settlement transaction is canonical:panofx-node/internal/solana/doc.go specifies it and
testdata/vectors.json pins it. The relay and the web app share one TypeScript implementation,
panofx-api/src/domain/solana/settlement.ts, copied byte for byte into the web app and checked by CI. Every
party rebuilds the transaction from the terms and compares bytes before trusting a signature on it.
Three signers: the maker (the node), the taker, and the fee payer (the relay’s keeper). The
taker needs no SOL. The taker repays the keeper in the pair’s fee mint through the gas-fee instruction,
which also covers the rent for the taker’s receiving token account when it does not exist yet.
Order of events
- The node signs as maker when it answers the RFQ. The RFQ carries the rest of the terms:
feePayer,feeMint,feeBps,treasury,gasFee,computeUnitLimit,computeUnitPrice. - The relay checks the transaction byte for byte against the terms it asked for, simulates it, and quotes it.
- The taker’s client rebuilds the same transaction from
/v1/configand the quote, checks it, and signs as taker. - The keeper signs last as fee payer and submits.
Node identity on Solana
A node’s identity with the relay is its EVM key. A node that quotes a Solana chain addssolanaAddress and
solanaSignature to its hello: an ed25519 signature over panofx-node-hello|<nonce>|<solanaAddress>|<name>.
A valid proof binds that key to the node; the relay stores it and rejects any Solana offer whose maker is
not the bound key. Without a valid proof the relay keeps the node’s EVM corridors and drops its Solana ones.
A key already bound to another connected node is refused.
Offer lifetime
The relay rejects a Solana offer whose deadline is undernow + MIN_OFFER_TTL_MS (10 s) or over
now + 120 s. A transaction’s blockhash expires after about a minute anyway, so nodes use 60.
Indexer
The program emits no events, so the indexer reads transactions.- What it reads. The program’s transactions from
getSignaturesForAddress, oldest first, at finalized commitment. Finalized transactions do not roll back, so rows are written confirmed. Failed transactions are skipped. - Fills. Every top-level
fillinstruction becomes a Fill row with id<signature>:<instruction index>. Its resolver is the node whose bound Solana key made it, or the key itself when no node has bound it. - Settlements. A fill whose transaction is exactly the canonical settlement also becomes a Settlement row. The indexer rebuilds that settlement from the fill’s accounts, the compute budget, the gas-fee transfer and the chain’s configuration, and it must match the landed message byte for byte.
- Matching a quote. First the quote whose execution sent that signature, then the quote storing the message hash as its offers hash, on the same chain only. A match settles the quote and releases the maker’s reservation.
- Cursor. The last signature indexed and the slot indexed through. It moves up to the finalized slot when the program is idle, so an idle program does not show as lag. A listing never goes further than 150 slots before the cursor’s slot.
- Start. An empty cursor starts at the current finalized slot. To backfill, set
indexerStartSloton the chain’sCHAINSentry. - Health.
/healthreportsheadandcursoras slots; the chain is unhealthy 300 slots (about two minutes) behind finalized. Before the first tick, the indexer checks that the RPC serves the cluster the chain id names (by genesis hash) and that the program is deployed there. - Not indexed. Fills that reach the program through another program (CPI), and versioned transactions.
Devnet
Deployed 13 September 2026 in slot 497,711,080. The deployed bytes equal a local build of the vendored directory. The end-to-end test passed on devnet the same day: the node signed both directions with the taker holding no SOL throughout, devnet answeredSignatureFailure with the fee instruction removed, and
the program refused an expired fill.
The integrator-facing shapes are under Solana.

