LODE / Technical

The contracts and the math.

01 — Overview

The contracts, and how a pool is oriented

Each chain runs one hook contract, and it serves every launch on that chain. Each launch has its own Uniswap v4 pool, and its own vault for that launch’s ETH and tokens. The hook keeps each launch’s accounting under that pool’s id.

In every pool, native ETH is currency0 and the launch token is currency1. The orientation block below the contract list shows what that means for tick direction.

01SingletonFloorHook.sol
Runs the skim, bid, inventory, ask, staircase, ratchet and protocol-owned seed for every pool on the chain.

The hook is an OpenZeppelin uniswap-hooks BaseHook. It takes the beforeInitialize permission at pool creation. On each swap it takes beforeSwap and afterSwap. Both may return a delta, through beforeSwapReturnDelta and afterSwapReturnDelta. It takes no liquidity or donate permissions. An approved factory calls registerPool(key, config, launch, platformPool) once per launch. That call deploys the pool’s FloorPoolVault and writes its Config. Nothing can rewrite the platformVault, the creator, any skim rate, the creator’s rates or the holder tax. No setter exists for any of them. Every per-pool read and write goes through execute(poolId, calldata). The hook delegates it to SingletonFloorEngine or SingletonFloorViews. One call to poolState(poolId) returns the stored state as one struct. The hook is deployed directly, with no proxy or beacon, so its code cannot be upgraded. It has no admin and no withdraw function. The vaults it deploys are beacon proxies (row 02).

164 lines + a 1087-line engine
bindPool once per pool, by its sale,
DYNAMIC_FEE_FLAG, currency0 == 0
02FloorPoolVault.sol
Holds one launch’s ETH and tokens, and no other launch’s.

registerPool deploys it as a BeaconProxy at a CREATE3 address salted by the pool id. The same call initialises it with its hook, pool id and assets. Skim, fees and closed positions go straight into it, and settlement pays out of it. Only its own hook can ask it to pay. A payment can never draw on another launch’s vault. The vault has no owner and no sweep. Its code cannot change. Every vault on the chain reads one FloorPoolVaultBeacon, the hook’s immutable vaultBeacon. That beacon has no owner and no upgrade function, and its implementation is fixed when it is deployed.

85 lines, behind a BeaconProxy
poolState(poolId).custodyVault
hook.vaultBeacon()
03FloorMath.sol
Turns a backing ratio into a sqrt price and a tick, rounds conservatively, and derives the ask.

A pure library. Its helpers are sqrtPriceX96FromRatio, floorTick and askTick, with the rounding pair _ceilToSpacing / _floorToSpacing. It defines the constant TICKS_PER_DOUBLING = 6932. The table in 02 gives each helper’s rounding and guards.

109 lines
orientation note lives here
04FloorToken.sol
A solmate ERC20 with a fixed supply.

TOTAL_SUPPLY = 1_000_000_000e18 at 18 decimals, minted once, straight to the sale. The token has no mint function and no transfer tax. It is deployed behind an upgradeable proxy whose admin is the deployment’s upgrade owner. It has three optional extras. bindRewards() links it to the chain’s FloorHolderRewards. The deployer calls it once, on a launch with a holder tax. After that, transfer and transferFrom notify the rewards contract. burn(amount) destroys the caller’s own tokens. bindLaunch(), called once by the deployer, names the one sale that may move the token while it is locked. For anyone else, transfer, transferFrom and burn then revert with Locked. The lock lasts until that sale calls unlock() at graduation. While the token is locked, sellBack(amount) is the exit. It moves the caller’s own balance to the sale and tells the sale to pay. There is no pull form, so nothing can move a balance its holder did not authorise.

17 lines
05FloorLaunch.sol
A tranche sale whose proceeds fund the floor.

The factory mints 100% of supply to the sale before the sale exists. buy() takes the quote asset and hands the buyer their tokens in the same call, so there is no claim step. Until graduation, FloorToken.sellBack() returns the seller’s cost basis less the sale’s LODE Platform fee. Each buy pays that fee too, on top of the sale price, and the sale holds it apart from the raise. Before any seeding moves a token, graduate() releases the token’s sale lock. It initializes the pool, binds it on the hook, seeds the floor, POL and staircase, and calls ratchet(). The sale has no deadline. It graduates when it sells out. Each sale is a BeaconProxy, and every sale on the chain reads one UpgradeableBeacon. The deployment’s upgrade owner can point that beacon at new code.

259 lines
founder allocation 0 on every launch
06FloorModes.sol
Three modes: Meme / Bluechip / Custom presets.

A pure library that holds no state and reads no oracle. The launchpad calls it at deploy time. It exposes preset(Mode) and hookConfig(Mode, liftMaxBps, platformVault, creator). It also exposes tranchePriceFor(mcapUsd, ethUsd, totalSupply). The enum has three modes: Meme, Bluechip and Custom.

see 11
07FloorHolderRewards.sol
The holder tax’s books: one contract per chain, paid out when a holder claims.

One contract keeps a per-share accumulator for every launch on the chain. Each token calls onTransfer before every balance change. The contract holds no money. Each launch’s holder tax waits in that launch’s own vault. claim(token) has the engine pay what the caller’s balance earned, capped by what that pool accrued. claimMany and claimAll do the same for several launches in one transaction. Protocol addresses are excluded when a launch registers. The contract sits behind a TransparentUpgradeableProxy. Its ProxyAdmin is owned by the deployment’s upgrade owner, who can replace the implementation.

one per chain, behind a proxy
ProxyAdmin → the upgrade owner
see 12
08FloorVesting.sol
Lock & vest, one contract for every token.

lock(token, beneficiary, amount, cliff, duration) records an immutable schedule. claim(id) pays only the beneficiary. A lock cannot be revoked or released early.

see 13

Orientation (read before touching any sign)

currency0 = native ETH (address(0)), currency1 = LODE
pool price = token1 / token0 = TOKEN per ETH
P_eth      = 1 / poolPrice

higher tick   == more TOKEN per ETH == CHEAPER token
"floor up"    == floorTick DOWN
bid (all ETH) sits ABOVE the current tick
ask (all TOK) sits BELOW the current tick

hard ordering:   askTick < currentTick < floorTick

Pool state that matters · poolState(poolId)

vaultEthidle ETH booked to the pool, held in its FloorPoolVault, not deployed
bidEthDeployedETH inside the bid at deploy time
inventoryTokens / inventoryCostEthvault tokens and their cumulative ETH cost
askTokensDeployedinventory currently inside the ask
floorTickthe published floor: the bid band’s cheap edge, the lowest price any fill in the band can pay. The tick only ratchets down, to a higher price. It starts at ceil(MAX_TICK)
bidLower / bidWidth / bidLiquiditythe deployed band [bidLower, bidLower + bidWidth]. When the band is whole, bidLower = floorTick − bidWidth is its dear edge, where a sell fills first
skimBuyAtFloorBps / skimSellAtFloorBpsthe ramp’s at-the-floor endpoints, per pool, fixed at registration: 0, and the mode’s multiple × the far sell rate (1.5× on a preset and on $LODE, and 1× to 5× on Custom, as its launcher picks)
the staircasefixed token-only steps, one doubling of the price each, and the open tail above them
seedLiq / seedLower / seedUpperprotocol-owned position, only grows
cumulativeVolumeEth / unpaidSkim / needsRatchetvolume (read by nothing that prices), bounty source, re-solve flag

02 — Floor math

Floor = backing ÷ circulating supply

The floor comes from the pool’s backing divided by the tokens that could be sold into it. Backing, Q, is the pool’s ETH: what sits idle in its vault plus what is inside the bid. S counts only tokens that could actually be sold into the bid. The hook solves for the highest price at which Q buys every one of those tokens. It publishes the floor one narrow band, about 1.2%, below that price.

In pool state, Q is vaultEth + bidEthDeployed. S leaves out the vault’s inventory, the staircase’s unsold tokens and the tokens the protocol-owned position holds at the floor price.

// backing per circulating token
F = Q / S_circ

// the same ratio, two readings
floor / price = backing / mcap

// selling q ETH into the bid removes q/F tokens
F' = (Q − q) / (S − q/F)
   = (Q − q) / (S·(Q − q)/Q)
   = Q / S = F                 // invariant under own fills

// bid capacity in tokens: the whole circulating supply
Q / F = S_circ

// where step n of the staircase starts selling
Step(n) = open × 2^n

F is the price at the bid band’s dear edge. The published floor, floorTick, is one band cheaper. The band is BID_BAND_TICKS = 120 ticks, rounded up to the spacing: about 1.2% in price (see 04 and 05). The floor rises only from skim, ask proceeds, staircase proceeds and seed-fee sweeps. A fill of the bid itself leaves it unchanged.

Every launch sells its reserve on a staircase: fixed token-only steps, each one doubling of the price. A preset’s steps run from the graduation market cap to about $1B, and $LODE’s from its open to about $2.6B. A Custom launch takes one step for each doubling from its graduation market cap to about $1B, at most 16, each holding the same tokens. At each ratchet the money the steps took is split between the floor and the protocol-owned position (see 04 and 11, and 15 for measured figures).

The hook takes its fee in ETH only. A fee worth V taken as ETH gives F = (Q+V)/S. Taken as tokens and retired, it gives F = Q/(S − V/P). The second is larger only when Q + V > S·P, which means backing above market cap. That does not hold while the pool trades.

// what the bid must cover
sellable(F) = totalSupply − inventory − steps − T_pol(F)

// T_pol(F): tokens the protocol-owned position holds at the
// floor price. A concentrated position's composition is a
// pure function of price, so this is exact, not a bound.
polTokensAtTick(t) = getAmount1Delta(sa, min(sp, sb), seedLiq, false)

// the dear edge is the highest F with  F · sellable(F) <= Q
// F on both sides: a fixed point, bisected in tick space
_solveFloorTick(q):
  hi = ceil(MAX_TICK), lo = ceil(MIN_TICK)
  if sellable(lo) < MIN_SELLABLE        return floorTick − W   // the dear edge, unchanged
  if cost(hi) > q                       return hi
  while hi − lo > tickSpacing:
    mid = ceilToSpacing(lo + (hi − lo)/2)
    cost(mid) <= q ? hi = mid : lo = mid
  return hi

cost(t) = _costAtTick(sellable(t), t)
        = tokens · 2^192 / sqrtP(t)^2

// what is published is the band's cheap edge, W = ceilToSpacing(BID_BAND_TICKS)
_publish(solved) = ceilToSpacing(solved + W)

03 — Fee and skim schedule

How a swap is charged: the LP fee and the skim

A trader pays five things on a swap: the 0.25% LP fee, the floor skim, the creator fee, the holder tax and the LODE Platform fee. This section covers the first two, and 06 lists them all. The skim is taken from the ETH side of the trade and goes to the launch’s vault, less the $LODE cut. Its rate ramps with the price’s distance from the floor. A buy’s skim falls to zero at the floor and a sell’s rises to 1.5× its far rate on a preset and on $LODE. A Custom launch picks its own multiple, 1× to 5×.

The pool is created with DYNAMIC_FEE_FLAG. On every swap the hook overrides the fee with LP_FEE | OVERRIDE_FEE_FLAG. Each pool has four skim rates: far from the floor and at it, on each side. They are written when the pool is registered, and nothing can change them after that. The ramp’s width is the same for every launch, and it is measured to the bid band’s dear edge. The LODE Platform fee is taken in the same step as the skim, at the pool’s own flat rate: 0.25% today, never above 0.5%, and 0 allowed (see 08).

LP_FEE

2500

hundredths of a bip = 0.25%, flat

skimBuyFarBps → skimBuyAtFloorBps

≤ 1000 → 0

bps of the ETH leg, per pool (modes: 150 / 200 far; 0 at the floor, so buys ramp to 0% skim)

skimSellFarBps → skimSellAtFloorBps

≤ 1000 → ≤ 1500

bps, per pool (presets: 200 far, 1.5× at the floor; Custom picks 1× to 5×, and a sell at the floor never pays more than 15%)

SKIM_RAMP_TICKS

16094

≈ 5× in price; ramp width above the band’s dear edge

_skimBps(zeroForOne):
  distance  = (floorTick − bidWidth) − currentTick   // to the DEAR edge; ≤ 0 inside the band
  closeness = distance <= 0            ? 10_000
            : distance >= RAMP_TICKS   ? 0
            : 10_000 − distance · 10_000 / RAMP_TICKS

  buy  (zeroForOne):  buyFar  − (buyFar − buyAtFloor)   · closeness / 10_000   // or + if buyAtFloor > buyFar
  sell (!zeroForOne): sellFar + (sellAtFloor − sellFar) · closeness / 10_000   // flat if sellFar ≥ sellAtFloor

fee  = ethLeg · bps / 10_000                 // floor skim → vault (less the $LODE cut)
cfee = ethLeg · creatorBps(dir) / 10_000     // creator fee → creator (see 09)
tfee = ethLeg · teamFeeBps / 10_000          // LODE Platform fee, the pool's own rate → teamFeesOwed (see 08)
poolManager.take(currency0, custodyVault, fee + cfee + tfee)
vaultEth += _intake(fee); unpaidSkim += kept; creatorFeesOwed += cfee; teamFeesOwed += tfee

cumulativeVolumeEth adds the ETH leg of every swap, skimmed or not. A 0%-skim buy at the floor still counts. The hook’s own nudges are swaps with sender == address(this). They pay the LP fee, but no skim, and add no volume. The nudges are the re-anchor inside a sell and the ratchet’s dust crossing (see 05). custodyVault is the launch’s own FloorPoolVault. The hook takes every fee straight into it and holds none itself.

// ETH is the SPECIFIED currency exactly when
_ethIsSpecified(p) = p.zeroForOne == (p.amountSpecified < 0)

One boolean decides which callback skims. When ETH is the specified currency, the fee is returned through BeforeSwapDelta. In the other two cases, afterSwap takes it from the unspecified leg and returns it as the hook delta.

04 — Positions

The four positions the hook owns in each pool

The hook holds four kinds of liquidity position in every launch’s pool. The bid holds the vault’s ETH and buys tokens at the floor. The protocol-owned position and the staircase sell tokens as the price rises. The ask re-sells the tokens the vault bought, at twice their cost or more.

Every modifyLiquidity call runs inside unlockCallback, with an action code for the ratchet, the seed or the staircase. The pool id rides at the end of the callback data. That lets one hook own positions in every launch’s pool without two launches sharing a position. Each position has its own salt.

01Bid · BID_SALT = 1
One all-ETH band, its cheap edge pinned to the published floor.

The range is [floorTick − W, floorTick], with W = _ceilToSpacing(BID_BAND_TICKS = 120). That is 120 at spacing 60, about 1.2% in price. floorTick marks the band’s cheap edge: the lowest price a sell into the bid can receive. The dear edge, bidLower = floorTick − W, is where the backing solves and where a sell fills first. Read it with bidRawQuotePerToken. A partial fill leaves the market inside the band. The re-place then keeps only the part still above the market, so the band narrows from the dear side: lower = max(floorTick − W, _ceilToSpacing(current + 1)). The cheap edge does not move, so sells do not walk the band down one spacing at a time. The band’s token capacity is Q / geomean(edges) > sellable, so no sequence of sells carries the price through it. It falls back to one spacing above market only when the whole band is behind the market. That happens once the sellable float is absorbed, or for a pool that arrived in a gap. _liqForAmount0(sa, sb, vaultEth) sizes it, and it is skipped below MIN_BID_ETH = 0.001 ether. bidWidth records the placed width. A bid placed before the band existed has a width of 0, read as one spacing.

BID_BAND_TICKS 120
teardown: spent = bidEthDeployed − ethBack
inventoryTokens += tokBack
inventoryCostEth += spent
02POL · SEED_SALT = 3
Protocol-owned seed, tokens only, from the open to the top of the tick range: the tail.

seedLiquidity(tickLower, tickUpper, tokenAmount) is payable and permissionless. The first call fixes the range, and a later call with another range reverts SeedRangeFixed. Ticks must be spacing-aligned. FloorLaunch sets lower = _ceilToSpacing(TickMath.MIN_TICK), which is v4’s minUsableTick (−887220 at spacing 60). It sets upper = _floorToSpacing(open), so upper ≤ current and the position is 100% tokens. That gives buy-side depth at every price above the open. The tail has constant liquidity: it thins as price rises but never runs out. The ETH a buy leaves in it is reversible, because it pays sellers on the way down. That ETH is not backing. No code path removes the position. Each ratchet raises its lowest-price edge to the bid band’s dear edge (raisePol), so nothing of the protocol’s ever sells inside the band. Each ratchet, _sweepSeedFees also does a zero-delta modifyLiquidity. ETH fees go to vaultEth, and token fees go to inventory at zero basis.

seedLiq only grows
fees swept to seedFeesSweptEth / Tokens
03Ask · ASK_SALT = 2
The vault keeps the tokens it buys and re-lists them as one band four doublings wide.

The range is [at − RECYCLING_WIDTH_TICKS, at]. The top, at, starts as FloorMath.askTick(inventoryCostEth, inventoryTokens, ASK_MULTIPLE_BPS, floorTick, MIN_ASK_SPREAD_TICKS, tickSpacing). Then at = min(at, _floorToSpacing(current)), so an ask the market has already passed moves up to market instead of sitting idle. The ask is deployed only if current >= upper. With MIN_ASK_SPREAD_TICKS = TICKS_PER_DOUBLING, the ask never sits within 2× of the floor. The vault buys at or below the floor, so the band normally runs 2× → 8× of the floor. The multiple is fixed and does not move with cumulative volume. If at − RECYCLING_WIDTH_TICKS would pass MIN_TICK, the inventory stays idle and the band is not narrowed. At teardown, tokBack >= deployed is fee income, joining inventory at zero basis. Otherwise the sold share retires basis pro rata. Either way, inventory ends at before − deployed + returned.

ASK_MULTIPLE_BPS = 20_000 (2× cost)
RECYCLING_WIDTH_TICKS 13_862 (4×)
→ 13_920 at spacing 60
askWidth records the placed width
04Staircase
Every launch’s reserve as fixed token-only steps, one doubling of the price each.

On Cook, Bluechip and Moonshot, 69% of supply sits in the steps. Graduation places them from the graduation market cap up to about a $1B market cap, and 1% of supply as an open tail above them. On Moonshot each step holds the same number of tokens. On Cook and Bluechip each step up holds 1.04× the tokens of the one below it. The steps never move. Because every doubling sells about the same number of tokens for twice the money, the money the steps take grows with the market cap. At each ratchet the hook takes out what the steps sold and splits it by two numbers: the floor over the price, with a target of 20%, and the protocol-owned position’s money over the market cap, with a target of 15%. Each gets a share in proportion to how far it is short of its target. When both are met, all of it goes to the floor. The protocol-owned position’s share is placed as bids from 90% of the price up to the price, and it follows the price up at later ratchets. It is never swept to the floor. The $LODE cut applies to what reaches the vault. The tokens the vault buys at the floor join the step just above the price, at no less than twice what it paid. Their money goes through the same split when they sell. A Custom launch’s steps hold its supply less its sale, its protocol-owned position and the 1% open tail. They run from its graduation market cap toward about a $1B market cap, one step for each doubling, at most 16, and each step holds the same number of tokens. With its default 30 / 10 / 60 split, a Custom launch softens a 3% sell about as the presets do from a graduation market cap of about $200k, and less below it (see 16). $LODE runs its own staircase. 93% of its supply sits in steps from its open to about a $2.6B market cap, as many doublings as fit. The protocol owns the bottom 13% and the team owns the 80% above it. Each step up holds 0.95× the tokens of the one below it, and 1% of supply is the open tail. One team step’s unsold tokens can be withdrawn every 30 days from day 30, lowest step first. Its protocol-owned position’s target is 20% of the market cap, and its bids sit from 70% of the price up to the price. The genesis sets the top in SOL before the raise opens, and the step count is fixed when the raise closes. With ETH at $2,400 and a steady climb, Cook’s vault holds $30.3k at a $1M market cap, $308k at $10M, $3.57M at $100M and $41.0M at $1B (see 16).

20% sale · 10% POL
69% staircase · 1% open tail
floor ÷ price target 20%
POL money ÷ mcap target 15%
Custom: round(log2($1B ÷ cap)) equal steps, 1–16
$LODE: 5% sale · 1% POL
13% protocol steps · 80% team steps
1% open tail · 0% vesting
POL money ÷ mcap target 20% · bids from 70%

Liquidity helpers (overflow-safe)

_liqForAmount0(sa, sb, amount0):
  l = mulDiv(amount0, mulDiv(sa, sb, Q96), sb − sa)
  return l > type(uint128).max ? 0 : uint128(l)

_liqForAmount1(sa, sb, amount1):
  l = mulDiv(amount1, Q96, sb − sa)
  return l > type(uint128).max ? 0 : uint128(l)

// 0 means "not deployed"; the ETH / tokens stay idle in the pool's
// vault instead of reverting the whole ratchet on an extreme range.

Views · execute(poolId, selector), decoded twice

circulatingSupply()totalSupply − inventoryTokens − the staircase’s unsold tokens
sellableSupply()circulating − polTokensAtTick(floorTick)
backingEth()vaultEth + bidEthDeployed (upper bound mid-fill)
backingRawQuotePerToken()backingEth · 1e18 / sellableSupply, in raw quote per whole token. This is F, the ratio the dear edge solves to, before spacing. It is not the published floor.
bidRawQuotePerToken()price at bidLower, in raw quote per whole token: the band’s dear edge, where a sell fills first. It reads 0 when there is no bid. It falls as a part-filled band narrows, until the next ratchet.
the published floorprice at floorTick, the cheap edge. No view prints it: read floorTick (or Ratcheted) and convert
bidWidth()the deployed band’s width. A bid placed before the band reads 0, meaning tickSpacing
liveFloorTick()_publish(_solveFloorTick(backingEth)), before the ratchet guard: the cheap edge the current backing supports

05 — ratchet()

How a ratchet raises the floor

Anyone can ratchet a pool. A ratchet takes the pool’s positions out, pays the caller a small bounty from new skim, and solves the floor again. It then publishes the new floor and places the positions back. The published floor can only move to a higher price. Between ratchets, a sell that fills the bid re-places the bid inside the same swap.

The call is execute(poolId, ratchet()), one pool at a time. It reverts NotBound until that pool’s bindPool. Otherwise it unlocks the PoolManager with ACTION_RATCHET and runs _ratchet(msg.sender). The bid is a band whose cheap edge is the published floor, so no gap opens between the bid and the floor. Between ratchets, the only thing that buys is the re-anchor inside a sell (below).

_ratchet(beneficiary):  // no-op guard against rounding drains (no lift, so no "gap" clause)  if !needsRatchet && unpaidSkim == 0 && vaultEth < MIN_BID_ETH:    bidCanRise = bidLiquidity > 0 && floorTick − W < bidLower               && ceilToSpacing(cur + 1) < bidLower    if !bidCanRise: return  needsRatchet = false; floor0 = floorTick  _redeemLiftClaims()          // the in-swap re-place's ERC-6909 claims come home  _sweepSeedFees(key)          // zero-delta modifyLiquidity on SEED_SALT  // the staircase: what the steps sold is split before the solve  _teardownBid(key)            // tokens → inventory at cost  _teardownAsk(key)            // ETH → vault; fee income vs sold accounting  // everything that leaves before the bid is placed leaves BEFORE the solve  bounty = min(unpaidSkim · BOUNTY_BPS / 10_000, MAX_BOUNTY, vaultEth)  unpaidSkim = 0; pay beneficiary (failure re-credits vaultEth)  reserve = min(vaultEth, MIN_BID_ETH)        // the nudge's dust, held out of Q  for i in 0..2:                              // solve, then the POL follows the floor    if vaultEth <= reserve || circulatingSupply() == 0: break    candidate = _publish(_solveFloorTick(vaultEth − reserve))   // dear edge + W    if candidate < floorTick: floorTick = candidate     // ratchet guard    else if i != 0: break    if !_raisePol(): break  _nudge(key, reserve)         // up to the dear edge; free across an empty gap  stand = ceilToSpacing(cur + 1 + W)          // the closing clamp  if floorTick < stand: floorTick = min(stand, floor0)   // withholds a rise, never lowers  // the vault's tokens join the step above the price, at ≥ 2× cost  at = _deployAsk(key); _deployBid(key)  emit Ratcheted(floorTick, at, vaultEth, inventoryTokens, bounty)
BOUNTY_BPS200 bps of the skim since the last ratchet, never of the vault
MAX_BOUNTYa deploy immutable; 0.05 ether on ETH-native chains
MIN_BID_ETH0.001 ether; also the nudge’s dust budget
MIN_SELLABLE1_000e18; below it _solveFloorTick returns the dear edge unchanged
BID_BAND_TICKS120, rounded up to the spacing = W; ≈ 1.2% in price
needsRatchetset by seedFloor, seedLiquidity, seedInventory, and every in-swap re-place

Each teardown and redeploy loses a wei or two to v4’s rounding, which runs against the liquidity provider. The no-op guard at the top stops a spammer turning idle ratchets into a slow drain. For the same reason, the bounty is paid from unpaidSkim only. The bounty and the nudge’s reserve both leave before the solve. So the bid always holds at least the Q its floor was solved on. If they left after, the bid could land up to MAX_BOUNTY short of the floor it was solved for. The closing clamp holds the published floor where the bid can stand: one spacing above where the market ended, plus W. So a third party parked in the gap delays the rise, rather than leaving a bid under the floor. The clamp never lowers the standing floor.

In-swap re-anchor · replaceBidInSwap, inside a sell that filled the bid

afterSwap, on a sell:
  (bidEth, bidTok) = _bidLive(sqrtP)     // exact mid-fill composition
  if bidTok > 0: self.call(replaceBidInSwap)   // an empty revert fails the swap closed

replaceBidInSwap():                      // hook only, inside the NET window
  fold earlier claims into the net       // every wei of vaultEth: in custody or a credit here
  _teardownBid(key)                      // tokens absorbed → inventory at cost; ETH left → vault
  _nudge(key, vaultEth)                  // RE-ANCHOR: back up to the dear edge, across the emptied band
  _deployBid(key)                        // the whole band again; cheap edge = floorTick, unchanged
  needsRatchet = true
  net quote debit → settled from custody; net credit → ERC-6909 claims (liftClaims)
  // no floor re-solve, no bounty; costs 0 wei in every honest state

A sell that crosses into the band leaves tokens in the bid. A two-sided v4 position would resell them at the floor, skim-free, to the next buyer. So the hook takes the filled bid out inside the same sell, and places it again with the ETH it has left. Without the re-anchor, each re-place would give up the ticks between the market and the next spacing boundary. A few small sells would then close the band from the dear side: at spacing 60 with a 120-tick band, two are enough. The nudge crosses the bid’s own just-emptied band, so it spends nothing. Anything a third party parked there was filled by the seller at those same ticks a moment earlier. liftClaims holds the ERC-6909 claims this netting produces.

The band · _nudge, _deployBid

W = ceilToSpacing(BID_BAND_TICKS)      // 120 → 120 at spacing 60
dearEdge = floorTick − W               // where the backing solves; a sell fills here first

_nudge(key, budget):                   // buy up to the dear edge, spending at most budget
  if budget == 0 || price already at or above dearEdge: return
  _buyUpTo(key, budget, dearEdge)      // bought tokens → inventory at cost

_deployBid(key):
  if vaultEth < MIN_BID_ETH: return
  upper = floorTick                    // the cheap edge: the published floor
  lower = max(upper − W, ceilToSpacing(current + 1))   // a partial fill narrows from the dear side
  if lower >= upper: upper = lower + spacing            // last resort: one spacing above market
  bidLiquidity = liqForAmount0(lower, upper, vaultEth)
  bidEthDeployed = owed; bidLower = lower; bidWidth = upper − lower

The POL’s edge, the nudge’s limit and the skim ramp all measure to the dear edge. So nothing of the protocol’s sits inside the band, and the market rests just above it.

06 — Every fee

Every fee a trade can pay, and where each one goes

A trade on a LODE launch pays up to six fees. Each launch keeps the rates it was given, and nothing can change them afterwards. This section lists them all, and the sections after it explain each one.

Two more amounts come out of these fees rather than on top of them. The $LODE cut is 2.5% of every inflow to a launch’s vault, taken from the skim and the raise (07). The referral share is 10% of the creator fee, taken from the creator’s side (10). Neither changes what the trader pays.

07 — The $LODE cut

Where the 2.5% $LODE cut goes

Every launch pays the platform 2.5% of what flows into its vault. On a ETH launch on Ethereum, LODE’s own chain, a keeper sweeps that cut into the $LODE token’s pool. On every other launch, its chain’s platform treasury holds it in the launch’s own quote asset.

LODE is the launchpad and a token. Every launch is a pool on its chain’s single hook. When a pool registers, the hook gives it a destination for the cut, from the destinations its deployer set once. On an EVM chain that call is configurePlatform(treasury, genesisFactory, teamTreasury). The team treasury is where the LODE Platform fee is swept; it is never the platform treasury and it takes no cut. The Ethereum contracts that send a ETH launch’s cut to $LODE are ruled and not yet deployed.

SOL, on Ethereumthe cut is booked on the launch’s own pool, and a permissionless keeper sweeps it into $LODE’s pool on the same cluster once $LODE has graduated
Everything elsea token-quoted launch on Ethereum, and every launch on an EVM chain, pays its chain’s platform treasury. No EVM chain has a $LODE pool (genesisFactory == address(0)), so an ETH launch’s cut is held there in ETH.
$LODE’s own poola Solana pool that forwards nothing; the keeper sweeps Solana’s SOL cuts into it

Each chain’s treasury takes the cut in the pool’s own quote and holds it per asset (on EVM, balanceOf[quote], with ETH at address(0)). Governance can withdraw whatever the treasury holds. Nothing is bridged, and none of the treasury’s holdings reach the $LODE pool.

$LODE is an Ethereum launch, and its pool runs the same hook on its own terms. The rates set for it are a 2.5% floor skim and a 0.5% creator fee each side, and no holder rewards. After its raise its pool pays the LODE Platform fee like every other pool (0.25% today), and the timed raise itself pays none. Its sale is the only raise on LODE that runs on a clock, and it has no target, cap or minimum. While the clock runs, anyone can bid SOL. When it stops, the tokens on offer split pro rata by contribution at one clearing price: total raised ÷ tokens offered. Each bidder claims their share once the pool is seeded. The pool pays no cut to itself, so all of what it raises becomes backing.

uint256 public constant PLATFORM_BPS = 250;
address platformVault;       // per pool, written once at registerPool (see 07, 08, 09)
bytes32 platformPoolId;      // LODE's pool, when platformVault is the hook itself
uint256 platformPaidEth;     // lifetime forwarded (launched pools)
uint256 platformReceivedEth; // lifetime received  (LODE's own pool)

function _intake(uint256 amount) internal returns (uint256 kept) {
    if (platformVault == address(0) || amount == 0) return amount;
    uint256 cut = (amount * PLATFORM_BPS) / 10_000;
    if (cut == 0) return amount;
    platformPaidEth += cut;
    if (platformVault == address(this)) {      // a native pool on a set with a LODE pool; no EVM chain has one
        PoolState storage target = pools[platformPoolId];
        if (target.quoteToken != quoteToken) revert BadQuoteToken();
        FloorPoolVault(custodyVault).payQuote(target.custodyVault, cut);
        target.vaultEth += cut;
        target.platformReceivedEth += cut;
        target.needsRatchet = true;
    } else {                                   // the treasury: any stock pool, or an ETH pool with no LODE pool here
        FloorPoolVault(custodyVault).forwardQuote(platformVault, cut, false);
    }
    emit PlatformCut(poolId, cut);
    return amount - cut;
}

Every inflow passes through _intake before the vault books it:

RaiseseedFloor()
Skim_beforeSwap and _afterSwap; unpaidSkim and the bounty base are the kept 97.5%
Staircase proceedsthe floor’s share of what the steps sold, at the ratchet
Ask proceeds_teardownAsk
ETH fees_sweepSeedFees

The bid’s own ETH returning in _teardownBid is the vault’s money coming home, so it never passes through _intake. The code above is the EVM hook. No EVM chain has a $LODE pool, so there the launch’s vault pays the treasury’s intake. Only a registered pool’s own vault may call it, and it credits balanceOf[quote]. Every vault payment holds a transient reentrancy lock.

On Ethereum nothing moves to $LODE during a trade. The program books a ETH launch’s cut on the launch’s own pool. A permissionless keeper sweeps booked cuts into $LODE’s pool, and until $LODE graduates they stay where they were booked.

The skim column is the floor skim alone. The two all in columns are what the trader pays, which is why Bluechip’s 2% skim reads 3.492%. Skim, creator fee, holder tax and the LODE Platform fee (today’s 0.25% pool rate) are four percentages of the same ETH leg, so they are summed. The pool’s 0.25% LP fee is then charged on what is left, so the two compose multiplicatively. Buys slide to 0% skim at the bid. Sells climb to 1.5× their far rate on Cook, Bluechip, Moonshot and $LODE. A Custom launch picks its own multiple, 1× to 5×. So the at-the-bid pair is asymmetric in every mode. Each pool pays the LODE Platform fee its launch was created with. At the 0.5% cap every all-in figure is about 0.25 points higher, and at 0 about 0.25 points lower.

Every preset’s far skim is 2%, so the presets differ only by their creator fee. Far from the bid, $LODE costs the same as Bluechip and more than Cook or Moonshot. On a buy at the bid it costs the same as Moonshot, and on a sell into the bid it costs more than every preset. Moonshot is the cheapest preset at both ends. 2.5 of $LODE’s 3.25 points are floor skim, booked under the token.

$LODE’s floor grows with the platform’s ETH volume on its chain: F_LODE = (Q_LODE + Σ 0.025 · inflows_i) / S_LODE, summed over every ETH pool i whose cut lands there. Nothing can withdraw it from there.

Source: the reference launch in 15 (raise ≈ $297k), ETH = $2,400, before the LODE Platform fee.

08 — The LODE Platform fee

LODE’s own fee on sales and swaps

LODE charges a fee on each sale buy, each sale sell and every pool swap. The trader pays it on top of the price. It never enters the raise or the vault, so it never moves the floor.

There are two rates, one for the sale and one for the pool. A governance key sets the current value of each, from 0 up to its cap. A launch copies both values when it is created and keeps them for life.

At the default sale rate, a $1,000 buy costs $1,010. Selling those tokens returns $990, because the sell pays the fee too and the buy’s fee is kept.

In the saleheld in the sale’s own escrow, recorded apart from the raise
At graduationhanded to the pool’s owed line before the raise is seeded
After graduationtaken in the hook’s fee step beside the skim and the creator fee
Sweptanyone may sweep a batch of pools, and a sweep can only pay the team vault
Team vaultone per chain, a balance per asset, withdrawn by the team key

09 — The creator fee

A flat fee of up to 5% a side, shared among up to five recipients

Whoever launches a token can charge a flat fee on every buy and every sell, up to 5% a side. The fee goes to one to five recipients set at launch, each with a share, and the shares add up to 100%. The launchpad defaults to the launcher’s own wallet at 100%. After launch, nobody can change the rates, the recipients or their shares, including the creator and LODE.

The factory writes the recipient list and both rates into the launch’s pool once, when it registers the pool. The hook has no setter for any of them.

uint256 public constant MAX_CREATOR_BPS = 500;     // 5% each way, hard cap
uint256 creatorSlotCount;                          // 1–5 recipients, written once at registration
uint256 creatorPendingMask;                        // bit i set while slot i is a handle not yet claimed
uint256 creatorBuyBps;
uint256 creatorSellBps;
uint256 creatorFeesOwed;                           // accrued, unpaid, every slot together
uint256 creatorFeesPaid;                           // lifetime paid out, every slot together
Payee[5] creatorSlots;                             // each slot: wallet, share in bps, paid so far

registerPool(key, config, launch, referrer2, referrer3, creatorSlots)
initialize(c, launch)                              // BadRange if a rate > cap, or a rate with no recipient

// _beforeSwap / _afterSwap, per swap (currency0 = ETH, zeroForOne = buy):
fee  = ethLeg · skimBps(dir) / 10_000              // to the vault, after the $LODE cut
cfee = ethLeg · creatorBps(dir) / 10_000           // to the fee recipients, no $LODE cut
tfee = ethLeg · teamFeeBps / 10_000                // to the team at the pool's own rate, owed on the pool, no $LODE cut
poolManager.take(ETH, custodyVault, fee + cfee + tfee)
vaultEth += _intake(fee); creatorFeesOwed += cfee; teamFeesOwed += tfee
return delta(fee + cfee + tfee)

function claimCreatorFees(uint8 slot) external returns (uint256 paid) {
    Payee storage p = creatorSlots[slot];          // RecipientPendingClaim while the slot waits on its handle
    uint256 lifetime = creatorFeesPaid + creatorFeesOwed;
    paid = min(lifetime · p.bps / 10_000 − p.paid, creatorFeesOwed);
    creatorFeesOwed −= paid; creatorFeesPaid += paid; p.paid += paid;
    bool ok = _payQuote(p.wallet, paid);           // out of the pool's vault, only ever to that slot's wallet
    if (!ok) revert CreatorTransferFailed();
}

The hook takes the creator fee in the same take as the skim and returns both in the same hook delta. A swap therefore pays five things: the LP fee (0.25%), the floor skim (0–15%, ramped, to the vault), the creator fee (flat, ≤ 5%), the holder tax (flat, ≤ 10%) and the LODE Platform fee (0.25% by default, ≤ 0.5%).

The creator fee is not a vault inflow. It never passes through _intake, so $LODE takes no cut of it. It is never counted in vaultEth, unpaidSkim or the bounty base, so it never moves the floor. The launch’s FloorPoolVault therefore holds idle vault ETH plus every fee it owes: to the fee recipients, the holders, the referrers and the team. Its ETH balance covers them all after every completed operation.

Each slot earns its share of everything the pool has ever booked for the creator, rounded down. A claim pays the slot what it has earned less what it was already paid. The rounding leaves at most a few wei per pool in the vault.

A slot is claimed with execute(poolId, claimCreatorFees(slot)) on the hook. Anyone may send that call, and the ETH goes only to that slot’s wallet.

Immutabilityno setter for the rates or the shares; a slot’s wallet is written at registration or, for a handle still pending, once by FloorRecipientClaims.claimPool
Claimpermissionless pull of one slot through execute(poolId, …); a keeper or the launchpad may trigger it, the ETH lands only with that slot’s wallet
Bad recipienta recipient that rejects ETH strands only its own share; the other slots, trading, the ratchet and the bid are unaffected
LODE itself0.5% / 0.5% to its configured recipient, like any Custom launch; no $LODE cut either way, and the LODE Platform fee after its raise

10 — Referrals

The referral share, split across up to three referrers

A launch can name the wallet that referred its creator. That referrer earns 10% of the launch’s creator fee, and the fee recipients share the other 90%. The trader pays nothing extra.

A launch may also name the wallet that referred the referrer, and the wallet above that one. Each referrer keeps 90% of what reaches it and passes 10% up to the next.

The shares are of the referral share, not of the trade. On a launch with three referrers, a $100 creator fee sends $90 to the fee recipients, $9 to tier 1, $0.90 to tier 2 and $0.10 to tier 3.

Who may refera referrer is allowed only when the creator’s own wallet holds one of the fee recipient slots, and no referrer is the creator or a recipient
Tierstier 2 needs tier 1 and tier 3 needs tier 2; no wallet appears twice, and none is the creator or a fee recipient
Written oncethe factory passes every tier to the hook when it registers the pool; the hook has no setter
Bookedeach swap adds to one referral total; the split happens when a tier claims
Claimanyone may send a tier’s claim, and it pays only that tier’s wallet; one call can claim many launches and tiers at once

11 — Launch modes

Meme, Bluechip, Custom: three bundles of fixed settings

A mode is a preset bundle of launch settings. It fixes the market cap at which the token graduates, the skim far from the floor, the creator fee and the holder tax, which the presets leave at zero. Meme and Bluechip are fixed. Only Custom can be edited.

The modes live in src/FloorModes.sol. The launchpad does not let anyone edit Meme or Bluechip, because a preset is useful only if everyone recognises it. It converts the cap to a tranche price with the ETH price at deploy time. Nothing on-chain reads an oracle.

The enum has three members, with the ordinals Meme 0, Bluechip 1, Custom 2. FloorFactory casts the ordinal to uint8, and FloorRegistry stores it permanently. A registry written under one numbering cannot be re-read under another. A set of contracts therefore moves to a new enum by being redeployed, never by an upgrade in place.

library FloorModes {
    enum Mode { Meme, Bluechip, Custom }
    struct Preset { uint256 graduationMcapUsd; uint256 creatorBps; uint256 skimBuyFarBps; uint256 skimSellFarBps; }
    function preset(Mode) internal pure returns (Preset memory);
    function hookConfig(Mode, uint256 liftMaxBps, address platformVault, address creator)
        internal pure returns (FloorHookStorage.Config memory);   // liftMaxBps: still passed and range-checked, read by no engine path
    function tranchePriceFor(uint256 mcapUsd, uint256 ethUsd, uint256 totalSupply)
        internal pure returns (uint256);   // mcapUsd·1e36 / (ethUsd·totalSupply)
}

The near-floor ramp has the same shape in every mode. Buys slide to 0% at the floor. Sells climb to 1.5× the far rate on a preset. On Custom the launcher picks the multiple, 1× to 5×. Only the skim ramps, and no rate can change after launch (see 03). Custom is the empty preset, so the hook’s own caps are its only limits. The hook caps the far skim at 10% (MAX_SKIM_FAR_BPS) and the creator fee at 5% (MAX_CREATOR_BPS). It caps the holder tax at 10% (MAX_HOLDER_BPS).

The launchpad’s presets

The launchpad offers three fixed presets, Cook, Bluechip and Moonshot, beside Custom. Each sells 20% of supply in the sale, so it raises 20% of its graduation market cap. Each keeps 10% as the protocol-owned position and sells 69% on the staircase, with 1% as an open tail (see 04).

Moonshotgraduates at $25k and raises $5k · 15 equal steps to about $819M · skim 2% / 2%, creator 0.5%
Cookgraduates at $50k and raises $10k · 15 steps to about $1.64B, each 1.04× the one below · skim 2% / 2%, creator 0.75%
Bluechipgraduates at $100k and raises $20k · 14 steps to about $1.64B, each 1.04× the one below · skim 2% / 2%, creator 1%
Customgraduates at the launcher’s cap · one equal step per doubling to about $1B, at most 16 · its own split, skim and fees
Every presetsteps double the price up to about a $1B market cap · each ratchet splits the steps’ money by floor ÷ price (target 20%) and POL money ÷ market cap (target 15%)

Measured · $LODE cut on, ETH at $2,400

Source: each preset at its rates before 2026-09-25 (Bluechip then graduated at $100k), on a 55/15/30 split with a 2× → 8× ask in place of the staircase, launched with the $LODE cut on, ETH = $2,400.

12 — Holder rewards

How the holder tax is shared with holders

A launch can charge an optional holder tax on every trade, fixed at launch like the creator fee. The tax is owed to the token’s holders, and each holder claims their pro-rata share whenever they choose. Balances never change on their own.

Rebasing balances would change the pool’s token balance underneath the PoolManager, which would corrupt the bid, the staircase and the floor math. Instead, one contract per chain keeps a per-share accumulator for each launch, and the token reports every transfer to it. Claims are continuous rather than in rounds: a claim pays what is owed at that moment.

// hook (Config): holderRewards, holderBuyBps, holderSellBps
uint256 public constant MAX_HOLDER_BPS = 1000;    // 10% each way, hard cap

function _takeFees(key, ethLeg, zeroForOne) internal returns (uint256 total) {
    fee  = ethLeg · skimBps / 10_000            // vault, after the $LODE cut
    cfee = ethLeg · creatorBps / 10_000         // fee recipients, accrued (10% owed to a referrer)
    hfee = ethLeg · holderBps / 10_000          // holders, booked now, paid on claim
    tfee = ethLeg · teamFeeBps / 10_000         // LODE Platform fee at the pool's own rate, owed to the team (teamFeesOwed)
    poolManager.take(ETH, custodyVault, fee + cfee + hfee + tfee)
    …; holderOwedEth += hfee; IFloorHolderRewards(holderRewards).accrue(poolId, hfee);
}

// FloorHolderRewards — one per chain, behind a proxy; books only, holds no money
rewardPerShareE36[token] += amount · 1e36 / eligibleSupply   // on every accrue (1e36, not 1e18 — see the note)
onTransfer(from, to, amount):                            // called by the token, pre-transfer
    settle(from): owed += shares·rps/1e36 − debt; shares −= amount; debt = shares·rps/1e36
    settle(to):   same, shares += amount
claimable(token, who) = owed[who] + shares[who]·rps/1e36 − debt[who]
claim(token): settle(msg.sender); paid = owed; owed = 0; hook.execute(poolId, releaseHolderRewards(msg.sender, paid))

Never earns · fixed when the launch registers

PoolManagerholds the pool’s tokens
FloorPoolVaultthe launch’s inventory
Hookthe chain’s hook
Launch contractthe sale tokens it still holds
Vesting contractlocked tokens
OFT adapterbridged tokens
Token, book, zerothe token itself, the book itself and the zero address

Only an approved factory can register a launch, and the book checks it against the pool’s own state on the hook. An accrue made while nobody holds yet is parked in unallocated and folded into the next one. The hook books the tax on a buy in beforeSwap, before the buyer’s tokens arrive, so a buyer never pays themselves. A wallet whose balance predates binding, such as the deployer’s supply, is mirrored on its first outgoing transfer.

The book holds no money. Each launch’s holder tax waits in that launch’s own FloorPoolVault, counted as holderOwedEth. The engine’s releaseHolderRewards pays each claim. Only the pool’s own book can reach it, through hook.execute, and it refuses to pay more than that pool has accrued. A contract that holds tokens and never claims keeps its share, and nobody else can take it. On Ethereum the same tally is floor_holders, a Token-2022 transfer hook on new launches with a holder tax. It counts only the wallets that have started earning.

The accumulator is rewardPerShareE36, scaled by 1e36 so that a pool can carry a quote of any decimals. At a 1e18 scale, _deposit would floor total · ACC / eligibleSupply. Any deposit under eligibleSupply / 1e18 raw units would then credit nobody and be stranded for good. On an ETH quote with 30% sold, that threshold is 3e8 wei, which is invisible. On a 6-decimal quote the same 3e8 raw units is 300, and a holder tax on a small swap is well under it. With eligibleSupply ≤ 1e27 < 1e36, the remainder per deposit is under one raw unit. The multiplications go through FullMath.mulDiv and round down. There is no per-launch contract: every launch’s book is a slot in the chain’s single FloorHolderRewards (see 01, row 07).

Claimclaim(token) from the holder’s wallet, or claimMany / claimAll for several launches in one transaction; pays the launch’s quote asset; nothing is pushed
Capsholder tax ≤ 10% a side; creator fee ≤ 5% a side; worst case skim + 15.25% with today’s 0.25% LODE Platform fee (skim + 15.5% at its 0.5% cap), then the LP fee
Vaultwaits in it, booked apart from the floor (holderOwedEth); no $LODE cut; floor unchanged by accruals or claims
Wiringone createLaunch: token → registerPool (the pool and its vault) → launch → FloorHolderRewards.registerLaunch(token, poolId, launch, OFT adapter, vault, quote) → token.bindRewards(book) once
Modes0 by default in every mode; opt-in on the create page
Trade-offevery point of holder tax is a point not going under the token

Over five buy-sell rounds with a 2% / 2% holder tax and a 1% / 1% creator fee, $1,415 was deposited for holders. The first five wallets claimed $766, and the rest stayed claimable.

Source: the five-round figures from a single-launch version of the hook, with a book of its own. The accumulator’s figures from HolderRewards.sol:42-43 at ratchet-floor df191ce.

13 — Lock & vest, burn

Locking, vesting and burning tokens

Any holder can lock tokens on a vesting schedule for a beneficiary, or burn tokens they hold. One contract, FloorVesting.sol, serves every token on the platform. Burning is a function on the token itself, FloorToken.burn.

struct Lock { token; beneficiary; total; claimed; start; cliff; end; }
lock(token, beneficiary, amount, cliffSeconds, durationSeconds) → id
    // cliff ≤ duration; tokens pulled now; schedule immutable
vested(id)    = t < cliff ? 0 : t ≥ end ? total : total · (t − start) / (end − start)
claimable(id) = vested(id) − claimed
claim(id)     // anyone may call; pays only the beneficiary

Once a lock is made, nobody can revoke it, unlock it early or change who receives it. The contract has no owner.

function burn(uint256 amount) external {
    _notify(msg.sender, address(0), amount);   // stops earning
    _burn(msg.sender, amount);                 // totalSupply falls
    totalBurned += amount;
}

The hook reads totalSupply() live in circulatingSupply(). A burn therefore shrinks sellable supply against the same ETH, and the floor rises on the next ratchet for everyone else. Only a holder can burn what they hold. The vault never burns its own inventory: it keeps what it buys.

14 — FloorLaunch

How the sale, selling and graduation work

Each launch sells its tokens through a sale contract before its pool opens. The sale holds the quote asset until it sells out and graduates, and until then any buyer can sell at their cost basis, less the sale’s LODE Platform fee. Buyers receive their tokens inside the buy, so there is no claim step.

The sale’s config is immutable. The factory mints the whole supply to the sale when it creates the launch. The sale’s LODE Platform fee is copied into its config at creation and kept for life: 1% today, never above 1%, and 0 allowed. Each buy pays it on top of the sale price, and the sale holds it apart from the raise.

On Ethereum, creating a launch takes two transactions that the creator approves once. The first sets up an address lookup table for that launch, and the second creates the launch through it. Solana’s transaction size limit needs the split, and the table’s rent can be reclaimed afterwards.

struct Config {
  uint256 saleTokens;      // sold in tranches
  uint256 polTokens;       // protocol-owned, token-only, open → ∞ (the tail)
  uint256 vaultTokens;     // the staircase's tokens
  uint256 p0;              // ETH wei per whole token, first tranche
  uint256 p1;              // last tranche; pool opens at p1
  uint8   tranches;
  uint256 floorShareBps;   // share of the raise → seedFloor()
}
// constructor: saleTokens + polTokens + vaultTokens == totalSupply
//              p1 >= p0, p0 != 0, tranches != 0, floorShareBps <= 10_000

tranchePrice(i) = p0 + (p1 − p0) · i / (tranches − 1)
openTick()      = getTickAtSqrtPrice(sqrtPriceX96FromRatio(p1, 1e18))
canGraduate()   = funded && !graduated && sold >= saleTokens
_buy(gross)     = walk(gross · 10_000 / (10_000 + teamFeeBps)); fee = spent · teamFeeBps / 10_000   // the fee rides on top; contributed and raised count spent only
_sell(tokens)   = contributed · tokens / allocation   // your own average price; raised falls by this
sellBack pays   = _sell(tokens) − _sell(tokens) · teamFeeBps / 10_000   // less the sale's LODE Platform fee (1%) → teamOwed
custodyAtBuy    = token.launch() == address(this)     // probed at initialize: true on every factory sale
// the exit: token.sellBack(tokens) -> onSellBack -> _sell; FloorLaunch.sell() reverts UseSellBack
// no claim step: buy() already transferred the tokens; claim() reverts Nothing

Reference launch config

saleTokens / polTokens / vaultTokens300_000_000e18 / 100_000_000e18 / 600_000_000e18
p0 = p1225 gwei (LAUNCH_PRICE_GWEI), flat sale
tranches10
floorShareBps10_000: the whole raise to the bid
POL range at graduation[−887220, open] · MIN_TICK ceiled and open floored to spacing 60
ErrorsNotFunded AlreadyFunded SaleOver CannotGraduate AlreadyGraduated NotGraduated Nothing BadConfig UseSellBack NotToken
01funded
balanceOf(this) == totalSupply(), checked at initialize

The factory mints the whole supply straight to the sale’s predicted address before the sale exists, and binds the token to it. The sale therefore starts funded and delivers at the buy. Founder allocation is 0% on every launch: the factory mints the whole supply to the sale.

02buy()
Walk the tranches with msg.value less the sale’s LODE Platform fee; refund the unspendable remainder.

It reverts SaleOver after graduation or once sold out, and Nothing if zero tokens result. It records allocation and contributed for the sale price only. The fee is booked to teamOwed, apart from raised. Then the sale transfers the tokens to the buyer before refunding the change. All sale state is settled first. Solmate’s transfer has no receiver callback, so the buy hands control to nobody. claim() reverts Nothing rather than paying a second time out of the ask ladder.

03graduate()
initialize(p1) → bindPool → seedFloor → seedLiquidity → seedInventory → ratchet.

floorEth = raised · floorShareBps / 10_000, and the rest pairs into POL. Before anything is seeded, the sale hands teamOwed to its pool’s owed line (bookTeamFees). The LODE Platform fees are therefore never backing and never pay the $LODE cut. Any balance the POL position could not use goes to seedFloor. Unsold sale tokens join vaultTokens on the staircase. It emits Graduated with the raise, the floor and POL ETH, the staircase’s tokens and the open tick.

no ETH left in the launch contract
04sell
The quote asset back, less the sale’s LODE Platform fee, any time before graduation.

The seller calls FloorToken.sellBack(tokens). It moves the seller’s own balance to the sale, then tells the sale to pay through the same _sell. The payout is contributed × tokens / allocation, your own average price, less the sale’s LODE Platform fee. The buy’s fee is not returned. The tranche capacity is released, and raised falls by the whole average-price amount. FloorLaunch.sell() reverts UseSellBack, because the tokens are in the seller’s wallet and paying out without taking them back would be an unconditional drain.

one press
no approval

Sale

30%

300_000_000e18, 10 flat tranches at 225 gwei (reference config)

POL

10%

100_000_000e18, tokens only, open → ∞ (the tail; overlaps the steps)

Staircase

60%

600_000_000e18, equal steps from the open, one per doubling, and a 1% open tail

Founder

0

floorShareBps 10_000; no minimum raise, no deadline

Day-one ratio, with floor share f: floor / price = f · (sold / circulating) · (avg / p1). A flat sale (avg == p1) that sends everything to the floor opens at 100% of the bid-covered supply. The measured 78% backing-to-market-cap headline counts POL tokens in circulating supply.

The counts above are the reference config’s. Cook, Bluechip and Moonshot split supply 20% sale, 10% POL, 69% staircase and 1% open tail (see 04 and 11).

Source: the reference launch config at 225 gwei.

15 — The team buy

A team buy paid out to up to five team payees

A launcher may buy up to 5% of supply in the sale with their own money. What the team buy pays out goes to one to five team payees, each with a share, and the shares add up to 100%.

At least 80% of the team buy goes on team steps, which lock for at least 90 days. The rest is the wallet part, which vests on cliffs. The launchpad defaults to the launcher’s own wallet at 100%.

Every payee is paid its share of three things: the team’s 80% of what the team steps sell for, the team steps’ unsold tokens after the lock, and the vested wallet part.

Each payee earns its share of everything the team has been owed, rounded down. A claim pays one payee what it has earned less what it was already paid. The rounding leaves at most a few raw units in the vault.

Payeesone to five, with shares adding up to 100%; written once when the team buy registers, with no setter
Proceedsone payee per payout; anyone may sweep them, and each share goes only to that payee’s wallet
Unsold tokenseach payee withdraws its own share once the lock ends
Wallet parteach cliff pays every payee its share of what has vested

16 — Measured results

A reference launch under stress, with ETH at $2,400

These tables follow one reference launch. It raised about $297k and split its supply 55/15/30 between the sale, the POL and a genesis ask from 2× to 8×. Its dollar figures are priced at ETH = $2,400. The tables show a pump and then a full dump, a dump on day one, and 30 days of balanced trading. The last table follows Cook, Bluechip and Moonshot on the staircase instead, from graduation to a $100M market cap.

Pump, then everyone dumps

Market cap is price × total supply, as every tracker quotes it. The vault never burns, so tokens it buys back still count, and the cap cannot fall below bid × supply. After the dump the price sits on the bid and the cap is $1.84M. The vault holds 91% of the supply as inventory it will re-list at ≥ 2× cost. Sellers were paid $714k in row three. The vault is the money sellers can take out, so it fills on the pump and empties on the dump.

In this run the bid never falls, and it ends at 3.4× the launch price. The near-floor sell skim retires tokens more cheaply than the bid pays for them. On the live hook, the figure that never falls is the published floor, floorTick. The bid, bidRawQuotePerToken, is the band’s dear edge. It steps down as a part-filled band narrows, until the next ratchet re-solves it.

Day-one dump

30 days of balanced flow · ratchet() once a day

Each day is a set of round trips (buy, then sell everything back), so volume counts both sides. Effective take is vault growth ÷ volume. Far from the floor it is 1.25%: the 1% skim plus the 0.25% LP sweep, with no creator fee or holder tax in this run. On the floor, sells pay up to 10% and buys 0%.

The staircase presets · one steady climb to about a $1B market cap

This table is measured on the live hook with 69% of supply in steps, a 1% open tail and a 15% POL target. Each preset starts from its graduation market cap: $25k for Moonshot, $50k for Cook and $100k for Bluechip. Moonshot’s steps end at about $819M, and Cook’s and Bluechip’s at about $1.64B. Every 5 minutes a buyer spends 0.1% of the market cap, and a ratchet follows. At each row the clock moves 30 minutes and one ratchet runs. Then a single sell of 1% or 3% of the market cap is measured on its own. Bought since graduation leaves out the raise. POL money is what the protocol-owned position and its bids under the price hold. A faster climb leaves those bids further under the price, so the same sells move it more. Past Moonshot’s last step only the open tail is left, and at a $1.17B market cap a 3% sell moves its price 26.6%.

Custom on the staircase · a 3% or 4% sell, to about a $1B market cap

This table is measured on the live hook with every launch’s steps to about a $1B market cap and a 15% POL target. The Custom launches use their default fees: a 1% skim and a 1% creator fee each way, and 1.5× at the floor. Each launch climbs as in the table above. Each range runs from a $1M market cap to the launch’s last step, or from $10M on the $1M-cap launch. The last column is the smallest sell that moves the price more than 20%, read from a $10M market cap.

A Custom launch puts 59% of its supply on equal steps, where a preset puts 69%. Each doubling therefore feeds less money to the bids under the price. With the default 30 / 10 / 60 split, a Custom launch softens a 3% sell about as the presets do from a graduation market cap of about $200k. Below that cap, or with a bigger sale share, the cushion is thinner and a 3% sell can move the price much more. A sell larger than the cushion falls to the floor. Above the last step only the thin open tail is left, so sells move the price more there.

Source: reference launch, 55/15/30 split at 225 gwei, raise ≈ $297k, ETH = $2,400. Staircase and Custom tables: the live hook, each launch at its own fees with the LODE Platform fee and the $LODE cut, ETH = $2,400.

Verify

Read it on-chain.


The floor, the bid and the vault are public on every launch. Check them yourself.
Browse launches →