Pricing & allocation
The price of a pull is not set by anyone. It falls out of the pool's composition, and it moves as positions enter and leave.
Expected value
The pool's expected value is the total weighted backing divided by the total weight:
EV = weighted_backing_total / total_weight
Because weights are the inverse of backing, this expression is exactly the harmonic mean of every backing in the pool. Harmonic means are dominated by the smallest terms, which is the honest answer here: you are overwhelmingly likely to draw a cheap position, so the fair value of a pull sits near the cheap end of the range.
Concretely, with positions backed at 0.01, 0.10 and 1.00 SOL, EV = 3 / (100 + 10 + 1) = 0.027027 SOL — close to the cheapest position, not to the average of the three.
The acquisition fee
acquisition fee = EV × (10000 + surcharge_bps) / 10000
The surcharge defaults to 1000 basis points — 10%. That surcharge is the protocol's margin over fair value, and it is what ultimately pays depositors.
The surcharge does not always go to depositors. It is dynamically split between depositor fee income and a $RIP buy allowance for the purchaser, based on how long the pool has been idle: a busy pool routes it to depositors, a cold pool hands the whole surcharge back to the next purchaser as $RIP buying power. See $RIP.
The oracle fee
On top of the acquisition fee, every request pays a fixed oracle fee (about 0.003 SOL) covering the cost of the randomness request and the keeper that processes it. This fee is nonrefundable in every outcome — including refunds and expiries. Randomness costs money whether or not your pull succeeds.
Overpayment on the acquisition fee itself is refunded immediately, in the same transaction.
Purchaser protections
Because there is a gap between requesting and settling, every request snapshots the bounds you chose:
| Bound | What it does |
|---|---|
| Max acquisition fee | Reject the request up front if the live fee is above your ceiling. Zero disables it. |
| Min weighted value | Reject if the pool's total weighted backing has dropped below your floor — i.e. the pool got thinner than you were willing to buy into. Zero disables it. |
| Max negative slippage | Your tolerance, in basis points, for the fee drifting down between request and settlement. Defaults to 10%. |
Upward drift is bounded separately by a protocol-level cap (10%), snapshotted at request time so it cannot be widened underneath you. If the settled fee falls outside the allowed band, the request refunds instead of settling at a stale price.
Up to 5 acquisitions may be batched into one transaction. Each is an independent request, sequenced one after another.
Randomness: Switchboard On-Demand
Ripstr does not use Chainlink VRF. Randomness comes from Switchboard On-Demand, which works as a commit-then-reveal: the pool creates a commitment before any random value exists, and the value is revealed afterward against that commitment. The pool reads the revealed value — there is no callback into the program, so there is no callback to exhaust, re-enter, or grief.
Each request carries an inclusive deadline measured in slots (400 slots by default, roughly 2.7 minutes, bounded by Switchboard's reveal window). A value revealed exactly at the deadline is valid. Strictly after it, anyone may expire the request, which refunds the escrowed pool fee and unblocks the requests behind it.
Strict ordering, and why it matters
Requests settle in the order they were created, never in the order randomness happens to arrive. Four rules enforce this:
- Sequenced at request time. Each request takes the next sequence number and its randomness commitment is created before any random value exists.
- The pool freezes while requests are open. While any request is unsettled, the selectable set cannot change: no withdrawals, no repricing, no crown claims, no new positions going live.
- Deposits queue in FIFO. A deposit made while requests are open enters a staging line and waits. A fresh listing can never leapfrog an older staged one.
- Staged listings are reserved to a sequence. Up to 6 staged listings are reserved off the head of the queue for each request, and they activate exactly when that request goes terminal — before selection for a successful request.
Together these mean an earlier request's candidate set is frozen before its randomness is committed, and nobody can reshape it afterward. A depositor who sees an unfavourable outcome coming cannot pull their position out of the way; someone who wants a specific NFT drawn cannot stuff the pool at the last moment; and a later request cannot jump the queue because its randomness landed first.
Selection
Once a request's randomness is available and it is at the head of the queue:
target = random_value mod total_weight
The pool then walks a binary tree of weights from the root: go left if target is below the left child's weight, otherwise subtract that weight and go right. The leaf it lands on is the selected position.
The selected position still takes its share of the fee from the acquisition that drew it, and is then removed from the pool and marked allocated to the purchaser.
When a pull does not settle
Three things can end a request without an allocation. All of them produce a refund credit you pull yourself — never a push that a hostile recipient could block — and none of them refund the oracle fee.
- Empty pool. Every position left before the request reached the head of the queue.
- Price drift. The live fee moved outside your snapshotted tolerance band.
- Late or missing randomness. The reveal did not arrive by the deadline, and someone expired the request.
Who runs the queue
The processor is permissionless: anyone can advance the queue, and anyone can expire a timed-out request. An operator-run sponsored route reimburses processing costs out of oracle-fee surplus so pulls keep moving without users thinking about it — but it has no special powers. Neither route can supply randomness, influence a result, or skip an earlier request.