mainnet is not live yetdevnet beta is live at play.dust.bid
skip to content

ASH math ​

ASH is a weight. a wallet's net fees of a closed day become weight. the weight gets a share of every fee, paid in USDC. all of it is integer math in the program, and the SDK has the same functions.

ASH is the plain version. this page is the exact one.

the day ​

day(t)      = floor((t − day_offset_s) / 86400)            day_offset_s is 0 on devnet: UTC days
day_start(d) = d × 86400 + day_offset_s

a wallet keeps a ring of two day entries on its account. an entry holds the nominal loss fees and the nominal win fees of one day, and whether it has been rolled. when both entries hold older days, the older one is rolled inline before a new day is written.

what goes into a day ​

  • a loss or a bust adds its nominal loss fee: floor(amount × loss_fee_bps / 10000). for a bust the amount is the whole margin. it counts whether or not the fee was actually collected, so a loss while the payout line is busy still counts.
  • a win adds its win fee to the same day.
  • a push adds nothing.
  • every settle path calls it: close, bust, stop loss and take profit, DUST MODE round, MOONSHOT ticket.
  • a wallet made before W8 and not yet upgraded skips: no ASH until it joins.

from fees to weight ​

when the day has closed and is rolled:

basis  = min(wallet_day_cap, max(0, loss_fees − win_fees))
mult   = the early multiplier of that day, in bps
S      = the credit scale of that day (1.0 when half_life_days is 0)
weight = floor( floor(basis × mult / 10000) × S / 1e12 )

the weight is added to the wallet and to the total, ash_total. 1 ASH is 1,000,000 units, six decimals.

worked examples ​

computed with the SDK's rollMath on the devnet settings, 2026-10-15:

the daybasismultiplierweight
lose $100$2.002.0×4.000000 ASH
lose $100, and a win fee of $3 the same day$02.0×0
lose $5,000$100 of fees, held to the $50 cap2.0×100.000000 ASH

a referrer of the first wallet gets a bonus of floor(4,000,000 × 1000 / 10000) = 0.400000 ASH, on top.

the early multiplier ​

before t1:       mult = mult_max_bps
t1 to t2:        mult = mult_max − ceil( (mult_max − 10000) × (day_start − t1) / (t2 − t1) )
from t2:         mult = 10000

it works on the start of the day. the decrease rounds up, so the multiplier rounds down and never falls below 10,000. mult_max_bps is 10,000 to 20,000.

on devnet, mult_max_bps is 20,000, t1 is 1794532830 and t2 is 1799716830. that is 2026-11-13 01:20:30 UTC and 2027-01-12 01:20:30 UTC. the day that starts on 2026-11-13 is still 2.0×. 2027-01-13 is the first day at 1.0×.

after the enable, the schedule can only be moved earlier or lowered, never later or higher: ScheduleOnlyEarlier, 6075. it must end within 365 days of the enable.

the credit scale and the half life ​

S(d) = 1.0                                   when half_life_days is 0
S(d) = 2^(r / H) << k                        k = floor((d − day0) / H), r = (d − day0) mod H, capped at 2^20

a growing scale makes new weight worth more than old weight, which is the same as old weight decaying, without touching any account. H is 30 to 3,650 days and is set once, at enable. devnet has half_life_days 0: no decay.

the pool and the share ​

fees go in as they are paid. a fee is split into the ASH part, the dev part and a referrer part:

ash part  = floor(fee × staker_share_bps / 10000)       staker_share_bps is 5000 at the seed and can only go up
dev part  = the rest, less the referrer's cut

the ASH part lands in ash_pending. push_fees shares it out, together with the house overflow:

sweep = min( max(0, tracked_lp − reward_cap), lp_cash − reserved_total )      only when the payout line is empty
pool  = ash_pending + sweep
dacc  = floor( pool × 1e24 / ash_total )                                      a 256 bit product
ash_acc += dacc ;  ash_reserve += pool

if ash_total is below 1 ASH or the pool is 0 or dacc is 0, nothing moves and the pool waits. if any step would overflow, the call does nothing and emits AshAccSaturated. it can never fail push_fees.

each wallet accrues lazily:

ash_due += floor( weight × (ash_acc − ash_snap) / 1e24 )        saturating

a new weight starts at the current ash_acc, so it counts only fees that arrive after its roll.

the credit ​

ash_credit moves min(ash_due, ash_reserve) into the wallet's own balance. it adds to a balance and nothing else. if the reserve holds less than the due, the rest stays due and AshCreditClamped says so.

roll_day rolls the closed days of up to 6 wallets, with each one's referrer, and credits what they are owed. both instructions are permissionless.

the referrer bonus ​

when a wallet's day is rolled, its referrer's weight grows by floor(weight × ref_dust_bps[tier] / 10000). the field kept its W5 name, and it is the ASH bonus rate now. the seed is 1000, which is 10%. a referrer on a wallet that is not upgraded gets nothing: AshSkipped reason 1.

the farm bound ​

a wallet that loses on purpose to farm ASH pays the fee out of its own pocket. the program keeps that loop a loser by bounding the ASH share and the referral rates together:

s + (10000 − s) × max(ref_bps) / 10000  ≤  9000       s = staker_share_bps

a setting that breaks it is refused with FarmBound, 6077. a wallet's hedge inside one day also counts for nothing, because win fees net against loss fees.

the settings ​

admin_enable_w8 is one way. it turns ASH on, closes ledger staking after one final flush, and checks the farm bound. the reservation switch has to be off in its arguments. nothing here can be undone, and a second enable answers W8AlreadyOn, 6071.

settingboundwho and how
ash_mult_max_bps10,000 to 20,000owner. it can only go down: ScheduleOnlyEarlier, 6075
ash_mult_t1, ash_mult_t2t1 ≤ t2, t2 within 365 days of the enableowner. they can only move earlier
ash_half_life_days0, or 30 to 3,650set once at enable: SetOnce, 6076
day_offset_s0 to 86,399set once at enable
ash_wallet_day_cap$1 to $100,000 of feesowner, any value inside the bound
staker_share_bpscan only go up: StakerShareDown, 6061owner

the risk keeper cannot touch any ASH setting. its only moves on these settings are to turn the payout reservation on and to lower its utilisation. the owner can change anything inside the bounds, subject to the two rules above. every change emits W8SettingsChanged.

the switches live in w8_flags: bit 0 ASH on, bit 1 payout reservation on, bit 3 ledger staking closed. devnet reads 9, which is bits 0 and 3. the reservation switch is described in the house and the line.

the vault with ASH ​

the vault identity gains two buckets, AF (ash_pending) and AR (ash_reserve):

vault tokens = A + Mg + LP + SB + SF + SP + DF + RF + AF + AR

SF and SP, the old staker buckets, stay at 0 once the final flush has run.

accounts and events ​

  • a wallet's ASH state is a 171 byte extension on its UserAccount: the weight, the snapshot, what is due, what was paid, and the two day entries. a new wallet is made at the full 484 bytes. an old one grows once with upgrade_user_v8, rent from a payer.
  • the exchange-wide counters sit in the reserved bytes of Exchange: no realloc.
  • events: W8Enabled, W8SettingsChanged, UserUpgradedV8, AshRolled, AshDistributed, AshCredited, AshCreditClamped, AshSkipped, AshAccSaturated, StakerFlush.