Skip to content

Marking the winner has to settle every open stake at once, or the ledger and the leaderboard disagree

A prediction platform for football fixtures where the operator marks a result once and every open stake, balance and standing resolves from that single action. A settlement that happens in two steps is a settlement that can happen halfway.

FulltimeThornevale League · Matchday 7$98.00walletDepositEM
Matchday 7Sat 09/19 · 1:47 PM5 fixtures openStakes close at kick-off · every pick settles when its result is markedThornevale League

Open fixtures

Prices update until kick-off
3:00 PMBrackenfield v Oxley WanderersFX-1187Open
Match result
Home2.00Draw3.30Away3.60
Both teams score
Yes1.80No1.95
Total goals 2.5
Over1.90Under1.85
3:00 PMPenrose City v Halden TownFX-1188Open
Match result
Home1.75Draw3.50Away4.40
Both teams score
Yes1.90No1.85
Total goals 2.5
Over2.05Under1.72
3:00 PMTarn Rovers v Esk UnitedFX-1189Open
Match result
Home2.60Draw3.10Away2.70
Both teams score
Yes1.72No2.05
Total goals 2.5
Over2.10Under1.70
5:30 PMCorvey Athletic v WexmoorFX-1190Open
Match result
Home2.30Draw3.20Away3.05
Both teams score
Yes1.85No1.90
Total goals 2.5
Over1.95Under1.80
5:30 PMMillbrook v Garrow FCFX-1191Open
Match result
Home3.40Draw3.40Away2.10
Both teams score
Yes1.78No2.00
Total goals 2.5
Over1.88Under1.88

Bet slip

1 selection
Match result · 3:00 PM

Brackenfield v Oxley Wanderers

Brackenfield to win2.00

Odds are fixed when the stake is placed

Stake
$15.00+$5+$10
Stake$15.00
Odds2.00
Wallet after stake$83.00
Return if right$30.00
Place stake · $15.00

Your stake leaves your wallet as one movement. When the result is marked, the return lands as another, in the same action that settles every stake on this market.

The shape of the work

Industry

E-commerce & Retail

Duration

18 weeks

Cooperation model

Dedicated team

Services
Platform engineeringPaymentsBack office
Integrations
Payment gateway APIsBank transfer reconciliationFixture data feedTransactional emailSMS notifications
Technologies
LaravelMySQLBladeRedisBootstrap
Team
1 Engagement lead2 Backend engineers1 Frontend engineer1 QA engineer

Client name withheld under NDA. Engagement details are shown to the extent our agreement permits.

The problem

The hard problem

Their previous system settled in stages. An operator marked a result, a job worked through the stakes, and balances updated as it went. When the job stalled (and it did stall), some users had been paid and some hadn't, the leaderboard showed a standing the balances didn't support, and support had no way to tell which half of the settlement had run.

A staged settlement can stop between stages, and the state it stops in can't be described: some users paid, some not, a leaderboard showing standings the balances didn't support, and no way to tell from the data which half had run. Balances were also a stored number that could be edited, so the repair for a bad settlement was to type a corrected figure, which erased the evidence.

One settlement action that either completes or doesn't happen. Marking a winner resolves every open stake on that market inside a single transaction, writes the wallet movements, and recomputes the standings from the stake records, never from a running total that can drift.

The solution

How the pieces fit

  1. 01

    Made settlement a single transaction, so a stalled job can't leave half the market paid

    Marking a winner resolves every open stake, writes the wallet movements and invalidates standings inside one transaction, so there is no state between.

  2. 02

    Recomputed standings from stake records every time, with no running total to drift

    Standings recompute from the stake records every time, so a failed job costs a retry instead of a reconciliation.

  3. 03

    Built the manual gateway as a first-class path, never a fallback bolted onto the automated one

    Manual deposits were roughly a third of volume, so treating them as an exception would have made a third of the ledger unauditable.

  4. 04

    Kept every wallet movement as an immutable row, so a balance is a sum and never an edit

    The wallet is an append-only ledger and the balance is its sum, so nothing writes a balance and a correction is a reversing entry, never an overwrite.

  5. 05

    Separated the operator who marks a result from the operator who approves a deposit

    The separation was the operator's own requirement and turned out to be what made a disputed settlement answerable at all.

Introduction

The system we were asked to build

The operator runs prediction contests on football fixtures. We built the platform: leagues and matches, the questions users answer, the wallet the stakes come out of, deposits through both automated and manual gateways, and the back office where results are marked.

An operator running prediction contests on football fixtures, whose previous platform settled markets in stages: an operator marked a result and a background job worked through the stakes. The engagement was commissioned after a weekend where that job stalled mid-market and support spent two days establishing which users had been paid.

Full-Stack Engineering

Settlement is atomic

Marking a winner resolves every open stake in one transaction. There's no state where half the market has been paid.

Marking a winner resolves every open stake on that market inside one database transaction: outcomes written, wallet movements recorded, standings invalidated. There's no state in which half the market has been paid. The transaction either completes or the market stays open, and that's the only acceptable arrangement when the alternative is a partially settled market and no way to tell which half.

What shipped
  • Every stake on a market resolved in one transaction
  • No partially-paid state can exist
  • Failure leaves the market open, never half-settled
Brackenfield v Oxley Wanderers · Match result

MKT-2291 · FX-1187 · full time 2–1 · marked by Priya Raman

Settled in one transactionStake, wallet or txn idSat 09/19 · 4:58 PM
Full timeBrackenfield 2–1 Oxley
Open stakes on the market10 $95.00 staked
Steps to settle1 mark the winner
TransactionTXN-40817Committed

Every open stake on this market

resolved by TXN-40817
StakePlayerAnswerOddsStakedOutcomePayout
S-88410J. OkaforBrackenfield2.10$10.00Won$21.00
S-88426M. BrandtBrackenfield2.20$5.00Won$11.00
S-88453R. CastellanoDraw3.30$8.00Lost—
S-88471T. NguyenBrackenfield2.05$20.00Won$41.00
S-88502A. WhitlockOxley Wanderers3.60$6.00Lost—
S-88519D. PruittBrackenfield2.25$4.00Won$9.00
S-88547L. HaddadBrackenfield2.10$12.00Won$25.20
S-88560K. SorensenDraw3.25$10.00Lost—
S-88588E. MarshBrackenfield2.00$15.00Won$30.00
S-88593V. IbarraOxley Wanderers3.60$5.00Lost—
10 stakes$95.00$137.20
Won6 stakes6 payout movements
Lost4 stakesoutcome written, no movement
Left open0 stakesnothing half-settled

Mark the winner

results.mark · OP-02
Brackenfield win6 stakesDraw2 stakesOxley Wanderers win2 stakes
One transaction wroteTXN-40817
10 stake outcomes6 payout movements · $137.20MKT-2291 closedStandings cache invalidated
Market closed · nothing left to settle

Audit trail

append-only
4:58:06 PMOP-02Marked Brackenfield win
4:58:06 PMsystemBEGIN TXN-40817
4:58:07 PMsystem10 outcomes, 6 movements, 1 market
4:58:07 PMsystemCOMMIT TXN-40817
4:58:07 PMsystemStandings cache dropped
On screen

The settlement desk after the winner is marked: every open stake on the market resolved by one transaction, six payouts each the stake multiplied by the odds it was taken at, none left open, and the audit trail from the mark to the commit.

E. Marsh · statement

Wallet W-10418 · 09/12 – 09/19 · every line a movement, the balance their sum

Export statementStake, wallet or txn idSat 09/19 · 5:20 PM
Balance$113.00sum of 10 movements
Stored balance columnNone derived on read
Editable movement rowsNone append-only
Player since03/02/2026

Movements

newest last · running sum
All · 10Deposits · 2Stakes · 3Payouts · 2Void returns · 1Corrections · 2
WhenMovementNoteAmountBalance
09/12 10:14 AMM-58820Deposit · card gateway · DEP-7581+$50.00$50.00
09/12 10:20 AMM-58831Stake · Penrose City v Dunmere · Penrose @ 1.80−$10.00$40.00
09/12 5:02 PMM-59204Payout · TXN-40561 · posted at 1.60+$16.00$56.00
09/13 9:41 AMM-59377Reversal of M-59204 · wrong odds−$16.00$40.00
09/13 9:41 AMM-59378Payout re-posted · $10.00 × 1.80+$18.00$58.00
09/16 11:05 AMM-60412Deposit · bank transfer · DEP-7688 · OP-04+$40.00$98.00
09/19 1:48 PMM-60955Stake · Brackenfield v Oxley · Home @ 2.00−$15.00$83.00
09/19 1:52 PMM-60961Stake · Tarn Rovers v Esk United · Draw−$6.00$77.00
09/19 4:12 PMM-61160Void return · TXN-40809+$6.00$83.00
09/19 4:58 PMM-61224Payout · TXN-40817 · $15.00 × 2.00+$30.00$113.00
Σ 10 movements$113.00

How the balance is read

never written
SELECT SUM(amount)FROM wallet_movementsWHERE wallet_id = 'W-10418'
Deposits+$90.00
Stakes−$31.00
Payouts & void returns+$52.00
Corrections+$2.00
Balance$113.00

Dispute DSP-318

Answered

“Penrose paid me $16. My slip said 1.80.”

StakeM-58831 · $10.00 @ 1.80
PaidM-59204 · at 1.60
FixM-59377 + M-59378
Answered byOP-05 · 09/13 9:41 AM
The wrong payout stays on the statement. A reversing entry cancels it and a new entry posts the right amount.

Balances are sums

On screen

One player's statement as support reads it: every line a movement and the balance a running sum, the query that derives it, and a disputed payout answered with a reversing entry and a re-post instead of an edit.

The wallet is an append-only ledger of movements, so a balance can always be traced to the events behind it.

The wallet is an append-only ledger of movements and the balance is their sum, so a figure can always be traced to the events behind it. Nothing writes a balance directly. A disputed amount is answered by showing the movements instead of opening an investigation, and a bug that produced a wrong entry is corrected with a reversing entry, so history is never overwritten.

What shipped
  • Append-only movement ledger; the balance is derived
  • Nothing writes a balance directly
  • Corrections are reversing entries, never overwrites

Manual deposits are first-class

A bank transfer approved by an operator produces the same ledger record as an automated one, differing only in who confirmed it.

A bank transfer approved by an operator produces the same kind of ledger record as an automated deposit, differing only in the confirmation source and the approver's identity. Treating manual money as a second-class path is how reconciliation breaks: the two drift, and only one of them gets trusted. Here there is one deposit concept with two ways of being confirmed.

What shipped
  • Manual and automated deposits produce identical ledger records
  • They differ only in confirmation source and approver
  • One deposit concept, so nothing drifts out of reconciliation
Deposits

Card gateway and bank transfer · one deposit record, two ways of confirming it

3 awaiting approvalStake, wallet or txn idSat 09/19 · 1:02 PM
Awaiting an approver3 transfers
Deposit paths2 card gateway · bank transfer
Ledger recordOne shape for both paths
Signed inOP-04 cannot mark results

Manual approval queue

bank transfers, matched to the bank line
DepositPlayerAmountBank line
DEP-774112:48 PMW. TanakaW-10466$25.00 Matched · FT 10466Awaiting
DEP-773912:31 PMH. LindgrenW-10390$60.00 Matched · FT 10390Awaiting
DEP-773411:58 AMP. OseiW-10301$100.00 Matched · FT 10301Awaiting

Credited this morning

one table for both paths
DepositPlayerSourceConfirmed byAmountMovement
DEP-7731S. QuinteroCard gatewayGateway callback+$30.00M-60318
DEP-7729L. HaddadCard gatewayGateway callback+$50.00M-60307
DEP-7726C. DelacroixBank transferOP-04 · O. Kessler+$40.00M-60294
DEP-7722F. MbekiCard gatewayGateway callback+$20.00M-60281
DEP-7719B. AchterbergCard gatewayGateway callback+$75.00M-60266
DEP-7715G. FarrowBank transferOP-04 · O. Kessler+$35.00M-60250

Approve DEP-7734

deposits.approve
PlayerP. Osei · W-10301
Amount$100.00
Bank line09/19 · FT 10301 · $100.00
Received11:58 AM · bank transfer
ApproverOP-04 · Owen Kessler
Approve and creditReject

The ledger row either path writes

wallet_movements
FieldDEP-7729DEP-7726
typedepositdeposit
wallet_idW-10251W-10212
amount50.0040.00
movement_idM-60307M-60294
deposit_idDEP-7729DEP-7726
confirm_sourcegatewaymanual
confirmed_bycallbackOP-04
confirmed_at11:40 AM11:24 AM
On screen

The deposits desk: bank transfers waiting on a named approver beside card deposits in the same table, and the ledger row both paths write, differing only in confirmation source and who confirmed it.

Thornevale League · standings

Recomputed from stake records at read time · no running total is kept anywhere

Recompute nowStake, wallet or txn idSat 09/19 · 5:01 PM
Last recompute5:01:12 PMafter TXN-40817
Running total keptNone counted from records
Read cacheDerived safe to throw away
Points rule3 per correct void scores 0

Table after matchday 7

top 12
#PlayerSettledCorrectVoidPoints
1TNT. Nguyen169127
2LHL. Haddad158124
=3EME. Marsh117121
=3JOJ. Okafor137121
=5POP. Osei126118
=5MBM. Brandt146018
=7KSK. Sorensen125015
=7DPD. Pruitt105015
=9CDC. Delacroix94112
=9RCR. Castellano114012
11AWA. Whitlock8309
12VIV. Ibarra7206

E. Marsh · 21 pts, from records

12 stakes
MDStake recordPts
MD7Brackenfield v Oxley · HomeWon+3
MD7Tarn Rovers v Esk United · DrawVoid0
MD6Penrose City v Dunmere · PenroseWon+3
MD5Halden Town v Corvey · Over 2.5Won+3
MD5Wexmoor v Millbrook · BTTS yesLost0
MD4Garrow FC v Tarn Rovers · DrawWon+3
MD1–3Earlier records, won × 3Won+9
MD1–3Earlier records, lost × 3Lost0
Σ counted just now21

Recompute log

cache is derived
5:01:12 PMRecomputed after TXN-40817
4:58:07 PMCache dropped by TXN-40817
4:13:02 PMRecomputed after TXN-40809 · points unchanged
4:12:58 PMRecompute timed out · retried, nothing to reconcile
4:12:44 PMCache dropped by TXN-40809
On screen

The leaderboard recomputed from stake records at read time, with no running total kept, one player's points traced to the records they were counted from, and a recompute that timed out simply retried.

Standings are derived

Leaderboards are recomputed from stake records, so there's no running total to drift when a job fails.

Leaderboards are recomputed from the stake records, because a running total is only ever as correct as the last job that updated it, and a failed job leaves a wrong number that nobody can tell from a right one. Recomputation is cached for reads, but the cache is derived and can always be thrown away and rebuilt.

What shipped
  • Standings recomputed from stake records, never accumulated
  • A failed job can't leave an undetectably wrong figure
  • The cache is derived and disposable by design

Split operator duties

Marking results and approving deposits are separate permissions, because one person holding both is how disputes become unanswerable.

Marking results and approving deposits are separate permissions that can't be held by the same operator account. One person able to both decide an outcome and approve the money against it makes every dispute unanswerable, whatever actually happened. The separation is what lets the platform show its work when challenged.

What shipped
  • Result marking and deposit approval are separate permissions
  • No single account can hold both
  • Disputes are answerable because the duties were split
Operators & roles

Permissions per operator account · marking results and approving deposits never meet

Invite operatorStake, wallet or txn idSat 09/19 · 5:30 PM

Operators

7 accounts
OperatorMark resultsVoid fixturesApprove depositsEdit fixturesRead walletsAnswer disputesManage operators
RARuth AdeyemiOP-01 · Admin
PRPriya RamanOP-02 · Results
MAMarcus AlveyOP-07 · Results
OKOwen KesslerOP-04 · Deposits
NSNadia SerranoOP-09 · Deposits
DCDana ChoOP-05 · Support
TBTheo BarrosOP-11 · Fixtures

Separation rule

not configurable per operator
results.markdeposits.approvecannot be held by the same account

Whoever decides an outcome can't also approve the money against it. A disputed settlement therefore always traces to two different people: the one who marked the result and the one who approved the deposit.

Checked onGrantRole editEvery action

Grant refused

09/19 · 9:14 AM
Request
PRPriya RamanOP-02
Add deposits.approve · requested by OP-01
Refused: conflicting permissionOP-02 holds results.mark. No account may hold results.mark and deposits.approve together. Nothing was changed.
Account after requestunchanged
Logged asAUD-55120

Who did what, today

audit log
RAGrant to OP-02 refusedOP-01 · operators.manage9:14 AM
OKApproved DEP-7715 · $35.00OP-04 · deposits.approve10:12 AM
OKApproved DEP-7726 · $40.00OP-04 · deposits.approve11:24 AM
TBEdited kick-off FX-1190OP-11 · fixtures.edit12:40 PM
PRVoided FX-1189 · 12 stakes returnedOP-02 · results.void4:12 PM
PRMarked MKT-2291 · Brackenfield winOP-02 · results.mark4:58 PM
DCAnswered dispute on W-10204OP-05 · disputes.answer5:18 PM
On screen

Operators and roles: marking results and approving deposits held by different accounts, a grant that would have put both on one operator refused, and the day's audit log naming who did each.

Process

Phase by phase

  1. Phase 1: Fixtures And Markets

    Leagues, Matches, Questions

    Modeled leagues, fixtures and the questions users answer against them, so a market belongs to a match and a match belongs to a league. That's what makes bulk settlement addressable at all.

    • Leagues
    • Fixtures
    • Question Markets
  2. Phase 2: The Wallet

    Movements, Not Balances

    Built the wallet as an append-only ledger of movements. A balance is the sum of its rows, so no code path can quietly set a number, and every figure on screen can be traced to the event that caused it.

    • Movement Ledger
    • Balance Derivation
    • Statement View
  3. Phase 3: Deposits

    Two Gateways, One Path

    Built the automated gateway and the manual one against the same deposit record. A manually approved transfer isn't a special case in the ledger; it differs only in who confirmed it and when.

    • Automated Gateway
    • Manual Approval Queue
    • Deposit Audit
  4. Phase 4: Settlement

    One Action, All Or Nothing

    Built the settlement desk. The operator selects the winning answer, the platform resolves every open stake on that market in one transaction, and the standings are recomputed from the resulting records.

    • Settlement Desk
    • Atomic Resolution
    • Standings Recompute
Tarn Rovers v Esk United · voided

FX-1189 · Abandoned 61′ · floodlight failure · every open stake on every question returned

Voided in one transactionStake, wallet or txn idSat 09/19 · 4:12 PM
FixtureAbandoned 61′
Open stakes12 across 3 questions
Returned$112.00at the amounts taken
TransactionTXN-40809Committed

Stakes returned

12 of 12 · none left open
StakePlayerQuestionAnswerTakenReturnedMovement
S-88402B. AchterbergMatch resultTarn Rovers$10.00+$10.00M-61140
S-88418J. OkaforBoth teams scoreYes$5.00+$5.00M-61142
S-88437C. DelacroixMatch resultEsk United$12.00+$12.00M-61144
S-88449F. MbekiOver 2.5 goalsOver$8.00+$8.00M-61146
S-88466T. NguyenMatch resultDraw$10.00+$10.00M-61148
S-88480H. LindgrenMatch resultTarn Rovers$4.00+$4.00M-61150
S-88495G. FarrowBoth teams scoreNo$6.00+$6.00M-61152
S-88511S. QuinteroOver 2.5 goalsUnder$15.00+$15.00M-61154
S-88534L. HaddadMatch resultEsk United$7.00+$7.00M-61156
S-88552P. OseiMatch resultTarn Rovers$20.00+$20.00M-61158
S-88571E. MarshMatch resultDraw$6.00+$6.00M-61160
S-88590W. TanakaBoth teams scoreYes$9.00+$9.00M-61162
12 stakes$112.00+$112.00

Void TXN-40809

results.void · OP-02
Abandoned 61′ · floodlight failureReferee report attached · 4:09 PM
12 stakes marked void12 return movements · $112.003 markets closedNo outcome written as won or lostStandings cache invalidated
COMMIT TXN-408094:12:44 PM

Standings

recomputed 4:13:02 PM

A void scores nothing either way, so the recompute from stake records leaves every player's points where they were.

T. Nguyen24 pts24 ptsunmoved
L. Haddad21 pts21 ptsunmoved
E. Marsh18 pts18 ptsunmoved
J. Okafor18 pts18 ptsunmoved
On screen

The failure path on the same machinery: an abandoned fixture voided, all twelve stakes across its questions returned in one transaction at the amounts they were taken, and standings unmoved.

What it carries now

1

Steps to settle a market

0

Partially settled markets

None

Balance rows that are editable

2

Deposit paths sharing one ledger

Steps to settle a market is one. Partially settled markets is zero by construction: the transaction either completes or the market stays open. Editable balance rows is none, because the wallet is append-only. Deposit paths sharing one ledger is a count, and describes manual and automated deposits producing the same record shape.

Client name withheld under NDA. Figures are approximate, drawn from the engagement’s own reporting.

What the architecture settled

  1. 01

    A settlement that runs in stages is a settlement that can stop between them, and the state it stops in is unexplainable.

    A staged settlement can halt between stages, and the resulting state is worse than wrong: it's undescribable. Nobody could say which stakes had been paid.

  2. 02

    Derive standings from the records instead of maintaining a total, and a failed job costs you a retry, not a reconciliation.

    Deriving standings makes a failed job cheap: rerun it. Maintaining a total makes the same failure a reconciliation nobody can perform confidently.

  3. 03

    Manual payment paths deserve the same ledger as automated ones. Treating them as an exception is what makes them unauditable.

    Treating manual deposits as an exception is what makes them unauditable. Giving them the same ledger record keeps the two paths from drifting.

  4. 04

    Separating who marks results from who approves money isn't bureaucracy. It's the only thing that makes a dispute answerable.

    The operator asked for this before we proposed it, having already spent a weekend unable to answer a customer who believed a market had been marked wrongly.

Ways of working

How the work was run

The operator's support team joined the first two weeks, because they were the people who could describe what a half-settled market looked like from the outside. Their ticket history set the acceptance criteria: every figure on a user's screen has to be traceable to the events behind it, and no screen may show a number the ledger can't explain.

A dedicated team for eighteen weeks, with settlement atomicity fixed as a non-negotiable in the first design session. Every later decision about wallets, standings and deposits follows from it. Separating the operator who marks results from the one who approves deposits was the operator's own requirement, and it turned out to be the thing that made disputes answerable at all.

One result, marked three ways

A settlement that runs in stages can stop between them. One that runs as a transaction can't.

The same market with ten open stakes, settled first by the staged job the operator used to run, then by the single transaction Fulltime runs now, and finally with that transaction failing halfway. Switch tabs, or use the arrow keys once one is focused.

The old platform marked the result, then a background job worked through the stakes, updating balances as it went. When it stalled, some winners had been paid and some hadn't, the leaderboard already showed standings the balances didn't support, and nothing in the data said which half had run.

Brackenfield v Oxley WanderersMKT-2291 · Match result · FT 2–1
  1. Result marked · Brackenfield win
  2. Leaderboard running total bumped
  3. Job pays stakes 1 to 3
  4. Job pays stakes 4 to 5
  5. Job stalls · no marker of where
Ledger vs leaderboardDisagree · which half ran?
StakeWalletPts
  • J. Okafor · Home · $10.00+$21.00+3
  • M. Brandt · Home · $5.00+$11.00+3
  • R. Castellano · Draw · $8.00Lost0
  • T. Nguyen · Home · $20.00+$41.00+3
  • A. Whitlock · Away · $6.00Lost0
  • D. Pruitt · Home · $4.00Not paid+3
  • L. Haddad · Home · $12.00Not paid+3
  • K. Sorensen · Draw · $10.00—0
  • E. Marsh · Home · $15.00Not paid+3
  • V. Ibarra · Away · $5.00—0

3 winners paid, 3 not, and the leaderboard already credits all 6. The ledger and the leaderboard disagree.

Why there is no state between: the outcomes, the wallet movements and the market’s close are one unit of work, and standings are counted from what that unit wrote. Step timings above are illustrative.

Architecture

From one mark to a ledger and a leaderboard that agree

Settlement isn't a job that works through stakes. It's a single transaction, and everything downstream of it is either written inside that transaction or derived from what it wrote.

  1. 01 · Trigger
    Winner marked on the settlement deskOne action per market. A market belongs to a fixture and a fixture to a league, so every open stake on it is addressable at once.
  2. 02 · Gate
    Permission checkOnly an account holding result marking may act, and that account can never also approve deposits.
  3. 03 · Engine
    One database transactionOutcomes written, wallet movements recorded, standings invalidated. It completes, or the market stays open with nothing written.
  4. 04 · State
    Append-only movements in MySQLA balance is the sum of its rows. Nothing writes a balance, and a correction is a reversing entry, never an overwrite.
  5. 05 · Delivery
    Standings derived on readRecomputed from stake records, never accumulated. The read cache is derived, so it can be thrown away and rebuilt.

When a customer says the result was wrong

Settlement integrity & answerable disputes

No market can be half-settled

Every open stake on a market resolves inside one transaction. If it fails, the market stays open with nothing written, so there is never a state where some users were paid and nobody can say which.

Every figure traces to its movements

Wallets are append-only and a balance is their sum. A disputed amount is answered by showing the rows, and a wrong entry is corrected with a reversing entry, so the evidence is never erased.

The outcome and the money have different owners

Marking results and approving deposits are separate permissions no single operator account can hold, which is what lets the operator show its work when a result is challenged.

Moving money in stages that can stop halfway? Scope your build in 3 minutes.

Scope your build
Have a project?

Let's talk

Running a large platform, shaping a first MVP, or getting a product ready for a funding round? Tell us where you are. We'll shape the process around it, and stay with you after launch.