No-loss prize savings · Zama FHE · gasless
Win the yield.
Never risk the principal.
Keep every number private.
A confidential PoolTogether. Deposit, and your amount is encrypted before it ever leaves your device. Each round, one depositor wins the pooled yield — picked onchain by fully-homomorphic randomness, weighted by deposit size. Only the winner ever learns the outcome. Withdraw your principal anytime, in full.
🔒Deposits, balances, winnings, odds, and the winner are all ciphertext onchain — never plaintext.
Why it's different
Public prize pool. Blind to everyone.
Encrypted by default
Every deposit is an ERC-7984 confidential transfer. Balances, the pooled total, each player's share and odds live onchain only as euint64 handles — decryptable by their owner alone.
The winner is drawn onchain
No offchain RNG, no oracle, no operator discretion. FHE.randEuint64 draws a ticket inside the EVM and settles it against encrypted balances — deposit-weighted, verifiable, blind.
No loss, no gas
You can only win. Principal is withdrawable in full at any time, clamped in encrypted math so a withdrawal never reveals a balance. Every action is gasless via an ERC-4337 smart account.
One round, end to end
How a round works
The full cycle runs onchain on Sepolia — every step a single gasless transaction from your smart account.
Faucet
Mint test USDC to your smart account to play with.
mint(account, 1,000e6)
Deposit
Approve → wrap to cUSDC → confidential transfer into the pool. The amount is encrypted client-side first.
confidentialTransferAndCall(pool, enc, proof)
Reveal
Decrypt your own balance and winnings with an EIP-712 signature. Nobody else can.
userDecrypt(handle)
Draw
The keeper triggers the round. A winner is chosen onchain over the encrypted balances.
draw() → FHE.randEuint64()
Claim
The winner — and only the winner — sees a non-zero prize and claims it confidentially.
claim() → confidentialTransfer
Withdraw
Take back your principal anytime, in full. Clamped in FHE so the amount never leaks.
withdraw(enc, proof) · no loss
Confidentiality design
What stays private — and what doesn't
Confidentiality is a design, not a slogan. Here is exactly what the chain can and cannot see, stated plainly.
CIPHERTEXT Hidden from everyone
- ›Your deposit amount — encrypted on your device before it's sent.
- ›Your balance & winnings — euint64 handles, only you hold the key.
- ›The pooled total — never a plaintext number onchain.
- ›Each player's odds & share — derived from encrypted balances.
- ›Who won — the prize lands as ciphertext; only the winner can decrypt it.
PUBLIC Visible by design
- ›The set of depositors — addresses that joined are onchain.
- ›The depositor count — the draw loops over it, so its length is observable.
- ›Timing — when deposits, draws and withdrawals happen.
- ›That a draw occurred — the event fires; the outcome inside it does not.
Leakage, acknowledged: membership and timing are the deliberate, documented trade-off — the minimum a permissionless onchain pool must expose to function. Every monetary quantity and the winner selection remain encrypted end to end.
The moat
A fair draw nobody can see
Picking a deposit-weighted winner over encrypted balances — without ever decrypting them — is the hard part. Here's the whole trick.
Randomness stays in the EVM
FHE.randEuint64() produces an encrypted random value onchain — never generated offchain, never revealed.
Weighted by deposit, exactly
A ticket r · total is tested against each player's encrypted prefix sum scaled by 2⁶⁴ — bigger deposits span more of the range, with no encrypted division and no rounding.
Exactly one winner, revealed to one
The first crossing wins; euint128 math avoids overflow. The prize is added as ciphertext, so the pool itself can't tell who won.
Built on
The stack
See it live
Try it yourself
The contract is deployed and running on Sepolia right now. Load the wallet extension and run a full round.
Run a round
- Open
chrome://extensionsand turn on Developer mode. - Load unpacked → select the extension's
build/folder. - Open the wallet → Prize Pool tab.
- Faucet → Deposit → Enable private view → Reveal → Draw → Claim → Withdraw.