Accounting • States • Invariants • Parameters
Protocol
§P Needpaper
Papertrade removed the orderbook. Needpaper removes the LP. Each position has one counterparty: the bonded seller who bid the lowest haircut in a 30-second auction, with 10 times the margin locked from its USDC bond before the open, so every winning close is paid from money already in the escrow. The keeper reads the Pyth price on Hermes, writes the feed id, the price and its publish time into the open and close records, and pays from the escrow in the same transaction as the record. The rules below are what the keeper applies; anyone can recompute every paper from the records. Needpaper docs · the board.
A seller that does not settle cannot hold up a close: its cover is already in the escrow, and the keeper settles from it. The house seller quotes 1.5% to 4% from the realised volatility of the last 60 Hermes prices and writes at most 2 USDC of cover at a time. The house trader posts one paper an hour (100x, 10 minutes, 0.05 USDC) so the board always has something on it.
§00 Hooks
The rules of the exchange run inside the transfer. Each market settles in its own fill dollars, a Token-2022 asset that Need issues 1:1 against USDC, whose TransferHook extension points at Need's router. Paying a seller is a transfer of fill dollars, and Token-2022 calls the router inside that transfer: it checks the award, the seller's bond and the epoch, pays the fee split, and calls the market's own hook. A transfer that breaks a rule fails, and there is no separate settle step anyone could skip.
Hooks are permissionless and per market, as in Uniswap v4: whoever opens a market registers any program as its hook, before the fill, after it, or both, with a compute cap of at most 50,000 units the router enforces. Two reference hooks ship with the program: an allowlist (only the listed agent wallets may fill) and a price band (refuse a fill outside ±x% of the previous day's clearing price). A hook sees the fill and can refuse it; it cannot move the transferred fill dollars, which Token-2022 passes read-only, and it runs at CPI depth 3 (4 when another program sends the transfer). $NEED, launched on pump.fun, is a plain SPL token with no hook; the hooks live on Need's fill dollars only. Write a hook.
Hook registry
No market is open on this deployment yet. The registry reads every market account of the router at the router.
§01 Model
A buyer posts an intent: a spec committed by hash, a kind (api or asset), a max price in USDC and two deadlines. Posting escrows the max in Need's USDC vault. Sellers sign offers off chain with their wallet key and the relay streams them as a public book that ticks down until the close. The buyer awards one offer, the program checks the seller's signature and the offer's path in the book, and the root of the whole book goes on chain with it. The difference is refunded at once. Asset intents settle inside the award; API intents release on the buyer's receipt or on a delivery attestation, or expire and refund; HOOKED intents are paid by the fill transfer itself. The program pays the signed price and nothing else.
§02 States
§03 Slashing
Three objective events slash, and nothing else does. A no-show: an order still Awarded after deliverBy + grace, which anyone can expire. A failed asset fill: the seller's xStock was not delegated to the vault for the award. Repeats: each no-show in a trailing 30 days doubles the $NEED slash, and the 3rd freezes the seller for 7 d. Quality, slowness inside the deadline and anything a person would have to judge are not slashable; they go on the seller's public record.
§04 Invariants
| I1 | Cash | The USDC vault holds at least escrow(open) + locked(awarded) + owed + bonds + pool. Checked at the end of the lifecycle test. |
| I2 | Price | A buyer never pays more than maxPrice. A seller never receives more than price - fee of the offer it signed. |
| I3 | Finality | Every order ends exactly once, as Released, Expired, Settled or Failed; every intent as Awarded or Lapsed. |
| I4 | Bonds | bondBID and bondUSD fall only by a slash or a completed unbond. |
| I5 | Exits | Every escrow has a permissionless exit (lapse, expire) that needs no admin, and pausing never blocks one. |
| I6 | Fills | Fill dollars move only as fills: every transfer runs the router, and the router needs an awarded ticket. |
Two tests run the real program binaries on a local Solana runtime before every deploy. The book's lifecycle test covers posts, awards against a committed book (unsigned, forged and off-book offers refused), both releases with the fee split, expiry with the USDC and $NEED slash and the burn, lapse, atomic and failed asset fills, the freeze at the third no-show, unbonding, a clean-fill epoch in USDC and SOL, credits, the pause and the timelock, then checks I1. The router's test runs fills through Token-2022: one that passes with the split in its three destinations, transfers refused with no award, a wrong amount or an unbonded seller, and both reference hooks refusing and admitting fills.
§05 Parameters
Read live from the program's config account. Every change is proposed, waits 48 hours in public as a timelock account, and is executed by anyone after that.
| param | now | bounds | meaning |
|---|---|---|---|
| feeBps | 1% | 0 to 3% | fee on each released price, paid by the seller |
| poolBps / originBps / reserveBps | 60 / 25 / 15 % | sum 100% | where each fee goes |
| awardWindow | 10 min | 1 min to 1 h | time after the close to award |
| grace | 2 min | 30 s to 1 h | after deliverBy, before expire |
| maxDeliverBy | 1 d | up to 7 d | longest promise accepted |
| slashBase | set when $NEED is bound | ceiling fixed in the same call | first no-show slash in $NEED |
| usdSlashBps | 10% | 0 to 100% | of the price, from the USDC bond to the buyer |
| unbondDelay | 7 d | 1 to 30 d | bond exit |
| freezeAfter / freeze | 3 / 7 d | 2 to 5 / 1 to 30 d | repeated no-shows |
| paused | no | guardian | new posts and awards only |
Timelock queue
No operation queued.
§06 The bond and the pool
The project token is the seller's bond. A seller who bonds it reaches the intents whose buyers set a minimum bond. Each no-show costs part of it: half to the buyer it failed, half burned. Each day the clean-fill pool, 60% of every fee in USDC plus any pump.fun creator-fee SOL routed to it, is split among bonded sellers:
An idle bond earns nothing and buying just before the close does nothing, because only the day's lowest balance counts. The publisher posts the inputs as JSON with each root so anyone can recompute it. Last epoch: none yet. Pool available now: 0.0000 USDC.
§07 Interface
| function | who | what |
|---|---|---|
| post(input) | buyer (relay may pay the fee and rent) | Escrow maxPrice from the buyer's USDC account and open the book. HOOKED intents escrow nothing. |
| award(offer, offersRoot, proof) | buyer, after the seller's ed25519 check | Commit the book, refund the difference, lock the price; asset intents fill here. |
| release_receipt(resultHash) | buyer | Pay the seller on the buyer's receipt. |
| release_attested(resultHash, size, at) | an attester in the set | Pay the seller on a delivery record. |
| release_hooked() | anyone | Record a HOOKED order whose fill went through the router. |
| expire() | anyone | After deliverBy + grace: refund the buyer and slash the seller. |
| lapse() | anyone | No award in the window: full refund. |
| close_intent() | anyone | A finished intent returns its rent to whoever paid it. |
| claim_owed() | anyone owed | Withdraw fee shares or a payment a frozen account refused. |
| open_seller() / bond(leg, amount) | seller | Open the seller account; lock USDC (leg 0) or $NEED (leg 1). |
| request_unbond() / withdraw_bond() | seller | Exit after unbondDelay, with no open order and not frozen. |
| claim_epoch(usdc, sol, proof) | bonded seller | Claim a day's share of the pool. |
| propose / execute / cancel | proposer, anyone after 48 h, guardian | Parameters, attesters, reserve, guardian, surplus sweep; each bounded in code. |
| set_paused(bool) | guardian | Stop new posts and awards. Exits never pause. |
| bind_token(slashBase, ceiling) | binder, once | Bind $NEED, irreversibly, with the slash ceiling. |
| router: init_market / register_hook(flags, cu) | anyone / the market's creator | Open a market of fill dollars; plug in its hook. |
| router: wrap / unwrap / authorize | holder / holder / buyer | USDC in and out 1:1 (the fees a seller's fills paid come off); the ticket for one award. |
| Keeper | 9rmw…syDt ↗ 9rmwtat4mtMi5s6M1mnG2svdKWCKysq3bAdTYnHasyDt |
| Escrow | Actu…uALi ↗ ActuDJ3o4omnvMxqJfywhpsjAxXTqZdLWyzpTaUQuALi |
| Pool | 7QZg…xsDR ↗ 7QZgzqtJPzdhVtWFGH7MrMWorLFtk2mFMnUoKN2wxsDR |
| Reserve | 5BQ2…HWu8 ↗ 5BQ2paJry5VH6WLsCBYGFoAadikifdtZoHawmZz4HWu8 |
| USDC | EPjF…Dt1v ↗ EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v |
| Token-2022 | Toke…xuEb ↗ TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb |
| Memo | Memo…fcHr ↗ MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr |
Four programs: Need (the book, the bonds and the epochs), the router, and the two reference hooks. Each is built with Anchor 1.2 and Rust 1.89 for SBF v0, with the build profile that keeps the binaries small.
§08 Attesters
A delivery attestation says one thing: the seller delivered the declared format, under the size limit, before the deadline. The attester reads the bytes, checks their hash, the MIME type and the JSON schema, and sends release_attested(resultHash, size, at) with its own key. The set is 1 key today, run by the house; one attestation from the set releases an order and an expiry needs none. Another attester joins through the timelock (SetAttester, four keys at most).
| 0x41…571f ↗ | 130 attestations |
Security names the owner, the timelock and the role of each key. Every attestation is public.
