No. 11Solucky
Who can touch the pot
Alex wrote about how Solucky draws a winner. This is the other half: where the players’ money sits until then, who can move it, and who cannot.
Vincent Porret
Co-founder and CEO · · 4 min read
Alex wrote about how Solucky draws its winning ticket. In risk, the draw is only half the question. The other half is custody: where the money sits between the first ticket and the payout, who can move it, and what stops them. In a bank, the answer is a set of controls and the people who sign them off. In Solucky 1.0, it is a program of eight instructions, and anyone can read every one.
The pot is the round
There is no vault and no house wallet in the middle. Each UTC day is a round, and the round’s own account holds its pot, on top of the small balance Solana requires for an account to exist. A ticket is a plain transfer from the buyer straight into that account. The account belongs to the program, and no person holds a key that can sign for it, so lamports only leave it when one of the program’s instructions says so.
Of the eight, two take money out of a round, and the code is short enough to quote. claim_prize pays the winner:
let fee = fee_for(round.pot, round.fee_bps);
let payout = round.pot - fee;
ctx.accounts.round.sub_lamports(payout)?;
ctx.accounts.owner.add_lamports(payout)?;collect_fee pays the house, and nothing else takes lamports out of a round. A third instruction, close_ticket, only hands a ticket account’s own rent back to the player who paid it, once the round is settled.
Anyone can send it, one person gets it
claim_prize needs no signature. The winner, a friend or a script can send it, but the program checks that the account it pays is the owner written on the winning ticket, and that the ticket belongs to this round. Send it with any other address and it fails. The fee works the same way: anyone can send collect_fee, and it pays only the treasury address in the program’s config. There is no deadline to claim either. An unclaimed prize waits in its round, and nobody else can take it.
The order matters as much. collect_fee only runs on a settled round, so the fee stays in the round until the winner has been paid. A treasury that is misconfigured, or just slow, cannot hold up a prize.
The house is paid after the winner, never before.
What the operator can change
The program has an authority, and today that is us. It can change the ticket price, the house fee, the highest ticket number and the treasury address, and it can pause sales. Each of those powers stops somewhere. The fee is capped in the code at 10%, MAX_FEE_BPS: u16 = 1_000. A round copies the price and the fee from the config when its first ticket is sold, and keeps them, so a change only reaches rounds that have not opened yet, never a pot already filling. Buyers also send the most they accept to pay, so a price raised between the screen and the signature makes the purchase fail instead of charging more.
A pause stops ticket sales and nothing else. Closing, drawing and claiming stay open, and a comment in the code gives the reason: so a pause can never lock a pot. The one lever the authority keeps over money is the treasury address: a fee goes to whatever treasury is set when it is collected. What it does not have is just as plain. No instruction sends a pot anywhere but to its winner, or a fee anywhere but to the treasury.
Rounding and overflow
The fee is rounded down, so a fraction of a lamport goes to the winner, not to the house. The ticket count and the pot are added with checked arithmetic, and the program is built with overflow checks on, so an overflow aborts the transaction instead of wrapping round. And tickets cost at least 0.001 SOL: with the fee capped at 10%, a prize is always above the minimum balance an empty wallet needs on Solana, so it can always be paid.
What the code does not cover
Two limits sit outside the instructions. The program is upgradeable, so whoever holds its upgrade authority could replace the code, and every guarantee above holds for the code deployed now. Our README says so: an upgradeable program is only as trustworthy as its upgrade authority, and the way out is a multisig, or making the program immutable once it is stable. And as Alex wrote, the draw is fair against players, not against validators. That is why the public app runs on devnet, with test SOL, and why our security policy says not to run real-money pots on this draw.
In a bank, controls like these live in a policy document. Here they live in the code, and they are checked on every transaction.
Vincent

