ruin matches each 500–1000× bet on SOL, BTC and ETH with one house and puts both stakes in the same Solana contract. Two lines are fixed at the fill: yours one division away, the house's k divisions away. The first line the DOVES price reaches ends the bet, whichever side it is on. Stop before that and the table is split where the price stands, along a straight line you can read before you open.
On YOLO, the house is one pool for every market, filled only by traders' losses, and nobody can deposit into it [4]. A move of 0.002% or less in your favour pays nothing, and a bigger one is cut, most when it is small [5]. A win the pool cannot cover waits in a queue [4]. Here anyone can post a house, nothing is zeroed near the fill, the house's cut is one number printed on it, and everything a bet can win is in the house's vault before it fills.
This page is the manual and the record: how to bet and how to run a house, the formula and the code that settle a table, the values the program holds and every call it takes, what it was measured against, and what you are trusting.
1The window
A bet on ruin is a contract between two wallets. A, the trader, puts up margin m. B, the house, puts up k·m. Both amounts sit in the contract from the moment it fills. Their sum, (1+k)·m, is the table.
Two lines are fixed at the fill. A's line is 1/L from the fill price, against A. B's line is k/L from it, in A's favour. Measured in divisions of 1/L, A's line is one division away and B's line is k. At 1000× and k = 10, a long has its lines at −0.1% and +1%. Whoever's line the price reaches first gives up the table.
runreplay #2243 · YOLO fill, Binance SOL/USDT 1 s moves · 2026-10-15 14:03:19 UTC
SOL long, margin $10.00, ×1000, against k = 10, e = 7%. Filled at 101.74. A's line 101.6339, B's line 102.7530. The price fell to 101.66 at 10 s, turned, and reached B's line after 378 s. A takes $103.00. B keeps $7.00.
The fill is YOLO's own (4TB9…U7bk, slot 457,263,940). The moves after it are Binance 1 s closes, because the bet comes from the YOLO sample of 15 Oct. ruin itself settles only on DOVES (section 6).
2Using it
You need a Solana wallet in this browser, USDC for the margin or the stake, and a little SOL for network fees and account rent. The page builds each transaction; your wallet signs it and sends it.
Bet. On bet, pick a market, a side, a margin from $1.00 and a leverage of 500–1000×, then the house you bet against. Its window is drawn before you ask. Request bet moves the margin into that house's vault and asks for a fill, which comes 2–5 s later (section 5). From the fill the two lines stay where they are. Stop asks for the split at the price 2–5 s later (section 3). A line, a stop or 72 hours ends the table, and the transaction that ends it pays you. Your bets and their receipts are on you, and every ended table is on tape.
House. On house, pick a market, a stake, k, e and the sides you take, then Post house. The stake, and any $RUIN the house locks (section 8), moves into the house's vault. A house takes one bet at a time, up to its stake divided by k. Between bets you can top up, withdraw or close it on you, and pause stops new bets at any time.
figone table, from the ask to the payout · schematic, drawn on replay #2243
(1) Ask: the margin moves into the house's vault, beside the house's k·m. (2) Fill: 2–5 s later the DOVES price fixes the two lines. (3) Live: the split moves with the price, and you can stop at any point. (4) End: a line, a stop or 72 hours; the transaction that ends the table pays A, and the rest stays with the house.
Rent. A request pays the rent of its table account, and the trader gets it back when the table ends or is refunded. Posting a house pays the rent of the house and its vaults, and closing the house returns it.
3Stopping early
A can stop at any time. The table is split where the price stands between the two lines.
f = (1 + side·L·(p − p₀)/p₀) / (1 + k) clamped to [0, 1]
A = f · (m + k·m·(1 − e))
B = (1 + k)·m − A
f is the price's place in the window: 0 on A's line, 1 on B's. At the fill f = 1/(1+k), so a $10 long against a $100 house that stops with nothing moved gets $103 / 11 = $9.36.
runreplay #2243, stopped at 80 s
Price 101.90, 1.57 divisions up. f = 0.2337. A gets $24.07, B gets $85.93. (The same bet on YOLO closed at 80 s for $22.18.)
ruin · mainnet · receipt H0·0000 · 2026-10-26 20:55:44 UTC · SOL long · $5.00 · 1000× · k 10 · e 7% · filled 100.2396 · stopped at 100.2537, f 0.1037 · A $5.34 · B $49.66 · tx 53iy…Tc6p
// programs/ruin/src/math.rs
pub fn split(side: Side, lev: u16, m: u64, k: u8, e: u16, p0: u64, p: u64) -> Result<Split> {
require!(p0 > 0, RuinError::InvalidValue);
let movement = i128::from(p)
.checked_sub(i128::from(p0))
.and_then(|v| v.checked_mul(i128::from(lev)))
.ok_or(RuinError::Overflow)?;
let num = match side {
Side::Long => i128::from(p0).checked_add(movement),
Side::Short => i128::from(p0).checked_sub(movement),
}
.ok_or(RuinError::Overflow)?;
let den = i128::from(p0)
.checked_mul(i128::from(k).checked_add(1).ok_or(RuinError::Overflow)?)
.ok_or(RuinError::Overflow)?;
let c = claim(m, k, e)?;
let clamped = num.clamp(0, den) as u128;
let a = if num >= den {
narrow(c / 10_000)?
} else {
narrow(
c.checked_mul(clamped).ok_or(RuinError::Overflow)?
/ (den as u128)
.checked_mul(10_000)
.ok_or(RuinError::Overflow)?,
)?
};
Ok(Split {
a,
b: sub(mul(add(1, k.into())?, m)?, a)?,
f_e9: narrow(
clamped
.checked_mul(1_000_000_000)
.ok_or(RuinError::Overflow)?
/ den as u128,
)?,
line: if num <= 0 {
Some(End::TraderLine)
} else if num >= den {
Some(End::HouseLine)
} else {
None
},
})
}
4The edge
B's price is one number, e, printed on the house. Whatever happens, B keeps k·m·e of its stake: at its own line B loses k·m·(1 − e) and keeps the rest. Without e, A's split would be f·(1+k)·m, its place times the table. With e, A gets that less k·e/(1+k) of it, the same share at every price: 6.4% at k = 10 and e = 7%.
The split is one straight line from A's line to B's, set before you open. Nothing is zeroed near the fill, and the share taken does not change with the move. Every price in the window pays an amount you can read before you open.
figXY · what A gets at every price · margin $10
At the fill: A $9.36, B $100.64. YOLO's formula on the same move: $9.80.
Mint is ruin's split, one straight line from A's line to B's, set before you open. The dashed line is YOLO's published formula on the same move [5]. Past B's line the table has ended, and the rest of the move is given up. Pick k, e and the leverage and move across the window: it is the program's rule applied to the numbers you pick, not a forecast.
5Fills
You ask, and the fill comes later. A bet and a stop both fill at the DOVES value read in a window that opens at a random slot, 2.2 to 5.0 seconds after the request lands, and stays open 1.1 seconds. That value must be newer than the one on chain when you asked.
The delay is read from the hash of the slot your request lands in, so nobody, you included, knows it before the request lands. A fill at a price nobody can pick in advance means nobody opens or stops on a DOVES update they have already seen.
figone request, slot by slot · DOVES SOL writes of 20 Oct 2026, read from chain
A request lands in slot r. Its window opens d slots later, d between 10 and 23 and read from the hash of slot r, and stays open 5 slots. A fill reads the DOVES value of its own slot, which must be newer than the one at r, so a value written before the window opens counts. The ticks are every write to the DOVES SOL account in these slots, read from chain; the window drawn is one possible d. If no fill lands in the window, the margin can be refunded from slot r + 28 (section 7).
6The lines and the oracle
A line is crossed only by a DOVES value written on chain. The keeper reads the DOVES account of each market and calls settle for any table past a line. settle is permissionless: anyone can call it with the same account.
SOL FYq2BWQ1V5P1WFBqr3qB2Kb5yHVvSv7upzKodgQE5zXh
BTC hUqAT1KQ7eW1i6Csp9CXYtpPfSAvi835V7wKi5fRfmC
ETH AFZnHPzy4mvVCffrVwhewHbFc93uTHvDSFrVH7GtfXF1
price: u64 at offset 168, 8 decimals
// keeper/src/keeper/engine.ts
if (
price &&
price.publishTs >= t.fillPublishTs &&
r.split({
side: t.side,
lev: t.lev,
m: t.m,
k: t.k,
eBps: t.eBps,
p0: t.p0,
p: price.price,
}).line
)
return { kind: "settle-line" };
runDOVES what-if · recorded 2026-10-20 · slot 459,115,379
SOL long at 1000×, k = 10, filled 05:32:27 UTC at 104.6820. A's line 104.5773, B's line 105.7288. At 05:36:19 UTC the DOVES value 104.5673 (slot 459,116,446) crossed A's line, 3 min 52 s after the fill. The table goes to B.
ruin · mainnet · receipt H1·0000 · 2026-10-26 21:01:01 UTC · SOL short · $5.00 · 1000× · k 10 · e 7% · filled 100.1797 · the trader's line at 100.2877, slot 461,808,758 · A $0.00 · B $55.00 · tx 54jM…e6Hs
7When money waits
No fill. A request fills only inside its window, at a DOVES value newer than the one on chain when it landed. If none comes, the request can be refunded once the window has passed: the margin and the table's rent go back to the trader. The keeper does it, so can anyone, and your bets on you show a Refund key.
A missed stop. A stop fills only inside its own window, at a value newer than the one when it was asked. If it misses, the table stays open and you can ask again. A line reached meanwhile still ends it.
72 hours. A table still open 72 hours after its fill is split at the DOVES price then, by the same formula. When more than one applies, a line comes before a stop, and a stop before this.
No keeper. fill, refund and settle need no key of ours. If the operator's keeper stops, any wallet can send them with the same accounts, using the client in the source (section 12).
An old price. A DOVES value older than 30 s is refused. A request, a fill, a stop or a settle waits for a newer one.
8Houses and $RUIN
A house is a stake waiting for a bet: a market, the stake, k, e and the sides it takes. It takes one table at a time, and a bet of at most its stake divided by k. While a bet is asked for or open against it, a house cannot close, add stake or withdraw. It can stop taking new bets at any time.
A house with a stake up to $100 needs no token. $RUIN is not set yet; until it is, no house holds more than $100.
So the size of the bets a house can take is counted in $RUIN. At k = 10, a bet above $10 needs a house above $100, and until $RUIN is set no house is that big.
In the YOLO sample of 14–19 Oct (49,571 opens), the bets above $10, which a $100 house at k = 10 cannot take, were 52.0% of the count and 97.6% of the margin. Without the ten largest wallets they were 48.2% and 93.0%, and 1,236 of the 2,042 wallets placed at least one.
fig49,571 YOLO opens, 14–19 Oct, by margin · the house each one needs at k = 10
≤ $10a house up to $100 · no token
$10–100a house up to $1,000 · $RUIN once it is set
$100–1,000a house up to $10,000 · $RUIN once it is set
> $1,000a bigger house · $RUIN once it is set
Gold is what a $100 house at k = 10 cannot take: 52.0% of the bets and 97.6% of the margin.
9The ruin problem
ruin is named after it. Two players stake what they have on one walk, and whoever runs out first is ruined. On ruin, A stakes one division, B stakes k, and the walk is the DOVES price. The problem is older than the market. Huygens printed it in 1657 as the fifth problem of De Ratiociniis in Ludo Aleae [1]: two players with twelve pieces each play until one holds all twenty-four. Neither is the house. Pascal and Fermat had settled in 1654 how to split a game stopped early [2].
"A and B taking 12 Pieces of Money each, play with 3 Dice on this Condition, That if the Number 11 is thrown, A shall give B one Piece, but if 14 shall be thrown, then B shall give one to A; and he shall win the Game that first gets all the Pieces of Money."
Huygens, problem V, in the English of 1714 [1]
If the price is a martingale and touches lines exactly, the chance that B's line comes first is f [3]. At the fill that is 1/(1+k). A's share f·claim is then what A's position is worth at that moment, and by optional stopping its expected value is the same whenever A stops.
The contract leaves the model in three places: DOVES is read at discrete moments, the fill uses a later price, and the stop uses a later price.
10Measurements
reached B's line first9.33%the model: 9.09%
margin back, outside the top ten94.04%under YOLO's formula: 88.89%
ruin pays more, 1000× at k = 10+0.0049% to +1%of the price, your way
Held to a line. 25,724 real 1000× bets (YOLO, 14–19 Oct) held from their real open until a line at k = 10, on Binance 1 s highs and lows, ended within 24 h: 2,401 reached B's line first, 9.33%. The model says 1/11 = 9.09%. With the direction flipped, 10.47%.
Against the current formula. 48,496 real 500–1000× bets on the same paths and hold times, long and short averaged, beside YOLO's published fee formula as read on 25 Oct [5]. Wallets outside the top ten got back 94.04% of their margin under ruin's split and 88.89% under that formula. The top ten closed 61% of their margin inside a 10% move, after a median 28 s: 93.6% here, 95.5% there.
Where each pays more. A $10 long at 1000× against a house at k = 10 and e = 7%, beside the same formula on the same move: from +0.0049% up to B's line at +1%, ruin pays more ($14.05 against $12.50 at +0.05%, $103 against $95.48 at +1%). From −0.069% to +0.0049% it pays less ($9.36 against $9.80 with nothing moved). The table ends at B's line, so the house's k sets how far a bet can run: at k = 10 that is +1% and $103, and held on YOLO the same entry pays more from about +1.085%. Against a $1,000 house at k = 100 the line is at +10%. YOLO's payout stops at $499.80 from about +5.58%, at its cap of 50 times the collateral; this bet goes on to $940 at that line.
Conditions: Binance 1 s paths, original hold times, and YOLO's published values as read on 25 Oct 2026: nothing for a move of 0.002% or less, 0.9·P·d²/(d + c) above it, profit capped at 50 times the collateral, a 2% win fee and a 0.002% close fee [5]. The payout comparison leaves out fill prices and network fees. None of this is an expected return.
11Values and calls
These values are read from the program on Solana mainnet. A house keeps the $RUIN terms it was posted with and a table keeps the timing it was asked with, so a change applies only to new houses and new tables.
| value | now | what it means |
| markets | SOL · BTC · ETH | each read from its DOVES price account (section 6) |
| margin | USDC, from $1.00 | moved into the house's vault by the request |
| leverage | 500–1000× | one division is 1/L of the fill price |
| k | 1–100 | the house's line in divisions; the house puts up k·m |
| e | 0–10% | the house's edge, printed on the house (section 4) |
| fill | 2.2–5.0 s after the request, open 1.1 s | at a DOVES value newer than the request's |
| stop | 2.2–5.0 s after the stop, open 1.1 s | a stop that misses can be asked again |
| price age | at most 30 s | an older DOVES value is refused |
| table ends | a line, a stop, or 72 hours after the fill | in that order when more than one applies |
| no-token stake | up to $100 | a bigger house locks $RUIN |
| $RUIN | not set yet | until it is, no house is above $100 |
The program counts slots, not seconds: a delay of 10–23 slots, a fill window of 5 and a stop window of 5. Seconds on this page are at the recent slot length, 218 ms.
| call | who | what it does |
post_house | anyone | Moves the stake, and any $RUIN lock, into a new house's vault. |
top_up · withdraw | the house's owner, between tables | Adds or takes stake and $RUIN. The lock must still cover the stake. |
set_accepting | the owner, any time | Stops or resumes new bets. An open table plays out. |
close_house | the owner, between tables | Returns the stake, the lock and the rent, and closes the house. |
request_open | a trader | Moves the margin into the house's vault and asks for a fill. The house takes no other bet until this one ends. |
fill_open | anyone | Fills the request inside its window at a newer DOVES value. |
refund_open | anyone, after the window | Sends an unfilled margin and the table's rent back to the trader. |
request_close | the trader | Asks for a stop. A stop that missed its window can be asked again. |
settle | anyone | Ends a table at a line, at a filled stop, or after 72 hours, and pays the trader in the same transaction. |
set_params | the admin | Changes, for new houses and tables, the minimum margin, the no-token stake, the $RUIN per dollar, the delays and windows, and the oldest DOVES value accepted. |
set_feed | the admin | Points a market at another DOVES account for the same asset. |
set_ruin | the admin, once | Sets the $RUIN mint. |
init | the upgrade authority, once | Fixes the settlement mint, the admin, the leverage, k and e ranges, and how long a table may stay open. |
12Trust assumptions
- ruin is one program on Solana.
- The admin key can change, for new houses and new tables only, the minimum margin, the stake a house may hold without $RUIN, the $RUIN per dollar, the fill delay and windows, and how old a DOVES value may be. It cannot move any vault or lock. The admin is set when the program is initialized, and no call changes it.
- DOVES is a price account. Its terms are not published.
- The keeper is run by the operator. Anyone can run another, because fill, settle and refund are permissionless.
- This build links no source. The program's source is published with its mainnet deployment.
13Problems
Problem 1. A $10 long at 1000× against a $100 house. Nothing moves and A stops. What does A get?
f = 1/11. A = 103/11 = $9.36. B = $100.64.
run · DOVES what-if · 2026-10-20 05:32:27 UTC: at the fill (104.6820) the table read A $9.36, B $100.64.
Problem 2. The price reaches B's line. What does B keep?
k·m·e = $7.00. A takes $103.00.
run · replay #2243 · held 378 s after 14:03:19 UTC, 15 Oct: A $103.00, B $7.00.
Problem 3. Can B lose more than its stake?
No. The most B can lose on a table is k·m·(1 − e), $93 here, and it is on the table before A bets.
Problem 4. Why not fill at the price on the screen?
Because then whoever sees a DOVES update first could trade on it. A random 2–5 s delay takes that away (section 5).
Problem 5. What if nobody calls settle?
The keeper calls it, and so can anyone. A table that is never settled at a line is split at 72 hours (section 7).
Problem 6. You ask, and no fill comes in the window. Where is your margin?
In the house's vault until the window has passed. Then a refund, by you, the keeper or anyone, sends it back with the table's rent (section 7).
Problem 7. A $100 bet at 1000× comes in against houses at k = 10. Which can take it?
One with at least $1,000 of stake. Until $RUIN is set no house holds more than $100, so none can yet (section 8).
References
- [1] C. Huygens, De Ratiociniis in Ludo Aleae, 1657, problem V. In English as The Value of All Chances in Games of Fortune, 1714.
- [2] B. Pascal and P. de Fermat, correspondence of 1654.
- [3] W. Feller, An Introduction to Probability Theory and Its Applications, vol. 1, 3rd ed., Wiley, 1968, ch. XIV, "Random Walk and Ruin Problems".
- [4] Jupiter, YOLO payouts and the House, read 25 Oct 2026.
- [5] Jupiter, YOLO profit adjustment and fees, read 25 Oct 2026.
- [6] DOVES price program,
DoVEsk76QybCEHQGzkvYPWLQu9gzNoZZZt3TPiL597e. Its terms are not published.