Files
proxmox/docs/03-deployment/CONTRACT_DEPLOYMENT_RUNBOOK.md

299 lines
21 KiB
Markdown
Raw Normal View History

# 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.
**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)`.
```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 |