Files
proxmox/docs/03-deployment/CHAIN138_PMM_REDEPLOY_AND_POOL_FUNDING_RUNBOOK.md
defiQUG 790e489538
Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled
docs: FQDN matrix, public-sector baseline, Chain138 runbooks, eIDAS repo reference
Made-with: Cursor
2026-03-27 18:46:56 -07:00

15 KiB

Chain 138 PMM Redeploy and Pool Funding Runbook

Purpose: Execute the live on-chain PMM remediation and funding sequence on Chain 138 in the correct order:

  1. deploy live Chain 138 quote-side USDT and USDC ERC-20 mirror tokens
  2. redeploy DODOPMMIntegration with those live Chain 138 official stable addresses
  3. recreate the usable public stable pools on the new integration
  4. create public XAU pools using cXAUC or cXAUT as the Chain 138 XAU anchor
  5. deploy the PrivatePoolRegistry and register the XAU private stabilization pools
  6. fund the pools in the correct order

Primary chain: Chain 138
Operator requirement: deployer EOA with PRIVATE_KEY, gas, and the required token balances / mint authority.


0. Preconditions

0.1 Required environment

From smom-dbis-138/.env:

PRIVATE_KEY=0x...
RPC_URL_138=http://...
DODO_VENDING_MACHINE_ADDRESS=0x...
COMPLIANT_USDT_ADDRESS=0x93E66202A11B1772E55407B32B44e5Cd8eda7f22
COMPLIANT_USDC_ADDRESS=0xf22258f57794CC8E06237084b353Ab30fFfa640b
OFFICIAL_USDT_ADDRESS=0x...
OFFICIAL_USDC_ADDRESS=0x...

0.2 XAU anchor selection

Choose one Chain 138 XAU anchor for the PMM and private stabilization pools:

# Preferred default
XAU_ADDRESS_138=0x290E52a8819A4fbD0714E517225429aA2B70EC6b   # cXAUC

# Optional alternate
CXAUT_ADDRESS_138=0x94e408E26c6FD8F4ee00b54dF19082FDA07dC96E  # cXAUT

If XAU_ADDRESS_138 is unset, the scripts default to cXAUC on Chain 138.

0.3 Stop conditions

Stop immediately if any of these checks fail:

cd /home/intlc/projects/proxmox/smom-dbis-138
source .env

cast wallet address "$PRIVATE_KEY"
cast code "$DODO_VENDING_MACHINE_ADDRESS" --rpc-url "$RPC_URL_138"
cast code "$COMPLIANT_USDT_ADDRESS" --rpc-url "$RPC_URL_138"
cast code "$COMPLIANT_USDC_ADDRESS" --rpc-url "$RPC_URL_138"
cast code "$OFFICIAL_USDT_ADDRESS" --rpc-url "$RPC_URL_138"
cast code "$OFFICIAL_USDC_ADDRESS" --rpc-url "$RPC_URL_138"
cast code "${XAU_ADDRESS_138:-0x290E52a8819A4fbD0714E517225429aA2B70EC6b}" --rpc-url "$RPC_URL_138"

Expected result: each cast code returns non-empty bytecode.

0.4 Important blocker note

Do not use the historical placeholder addresses 0x15DF... or 0xA0b8... on Chain 138 unless cast code proves they are live ERC-20 contracts on Chain 138.

The local PMM integration requires live quote-side ERC-20s on Chain 138. If OFFICIAL_USDT_ADDRESS and OFFICIAL_USDC_ADDRESS have no bytecode, deploy the local mirror tokens first.


1. Snapshot the current state

Record the current integration and pool state before redeploying:

cd /home/intlc/projects/proxmox/smom-dbis-138
source .env

echo "Current integration: ${DODO_PMM_INTEGRATION_ADDRESS:-${DODO_PMM_INTEGRATION:-unset}}"
echo "Current cUSDT/cUSDC pool: ${POOL_CUSDTCUSDC:-unset}"
echo "Current cUSDT/USDT pool: ${POOL_CUSDTUSDT:-unset}"
echo "Current cUSDC/USDC pool: ${POOL_CUSDCUSDC:-unset}"

If the current integration exists, record its immutable token addresses:

INT="${DODO_PMM_INTEGRATION_ADDRESS:-${DODO_PMM_INTEGRATION:-}}"
[ -n "$INT" ] && cast call "$INT" "officialUSDT()(address)" --rpc-url "$RPC_URL_138"
[ -n "$INT" ] && cast call "$INT" "officialUSDC()(address)" --rpc-url "$RPC_URL_138"

1. Deploy the Chain 138 official stable mirrors

Deploy the local quote-side assets first. These are lightweight ERC-20 mirrors used only to unblock local PMM pools on Chain 138.

cd /home/intlc/projects/proxmox/smom-dbis-138
source .env

forge script script/DeployOfficialUSDT138.s.sol:DeployOfficialUSDT138 \
  --rpc-url "$RPC_URL_138" \
  --broadcast \
  --private-key "$PRIVATE_KEY" \
  --with-gas-price "${GAS_PRICE_138:-1000000000}" \
  --legacy \
  -vv

forge script script/DeployOfficialUSDC138.s.sol:DeployOfficialUSDC138 \
  --rpc-url "$RPC_URL_138" \
  --broadcast \
  --private-key "$PRIVATE_KEY" \
  --with-gas-price "${GAS_PRICE_138:-1000000000}" \
  --legacy \
  -vv

Persist the deployed addresses into .env:

OFFICIAL_USDT_ADDRESS=0x...
OFFICIAL_USDC_ADDRESS=0x...

Verify both:

cast code "$OFFICIAL_USDT_ADDRESS" --rpc-url "$RPC_URL_138"
cast code "$OFFICIAL_USDC_ADDRESS" --rpc-url "$RPC_URL_138"
cast call "$OFFICIAL_USDT_ADDRESS" "symbol()(string)" --rpc-url "$RPC_URL_138"
cast call "$OFFICIAL_USDC_ADDRESS" "symbol()(string)" --rpc-url "$RPC_URL_138"

Expected result:

  • both return non-empty bytecode
  • symbols return USDT and USDC

2. Redeploy PMM integration on Chain 138

This step creates a fresh DODOPMMIntegration using the corrected Chain 138 official stable addresses.

cd /home/intlc/projects/proxmox/smom-dbis-138
source .env

forge script script/dex/DeployDODOPMMIntegration.s.sol:DeployDODOPMMIntegration \
  --rpc-url "$RPC_URL_138" \
  --broadcast \
  --private-key "$PRIVATE_KEY" \
  --with-gas-price "${GAS_PRICE_138:-1000000000}" \
  --legacy \
  -vv

After deployment, update .env with the new integration address:

DODO_PMM_INTEGRATION_ADDRESS=0x...
DODO_PMM_INTEGRATION=0x...

Verify the new immutables:

INT="${DODO_PMM_INTEGRATION_ADDRESS:-${DODO_PMM_INTEGRATION:-}}"
cast call "$INT" "officialUSDT()(address)" --rpc-url "$RPC_URL_138"
cast call "$INT" "officialUSDC()(address)" --rpc-url "$RPC_URL_138"
cast call "$INT" "compliantUSDT()(address)" --rpc-url "$RPC_URL_138"
cast call "$INT" "compliantUSDC()(address)" --rpc-url "$RPC_URL_138"

Expected result:

  • officialUSDT = the live OFFICIAL_USDT_ADDRESS you just deployed or verified
  • officialUSDC = the live OFFICIAL_USDC_ADDRESS you just deployed or verified

3. Create the corrected public stable pools

Create the three public PMM pools on the new integration:

cd /home/intlc/projects/proxmox/smom-dbis-138
source .env

forge script script/dex/CreateCUSDTCUSDCPool.s.sol:CreateCUSDTCUSDCPool \
  --rpc-url "$RPC_URL_138" --broadcast --private-key "$PRIVATE_KEY" --with-gas-price "${GAS_PRICE_138:-1000000000}" -vv

forge script script/dex/CreateCUSDTUSDTPool.s.sol:CreateCUSDTUSDTPool \
  --rpc-url "$RPC_URL_138" --broadcast --private-key "$PRIVATE_KEY" --with-gas-price "${GAS_PRICE_138:-1000000000}" -vv

forge script script/dex/CreateCUSDCUSDCPool.s.sol:CreateCUSDCUSDCPool \
  --rpc-url "$RPC_URL_138" --broadcast --private-key "$PRIVATE_KEY" --with-gas-price "${GAS_PRICE_138:-1000000000}" -vv

Record the new pool addresses:

INT="${DODO_PMM_INTEGRATION_ADDRESS:-${DODO_PMM_INTEGRATION:-}}"

POOL_CUSDTCUSDC=$(cast call "$INT" "pools(address,address)(address)" \
  "$COMPLIANT_USDT_ADDRESS" "$COMPLIANT_USDC_ADDRESS" --rpc-url "$RPC_URL_138" | cast --to-addr)

POOL_CUSDTUSDT=$(cast call "$INT" "pools(address,address)(address)" \
  "$COMPLIANT_USDT_ADDRESS" "$OFFICIAL_USDT_ADDRESS" --rpc-url "$RPC_URL_138" | cast --to-addr)

POOL_CUSDCUSDC=$(cast call "$INT" "pools(address,address)(address)" \
  "$COMPLIANT_USDC_ADDRESS" "$OFFICIAL_USDC_ADDRESS" --rpc-url "$RPC_URL_138" | cast --to-addr)

echo "$POOL_CUSDTCUSDC"
echo "$POOL_CUSDTUSDT"
echo "$POOL_CUSDCUSDC"

Persist them into .env.


4. Create the public XAU pools

Use the new public XAU script so the XAU side is explicit as cXAUC or cXAUT.

cd /home/intlc/projects/proxmox/smom-dbis-138
source .env

forge script script/dex/CreatePublicXAUPoolsChain138.s.sol:CreatePublicXAUPoolsChain138 \
  --rpc-url "$RPC_URL_138" \
  --broadcast \
  --private-key "$PRIVATE_KEY" \
  --with-gas-price "${GAS_PRICE_138:-1000000000}" \
  --legacy \
  -vv

Optional controls:

CREATE_CUSDT_XAU=true
CREATE_CUSDC_XAU=true
CREATE_CEURT_XAU=true

Verify the created public XAU pools:

INT="${DODO_PMM_INTEGRATION_ADDRESS:-${DODO_PMM_INTEGRATION:-}}"
XAU="${XAU_ADDRESS_138:-0x290E52a8819A4fbD0714E517225429aA2B70EC6b}"

cast call "$INT" "pools(address,address)(address)" "$COMPLIANT_USDT_ADDRESS" "$XAU" --rpc-url "$RPC_URL_138"
cast call "$INT" "pools(address,address)(address)" "$COMPLIANT_USDC_ADDRESS" "$XAU" --rpc-url "$RPC_URL_138"
cast call "$INT" "pools(address,address)(address)" "0xdf4b71c61E5912712C1Bdd451416B9aC26949d72" "$XAU" --rpc-url "$RPC_URL_138"

Persist the returned pool addresses if they are non-zero.


5. Deploy PrivatePoolRegistry and register private XAU pools

cd /home/intlc/projects/proxmox/smom-dbis-138
source .env

forge script script/dex/DeployPrivatePoolRegistryAndPools.s.sol:DeployPrivatePoolRegistryAndPools \
  --rpc-url "$RPC_URL_138" \
  --broadcast \
  --private-key "$PRIVATE_KEY" \
  --with-gas-price "${GAS_PRICE_138:-1000000000}" \
  --legacy \
  -vv

Record:

PRIVATE_POOL_REGISTRY=0x...

Verify registrations:

REG="$PRIVATE_POOL_REGISTRY"
XAU="${XAU_ADDRESS_138:-0x290E52a8819A4fbD0714E517225429aA2B70EC6b}"

cast call "$REG" "getPool(address,address)(address)" "$COMPLIANT_USDT_ADDRESS" "$XAU" --rpc-url "$RPC_URL_138"
cast call "$REG" "getPool(address,address)(address)" "$COMPLIANT_USDC_ADDRESS" "$XAU" --rpc-url "$RPC_URL_138"
cast call "$REG" "getPool(address,address)(address)" "0xdf4b71c61E5912712C1Bdd451416B9aC26949d72" "$XAU" --rpc-url "$RPC_URL_138"

6. Fund the pools in the correct order

6.1 Funding order

Fund in this order:

  1. cUSDT / cUSDC
  2. cUSDT / USDT
  3. cUSDC / USDC
  4. public XAU pools:
    • cUSDT / XAU
    • cUSDC / XAU
    • cEURT / XAU
  5. private stabilization pools last

Reason:

  • cUSDT/cUSDC establishes the base compliant market first
  • official stable pools come next after the corrected addresses are live
  • XAU public pools should discover price before private stabilization paths are seeded

6.2 Mint compliant balances

Mint the compliant side first:

cd /home/intlc/projects/proxmox/smom-dbis-138
source .env

MINT_CUSDT_AMOUNT=2000000 \
MINT_CUSDC_AMOUNT=2000000 \
./scripts/mint-for-liquidity.sh

Mint additional compliant assets as needed:

DEPLOYER=$(cast wallet address "$PRIVATE_KEY")
cast send 0xdf4b71c61E5912712C1Bdd451416B9aC26949d72 \
  "mint(address,uint256)" "$DEPLOYER" 1000000000000 \
  --rpc-url "$RPC_URL_138" --private-key "$PRIVATE_KEY"

6.3 Acquire / verify non-mintable sides

Before adding liquidity, confirm balances of:

  • OFFICIAL_USDT_ADDRESS
  • OFFICIAL_USDC_ADDRESS
  • XAU_ADDRESS_138 (cXAUC or cXAUT)
DEPLOYER=$(cast wallet address "$PRIVATE_KEY")

cast call "$OFFICIAL_USDT_ADDRESS" "balanceOf(address)(uint256)" "$DEPLOYER" --rpc-url "$RPC_URL_138"
cast call "$OFFICIAL_USDC_ADDRESS" "balanceOf(address)(uint256)" "$DEPLOYER" --rpc-url "$RPC_URL_138"
cast call "${XAU_ADDRESS_138:-0x290E52a8819A4fbD0714E517225429aA2B70EC6b}" "balanceOf(address)(uint256)" "$DEPLOYER" --rpc-url "$RPC_URL_138"

Do not proceed on a pool until both sides have sufficient balance.

6.4 Fund cUSDT / cUSDC

Use the existing add-liquidity script first:

export ADD_LIQUIDITY_CUSDTCUSDC_BASE=1000000000000
export ADD_LIQUIDITY_CUSDTCUSDC_QUOTE=1000000000000

forge script script/dex/AddLiquidityPMMPoolsChain138.s.sol:AddLiquidityPMMPoolsChain138 \
  --rpc-url "$RPC_URL_138" \
  --broadcast \
  --private-key "$PRIVATE_KEY" \
  --with-gas-price "${GAS_PRICE_138:-1000000000}" \
  -vv

6.5 Fund cUSDT / USDT and cUSDC / USDC

Set per-pool liquidity amounts:

export ADD_LIQUIDITY_CUSDTUSDT_BASE=1000000000000
export ADD_LIQUIDITY_CUSDTUSDT_QUOTE=1000000000000
export ADD_LIQUIDITY_CUSDCUSDC_BASE=1000000000000
export ADD_LIQUIDITY_CUSDCUSDC_QUOTE=1000000000000

Then run the same liquidity script:

forge script script/dex/AddLiquidityPMMPoolsChain138.s.sol:AddLiquidityPMMPoolsChain138 \
  --rpc-url "$RPC_URL_138" \
  --broadcast \
  --private-key "$PRIVATE_KEY" \
  --with-gas-price "${GAS_PRICE_138:-1000000000}" \
  -vv

6.6 Fund public XAU pools

For each public XAU pool:

  1. approve both tokens to the integration
  2. call addLiquidity(pool, baseAmount, quoteAmount)

Example for cUSDT / XAU:

INT="${DODO_PMM_INTEGRATION_ADDRESS:-${DODO_PMM_INTEGRATION:-}}"
XAU="${XAU_ADDRESS_138:-0x290E52a8819A4fbD0714E517225429aA2B70EC6b}"
POOL=$(cast call "$INT" "pools(address,address)(address)" "$COMPLIANT_USDT_ADDRESS" "$XAU" --rpc-url "$RPC_URL_138" | cast --to-addr)

cast send "$COMPLIANT_USDT_ADDRESS" "approve(address,uint256)" "$INT" 1000000000000 --rpc-url "$RPC_URL_138" --private-key "$PRIVATE_KEY"
cast send "$XAU" "approve(address,uint256)" "$INT" 1000000000000 --rpc-url "$RPC_URL_138" --private-key "$PRIVATE_KEY"
cast send "$INT" "addLiquidity(address,uint256,uint256)" "$POOL" 1000000000000 1000000000000 --rpc-url "$RPC_URL_138" --private-key "$PRIVATE_KEY"

Repeat for:

  • cUSDC / XAU
  • cEURT / XAU

6.7 Seed private stabilization pools last

Only after the public pools have been created and seeded:

  1. verify private registry entries exist
  2. approve both sides
  3. fund the corresponding private pool addresses with smaller initial depth than the public pools

Use the same addLiquidity(address,uint256,uint256) pattern against the registered pool addresses.


7. Post-funding verification

7.1 Pool reserves

cast call "$POOL_CUSDTCUSDC" "getVaultReserve()(uint256,uint256)" --rpc-url "$RPC_URL_138"
cast call "$POOL_CUSDTUSDT" "getVaultReserve()(uint256,uint256)" --rpc-url "$RPC_URL_138"
cast call "$POOL_CUSDCUSDC" "getVaultReserve()(uint256,uint256)" --rpc-url "$RPC_URL_138"

Repeat for each XAU pool address.

7.2 Explorer alignment

After successful execution, update:

Also update the explorer pool inventory if new pool addresses were created.


8. Rollback / abort guidance

Abort if any of the following occurs:

  • official token bytecode missing on 138
  • integration deployed with wrong immutables
  • pool creation returns zero or reverts unexpectedly
  • deployer lacks balance for either side of a target pool

If the new integration is deployed but pool creation fails, stop there and do not fund the old incorrect pools.


9. References