How it works
Zorrito
The no-loss lottery on Stellar.
How it works
Your deposit goes straight into Blend and starts earning at once. There is no minimum. Every dollar, and every second it stays in, adds odds. If you hold XLM or USDT0, the app swaps them for USDC at the door. (The testnet pool uses XLM.)
Everyone's capital is lent on Blend, Stellar's lending market. The interest borrowers pay accumulates as the prize.
When the week is up (10 minutes in the test pool), the round closes and the draw fires automatically. The prize and everyone's odds are frozen at that instant.
The contract fixes in advance which drand random number will decide: one that does not exist yet. When drand publishes it, the contract verifies the signature before picking.
The whole prize goes to the winner's wallet in the same transaction. Everyone else keeps exactly what they put in, and a new round is already running.
๐ฏ How odds are calculated
Your weight in the draw is how much you put in, for how long it stayed in during the round. It is the same proportion in which your money earned the prize: whoever contributed more to the yield has more chances to take it.
Example: two people with 100 USDC. One joins on Monday, the other on Saturday. The first has 6 times the odds, because her money earned 6 times more. Jumping in at the last minute with a lot of money barely counts, and that is on purpose: there is no way to "buy" the draw at the last moment.
Withdrawing does not erase what you already accrued. If you had money in for three days and take it out, those three days still count for that round's draw.
๐ฅ The daily streak
Once a day (UTC) you can mark "I saved today". Every day in a row adds weight to the current round, and each day adds more than the last:
Since 1+2+โฆ+7 = 28, seven days in a row add exactly one full round of weight: whoever marks every day doubles their odds compared to someone with the same money who marks none. Skipping a day goes back to day 1. The bonus belongs to the round in which it is marked; the next round starts from zero, like everything else.
| Days in a row | Extra weight accumulated | Odds vs. not marking |
|---|---|---|
| 1 | 3.6 % of a round | ร1.04 |
| 3 | 21 % | ร1.21 |
| 5 | 54 % | ร1.54 |
| 7 ๐ | 100 % | ร2 |
๐ค Referrals
Your invite link is your address. Whoever joins through it, on their first deposit, declares you as their referrer, and from then on you weigh as if you held 10 % of their capital, for as long as they are in. If they withdraw, the bonus goes down with them.
The cap at half of your own capital is what makes inventing referrals pointless: with no money of your own in, there is no bonus, and with money in, the bonus is never worth more than half of it. Bringing friends adds up; bringing empty accounts does not.
๐ฒ How the randomness works
Picking a winner on-chain is hard: the chain is deterministic and public, so any value known at draw time can be influenced by whoever builds the block. Zorrito uses nothing from the chain as a source of randomness. It uses drand, a public randomness beacon run by about 20 organisations (Cloudflare, Protocol Labs, EPFL, Kudelski, among others) that publishes a signed number every 3 seconds.
When the round closes, the contract computes which drand round corresponds to 10 minutes in the future and stores it. At that moment nobody, not even drand, knows what value it will have. It also freezes the prize, the total weight and an accumulated entropy from all the round's deposits.
When drand publishes that round, the signature is brought to the contract. The contract verifies with BLS12-381, inside Soroban itself, that the signature is valid for that exact round and for the drand public key it has stored. If it does not verify, there is no draw. If it does, the seed is:
winner = whoever lands on seed mod total_weight
Approaches compared
| Approach | Who can influence it | Status |
|---|---|---|
| Ledger timestamp or hash | The validator building the block | Rejected |
| Commit-reveal with a keeper | The keeper, by withholding the reveal | Rejected: liveness |
| VRF oracle | The oracle operator | None on Stellar |
| drand + BLS verification on-chain โ | Nobody short of a drand majority | What runs today |
BLS12-381 verification inside the contract is possible because Stellar added it as a native host function in Protocol 22. Zorrito uses it to verify drand quicknet signatures: a pairing_check between the signature, the message hash, the G2 generator and the group public key.
๐ค Nobody has to be there
The draw fires on its own when the timer runs out: a keeper running as a serverless function closes the round and brings the drand signature, and so does every visit to the app that finds an expired round or a pending draw. Both actions are permissionless: there are no roles or privileged keys, and whoever performs them only pays the fee. If the keeper disappears, the pool does not get stuck: the first person to open the app unblocks it.
And if drand stopped publishing, the only thing that would not happen is the draw. Capital can still be withdrawn, at any time, with or without a pending draw.
๐งช Try it without waiting a week
The pool on the home page runs on mainnet with weekly rounds. To watch the whole cycle in minutes there is a test pool on testnet, with 10-minute rounds and test XLM: stellar.zorrito.app/test. Same contract, same Blend (its testnet pool) and same drand. Free testnet XLM from the Stellar Laboratory, or directly with Cosmos Wallet, which funds a testnet account on creation.
๐ Blend: where the prize comes from
Blend is Stellar's lending protocol. The pool deposits everyone's capital as Supply in a Blend v2 pool (the USDC reserve of the Fixed pool on mainnet; XLM on TestnetV2 on testnet): a position that lends and earns interest but is not used as collateral, so it carries no debt and cannot be liquidated.
- The interest is paid by borrowers in the pool. The APY the app shows is what Blend is paying right now.
- The contract talks to Blend on its own, through its own adapter. Nobody holds the key to that position: only the pool can deposit and withdraw.
- The prize is the difference between what is in Blend and the capital deposited. If the pool earned nothing, the prize is 0 and nobody loses.
๐ Built for a million accounts
Drawing among many participants with different weights is expensive if you have to walk through them. Zorrito keeps the weights in a Fenwick tree over the contract's storage, with room for 220 accounts (just over a million). Depositing, withdrawing and drawing always touch ~20 entries, whether there are 2 participants or 1,000,000.
Time is handled with algebra, not loops: each account's weight is deposit ร T โ b, where b is adjusted on every move. That way everyone's weight advances with the clock without anyone having to update it.
๐ Tech stack
| Contracts | Rust + Soroban (soroban-sdk 27). Three contracts: the pool, the Blend adapter and a mock yield source for tests. 60 tests, including BLS verification with our own keys, the streak, referrals, Blend's rounding and the pool operating against Blend's real bytecode. |
| Randomness | drand quicknet (bls-unchained-g1-rfc9380), verified on-chain with Stellar's BLS12-381 host functions. Point decompression on the client with @noble/curves. |
| Yield | Blend v2: the USDC reserve of the Fixed pool on mainnet, the XLM reserve of TestnetV2 on testnet. Integrated with contractimport! of the official WASMs. |
| Currency swap | To join with XLM or USDT0: simultaneous quotes from the Soroswap router (simulating router_get_amounts_out) and the classic DEX (Horizon's /paths/strict-send), then a swap through whichever pays more, with swap_exact_tokens_for_tokens or a pathPaymentStrictSend. In the user's wallet, never in the pool. |
| Web | Next.js 16, Tailwind 4, @stellar/stellar-sdk 17, Stellar Wallets Kit (Freighter, xBull, Lobstr) plus a custom module for Cosmos Wallet. Deployed on Vercel. |
| Keeper | Serverless function on Vercel (/api/keeper), triggered by cron and by every visit. Permissionless: anyone can run another one. |
๐ Contracts
View on stellar.expert โ
View on stellar.expert โ
View on stellar.expert โ
View on stellar.expert โ
View on stellar.expert โ
View on stellar.expert โ
View on stellar.expert โ
View on stellar.expert โ
View on drand โ
Open source: github.com/artugrande/zorrito. The tests, the deploy scripts and this very site are there.
๐ก๏ธ Security and risks, no makeup
Randomness comes from drand and is verified in the contract. There is no admin key in the pool: no role can pause it, change the draw or touch funds. The adapter has an admin that only serves to set, once, which contract is the pool.
Withdrawing needs no permission, has no penalty and works even with a pending draw, even if the keeper is down and even if drand stops publishing.
The capital is lent out. If the Blend pool has almost all of its liquidity borrowed, a withdrawal can fail until someone repays or deposits. This is the real risk behind "nobody loses": no capital is lost, but you may have to wait. Blend mitigates it with rates that rise with utilisation.
A bug in Blend affects the pool like any other lender. Blend v2 is audited and is the most used lending protocol on Stellar, but the risk is not zero.
If drand does not publish, there is no draw until it is back. Capital can still be withdrawn. drand has been publishing every 3 seconds for years with more than 20 operators.
Persistent entries expire unless someone extends them. The contract extends its own on every use; accounts idle for months need someone to extend them (anyone can). Automating it in the keeper is pending.
Built from scratch during the hackathon. The mainnet pool has a capital cap fixed in the contract precisely for this reason: it limits how much can be inside until an audit says it can be raised. Do not put in money you cannot afford to lose.
โ Questions
No. The draw only distributes the interest the pool earned. Your deposit is yours and you withdraw it whenever you want. The only thing that can happen is having to wait if Blend is short on liquidity at that moment.
Nothing, and you lose nothing. It is the same money you had, but with a shot at a prize every round instead of a small interest. If you prefer the guaranteed interest, Blend is right there.
Because weight is money times time, not just money. Someone with less money who joined earlier can weigh more. And even with more weight, it is a draw: more odds is not certainty.
Yes. Every draw emits an event with the drand round, the signature and the total weight. With that and the round's deposits, anyone can recompute the seed and the winner.
Nothing bad. It closes when the keeper or a visit to the app closes it, and until then the pool keeps earning. The draw is never lost, only delayed.
Because the prize comes from what people borrow, and on Stellar people borrow USDC: the USDC reserve of the Fixed pool is at 80 % utilisation and pays ~8 % a year to lenders; the XLM reserve sits at 0.1 % and pays 0 %. On testnet there is no easy USDC and XLM needs no trustline, so the test pool uses XLM. The contract is the same with any token Blend accepts.
Today you can join with USDT0 and it is swapped to USDC at the door. A pool that holds USDT0 directly needs Blend to lend it, because that is where the prize comes from. USDT0 arrived on Stellar in September 2026 and has no reserve in any Blend pool yet. The day it does, it is one more entry in the configuration: the contract does not know which token it is.
It does not apply. That minimum is for collateral, and the pool contract only checks it when a position has debt: it exists so that nobody leaves a loan with collateral too small to be worth liquidating. Zorrito only lends (non-collateral Supply) and never borrows, so the pool never evaluates that minimum on its position. Besides, the pool holds a single position on Blend that aggregates everyone's deposits: whoever puts in 0.50 USDC does not open their own position, they add to the pool's. Tested on mainnet with deposits under a dollar.
USDC and a little XLM for fees. If your wallet does not accept USDC yet, the app offers to add it with one tap: it is Stellar's trustline, done once. Freighter, Cosmos Wallet, xBull and Lobstr all work.
Yes, with both. Pick the currency next to the amount, the app gets quotes from the two places where swaps happen on Stellar, Soroswap and the classic DEX (the same one Freighter and Lobstr use), and shows you whichever gives more USDC at that moment. You sign twice: the swap and the deposit. The swap goes through your wallet, not through the pool: the pool receives USDC as always and your capital is in dollars from the first second. Up to 0.5 % less than the quote is accepted if the price moves between looking and signing; if it moves more, the whole swap fails and nothing happens. Withdrawals are always in USDC.
You can, but they add nothing: the referral bonus is worth at most half of your own capital, and with no capital it is worth zero.
