Files
proxmox/docs/03-deployment/CONTRACT_DEPLOYMENT_RUNBOOK.md
defiQUG dedb55e05c
Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled
docs(03-deployment): runbooks and deployment status updates
Made-with: Cursor
2026-03-27 18:48:41 -07:00

337 lines
24 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.
# Contract Deployment Runbook
**Last Updated:** 2026-02-12
**Full deployment order:** For the canonical sequence (prerequisites → core → PMM/pools → provider → optional → cW* → verification) and remaining recommendations, see [DEPLOYMENT_ORDER_OF_OPERATIONS.md](DEPLOYMENT_ORDER_OF_OPERATIONS.md).
**Deployment safety:** Use **RPC_URL_138** (Core only, from `smom-dbis-138/.env`) for all deployments; never use Public RPC. All secrets from **`smom-dbis-138/.env`** only. Run a gas/cost estimate before deploy (e.g. `cd smom-dbis-138 && ./scripts/deployment/calculate-costs-consolidated.sh`). **Do not deploy when transactions are stuck** — clear tx pool (`./scripts/clear-all-transaction-pools.sh`), wait ~60s, then retry; use scripts that check nonce when available.
## Chain 138 deployment requirements (learned 2026-02-12)
- **Gas price:** Chain 138 enforces a minimum gas price. Always use **`--with-gas-price 1000000000`** (1 gwei) for `forge script` and `forge create` when deploying to Chain 138; otherwise transactions fail with "Gas price below configured minimum gas price".
- **Gas 32xxx when deploying:** If you see gas-related RPC errors (e.g. -32000, execution reverted, or out of gas), add **`--gas-estimate-multiplier 150`** (or 200) to `forge script ... --broadcast` so the broadcast uses a higher gas limit. See [RPC_ERRORS_32001_32602.md](../09-troubleshooting/RPC_ERRORS_32001_32602.md).
- **On-chain check:** After deployments, run `./scripts/verify/check-contracts-on-chain-138.sh` (uses `RPC_URL_138`; optional URL arg). Address list comes from `config/smart-contracts-master.json` when available. See [CONTRACT_ADDRESSES_REFERENCE](../11-references/CONTRACT_ADDRESSES_REFERENCE.md), [ADDRESS_MATRIX_AND_STATUS](../11-references/ADDRESS_MATRIX_AND_STATUS.md).
- **TransactionMirror:** The deploy script can hit a Forge broadcast constructor-args decode error. If so, deploy manually: `forge create contracts/mirror/TransactionMirror.sol:TransactionMirror --constructor-args <ADMIN_ADDRESS> --rpc-url $RPC_URL_138 --private-key $PRIVATE_KEY --gas-price 1000000000`.
## RPC Routing Summary
Chain 138 uses two standard env vars: **RPC_URL_138** (Core, admin/deploy) and **RPC_URL_138_PUBLIC** (Public, bridge/frontend). See [RPC_ENDPOINTS_MASTER](../04-configuration/RPC_ENDPOINTS_MASTER.md).
| Use Case | VMID | IP | Ports | Variable |
|----------|------|-----|-------|----------|
| Admin / contract deployment | 2101 | 192.168.11.211 | 8545, 8546 | RPC_URL_138 (Core) |
| Bridge, monitoring, public-facing | 2201 | 192.168.11.221 **(FIXED)** | 8545, 8546 | RPC_URL_138_PUBLIC (Public) |
## Prerequisites
1. **.env check (keys only, no secrets printed):** From repo root: `./scripts/deployment/preflight-chain138-deploy.sh` (RPC, dotenv, nonce). Or from smom-dbis-138: `./scripts/deployment/check-env-required.sh` — verifies `PRIVATE_KEY`, `RPC_URL`, `RPC_URL_138` and optional PMM/mainnet/CCIP vars. Use **`smom-dbis-138/.env`** only for deploy secrets.
2. **Network access** to Chain 138 RPC (set `RPC_URL_138` in .env, e.g. http://192.168.11.211:8545 for Core)
- Run from a host on the same LAN as Proxmox, or via VPN
- WSL/remote dev environments may get "No route to host" if not on network
3. **PRIVATE_KEY** in `smom-dbis-138/.env` (deployer wallet with gas; same wallet holds LINK for bridge fees)
4. **Foundry** (`forge`) installed
5. **Test all contracts before deploy (Phase 0.8):** Run `./scripts/deployment/test-all-contracts-before-deploy.sh` from repo root. This runs `forge build` and `forge test` in smom-dbis-138. Use `--dry-run` to print commands only; `--alltra` to include alltra-lifi-settlement; `--no-match "Fork|Mainnet|Integration|e2e"` for unit tests only. See [DEPLOYMENT_ORDER_OF_OPERATIONS](DEPLOYMENT_ORDER_OF_OPERATIONS.md) § Phase 0.8.
### Deprecated bridge (R4)
Do not use CCIPWETH9Bridge at `0x89dd...`. Use only the canonical bridge at `0x971c...` and set `CCIPWETH9_BRIDGE_CHAIN138` in env. See docs/00-meta/RECOMMENDATIONS_OPERATOR_CHECKLIST.md R4.
### Env required per deploy script
| Script / phase | Required env | Notes |
|----------------|--------------|--------|
| DeployMulticall, DeployOracle, DeployMultiSig | `PRIVATE_KEY`, `RPC_URL_138` (Chain 138 Core) | deploy-all-contracts.sh |
| 01_DeployCore, 02_DeployBridges | `PRIVATE_KEY`, RPC; 02 needs `UNIVERSAL_ASSET_REGISTRY`, `GOVERNANCE_CONTROLLER` | Phased core |
| DeployCCIPReceiver, DeployCCIPSender | `PRIVATE_KEY`, `CCIP_ROUTER_ADDRESS`, `ORACLE_AGGREGATOR_ADDRESS`; Sender optional: `LINK_TOKEN_ADDRESS` | Set in .env; see .env.example |
| DeployWETHBridges (mainnet receiver) | `MAINNET_WETH9_BRIDGE_ADDRESS`, `MAINNET_WETH10_BRIDGE_ADDRESS` when configuring cross-chain | .env.example |
| DeploySmartAccountsKit | `PRIVATE_KEY`, `RPC_URL_138`; optional `ENTRY_POINT`, `SMART_ACCOUNT_FACTORY`, `PAYMASTER` if pre-deployed | Script does not deploy contracts; obtain EntryPoint/Factory from MetaMask kit or ERC-4337 impl and set in env |
| DeployTransactionMirror | `PRIVATE_KEY`, `MIRROR_ADMIN` (optional, default deployer) | If `forge script` fails with constructor-args decode, use forge create — see § TransactionMirror below |
| DeployReserveSystem | `TOKEN_FACTORY` in .env | Phase 6 |
## Deploy Core Contracts (Chain 138)
```bash
cd smom-dbis-138
source .env
# Verify RPC: curl -s -X POST "$RPC_URL" -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
bash scripts/deployment/deploy-all-contracts.sh
```
Deploys: Multicall, Oracle, MultiSig (WETH9/10 pre-deployed in genesis).
## Deploy Unified (Ordered or Parallel)
```bash
cd smom-dbis-138
./scripts/deployment/deploy-contracts-unified.sh --mode ordered
# or
./scripts/deployment/deploy-contracts-unified.sh --mode parallel
```
## Deploy WETH Bridges (CCIP)
```bash
# From project root (use GAS_PRICE=1000000000 if min-gas-price error)
GAS_PRICE=1000000000 ./scripts/deploy-and-configure-weth9-bridge-chain138.sh
# Then set CCIPWETH9_BRIDGE_CHAIN138 in smom-dbis-138/.env
```
## Smart accounts (ERC-4337)
**Script:** `smom-dbis-138/script/smart-accounts/DeploySmartAccountsKit.s.sol` (and `DeployAccountWalletRegistryExtended.s.sol` for registry).
**Required env (in `smom-dbis-138/.env`):** `PRIVATE_KEY`, `RPC_URL_138` (Chain 138 Core). Optional: set `ENTRY_POINT`, `SMART_ACCOUNT_FACTORY`, `PAYMASTER` if already deployed; otherwise the script will deploy and log addresses to set in `.env`.
**Deploy (Chain 138):**
```bash
cd smom-dbis-138
source .env
forge script script/smart-accounts/DeploySmartAccountsKit.s.sol --rpc-url $RPC_URL_138 --broadcast --with-gas-price 1000000000
# Set ENTRY_POINT, SMART_ACCOUNT_FACTORY, PAYMASTER from output
```
**Verification:** Run script; confirm logged addresses match env. See [PLACEHOLDERS_AND_TBD](../PLACEHOLDERS_AND_TBD.md) Smart Accounts Kit.
## TransactionMirror (Chain 138)
**Script:** `script/DeployTransactionMirror.s.sol`. **Deployed address:** Set in `smom-dbis-138/.env` as `TRANSACTION_MIRROR_ADDRESS` from the script output (e.g. past deploys: `0xE362aa10D3Af1A16880A799b78D18F923403B55a`, `0x4eeF36BBaf706C6da5859cF9B34E9934fEC3E006`).
**Recommended:** Use the combined script; it **always checks nonce**, **validates RPC is active (chainId 138)**, uses **proper gas** (1 gwei min), and loads the **correct dotenv** (`smom-dbis-138/.env` + `config/ip-addresses.conf` for RPC fallbacks).
**Required in `smom-dbis-138/.env`:** `PRIVATE_KEY`, `RPC_URL_138` (Core RPC, 192.168.11.211:8545). No Public fallback for deployments. Optional: `GAS_PRICE` or `GAS_PRICE_138` (default 1000000000). Before deploying: if Core was read-only, run `./scripts/maintenance/make-rpc-vmids-writable-via-ssh.sh` then `./scripts/maintenance/health-check-rpc-2101.sh`. See [RPC_2101_READONLY_FIX.md](RPC_2101_READONLY_FIX.md).
**If you see "Known transaction" or "Replacement transaction underpriced":** Clear the tx pool then retry: `./scripts/clear-all-transaction-pools.sh` (or RPC-only; see script). Run from a host that can reach `RPC_URL_138` (same LAN/VPN):
```bash
./scripts/deployment/deploy-transaction-mirror-and-pmm-pool-after-txpool-clear.sh
```
This deploys TransactionMirror and creates the DODO cUSDT/cUSDC PMM pool, then runs on-chain verification. **Core RPC only** (no Public fallback). If Core is unreachable, fix read-only and health first (see RPC_2101_READONLY_FIX.md). Options: `--dry-run` (env, RPC, nonce only); `--force` (skip RPC check).
**Skip stuck nonce manually:** Set `NEXT_NONCE` to the next nonce (e.g. `13370`) so the script uses `vm.setNonce` and deploys at a new address; then set `TRANSACTION_MIRROR_ADDRESS` in `.env` to the logged address. The combined script already sets `NEXT_NONCE` from pending nonce.
Or run the two forge commands manually (ensure RPC is Chain 138 and nonce is correct):
```bash
cd smom-dbis-138 && source .env
# Optional: export NEXT_NONCE=<pending nonce> if avoiding a stuck tx
forge script script/DeployTransactionMirror.s.sol:DeployTransactionMirror --rpc-url "$RPC_URL_138" --broadcast --private-key "$PRIVATE_KEY" --with-gas-price 1000000000
forge script script/dex/CreateCUSDTCUSDCPool.s.sol:CreateCUSDTCUSDCPool --rpc-url "$RPC_URL_138" --broadcast --private-key "$PRIVATE_KEY" --with-gas-price 1000000000
```
If `forge script` fails with "Failed to decode constructor arguments", deploy via `forge create`:
```bash
cd smom-dbis-138
source .env
ADMIN="${MIRROR_ADMIN:-$(cast wallet address --private-key $PRIVATE_KEY)}"
forge create contracts/mirror/TransactionMirror.sol:TransactionMirror \
--constructor-args "$ADMIN" \
--rpc-url "$RPC_URL_138" \
--private-key "$PRIVATE_KEY" \
--legacy \
--gas-price 1000000000
```
Or run the helper script (from repo root, **from a host on LAN** that can reach `RPC_URL_138` e.g. 192.168.11.211:8545): `./scripts/deployment/deploy-transaction-mirror-chain138.sh`. The script exports `ETH_RPC_URL` so Forge uses the correct RPC and, on success, updates or appends `TRANSACTION_MIRROR_ADDRESS` in `smom-dbis-138/.env`.
## AlltraAdapter — setBridgeFee after deploy
After deploying or using AlltraAdapter (138 ↔ ALL Mainnet 651940), set the bridge fee to match ALL Mainnet fee structure.
**Required:** `ALLTRA_ADAPTER_CHAIN138` in `smom-dbis-138/.env` (adapter address; see config/smart-contracts-master.json). Optional: `ALLTRA_BRIDGE_FEE` (wei) to pass to setBridgeFee. Deployer must have `DEFAULT_ADMIN_ROLE`.
```bash
cd smom-dbis-138 && source .env
# Use ALLTRA_BRIDGE_FEE from .env if set, else 0.001 ALL (1000000000000000 wei)
FEE="${ALLTRA_BRIDGE_FEE:-1000000000000000}"
cast send "$ALLTRA_ADAPTER_CHAIN138" "setBridgeFee(uint256)" "$FEE" \
--rpc-url "$RPC_URL_138" --private-key "$PRIVATE_KEY" --gas-price 1000000000
```
Document the final fee in [PLACEHOLDERS_AND_TBD](../PLACEHOLDERS_AND_TBD.md) § AlltraAdapter when known.
## Vault ac* / vdc* / sdc* (Chain 138)
After deploying the Vault System (`DeployVaultSystem.s.sol`), run the single script that creates all asset/deposit (ac*) and debt (vdc*) tokens via `VaultFactory.createVaultWithDecimals`.
**Required env:** `VAULT_FACTORY_ADDRESS`, `PRIVATE_KEY`, `RPC_URL_138`. Optional: `OWNER`, `ENTITY` (default deployer); `CUSDC_ADDRESS_138`, `CUSDT_ADDRESS_138` or `COMPLIANT_USDC_ADDRESS`, `COMPLIANT_USDT_ADDRESS` (skip currency if unset).
Option A — deploy Vault System then ac/vdc/sdc in one go: run `./scripts/deployment/deploy-vault-system-and-ac-vdc-sdc.sh` from `smom-dbis-138` (uses `PRIVATE_KEY`, `RPC_URL_138`; parses VaultFactory from broadcast and runs ac/vdc/sdc step).
Option B — run steps separately:
```bash
cd smom-dbis-138
source .env
forge script script/deploy/vault/DeployVaultSystem.s.sol:DeployVaultSystem \
--rpc-url "$RPC_URL_138" --broadcast --with-gas-price 1000000000
# From output, set VAULT_FACTORY_ADDRESS=0x... then:
forge script script/deploy/vault/DeployAcVdcSdcVaults.s.sol:DeployAcVdcSdcVaults \
--rpc-url "$RPC_URL_138" --broadcast --with-gas-price 1000000000
```
Deployer must have `VAULT_DEPLOYER_ROLE` on VaultFactory. Each configured base token gets one vault with deposit token (ac*) and debt token (vdc*), 6 decimals, transferable debt. See [MULTI_CHAIN_EXECUTION_DETERMINISTIC_DEPLOYMENT](../runbooks/MULTI_CHAIN_EXECUTION_DETERMINISTIC_DEPLOYMENT.md). Full architecture and phased roadmap: see [VAULT_SYSTEM_MASTER_TECHNICAL_PLAN](../VAULT_SYSTEM_MASTER_TECHNICAL_PLAN.md).
## EnhancedSwapRouter & DODOPMMProvider (post-deploy configuration)
When Uniswap V3, Balancer, or DODO PMM pools exist on Chain 138 / 651940, configure the router and provider so on-chain quotes and swaps work.
**Chain 138 dry-run helper (safe preflight):**
```bash
cd smom-dbis-138
bash scripts/deployment/dry-run-enhanced-swap-router-chain138.sh
```
This helper loads `smom-dbis-138/.env`, verifies the minimum required env (`PRIVATE_KEY`, `RPC_URL_138`), prints the exact token/provider vars the deploy script will use, and shows the sourced non-broadcast `forge script` command for a safe Chain 138 dry-run. It also distinguishes "env preflight passed" from "router would actually be usable after deploy". The updated deploy script now preloads the live 2026-03-26 DODO pair map on Chain 138:
- `cUSDT ↔ cUSDC`
- `cUSDT ↔ USDT`
- `cUSDC ↔ USDC`
- `cUSDT ↔ cXAUC`
- `cUSDC ↔ cXAUC`
- `cEURT ↔ cXAUC`
If provider env vars like `DODOEX_ROUTER`, `DODO_PMM_PROVIDER_ADDRESS`, `UNISWAP_V3_ROUTER`, `BALANCER_VAULT`, `CURVE_3POOL`, or `ONEINCH_ROUTER` are unset, the deploy script uses placeholders and disables those providers after deployment. This keeps the Chain 138 deployment honest: token-to-token DODO pairs are registered immediately, while `swapToStablecoin()` still requires real `WETH -> stable` routes before it is operational.
For current Chain 138, prefer `DODO_PMM_PROVIDER_ADDRESS` when the deployed `DODOPMMProvider` is available. The router now supports that provider as its DODO backend on Chain 138. If neither `DODO_PMM_PROVIDER_ADDRESS` nor `DODOEX_ROUTER` is set, the router can still deploy and register the live pair map, but the DODO provider will be disabled and no DODO execution path will remain enabled.
The dry-run helper also probes the live `DODOPMMProvider` over `RPC_URL_138` for `WETH -> stable` support. This is important because the current public/private PMM set is stable/stable and stable/XAU; `swapToStablecoin()` is still only operational when at least one live `WETH -> stable` route exists.
To run the sourced non-broadcast Forge simulation directly from the helper:
```bash
cd smom-dbis-138
bash scripts/deployment/dry-run-enhanced-swap-router-chain138.sh --run
```
You can increase visibility or the timeout if compilation/simulation is slow:
```bash
cd smom-dbis-138
bash scripts/deployment/dry-run-enhanced-swap-router-chain138.sh --run --timeout-seconds 180 --verbosity -vvv
```
**EnhancedSwapRouter** (set by address with `ROUTING_MANAGER_ROLE`):
| Config | Method | Env (optional) | When |
|--------|--------|----------------|------|
| Uniswap V3 Quoter | `setUniswapQuoter(address)` | `ENHANCED_SWAP_ROUTER_UNISWAP_QUOTER` | After Uniswap Quoter is deployed on chain |
| Balancer pool (WETH↔stable) | `setBalancerPoolId(tokenIn, tokenOut, poolId)` | `BALANCER_WETH_USDC_POOL_ID`, `BALANCER_WETH_USDT_POOL_ID` | After Balancer pool exists |
| Dodoex pool | `setDodoPoolAddress(tokenIn, tokenOut, pool)` | Set via cast or script when DODO pool is known | After DODO PMM pool deployed |
Example (Chain 138, after setting env):
```bash
cd smom-dbis-138 && source .env
# Uniswap Quoter
[ -n "$ENHANCED_SWAP_ROUTER_UNISWAP_QUOTER" ] && cast send "$ENHANCED_SWAP_ROUTER" "setUniswapQuoter(address)" "$ENHANCED_SWAP_ROUTER_UNISWAP_QUOTER" --rpc-url "$RPC_URL_138" --private-key "$PRIVATE_KEY" --gas-price 1000000000
# Balancer (bytes32 pool IDs; use cast send with 0x-prefixed hex)
# cast send "$ENHANCED_SWAP_ROUTER" "setBalancerPoolId(address,address,bytes32)" <WETH> <USDC> "$BALANCER_WETH_USDC_POOL_ID" ...
```
**DODOPMMProvider:** Register existing DODO PMM pools so `getQuote` / `executeSwap` work. Address with `POOL_MANAGER_ROLE` calls `registerPool(tokenIn, tokenOut, pool)`.
The corrected `RegisterDODOPools.s.sol` now reads `DODOPMMIntegration.getAllPools()` and `getPoolConfig(pool)` on-chain, then registers both directions for every discovered pool. That means it covers the current 2026-03-26 public live set and any future c* full-mesh pools already created in the integration. This is required because `DODOPMMProvider` stores routes as `pools[tokenIn][tokenOut]`. If the dry-run helper shows a documented live pair as missing, rerun this script before treating the provider as fully reconciled.
```bash
# After DODO pool is deployed (e.g. cUSDT↔USDT)
cast send "$DODO_PMM_PROVIDER_ADDRESS" "registerPool(address,address,address)" "<CUSDT>" "<USDT>" "<POOL_ADDRESS>" --rpc-url "$RPC_URL_138" --private-key "$PRIVATE_KEY" --gas-price 1000000000
```
Optional .env placeholders (see `smom-dbis-138/.env.example`): `ENHANCED_SWAP_ROUTER_UNISWAP_QUOTER`, `BALANCER_WETH_USDC_POOL_ID`, `BALANCER_WETH_USDT_POOL_ID`, `DODO_PMM_PROVIDER_ADDRESS`. Until set, router returns 0 for Uniswap/Balancer quotes and DODO provider returns no pool.
## Private stabilization pools (Master Plan Phase 2)
XAU-anchored private pools (cUSDT↔XAU, cUSDC↔XAU, cEURT↔XAU) for the Stabilizer; only the Stabilizer or whitelisted keeper should execute swaps. See [VAULT_SYSTEM_MASTER_TECHNICAL_PLAN](../VAULT_SYSTEM_MASTER_TECHNICAL_PLAN.md) §5.
**Script:** `smom-dbis-138/script/dex/DeployPrivatePoolRegistryAndPools.s.sol` — deploys [PrivatePoolRegistry](../../smom-dbis-138/contracts/dex/PrivatePoolRegistry.sol) and optionally creates pools via [DODOPMMIntegration](../../smom-dbis-138/contracts/dex/DODOPMMIntegration.sol) `createPool`, then registers them in the registry.
**Env (in `smom-dbis-138/.env`):**
| Variable | Description |
|----------|--------------|
| `PRIVATE_KEY` | Deployer (must have `POOL_MANAGER_ROLE` on DODOPMMIntegration to create pools) |
| `PRIVATE_POOL_REGISTRY_ADMIN` | Admin for PrivatePoolRegistry (default: deployer) |
| `DODOPMM_INTEGRATION_ADDRESS` | Deployed DODOPMMIntegration (set to create XAU pools in same run) |
| `XAU_ADDRESS_138` | XAU token address on Chain 138 (required to create XAU-anchored pools) |
| `COMPLIANT_USDT_ADDRESS` | cUSDT on Chain 138 |
| `COMPLIANT_USDC_ADDRESS` | cUSDC on Chain 138 |
| `cEURT_ADDRESS_138` | cEURT on Chain 138 (optional; omit to skip cEURT↔XAU pool) |
**Deploy:**
```bash
cd smom-dbis-138 && source .env
forge script script/dex/DeployPrivatePoolRegistryAndPools.s.sol:DeployPrivatePoolRegistryAndPools \
--rpc-url "$RPC_URL_138" --broadcast --with-gas-price 1000000000
```
If only the registry is needed (pools created later), leave `DODOPMM_INTEGRATION_ADDRESS` or `XAU_ADDRESS_138` unset. To register existing pools manually: `cast send "$PRIVATE_POOL_REGISTRY" "register(address,address,address)" <TOKEN_A> <TOKEN_B> <POOL_ADDRESS> ...`. Grant `STABILIZER_LP_ROLE` to allowed LPs when using a wrapper that checks it.
## Stabilizer deployment and configuration (Phase 3 + 6)
Deploy the [Stabilizer](../../smom-dbis-138/contracts/bridge/trustless/integration/Stabilizer.sol) (Master Plan Phase 3) after PrivatePoolRegistry and private XAU-anchored pools exist. The Stabilizer calls `checkDeviation()` (peg manager or TWAP) and `executePrivateSwap(tradeSize, tokenIn, tokenOut)` via the private pool registry. Phase 6: TWAP/sustained N-block deviation, per-block volume cap, flash drain recovery target &lt;3 blocks (see [OPERATIONS_RUNBOOK](../../smom-dbis-138/docs/OPERATIONS_RUNBOOK.md) Flash Loan Containment).
**Deploy (e.g. via forge create or a small script):**
- Constructor: `(admin, privatePoolRegistryAddress)`.
- Set in `.env`: `STABILIZER_ADDRESS`, `PRIVATE_POOL_REGISTRY_ADDRESS`, `STABLECOIN_PEG_MANAGER_ADDRESS` (or commodity peg), peg asset address.
**Configuration (admin):**
| Parameter | Method | Typical / notes |
|-----------|--------|------------------|
| Peg source | `setStablecoinPegSource(manager, asset)` or `setCommodityPegSource(manager, asset)` | One of them; deviation from peg used for `checkDeviation()` |
| thresholdBps | `setThresholdBps(uint256)` | e.g. 50 (0.5%) |
| minBlocksBetweenExecution | `setMinBlocksBetweenExecution(uint256)` | e.g. 35 (block delay) |
| maxStabilizationVolumePerBlock | `setMaxStabilizationVolumePerBlock(uint256)` | Cap per block |
| maxSlippageBps | `setMaxSlippageBps(uint256)` | e.g. 100 (1%) |
| maxGasPriceForStabilizer | `setMaxGasPriceForStabilizer(uint256)` | MEV resistance; 0 = disabled |
| sustainedDeviationBlocks | `setSustainedDeviationBlocks(uint256)` | N blocks over threshold before rebalance (Phase 6) |
**Keeper:** Grant `STABILIZER_KEEPER_ROLE` to the keeper EOA or bot:
`cast send "$STABILIZER_ADDRESS" "grantRole(bytes32,address)" $(cast keccak "STABILIZER_KEEPER_ROLE()") "$KEEPER_ADDRESS" --rpc-url "$RPC_URL_138" --private-key "$PRIVATE_KEY" --gas-price 1000000000`
**Operational target (Phase 6):** Flash drain recovery &lt;3 blocks. The contract enforces sustained deviation over N blocks, per-block volume cap, and block delay; document in [OPERATIONS_RUNBOOK](../../smom-dbis-138/docs/OPERATIONS_RUNBOOK.md) and [VAULT_SYSTEM_MASTER_TECHNICAL_PLAN](../VAULT_SYSTEM_MASTER_TECHNICAL_PLAN.md) §8/§16.
## Contract Verification (Blockscout)
Use the **Forge Verification Proxy** for `forge verify-contract` (Blockscout expects `module`/`action` in query; Forge sends JSON only). The verification script uses **canonical addresses** from `smom-dbis-138/.env` and `config/ip-addresses.conf` (ORACLE_PROXY, AGGREGATOR_ADDRESS, CCIP_SENDER, CCIPWETH9_BRIDGE_CHAIN138, etc.); run from a host on LAN that can reach Blockscout (192.168.11.140:4000).
**Preferred: orchestrated script (starts proxy if needed, timeout 900s default):**
```bash
source smom-dbis-138/.env 2>/dev/null
./scripts/verify/run-contract-verification-with-proxy.sh
```
**Manual (proxy + verify):**
```bash
# 1. Start proxy (in separate terminal)
BLOCKSCOUT_URL=http://192.168.11.140:4000 node forge-verification-proxy/server.js
# 2. Run verification
./scripts/verify-contracts-blockscout.sh
```
**See:** [forge-verification-proxy/README.md](../../forge-verification-proxy/README.md), [BLOCKSCOUT_FORGE_VERIFICATION_EVALUATION.md](BLOCKSCOUT_FORGE_VERIFICATION_EVALUATION.md). Fallback: manual verification at https://explorer.d-bis.org/address/<ADDR>#verify-contract
**Runbooks in sync (R12):** [BLOCKSCOUT_FIX_RUNBOOK](BLOCKSCOUT_FIX_RUNBOOK.md), [BLOCKSCOUT_FORGE_VERIFICATION_EVALUATION](BLOCKSCOUT_FORGE_VERIFICATION_EVALUATION.md), this runbook. **Full recommendations (R1R24):** [RECOMMENDATIONS_OPERATOR_CHECKLIST](../00-meta/RECOMMENDATIONS_OPERATOR_CHECKLIST.md).
---
## Troubleshooting
| Error | Cause | Fix |
|-------|-------|-----|
| `No route to host` | Dev machine cannot reach RPC (RPC_URL_138, e.g. 192.168.11.211:8545) | Run from machine on LAN or VPN; or set RPC_URL_138=https://rpc-core.d-bis.org |
| `PRIVATE_KEY not set` | Missing in .env | Add deployer key to smom-dbis-138/.env |
| `Gas price below configured minimum gas price` | Chain 138 minimum gas not met | Use `--with-gas-price 1000000000` for all `forge script` / `forge create` on Chain 138 |
| RPC -32xxx / out of gas when deploying | Gas estimate too low or estimation failed | Use `--gas-estimate-multiplier 150` (or 200) with `forge script ... --broadcast`; ensure deployer has enough ETH. See [RPC_ERRORS_32001_32602.md](../09-troubleshooting/RPC_ERRORS_32001_32602.md). |
| `Failed to decode constructor arguments` (TransactionMirror) | Forge broadcast decode bug | Deploy via `forge create ... --constructor-args <ADMIN> --gas-price 1000000000` |
| `pam_chauthtok failed` (Blockscout) | Container PAM restriction | Use Proxmox Web UI: Container 5000 → Options → Password |
| `pvesm not found` (verify-storage) | Script must run ON Proxmox host | `ssh root@r630-01` then run script |