> ## Documentation Index
> Fetch the complete documentation index at: https://docs.windrose.market/llms.txt
> Use this file to discover all available pages before exploring further.

# Add a chain

> What a chain needs before Windrose can run on it, and the checklist to register, deploy and serve it.

Nothing in the repository hard-codes a chain. A new one is an entry in `packages/abis/chains.json`, a row in the deploy script's chain table, a deployment, and one keeper and indexer more. Robinhood Chain was added this way; Arc mainnet is registered and waits for its deployment.

## What the chain must have

* **Cancun EVM.** The contracts and Uniswap v4 are compiled for `evm_version = "cancun"` and use transient storage (`TSTORE` and `TLOAD`) and `MCOPY`. Arbitrum Orbit (ArbOS 40 and later) and OP Stack chains qualify. Check by executing, not by reading docs.
* **A dollar stablecoin** launches settle in, 6 decimals preferred (USDC, USDG). On a testnet without one, `Deploy.s.sol` can deploy a mintable mock (`DEPLOY_TEST_DOLLAR=true`) and the app offers a "Get test dollars" button for it.
* **Permit2**, canonical `0x000000000022D473030F116dDEE9F6B43aC78BA3` on most chains. The `PositionManager` and the `LaunchFactory` use it.
* **Uniswap v4**: either the official `PoolManager`, `PositionManager`, `StateView` and `Quoter` (put them under `uniswapV4` in the registry and in `_defaults`, and run the indexer with `INDEX_POOL_SWAPS=false`, because a shared `PoolManager` emits every swap on the chain) or nothing, in which case `Deploy.s.sol` deploys its own set and the indexer can index swaps.
* **An FX price source.** The keeper oracle works anywhere; add Pyth (`pyth` in the registry) or a Chainlink adapter where FX feeds exist.
* Optional: EURC or a bridged equivalent (`eurc`; without it EUR is simply not registered), Multicall3 (probed at runtime), a Blockscout instance (`explorerApi`, for verification), a faucet.

## Checklist

<Steps>
  <Step title="Register the chain">
    Add an entry to `packages/abis/chains.json`. The loader validates it at startup.

    Required: `id`, `key` (slug, used for token-list file names and as the Ponder chain name), `name` (shown in the UI), `testnet`, `rpc`, `explorer`, `nativeCurrency` (`name`, `symbol`, `decimals`; the symbol appears in "Gas is paid in ..."), `gas` (`maxFeeGwei`, `priorityGwei`; add `minBaseFeeGwei` only when the chain enforces a floor like Arc's 20 gwei, which pins the fee in the deploy scripts and is mentioned in the UI), `dollar` (`symbol`, `decimals`, optional `address`, `mintable`, `logo`; leave `address` out until a mock is deployed).

    Optional: `rpcs` (alternates; the indexer and deploy script default to them), `ws`, `explorerApi`, `eurc`, `faucet`, `multicall3`, `permit2`, `pyth`, `uniswapV4`, `ethGetLogsBlockRange` (the public RPC's limit for the indexer), `maxLogAddresses` (addresses per log filter), `notes`.
  </Step>

  <Step title="Mirror the addresses in the deploy script">
    Add a row to `_defaults()` in `contracts/script/Deploy.s.sol` with `usdc`, `usdcSymbol`, `eurc`, `permit2`, `pyth` and the Uniswap v4 addresses (or `testDollarAllowed` on a testnet). A chain without a row must pass `USDC`, `EURC` (`0x0` skips EUR) and `PERMIT2` in the environment.
  </Step>

  <Step title="Deploy">
    Fund the deployer with the chain's gas token, then `CHAIN_ID=<id> bash scripts/deploy-testnet.sh` (or the mainnet script). It writes `packages/abis/deployments/<id>.json`; if it deployed a mock dollar, copy that address into `dollar.address` of the registry entry. See [Deploy contracts](/operate/deploy-contracts).
  </Step>

  <Step title="Run the keeper and the indexer">
    Keeper with `CHAIN_ID=<id>` (`pnpm --filter @launchpad/keeper once` first so every currency has a rate, then `start`). Indexer with `CHAIN_ID=<id>`, `PORT=<free port>`, `PONDER_RPC_URL_<id>`, `START_BLOCK_<id>=<deploy block>` and `INDEX_POOL_SWAPS=false` on a shared `PoolManager`. Add both to `deploy/ecosystem.config.cjs` and a `server` block or `location` for the indexer to the reverse proxy.
  </Step>

  <Step title="Wire the web app">
    Add a row for the chain id to `RPC_ENV` and `INDEXER_ENV` in `apps/web/src/lib/chain.ts` (Next.js only inlines literal `process.env.NEXT_PUBLIC_*` reads), set `NEXT_PUBLIC_INDEXER_URL_<id>=<that indexer>` (plus `NEXT_PUBLIC_RPC_URL_<id>` if the registry RPC is not the one to use) and rebuild. The switcher lists the chain as soon as its deployment file exists at build time; `NEXT_PUBLIC_CHAIN_ID=<id>` only makes it the default.
  </Step>

  <Step title="Assets">
    `CHAIN_ID=<id> pnpm abis:logos` and `CHAIN_ID=<id> pnpm abis:tokenlist`. If the dollar is a new token, add `apps/web/public/tokens/<symbol>.svg` (the `CoinLogo` component falls back to initials when it is missing).
  </Step>

  <Step title="Copy">
    Product copy that names the dollar, the chain, the gas token or the faucet is registry-driven; brand strings are not. Check the chain's terms of use: Robinhood's, for instance, forbid "Robinhood" in product or token names, while "Built on Robinhood Chain" is fine.
  </Step>
</Steps>

## Worked example: Robinhood Chain

Arbitrum Orbit L2 (ArbOS 61, Nitro 3.12), ETH gas, permissionless deployment, Cancun opcodes verified by execution. Both networks are in the registry with `ethGetLogsBlockRange: 1000` and `maxLogAddresses: 60`.

|                     | Mainnet (`robinhood`, 4663)                                                                                                                                                                                                                                                                   | Testnet (`robinhood-testnet`, 46630)                                                                                               |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| RPC                 | `https://rpc.mainnet.chain.robinhood.com` (no archive state; the indexer uses `https://robinhood.drpc.org`)                                                                                                                                                                                   | `https://rpc.testnet.chain.robinhood.com` (full node, no archive state; indexer: `https://robinhood-testnet.drpc.org`)             |
| Websocket           | `wss://robinhood-rpc.publicnode.com`                                                                                                                                                                                                                                                          | `wss://robinhood-sepolia-rpc.publicnode.com`                                                                                       |
| Explorer            | [https://robinhoodchain.blockscout.com](https://robinhoodchain.blockscout.com)                                                                                                                                                                                                                | [https://explorer.testnet.chain.robinhood.com](https://explorer.testnet.chain.robinhood.com)                                       |
| Gas                 | ETH, base fee about 0.02 to 0.06 gwei; registry pins 0.5 gwei max, 0 priority                                                                                                                                                                                                                 | ETH, base fee about 0.01 gwei; registry pins 0.05 gwei max, 0 priority                                                             |
| Dollar              | USDG (Paxos) `0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168`, 6 decimals                                                                                                                                                                                                                         | a mintable test USDG deployed by the script (`0xd594d4A6bFD57Ba907cd5fB4Fa9bDc57C138738f`)                                         |
| EURC                | bridged `0x697532723B423D38A6769bA2F804AEc3fAaF740B`, tiny supply, optional (`EURC=...` registers it)                                                                                                                                                                                         | none                                                                                                                               |
| Uniswap v4          | official (`PoolManager` `0x8366a39CC670B4001A1121B8F6A443A643e40951`, `PositionManager` `0x58daec3116aae6D93017bAAea7749052E8a04fA7`, `StateView` `0xF3334192D15450CdD385c8B70e03f9A6bD9E673b`, `Quoter` `0x8Dc178eFB8111BB0973Dd9d722ebeFF267c98F94`); indexer with `INDEX_POOL_SWAPS=false` | none official: the script deploys its own                                                                                          |
| Permit2, Multicall3 | canonical addresses                                                                                                                                                                                                                                                                           | canonical addresses                                                                                                                |
| FX oracle           | no Pyth, no Chainlink FX pairs: the keeper oracle is the price source                                                                                                                                                                                                                         | same                                                                                                                               |
| Faucet              |                                                                                                                                                                                                                                                                                               | [https://faucet.quicknode.com/robinhood/testnet](https://faucet.quicknode.com/robinhood/testnet) (also Alchemy's, 0.1 ETH per day) |

RPC limits: the official public RPC caps `eth_getLogs` at 10,000 logs and answers 429 after a burst; thirdweb's testnet endpoint caps 1,000 blocks and rejects server IPs without a client id; Alchemy's free tier caps 10 blocks; dRPC's free plan caps 100 blocks but is the only public archive endpoint. Use an Alchemy or QuickNode key for `PONDER_RPC_URL_<chainId>` (a paid endpoint for mainnet) and keep `MAX_REQUESTS_PER_SECOND` low on the public one.
