Reference overview
Five programs, 64 instructions, joined by addresses and never by CPI.
Every page in this section is generated from target/idl/*.json, the IDL Anchor
emits at build time. Names, accounts, arguments, PDA seeds, events and error
codes are read off the artifact rather than recalled, so they are what the
deployed bytes actually accept.
26 instructions. Denomination and settlement: the registry, the reward ledger, the fulfilment valve. Devnet.
5 instructions. The venue's own AMM, for the mints no other venue will quote against. Devnet, two live pools.
17 instructions. The venue's own perpetuals, on a pairable with no mint behind it. Devnet, 48 markets, all migrated to the backing layout on 2026-08-25.
9 instructions. The NFT reserve of real things. Never deployed.
7 instructions. Parimutuel over-unders. Never deployed.
No CPI in any direction
The programs are joined by addresses and by hand-parsed account layouts, never by cross-program invocation. Three places in the source give the reason, and it is the same reason each time.
| Seam | How it is joined | Why not a CPI |
|---|---|---|
| peard_amm to peard | a pool's fee_claimer is a market PDA; claim_fees transfers, sweep_fees credits | "either program can be replaced without the other noticing" |
| peard_perps to peard | sync_index reads a Pairable at fixed byte offsets, checking owner, id and address | a build dependency would make the two upgrade together |
| peard_tote to peard | settle_round reads the same layout the same way | as above |
| peard to peard_vault | sync_backing parses a Backing attestation with a magic and a version | "a build dependency would make the two programs upgrade together and that is the coupling the attestation exists to avoid" |
The cost is real and it is a crank. The benefit is that peard contains no
AMM, and an AMM that turns out to be wrong can be swapped for a different
address without a peard upgrade.
The byte offsets are load-bearing and invisible
Reading a foreign account by hand means the offsets matter and nothing in the
type system checks them. peard_perps and peard_tote both declare theirs, and
tests/peard_perps.ts asserts every one of them against a real account rather than
trusting the comment.
pub const PB_ID: usize = 0; // past the 8-byte discriminator
pub const PB_MAX_AGE: usize = 60;
pub const PB_TWAP: usize = 64;
pub const PB_LAST_TS: usize = 80;
pub const PB_FROZEN: usize = 88;
pub const PB_EXPIRES: usize = 105;
pub const PB_MIN_LEN: usize = 113;
The Backing attestation is pinned three ways instead: the program that owns
the account, the address the market names, and the market the attestation names
back. Plus a magic and a version, so a wrong or moved account fails loudly
rather than parsing into something plausible.
pub const BACKING_MAGIC: u64 = 0x4241_434b_494e_4731; // "BACKING1"
pub const BACKING_VERSION: u8 = 1;
Fixed-point scales
Shared across the programs, and the ones that differ do so on purpose.
| Constant | Value | What it scales |
|---|---|---|
USD_ONE | 1e6 | every dollar amount. USDC has 6 decimals, so a USD-e6 quantity and a USDC base-unit quantity are the same number and are never converted |
UNIT_ONE | 1e9 | pairable units. Bottles are routinely fractional |
ACC_ONE | 1e15 | the reward accumulator. 1e15 rather than 1e18 for u128 headroom |
S_ONE | 1e6 | the coverage ratio. S_ONE means fully covered |
ROLL_ONE | 1e12 | the cumulative roll index |
BASE_ONE | 1e9 | peard_perps base size |
FUNDING_ONE | 1e12 | peard_perps's cumulative funding index |
BPS | 10,000 | everything in basis points |
Caps and floors written into the programs
| Constant | Value | Where |
|---|---|---|
protocol_fee_bps | max 2,000 | peard::init_global, set_protocol_fee |
MAX_CONVERT_BOUNTY_BPS | 500 | peard, the inbox bounty |
MAX_ROLL_FACTOR | 100x either way | peard::roll_market |
MAX_FEE_BPS | 500 | peard_amm::create_pool |
fee_bps | max 1,000 | peard_tote::open_round |
breaker_bps | 1 to 9,000 | peard::register_pairable and peard_vault::open_reserve |
Instructions no privileged role gates
Worth listing, because "anyone may call it" is a design decision each time
rather than an oversight. Most of these carry no signer at all in their
accounts struct; liquidate signs only because the caller is the one collecting
the fee.
| Instruction | Program | Why nobody needs permission |
|---|---|---|
sweep_fees | peard | it credits holders from money already in their vault |
sync_position | peard | it can only ever raise the named holder's own earning balance |
roll_market | peard | the outcome is fully determined by the two pairables |
sync_backing | peard | the address was pinned first, twice |
expire_fulfillment | peard | an agent going dark must not hold a holder's units hostage |
withdraw_protocol_fees | peard | the destination is fixed, so there is no discretion to guard |
claim_fees | peard_amm | the destination was written once at creation and has no setter |
sync_reserves | peard_amm | a pool that has been reached into should show it before somebody sells into it |
sync_index, settle_funding | peard_perps | a market nobody cranks is a frozen number |
settle_market, settle_position | peard_perps | expiry is a fact rather than a decision, and everyone leaves at the same number |
liquidate | peard_perps | the fee is the only reason anyone would run a liquidator |
settle_round, void_round, claim, withdraw_fee | peard_tote | the pot answers the question and the protocol never takes a side |
open_round | peard_tote | anybody may ask a question; governance pins which registry may be quoted, not which questions are interesting |
close_epoch, mark_item, settle_carry | peard_vault | marking and carry are bookkeeping over numbers an appraiser already signed |
peard