Concepts
The vault
Where fees wait, how they are split, when they expire, and what is guaranteed to add up.
States
| State | Meaning |
|---|---|
escrow | No wallet bound. The streamer share accumulates as pending. |
bound | A wallet is bound. Fees go straight through. |
rotating | A wallet is bound and a change to another one is waiting out its delay. |
Receiving fees
Each fee event is split with whole-number arithmetic:
burn = amount * burnBps / 10000 (rounded down)
launcher = amount * launcherBps / 10000 (rounded down)
streamer = amount - burn - launcher (the remainder)
The streamer share is the remainder on purpose: rounding can only ever favour the person the money is for, and the three parts always add up to the amount.
The one invariant
accrued == pending + paidStreamer + paidLauncher + burned
True after every call, in every state. The test suite checks it after each of 400 random operations (fees, claims, rotations, expiries at random times) and after every call in the scenario tests.
Expiry
If no wallet is bound and the waiting fees have been waiting for the full expiry (default 180 days), they are swept once:
onExpiry: launcher(default): to the launcher wallet.onExpiry: burn: burned.
The expiry window counts from when fees start waiting. After a sweep, fees that arrive later start a new window. A vault that was never claimed does not lock money forever, and a streamer who shows up late still gets everything that arrived in the current window.
Configuration limits
- The three shares must add up to 10000 basis points exactly.
- The streamer must keep at least 50 percent (5000 bps). A vault cannot be set up to mostly pay the launcher.
- Between 1 and 5 trusted attesters, all distinct valid Solana public keys.
- A threshold of at least 1 and at most the number of attesters. A threshold of 0 would let a claim through with no attestation at all, so it is refused.
- A positive expiry and a non-negative rotation delay.
On chain
The program applies the same rules. Fees arrive as plain SOL transfers to the vault's address, and crank, which anyone can call, measures what arrived and splits it. Due rotations and expiries are applied at the next instruction rather than continuously. See On-chain program.
What anyone can read
snapshot() returns the state, the bound wallet, the epoch, any pending rotation, every balance and when waiting fees expire. Everything needed to audit a vault is there.
The escrow lab on the home page runs this engine for 300 days with sliders for the fee rate, the claim day and the expiry.