Contracts
Deployed on Unichain mainnet, chain ID 130. Every one of them is verified on Uniscan, so the code below the links is the code that runs.
Not deployed yet. The Unichain deployment has not happened; the addresses are filled in here, and in the site's configuration, when it does. Until then every page of the site says so rather than reading an empty address.
| contract | address |
|---|---|
PairLaunchpad | not deployed yet |
FeeVault | not deployed yet |
PairLaunchpadV2 (hooked launches) | not deployed yet |
FeeVaultV2 (hooked launches) | not deployed yet |
RainbowHook (the fee hook) | not deployed yet |
QuoteRegistry | not deployed yet |
CompositeQuoteGate | not deployed yet |
UniV3QuoteGate | not deployed yet |
| Slipstream gate (Velodrome) | not deployed yet |
SampledV4QuoteGate | not deployed yet |
V2QuoteGate | not deployed yet |
Your coin's own contract
Every coin launched here is a PairToken, and every one of them shows as verified without anyone
submitting it. That is not a courtesy — PairToken takes no constructor arguments (it calls back
into the launchpad for its name, symbol and supply), so the creation bytecode is byte-identical
every time. One instance was verified; the explorer matches all the others, including coins that do
not exist yet.
What that verified source says, and what it therefore cannot do: fixed supply set once in the constructor, no mint function, no transfer tax, no blacklist, no pause, no owner.
External contracts it uses
| Uniswap v4 PoolManager | 0x1F98400000000000000000000000000000000004 |
| Uniswap v4 StateView (reads only) | 0x86e8631A016F9068C3f085fAF484Ee3F5fDee8f2 |
| Uniswap v4 Quoter | 0x333E3C607B141b18fF6de9f258db6e77fE7491E0 |
| Uniswap Universal Router | 0xEf740bf23aCaE26f6492B10de645D6B98dC8Eaf3 |
| Permit2 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| Uniswap V3 factory (measurement only) | 0x1F98400000000000000000000000000000000003 |
| Uniswap V2 factory (measurement only) | 0x1F98400000000000000000000000000000000002 |
| Velodrome Slipstream factory (measurement only) | 0x04625B046C69577EfC40e6c0Bb83CDBAfab5a55F |
| Velodrome V2 factory (measurement only) | 0x31832f2a97Fd20664D76Cc421207669b55CE4BC0 |
| Chainlink ETH/USD (18 decimals) | 0xBcE70e194940a157f3A80566505a7E96f5238CCa |
| Chainlink BTC/USD (18 decimals) | 0xC13f3E310Dd7436FA24338174acB64254b9A8039 |
| Chainlink USDC/USD (18 decimals) | 0xbd1cD1518eFB92a92100da62D4C488c810dFd75b |
| KyberSwap router (dev buy and trade panel) | 0x6131B5fae19EA4f9D964eAc0408E4408b66337b5 |
Unichain's Chainlink USD feeds report 18 decimals, not the usual 8. Everything that reads them
asks decimals() rather than assuming.
Calls worth knowing
Before launching
// Non-reverting. Returns whether it would be allowed, and why not if not.
registry.status(address quote)
returns (bool eligible, Decision decision, Report report)
// The bar this particular asset has to clear — $10,000 if listed, $25,000 if not.
registry.requiredDepthUsdE8(address quote) returns (uint256)
Naming a v4 pool
Every other venue can be searched: a factory answers getPair(a, b) or getPool(a, b, fee).
Uniswap v4's singleton cannot — a pool is identified by the hash of its key, and the key contains a
160-bit hook address, so it can be verified but never enumerated. A v4 pool therefore has to be
named once before the gate can see it. Anyone may do it, once, for everyone.
v4Gate.registerPool(PoolKey key) returns (address token)
v4Gate.registerLaunch(address token) returns (address) // for a coin launched here
v4Gate.probe(PoolKey key) returns (uint256 usdE8, address anchor) // read-only, changes nothing
Nothing equivalent is needed for the V2-shaped venues or for Uniswap V3 and Slipstream: those are searched directly, so a coin with a market there is usable the moment its address is pasted in.
Launching
launchpad.launch(LaunchConfig cfg) payable returns (address token, bytes32 poolId)
launchpad.predictToken(address creator, bytes32 salt) returns (address)
launchpad.launchFee() returns (uint256)
LaunchConfig carries, beyond the obvious:
| field | what it does |
|---|---|
creatorBps | share of supply kept back for you, capped at 20% |
feeRoute | 0 keeps your fees, 1 buys the coin back and burns it, 2 gives them to your holders (final; needs a distributor) |
feeRecipient | zero means the launching wallet |
devBuyQuote | pair-asset amount spent buying your own coin as the pool's first trade; needs an approval to the launchpad first |
devBuyMinOut | least you will accept from that buy — nobody can front-run it, so this guards against you and the pool disagreeing on the opening price |
After launching
// Anyone can call. Proceeds only ever reach the recorded recipient or the rewards pot, the sink
// and the treasury, or the burn.
feeVault.collect(address token)
// Creator only. Both take effect from the next collect. HolderRewards (2) is final.
feeVault.setFeeRoute(address token, FeeRoute route)
feeVault.setFeeRecipient(address token, address recipient)
// Reads
launchpad.launchOf(address token) // a public mapping: an unnamed tuple
feeVault.launchOf(address token) returns (Launch) // includes the PoolKey
feeVault.feeRouteOf(address token) returns (FeeRoute)
feeVault.feeRecipientOf(address token) returns (address)
feeVault.owed(address token, address who) returns (uint256) // a payout that could not be pushed
feeVault.claim(address token) // collect one
feeVault.buybackSink() returns (address) // where the protocol's share goes
// FeeVaultV2 (hooked launches) only — all in the pair asset
feeVault.pendingFees(address token) returns (uint256) // charged by the hook, not yet collected
feeVault.totalFees(address token) returns (uint256) // everything charged since launch
feeVault.feeRateOf(address token) returns (uint24) // the launch's rate, hundredths of a bip
The fee hook
Launches since the hook open a pool with no Uniswap LP fee and RainbowHook as its hook. The
hook charges the launch's rate on the pair-asset leg of every swap — the pair asset a buyer pays in,
or a seller receives — and never on the coin:
| swap | the pair asset is | the hook takes |
|---|---|---|
| buy, exact input | the amount paid in | in × rate, before the swap |
| buy, exact output | what the pool charges | in × rate / (1 − rate) on top, after the swap |
| sell, exact input | what the pool pays out | out × rate out of it, after the swap |
| sell, exact output | the amount asked for | out × rate / (1 − rate) extra from the pool, before the swap |
Each row is the same rate of the gross pair-asset amount. The fee is minted to FeeVaultV2 as a
Uniswap v4 claim (ERC-6909) inside the swap — no token moves mid-swap, so no token can block trading —
and collect turns it into the pair asset and splits it. Only the launchpad can open a pool on the
hook, the rate is written once when it does, and there is no function that changes it or sends a fee
anywhere but the vault. The vault's own buyback swaps are the one thing the hook does not charge.
Routers that go straight to Uniswap v4 (the Universal Router, Uniswap's quoter) handle hooked pools natively; an aggregator has to integrate a hook before it routes through it, so the site trades these coins through the Universal Router itself.
Holder rewards
feeVault.holderRewardsDistributor() returns (address) // zero until set; route 2 is refused while it is
feeVault.rewardsPot(address token) returns (uint256) // pair asset waiting for the coin's holders
feeVault.pendingRewards(address token) returns (uint256) // coins the price cap left unsold
feeVault.pendingBuyback(address token) returns (uint256) // pair asset the price cap left unspent
// Distributor only. Lengths must match; the total may not exceed the pot.
feeVault.payHolderRewards(address token, address[] recipients, uint256[] amounts)
event HolderRewardsAdded(address indexed token, uint256 coinSold, uint256 soldFor, uint256 added, uint256 pot);
event HolderRewardsPaid(address indexed token, address indexed quote, address[] recipients, uint256[] amounts, uint256 total, uint256 pot);
Where the liquidity lock actually lives
There is no lock contract and no locked LP token, because Uniswap v4 has no LP tokens at all — a
position is a storage slot keyed by (owner, tickLower, tickUpper, salt). The usual "liquidity
locked" detectors look for LP tokens sent to a burn address or a locker, and here there is nothing
of the sort for them to find.
What holds instead is a property of FeeVault, and now that its source is published you can check
it rather than believe it: no code path in the vault ever passes a negative liquidityDelta.
Collecting fees calls modifyLiquidity with a delta of exactly zero. There is no withdraw, no
emergency exit, and no owner function that adds one. The float cannot come back out — not by the
creator, not by us.
Owner powers
The owner can retune the depth bars, the fee bounds, the launch fee, the treasury, the buyback sink, the holder-rewards distributor (replace it, never remove it), the curated list, the gates and their parameters. It cannot touch a launch's liquidity, change an existing launch's creator share, or redirect a creator's fees.
Ownership transfer is two-step everywhere: a handover that is never accepted leaves the current owner in place rather than sending the protocol to a wrong address.
Today the owner is a single EOA. It should be a multisig behind a timelock, and the contracts do not enforce that themselves.
None of this has been audited.
