> ## 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.

# ABIs and the chain registry

> The @launchpad/abis package: ABIs, the chain registry, deployment files, the generator scripts for token lists and logos, and how the web app and indexer resolve addresses.

`packages/abis` is the one place off-chain code gets contract interfaces, chain facts and deployed addresses from. Nothing else in the repository hard-codes a chain. It is a workspace package (`@launchpad/abis`, TypeScript source, no build step) imported by the web app, the indexer, the keeper and the generator scripts.

## What the package exports

```ts theme={"system"}
import {
  abis,                       // { LaunchFactory, BondingCurve, Router, CurrencyRegistry, FxVault, SynthToken, LaunchToken,
                              //   LiquidityLocker, KeeperFxOracle, PythFxOracle, ChainlinkFxOracle, PoolManager, StateView, ERC20 }
  launchFactoryAbi, bondingCurveAbi, routerAbi, currencyRegistryAbi, fxVaultAbi, synthTokenAbi, launchTokenAbi,
  liquidityLockerAbi, keeperFxOracleAbi, pythFxOracleAbi, chainlinkFxOracleAbi, poolManagerAbi, stateViewAbi, erc20Abi,
  CHAINS, CHAIN_LIST, getChain, findChain, chainByKey, parseChainId, DEFAULT_CHAIN_ID, FALLBACK_CHAIN_ID, deploymentPath,
  type Deployment, type ChainInfo, type DollarInfo, type UniswapV4Addresses, type Address,
} from "@launchpad/abis";
```

The ABIs are JSON exported from Foundry's build output (`packages/abis/src/<Name>.json`); `PoolManager` and `StateView` are Uniswap v4's, `ERC20` is the standard interface. `pnpm abis` at the repository root (`node packages/abis/scripts/export.mjs` run from `contracts/`) regenerates them with `forge inspect <Name> abi --json` after a contract change. The indexer re-exports them as `as const` TypeScript in `packages/indexer/abis/index.ts` (`pnpm --filter @launchpad/indexer codegen`, also run by `postinstall`).

Package exports: `.` (the module), `./chains.json`, `./deployments/*` and `./tokenlist/*`.

## Deployment files

`contracts/script/Deploy.s.sol` writes `contracts/deployments/<chainId>.json`; the deploy scripts copy it to `packages/abis/deployments/<chainId>.json`, which is the path everything reads. Shape (`Deployment`):

```ts theme={"system"}
type Deployment = {
  chainId: number;
  usdc: Address;              // the dollar launches settle in (USDC on Arc, USDG on Robinhood Chain)
  usdcSymbol?: string;        // "USDC" | "USDG"; loaders default to "USDC" when absent
  usdcMintable?: boolean;     // true when `usdc` is the mintable mock a testnet deployment created
  eurc: Address;              // 0x0 when EUR is not registered
  permit2: Address;
  keeperOracle: Address;
  pythOracle: Address;        // 0x0 where Pyth is absent
  registry: Address;
  poolManager: Address;       // Uniswap v4 (official or the launchpad's own)
  positionManager: Address;
  stateView: Address;
  quoter: Address;
  locker: Address;
  curveImplementation: Address;
  factory: Address;
  router: Address;
  currencies: Record<string, { synth: Address; vault: Address }>;   // "INR" -> wINR + its FxVault
};
```

Mainnet files also carry `synthNamePrefix` / `synthSymbolPrefix` ("Windrose" / "w") and `startBlock` (the first block of the deployment broadcast, used as the indexer's default start). `deploymentPath(4663)` returns `deployments/4663.json`.

## The chain registry: `chains.json`

One entry per chain the launchpad can run on. `CHAIN_LIST` is the validated array in file order, `CHAINS` the same keyed by id, `getChain(id)` throws with the list of known chains for an unknown id, `findChain(id)` returns `undefined`, `chainByKey("robinhood")` looks up by slug. Validation runs at import time so a typo fails at startup with a readable message.

| Field                                     | Required     | Meaning                                                                                                                          |
| ----------------------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| `id`, `key`, `name`, `testnet`            | yes          | Chain id, slug (`robinhood-testnet`, used for token-list file names and as the Ponder chain name), display name, testnet flag    |
| `rpc`                                     | yes          | Primary JSON-RPC URL (web app and keeper default)                                                                                |
| `rpcs`                                    | no           | Alternate RPCs; the indexer and deploy scripts default to these where the primary rate-limits                                    |
| `ws`                                      | no           | Websocket for the indexer's realtime blocks                                                                                      |
| `explorer`, `explorerApi`                 | explorer yes | Block explorer; Blockscout verifier URL for `forge verify-contract`                                                              |
| `nativeCurrency`                          | yes          | `{ name, symbol, decimals }`; the symbol appears in "Gas is paid in ..."                                                         |
| `gas`                                     | yes          | `{ maxFeeGwei, priorityGwei, minBaseFeeGwei? }` pinned on writes; `minBaseFeeGwei` documents a floor like Arc's 20 gwei          |
| `dollar`                                  | yes          | `{ address?, symbol, decimals, mintable?, logo? }`: the settlement dollar; `address` is absent until a testnet mock exists       |
| `eurc`, `permit2`, `pyth`, `multicall3`   | no           | Optional contracts                                                                                                               |
| `uniswapV4`                               | no           | `{ poolManager, positionManager, stateView, quoter }` of the official deployment; absent means the deploy script deploys its own |
| `faucet`                                  | no           | Linked from the app on testnets                                                                                                  |
| `ethGetLogsBlockRange`, `maxLogAddresses` | no           | Public RPC limits the indexer respects (Arc 50 blocks / 20 addresses, Robinhood 1000 / 60)                                       |
| `notes`                                   | no           | Free text                                                                                                                        |

Registered today: Arc testnet 5042002 (`arc-testnet`), Arc 5042 (`arc`), Robinhood testnet 46630 (`robinhood-testnet`), Robinhood Chain 4663 (`robinhood`). `contracts/script/Deploy.s.sol` mirrors the dollar, EURC, Permit2, Pyth and Uniswap v4 addresses in its `_defaults` table because forge scripts cannot read JSON conveniently; keep the two in sync. `scripts/chain-env.sh` prints an entry as shell variables for the deploy scripts.

### Selecting a chain

`parseChainId(raw, fallback)` turns an environment value into a registered id (empty means `fallback`, anything else must be a registered positive integer). `DEFAULT_CHAIN_ID` is read from `LAUNCHPAD_CHAIN_ID`, then `NEXT_PUBLIC_CHAIN_ID`, else `FALLBACK_CHAIN_ID` (46630). Each process reads its own variable:

| Process                            | Variable                                                                                            |
| ---------------------------------- | --------------------------------------------------------------------------------------------------- |
| Web app                            | `NEXT_PUBLIC_CHAIN_ID` (default chain only; the switcher offers every chain with a deployment file) |
| Keeper, indexer, generator scripts | `CHAIN_ID` (or `LAUNCHPAD_CHAIN_ID`)                                                                |
| Deploy and seed scripts            | `CHAIN_ID`                                                                                          |

## How addresses are resolved at runtime

**Web app.** `GET /api/deployment?chainId=<id>` reads `packages/abis/deployments/<id>.json` lazily on every request (through the workspace symlink, the relative path or `DEPLOYMENTS_DIR`), so a file written after the server started shows up and a build passes without one. For the default chain only, `NEXT_PUBLIC_FACTORY`, `NEXT_PUBLIC_ROUTER`, `NEXT_PUBLIC_REGISTRY`, `NEXT_PUBLIC_STATE_VIEW`, `NEXT_PUBLIC_USDC` and `NEXT_PUBLIC_USDC_SYMBOL` are a fallback when the file is absent. `GET /api/chains` lists every registry chain with whether it is deployed. Both are documented under [HTTP endpoints](/api).

**Indexer.** `packages/indexer/config/deployment.ts` resolves, in priority order: environment overrides (`FACTORY_ADDRESS`, `REGISTRY_ADDRESS`, `KEEPER_ORACLE_ADDRESS`, `POOL_MANAGER_ADDRESS`, `VAULT_ADDRESSES`, `START_BLOCK_<chainId>` then `START_BLOCK`), then the deployment file (`DEPLOYMENTS_FILE` or `@launchpad/abis/deployments/<chainId>.json`, including its `startBlock`), then zero addresses so codegen and typecheck work before a deployment exists. Vault addresses come from `currencies[*].vault`, or are discovered from `SyntheticCreated` when neither source is available.

**Keeper.** `DEPLOYMENTS_FILE` (default `../abis/deployments/<CHAIN_ID>.json`) for the registry and oracle; it refuses to post when `registry.oracle()` is no longer the file's `keeperOracle`.

## Generator scripts

Both read the deployment file and `contracts/script/Currencies.sol` (parsed from source, so they never drift from the deployment) and apply the deployment's naming prefixes.

**Token list**: `pnpm abis:tokenlist` (`node packages/abis/scripts/tokenlist.mjs [--chain <id>] [--name <list>] [--offline] [--verbose]`) writes `packages/abis/tokenlist/<key>.tokenlist.json` in the Uniswap Token List format: the dollar, EURC where registered and every synthetic, with names, symbols and decimals read from the chain (falling back to the deployment file and `Currencies.sol` when the RPC is unreachable or `--offline`). Tags: `stablecoin`, `synthetic`, `fx`. Logos point at `TOKEN_LOGO_BASE` (default `https://windrose.market/tokens/`; set it to the deployed origin before publishing). The version follows the token-list rules (major when a token is removed, minor when one is added, patch for metadata) and re-running on an unchanged deployment is a no-op. `robinhood.tokenlist.json` and `arc-testnet.tokenlist.json` are committed.

**Logos**: `pnpm abis:logos` (`node packages/abis/scripts/logos.mjs [--chain <id>] [--out apps/web/public/tokens] [--force]`) writes one deterministic SVG coin per synthetic (`wINR.svg`, `acINR.svg`, ...) into `apps/web/public/tokens/`, each under 4 KB, with hues spread evenly around the wheel so sixty coins stay distinguishable. `USDC.svg` / `EURC.svg` / `USDG.svg` are only written as placeholders when missing; the committed ones are the official marks.

## Consuming the package outside the monorepo

The package is not published to npm. Copy `packages/abis/src/*.json`, `chains.json` and the deployment file you need, or depend on the repository as a git dependency. Everything is plain JSON plus one TypeScript module with no dependencies, so it also works from a bundler-free Node script:

```ts theme={"system"}
import { readFileSync } from "node:fs";
const routerAbi = JSON.parse(readFileSync("packages/abis/src/Router.json", "utf8"));
const deployment = JSON.parse(readFileSync("packages/abis/deployments/4663.json", "utf8"));
```
