Roles and Limits
The buzz
"Who can do what?" is the question that matters, so here is the whole answer with no
adjectives. The hook has four keys and one pressed button: the key that opened the pool (used
once), the one that earns the creator's share, the one that owns three settings, the keeper
that only presses compound(), and — beside them — the key that owns the seed's time lock.
Each can do a short list of things and NOTHING else. The list of things nobody can do is
longer, and it is the part we are proud of. Print it. Frame it. Check it against the bytecode.
The boring part
flowchart TB
subgraph PC [poolCreator · immutable]
A1[create the one pool] --- A2[open trading<br/>once, one-way]
end
subgraph CR [creator · two-step]
A3[receive its share<br/>of the protocol fee]
end
subgraph CT [controller · two-step]
A5[setCompoundBounty<br/>≤ 0.5 %] --- A6[setFeePaused<br/>the fee, never trading] --- A10[setKeeper<br/>who presses first]
end
subgraph KP [keeper · named by the controller]
A11[compound only<br/>nothing else]
end
subgraph LO [seed locker owner · two-step]
A7[collectFees<br/>liquidity untouched] --- A8[extendLock<br/>later only] --- A9[withdraw<br/>only after unlockAt]
end
The keys
| Key | Can do | Bound by | Can it be changed? |
|---|---|---|---|
poolCreator |
create the one pool; open trading (once) | pair, fee flag and tick spacing enforced; opening is one-way | No. Immutable. Used at launch, then it has nothing left to do. |
creator |
receive its share of the protocol fee | nothing else — no other function checks this address | Yes: two-step transfer (propose, then the new address accepts) |
controller |
setCompoundBountysetFeePausedsetKeeperand may press compound() itself |
bounty ≤ 0.5% (and ≤ 3 USDG a call); nothing else — the protocol fee, its split and the curve are immutable | Yes: two-step transfer |
keeper |
compound() — first, ahead of everyone |
a hot key with no owner powers: the hook refuses it every other function. After six hours without a compound the button is public again whatever is set | Yes: the controller names a new one, or setKeeper(0) reopens compound() to everyone at once |
seed locker owner |
collectFees (the seed's LP fees, liquidity untouched)extendLock (later only)withdraw — only after unlockAt |
one position, once; the lock never shortens; nothing else in the contract | Yes: two-step transfer |
At launch the controller, the creator and the seed locker's owner are the same hardware wallet; the keeper is a separate hot key. Every hat is listed, every transfer is two-step and an on-chain event, and the transfer paths were exercised on testnet before mainnet (The Launch).
Things nobody can do 🚫
- Withdraw the locked liquidity. There is no function. Not a disabled one — none.
- Withdraw the seed before its
unlockAt. Not even its owner; the locker has no rescue path, because a rescue path is a rug path. - Mint FLY. The token has no mint function; supply was created once.
- Halt trading. Once
openTrading()has run, nothing in the hook can refuse a swap again.setFeePausedstops the fee, not trading. You can always leave. That is the point. - Change the protocol fee, its split or the fee curve. They are set once, in the constructor, and there is no setter: 0.90 %, half to the creator and half to the floor,
k= 15 — for the life of the pool. Not the controller, not the creator, nobody. - Upgrade anything. No proxies. What you read is what runs, forever.
- Create a second pool through this hook, or change the pair.
- Take the pot anywhere but into the pool.
compound()has exactly one destination. - Make the keeper more than a button-presser. The keeper's whole power is who presses first; it cannot move funds, change a fee, or touch liquidity, and a dead or hostile one delays the floor by hours at most.
What the controller's knobs are for
setFeePausedis a circuit breaker for the fee path: if the stablecoin issuer ever restricted an address the fee is paid to, trading would continue without the fee. It is a way to make trading cheaper in an emergency, not a way to take anything.setCompoundBountyprices the keeper's click, 0–0.5 % of the tranche (3 USDG at most).setKeepernames the hot key that pressescompound()on a schedule, or clears it. That is the whole list: three knobs.
Things anyone can do
- Add or remove their own liquidity.
- Call
compound()and be paid for it — once the keeper has been idle for six hours, or at any time if no keeper is set. - Call
sweepBands()andburnAccruedFly(). - Read everything: every number on the dashboard is a public view. Don't trust. Verify. Then vibe.
Events to watch, if you watch such things
TradingOpened, FeePausedUpdated, CompoundBountyUpdated, KeeperUpdated,
CreatorTransferStarted / CreatorTransferred, ControllerTransferStarted /
ControllerTransferred, LiquidityLocked, AccruedFlyBurned, WeatherChanged,
ProtocolFeeCollected. On the seed locker: Locked, LockExtended, FeesCollected,
Unlocked, OwnershipTransferStarted / OwnershipTransferred. Set alerts on these and you
will know about any change to the machine before anyone can spin it. 🪰
The launchpad's keys
The launchpad (The Launchpad) has its own, smaller set. The FLY owner (the same hardware wallet) holds the owner roles; one hot key presses the furnace's burn.
flowchart TB
subgraph FO [FLY owner · the hardware wallet]
B1[LaunchFactory<br/>launch fee, protocol fee<br/>for FUTURE launches only] --- B2[QuoteRegistry<br/>which assets may be a pairing] --- B3[LaunchHook<br/>the LP-fee curve<br/>≤ 5 %]
B4[FlyFurnace<br/>burn by hand, name the keeper,<br/>set tolerance and cap]
end
subgraph FK [furnace keeper · a hot key]
B5[autoBurn only]
end
subgraph LC [a launch's creator]
B6[its share of the fee<br/>claimed, never pushed] --- B7[its description and logo<br/>signed edit]
end
| Key | Can do | Bound by |
|---|---|---|
LaunchFactory owner |
setLaunchFee; setProtocolFeeBps |
applies to future launches only: a live pool's fee is fixed when it is registered and never moves under its holders; the protocol fee is ≤ 1.00% |
QuoteRegistry owner |
setQuote: allow or disallow a pairing asset |
a launch's pairing is fixed when it is created |
FLY owner on LaunchHook |
setLpFeeSchedule: start fee, base fee, decay |
LP fee ≤ 5.00%, decay ≤ one day; it is paid to liquidity providers, not the protocol |
FlyFurnace owner |
burn(asset, minFlyOut) by hand; setKeeper; setTolerance (1% to 10%); setMaxAutoBurnUsdg; setFlyUsdgPool once |
the FLY/USDG pool cannot be re-pointed afterwards, so no one can later route every burn through a pool they control |
furnace keeper |
autoBurn(asset); the owner can call it too, as the backup |
no minimum stated by the caller: the contract requires the USDG-to-FLY swap to return at least what the FLY hook's reference price buys, less the tolerance, or the call reverts. A sandwich in front of it makes it revert instead of burn less. |
| a launch's creator | receive its share of each pool's fee (50% of the protocol fee); sign an edit to its own description and logo | the share is credited, then claimed: anyone can trigger the payout to the creator, and a frozen address can never block a trade |
Things nobody can do on the launchpad: change a live pool's fee; remove the liquidity a graduation seeds (the hook only ever adds liquidity or collects fees); change a launch's pairing; or make a trade depend on a recipient being able to receive.
One honest limit, on purpose: the furnace's price guard covers the USDG-to-FLY swap only. An asset that is neither USDG nor FLY (ETH from launch fees, WETH, a stock token) is first swapped to USDG through its pairing pool, and that first hop has no time-weighted price to check against. A sandwich of that hop could take part of that asset's balance in the furnace. The balances are small, the keeper cannot reach anything else, and the owner can always burn by hand with a minimum of their own.