# cUSDT / cUSDC Multi-Chain Deployment and Liquidity Runbook

**Purpose:** Deploy cUSDT and cUSDC to other blockchain networks, create Dodo PMM and Uniswap liquidity pools, and add cUSDT/cUSDC to Balancer, Curve, and other protocols.

**Status:** Active runbook  
**Last updated:** 2026-02-20

---

## Overview

| Phase | Description |
|-------|-------------|
| **1. Deploy cUSDT/cUSDC to other chains** | Deploy CompliantUSDT and CompliantUSDC on each target chain (BSC, Polygon, Base, etc.) using existing Forge scripts. |
| **2. Dodo PMM** | Create PMM pools: Chain 138 (existing) + L2s via `deploy-pmm-all-l2s.sh`; optionally use cUSDT/cUSDC on L2s once deployed. |
| **3. Uniswap** | Create Uniswap V2/V3 pools for cUSDT/cUSDC on each chain (Uniswap factory + add liquidity). |
| **4. Balancer** | Create Balancer pools for cUSDT/cUSDC (Balancer UI or factory); on Ethereum mainnet, register pool IDs in EnhancedSwapRouter. |
| **5. Curve** | Add cUSDT/cUSDC to Curve pools (Curve UI or factory; mainnet-focused). |
| **6. Other protocols** | Document 1inch (aggregator), and other DEXes as needed. |

---

## Prerequisites

- **.env** in `smom-dbis-138/` with:
  - `PRIVATE_KEY` — deployer (must hold native gas token on each target chain).
  - Per-chain RPC: `BSC_RPC_URL`, `POLYGON_MAINNET_RPC`, `BASE_MAINNET_RPC`, `ARBITRUM_MAINNET_RPC`, `OPTIMISM_MAINNET_RPC`, `AVALANCHE_RPC_URL`, `CRONOS_RPC_URL`, `GNOSIS_MAINNET_RPC`, `ETHEREUM_MAINNET_RPC`.
- **Gas:** Deployer funded with ETH (mainnet), BNB (BSC), MATIC (Polygon), etc.
- **Compliance:** Same compliance/owner setup as Chain 138 if required (e.g. `COMPLIANCE_ADMIN`, `USDT_OWNER`, `USDC_OWNER`).

---

## Phase 1: Deploy cUSDT and cUSDC to Other Chains

cUSDT/cUSDC are deployed per chain using the same contracts; only the RPC and chain ID change.

### Chains to target

- **Ethereum** (1) — mainnet
- **BSC** (56)
- **Polygon** (137)
- **Base** (8453)
- **Arbitrum** (42161)
- **Optimism** (10)
- **Avalanche** (43114)
- **Cronos** (25)
- **Gnosis** (100)

### Deploy commands (per chain)

From `smom-dbis-138/`:

```bash
# Set RPC and chain for the target network
export RPC_URL="$POLYGON_MAINNET_RPC"   # or BSC_RPC_URL, BASE_MAINNET_RPC, etc.
export CHAIN_ID=137                      # 56=BSC, 137=Polygon, 8453=Base, etc.

# Deploy CompliantUSDT
forge script script/DeployCompliantUSDT.s.sol:DeployCompliantUSDT \
  --rpc-url "$RPC_URL" \
  --chain-id "$CHAIN_ID" \
  --broadcast \
  --private-key "$PRIVATE_KEY" \
  -vv

# Deploy CompliantUSDC
forge script script/DeployCompliantUSDC.s.sol:DeployCompliantUSDC \
  --rpc-url "$RPC_URL" \
  --chain-id "$CHAIN_ID" \
  --broadcast \
  --private-key "$PRIVATE_KEY" \
  -vv
```

Record the deployed addresses and set them in `.env` (see **Env vars** below).

### Optional: batch deploy script

Use `scripts/deployment/deploy-cusdt-cusdc-all-chains.sh` (see below) to loop over chains and deploy both tokens, then record addresses.

### Env vars (per-chain cUSDT/cUSDC)

After deployment, set in `.env` for token-aggregation and PMM:

```bash
# Example for Polygon (137)
CUSDT_ADDRESS_137=0x...
CUSDC_ADDRESS_137=0x...

# Example for BSC (56)
CUSDT_ADDRESS_56=0x...
CUSDC_ADDRESS_56=0x...
```

Use the same pattern for other chain IDs: `CUSDT_ADDRESS_<chainId>`, `CUSDC_ADDRESS_<chainId>`. Token-aggregation reads these when provided (see `canonical-tokens.ts` and `.env.example`).

---

## Phase 2: Dodo PMM Liquidity Pools

### Chain 138 (existing)

- **DODOPMMIntegration** and mock DVM are already deployed.
- Create cUSDT/cUSDC pools: `createCUSDTUSDTPool`, `createCUSDCUSDCPool`, `createCUSDTCUSDCPool` via `scripts/setup-dodo-pools.sh` or cast (see [DODO_PMM_INTEGRATION.md](../integration/DODO_PMM_INTEGRATION.md)).
- Add liquidity: `DODOPMMIntegration.addLiquidity(pool, baseAmount, quoteAmount)`.

### L2s (BSC, Polygon, Base, etc.)

**Option A — Official USDT/USDC on L2 (current script behavior)**  
Pools are USDT/USDC on each L2 (no cUSDT/cUSDC on L2 yet):

```bash
cd smom-dbis-138
# Set in .env: BSC_RPC_URL, BSC_DODO_VENDING_MACHINE_ADDRESS, BSC_OFFICIAL_USDT_ADDRESS, BSC_OFFICIAL_USDC_ADDRESS
# (and same for POLYGON_*, BASE_*, etc.). DODO DVM addresses: https://docs.dodoex.io/developer/contracts/dodo-v1-v2/contracts-address
./scripts/deployment/deploy-pmm-all-l2s.sh
# Or one chain: ./scripts/deployment/deploy-pmm-all-l2s.sh --chain polygon
```

**Option B — cUSDT/cUSDC on L2**  
After deploying cUSDT/cUSDC to an L2 (Phase 1), set that chain’s compliant addresses and DVM, then run the same script with overrides so the integration uses cUSDT/cUSDC for that chain (e.g. `POLYGON_COMPLIANT_USDT_ADDRESS`, `POLYGON_COMPLIANT_USDC_ADDRESS`). The script currently uses `COMPLIANT_USDT_ADDRESS` / `COMPLIANT_USDC_ADDRESS` as fallback; for L2 cUSDT/cUSDC you would set per-chain env (e.g. `POLYGON_CUSDT_ADDRESS_137`) and, if the script supports it, pass them as compliant tokens for that chain. If not yet supported, deploy DODOPMMIntegration for that L2 manually with `COMPLIANT_USDT_ADDRESS` / `COMPLIANT_USDC_ADDRESS` set to the L2 cUSDT/cUSDC addresses.

Record `DODOPMM_INTEGRATION_<CHAIN>` (or per-chain integration address) in `.env`.

---

## Phase 3: Uniswap Liquidity Pools

Uniswap does not have a single “create pool” script in this repo. Create pools on each chain as follows.

### Uniswap V3 (recommended for mainnet and L2s)

- **Factory:** Same address on mainnet, Polygon, BSC, Base, Arbitrum, Optimism: `0x1F98431c8aD98523631AE4a59f267346ea31F984` ([Uniswap V3 Deployments](https://docs.uniswap.org/contracts/v3/deployments)).
- **Create pool:** Call `UniswapV3Factory.createPool(tokenA, tokenB, fee)` (e.g. fee 500 = 0.05% for stables).
- **Initialize:** Call `UniswapV3Pool.initialize(sqrtPriceX96)`.
- **Add liquidity:** Use Uniswap’s NonfungiblePositionManager or Uniswap UI.

**Helper script (from `smom-dbis-138/`):**

```bash
# Create cUSDT/cUSDC pool on Polygon (set CUSDT_ADDRESS_137, CUSDC_ADDRESS_137 and POLYGON_MAINNET_RPC in .env)
RPC_URL=$POLYGON_MAINNET_RPC ./scripts/deployment/create-uniswap-v3-pool-cusdt-cusdc.sh

# Or pass tokens and RPC explicitly
TOKEN_A=0x... TOKEN_B=0x... FEE=500 RPC_URL=$ETHEREUM_MAINNET_RPC ./scripts/deployment/create-uniswap-v3-pool-cusdt-cusdc.sh
```

Example (manual cast, Ethereum mainnet):

```bash
FACTORY=0x1F98431c8aD98523631AE4a59f267346ea31F984
cast send $FACTORY "createPool(address,address,uint24)" $CUSDT_ADDRESS_1 $CUSDC_ADDRESS_1 500 \
  --rpc-url "$ETHEREUM_MAINNET_RPC" --private-key "$PRIVATE_KEY"
# Then initialize and add liquidity via Uniswap UI or position manager.
```

### Uniswap V2

- Use the chain’s Uniswap V2 (or SushiSwap) factory: `createPair(tokenA, tokenB)`.
- Add liquidity via router `addLiquidity` or the pair’s `mint`.

### Documentation

- Uniswap V3: https://docs.uniswap.org/contracts/v3/reference/core/UniswapV3Factory  
- Uniswap V2: https://docs.uniswap.org/contracts/v2/reference/smart-contracts/factory  

After creating pools, the token-aggregation service can index them if it is configured to index Uniswap V2/V3 on that chain (see pool-indexer and config).

---

## Phase 4: Balancer

- **Create pools:** Use [Balancer](https://app.balancer.fi/) (or the chain’s Balancer app) to create a pool containing cUSDT and cUSDC (and optionally WETH or other assets). Balancer uses pool IDs (bytes32).
- **Ethereum mainnet + EnhancedSwapRouter:** After creating a pool, set the pool ID in EnhancedSwapRouter for the pairs you route (e.g. WETH–cUSDT, WETH–cUSDC):

```bash
cast send $ENHANCED_SWAP_ROUTER \
  "setBalancerPoolId(address,address,bytes32)" \
  <tokenA> <tokenB> <poolId> \
  --rpc-url "$ETHEREUM_MAINNET_RPC" --private-key "$PRIVATE_KEY"
```

See [ENHANCED_SWAP_ROUTER_UPGRADE.md](../bridge/trustless/ENHANCED_SWAP_ROUTER_UPGRADE.md).

---

## Phase 5: Curve

- **Curve** is used mainly on Ethereum (and a few L2s). Add cUSDT/cUSDC to an existing Curve pool or create a new pool via Curve’s factory/UI.
- **Resources:** [Curve Docs](https://docs.curve.fi/), [Curve Factory](https://curve.fi/).
- **EnhancedSwapRouter:** Uses Curve for large swaps; ensure the router is configured with the correct Curve pool (or pool getter) for cUSDT/cUSDC if you add a dedicated pool.

---

## Phase 6: Other Protocols

- **1inch:** Aggregator only; it discovers existing pools. Once cUSDT/cUSDC have liquidity on Uniswap, Balancer, Curve, DODO, etc., 1inch will quote them if the pool is indexed.
- **Other DEXes:** Per chain (e.g. SushiSwap, Camelot, etc.): create pairs/pools via their factory or UI, then add liquidity. Token-aggregation can index additional DEXes if the pool indexer is extended (see `services/token-aggregation/src/indexer/pool-indexer.ts`).

---

## Checklist Summary

| Step | Action | Script / reference |
|------|--------|--------------------|
| 1a | Deploy cUSDT to each target chain | `forge script script/DeployCompliantUSDT.s.sol` with chain RPC |
| 1b | Deploy cUSDC to each target chain | `forge script script/DeployCompliantUSDC.s.sol` with chain RPC |
| 1c | Set `CUSDT_ADDRESS_<chainId>`, `CUSDC_ADDRESS_<chainId>` in .env | — |
| 2a | Dodo PMM on Chain 138 | `scripts/setup-dodo-pools.sh`, [DODO_PMM_INTEGRATION.md](../integration/DODO_PMM_INTEGRATION.md) |
| 2b | Dodo PMM on L2s | `./scripts/deployment/deploy-pmm-all-l2s.sh` |
| 3 | Uniswap V2/V3 pools for cUSDT/cUSDC | `scripts/deployment/create-uniswap-v3-pool-cusdt-cusdc.sh` or Uniswap factory + init + add liquidity (per chain) |
| 4 | Balancer pools + EnhancedSwapRouter pool IDs | Balancer UI/factory; `setBalancerPoolId` on router |
| 5 | Curve pools | Curve UI/factory |
| 6 | Token-aggregation / CoinGecko | Set canonical addresses; report API and CoinGecko submission |

---

## Verification

- **Token on chain:** `cast call <CUSDT_ADDRESS> "totalSupply()(uint256)" --rpc-url $RPC_URL` (and same for cUSDC). Expect 6 decimals (e.g. 1e6 = 1 token).
- **PMM integration (L2):** After `deploy-pmm-all-l2s.sh`, set `DODOPMM_INTEGRATION_<CHAIN>` in .env; verify with `cast call $INTEGRATION "..." --rpc-url $RPC`.
- **Uniswap pool:** `cast call $UNISWAP_V3_FACTORY "getPool(address,address,uint24)(address)" $TOKEN_A $TOKEN_B 500 --rpc-url $RPC_URL`; non-zero address = pool exists.
- **Token-aggregation:** `GET /api/v1/report/token-list?chainId=<id>` and `GET /api/v1/report/coingecko?chainId=<id>` should include cUSDT/cUSDC when `CUSDT_ADDRESS_<id>` and `CUSDC_ADDRESS_<id>` are set in the service env.

---

## Troubleshooting

| Issue | Check | Fix |
|-------|--------|-----|
| Deploy fails "insufficient funds" | Deployer balance on that chain | Fund with native gas (ETH, BNB, MATIC, etc.) |
| Deploy fails "nonce" / "replacement" | Pending or stuck tx on that chain | Clear mempool or use next nonce; see [RPC_NODES_BLOCK_PRODUCTION_FIX](../../../docs/09-troubleshooting/RPC_NODES_BLOCK_PRODUCTION_FIX.md) for Chain 138 |
| deploy-pmm-all-l2s skips chain | Missing RPC or DVM/token env for that chain | Set `BSC_RPC_URL`, `BSC_DODO_VENDING_MACHINE_ADDRESS`, `BSC_OFFICIAL_USDT_ADDRESS`, `BSC_OFFICIAL_USDC_ADDRESS` (and equivalent for other chains) |
| Uniswap createPool reverts | Token order or fee; pool may already exist | Use factory `getPool(tokenA, tokenB, fee)` first; 0x0 = create, else pool exists |
| Token-aggregation report missing token | Canonical list only has 138/651940 by default; L2 needs env | Set `CUSDT_ADDRESS_56`, `CUSDC_ADDRESS_56`, etc. in env used by token-aggregation (see `canonical-tokens.ts`) |

---

## Related docs

- [ALL_MAINNETS_DEPLOYMENT_RUNBOOK.md](ALL_MAINNETS_DEPLOYMENT_RUNBOOK.md) — CCIP, Trustless, Oracle, PMM overview  
- [DVM_DEPLOYMENT_CHECK.md](DVM_DEPLOYMENT_CHECK.md) — DODO DVM on Chain 138 vs L2s  
- [DODO_PMM_INTEGRATION.md](../integration/DODO_PMM_INTEGRATION.md) — Pool creation and swap methods  
- [ENHANCED_SWAP_ROUTER_UPGRADE.md](../bridge/trustless/ENHANCED_SWAP_ROUTER_UPGRADE.md) — Balancer/Curve/Uniswap on mainnet  
- [ALL_NEXT_STEPS_CURRENT.md](ALL_NEXT_STEPS_CURRENT.md) — Current G1 PMM on L2s and G2/G3 Trustless follow-up steps  
