@azuro-org/toolkit v7.0.0
Per-outcome resolution. The protocol no longer resolves conditions — it resolves outcomes. Within
one condition, outcomes can independently be Won, Lost, Canceled (voided, stake refunded) or still taking
bets, all at the same time. The old condition-level model could not express that, and everything in this
release follows from removing it.
⚠️ This is a breaking release, and one of its changes produces no compile error. CombototalOdds
and every figure derived from them change value for every unredeemed combo. A greentscdoes not mean
the migration is done.
📖 Toolkit v7 Migration Guide — full before/after
for every item below, plus a grep-your-call-sites section.
Breaking changes
1. ConditionState is now exactly { Active, Stopped }
Canceled, Removed and Resolved are removed. A condition only ever says whether it is taking bets;
won / lost / voided is per outcome now.
// Before
condition.state === ConditionState.Resolved
// After — ask the outcome, not the condition
isOutcomeSettled(outcome.state)If you used Removed to mean "absent from the feed" and Stopped to mean "temporarily suspended", those
are one state now. Use the condition's hidden flag to make that distinction instead.
2. MarketOutcome.isWon is removed
No alias, no deprecation. It was derived from condition-level wonOutcomeIds and was undefined whenever
those were absent — so !isWon silently rendered every outcome as lost.
// Before
outcome.isWon
// After
outcome.state === OutcomeState.Won3. BetsReportEntry.createdAt is now required
Only affects code that builds entries itself and calls calcBetsReport directly —
getBetsReport fills it in for you.
It is required rather than optional because no default is safe: a combo is priced by the rules in force
when it was placed, so defaulting the timestamp either way silently mis-prices one era of bets.
4. BetMetaData.selections[].outcome.condition.outcomes[].result is retyped
SelectionResult → OutcomeResult. The old type had no way to express a void.
New
calcComboOdds({ odds, createdAt })— a combo's total odds, priced the way the protocol prices them,
for the rules in force when the bet was placed. This is now the one place that decision lives; use it
instead of multiplying leg odds, or callingcalcMinOddsfor a bet that already exists. Pass the bet's
realcreatedAt— the same legs price differently either side of the fee's start date. A voided leg must
be left out by the caller; an empty list prices at1, so a bet with nothing left standing returns its
stake.isOutcomeSettled(state)—trueforWon | Lost | Canceled,falseforActive | Stopped.MARGIN_APPLIED_AT— unix seconds, the moment the feed started applying its fee to every outcome's
odds. Exported because reconstructing any historical combo figure needs it. The switch was global and
happened at one moment on every chain, so one constant covers all of them.OutcomeResult(Won | Lost | Canceled) is now exported from the package root.BetFragment/BetsQuery:selections[].outcomenow includesresult?: OutcomeResult | null— the
authoritative per-outcome settlement signal for a bet.
Changed behaviour — no compile error
Combo odds are re-priced. The feed applies its fee to every outcome's odds, so a combo is priced by
removing that fee per leg and applying it once to the product. The subgraph records the plain product
instead, which compounds the fee once per leg:
ceil(1.5 / 0.99) * ceil(2 / 0.99) * 0.99 = 3.05 ← what the protocol pays
1.5 * 2 = 3.00 ← the plain product
calcBetsReport and getBetsReport now price combos the first way, so every unredeemed combo reports
higher, correct figures — by more the more legs the bet has.
Three shapes are deliberately unchanged: a redeemed bet (its recorded payout is the amount actually
paid), a combo placed before MARGIN_APPLIED_AT (its leg odds are raw, so the plain product is already
right), and a cashed-out bet (paid at the price the bettor took). Singles are unaffected either way.
A combo whose every leg was voided has odds of 1 — the stake is simply returned. The same holds for any
bet the protocol canceled outright, single or combo.
If you compare these figures against your own backend, snapshot tests or cached values, refresh them.
Unchanged — worth stating
OutcomeState and GameState are untouched. So is ConditionStatus / BetConditionStatus, which is a
different enum from the bets subgraph (Created | Resolved | Canceled | Paused), not the feed's
ConditionState — do not migrate it by mistake. Condition-level wonOutcomeIds is still served by the feed.
Install
npm install @azuro-org/toolkit@^7Pairs with @azuro-org/sdk v8.0.0, which carries this
model change through the React hooks.
Full Changelog: v6.5.0...v7.0.0