#!/usr/bin/env bash # Shared bootstrap functions for local dev and CI. # Source this file after setting these variables: # ANVIL_RPC - Anvil JSON-RPC URL (required) # DEPLOYER_PK - Deployer private key (defaults to Anvil account 0) # DEPLOYER_ADDR - Deployer address (defaults to Anvil account 0) # TXNBOT_ADDRESS - TxnBot wallet address (optional) # TXNBOT_PRIVATE_KEY- TxnBot private key (optional) # TXNBOT_FUND_VALUE - Amount to fund txnBot (default: 1ether) # CONTRACT_ENV - Path to write contracts.env (required) # LOG_FILE - Log file for cast/forge output (default: /dev/null) # ONCHAIN_DIR - Path to onchain/ directory (required) # KRAIKEN_LIB_DIR - Path to kraiken-lib/ directory (optional, for CI build) set -euo pipefail # ── Constants ────────────────────────────────────────────────────────── FEE_DEST=0xf6a3eef9088A255c32b6aD2025f83E57291D9011 WETH=0x4200000000000000000000000000000000000006 SWAP_ROUTER=0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4 MAX_UINT=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff DEFAULT_DEPLOYER_PK=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 DEFAULT_DEPLOYER_ADDR=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 DEFAULT_TXNBOT_PK=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d DEFAULT_TXNBOT_ADDR=0x70997970C51812dc3A010C7d01b50e0d17dc79C8 # ── Defaults ─────────────────────────────────────────────────────────── DEPLOYER_PK=${DEPLOYER_PK:-$DEFAULT_DEPLOYER_PK} DEPLOYER_ADDR=${DEPLOYER_ADDR:-$DEFAULT_DEPLOYER_ADDR} TXNBOT_FUND_VALUE=${TXNBOT_FUND_VALUE:-1ether} LOG_FILE=${LOG_FILE:-/dev/null} # ── Helpers ──────────────────────────────────────────────────────────── bootstrap_log() { echo "[bootstrap] $*" } # ── Functions ────────────────────────────────────────────────────────── 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 bootstrap_log "Timed out waiting for Anvil at $ANVIL_RPC" return 1 } run_forge_script() { bootstrap_log "Deploying contracts to fork" pushd "$ONCHAIN_DIR" >/dev/null forge script script/DeployLocal.sol --fork-url "$ANVIL_RPC" --broadcast >>"$LOG_FILE" 2>&1 popd >/dev/null } extract_addresses() { local run_file run_file="$(ls -t "$ONCHAIN_DIR/broadcast/DeployLocal.sol"/*/run-latest.json 2>/dev/null | head -n1)" if [[ -z "$run_file" ]]; then bootstrap_log "Deployment artifact not found" exit 1 fi bootstrap_log "Using artifact $run_file" 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)" OPTIMIZER_PROXY="$(jq -r '.transactions[] | select(.contractName=="ERC1967Proxy") | .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 bootstrap_log "LiquidityManager address missing" exit 1 fi } write_contracts_env() { cat >"$CONTRACT_ENV" <>"$LOG_FILE" 2>&1 } grant_recenter_access() { bootstrap_log "Granting recenter access to deployer" cast rpc --rpc-url "$ANVIL_RPC" anvil_impersonateAccount "$FEE_DEST" >>"$LOG_FILE" 2>&1 cast send --rpc-url "$ANVIL_RPC" --from "$FEE_DEST" --unlocked \ "$LIQUIDITY_MANAGER" "setRecenterAccess(address)" "$DEPLOYER_ADDR" >>"$LOG_FILE" 2>&1 cast rpc --rpc-url "$ANVIL_RPC" anvil_stopImpersonatingAccount "$FEE_DEST" >>"$LOG_FILE" 2>&1 if [[ -n "${TXNBOT_ADDRESS:-}" ]]; then bootstrap_log "Granting recenter access to txnBot ($TXNBOT_ADDRESS)" cast rpc --rpc-url "$ANVIL_RPC" anvil_impersonateAccount "$FEE_DEST" >>"$LOG_FILE" 2>&1 cast send --rpc-url "$ANVIL_RPC" --from "$FEE_DEST" --unlocked \ "$LIQUIDITY_MANAGER" "setRecenterAccess(address)" "$TXNBOT_ADDRESS" >>"$LOG_FILE" 2>&1 cast rpc --rpc-url "$ANVIL_RPC" anvil_stopImpersonatingAccount "$FEE_DEST" >>"$LOG_FILE" 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 # If the deploy script already bootstrapped VWAP (cumulativeVolume > 0), positions # are in place at the post-seed-buy tick. Calling recenter() now would fail with # "amplitude not reached" because currentTick == anchorCenterTick. Skip it. local cumvol cumvol="$(cast call --rpc-url "$ANVIL_RPC" \ "$LIQUIDITY_MANAGER" "cumulativeVolume()(uint256)" 2>/dev/null || echo "0")" if [[ "$cumvol" != "0" && -n "$cumvol" ]]; then bootstrap_log "VWAP already bootstrapped by deploy script (cumulativeVolume=$cumvol) -- skipping initial recenter" return 0 fi bootstrap_log "Calling recenter() via $recenter_addr" cast send --rpc-url "$ANVIL_RPC" --private-key "$recenter_pk" \ "$LIQUIDITY_MANAGER" "recenter()" >>"$LOG_FILE" 2>&1 } seed_application_state() { bootstrap_log "Wrapping ETH to WETH" cast send --rpc-url "$ANVIL_RPC" --private-key "$DEPLOYER_PK" \ "$WETH" "deposit()" --value 2ether >>"$LOG_FILE" 2>&1 bootstrap_log "Approving router" cast send --rpc-url "$ANVIL_RPC" --private-key "$DEPLOYER_PK" \ "$WETH" "approve(address,uint256)" "$SWAP_ROUTER" "$MAX_UINT" >>"$LOG_FILE" 2>&1 # Swap with retry — recenter may not position liquidity at the right tick on first call local swap_success=false for attempt in 1 2 3; do bootstrap_log "KRK swap attempt $attempt/3" local balance_before balance_after balance_before=$(cast call --rpc-url "$ANVIL_RPC" "$KRAIKEN" "balanceOf(address)(uint256)" "$DEPLOYER_ADDR" 2>/dev/null || echo "0") 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,1000000000000000000,0,4295128740)" >>"$LOG_FILE" 2>&1 || true balance_after=$(cast call --rpc-url "$ANVIL_RPC" "$KRAIKEN" "balanceOf(address)(uint256)" "$DEPLOYER_ADDR" 2>/dev/null || echo "0") if [[ "$balance_after" != "$balance_before" && "$balance_after" != "0" ]]; then bootstrap_log "Swap successful — got KRK tokens (balance: $balance_after)" swap_success=true break fi bootstrap_log "Swap returned 0 KRK — recentering and retrying" # Mine a few blocks to advance time, then recenter cast rpc --rpc-url "$ANVIL_RPC" evm_mine >>"$LOG_FILE" 2>&1 || true cast rpc --rpc-url "$ANVIL_RPC" evm_mine >>"$LOG_FILE" 2>&1 || true local recenter_pk="${TXNBOT_PRIVATE_KEY:-$DEPLOYER_PK}" cast send --rpc-url "$ANVIL_RPC" --private-key "$recenter_pk" \ "$LIQUIDITY_MANAGER" "recenter()" >>"$LOG_FILE" 2>&1 || true done if [[ "$swap_success" != "true" ]]; then bootstrap_log "WARNING: All swap attempts returned 0 KRK. Pool may have no liquidity at current tick." fi } fund_txn_bot_wallet() { if [[ -z "${TXNBOT_ADDRESS:-}" ]]; then return fi bootstrap_log "Funding txnBot wallet $TXNBOT_ADDRESS with $TXNBOT_FUND_VALUE" cast send --rpc-url "$ANVIL_RPC" --private-key "$DEPLOYER_PK" \ "$TXNBOT_ADDRESS" --value "$TXNBOT_FUND_VALUE" >>"$LOG_FILE" 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" >>"$LOG_FILE" 2>&1 } write_deployments_json() { local target="${1:-$ONCHAIN_DIR/deployments-local.json}" cat >"$target" <