Conversation
Adds the arithmetic for an inventory charge on the spread of the pool's payout
book, counting each settlement price by how likely it is rather than equally:
D(W) = sqrt( sum q(S)*W(S)^2 - ( sum q(S)*W(S) )^2 )
Nothing calls it. The module owns the statistic and its fold; the accumulators
and the range reads they need are a separate change.
Weighting by probability is what removes the window. Counting every tick equally
over an unbounded ladder is meaningless, so the unweighted form has to bound it,
and then owns a parameter that has to be sized, frozen in the right place, kept
above one tick of resolution, and re-derived whenever it changes because the
charge scales with the window's width. A price the market will almost certainly
never reach contributes almost nothing here without being excluded, so none of
that exists.
Both totals are plain sums with no division, so the fold is exact: a range opened
and closed returns them bit for bit, and splitting a trade into pieces reaches
the same totals as making it whole. Both are asserted.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…BU-732) The statistic module now carries its totals raw — payout times 1e9-scaled mass, division deferred to the read — so a fold is integer sums and products with no division at all. The previous form divided inside the fold and floored differently on open and close: fifty open-close cycles at a payout and mass whose product does not divide left an empty book scoring 273,861. Raw carriage makes the round trip exact by construction, and the ragged regression pins it. The payout index stores each boundary's frozen weight and carries mass-weighted first moments per subtree, so range_weighted_payout_sum resolves a range's sum(q*W) in O(log n) by the same two reads range_max_payout performs — the weighted analog of a tick-weighted first moment, with the frozen survival function replacing the tick coordinate, which is also what makes unbounded legs finite. The measure freezes from the first mint's own validated pricer, atomically with that mint: the book is empty at the freeze by construction, no admin step or keeper exists, and a market whose snapshotted rate is zero never evaluates a weight. Charges and rebates are differences of the floored potential rate * D(W), so collections telescope to the current potential exactly; the escrow is isolated in skew_reserve, senior to fee revenue on the close path (RP-29 owns that ordering), and released at settlement. Flow tests drive the custody end to end: a complete set's second leg is rebated the first leg's charge exactly, a round trip across an oracle move refunds to the unit, a close that unbalances a flat book is charged through the clamp chain and the drain refunds it, and settlement releases the residual.
…pread # Conflicts: # packages/predict/docs/concepts/fees-and-rebates.md # packages/predict/docs/design/decisions.md # packages/predict/sources/config/config_constants.move # packages/predict/sources/events/order_events.move
…-732) Ports the reaching test for ESkewChargeExceedsCloseProceeds — a complete set plus a deep out-of-the-money spot makes the closed leg worthless, so its skew charge exceeds everything the close releases — closing the register's open pinning gap. Adds partial-close coverage (two half-closes drain the escrow like one full close, with the invariant held at the midpoint) and a stacked-charges flow proving skew and inventory impact keep isolated escrows under one mint.
…d (DBU-732) The skew reserve guard gets the mirror of the impact reserve's abort test, and both escrows are pinned folding into required cash and out of free cash together. The concentrating-mint flow now asserts the exact charge — for one ATM digital the deviation is flat in the frozen mass to first order, so the scipy-derived reference up price pins rate * D at exactly 2,499,999 across the whole reference budget, closing the seam between the frozen surface, the mass, and the rate that relational tests alone could not see. Drops the unused SkewTerms getter, trims SkewAdjustment and SkewTerms to copy + drop (nothing stores them), and adds the skew leg to the fee-routing table and diagram. C-1 carries a note that the payout node grew and a skew-enabled trade costs two surface evaluations plus two weighted reads, so the flush and batch bounds want re-measuring if skew ships enabled.
tonylee08
marked this pull request as ready for review
August 21, 2026 19:45
sdelo
approved these changes
Aug 21, 2026
0xaslan
reviewed
Aug 21, 2026
| /// admin-tunable with a floor of zero: unlike the premium relation, whose both | ||
| /// sides are upgrade-required constants, this one is reachable through config. | ||
| public(package) fun snapshot(config: &StrikeExposureConfig): StrikeExposureConfig { | ||
| assert!(config.inventory_skew_rate <= 2 * config.min_fee, ESkewRateExceedsFeeFloor); |
Collaborator
There was a problem hiding this comment.
can we assert this earlier, when inventory_skew_rate is being set? if this assertion is ever triggerable, then it bricks market creation, right?
OrderMinted's skew_charge and skew_rebate were asserted only at rate zero by the referral mirrors; a charging mint now pins the whole event byte-for-byte with the charge at its independently derived ATM magnitude, so the amount the trader pays and the amount the event reports are the same number by test.
0xaslan
reviewed
Aug 21, 2026
| inventory_impact_charge: u64, | ||
| /// Inventory-skew amounts for this mint; at most one is nonzero. A rebate | ||
| /// reduces the withdrawal rather than paying the trader. | ||
| skew_charge: u64, |
Collaborator
There was a problem hiding this comment.
should we rename to inventory_charge / inventory_rebate?
…batch (DBU-732) Monitoring the skew fee needs no delta replay: OrderMinted and LiveOrderRedeemed carry the post-trade skew_reserve, and MarketSettled carries the released residual — every event that moves the escrow emits its post-state, making the family the canonical stream per the single-owner-facts rule, sampled after the transition. The mint-side event is pinned byte-exact with the reserve at the independently derived ATM magnitude, and the template config-history test now drives the skew setter so the emitted rate is asserted at a set value, not its default. The coverage batch closes the review gaps: the close-side charge and the half-close midpoint get exact pins in place of range asserts (both derived by hand from the m <-> S-m symmetry of the ATM mass); a re-mint after a full drain repays the frozen ATM charge exactly, proving the measure survives an empty book and an oracle move; a worthless flattening close collects its full rebate with every fee clamped to the zero payout, pinning the rebated branch of the close ordering; two markets prove isolated frozen measures and escrows; a u64-max payout executes the width audit; the dust clamps in the fold and the weighted read get direct coverage; zero-payout and zero-mass folds pin their no-ops; the snapshot guard gets its pass-side boundary; and the three structurally unreachable asserts now say so at the assert site.
… (DBU-732) The relation was asserted only at snapshot, so a template holding an individually-valid but jointly-bad pairing — a low fee floor set after a high skew rate — would abort every market creation until an admin repaired it. Both setters now refuse the pairing at write time, the template can never hold it, and the snapshot assert becomes a structurally unreachable tripwire at the moment the pairing freezes into a market. The snapshot-level test is replaced by one per setter, covering the raising and the lowering side.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
rate * D(W), whereD(W)is the standard deviation of the pool's payout profile under a probability measure frozen at the market's first mint.O(log n)— the weighted analog of predict: add a tick-weighted first moment to the payout tree (DBU-732) #1248's tick-weighted moment, with the frozen survival function replacing the tick coordinate, which is also what makes unbounded legs finite without a measurement window.skew_reserve, rebates are paid only from it, and cumulative collections equal the current potential exactly; settlement releases the residual.inventory_skew_rateships at0and is snapshotted per market.Why
This is the windowless alternative to the #1248 + #1250 pair: the same charge-on-deviation mechanism with one substitution — each settlement price counts by its frozen probability instead of equally. That substitution deletes the measurement window and everything the window owned: the fraction to size, the tenor scaling, the freeze on reference-tick landing, the mint gate behind it, and the zero-tick rounding edge. A price the market will almost certainly never reach carries almost no weight, so nothing has to be excluded.
The measure freezes from the first mint's own validated pricer, atomically with that mint. The book is empty at the freeze by construction, so every boundary the index ever creates carries a weight from one fixed surface; there is no admin step, no keeper, and no market-launch ordering constraint. Freezing is what makes the charge a state function: cycles telescope exactly and a round trip refunds to the unit, which the flow tests assert bit-for-bit through custody, across an oracle move.
Linear / Context
Key decisions
payout * massproducts at the 1e9 scale, with division deferred to the read. The earlier statistic-only commit divided inside the fold, which floored differently on open and close and ratcheted residue upward — fifty open-close cycles at a non-dividing payout/mass pair left an empty book scoring 273,861. Raw carriage makes the round trip exact by construction; a ragged-pair regression pins it.predeploy/response-policies.mdRP-29 records the trigger, blast radius, and why clamping is worse.Scope / Descoped
Measurement caveat
The earlier draft body leaned on rank-correlation-with-dispersion columns (0.983 / 0.951). An independent re-derivation reproduced those numbers exactly but showed the dispersion target rewards functional form: a control with the same shape and one constant volatility per cadence — no surface at all — scores 0.9948, and the target carries no per-market volatility variation for a measure to be credited with tracking. On the one metric grounded in realised loss at the single actual settlement price, this measure scores 0.4330 against 0.4488 for #1252 and 0.3918 for the windowed form. The case for this design is therefore not the dispersion columns: it is that it beats the windowed form on the clean metric while deleting the window's defect class, at a per-mint marginal of ~209 instruction units against the windowed form's ~294 (rate-zero baseline 179 against main's 175), with ~30 units attributable to the charge itself. On a rebuilt dispersion benchmark whose target varies per market, the measure's surface holds while every no-information control falls, so its rank quality survives the control that invalidated the original benchmark.
Tests
sui move test --path packages/predict --gas-limit 100000000000(CI-pinned toolchain): 536 passed, 0 failed, on the branch merged with current main (referral fees, config-history events, settlement fallback, and the calibrated default economics all composed and re-verified; the skew flow tests pin their own fee floor, so they are default-independent).inventory_weighted_tests(raw-unit arithmetic, ragged round trip, ratchet-free cycles, stacked drains),strike_payout_tree_weighted_sum_tests(hand-derived per-position identities, sentinel legs, rebalanced/drained books, weight-mismatch and inversion aborts),inventory_skew_flow_tests(custody end to end: charge into escrow, complete-set rebate exact, round-trip refund across an oracle move, close-side charge and drain, two half-closes draining like one full close, skew and inventory-impact escrows stacking in isolation, settlement release, zero-rate inertness, and the RP-29 abort reached via a worthless-leg close), plus config bounds, the rate/fee-floor relational guards, the skew-escrow guard's abort test, and an exact end-to-end magnitude pin: for one ATM digital the deviation is flat in the frozen mass to first order, so the scipy-derived reference up price pins the quoted charge at exactly 2,499,999 across the whole reference budget — closing the seam between the frozen surface, the mass, and the rate that relational tests alone cannot see.Risk
Ships inert (
inventory_skew_rate = 0): no market charges, freezes, or evaluates a weight until an admin sets a template rate, and existing markets never change. The mechanism's custody is isolated in its own reserve with the backing invariant asserted after every cash-mutating flow.🤖 Generated with Claude Code