#!/usr/bin/env bash set -euo pipefail # Install jq if not available if ! command -v jq >/dev/null 2>&1; then apk add --no-cache jq >/dev/null 2>&1 || apt-get update && apt-get install -y jq >/dev/null 2>&1 || true fi ROOT_DIR=/workspace GIT_BRANCH="${GIT_BRANCH:-}" # Checkout branch if specified if [[ -n "$GIT_BRANCH" ]]; then cd "$ROOT_DIR" git config --global --add safe.directory "$ROOT_DIR" 2>/dev/null || true CURRENT=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown") if [[ "$CURRENT" != "$GIT_BRANCH" ]]; then echo "[bootstrap] Switching to branch: $GIT_BRANCH" # Try local branch first, then remote if git rev-parse --verify "$GIT_BRANCH" >/dev/null 2>&1; then git checkout "$GIT_BRANCH" 2>/dev/null || echo "[bootstrap] WARNING: Could not checkout $GIT_BRANCH" else git fetch origin "$GIT_BRANCH" 2>/dev/null || true git checkout "$GIT_BRANCH" 2>/dev/null || echo "[bootstrap] WARNING: Could not checkout $GIT_BRANCH" fi fi fi STATE_DIR=$ROOT_DIR/tmp/containers LOG_DIR=$STATE_DIR/logs SETUP_LOG=$LOG_DIR/setup.log CONTRACT_ENV=$STATE_DIR/contracts.env TXNBOT_ENV=$STATE_DIR/txnBot.env MNEMONIC_FILE=$ROOT_DIR/onchain/.secret.local mkdir -p "$LOG_DIR" : >"$SETUP_LOG" ANVIL_RPC=${ANVIL_RPC:-"http://anvil:8545"} FEE_DEST=0xf6a3eef9088A255c32b6aD2025f83E57291D9011 WETH=0x4200000000000000000000000000000000000006 SWAP_ROUTER=0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4 MAX_UINT=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff DEFAULT_DEPLOYER_PK=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 DEFAULT_DEPLOYER_ADDR=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 DEPLOYER_PK=${DEPLOYER_PK:-$DEFAULT_DEPLOYER_PK} DEPLOYER_ADDR=${DEPLOYER_ADDR:-$DEFAULT_DEPLOYER_ADDR} TXNBOT_FUND_VALUE=${TXNBOT_FUND_VALUE:-1ether} log() { echo "[bootstrap] $*" } BOOTSTRAP_START=$(date +%s%3N) wait_for_rpc() { for _ in {1..120}; do if cast chain-id --rpc-url "$ANVIL_RPC" >/dev/null 2>&1; then return 0 fi sleep 1 done log "Timed out waiting for Anvil at $ANVIL_RPC" return 1 } maybe_set_deployer_from_mnemonic() { if [[ -n "$DEPLOYER_PK" && -n "$DEPLOYER_ADDR" ]]; then return fi if [[ -f "$MNEMONIC_FILE" ]]; then local mnemonic pk addr mnemonic="$(tr -d '\n\r' <"$MNEMONIC_FILE")" if [[ -n "$mnemonic" ]]; then pk="$(cast wallet private-key --mnemonic "$mnemonic" --mnemonic-derivation-path "m/44'/60'/0'/0/0")" addr="$(cast wallet address --private-key "$pk")" DEPLOYER_PK=${DEPLOYER_PK:-$pk} DEPLOYER_ADDR=${DEPLOYER_ADDR:-$addr} fi fi DEPLOYER_PK=${DEPLOYER_PK:-$DEFAULT_DEPLOYER_PK} DEPLOYER_ADDR=${DEPLOYER_ADDR:-$DEFAULT_DEPLOYER_ADDR} } derive_txnbot_wallet() { if [[ -f "$MNEMONIC_FILE" ]]; then local mnemonic mnemonic="$(tr -d '\n\r' <"$MNEMONIC_FILE")" if [[ -n "$mnemonic" ]]; then TXNBOT_PRIVATE_KEY="$(cast wallet private-key --mnemonic "$mnemonic" --mnemonic-index 2)" TXNBOT_ADDRESS="$(cast wallet address --private-key "$TXNBOT_PRIVATE_KEY")" log "Derived txnBot wallet: $TXNBOT_ADDRESS (account index 2)" return fi fi # Fallback to hardcoded Anvil account 1 TXNBOT_PRIVATE_KEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d TXNBOT_ADDRESS=0x70997970C51812dc3A010C7d01b50e0d17dc79C8 log "Using default txnBot wallet: $TXNBOT_ADDRESS" } run_forge_script() { log "Deploying contracts to fork" pushd "$ROOT_DIR/onchain" >/dev/null forge script script/DeployLocal.sol --fork-url "$ANVIL_RPC" --broadcast >>"$SETUP_LOG" 2>&1 popd >/dev/null } extract_addresses() { local run_file run_file="$(ls -t "$ROOT_DIR/onchain/broadcast/DeployLocal.sol"/*/run-latest.json 2>/dev/null | head -n1)" if [[ -z "$run_file" ]]; then log "Deployment artifact not found" exit 1 fi log "Using artifact ${run_file#$ROOT_DIR/}" LIQUIDITY_MANAGER="$(jq -r '.transactions[] | select(.contractName=="LiquidityManager") | .contractAddress' "$run_file" | head -n1)" KRAIKEN="$(jq -r '.transactions[] | select(.contractName=="Kraiken") | .contractAddress' "$run_file" | head -n1)" STAKE="$(jq -r '.transactions[] | select(.contractName=="Stake") | .contractAddress' "$run_file" | head -n1)" DEPLOY_BLOCK="$(jq -r '.receipts[0].blockNumber' "$run_file" | xargs printf "%d")" if [[ -z "$LIQUIDITY_MANAGER" || "$LIQUIDITY_MANAGER" == "null" ]]; then log "LiquidityManager address missing" exit 1 fi cat >"$CONTRACT_ENV" <>"$SETUP_LOG" 2>&1 } grant_recenter_access() { log "Granting recenter access" cast rpc --rpc-url "$ANVIL_RPC" anvil_impersonateAccount "$FEE_DEST" >>"$SETUP_LOG" 2>&1 cast send --rpc-url "$ANVIL_RPC" --from "$FEE_DEST" --unlocked \ "$LIQUIDITY_MANAGER" "setRecenterAccess(address)" "$DEPLOYER_ADDR" >>"$SETUP_LOG" 2>&1 cast rpc --rpc-url "$ANVIL_RPC" anvil_stopImpersonatingAccount "$FEE_DEST" >>"$SETUP_LOG" 2>&1 if [[ -n "$TXNBOT_ADDRESS" ]]; then cast rpc --rpc-url "$ANVIL_RPC" anvil_impersonateAccount "$FEE_DEST" >>"$SETUP_LOG" 2>&1 cast send --rpc-url "$ANVIL_RPC" --from "$FEE_DEST" --unlocked \ "$LIQUIDITY_MANAGER" "setRecenterAccess(address)" "$TXNBOT_ADDRESS" >>"$SETUP_LOG" 2>&1 cast rpc --rpc-url "$ANVIL_RPC" anvil_stopImpersonatingAccount "$FEE_DEST" >>"$SETUP_LOG" 2>&1 fi } call_recenter() { local recenter_pk="$DEPLOYER_PK" local recenter_addr="$DEPLOYER_ADDR" if [[ -n "$TXNBOT_ADDRESS" ]]; then recenter_pk="$TXNBOT_PRIVATE_KEY" recenter_addr="$TXNBOT_ADDRESS" fi log "Calling recenter() via $recenter_addr" cast send --rpc-url "$ANVIL_RPC" --private-key "$recenter_pk" \ "$LIQUIDITY_MANAGER" "recenter()" >>"$SETUP_LOG" 2>&1 } seed_application_state() { log "Wrapping ETH to WETH" cast send --rpc-url "$ANVIL_RPC" --private-key "$DEPLOYER_PK" \ "$WETH" "deposit()" --value 0.02ether >>"$SETUP_LOG" 2>&1 log "Approving router" cast send --rpc-url "$ANVIL_RPC" --private-key "$DEPLOYER_PK" \ "$WETH" "approve(address,uint256)" "$SWAP_ROUTER" "$MAX_UINT" >>"$SETUP_LOG" 2>&1 log "Executing initial KRK swap" cast send --legacy --gas-limit 300000 --rpc-url "$ANVIL_RPC" --private-key "$DEPLOYER_PK" \ "$SWAP_ROUTER" "exactInputSingle((address,address,uint24,address,uint256,uint256,uint160))" \ "($WETH,$KRAIKEN,10000,$DEPLOYER_ADDR,10000000000000000,0,0)" >>"$SETUP_LOG" 2>&1 } prime_chain() { log "Pre-mining 5 blocks (minimal warmup for fast Ponder sync)..." # Mine just 5 blocks - enough for Ponder to have some history but keeps sync fast if cast rpc --rpc-url "$ANVIL_RPC" anvil_mine "0x5" "0x1" >/dev/null 2>&1; then log "Mined 5 blocks" else log "Batch mining failed, using individual evm_mine calls" for i in {1..5}; do cast rpc --rpc-url "$ANVIL_RPC" evm_mine >/dev/null 2>&1 || true done fi log "Pre-mining complete" } write_deployments_json() { cat >"$ROOT_DIR/onchain/deployments-local.json" <"$ROOT_DIR/services/ponder/.env.local" <"$TXNBOT_ENV" <>"$SETUP_LOG" 2>&1 || true local wei hex wei="$(cast --to-unit "$TXNBOT_FUND_VALUE" wei)" hex="$(cast --to-hex "$wei")" cast rpc --rpc-url "$ANVIL_RPC" anvil_setBalance "$TXNBOT_ADDRESS" "$hex" >>"$SETUP_LOG" 2>&1 } main() { log "Waiting for Anvil" wait_for_rpc maybe_set_deployer_from_mnemonic derive_txnbot_wallet run_forge_script extract_addresses fund_liquidity_manager grant_recenter_access call_recenter seed_application_state write_deployments_json write_ponder_env write_txn_bot_env fund_txn_bot_wallet prime_chain & local prime_pid=$! wait "$prime_pid" BOOTSTRAP_END=$(date +%s%3N) elapsed_ms=$((BOOTSTRAP_END - BOOTSTRAP_START)) elapsed_sec=$(awk -v ms="$elapsed_ms" 'BEGIN { printf "%.3f", ms/1000 }') log "Bootstrap complete in ${elapsed_sec}s" log "Kraiken: $KRAIKEN" log "Stake: $STAKE" log "LiquidityManager: $LIQUIDITY_MANAGER" log "txnBot: $TXNBOT_ADDRESS" } main "$@"