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

# Deploy contracts

> Deploy the protocol to a testnet or a mainnet with the Foundry scripts, seed it, and verify it on Blockscout.

`contracts/script/Deploy.s.sol` deploys the whole protocol in one `forge script` run on any chain in its `_defaults` table (Arc testnet and mainnet, Robinhood testnet and Robinhood Chain); other chains pass every external address in the environment. The wrapper scripts under `scripts/` take the RPC, the gas flags and the deployment file name from the chain registry through `scripts/chain-env.sh`.

## What gets deployed

In order, from one deployer account:

1. **A mintable test dollar** (`MockERC20`, 6 decimals, one million units to the deployer), only with `DEPLOY_TEST_DOLLAR=true` on a testnet without a real dollar.
2. **`KeeperFxOracle`** with a 2 hour fresh window, a 5 day stale limit and `maxMoveBps` 1500; the `KEEPER` address is allow-listed and EUR plus the 60 codes of `Currencies.sol` are enabled. **`PythFxOracle`** (60 s fresh, 5 days stale, confidence 100) only where the chain table or `PYTH` names a Pyth contract; its feeds come from `PythFeeds.sol`.
3. **`CurrencyRegistry`** pointed at the dollar and the keeper oracle. EUR is registered as a real currency when EURC is known. Then one `SynthToken` plus `FxVault` per synthetic currency (up to `MAX_CURRENCIES`, default 60) through `registry.createSynthetic`, every vault with 0.3% mint and redeem fees, 1% stale fee, 20% protocol share, `minCRBps` 10,000 (fully backed by the dollars paid in) and a 1,000,000 dollar liability cap.
4. **Uniswap v4**: the official `PoolManager`, `PositionManager`, `StateView` and `Quoter` when the chain table or the environment names them, otherwise the script deploys its own set (plus a `WETH` for the position manager).
5. **The launchpad**: `LiquidityLocker`, the `BondingCurve` implementation, `LaunchFactory` and `Router`. The factory's launch config is 1e27 total supply, 750e24 on the curve, 250e24 for the pool, 375e24 virtual tokens, 6,000 USD of virtual quote, 1% trade fee with 30% to the creator, 2% graduation fee.

The parameters behind these numbers are explained in [Fees](/concepts/fees) and [Architecture](/protocol/architecture).

## Inputs

The signer is either an encrypted keystore (`forge script --account <name>`; `PRIVATE_KEY` is then ignored) or `PRIVATE_KEY` from the environment or `contracts/.env` (testnets only).

| Variable                                                   | Default      | Meaning                                                                                                                                   |
| ---------------------------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `USDC`                                                     | chain table  | The dollar ERC-20 launches settle in. Required on unknown chains unless `DEPLOY_TEST_DOLLAR=true`. An explicit `USDC` wins over the mock. |
| `USDC_SYMBOL`                                              | chain table  | Its symbol, written to the deployment file.                                                                                               |
| `DEPLOY_TEST_DOLLAR`                                       | `false`      | Deploy a mintable mock dollar. Only allowed on testnets without a real dollar.                                                            |
| `EURC`                                                     | chain table  | EUR stablecoin. `0x0` skips EUR. Required (possibly `0x0`) on unknown chains.                                                             |
| `PERMIT2`                                                  | chain table  | Canonical `0x000000000022D473030F116dDEE9F6B43aC78BA3` on most chains. Required.                                                          |
| `PYTH`                                                     | chain table  | Pyth contract. `0x0` skips `PythFxOracle`.                                                                                                |
| `POOL_MANAGER`, `POSITION_MANAGER`, `STATE_VIEW`, `QUOTER` | chain table  | Set all four to reuse an existing Uniswap v4 deployment; leave all unset to deploy one.                                                   |
| `FEE_COLLECTOR`                                            | the deployer | Receives protocol fees.                                                                                                                   |
| `KEEPER`                                                   | the deployer | Address allow-listed on the keeper oracle.                                                                                                |
| `MAX_CURRENCIES`                                           | `60`         | How many entries of `Currencies.sol` to create; the first entries are the ones with Pyth feeds.                                           |
| `SYNTH_NAME_PREFIX`, `SYNTH_SYMBOL_PREFIX`                 | `Arc`, `ac`  | Naming of the synthetic coins: `Windrose` and `w` give "Windrose Indian Rupee" and `wINR`. Names are immutable once deployed.             |

The per-chain table (`_defaults`) mirrors `dollar`, `eurc`, `permit2`, `pyth` and `uniswapV4` from `packages/abis/chains.json`; keep both in sync:

| Chain                   | Dollar                                              | EURC                                                       | Pyth | Uniswap v4             |
| ----------------------- | --------------------------------------------------- | ---------------------------------------------------------- | ---- | ---------------------- |
| Arc testnet 5042002     | USDC `0x3600…0000`                                  | yes                                                        | yes  | deployed by the script |
| Arc 5042                | USDC `0x3600…0000`                                  | yes                                                        | no   | official addresses     |
| Robinhood testnet 46630 | none: `USDC=<address>` or `DEPLOY_TEST_DOLLAR=true` | no                                                         | no   | deployed by the script |
| Robinhood Chain 4663    | USDG `0x5fc5…d168`                                  | bridged, tiny supply: pass `EURC=<address>` to register it | no   | official addresses     |

## Testnet

<Steps>
  <Step title="Fund a deployer">
    Put a funded testnet key in `contracts/.env` as `PRIVATE_KEY`. Arc testnet: [https://faucet.circle.com](https://faucet.circle.com) (budget about 8 USDC of gas for all 60 currencies). Robinhood testnet: [https://faucet.quicknode.com/robinhood/testnet](https://faucet.quicknode.com/robinhood/testnet).
  </Step>

  <Step title="Deploy">
    ```bash theme={"system"}
    CHAIN_ID=46630 bash scripts/deploy-testnet.sh     # default CHAIN_ID is 5042002; RPC=... overrides the endpoint
    ```

    The script refuses to run when the RPC's chain id differs from `CHAIN_ID`, uses the first alternate RPC of the registry (mirrors handle the broadcast burst better than Arc's primary), pins `--with-gas-price` and `--priority-gas-price` only on chains with a base-fee floor (Arc's 20 gwei; ETH-gas chains let forge estimate), runs the script with `--broadcast --slow`, checks that the factory has code (a deployer with pending transactions produces a nonce mismatch and an empty address) and copies `contracts/deployments/<id>.json` to `packages/abis/deployments/<id>.json`. On a testnet whose registry `dollar` has no address yet, it sets `DEPLOY_TEST_DOLLAR=true` and prints the mock's address: put it under `dollar.address` in `chains.json` afterwards.
  </Step>

  <Step title="Post rates">
    ```bash theme={"system"}
    CHAIN_ID=46630 pnpm --filter @launchpad/keeper once
    ```

    Every currency needs a rate before anything can be minted or launched in it. Then keep `pnpm --filter @launchpad/keeper start` running ([Keeper](/operate/keeper)).
  </Step>

  <Step title="Seed (optional)">
    ```bash theme={"system"}
    CHAIN_ID=46630 bash scripts/seed-testnet.sh
    ```

    Backs the INR, JPY and NGN vaults with `UW` dollars each (default 2 units) and creates four sample launches with `BUY` dollar initial buys (default 1 unit), all with `cast send`. On Arc, `forge script` cannot simulate the native USDC contract (it calls a precompile), so anything that moves USDC has to be sent with `cast` or a wallet; `contracts/script/Seed.s.sol` (`SEED_RATES`, `SEED_UNDERWRITE_USDC`, `SEED_UNDERWRITE_COUNT`, `SEED_LAUNCHES`, `SEED_INITIAL_BUY_USDC`) is for the local anvil demo.
  </Step>

  <Step title="Point the services at it">
    Indexer with `CHAIN_ID`, `PORT`, `PONDER_RPC_URL_<id>` and `START_BLOCK_<id>` (the deploy block; the testnet script does not record it), keeper with `CHAIN_ID`, web with `NEXT_PUBLIC_INDEXER_URL_<id>` and a rebuild. See [Self-hosting](/operate/self-hosting).
  </Step>
</Steps>

## Mainnet

Mainnet deployments are signed by an encrypted Foundry keystore, never by a `PRIVATE_KEY` in a file. `scripts/deploy-mainnet.sh` refuses testnets, `unset`s `PRIVATE_KEY`, and requires `KEEPER`.

<Steps>
  <Step title="Create the keystore once">
    ```bash theme={"system"}
    cast wallet new ~/.foundry/keystores windrose-deployer          # password in ~/.foundry/windrose-deployer.password
    cast wallet address --account windrose-deployer --password-file ~/.foundry/windrose-deployer.password
    ```

    Keep both files readable only by your account. `DEPLOYER_ACCOUNT` and `DEPLOYER_PASSWORD_FILE` override the names.
  </Step>

  <Step title="Fund the deployer">
    ETH on Robinhood Chain; the whole deployment simulates at about 0.003 ETH, and the script requires at least 0.01 ETH before broadcasting.
  </Step>

  <Step title="Simulate, then deploy">
    ```bash theme={"system"}
    DRY_RUN=1 KEEPER=<keeper hot key> SYNTH_NAME_PREFIX=Windrose SYNTH_SYMBOL_PREFIX=w bash scripts/deploy-mainnet.sh
    KEEPER=<keeper hot key> SYNTH_NAME_PREFIX=Windrose SYNTH_SYMBOL_PREFIX=w bash scripts/deploy-mainnet.sh
    ```

    `CHAIN_ID` defaults to 4663. `FEE_COLLECTOR` defaults to the deployer, `MAX_CURRENCIES` to 60. The script prints the chain, deployer, balance, keeper, fee collector and coin naming before running `forge script --account`, then records the deploy block from the broadcast receipts as `startBlock` in the deployment file, copies it to `packages/abis/deployments/4663.json` and runs `scripts/verify.sh`.
  </Step>

  <Step title="Hand over">
    Commit the deployment file and push it to the box; then, on the box, pull, install, set the web app's default chain and indexer URLs, restart the keeper and indexer under their mainnet names and rebuild the web app (see [Self-hosting](/operate/self-hosting)).
  </Step>
</Steps>

<Warning>
  The deployer keeps ownership of every contract. They are `Ownable2Step`, so moving control to a hardware wallet or a multisig takes `transferOwnership` by the deployer followed by `acceptOwnership` by the new owner. The keeper address only holds the oracle's keeper role. Nothing here is audited: do not hold real money in the protocol before an external audit.
</Warning>

## Verification

`scripts/verify.sh` verifies every contract of a deployment on the chain's Blockscout (`explorerApi` in the registry), skipping the ones already verified:

```bash theme={"system"}
CHAIN_ID=4663 bash scripts/verify.sh                              # all contracts of packages/abis/deployments/4663.json
ONLY=factory,router bash scripts/verify.sh                        # a subset (keys of the deployment file; synths / vaults = the coins)
ONLY=synths,vaults CODES="AED BRL" bash scripts/verify.sh         # only those currencies
VERIFIER=sourcify bash scripts/verify.sh                          # submit to Sourcify instead
```

Details worth knowing:

* `VERIFIER_URL` overrides the API, `RPC` the endpoint, `DELAY` the pause between submissions (4 s).
* The self-deployed Uniswap v4 contracts (testnets) need their own compiler profiles: `PoolManager` with `--compilation-profile v4core` and `PositionManager` with `posm` (both via-IR, see `contracts/foundry.toml`). Official deployments are already verified.
* The coins and vaults are created by `CurrencyRegistry.createSynthetic`, so forge cannot guess their constructor arguments from a creation transaction; the script rebuilds them from the contracts' own getters. Blockscout marks bytecode-identical contracts as verified once one of them is, so the 60 coins usually need one `SynthToken` and one `FxVault` submission.
* Blockscout's public verifier answers "Too many requests" under load; get an API key at [https://dev.blockscout.com](https://dev.blockscout.com) and pass `--etherscan-api-key`. Where a Blockscout API sits behind Cloudflare (Robinhood Chain's does), `VERIFIER=sourcify` is the way in: Blockscout imports Sourcify matches.

## The deployment file

`Deploy.s.sol` writes `contracts/deployments/<chainId>.json`; the wrappers copy it to `packages/abis/deployments/<chainId>.json`, which every off-chain process reads (its TypeScript shape is `Deployment` in `@launchpad/abis`). Commit it: the web app's chain switcher, the keeper and the indexer all key off its presence.

| Field                                                   | Meaning                                                                                                                     |
| ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `chainId`, `deployer`                                   | Chain and the deploying account.                                                                                            |
| `usdc`, `usdcSymbol`, `usdcMintable`                    | The dollar launches settle in (the field is called `usdc` on every chain), its symbol, and whether it is the script's mock. |
| `eurc`                                                  | EURC or the zero address.                                                                                                   |
| `permit2`                                               | Permit2.                                                                                                                    |
| `keeperOracle`, `pythOracle`                            | Oracle adapters (`pythOracle` is zero where Pyth is absent).                                                                |
| `registry`                                              | `CurrencyRegistry`.                                                                                                         |
| `poolManager`, `positionManager`, `stateView`, `quoter` | Uniswap v4, official or self-deployed.                                                                                      |
| `locker`, `curveImplementation`, `factory`, `router`    | The launchpad.                                                                                                              |
| `currencies`                                            | `code` to `{ synth, vault }` for every synthetic currency created.                                                          |
| `synthNamePrefix`, `synthSymbolPrefix`                  | The coin naming used, read back by the logo and token-list generators.                                                      |
| `startBlock`                                            | Deploy block, written by the mainnet script; the indexer starts there.                                                      |

The live addresses per chain are listed under [Addresses](/protocol/addresses).
