Build

Contracts and events

Every POO.MEME launch goes through one contract, PooLauncher, and announces itself in one event. This page names the addresses to follow, how to tell a POO.MEME token from any other, how to read where a launch stands, and every event a tracker follows. Launch, trade and graduate covers the transactions themselves: building a launch request, buying, selling, collecting a splitter's fees, and creating a token's pool once its curve is swept.

Every signature on these two pages is generated from the contracts' own build — one item per line, in the human-readable form viem's parseAbi takes — so if a page and the Solidity disagree, the Solidity wins, and it changes only when the contracts do. A contract verified on its own chain's explorer carries its ABI there too; the npm package @poomeme/sdk is built for module authors instead, shipping the module-factory interface and manifest library to compile against (Build a module), and carries none of Pons's own contracts — those are read through the ABIs on these two pages.

Addresses and roles

PooLauncher is the one door in: it creates a token and its curve through Pons, deploys its splitter and any modules the creator chose, and can run the creator's own opening buy — all in the same transaction. Every token it creates trades against ETH or the one Pons-approved asset its creator picked, and its address always ends 0x8888. Pons is the external protocol its curve, sweep and pool all run on. PooTradeRouter is one address to buy and sell any POO.MEME token, wherever it's trading. PooSplitter is a launch's own contract, receiving its token's fee stream from Pons and dividing it between POO.MEME, its chosen modules and its payout address. PooStakingModuleFactory and PooBuybackBurnModuleFactory build POO.MEME's own two first-party modules, Staking and Buyback & Burn, when a launch names them — for PooLauncher alone, refusing every other caller.

PooLauncher and PooTradeRouter are POO.MEME's own, fixed once deployed: neither carries an owner or an upgrade path of any kind, so there is no proxy indirection to follow, and PooLauncher has no admin function of any kind that could reach a launch that already exists. Pons's own factory is fixed per chain too, and its address is what pons() answers on either of ours.

Robinhood Chain · 4663Deployment record
ContractAddressFollow It For
PooLauncher0x77af671da987e6a443dabe16e1e293c303c01e2eEvery new launch: Launched, and splitterOf(token)
PooTradeRouter0x4d6c136433a1d4311cca03f2cb12d4861ac05d27Every trade: Bought and Sold
Pons's own factory0x7eD598BcEf8bd9Edd8C97A195C6d13f40801EC7eA launch's live state: getLaunchedToken(token)
PooModuleDirectory0xf4929ef6e8694f7d49a1b3818115c7b8f9039851Every registered module factory: isRegistered(factory)
PooBuybackBurnModuleFactory0x5b46a1b37763fb2aa19a82c86160b3810027d319The Buyback & Burn modules launches name
PooStakingModuleFactory0x5707fac65b6d9c8e8074e03173724015e8837b7aThe Staking modules launches name
PooRefundPricer0x3fd599238e62cec582d4150fe2119ce2ad2e131cWhich pair assets can launch: select(asset)
PooRefundSwapper0x8B8b336891f24F6B05b01a685E7b3A1015F0DdB7Every gas refund paid in ETH: Refunded
PooTradeZap0x290b7f01068efb3c9fc7ad7d290b82d12996d5daTrades paid in, or sold into, an asset other than the launch's own pair: its own Bought and Sold
PooLauncher
function pons() view returns (address)
function directory() view returns (address)
function isInstallable(address factory) view returns (bool)

isInstallable(factory) is the check a launch makes of every module factory it names: true only for a factory registered in directory() — the PooModuleDirectory this launcher is bound to for good — whose deployed code still matches its listing and whose own launcher() is this launcher (Build the request). Registering is open to any developer and decided by checks the directory runs on chain, with no fixed allow-list and no admin who could refuse one; each new listing announces itself with Published, the one way a factory becomes installable:

PooModuleDirectory
event Published(uint256 indexed index, address indexed factory, address indexed developer, string handle, string version, bytes32 manifestHash, bytes32 codehash)

A new launch announces itself

PooLauncher emits Launched in the one transaction that creates a token, its curve, its splitter and any modules the creator chose. It names the token, its creator, the splitter, the curve, each module with its own factory and its share of the creator's fee, the creator's fee rate, the payout address, the splitter's own owner (if any), and what the creator's own opening buy bought, if it made one.

PooLauncher
event Launched(address indexed token, address indexed creator, address indexed splitter, address curve, address[] factories, address[] legs, uint16[] legBps, uint16 creatorTaxBps, address payout, address owner, uint256 bought)
function splitterOf(address token) view returns (address splitter)

legs and factories line up index for index: a module leg carries its own factory in factories; a payout leg carries the zero address there instead, since it names no factory and nothing was created for it — legs[i] is a module instance for the first, and the recipient address itself for the second.

There is no second transaction to wait on: the token, its curve, its splitter and every module it carries all exist by the time Launched is mined. Unlike the platform's old model, a token launched this way cannot be given a second launch — PooLauncher runs launch once per token, in the same call that creates it.

Is it a POO.MEME token?

  • Ask PooLauncher. splitterOf(token) answers a real address only for a token this launcher itself launched — anything a token says about itself is the token's own word, never take it as proof.
  • Ask Pons. getLaunchedToken(token).exists is true for any token Pons has launched, whoever launched it; check creatorFeeRecipient against splitterOf(token) to confirm it was launched through POO.MEME's own PooLauncher rather than directly against Pons.
IPonsFactory
function getLaunchedToken(address token) view returns ((address token, address curve, address deployer, address creatorFeeRecipient, address pairToken, uint256 graduationThreshold, uint24 poolFee, int24 tickSpacing, uint16 creatorTaxBps, bool buybackEnabled, uint8 phase, uint256 sweptQuote, uint256 sweptTokens, uint256 sweptAt, bool exists))

Where a launch stands

getLaunchedToken(token).phase is the one read: a Pons PonsPhase enum, three values a launch can actually reach and a fourth, admin-only path this app never triggers.

Statusphase
Live — The launch is accepting funds.NotGraduated — trading on the curve
Graduating — Crossed its graduation threshold; its pool is not created yet — anyone may create it.Swept — crossed its graduation threshold; its pool is not created yet
Launched — Trading is open.PoolCreated — trading in its pool

Rescued is an admin recovery path on Pons's own factory; POO.MEME's own contracts never call it and no launch reaches it in the ordinary course of trading. The mechanics behind each phase, and the one call that moves a launch from Swept to PoolCreated, are on Launch, trade and graduate.

A launch's phase moving is not an event: Swept and PoolCreated are chain state, read live off Pons's own factory rather than announced by either contract. Poll getLaunchedToken(token).phase, or watch for the pool's own first trade as a proxy for it.

Trades

PooTradeRouter emits one event per side, whichever phase the token traded in.

PooTradeRouter
event Bought(address indexed token, address indexed buyer, bool pool, uint256 quoteIn, uint256 tokensOut)
event Sold(address indexed token, address indexed seller, bool pool, uint256 tokensIn, uint256 quoteOut)

quoteIn/tokensOut and tokensIn/quoteOut are what the trader actually sent and actually received, every fee already settled inside the trade, the quote side in the token's own pair asset. pool tells the two phases apart without a second read — see Buy, sell and quote a trade.

A launch's own fee stream

Each launch's own splitter emits these from its own address — the splitter in its Launched.

PooSplitter
event Collected(address indexed caller, uint256 amount, uint256 platform, uint256 refund, uint256[] legs, uint256 payout)
event Deferred(address indexed recipient, uint256 amount)
event Released(address indexed recipient, uint256 amount)
event HandoffProposed(address indexed to, uint256 readyAt)
event HandoffExecuted(address indexed to)
event HandoffCancelled(address indexed to)

Collected fires on every collect(), every figure in the launch's own pair asset: platform is POO.MEME's own cut, refund the caller's own gas back (the pair asset taken for it, which the caller receives as ETH when the swap fills), legs each module's share in the same order as legs(), and payout the remainder (Collecting a launch's fees). Deferred names a leg the pair asset refused to pay, set aside for it, and Released the moment it was paid. The three Handoff* events only ever fire for a splitter whose launch named an owner — most do not, and never emit any of them (Changing where fees go).