Files
smom-dbis-138/docs/deployment/CUSDT_CUSDC_MULTICHAIN_LIQUIDITY_RUNBOOK.md
2026-03-02 12:14:09 -08:00

246 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 chains 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 Uniswaps 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 chains Uniswap V2 (or SushiSwap) factory: `createPair(tokenA, tokenB)`.
- Add liquidity via router `addLiquidity` or the pairs `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 chains 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. WETHcUSDT, WETHcUSDC):
```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 Curves 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
- [OPERATOR_NEXT_STEPS_RUNBOOK.md](OPERATOR_NEXT_STEPS_RUNBOOK.md) — G1 PMM on L2s, G2/G3 Trustless