Some checks failed
CI/CD Pipeline / Lint and Format (push) Failing after 46s
CI/CD Pipeline / Terraform Validation (push) Failing after 35s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 37s
Deploy ChainID 138 / Deploy ChainID 138 (push) Failing after 1m50s
HYBX OMNL TypeScript & anchor / token-aggregation build + reconcile artifact (push) Failing after 2m19s
Validation / validate-genesis (push) Successful in 51s
Validation / validate-terraform (push) Failing after 39s
Validation / validate-kubernetes (push) Failing after 10s
CI/CD Pipeline / Solidity Contracts (push) Failing after 12m56s
Validation / validate-smart-contracts (push) Failing after 12s
CI/CD Pipeline / Security Scanning (push) Failing after 15m52s
Validation / validate-security (push) Failing after 10m59s
Validation / validate-documentation (push) Failing after 17s
Validate Token List / validate (push) Failing after 30s
OMNL reconcile anchor / Run omnl:reconcile and upload artifacts (push) Failing after 26s
Verify Deployment / Verify Deployment (push) Failing after 56s
298 lines
12 KiB
Bash
298 lines
12 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
SMOM_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
cd "$SMOM_ROOT"
|
|
|
|
if [[ -f "$SMOM_ROOT/scripts/lib/deployment/dotenv.sh" ]]; then
|
|
# shellcheck disable=SC1091
|
|
source "$SMOM_ROOT/scripts/lib/deployment/dotenv.sh"
|
|
load_deployment_env --repo-root "$SMOM_ROOT"
|
|
else
|
|
source "$SMOM_ROOT/scripts/load-env.sh" >/dev/null 2>&1 || true
|
|
fi
|
|
|
|
PLAN_JSON="${PLAN_JSON:-$SMOM_ROOT/config/chain138-eth-pmm-liquidity-plan.json}"
|
|
PROFILE="${PROFILE:-}"
|
|
EXECUTE="${EXECUTE:-0}"
|
|
RPC_URL_138="${RPC_URL_138:-${RPC_URL:-http://192.168.11.211:8545}}"
|
|
DODO_PMM_INTEGRATION_ADDRESS="${DODO_PMM_INTEGRATION_ADDRESS:-${DODO_PMM_INTEGRATION:-}}"
|
|
PRIVATE_KEY="${PRIVATE_KEY:-}"
|
|
|
|
command -v jq >/dev/null 2>&1 || { echo "jq is required" >&2; exit 1; }
|
|
command -v cast >/dev/null 2>&1 || { echo "cast is required" >&2; exit 1; }
|
|
|
|
[[ -f "$PLAN_JSON" ]] || { echo "PLAN_JSON not found: $PLAN_JSON" >&2; exit 1; }
|
|
[[ -n "$DODO_PMM_INTEGRATION_ADDRESS" ]] || { echo "DODO_PMM_INTEGRATION_ADDRESS not set" >&2; exit 1; }
|
|
require_private_key_env || exit 1
|
|
|
|
EXECUTION_CONFIG="$(jq -r '.executionConfig' "$PLAN_JSON")"
|
|
if [[ "$EXECUTION_CONFIG" != /* ]]; then
|
|
EXECUTION_CONFIG="$SMOM_ROOT/${EXECUTION_CONFIG#smom-dbis-138/}"
|
|
fi
|
|
[[ -f "$EXECUTION_CONFIG" ]] || { echo "Execution config not found: $EXECUTION_CONFIG" >&2; exit 1; }
|
|
|
|
if [[ -z "$PROFILE" ]]; then
|
|
PROFILE="$(jq -r '.defaultProfile' "$PLAN_JSON")"
|
|
fi
|
|
|
|
FIAT_BASE_UNITS="$(jq -r --arg p "$PROFILE" '.profiles[$p].fiatBaseUnits' "$PLAN_JSON")"
|
|
XAU_BASE_UNITS="$(jq -r --arg p "$PROFILE" '.profiles[$p].xauBaseUnits' "$PLAN_JSON")"
|
|
[[ "$FIAT_BASE_UNITS" != "null" ]] || { echo "Unknown PROFILE: $PROFILE" >&2; exit 1; }
|
|
[[ "$XAU_BASE_UNITS" != "null" ]] || { echo "Unknown PROFILE: $PROFILE" >&2; exit 1; }
|
|
|
|
APPROVE_GAS_LIMIT="$(jq -r '.execution.approveGasLimit' "$PLAN_JSON")"
|
|
MINT_GAS_LIMIT="$(jq -r '.execution.mintGasLimit' "$PLAN_JSON")"
|
|
WRAP_GAS_LIMIT="$(jq -r '.execution.wrapGasLimit' "$PLAN_JSON")"
|
|
ADD_LIQUIDITY_GAS_LIMIT="$(jq -r '.execution.addLiquidityGasLimit' "$PLAN_JSON")"
|
|
GAS_PRICE_WEI="${CHAIN_GAS_PRICE:-$(jq -r '.execution.legacyGasPriceWei' "$PLAN_JSON")}"
|
|
WRAP_WHEN_NEEDED="$(jq -r '.execution.wrapNativeEthWhenNeeded' "$PLAN_JSON")"
|
|
MINT_WHEN_OWNER="$(jq -r '.execution.mintMissingBaseWhenOwner' "$PLAN_JSON")"
|
|
APPROVE_MAX="$(jq -r '.execution.approveMax' "$PLAN_JSON")"
|
|
|
|
DEPLOYER="$(derive_deployer_address || true)"
|
|
[[ -n "$DEPLOYER" ]] || { echo "ERROR: Could not derive DEPLOYER_ADDRESS from PRIVATE_KEY." >&2; exit 1; }
|
|
WETH_ADDRESS="$(jq -r '.tokens.WETH' "$EXECUTION_CONFIG")"
|
|
MAX_UINT="0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
|
ZERO_ADDR="0x0000000000000000000000000000000000000000"
|
|
|
|
log() {
|
|
printf '%s\n' "$*"
|
|
}
|
|
|
|
get_pool_reserves() {
|
|
local pool="$1"
|
|
local lines
|
|
mapfile -t lines < <(cast call "$pool" 'getVaultReserve()(uint256,uint256)' --rpc-url "$RPC_URL_138")
|
|
printf '%s\t%s\n' "$(uint_clean "${lines[0]:-0}")" "$(uint_clean "${lines[1]:-0}")"
|
|
}
|
|
|
|
uint_clean() {
|
|
printf '%s\n' "$1" | awk '{print $1}'
|
|
}
|
|
|
|
hex_to_addr() {
|
|
local raw="${1#0x}"
|
|
if [[ ${#raw} -lt 40 ]]; then
|
|
printf '%s\n' "$ZERO_ADDR"
|
|
return 0
|
|
fi
|
|
printf '0x%s\n' "${raw: -40}"
|
|
}
|
|
|
|
send_tx() {
|
|
local to="$1"
|
|
shift
|
|
cast send "$to" "$@" \
|
|
--rpc-url "$RPC_URL_138" \
|
|
--private-key "$PRIVATE_KEY" \
|
|
--legacy \
|
|
--gas-price "$GAS_PRICE_WEI" \
|
|
-q
|
|
}
|
|
|
|
big_add() {
|
|
python3 - "$1" "$2" <<'PY'
|
|
import sys
|
|
print(int(sys.argv[1]) + int(sys.argv[2]))
|
|
PY
|
|
}
|
|
|
|
big_sub_if_positive() {
|
|
python3 - "$1" "$2" <<'PY'
|
|
import sys
|
|
a = int(sys.argv[1])
|
|
b = int(sys.argv[2])
|
|
print(a - b if a > b else 0)
|
|
PY
|
|
}
|
|
|
|
big_lt() {
|
|
python3 - "$1" "$2" <<'PY'
|
|
import sys
|
|
print("1" if int(sys.argv[1]) < int(sys.argv[2]) else "0")
|
|
PY
|
|
}
|
|
|
|
raw_to_human_6() {
|
|
awk -v v="$1" 'BEGIN { printf "%.6f", v / 1000000 }'
|
|
}
|
|
|
|
raw_to_human_18() {
|
|
awk -v v="$1" 'BEGIN { printf "%.18f", v / 1000000000000000000 }'
|
|
}
|
|
|
|
mul_div_round() {
|
|
awk -v a="$1" -v b="$2" -v c="$3" 'BEGIN { printf "%.0f", (a * b) / c }'
|
|
}
|
|
|
|
derive_usd_per_eth() {
|
|
local pool base_reserve quote_reserve
|
|
local cusdt quote
|
|
cusdt="$(jq -r '.tokens.cUSDT' "$EXECUTION_CONFIG")"
|
|
quote="$WETH_ADDRESS"
|
|
pool="$(hex_to_addr "$(cast call "$DODO_PMM_INTEGRATION_ADDRESS" 'pools(address,address)(address)' "$cusdt" "$quote" --rpc-url "$RPC_URL_138")")"
|
|
[[ "$pool" != "$ZERO_ADDR" ]] || { echo "0"; return 1; }
|
|
read -r base_reserve quote_reserve < <(get_pool_reserves "$pool")
|
|
awk -v base="$base_reserve" -v quote="$quote_reserve" 'BEGIN { printf "%.12f", (base / 1000000) / (quote / 1000000000000000000) }'
|
|
}
|
|
|
|
derive_xau_usd_per_unit() {
|
|
local pool base_symbol quote_symbol base_addr quote_addr base_reserve quote_reserve fallback
|
|
base_symbol="$(jq -r '.xauPricing.poolBaseSymbol' "$PLAN_JSON")"
|
|
quote_symbol="$(jq -r '.xauPricing.poolQuoteSymbol' "$PLAN_JSON")"
|
|
fallback="$(jq -r '.xauPricing.fallbackUsdPerUnit' "$PLAN_JSON")"
|
|
base_addr="$(jq -r --arg sym "$base_symbol" '.tokens[$sym]' "$EXECUTION_CONFIG")"
|
|
quote_addr="$(jq -r --arg sym "$quote_symbol" '.tokens[$sym]' "$EXECUTION_CONFIG")"
|
|
pool="$(hex_to_addr "$(cast call "$DODO_PMM_INTEGRATION_ADDRESS" 'pools(address,address)(address)' "$base_addr" "$quote_addr" --rpc-url "$RPC_URL_138")")"
|
|
if [[ "$pool" == "$ZERO_ADDR" ]]; then
|
|
echo "$fallback"
|
|
return 0
|
|
fi
|
|
read -r base_reserve quote_reserve < <(get_pool_reserves "$pool")
|
|
if [[ "$base_reserve" == "0" || "$quote_reserve" == "0" ]]; then
|
|
echo "$fallback"
|
|
return 0
|
|
fi
|
|
awk -v base="$base_reserve" -v quote="$quote_reserve" 'BEGIN { printf "%.12f", (quote / 1000000) / (base / 1000000) }'
|
|
}
|
|
|
|
usd_per_eth="$(derive_usd_per_eth)"
|
|
[[ "$usd_per_eth" != "0" ]] || { echo "Could not derive USD per ETH from live cUSDT/WETH pool" >&2; exit 1; }
|
|
xau_usd_per_unit="$(derive_xau_usd_per_unit)"
|
|
|
|
log "=== Chain 138 ETH PMM Liquidity Funding ==="
|
|
log "Mode: $( [[ "$EXECUTE" == "1" ]] && echo EXECUTE || echo DRY_RUN )"
|
|
log "Profile: $PROFILE"
|
|
log "Plan: $PLAN_JSON"
|
|
log "Config: $EXECUTION_CONFIG"
|
|
log "Integration: $DODO_PMM_INTEGRATION_ADDRESS"
|
|
log "RPC: $RPC_URL_138"
|
|
log "Deployer: $DEPLOYER"
|
|
log "USD per ETH: $usd_per_eth"
|
|
log "XAU USD: $xau_usd_per_unit"
|
|
log ""
|
|
|
|
total_quote_units=0
|
|
wrap_needed=0
|
|
|
|
declare -a ROWS=()
|
|
while IFS=$'\t' read -r base_symbol base_address; do
|
|
pool="$(hex_to_addr "$(cast call "$DODO_PMM_INTEGRATION_ADDRESS" 'pools(address,address)(address)' "$base_address" "$WETH_ADDRESS" --rpc-url "$RPC_URL_138")")"
|
|
[[ "$pool" != "$ZERO_ADDR" ]] || continue
|
|
|
|
case "$base_symbol" in
|
|
cXAUC|cXAUT)
|
|
base_units="$XAU_BASE_UNITS"
|
|
usd_per_unit="$xau_usd_per_unit"
|
|
;;
|
|
*)
|
|
base_units="$FIAT_BASE_UNITS"
|
|
usd_per_unit="$(jq -r --arg sym "$base_symbol" '.usdPerUnit[$sym]' "$PLAN_JSON")"
|
|
[[ "$usd_per_unit" != "null" ]] || { echo "Missing usdPerUnit for $base_symbol" >&2; exit 1; }
|
|
;;
|
|
esac
|
|
|
|
quote_units="$(awk -v base="$base_units" -v usd="$usd_per_unit" -v usd_eth="$usd_per_eth" 'BEGIN { printf "%.0f", ((base / 1000000) * usd / usd_eth) * 1000000000000000000 }')"
|
|
read -r current_base_reserve current_quote_reserve < <(get_pool_reserves "$pool")
|
|
base_delta="$(big_sub_if_positive "$base_units" "$current_base_reserve")"
|
|
quote_delta="$(big_sub_if_positive "$quote_units" "$current_quote_reserve")"
|
|
base_balance="$(cast call "$base_address" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL_138" 2>/dev/null || echo 0)"
|
|
base_balance="$(uint_clean "$base_balance")"
|
|
owner_addr="$(cast call "$base_address" 'owner()(address)' --rpc-url "$RPC_URL_138" 2>/dev/null || echo "$ZERO_ADDR")"
|
|
ROWS+=("$base_symbol|$base_address|$pool|$base_units|$quote_units|$current_base_reserve|$current_quote_reserve|$base_delta|$quote_delta|$base_balance|$owner_addr")
|
|
total_quote_units="$(big_add "$total_quote_units" "$quote_delta")"
|
|
done < <(jq -r '.explicitPairs[] | [.baseSymbol, (.baseSymbol as $s | .baseSymbol)] | @tsv' "$EXECUTION_CONFIG" | while IFS=$'\t' read -r base_symbol _; do
|
|
base_address="$(jq -r --arg sym "$base_symbol" '.tokens[$sym]' "$EXECUTION_CONFIG")"
|
|
printf '%s\t%s\n' "$base_symbol" "$base_address"
|
|
done)
|
|
|
|
weth_balance="$(cast call "$WETH_ADDRESS" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL_138" 2>/dev/null || echo 0)"
|
|
weth_balance="$(uint_clean "$weth_balance")"
|
|
eth_balance="$(cast balance "$DEPLOYER" --rpc-url "$RPC_URL_138" 2>/dev/null || echo 0)"
|
|
if [[ "$(big_lt "$weth_balance" "$total_quote_units")" == "1" ]]; then
|
|
wrap_needed="$(big_sub_if_positive "$total_quote_units" "$weth_balance")"
|
|
fi
|
|
|
|
log "Current ETH: $(raw_to_human_18 "$eth_balance")"
|
|
log "Current WETH: $(raw_to_human_18 "$weth_balance")"
|
|
log "Total WETH needed for profile: $(raw_to_human_18 "$total_quote_units")"
|
|
log "Additional WETH to wrap: $(raw_to_human_18 "$wrap_needed")"
|
|
log ""
|
|
|
|
for row in "${ROWS[@]}"; do
|
|
IFS='|' read -r base_symbol base_address pool base_units quote_units current_base_reserve current_quote_reserve base_delta quote_delta base_balance owner_addr <<< "$row"
|
|
need_mint=0
|
|
if [[ "$(big_lt "$base_balance" "$base_delta")" == "1" ]]; then
|
|
need_mint="$(big_sub_if_positive "$base_delta" "$base_balance")"
|
|
fi
|
|
log "$base_symbol pool=$pool"
|
|
log " target base: $(raw_to_human_6 "$base_units")"
|
|
log " target quote: $(raw_to_human_18 "$quote_units") WETH"
|
|
log " current base: $(raw_to_human_6 "$current_base_reserve")"
|
|
log " current quote: $(raw_to_human_18 "$current_quote_reserve") WETH"
|
|
log " top-up base: $(raw_to_human_6 "$base_delta")"
|
|
log " top-up quote: $(raw_to_human_18 "$quote_delta") WETH"
|
|
log " base balance: $(raw_to_human_6 "$base_balance")"
|
|
if [[ "$need_mint" != "0" ]]; then
|
|
log " mint needed: $(raw_to_human_6 "$need_mint")"
|
|
else
|
|
log " mint needed: no"
|
|
fi
|
|
done
|
|
|
|
if [[ "$EXECUTE" != "1" ]]; then
|
|
log ""
|
|
log "Dry run only. Re-run with EXECUTE=1 to mint/wrap/approve/add liquidity."
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "$wrap_needed" != "0" ]]; then
|
|
if [[ "$WRAP_WHEN_NEEDED" != "true" ]]; then
|
|
echo "Need additional WETH but wrapNativeEthWhenNeeded=false" >&2
|
|
exit 1
|
|
fi
|
|
if [[ "$(big_lt "$eth_balance" "$wrap_needed")" == "1" ]]; then
|
|
echo "Insufficient ETH to wrap required WETH" >&2
|
|
exit 1
|
|
fi
|
|
log "Wrapping $(raw_to_human_18 "$wrap_needed") ETH into WETH"
|
|
send_tx "$WETH_ADDRESS" 'deposit()' --value "$wrap_needed" --gas-limit "$WRAP_GAS_LIMIT" >/dev/null
|
|
fi
|
|
|
|
for row in "${ROWS[@]}"; do
|
|
IFS='|' read -r base_symbol base_address pool base_units quote_units current_base_reserve current_quote_reserve base_delta quote_delta base_balance owner_addr <<< "$row"
|
|
if [[ "$base_delta" == "0" && "$quote_delta" == "0" ]]; then
|
|
log "Skipping $base_symbol/WETH; already at or above target."
|
|
continue
|
|
fi
|
|
|
|
if [[ "$(big_lt "$base_balance" "$base_delta")" == "1" ]]; then
|
|
mint_amount="$(big_sub_if_positive "$base_delta" "$base_balance")"
|
|
if [[ "$MINT_WHEN_OWNER" != "true" ]]; then
|
|
echo "Need to mint $base_symbol but mintMissingBaseWhenOwner=false" >&2
|
|
exit 1
|
|
fi
|
|
if [[ "${owner_addr,,}" != "${DEPLOYER,,}" ]]; then
|
|
echo "Cannot mint $base_symbol; deployer is not owner ($owner_addr)" >&2
|
|
exit 1
|
|
fi
|
|
log "Minting $(raw_to_human_6 "$mint_amount") $base_symbol"
|
|
send_tx "$base_address" 'mint(address,uint256)' "$DEPLOYER" "$mint_amount" --gas-limit "$MINT_GAS_LIMIT" >/dev/null
|
|
fi
|
|
|
|
if [[ "$APPROVE_MAX" == "true" ]]; then
|
|
log "Approving $base_symbol to integration"
|
|
send_tx "$base_address" 'approve(address,uint256)' "$DODO_PMM_INTEGRATION_ADDRESS" "$MAX_UINT" --gas-limit "$APPROVE_GAS_LIMIT" >/dev/null
|
|
log "Approving WETH for $base_symbol pair"
|
|
send_tx "$WETH_ADDRESS" 'approve(address,uint256)' "$DODO_PMM_INTEGRATION_ADDRESS" "$MAX_UINT" --gas-limit "$APPROVE_GAS_LIMIT" >/dev/null
|
|
fi
|
|
|
|
log "Adding liquidity to $base_symbol/WETH at $pool"
|
|
send_tx "$DODO_PMM_INTEGRATION_ADDRESS" 'addLiquidity(address,uint256,uint256)' "$pool" "$base_delta" "$quote_delta" --gas-limit "$ADD_LIQUIDITY_GAS_LIMIT" >/dev/null
|
|
done
|
|
|
|
log ""
|
|
log "Liquidity funding complete."
|