# Publish

Publishing records your factory, probe and manifest as a registry entry creators can pick.

This page publishes a token module, through `publishTokenModule` — open to anyone. A launch module publishes through `publishLaunch` instead, and the registry refuses that call to everyone but POO.MEME until its owner opens `launchPublishingOpen()` on the chain you’re on ([Two kinds](https://poo.meme/docs/developers/build#kinds)).

## The factory

`src/MyModuleFactory.sol`

```solidity
constructor(address moduleRegistry) {
    if (moduleRegistry.code.length == 0) revert NotAContract(moduleRegistry);
    developer = msg.sender;
    address standIn = IPooProbeHost(moduleRegistry).probeStandIn();
    probe = _spawn(
        TokenModuleContext({token: standIn, quote: standIn, pair: standIn, router: standIn}), _probeConfig()
    );
}

function create(TokenModuleContext calldata ctx, bytes calldata config)
    external override returns (address tokenModule)
{
    tokenModule = _spawn(ctx, config);
}
```

- `create` deploys a full contract with the probe’s exact code — no proxy, no clone. Keep per-instance values in storage.
- The token has no code yet when `create` runs; use `onInstalled` for anything that needs it.
- Anyone can call `create`. Guard hooks on the token, and `onInstalled` on the token’s factory.
- Publish from the factory, or have its `developer()` or `owner()` return your wallet.

## The probe

- A real instance, built in your factory’s constructor. Every future instance must match its code.
- It is built on the registry’s stand-in, `IPooProbeHost(moduleRegistry).probeStandIn()`, which answers as its token and pair and makes its hook calls.
- It must answer every read your manifest names.
- The registry calls each declared hook on it, within your gas cap.

Passing the probe allows publishing. It does not prove the module is safe.

## Test it

`forge test` stands POO.MEME up in memory through the devkit’s `PlatformHarness.install`: the venue registry, module registry, token factory, metadata, and Uniswap V2 at its canonical addresses. Your test then publishes to the real registry:

`test/MyModule.t.sol`

```solidity
function test_theRegistryAcceptsThisModule() public {
    uint32 entryId = platform.moduleRegistry.publishTokenModule(IPooTokenModuleFactory(address(factory)));
    assertGt(entryId, 0, "the registry refused the manifest");
}
```

A refused manifest fails with the registry’s error name. To test a hook alone, initialize an instance with your test contract as its token and call the hook yourself, as the template’s `_standalone` does.

### Create a token

To see your module inside a token, create one through the real factory. The devkit’s `ConformanceLaunchFixtureFactory` is the smallest launch module the registry accepts. `script/Poo.s.sol` creates its token this way:

`script/Poo.s.sol`

```solidity
(, bytes32 signedBlueprint) = p.tokenFactory.blueprint();

TokenModuleChoice[] memory chosen = new TokenModuleChoice[](1);
chosen[0] = TokenModuleChoice({entryId: g.moduleEntry, supply: 0, config: moduleConfig(), buyBps: 0, sellBps: 0});

CreatorTaxTerms memory tax;
tax.payeeCap = TAX_PAYEE_CAP;

return p.tokenFactory.create(
    tokenTerms(p, g.quote),
    Metadata({description: "", website: "", x: "", telegram: ""}),
    LaunchChoice({entryId: g.launchEntry, supply: TOTAL_SUPPLY, config: "", buyBps: 0, sellBps: 0}),
    chosen,
    new SupplyRow[](0),
    tax,
    signedBlueprint
);
```

The factory refuses a creation that breaks any of these:

| Rule | Refused As |
| --- | --- |
| The launch’s supply, every token module’s supply and every supply row total `totalSupply` exactly | `SupplyNotWhole` |
| At most 8 supply rows, none zero, no recipient twice, none to a module | `TooManySupplyRows`, `SupplyRowEmpty`, `SupplyRowRepeated`, `SupplyRowIsAModule` |
| A row to the creator or to `0x…dEaD` has no name; every other row has one of 1–31 bytes | `SupplyRowNameInvalid` |
| At most 6 token modules, each meeting its entry’s [requirements](https://poo.meme/docs/developers/build#requires) | `TooManyTokenModules` |
| A row paying the creator has no name; every other payee row has one of 1–31 bytes, and none pays a module | `PayeeNameInvalid`, `PayeeIsAModule` |
| `payeeCap` is at most 13 and at least the rows written | `TaxCapAboveMaximum`, `TaxCapBelowRows` |
| A payee contract with the Receive hook answers a `receiveGas()` of 1 to 265,000 | `ReceiveGasUnreadable`, `ReceiveGasZero`, `ReceiveGasAboveCeiling` |
| The token’s heaviest leg fits the transaction gas cap | `SellGasOverTxCap` |
| `expectedBlueprint` is the factory’s current token code | `BlueprintMismatch` |

### What to test

- Publishing through the real registry, and creation through the real factory with your minimum shares and a config at each bound.
- Every hook on a real token: the trades that reach it, a starved gas cap, a quote asset that reverts.
- For Operate, what a run does to the next seller’s price.
- Every error your module declares, and every action’s minimum and deadline.

`script/Poo.s.sol` is the same setup as a script, and the two commands run it differently: `poo dev` broadcasts it onto a local chain, while `poo preview` runs its `Preview` contract in memory — `forge script --tc Preview`, no chain and no key — and reads your manifest from there. Its token terms and your module’s config sit at the top of the file; change them when your config changes.

## Send it

Try it on your own `poo dev` chain first. Without `--broadcast` it only checks, and it checks six things: the factory has code, its manifest is this checkout’s byte for byte, the registry would accept it, you are its developer, the handle is free or yours, and this factory is not published yet. The last one is why a new version needs a new factory — a factory the registry already lists is refused with the entry it holds.

`publishFee()` is a plain number the registry’s owner can change at any time, paid in the chain’s native coin — BNB, ETH, whatever the chain you’re on uses, never a token. Read it before you broadcast:

```text
cast call <registry> "publishFee()(uint256)" --rpc-url <url>
```

cast prints the wei amount; a round number also gets a bracketed scientific reading beside it (`5000000000000000 [5e15]`) — the number before the bracket is what you send. The dry run below prints the same figure on its own `fee` line.

```text
poo publish --rpc <url> --registry 0x… --factory 0x… --broadcast -- --ledger
```

Everything after `--` is handed to `cast` untouched — the SDK never sees a key. Sign with whatever `cast send` accepts: `--ledger` or `--trezor` for a hardware wallet, `--account <name>` for a keystore under `~/.foundry/keystores`, or `--private-key`/`--mnemonic` for a raw key. With `--broadcast`, the SDK prints the call itself, then hands your `--rpc-url` and everything after `--` straight to `cast` to sign and send — this is what runs:

```text
cast send <registry> "publishTokenModule(address)" <factory> --value <fee> --rpc-url <url> --ledger
```

It sends exactly the fee you read above; any other amount reverts `WrongFee`. Once that transaction is mined, the entry is live — no separate step. `poo publish` reads it straight back and reports the entry id, and the handle is now yours for every later version.

## Handles and versions

- Your manifest’s [handle](https://poo.meme/docs/start/overview#handles) names your module family. The first wallet to publish under it holds it; later versions must come from that wallet.
- Versions are numbered v1, v2, … in the order they are published. The catalogue shows the newest.
- The module page reads, for example, **Version** v2 – Sep 14, 12:13, and links the other versions. A token page links the version that token uses.
- Entries never change. Any code or manifest change means a new factory and a new version, and the tokens already carrying the old one keep carrying it.

## Before you publish

- [ ] `forge build --sizes`: factory and runtime fit EIP-170.
- [ ] `forge test`: publishing and creation pass.
- [ ] `poo check`: every import is on the published surface.
- [ ] `poo preview`: the page reads as you meant.
- [ ] The handle is the one you want to keep.
- [ ] The description names every power, exemption path and run.
- [ ] ERC-165 matches your hooks; caps come from measured worst cases.
- [ ] `requires` accepts exactly the shares you use.
- [ ] Security obligations answered.
- [ ] Published to your own `poo dev` chain first.
- [ ] `publishFee()` read on the chain you’re publishing to, and you hold that much of its native coin.
- [ ] Your signing wallet ready — the flags `cast send` takes after `--`.
