Skip to main content

SpecificationsLottery personal boost v2

Lottery personal boost v2

The planned personal lottery boost that can raise a buy's odds up to 2.5x for ve4626 lockers. Documented, not live.

This is the locked design for personal boost v2: how locking the protocol ShareOFT (4626) could raise your lottery odds on qualifying buys. Read it if you lock ve4626, build lottery tooling, or review the LotteryManager.

Status: documented, not live

Personal boost stays at 1.0× until it is armed. On the live LotteryManager, boostManager is address(0), so no buy is boosted today. See Live vs this spec.

What the boost does​

When armed, a qualifying buy's base odds (BasePPM) can be multiplied by up to 2.5× on the covered part of the ticket. You lock protocol 4626 (not a creator ) and assign veLottery. Background: ve4626. Curve kernel bounds: 2.5× proof.

Two things set your boost:

  • Rate (boost): how strong the multiplier is, from your share of total ve power.
  • Cover: how much of this buy's USD the rate applies to, from the USD value of your lock.

Locked formula​

BasePPM is unchanged: min(swapUSD / 250_000, baseCeilingPPM) (default ceiling 40,000 PPM / 4% at $10,000). Solana then scales odds by 400 / 690. Stake USD stays gross venue notional.

swapEff = min(swapUSD, 10_000)            # same ceiling as base PPM
boost = Curve(swapEff, D=100_000, ve/Ve)
cover = min(1, remaining / swapUSD) # remaining starts at 1× veUSD each UTC day
veUSD = locked■4626_amount × markPrice(■4626)
FinalPPM = BasePPM × (1 + (boost - 1) × cover)

ve / Ve are effective veLottery for the beneficiary and live total ve4626 power. boost is the Curve rate and still decays via ve/Ve independently of the daily cover budget. cover is how much of this fill's USD that rate may touch. On the consume path, substitute remaining for veUSD. remaining starts at 1× veUSD each UTC calendar day (multiplier k = 1).

One venue fill: Curve sets the rate on capped swapEff (ve/Ve). Remaining daily veUSD sets cover. Mix is outside Curve, so extra lock USD still raises cover until 1. Personal boost stays at 1.0× until it is armed. Consume is the numbered consume steps below.

Classic Curve (α = 0.4)​

The rate uses the classic Curve working-balance formula. l in Curve is swapEff, not coverage USD and not creator Share supply.

working = min(l, 0.4·l + 0.6·D·(ve/Ve))   # D = 100_000; l = swapEff
boost = min(2.5, working / (0.4·l)) # ∈ [1.0, 2.5]
SymbolMeaning
l / swapEffmin(swapUSD, 10_000)
DProtocol constant 100,000 (not creator ShareOFT supply USD)
ve / VeBeneficiary veLottery / live total ve power
veUSDLocked 4626 principal ×
covermin(1, remaining / swapUSD) on the 24h UTC budget; see consume below

veUSD is not decaying ve-units × price. Decay changes ve/Ve (the rate). Lock size × mark changes cover (how much of the ticket that rate applies to).

Why swapEff matches the $10k base ceiling​

Uncapped Curve(l = swap) after BasePPM is already capped makes personal EV+ non-monotonic for tickets above $10k: more USD can lower the Curve rate faster than base PPM grows. Cap Curve’s l at the same $10,000 as baseCeilingPPM. That plateau is for fully covered tickets (cover = 1). If remaining < swapUSD, cover still falls as ticket size grows.

How much ve you need for 2.5×​

Full 2.5× requires working = l, which is ve/Ve >= swapEff / D:

Ticket (swapEff)ve share for 2.5×
$1000.1% of Ve
$1,0001% of Ve
$10,000 (full base ceiling)10% of Ve

Small covered retail tickets reach 2.5× with a small ve share. A full $10k ticket needs a real slice of total ve. Whales do not mint 2.5× on large tickets by locking dust.

Cover vs rate​

boost and cover are independent knobs, and you need both:

PositionRate (boost)CoverEffective personal mult
Thin lock, fat veHigh (near 2.5×)SmallClose to 1.0×: almost none of the ticket is covered
Fat lock, tiny veLow (near 1.0×)FullClose to 1.0×: covered, but the Curve rate is weak
Fat lock, enough veHighFullUp to 2.5× on swapEff

Worked cover: $2,000 swap, $500 remaining veUSD → cover = 0.25. If boost = 2.5, effective multiplier is 1 + 1.5 × 0.25 = 1.375×.

Extra lock USD raises cover until 1. Extra ve share raises boost until 2.5×. Even a 2.5× rate still consumes remaining that UTC day.

Cover sits outside Curve. That is why extra lock USD keeps helping until cover reaches 1. Blend B (rejected below) puts coverage inside Curve, so extra lock stops helping once working < l.

Daily cover budget​

Your lock's USD value is a daily budget. Each boosted buy spends from it, and it refills at UTC midnight.

The consume window is one UTC calendar day. It is not an AMOE epoch, not an LM sponsorship epoch (default 1 hour), and not the weekly Thursday gauge epoch.

Thursday week is not UTC-day cover

Gauge votes, fees, and bribes follow the Thursday week. Personal cover resets at UTC midnight (k = 1) across all chains. Leftover does not carry. They are not the same clock.

Each beneficiary (canonical CSW / wallet) holds one remaining cover budget for that UTC day, shared across all chains (every EVM, Solana, and any future venue). Spend on one chain reduces remaining everywhere until the next UTC day. Day boundary is Unix UTC midnight: dayStart = block.timestamp - (block.timestamp % 1 days) (startOfToday on ve4626GaugeVoting).

Consume steps​

  1. Window: [dayStart, dayStart + 1 days) in UTC. The next UTC midnight starts a new day.
  2. Budget (k = 1): at day start, or on the first boost touch of the day, remaining = veUSD where (live fail-closed mark). Multiplier k = 1 (not 2×). A live-mark refresh is allowed only through the same fail-closed oracle as veUSD (below).
  3. Each qualifying venue fill: cover = min(1, remaining / swapUSD), then used = min(swapUSD, remaining), then remaining -= used. Use remaining (not a second snapshot of veUSD) for that fill's cover.
  4. Next calendar day: remaining refills to current veUSD. Leftover does not carry. The new day is current veUSD, not leftover + new.
  5. Unlock / lock decrease mid-day: remaining = min(remaining, live veUSD). Remaining never exceeds live lock USD.
  6. No unlimited daily reuse: the same $100 of veUSD cannot boost unlimited $100 swaps in one day. After $100 notional is consumed, further swaps that day get cover = 0 (identity personal mult) until the next UTC day.

AMOE: attested points, not a venue swap. This budget is for venue fills only unless a later spec extends consume to attested USD.

Why the budget works this way​

Wash intent: one unit of lock USD cannot cover more than one unit of swap USD per UTC day across all chains. The Curve rate (boost from ve/Ve) still decays independently of this daily cover budget.

Coverage EV+ scales linearly with cover up to 1.0. For fully covered tickets (cover = 1), the swapEff cap keeps personal EV+ from decaying as swap grows past $10k. Partial cover is different: cover = remaining / swapUSD still falls. Splitting fills can raise total chance when ve share is small. Consume still stops unlimited reuse of the same veUSD.

Oracle​

The lock's USD value comes from a price mark that fails closed: no trusted price means no boost.

uses the same fail-closed staleness and deviation posture as other lottery USD notionals (venue quote × feed). Missing, stale, or out-of-band price → no personal boost for that fill (identity 1.0×). Never silently default the mark to 1. Ticket USD stays venue spend, not this mark: LotteryManager4626, CreatorOracle.

Out of scope​

These stay separate from personal Curve boost. Do not fold them into FinalPPM above.

Gauge lottery pot (additive)​

A vault-level odds add funded by gauge votes, added on top of personal boost.

Global pot 6.942% (69,420 PPM). Per-vault cap:

C(n) = 35_000 - 25_000 / n

n is the number of vaults sharing the pot. 1% at n = 1 (10,000 PPM), 3.5% asymptote (35,000 PPM). Added after personal boost; maxWinChance still caps the envelope.

Composition is BasePPM × CoveredPersonalBoost + VaultGaugeBoostPPM. The 69,420 PPM figure is the global pot; a ticket only receives that vault's capped share C(n), never the whole pot. maxWinChance is the envelope.

C(n) at the edges: n = 1 → 10,000 PPM / 1%; large n → 35,000 PPM / 3.5%.

Fees-on-vote / bribes​

Solidly-style gauge loop (vote, bribe, stream). Not an input to boost, cover, or veUSD.

Rejected alternative (blend B)​

An earlier design put coverage inside the Curve formula. It is recorded here so nobody reintroduces it.

Do not implement:

l       = min(swapUSD, veUSD)             # coverage inside Curve
boost = Curve(l, D=100_000, ve/Ve)
FinalPPM = BasePPM × (1 + (boost - 1) × (l / swapUSD))

Why it is wrong (unsaturated): when working < l,

boost  = 1 + 1.5 · D · (ve/Ve) / l
uplift = (boost - 1) · (l / swapUSD)
= 1.5 · D · (ve/Ve) / swapUSD

Coverage l cancels. Locking more past the saturation threshold does not raise EV in that regime; only ve/Ve and swapUSD remain. The locked formula keeps Curve’s l on swapEff and applies coverage outside Curve, so extra lock USD can still raise cover until 1. Blend B puts coverage inside Curve; that is why FinalPPM mixes cover outside.

Why uncapped l = swap is also wrong: past the $10k base ceiling, BasePPM is flat while Curve’s l keeps growing, so EV+ is non-monotonic. That is the swapEff cap above.

July 2026 LM comments (l = min(creatorShareUSD, swapUSD), L = creator Share supply USD) are the unarmed v1 door plus FriendKey-era coverage. They are not this lock. FriendKey stays out of lottery.

Live vs this spec​

What runs onchain today compared with this design.

ModeAs of 2026-09-05This spec
boostManageraddress(0)Set after propose → 24h → commit → timelock arm
Personal mult1.0×1 + (boost - 1) × cover ∈ [1.0, 2.5]
Gauge PPM0Additive pot above; still optional / separate window
Coveragen/aLock 4626 USD budget, one remaining per beneficiary per UTC day across all chains at 1× veUSD

Arming takes four steps: a proposal, a 24-hour wait, a commit, and a timelock arm. Keep LM pointers at zero until an implementation matches this page. Arming the live Share-coverage _applyBoost path is not v2.

See also​