PROJECT REGISTRY

Savora

Your savings circle, without someone holding the money.

DEVNET2026
// $ cat brief.md

Rotating savings circles are how a large part of the world actually saves — but they run on one person holding everyone’s money and everyone else trusting them not to disappear with it, or to skip a friend when it is their turn.

Savora removes that person. Contributions go into a vault owned by the program; Savora holds no key that can move, pause, or sweep it. The collection order is shuffled on-chain when the last seat fills — nobody picks it, not even the organiser. Once a round is funded, or its deadline passes, anyone can trigger the payout and it goes to whoever’s turn it is.

usesavora.vercel.app — the pitch, stated as plainly as the constraints
usesavora.vercel.app — the pitch, stated as plainly as the constraints
The product mock and "What Savora cannot do" — including the shuffle-grinding caveat
The product mock and "What Savora cannot do" — including the shuffle-grinding caveat

ENGINEERING CHALLENGES

#01

A deterministic on-chain shuffle nobody controls

At seal, the collection order is permuted with Fisher–Yates. The seed is read from the SlotHashes sysvar, folded with the full 32-byte group PDA, salted with the rotation index and both bytes of the cycle counter, then expanded with SplitMix64. The salt is load-bearing: without it, a fresh rotation on a chain that has not advanced its slot hash would reproduce the previous permutation exactly. Anyone can recompute the order; no party gets to choose it.

#02

Auto-payout in the same transaction, with a fallback

contribute detects when it is the last contribution a round needs and disburses the pot in the same instruction. The recipient token account is passed unchecked and verified against the canonical ATA derivation; if it does not exist yet, the inline payout is skipped and the permissionless crank handles it — creating the ATA there. A round can always be closed out, by anyone.

#03

The payout cannot be redirected

The recipient is pinned at the Anchor constraint level — `address = group.members[cycle.recipient_index]` — and the destination token account is checked as that owner’s canonical ATA for the group’s mint. Every crank re-checks it. There is no admin instruction that can point a payout somewhere else because there is no admin instruction at all.

#04

A permissionless crank that slashes no-shows

Past deadline + grace, anyone can call disburse_payout. It ejects each member who did not contribute, forfeits their deposit into the round they missed so that round’s recipient is still made whole, tombstones their seat, and records it in Group.defaulted. Membership is tracked as u16 bitmasks — seat_mask, live_mask = seat & !ejected — so none of this allocates.

#05

The Privy confirmation race

Privy’s signAndSendTransaction runs a hard-coded 10s websocket confirmation against a different RPC node than the one that broadcast. When it loses that race it throws a bare "confirmation timed out" with no signature attached — surfacing as a red error modal on a transaction that actually landed. The fix: pass optimisticBroadcast so it returns the signature the moment broadcast succeeds, then confirm in-house with 30s of getSignatureStatuses polling over the app’s own RPC.

#06

Routing around free-tier RPC limits

Alchemy’s free tier rejects getProgramAccounts outright and will not serve accountSubscribe / signatureSubscribe. Savora regex-detects an Alchemy URL and falls the scan endpoint back to the public devnet RPC, with a separate RPC_SCAN_URL override. A getProgramAccounts scan also skips any account whose byte length does not match the current Codama decoder — leftovers from an older program layout — rather than letting one bad decode take down the whole list.

STACK

Program
Anchor 0.32RustSolanaLiteSVM + Vitest (18 tests)
Client
@solana/kitCodama codegenNext.js 16React 19
Wallet & state
Privy embedded walletsTanStack QueryUSDC (Circle)
Interface
Tailwind CSS v4motionruntime-generated OG cards

OUTCOME

11
Instructions
29
Typed errors
12
Seats / circle
18
Program tests
  • Honesty is a shipped feature. The landing page publishes its own threat model: a block producer who controls the exact sealing moment could nudge the shuffle seed. Stated plainly rather than hidden — an acceptable trade for a circle of people who know each other.
  • The activity feed is round-grained, not minute-grained — the program does not timestamp individual contributions, so Savora does not invent a "2h ago".
  • Currently on devnet. The upgrade authority is not yet burned and a v2 redeploy is pending — circles created under the old program layout are not readable by the new client.
NEXT PROJECT
Callsign
STATUS: AVAILABLE FOR ENGAGEMENTS

Open to audits, contract work, and security writing.

Competitive & private audits · Secure Web3 contract work · Security writing. If you have a protocol going to production, or one that already is — let's talk.