Sortis is confidential prize-linked savings. You commit funds, you cannot lose your principal, the pooled yield is awarded as a prize on a schedule, and the size of your position is not visible to anyone but you.
The idea is old and the interesting part is not the lottery. It is that a prize-linked pool has to answer two questions at once: it must convince everyone the draw was fair, and it must show nobody how much anyone has. Those pull in opposite directions, because the usual way to prove a weighted draw was fair is to publish the weights.
Sortis publishes the shape of the draw and none of its contents. Anyone can check that the lot was drawn against a register root committed one block before any randomness existed, that the denominator it was reduced against carries a KMS signature, and that the descent ran to a leaf. What nobody can see is which leaf, including the person who won it.
One full cycle
- Commit. Your amount is encrypted in your browser, sent as a ciphertext handle with an input proof, and pulled by the pool with confidentialTransferFrom. The plaintext never leaves your machine.
- Wait. Weight is money multiplied by the hours it sat there, so a deposit made moments before a draw carries nothing. This is a property of the accounting rather than a lockup, and your principal stays withdrawable throughout.
- Draw. openDraw commits both register roots, the block and the reference hour. No randomness exists yet. In a later block drawLot produces the lot with FHE.randEuint64 and descends the register.
- Claim. Every depositor claims the same way. An encrypted comparison pays the drawn leaf and pays everyone else an encrypted zero, on identical code and identically shaped ciphertexts.
- Release. Principal is withdrawable at any time. An over-withdrawal is an encrypted no-op rather than a revert, for reasons the privacy model page sets out.
Why the draw is two transactions
If a single transaction both read the register and produced the randomness, whoever submitted it could see the state it was about to draw from and choose whether to send it. Splitting the two means the root is fixed and public before the lot exists.
drawLot refuses to run in the opening block, and refuses if either root handle moved in between. FHEVM handles are content-derived, so that second check is cryptographic rather than a promise: a register that changed cannot produce the handle that was recorded.
The yield source is a mock, and this page says so
On Sepolia the prize comes from a mock adapter with an admin-callable accrue, which books an amount, and a harvest that mints that much cUSDT into the draw contract. That is the whole of it. A draw opened when nothing has accrued has a prize of zero, which is honest rather than broken.
On mainnet the same ISortisYieldAdapter interface sits in front of an ERC-4626 vault. SortisDraw only ever calls harvest(to) and only ever treats the result as a public uint64, so swapping the source is a constructor argument and no contract change. The prize being public is what keeps that boundary clean: no part of the yield path touches a ciphertext.
Sortis has not been audited, runs on Sepolia only, and draws its prize from a mock. Nothing here should be treated as production infrastructure.