@solana/kit and a client generated from Fade’s IDL. The wallet plays every role: app, payer, stake owner and beneficiary.
Requirements: Node 20 or later, a devnet keypair with about 0.02 devnet SOL and some test USDC. Claim both from the faucet in app.fade.finance, or call the faucet API (step 2).
1
Install and generate the client
generate.mjs
src/generated/fade with a builder for every instruction (getOpenWagerInstructionAsync, …), account fetchers (fetchConfig, fetchMaybeWager, …), PDA helpers, error codes and event decoders.2
Get test USDC
The faucet sends 1 000 test USDC, and a little SOL when the wallet has almost none. One claim per wallet and per IP every 5 minutes.If the wallet still lacks SOL, use faucet.solana.com.
3
Set up the connection
src/fade.ts
cfg anyway, so the code says which deployment it targets.4
Build a paytable
Probabilities are integers in billionths and must sum to exactly 1 000 000 000. Multipliers are integers in basis points: 10 000 is 1×. Compute in
bigint, never in floating point.src/paytable.ts
allOrNothing(1_000_000n, 10_000_000n, 350n) returns [{ p: 96_500_000n, m: 100_000n }, { p: 903_500_000n, m: 0n }]: 10× at 9.65 %, a 3.5 % edge. More shapes and the sizing rules are on Paytables.5
Open the wager
src/open.ts
config, pool, integrator, vaultUsdc, wager, vrfPayer and ORAO’s network state for you. If a check fails, sending throws with the program’s error; see Errors.6
Hand it to a keeper
The randomness request and the settlement are permissionless. The public keeper sends both:Even without the
src/keeper.ts
POST, the keeper sweeps for open wagers about every 30 seconds; posting just starts it at once.7
Run it
src/main.ts
bucket is the index of the paytable row that was drawn: 0 is the 10× row here, 1 is nothing. A payout is in USDC base units and has already reached the beneficiary.Settling without a keeper
Anyone may send the two remaining steps, including your own server or the user’s wallet. Request the randomness from two slots after the open, wait for ORAO to fulfil, then settle.src/settle-yourself.ts
T_SETTLE (150 slots), settle_wager is impossible and anyone may call expire_wager instead. See Settlement and keepers.
Reading the result
- From the keeper:
GET /v1/wagers/:addressreturnsbucket,payoutand the settle signature. - From the chain: the wager account closes at settlement. The settle transaction carries a
WagerSettledevent withbucket,payoutandpending(true when the payout was parked because the beneficiary could not receive). See Events for decoding. - While open:
fetchMaybeWagerreturns the stored paytable,status(Open,RequestedorPayoutPending),openSlot,expirySlot, the boundrandomnessaccount and the reserved liability.
Next
- Size prizes against the pool: Paytables.
- Who pays which SOL and gets it back: Fees.
- Run your own keeper: Settlement and keepers.