harb/scripts/local_env.sh

368 lines
9.3 KiB
Bash
Raw Normal View History

2025-09-23 14:18:04 +02:00
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
STATE_DIR="$ROOT_DIR/tmp/local-env"
LOG_DIR="$STATE_DIR/logs"
ANVIL_PID_FILE="$STATE_DIR/anvil.pid"
PONDER_PID_FILE="$STATE_DIR/ponder.pid"
LANDING_PID_FILE="$STATE_DIR/landing.pid"
ANVIL_LOG="$LOG_DIR/anvil.log"
PONDER_LOG="$LOG_DIR/ponder.log"
LANDING_LOG="$LOG_DIR/landing.log"
SETUP_LOG="$LOG_DIR/setup.log"
FORK_URL=${FORK_URL:-"https://sepolia.base.org"}
ANVIL_RPC="http://127.0.0.1:8545"
GRAPHQL_HEALTH="http://127.0.0.1:42069/health"
FRONTEND_URL="http://127.0.0.1:5173"
FOUNDRY_BIN=${FOUNDRY_BIN:-"$HOME/.foundry/bin"}
FORGE="$FOUNDRY_BIN/forge"
CAST="$FOUNDRY_BIN/cast"
ANVIL="$FOUNDRY_BIN/anvil"
DEPLOYER_PK=${DEPLOYER_PK:-"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"}
DEPLOYER_ADDR="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
FEE_DEST="0xf6a3eef9088A255c32b6aD2025f83E57291D9011"
WETH="0x4200000000000000000000000000000000000006"
SWAP_ROUTER="0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4"
MAX_UINT="0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
SKIP_CLEANUP=false
COMMAND="start"
cleanup() {
stop_process "Frontend" "$LANDING_PID_FILE"
stop_process "Ponder" "$PONDER_PID_FILE"
stop_process "Anvil" "$ANVIL_PID_FILE"
if [[ "$SKIP_CLEANUP" == true ]]; then
log "Skipping state cleanup (--no-cleanup). State preserved at $STATE_DIR"
return
fi
if [[ -d "$STATE_DIR" ]]; then
rm -rf "$STATE_DIR"
fi
}
stop_process() {
local name="$1"
local pid_file="$2"
if [[ -f "$pid_file" ]]; then
local pid
pid="$(cat "$pid_file")"
if kill -0 "$pid" 2>/dev/null; then
echo "[local-env] Stopping $name (pid $pid)"
kill "$pid" 2>/dev/null || true
wait "$pid" 2>/dev/null || true
fi
rm -f "$pid_file"
fi
}
log() {
echo "[local-env] $*"
}
wait_for_rpc() {
local url="$1"
for _ in {1..60}; do
if curl -s -o /dev/null -X POST "$url" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}'; then
return 0
fi
sleep 1
done
log "Timed out waiting for RPC at $url"
return 1
}
wait_for_http() {
local url="$1"
for _ in {1..60}; do
if curl -sSf "$url" >/dev/null 2>&1; then
return 0
fi
sleep 1
done
log "Timed out waiting for $url"
return 1
}
ensure_tools() {
for cmd in "$ANVIL" "$FORGE" "$CAST" jq npm curl; do
if ! command -v "$cmd" >/dev/null 2>&1; then
log "Required command not found: $cmd"
exit 1
fi
done
}
start_directories() {
mkdir -p "$LOG_DIR"
}
start_anvil() {
if [[ -f "$ANVIL_PID_FILE" ]]; then
log "Anvil already running (pid $(cat "$ANVIL_PID_FILE"))"
return
fi
log "Starting Anvil (forking $FORK_URL)"
"$ANVIL" --fork-url "$FORK_URL" --chain-id 31337 --block-time 1 \
--host 127.0.0.1 --port 8545 \
>"$ANVIL_LOG" 2>&1 &
echo $! >"$ANVIL_PID_FILE"
wait_for_rpc "$ANVIL_RPC"
}
deploy_protocol() {
log "Deploying contracts"
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="$ROOT_DIR/onchain/broadcast/DeployLocal.sol/84532/run-latest.json"
if [[ ! -f "$run_file" ]]; then
log "Deployment artifact not found: $run_file"
exit 1
fi
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)"
if [[ -z "$LIQUIDITY_MANAGER" || "$LIQUIDITY_MANAGER" == "null" ]]; then
log "Failed to extract LiquidityManager address"
exit 1
fi
echo "LIQUIDITY_MANAGER=$LIQUIDITY_MANAGER" >"$STATE_DIR/contracts.env"
echo "KRAIKEN=$KRAIKEN" >>"$STATE_DIR/contracts.env"
echo "STAKE=$STAKE" >>"$STATE_DIR/contracts.env"
}
source_addresses() {
if [[ ! -f "$STATE_DIR/contracts.env" ]]; then
return 1
fi
# shellcheck disable=SC1090
source "$STATE_DIR/contracts.env"
}
bootstrap_liquidity_manager() {
source_addresses || { log "Contract addresses not found"; exit 1; }
log "Funding LiquidityManager"
"$CAST" send --rpc-url "$ANVIL_RPC" --private-key "$DEPLOYER_PK" \
"$LIQUIDITY_MANAGER" --value 0.1ether >>"$SETUP_LOG" 2>&1
log "Granting recenter access"
"$CAST" rpc --rpc-url "$ANVIL_RPC" anvil_impersonateAccount "$FEE_DEST" >/dev/null
"$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" >/dev/null
log "Calling recenter()"
"$CAST" send --rpc-url "$ANVIL_RPC" --private-key "$DEPLOYER_PK" \
"$LIQUIDITY_MANAGER" "recenter()" >>"$SETUP_LOG" 2>&1
}
prepare_application_state() {
source_addresses || { log "Contract addresses not found"; exit 1; }
log "Wrapping 0.02 ETH into WETH"
"$CAST" send --rpc-url "$ANVIL_RPC" --private-key "$DEPLOYER_PK" \
"$WETH" "deposit()" --value 0.02ether >>"$SETUP_LOG" 2>&1
log "Approving SwapRouter" \
&& "$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 buy"
"$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
}
start_ponder() {
if [[ -f "$PONDER_PID_FILE" ]]; then
log "Ponder already running (pid $(cat "$PONDER_PID_FILE"))"
return
fi
log "Starting Ponder indexer"
pushd "$ROOT_DIR/ponder" >/dev/null
PONDER_NETWORK=local npm run dev >"$PONDER_LOG" 2>&1 &
popd >/dev/null
echo $! >"$PONDER_PID_FILE"
wait_for_http "$GRAPHQL_HEALTH"
}
start_frontend() {
if [[ -f "$LANDING_PID_FILE" ]]; then
log "Frontend already running (pid $(cat "$LANDING_PID_FILE"))"
return
fi
log "Starting frontend (Vite dev server)"
pushd "$ROOT_DIR/landing" >/dev/null
npm run dev -- --host 0.0.0.0 --port 5173 >"$LANDING_LOG" 2>&1 &
popd >/dev/null
echo $! >"$LANDING_PID_FILE"
wait_for_http "$FRONTEND_URL"
}
start_environment() {
ensure_tools
start_directories
start_anvil
deploy_protocol
extract_addresses
bootstrap_liquidity_manager
prepare_application_state
start_ponder
start_frontend
}
start_and_wait() {
start_environment
source_addresses || true
log "Environment ready"
log " RPC: $ANVIL_RPC"
log " Kraiken: $KRAIKEN"
log " Stake: $STAKE"
log " LM: $LIQUIDITY_MANAGER"
log " Indexer: $GRAPHQL_HEALTH"
log " Frontend: $FRONTEND_URL"
log "Press Ctrl+C to shut everything down"
wait "$(cat "$ANVIL_PID_FILE")" "$(cat "$PONDER_PID_FILE")" "$(cat "$LANDING_PID_FILE")"
}
stop_environment() {
local previous_skip="$SKIP_CLEANUP"
SKIP_CLEANUP=false
cleanup
SKIP_CLEANUP="$previous_skip"
log "Environment stopped"
}
on_exit() {
local status=$?
if [[ "$COMMAND" != "start" ]]; then
return
fi
if [[ $status -ne 0 ]]; then
log "Start command exited with status $status"
if [[ -f "$SETUP_LOG" ]]; then
log "Last 40 lines of setup log:"
tail -n 40 "$SETUP_LOG"
fi
fi
cleanup
if [[ "$SKIP_CLEANUP" == true ]]; then
log "State directory preserved at $STATE_DIR"
else
log "Environment stopped"
fi
}
usage() {
cat <<EOF
Usage: scripts/local_env.sh [--no-cleanup] [start|stop|status]
Commands:
start Bootstrap local chain, contracts, indexer, and frontend (default)
stop Terminate all background services
status Show running process information
Options:
--no-cleanup Preserve logs and state directory on exit (start command only)
EOF
}
status_environment() {
source_addresses 2>/dev/null || true
printf '\n%-12s %-8s %-10s %s\n' "Service" "Status" "PID" "Details"
printf '%s\n' "-------------------------------------------------------------"
service_status "Anvil" "$ANVIL_PID_FILE" "$ANVIL_LOG"
service_status "Ponder" "$PONDER_PID_FILE" "$PONDER_LOG"
service_status "Frontend" "$LANDING_PID_FILE" "$LANDING_LOG"
if [[ -f "$STATE_DIR/contracts.env" ]]; then
printf '\nContracts:\n'
cat "$STATE_DIR/contracts.env"
printf '\n'
fi
}
service_status() {
local name="$1" pid_file="$2" log_file="$3"
if [[ -f "$pid_file" ]]; then
local pid="$(cat "$pid_file")"
if kill -0 "$pid" 2>/dev/null; then
printf '%-12s %-8s %-10s %s\n' "$name" "running" "$pid" "$log_file"
return
fi
fi
printf '%-12s %-8s %-10s %s\n' "$name" "stopped" "-" "$log_file"
}
COMMAND="start"
while (($#)); do
case "$1" in
--no-cleanup)
SKIP_CLEANUP=true
;;
start|stop|status)
COMMAND="$1"
;;
*)
usage
exit 1
;;
esac
shift
done
if [[ "$COMMAND" == "start" ]]; then
trap on_exit EXIT
fi
case "$COMMAND" in
start)
trap 'log "Caught signal, stopping..."; exit 0' INT TERM
start_and_wait
;;
stop)
stop_environment
;;
status)
status_environment
;;
*)
usage
exit 1
;;
esac