Backing by real things
peard_vault holds NFTs and nothing else. Live on devnet, backing one pool, and pinned three ways.
Live on devnet since 26 August 2026, and holding one reserve. That reserve issues the receipt that a real pool quotes in, and the pool names the reserve on its own account. Both are linked at the bottom of this page.
What is still true: peard's init_global leaves Global.backing_program at
Pubkey::default() until governance pins it, so a market's backing_value
stays zero until somebody does. The pool-level backing below does not depend
on that pin, because it is a different join and is checked differently.
A pairable is a type. An NFT is an instance.
"A pear" is a type. 40 cartons of D'Anjou 80s, bought 14 September at $48, CA storage in Wenatchee, expires 15 May is an instance. Pears bought in September and pears bought in March share a name and nothing else, and no registry row can be true of both.
Every variable that decides value lives on the batch: quantity, cost basis, acquisition date, expiry, carry, haircut, appraiser, custodian.
The core values nothing itself
ItemKind is metadata and no instruction branches on it, exactly as Resolver
never dispatches in peard and Delivery never dispatches in the fulfilment
valve.
That is the only way "support every type" is tractable instead of becoming a growing switch statement inside a deployed program: adding graded cards or service vouchers is writing an appraiser, not editing this.
ItemKind | What it is |
|---|---|
PerishableLot | a quantity of something that spoils. Shrinks, then stops existing |
DurableLot | a quantity of something that does not. Metal, bags of rice, fuel |
Collectible | one graded, unique object. No clock, a floor price, and a spread |
Voucher | a claim on a service, good until it is not |
Tokenized | a position in something already tokenised, held here for custody |
Other | valued entirely by whoever appraises it |
Four decisions worth defending
Carry is charged against the appraised value rather than cost, because what it costs to store a thing tracks what the thing is worth far better than what somebody paid for it a season ago.
The join is an address and a hand parse
peard_vault publishes a Backing account: magic, version, value, cost basis,
item count, and as_of_ts. Tiny, frozen, and seeded by the market alone, so a
reader derives it rather than being handed it.
It is written in exactly one place, close_epoch, which is precisely when
every held item is known to be current.
pub const BACKING_MAGIC: u64 = 0x4241_434b_494e_4731; // "BACKING1"
pub const BACKING_VERSION: u8 = 1;
peard's sync_backing is permissionless and guarded by two pins:
governance pins which program may vouch, and the market pins which account.
Then the magic, the version, and the market named inside the attestation all
have to agree.
The layout is mirrored in peard rather than imported. A build dependency would make the two programs upgrade together, and that is precisely the coupling the attestation exists to avoid. The magic and version are what turn a wrong or moved account into a loud failure instead of plausible nonsense parsed out of somebody else's bytes.
peard_perps reads the same format the same way, and pins it three times: the
program that owns the account, the address the market names, and the market the
attestation names back.
The backing is on the pool, not beside it
A backing somebody has to be told about is one most people will never check. So
peard_amm's Pool carries the reserve on its own account:
/// The peard_vault reserve whose receipt this pool's quote leg IS, or
/// default for a pool with nothing behind it.
pub backing_reserve: Pubkey,
One account read, and a reader knows what stands behind the token they are about to buy. They do not have to know a second program exists, or which account in it to look up.
It is a fact, not a claim
attach_backing refuses unless the reserve's own receipt_mint is this
pool's quote_mint. A pool cannot point at a warehouse that does not issue the
token it trades.
One-way and immutable, for the same reason fee_claimer is. A backing
somebody can repoint is a story rather than a guarantee.
An owner constraint, which pins the program but says nothing about type.
Owner plus length is not a type check. peard_vault owns several kinds of
account and so does peard; only the discriminator separates them.
The join, and the only one of the four that makes the field mean anything. Without it the pool is a pointer at an unrelated warehouse.
What it does not do
It is 32 bytes of pubkey in the account's data. There is no vault behind it
and no instruction moves it. buy and sell touch exactly two token
accounts, base_vault and quote_vault, and the swap arithmetic never reads
the field. It is a label on the jar, not something in the jar.
A backed pool holds no USDC. Its quote leg is the receipt, so the price it quotes is "this token, in crates", and buying requires already holding receipts. Instant tradeability and a redeemable quote leg pull in opposite directions, and a USDC-quoted pool is a separate build.
Appending to Pool, and why the field is last
backing_reserve sits at the very end of the struct, and nothing may be added
above it. Borsh carries no field names on the wire, so a field inserted anywhere
but the end shifts every field after it.
This was not theoretical. The field was first declared in the middle, and when
migrate_pool grew the two pools already on devnet, both decoded their own
fee_bps, decimals and bump as the first five bytes of the new pubkey and read
zeroes as their fee. Nothing errored. The migration reported success on both. It
was caught because the migration printed a backing address where
11111111111111111111111111111111 belonged.
Cash coverage and total coverage are different numbers
holder_usdc still bounds every payout. Backing only counts in
total_coverage.
Pears in a warehouse do not pay dollars until somebody sells them, so folding them into the ratio that governs USDC claims would let a market read fully covered while holding no money. That is the same overstatement as counting the protocol's own accrued cut.
Appraisers live outside
relayer/src/appraise.ts is pure and offline-tested.
| Kind | Formula |
|---|---|
| Perishable lot | quantity x index x (1 - shrink x days) |
| Collectible | a floor, less what it costs to actually sell |
| Voucher | face value, less the odds nobody honours it |
Shrink is an appraisal input rather than on-chain state, because two lots of the same fruit in different rooms shrink at different rates and neither the program nor the registry should have an opinion.
It refuses rather than guesses, and a sanity band against cost basis catches a units error before the on-chain breaker has to. A units error looks exactly like a hundredfold rally and only one of them is real.
Not built
- Fractional redemption. A holder owed 3.7 pears cannot be handed part of a deed, so that path settles in USDC at appraisal and does not exist yet.
- Custodian limits. Nothing bounds how much a single custodian may hold.
- The bond, which is a field rather than a mechanism.
- Anything actually in the warehouse. The devnet reserve below holds zero items and has issued zero receipts. Every mechanism around it is live and exercised; what is missing is somebody buying the pears and renting the cold storage. A reserve holding nothing backs nothing, and the page says so rather than letting the presence of an account imply otherwise.
- A USDC-quoted pool with a declared backing. See the warning above: today a backed pool's quote leg is the receipt itself.
Look at it
Devnet, 26 August 2026. The pool's backingReserve is the reserve, and the
reserve's receiptMint is the pool's own quoteMint.
Both programs have their IDL published on devnet, so an explorer renders those
as named fields rather than base64. scripts/devnet-backed-pool.ts builds the
whole thing from nothing and prints the two links.
peard