Tokenomics
The math behind every BattleMeme token: bonding curve, fees, supply lifecycle, scoring, and graduation LP shape.
Headline numbers
| Initial mint per token | 1,600,000,000 (1.6B) — all seeded into the 5 LP rungs |
| Final winner supply | ≤ 1.6B — leftover unsold tokens are burned at graduation; only mints after that go to ClaimManager for loser claim coverage |
| LP rungs | 5 × 320M tokens each |
| Tick range | [127320, 173220] = ~99× price growth |
| Max ETH cap | ~990 ETH if curve fully consumed |
| Queue trigger | 5 ETH raised (creator’s 0.01 ETH pre-buy counts) |
| Early finish | 950 ETH in pool at commenceBattle |
| Battle duration | ~19 hours = 4 × 1h trade + 3 × 5h rest (last round finalizes immediately) |
| Pool fee | 1.3% — split on sweep: 1.0% LP fee + 0.3% creator (perpetual) |
| Creation fee | 0.01 ETH — used as an atomic creator pre-buy at the pool’s initial price |
| Creator upfront grant | 0% — only what the pre-buy bought at floor |
| Network | Ethereum mainnet |
1. Bonding curve — laddered single-sided LP
BattleMeme uses 5 single-sided concentrated LP positions chained together on Uniswap v4 instead of a custom curve via BeforeSwapDelta. Curve dynamics emerge from AMM math (no hardcoded formula); every swap runs natively through Uniswap UI / aggregators / DexScreener.
Pool configuration
| Param | Value |
|---|---|
| Pair | ETH (currency0) / TOKEN (currency1) on Uniswap v4 |
| Pool fee | 13_000 = 1.3% — accrues to LP, swept then split 1.0% LP fee + 0.3% creator |
| Tick spacing | 60 |
Raw price = TOKEN per ETH:
Bootstrap flow
Position layout
Each rung is 9180 ticks wide (≈ 2.51× price growth), stored under salt = bytes32(i) for i ∈ 0..4.
| Rung | Tick range | Tokens | Note |
|---|---|---|---|
| #0 | [164040, 173220] | 320M | initial — pool starts at top |
| #1 | [154860, 164040] | 320M | |
| #2 | [145680, 154860] | 320M | |
| #3 | [136500, 145680] | 320M | |
| #4 | [127320, 136500] | 320M | graduation edge |
Mechanics
- Pool initialized at
sqrtPriceX96corresponding to tick 173220 (top of position #0). - At bootstrap, all 5 positions are “above range” → each holds 100% TOKEN.
- A buy (ETH → TOKEN, zeroForOne swap) consumes token1 from position #0 first; price walks down.
- When price crosses 164040 (bottom of #0), #0 is fully sold and #1 starts being consumed.
- Continues through to #4’s bottom at tick 127320 → graduation edge.
Capital efficiency
5 chained narrow positions raise ~2× more ETH than 1 single LP covering the same range with the same tokens:
| Architecture | Tokens | Range | ETH max raise |
|---|---|---|---|
| Single LP (same tokens & range) | 1.6B | 99× | ~480 ETH |
| Laddered 5 positions | 1.6B | 99× | ~990 ETH (~2×) |
Why: each narrow position has higher concentrated liquidity (L), so each unit of L supplies more amount0 in its sub-range. Sum of 5 narrow positions raises more ETH than 1 wide position for the same tokens.
Position breakdown
(initial price = 2500, range 100× total, scaled by ×8 from the canonical 200M model since LP_MINT = 1.6B)
| Pos # | Tokens | Price from | Price to | Mult start → end | ETH consumed | Cum ETH |
|---|---|---|---|---|---|---|
| #0 | 320M | $0.0000750 | $0.000188 | 1.00× → 2.51× | ≈ 15.20 | 15.20 |
| #1 | 320M | $0.000188 | $0.000473 | 2.51× → 6.31× | ≈ 38.24 | 53.44 |
| #2 | 320M | $0.000473 | $0.001188 | 6.31× → 15.85× | ≈ 96.00 | 149.44 |
| #3 | 320M | $0.001188 | $0.002984 | 15.85× → 39.81× | ≈ 241.12 | 390.56 |
| #4 | 320M | $0.002984 | $0.0075 | 39.81× → 100.00× | ≈ 605.68 | 996.24 |
| TOTAL | 1.6B | $0.000075 | $0.0075 | 1× → 100× | ≈ 996 ETH | — |
(USD prices assume ETH = $2,500; actual ETH-per-position is the deterministic value.)
Curve characteristics
- Smooth FOMO early on — narrow positions, price climbs slowly
- Parabolic near graduation — later positions have higher ETH caps, FOMO intensifies
- No quadratic formula — shape emerges from the concatenation of 5 AMM curves
- Capital efficient — ~2× ETH cap vs single LP at same range
- Native AMM — DexScreener / Uniswap UI / aggregators work out of the box
2. Phase lifecycle
The token’s state machine has 9 states. See State machine for the table; here’s the high-level flow:
NONE → INIT → QUEUE → WARMUP → BATTLE ⇄ RESTING → ELIMINATED/GRADUATED → LOST / (stays GRADUATED)Phase 1: INIT (created → 5 ETH raised)
- Pool: laddered LP active, all positions hold 100% TOKEN
- Trading: open buy/sell via Uniswap UI / router / aggregators
- Hook tracks
totalETHRaised,ethRaisedInPool - Trigger to queue:
totalETHRaised ≥ INIT_ETH_THRESHOLD (5 ETH)- 5 ETH falls inside position #0’s range (which caps at ~15.2 ETH)
- Price multiplier at trigger ≈ 1.3–1.5×
Phase 2: QUEUE (frozen, up to 24h)
- Trading:
beforeSwaprevertsTokenFrozenForTrade - Token appended to orchestrator’s queue
- Auto-start when full (10 tokens) or 24h timeout reached with ≥ 2 in queue
- < 2 in queue at timeout → unlock (back to INIT)
Phase 3: WARMUP (10 minutes)
- All selected tokens transition QUEUE → WARMUP simultaneously
- Trading still frozen; UI shows countdown (
warmupEndsAt) so creators can rally - After 10 minutes, anyone can call
commenceBattle(battleId)→ WARMUP → BATTLE
EARLY_FINISH check @ 950 ETH: at commenceBattle, if any active token already has ethRaisedInPool ≥ EARLY_FINISH_THRESHOLD, orchestrator finalizes immediately with that token as winner (skipping all rounds).
Phase 4: BATTLE (4 rounds, fixed timing, ~19h total)
Total duration:
(last round has no trailing rest — it finalizes immediately on cut.)
Constants:
| Constant | Value |
|---|---|
TRADE_WINDOW | 1 hour |
REST_WINDOW | 5 hours |
ROUND_GAP (= TRADE + REST) | 6 hours |
TOTAL_ROUNDS | 4 |
WARMUP_DURATION | 10 minutes |
QUEUE_TIMEOUT | 24 hours |
UNLOCK_COOLDOWN | 24 hours |
Battle scoring — live ETH in pool
The live, signed net ETH balance across the 5 LP positions. No composite formula. No hidden weights. Sells reduce score immediately; buys add immediately. Pure capital-driven.
Cut size per round
See Bracket cut table for the full lookup. R4 always reduces to top 1.
Tiebreaker
Ties go to the earliest-indexed token in active[] (= queue insertion order). No time-to-5-ETH or other side rule.
Random source
The protocol has no on-chain randomness:
- Round gap: constant
ROUND_GAP - No VRF, no
block.prevrandao - Chainlink VRF is NOT used. Only Chainlink Automation (keeper-driven upkeep), itself permissionless.
Phase 5: Resolution
Winner → GRADUATED:
- Snapshot winner’s sqrtPriceX96 from
slot0 hook.withdrawForOrchestrator(winner)→ unwind all 5 LPs; ETH → orchestrator (winnerEth)- For each ELIMINATED loser, compute
ratio = winnerPriceQ96 × 1e18 / loserPriceQ96. MintwinnerCoverto ClaimManager viahook.provideTokens. Register with ClaimManager. hook.burnLeftoverTokens(winner)— burn 100% leftover excesshook.disableMintFor(winner)— permanent mint freezehook.transitionToGraduated(winner)GraduationLP.deployForGraduation(...)builds the post-grad wide + wall LP (see §5)
Loser → ELIMINATED → LOST:
- During battle: mid-cut → state → ELIMINATED, eliminationPrice snapshotted, ETH escrowed
- At finalize: registered with ClaimManager → state → LOST
- Transfers frozen except through the hook’s burn-to-claim path
- Holders can claim winner tokens at any time. No deadline.
3. Loser → Winner compensation
ClaimManager.claim(loserToken, amount) converts loser tokens to winner tokens at the locked-in ratio:
The ratio is USD-fair at-the-moment-of-elimination: a loser’s tokens are converted to winner tokens at the price ratio when the loser exited. Subsequent winner price moves don’t change past claims.
No deadline. Holders may claim indefinitely. The winner-token reserve was pre-minted to ClaimManager at finalize, so claiming is just a transfer (no further interaction with hook needed).
4. Graduation LP
At graduation, GraduationLP deploys two positions per battle, both custodied by GraduationLP itself, locked for GRADUATION_LP_LOCK:
Anchor tick
anchorTick = nearest TICK_SPACING to current pool slot0 tick (post-unwind).
Wide LP — price discovery
Range: [anchor − 16080 (clamped), anchor + min(GRAD_WIDE_UPSIDE_TICKS, headroom)]
≈ ±5× downside / up to ~1000× upside
Funded: winnerEth + winner tokens (minted via hook.provideTokens)
Purpose: two-sided liquidity for normal buying / sellingGRAD_WIDE_HALF_WIDTH = 16080, GRAD_WIDE_UPSIDE_TICKS = 69120. The 69,120-tick upside ≈ 1.0001^69120 ≈ 1000× pump headroom above graduation price. Covers PEPE-tier (100×), WIF-tier (1000×). Beyond that (DOGE/SHIB-tier) would exhaust into the ceiling — once-a-decade events.
Wall LP — buy support from loser ETH
Range: [anchor + TICK_SPACING, anchor + TICK_SPACING + 540]
≈ 5%-deep band just ABOVE current tick
(Buys push tick DOWN, sells push tick UP → wall is hit by sells first)
Funded: loserEth only (single-sided)
Purpose: absorbs sell pressure with strong ETH supportGRAD_WALL_DEPTH = 540.
LP locking
- In-protocol lock — GraduationLP custodies positions in
mapping(battleId => GradLock). AfterunlockAt = creation + GRADUATION_LP_LOCK, anyone can callunlock(battleId, recipient)to withdraw principal torecipient. - During lock, anyone may call
collectFees(battleId)→ fees split 1.0% →factory.feeRecipient()+ 0.3% → winner’screator(same split as pre-grad sweep). - UNCX is NOT integrated. The interface
IUNCXV4Lockerexists as a future migration target; all locking is custom in-protocol today.
Behavior post-grad
- Original 5 laddered LPs are fully unwound at finalize — they no longer exist
- The wide + wall LP is the only protocol-owned liquidity for the winner token (third-party LPs may join after grad)
- Sells push tick UP into the wall (sellers receive ETH from the wall); wall depletes first, then wide LP carries → gradual slippage, no crash to 0
5. Voucher campaigns (separate creator promo feature)
VoucherManager is independent of the battle lifecycle. It is not part of loser-side compensation, not linked to any battle state transition, not consulted by the orchestrator.
Two modes:
- Link mode — creator signs off-chain ECDSA vouchers, hands them out (DM/QR/email)
- Whitelist mode — creator publishes a Merkle root; wallets self-redeem
See VoucherManager for full API.
6. Total token supply
Lifecycle of the supply
Finalize — what happens to the winner’s tokens
Final winner supply
Typically less than 1.6B — leftover LP tokens are burned. Expect 0.3–1.6B circulating post-grad depending on how much of the curve was consumed.
Loser supply
- State →
LOST; transfers frozen except via the burn-to-claim path. - Loser
totalSupplyshrinks over time as holders callClaimManager.claim(each call burns their loser balance). hook.burnLeftoverTokensalso runs on losers post-elimination to burn any LP-returned tokens still on the hook.
7. Edge cases & math safety
Position fully consumed mid-battle
When an LP position runs dry (price crosses its tickLower), Uniswap v4 auto-switches to the next position. No special handling needed.
LP fully consumed (price hits graduation tick 127320)
Pool enters “below all ranges” state. Further buys can’t consume liquidity → swaps revert. In practice, EARLY_FINISH (ethRaisedInPool ≥ 950 ETH, checked at commenceBattle) would have already triggered before this.
Single-token-in-queue edge case
Queue needs ≥ MIN_TOKENS_PER_BATTLE (= 2) to start a battle. If only 1 token at QUEUE_TIMEOUT → unlock back to INIT, stamp unlockedCooldownUntil. The cooldown is stamped but never enforced — the token can immediately re-qualify for queue (TODO if behavior is intended).
Battle with all-zero scores
Score = getEthReserves ≥ 0 always (clamped). If all active tokens have 0 ETH in pool (e.g., all dumped fully), tiebreaker = first-index in active[].
Failed Chainlink Automation
All public entrypoints (tickQueue, commenceBattle, runRound) are permissionless — anyone can call them once the time gate passes. Chainlink is a fallback automator, not a trust assumption.
sqrtPrice extreme
Pool sqrtPrice initialized at tick 173220. Tick range 127320–173220 well within v4 limits (max ±887272). No overflow concerns.
Composite-score plumbing unused
BattleScoring.sol library, BuyerData.firstSeenAt / hold, hook’s roundScores[i] array, and commitRoundScore function all exist but are not exercised by the orchestrator. They’re scaffolding for an optional future composite score.
8. Constants quick reference
// Money / supply
CREATION_FEE = 0.01 ether
INIT_ETH_THRESHOLD = 5 ether
EARLY_FINISH_THRESHOLD = 950 ether
LP_MINT = 1_600_000_000 ether
MIN_BUYER_ETH = 0.01 ether
// Pool
POOL_FEE = 13_000 // 1.3%
PLATFORM_FEE_BPS = 10_000 // 1.0% — factory.feeRecipient
CREATOR_FEE_BPS = 3_000 // 0.3% — BattleMemeToken.creator
TICK_SPACING = 60
LP_TICK_UPPER = 173_220
LP_TICK_LOWER = 127_320
TICK_WIDTH_PER_POSITION = 9_180
N_POSITIONS = 5
TOKENS_PER_POSITION = LP_MINT / 5 // 320M
// Queue / Warmup
QUEUE_TIMEOUT = 24 hours
UNLOCK_COOLDOWN = 24 hours
WARMUP_DURATION = 10 minutes
MIN_TOKENS_PER_BATTLE = 2
MAX_TOKENS_PER_BATTLE = 10
// Battle
TRADE_WINDOW = 1 hour
REST_WINDOW = 5 hours
ROUND_GAP = TRADE + REST // = 6 hours
TOTAL_ROUNDS = 4
MIN_BUYER_HOLD = 30 minutes // unused (reserved for future)
// Graduation
GRADUATION_LP_LOCK = (set in BattleConfig)
GRAD_WIDE_HALF_WIDTH = 16_080 // ticks
GRAD_WALL_DEPTH = 540 // ticks
GRAD_WIDE_UPSIDE_TICKS = 69_120 // ticks (~1000× upside)
// Vouchers
MIN_ETH_PER_VOUCHER = 0.001 ether
MAX_VOUCHERS_PER_CAMPAIGN = 1000See also: Architecture, State machine, Cut table, Graduation LP, Security.