# 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](https://poo.meme/docs/developers/actions) 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](https://poo.meme/docs/developers/build#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 · 4663** (Deployment record)

| Contract | Address | Follow It For |
| --- | --- | --- |
| `PooLauncher` | `0x77af671da987e6a443dabe16e1e293c303c01e2e` | Every new launch: `Launched`, and `splitterOf(token)` |
| `PooTradeRouter` | `0x4d6c136433a1d4311cca03f2cb12d4861ac05d27` | Every trade: `Bought` and `Sold` |
| Pons's own factory | `0x7eD598BcEf8bd9Edd8C97A195C6d13f40801EC7e` | A launch's live state: `getLaunchedToken(token)` |
| `PooModuleDirectory` | `0xf4929ef6e8694f7d49a1b3818115c7b8f9039851` | Every registered module factory: `isRegistered(factory)` |
| `PooBuybackBurnModuleFactory` | `0x5b46a1b37763fb2aa19a82c86160b3810027d319` | The Buyback & Burn modules launches name |
| `PooStakingModuleFactory` | `0x5707fac65b6d9c8e8074e03173724015e8837b7a` | The Staking modules launches name |
| `PooRefundPricer` | `0x3fd599238e62cec582d4150fe2119ce2ad2e131c` | Which pair assets can launch: `select(asset)` |
| `PooRefundSwapper` | `0x8B8b336891f24F6B05b01a685E7b3A1015F0DdB7` | Every gas refund paid in ETH: `Refunded` |
| `PooTradeZap` | `0x290b7f01068efb3c9fc7ad7d290b82d12996d5da` | Trades paid in, or sold into, an asset other than the launch's own pair: its own `Bought` and `Sold` |

`PooLauncher`

```solidity
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](https://poo.meme/docs/developers/actions#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`

```solidity
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`

```solidity
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`

```solidity
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.

| Status | `phase` |
| --- | --- |
| Live | `NotGraduated` — trading on the curve |
| Graduating | `Swept` — crossed its graduation threshold; its pool is not created yet |
| Launched | `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](https://poo.meme/docs/developers/actions#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`

```solidity
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](https://poo.meme/docs/developers/actions#trade).

## A launch's own fee stream

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

`PooSplitter`

```solidity
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](https://poo.meme/docs/developers/actions#collect)). `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](https://poo.meme/docs/creators/run#handoff)).
