peard_tote: parimutuel over-unders
A short-dated instrument where the pot is the counterparty. Built and tested, deployed nowhere.
peard_tote has never been on a cluster. The program is written and
tests/peard_tote.ts carries 11 passing tests as of 2026-08-25. There is no deployed
instance, no live round, and no vault holding anything anywhere.
It is also not wanted. "Kill or keep peard_tote" is an open item on
docs/PRIORITIES.md and the standing position there is that it is undeployed
and explicitly not part of the product. This page documents what exists, not
what is planned.
peard_amm sells the asset, peard_perps gives you a position in it, and this
takes a short-dated view on it. It is the fifth program, and the one that gives
an underlying with a slow, authoritative print a shape worth trading at all.
| Program id (localnet) | 6Xniss9GBCMD4CdawyUtMYR7LEUGeHY3sr6cHYQyNcoa |
| Instructions | 7 |
| Round PDA | ["round", pairable, seq] |
| Ticket PDA | one per person per round, so a claim is O(1) |
| Fee cap | 1,000 bps, taken off the pot at settlement only |
Three properties, each answering why options do not work here
Nobody has to want the other side and no curve has to be seeded, because the losing side pays the winning side directly. Every on-chain options venue that needed a willing short died of not finding one.
A parimutuel pool's implied odds are the crowd's probability estimate. There is no volatility surface to source, which matters because a weekly produce print has no options market to derive one from and never will.
Settlement requires a price stamped at or after settles_at, so the number
that decides the round could not have been known while stakes were open.
That third property is the whole answer to a slow oracle. Thales and Ribbon both hit the same problem in production and solved it by buying a faster oracle; there is no faster oracle for a USDA pear print, so this closes the book instead. Nothing to front-run if trading has stopped before the print exists.
Opening a round
open_round is permissionless: anybody may ask a question, because the pot
answers it and the protocol never takes a side. What governance pins is which
registry may be quoted, not which questions are interesting.
require!(closes_at > now, Err::BadParam);
require!(settles_at >= closes_at, Err::BadParam);
require!(voids_at > settles_at, Err::BadParam);
thresholdu64The line, USD e6. Over wins strictly above it; a print exactly on the line is Under. Ties have to fall somewhere, and saying so up front is better than discovering it at settlement.
closes_ati64When stakes stop. Deliberately before settles_at.
settles_ati64Settlement needs a print stamped at or after this, which is enforced harder than the close itself.
voids_ati64If no usable print has landed by here, the round voids and everyone takes their stake back. A feed that dies must not strand the pot.
Settling
settle_round reads the pairable account by hand, the same way peard_perps does,
checking the owning program, the id it carries and the address the round names.
Then three refusals:
require!(b[PB_FROZEN] == 0, Err::IndexUnusable); // a tripped breaker's last
// print is exactly the number
// nobody should be paid against
require!(twap > 0, Err::IndexUnusable);
require!(ts >= round.settles_at, Err::AwaitingPrint);
That last line is the load-bearing one. A print older than settlement time was knowable while the book was open, so paying against it would be paying somebody for information rather than for a view.
A one-sided round voids rather than paying
if round.one_sided() {
round.status = RoundStatus::Voided;
return Ok(());
}
A round with nothing on one side is not a market. It cannot settle into a payout, because the winning side would either be everybody or nobody, and both are just the pot handed back with extra steps.
What a winning ticket is owed
pub fn payout_for(&self, stake: u64) -> u64 {
let winning = self.stake_on(self.winner) as u128;
let distributable = self.pot().saturating_sub(self.fee_taken) as u128;
((stake as u128 * distributable) / winning) as u64
}
Stake plus a pro-rata share of the losing side, less the fee. It rounds down, so the dust stays in the vault rather than being paid twice to the last two claimants.
The fee is taken off the pot at settlement and never off a refund. A voided round gave nobody anything, so it charges nobody.
Nothing expires unclaimed
claim is permissionless to crank and destination-fixed to the ticket's owner,
so a claim never depends on the holder still being around to make it. There is
no deadline and nothing expires: a settled round pays forever and a voided one
refunds forever.
The reason is stated plainly in the program: a deadline on collecting your own money is how a product that works starts feeling like one that does not.
void_round is likewise permissionless once voids_at has passed with no
settlement. The case it exists for is a feed that stopped, and "the data source
went away" is a thing that actually happens rather than a hypothetical.
peard