Solana escrow reference
PDA seeds, account layouts, instruction encoding, validation, and integer accounting.
Solana reference
This chapter covers Solana. For the main Robinhood Chain network, see the Robinhood guide. Wallets, token addresses and settlement mechanics are specific to each network.
Scope of this reference
This chapter documents the implemented escrow v1 source, currently not activated on mainnet. It is for technical review and integration planning. Use the complete ABI for exact account ordering, signer/writable flags, and unused-account placeholders; do not construct a transaction from a guessed address or abbreviated table.
Supported assets are initialized original SPL Token mints against native SOL or canonical mainnet USDC. Token-2022 extensions, transfer hooks, transfer fees and confidential transfers are not supported. SOL quote amounts use native lamports, not a wrapped-SOL quote token account.
PDA derivation and custody authority
Each derivation includes the deployed program ID and a bump. In the website client, market_seed and order_seed are SHA-256 hashes of the respective database IDs encoded as UTF-8. The ABI accepts 32-byte seeds; UUID text is a client convention rather than a program requirement.
Market and order state accounts are owned by the escrow program. Token vaults are owned at the Solana account level by the SPL Token Program; their token authority is the order PDA. The escrow program authorizes releases using that PDA’s seeds through invoke_signed. A native SOL bid holds quote principal on the order account above its protected rent reserve.
market = PDA(["market", launcher_public_key, market_seed], program_id)
order = PDA(["order", maker_public_key, order_seed], program_id)
vault = PDA(["vault", order_public_key], program_id)Persistent account layouts
All integers are little endian. Public keys and seeds are 32 bytes. The market account is 138 bytes, begins with SLMARK01, and stores launcher, fee wallet, base mint, seed, base decimals and bump. The order account is 166 bytes and begins with SLORDER1.
Order side is 0 for ask and 1 for bid. Quote is 0 for SOL and 1 for USDC. Contract status is 0 open, 1 filled, 2 cancelled, or 3 expired. The application can present an open on-chain order as expired by time before a refund instruction changes its terminal state.
| Order byte offset | Field / type |
|---|---|
| 0 | Magic SLORDER1 / 8 bytes |
| 8 / 40 / 72 | Maker pubkey / market pubkey / 32-byte seed |
| 104 / 112 / 120 | Original quantity / filled quantity / total quote, each u64 |
| 128 / 136 / 144 | Cumulative quote paid / fee paid / market share paid, each u64 |
| 152 | Expiry / i64 Unix seconds |
| 160 / 161 / 162 | Side / quote / status, each u8 |
| 163 / 164 / 165 | Order bump / vault bump / base decimals, each u8 |
Instruction set
The first instruction byte is the opcode. The reader rejects truncated or surplus instruction-data bytes. Required token accounts must already exist or be created with ATA instructions earlier in the same transaction. See the full ABI for account metas; unused token-account positions use System Program placeholders.
| Opcode | Data and effect |
|---|---|
| 0 · InitMarket | 65 bytes: opcode, market_seed[32], fee_wallet[32]. Launcher signs; records the initialized original SPL mint and immutable fee recipient. |
| 1 · CreateOrder | 59 bytes: opcode, order_seed[32], side u8, quote u8, quantity u64, quote_total u64, expiry i64. Maker signs; deposits the full ask inventory or bid quote plus fee budget. |
| 2 · Fill | 25 bytes: opcode, fill_quantity u64, max_buyer_debit u64, expected_filled u64. Taker signs; atomically exchanges assets, routes fees and advances counters. |
| 3 · Cancel / reclaim | 1 byte. Maker signs; refunds remaining assets or reclaims terminal token-vault surplus and rent. |
| 4 · Expire | 1 byte. Any caller signs after on-chain expiry; remaining assets are refunded only to the stored maker. |
Checks applied to a fill
- The taker signed, is not the maker, and supplies the expected maker, market, original Token Program and fee recipients.
- Market, order and vault addresses derive correctly; state ownership, mint and quote identities match the recorded order.
- The order is open, the on-chain Clock is before expiry, and the expected filled cursor equals the stored counter.
- Quantity is positive and within the remainder. Quote entitlement is positive, with no positive base remainder left with zero quote entitlement.
- The nominal buyer quote debit, including fee delta, does not exceed the maximum the taker signed.
- Transfer accounts have the expected mint and authority. Native SOL principal cannot consume the retained order rent reserve.
A failure rolls back transfers and counter changes together. Fee wallets can equal trading parties or each other; self-transfers do not move funds, while nominal quote and fee accounting still applies.
Cumulative quote and fee accounting
Let N be original base quantity, V the full quote amount and F cumulative filled quantity, all in integer base units. The full quote amount must be at least 100 quote base units. The program uses checked wide intermediates and bounded output amounts.
For a new fill from F0 to F1, it pays the difference between each cumulative entitlement at F1 and F0. It does not independently charge a newly rounded full-order fee for each fragment. This conserves the original funded bid’s quote and fee budget across valid partial fills.
Q(F) = ceil(V × F / N)
Fee(F) = ceil(Q(F) × 50 / 10000)
Market(F) = floor(Fee(F) / 2)
Platform(F) = Fee(F) − Market(F)
Fill quote = Q(F1) − Q(F0)
Fill fee = Fee(F1) − Fee(F0)
Buyer debit = fill quote + fill feeRefund destinations and retained state
Cancel requires the maker’s signature. Permissionless expiry requires an open order and on-chain time at or after expiry. Both refund only the recorded maker, using the proper base-token or USDC account when needed. Remaining principal, unused fee budget, and unsolicited surplus return to that maker.
Token-vault closure returns its rent. Filled token vaults need a later maker reclaim to close. Market state and terminal order state remain allocated; the terminal order is a tombstone preventing identifier reuse. There is no instruction to change the market fee wallet, reprice a funded order, or let an administrator withdraw a maker’s deposit.
Those are properties of this source, not a guarantee against a future upgrade. Any public deployment must separately disclose its program address, bytecode/source evidence, review status and upgrade-authority policy.
