Architecture
Curtain is one Anchor program on Solana plus five Arcis circuits that the Arcium cluster runs.
flowchart LR
B[Browser: encrypts side and amount] -->|place_bet| P[Curtain program on Solana]
P -->|queue computation| A[Arcium cluster]
A -->|signed callback| P
Y[Pyth Solana Receiver] -->|PriceUpdateV2| P
P --> V[(Vault: one token account)]
Accounts
| Account | Seeds | Holds |
|---|---|---|
| Config | ["config"] | authority, mint, treasury, pause flag, defaults, public money flow |
| Vault | ["vault"] | every deposited token |
| Purse | ["purse", owner] | sealed balance, the owner's x25519 key, the operation it waits on |
| Market | ["market", feed_id, strike, expiry] | question, pools, two batches, outcome, payout ratio |
| Position | ["position", market, owner] | sealed stake on both sides, latest ticket seat, claimed flag |
A market keeps two batches in a ring: batch i lives in batches[i % 2], so one can be tallied while the next takes bets.
Circuits
| Circuit | Inputs | Reveals |
|---|---|---|
credit | purse, deposit amount | nothing (new sealed balance) |
place_bet | purse, position, sealed bet, minimum ticket | accepted (one bit) |
tally | 8 tickets, which seats are live | the batch's Yes sum and No sum |
settle | position, outcome, payout ratio | nothing (payout goes into the purse) |
withdraw | purse, amount | whether the purse held the amount |
Every output is re-encrypted under the next nonce even when nothing changed, so no nonce is used twice for two different values.
One computation at a time per purse
A purse records the operation it is waiting on (Credit, Bet, Settle, Withdraw). While it waits, it cannot start another, because two computations reading the same ciphertext would overwrite each other's result. Every callback checks that it answers the computation the account is waiting for (StaleCallback otherwise).
Settlement path
settle_market reads the first print at or after the deadline, computes the fee and the payout ratio in public (pot after fee divided by the winning pool, as an integer part and a 64-bit fraction) and stores them on the market. Each claim then runs settle inside the cluster with that public ratio and the sealed stake.