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"
|
2025-09-23 16:57:49 +02:00
|
|
|
WEBAPP_PID_FILE="$STATE_DIR/webapp.pid"
|
2025-09-23 19:24:05 +02:00
|
|
|
TXNBOT_PID_FILE="$STATE_DIR/txnBot.pid"
|
2025-09-23 14:18:04 +02:00
|
|
|
|
|
|
|
|
ANVIL_LOG="$LOG_DIR/anvil.log"
|
|
|
|
|
PONDER_LOG="$LOG_DIR/ponder.log"
|
2025-09-23 16:57:49 +02:00
|
|
|
WEBAPP_LOG="$LOG_DIR/webapp.log"
|
2025-09-23 19:24:05 +02:00
|
|
|
TXNBOT_LOG="$LOG_DIR/txnBot.log"
|
2025-09-23 14:18:04 +02:00
|
|
|
SETUP_LOG="$LOG_DIR/setup.log"
|
2025-09-23 19:24:05 +02:00
|
|
|
TXNBOT_ENV_FILE="$STATE_DIR/txnBot.env"
|
2025-09-23 20:29:51 +02:00
|
|
|
MNEMONIC_FILE="$ROOT_DIR/onchain/.secret.local"
|
2025-09-23 14:18:04 +02:00
|
|
|
|
|
|
|
|
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"
|
2025-09-23 19:24:05 +02:00
|
|
|
GRAPHQL_ENDPOINT="http://127.0.0.1:42069/graphql"
|
2025-09-23 14:18:04 +02:00
|
|
|
FRONTEND_URL="http://127.0.0.1:5173"
|
2025-09-24 09:41:28 +02:00
|
|
|
LOCAL_TXNBOT_URL="http://127.0.0.1:43069"
|
2025-09-23 14:18:04 +02:00
|
|
|
|
|
|
|
|
FOUNDRY_BIN=${FOUNDRY_BIN:-"$HOME/.foundry/bin"}
|
|
|
|
|
FORGE="$FOUNDRY_BIN/forge"
|
|
|
|
|
CAST="$FOUNDRY_BIN/cast"
|
|
|
|
|
ANVIL="$FOUNDRY_BIN/anvil"
|
|
|
|
|
|
2025-09-23 20:29:51 +02:00
|
|
|
DEFAULT_DEPLOYER_PK="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
|
|
|
|
|
DEFAULT_DEPLOYER_ADDR="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
|
|
|
|
|
DEPLOYER_PK=${DEPLOYER_PK:-$DEFAULT_DEPLOYER_PK}
|
|
|
|
|
DEPLOYER_ADDR=${DEPLOYER_ADDR:-$DEFAULT_DEPLOYER_ADDR}
|
2025-09-23 14:18:04 +02:00
|
|
|
FEE_DEST="0xf6a3eef9088A255c32b6aD2025f83E57291D9011"
|
|
|
|
|
WETH="0x4200000000000000000000000000000000000006"
|
|
|
|
|
SWAP_ROUTER="0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4"
|
|
|
|
|
|
2025-09-23 19:24:05 +02:00
|
|
|
DEFAULT_TXNBOT_PRIVATE_KEY="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"
|
|
|
|
|
DEFAULT_TXNBOT_ADDRESS="0x70997970C51812dc3A010C7d01b50e0d17dc79C8"
|
|
|
|
|
|
|
|
|
|
TXNBOT_PRIVATE_KEY=${TXNBOT_PRIVATE_KEY:-$DEFAULT_TXNBOT_PRIVATE_KEY}
|
|
|
|
|
TXNBOT_ADDRESS=${TXNBOT_ADDRESS:-$DEFAULT_TXNBOT_ADDRESS}
|
|
|
|
|
TXNBOT_FUND_VALUE=${TXNBOT_FUND_VALUE:-1ether}
|
|
|
|
|
|
2025-09-23 14:18:04 +02:00
|
|
|
MAX_UINT="0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
|
|
|
|
|
|
|
|
|
SKIP_CLEANUP=false
|
|
|
|
|
COMMAND="start"
|
|
|
|
|
|
|
|
|
|
cleanup() {
|
2025-09-23 16:57:49 +02:00
|
|
|
stop_process "Frontend" "$WEBAPP_PID_FILE"
|
2025-09-23 14:18:04 +02:00
|
|
|
stop_process "Ponder" "$PONDER_PID_FILE"
|
2025-09-23 19:24:05 +02:00
|
|
|
stop_process "TxnBot" "$TXNBOT_PID_FILE"
|
2025-09-23 14:18:04 +02:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-23 20:29:51 +02:00
|
|
|
set_deployer_credentials() {
|
|
|
|
|
if [[ -n "$DEPLOYER_PK" && -n "$DEPLOYER_ADDR" ]]; then
|
|
|
|
|
return
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [[ -f "$MNEMONIC_FILE" ]]; then
|
|
|
|
|
local mnemonic
|
|
|
|
|
mnemonic="$(tr -d '\n\r' < "$MNEMONIC_FILE")"
|
|
|
|
|
if [[ -n "$mnemonic" ]]; then
|
|
|
|
|
local derived_pk
|
|
|
|
|
derived_pk="$("$CAST" wallet private-key --mnemonic "$mnemonic" \
|
|
|
|
|
--mnemonic-derivation-path "m/44'/60'/0'/0/0")"
|
|
|
|
|
local derived_addr
|
|
|
|
|
derived_addr="$("$CAST" wallet address --private-key "$derived_pk")"
|
|
|
|
|
DEPLOYER_PK=${DEPLOYER_PK:-$derived_pk}
|
|
|
|
|
DEPLOYER_ADDR=${DEPLOYER_ADDR:-$derived_addr}
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
DEPLOYER_PK=${DEPLOYER_PK:-$DEFAULT_DEPLOYER_PK}
|
|
|
|
|
DEPLOYER_ADDR=${DEPLOYER_ADDR:-$DEFAULT_DEPLOYER_ADDR}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-23 14:18:04 +02:00
|
|
|
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"
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-23 16:57:49 +02:00
|
|
|
ensure_dependencies() {
|
2025-09-23 20:29:51 +02:00
|
|
|
if [[ ! -d "$ROOT_DIR/services/ponder/node_modules" ]]; then
|
2025-09-23 16:57:49 +02:00
|
|
|
log "Installing ponder dependencies"
|
2025-09-23 20:29:51 +02:00
|
|
|
pushd "$ROOT_DIR/services/ponder" >/dev/null
|
2025-09-23 16:57:49 +02:00
|
|
|
npm install >>"$SETUP_LOG" 2>&1
|
|
|
|
|
popd >/dev/null
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [[ ! -d "$ROOT_DIR/web-app/node_modules" ]]; then
|
|
|
|
|
log "Installing web-app dependencies"
|
|
|
|
|
pushd "$ROOT_DIR/web-app" >/dev/null
|
|
|
|
|
npm install >>"$SETUP_LOG" 2>&1
|
|
|
|
|
popd >/dev/null
|
|
|
|
|
fi
|
2025-09-23 19:24:05 +02:00
|
|
|
|
|
|
|
|
if [[ ! -d "$ROOT_DIR/services/txnBot/node_modules" ]]; then
|
|
|
|
|
log "Installing txnBot dependencies"
|
|
|
|
|
pushd "$ROOT_DIR/services/txnBot" >/dev/null
|
|
|
|
|
npm install >>"$SETUP_LOG" 2>&1
|
|
|
|
|
popd >/dev/null
|
|
|
|
|
fi
|
2025-09-23 16:57:49 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-23 14:18:04 +02:00
|
|
|
start_anvil() {
|
|
|
|
|
if [[ -f "$ANVIL_PID_FILE" ]]; then
|
2025-09-24 09:41:28 +02:00
|
|
|
local existing_pid
|
|
|
|
|
existing_pid="$(cat "$ANVIL_PID_FILE")"
|
|
|
|
|
if kill -0 "$existing_pid" 2>/dev/null; then
|
|
|
|
|
log "Anvil already running (pid $existing_pid)"
|
|
|
|
|
return
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
log "Found stale Anvil pid file (pid $existing_pid); restarting Anvil"
|
|
|
|
|
rm -f "$ANVIL_PID_FILE"
|
2025-09-23 14:18:04 +02:00
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
log "Starting Anvil (forking $FORK_URL)"
|
2025-09-23 20:29:51 +02:00
|
|
|
local anvil_args=("--fork-url" "$FORK_URL" "--chain-id" 31337 "--block-time" 1 \
|
|
|
|
|
"--host" 127.0.0.1 "--port" 8545)
|
|
|
|
|
|
|
|
|
|
if [[ -f "$MNEMONIC_FILE" ]]; then
|
|
|
|
|
local mnemonic
|
|
|
|
|
mnemonic="$(tr -d '\n\r' < "$MNEMONIC_FILE")"
|
|
|
|
|
if [[ -n "$mnemonic" ]]; then
|
|
|
|
|
anvil_args+=("--mnemonic" "$mnemonic")
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
"$ANVIL" "${anvil_args[@]}" >"$ANVIL_LOG" 2>&1 &
|
2025-09-23 14:18:04 +02:00
|
|
|
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() {
|
2025-09-23 16:57:49 +02:00
|
|
|
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" || ! -f "$run_file" ]]; then
|
|
|
|
|
log "Deployment artifact not found under onchain/broadcast/DeployLocal.sol"
|
2025-09-23 14:18:04 +02:00
|
|
|
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)"
|
2025-09-23 16:57:49 +02:00
|
|
|
log "Using deployment artifact: ${run_file#$ROOT_DIR/}"
|
2025-09-23 14:18:04 +02:00
|
|
|
|
|
|
|
|
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"
|
2025-09-23 16:57:49 +02:00
|
|
|
|
|
|
|
|
# Get deployment block number
|
|
|
|
|
local deploy_block
|
|
|
|
|
deploy_block="$(jq -r '.receipts[0].blockNumber' "$run_file" | xargs printf "%d")"
|
|
|
|
|
|
|
|
|
|
# Create .env.local for Ponder with deployed addresses
|
2025-09-23 20:29:51 +02:00
|
|
|
cat > "$ROOT_DIR/services/ponder/.env.local" <<EOF
|
2025-09-23 16:57:49 +02:00
|
|
|
# Auto-generated by local_env.sh
|
2025-09-23 19:24:05 +02:00
|
|
|
PONDER_NETWORK=BASE_SEPOLIA_LOCAL_FORK
|
2025-09-23 16:57:49 +02:00
|
|
|
KRAIKEN_ADDRESS=$KRAIKEN
|
|
|
|
|
STAKE_ADDRESS=$STAKE
|
|
|
|
|
START_BLOCK=$deploy_block
|
|
|
|
|
# Use PostgreSQL connection
|
|
|
|
|
DATABASE_URL=postgresql://ponder:ponder_local@localhost/ponder_local
|
|
|
|
|
DATABASE_SCHEMA=ponder_local_${deploy_block}
|
|
|
|
|
EOF
|
|
|
|
|
log "Created Ponder .env.local with deployed addresses and block $deploy_block"
|
2025-09-23 14:18:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
2025-09-24 09:41:28 +02:00
|
|
|
|
|
|
|
|
if [[ -n "$TXNBOT_ADDRESS" ]]; then
|
|
|
|
|
log "Transferring recenter access to txnBot $TXNBOT_ADDRESS"
|
|
|
|
|
"$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)" "$TXNBOT_ADDRESS" >>"$SETUP_LOG" 2>&1
|
|
|
|
|
"$CAST" rpc --rpc-url "$ANVIL_RPC" anvil_stopImpersonatingAccount "$FEE_DEST" >/dev/null
|
|
|
|
|
else
|
|
|
|
|
log "TXNBOT_ADDRESS not set; recenter access left with deployer"
|
|
|
|
|
fi
|
2025-09-23 14:18:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-23 19:24:05 +02:00
|
|
|
prime_chain_for_indexing() {
|
|
|
|
|
log "Pre-mining blocks for indexer stability"
|
|
|
|
|
for _ in {1..1200}; do
|
|
|
|
|
"$CAST" rpc --rpc-url "$ANVIL_RPC" evm_mine > /dev/null 2>&1 || true
|
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
write_txnbot_env() {
|
|
|
|
|
source_addresses || { log "Contract addresses not found"; exit 1; }
|
|
|
|
|
|
|
|
|
|
cat > "$TXNBOT_ENV_FILE" <<EOF
|
|
|
|
|
ENVIRONMENT=BASE_SEPOLIA_LOCAL_FORK
|
|
|
|
|
PROVIDER_URL=$ANVIL_RPC
|
|
|
|
|
PRIVATE_KEY=$TXNBOT_PRIVATE_KEY
|
|
|
|
|
LM_CONTRACT_ADDRESS=$LIQUIDITY_MANAGER
|
|
|
|
|
STAKE_CONTRACT_ADDRESS=$STAKE
|
|
|
|
|
GRAPHQL_ENDPOINT=$GRAPHQL_ENDPOINT
|
|
|
|
|
WALLET_ADDRESS=$TXNBOT_ADDRESS
|
|
|
|
|
# Expose a non-conflicting status port for local dev
|
|
|
|
|
PORT=43069
|
|
|
|
|
EOF
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fund_txnbot_wallet() {
|
|
|
|
|
source_addresses || { log "Contract addresses not found"; exit 1; }
|
|
|
|
|
|
|
|
|
|
if [[ -z "$TXNBOT_ADDRESS" ]]; then
|
|
|
|
|
log "TxnBot wallet address not provided; skipping funding"
|
|
|
|
|
return
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
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" >>"$SETUP_LOG" 2>&1 || \
|
|
|
|
|
log "Funding txnBot wallet failed (see setup log)"
|
2025-09-24 09:41:28 +02:00
|
|
|
|
|
|
|
|
# Ensure local fork balance reflects the funding even if the upstream state is zero
|
|
|
|
|
local fund_value_wei
|
|
|
|
|
fund_value_wei="$("$CAST" --to-unit "$TXNBOT_FUND_VALUE" wei)"
|
|
|
|
|
local fund_value_hex
|
|
|
|
|
fund_value_hex="$("$CAST" --to-hex "$fund_value_wei")"
|
|
|
|
|
"$CAST" rpc --rpc-url "$ANVIL_RPC" anvil_setBalance "$TXNBOT_ADDRESS" "$fund_value_hex" \
|
|
|
|
|
>>"$SETUP_LOG" 2>&1
|
2025-09-23 19:24:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
start_txnbot() {
|
|
|
|
|
if [[ -f "$TXNBOT_PID_FILE" ]]; then
|
|
|
|
|
log "txnBot already running (pid $(cat \"$TXNBOT_PID_FILE\"))"
|
|
|
|
|
return
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
write_txnbot_env
|
|
|
|
|
fund_txnbot_wallet
|
|
|
|
|
|
|
|
|
|
log "Starting txnBot automation"
|
|
|
|
|
pushd "$ROOT_DIR/services/txnBot" >/dev/null
|
|
|
|
|
TXN_BOT_ENV_FILE="$TXNBOT_ENV_FILE" node service.js >"$TXNBOT_LOG" 2>&1 &
|
|
|
|
|
popd >/dev/null
|
|
|
|
|
echo $! >"$TXNBOT_PID_FILE"
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-23 14:18:04 +02:00
|
|
|
start_ponder() {
|
|
|
|
|
if [[ -f "$PONDER_PID_FILE" ]]; then
|
|
|
|
|
log "Ponder already running (pid $(cat "$PONDER_PID_FILE"))"
|
|
|
|
|
return
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
log "Starting Ponder indexer"
|
2025-09-23 20:29:51 +02:00
|
|
|
pushd "$ROOT_DIR/services/ponder" >/dev/null
|
2025-09-23 19:24:05 +02:00
|
|
|
PONDER_NETWORK=BASE_SEPOLIA_LOCAL_FORK npm run dev >"$PONDER_LOG" 2>&1 &
|
2025-09-23 14:18:04 +02:00
|
|
|
popd >/dev/null
|
|
|
|
|
echo $! >"$PONDER_PID_FILE"
|
|
|
|
|
|
|
|
|
|
wait_for_http "$GRAPHQL_HEALTH"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
start_frontend() {
|
2025-09-23 16:57:49 +02:00
|
|
|
if [[ -f "$WEBAPP_PID_FILE" ]]; then
|
2025-09-24 09:41:28 +02:00
|
|
|
log "Frontend already running (pid $(cat \"$WEBAPP_PID_FILE\"))"
|
2025-09-23 14:18:04 +02:00
|
|
|
return
|
|
|
|
|
fi
|
|
|
|
|
|
2025-09-24 09:41:28 +02:00
|
|
|
source_addresses || { log "Contract addresses not found"; exit 1; }
|
|
|
|
|
|
|
|
|
|
local vite_env=(
|
|
|
|
|
"VITE_DEFAULT_CHAIN_ID=31337"
|
|
|
|
|
"VITE_LOCAL_RPC_URL=/rpc/anvil"
|
|
|
|
|
"VITE_LOCAL_RPC_PROXY_TARGET=$ANVIL_RPC"
|
|
|
|
|
"VITE_KRAIKEN_ADDRESS=$KRAIKEN"
|
|
|
|
|
"VITE_STAKE_ADDRESS=$STAKE"
|
|
|
|
|
"VITE_SWAP_ROUTER=$SWAP_ROUTER"
|
|
|
|
|
"VITE_PONDER_BASE_SEPOLIA_LOCAL_FORK=$GRAPHQL_ENDPOINT"
|
|
|
|
|
"VITE_TXNBOT_BASE_SEPOLIA_LOCAL_FORK=$LOCAL_TXNBOT_URL"
|
|
|
|
|
)
|
|
|
|
|
|
2025-09-23 14:18:04 +02:00
|
|
|
log "Starting frontend (Vite dev server)"
|
2025-09-23 16:57:49 +02:00
|
|
|
pushd "$ROOT_DIR/web-app" >/dev/null
|
2025-09-24 09:41:28 +02:00
|
|
|
env "${vite_env[@]}" npm run dev -- --host 0.0.0.0 --port 5173 >"$WEBAPP_LOG" 2>&1 &
|
2025-09-23 14:18:04 +02:00
|
|
|
popd >/dev/null
|
2025-09-23 16:57:49 +02:00
|
|
|
echo $! >"$WEBAPP_PID_FILE"
|
2025-09-23 14:18:04 +02:00
|
|
|
|
|
|
|
|
wait_for_http "$FRONTEND_URL"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
start_environment() {
|
|
|
|
|
ensure_tools
|
2025-09-23 20:29:51 +02:00
|
|
|
set_deployer_credentials
|
2025-09-23 14:18:04 +02:00
|
|
|
start_directories
|
2025-09-23 16:57:49 +02:00
|
|
|
ensure_dependencies
|
2025-09-23 14:18:04 +02:00
|
|
|
start_anvil
|
|
|
|
|
deploy_protocol
|
|
|
|
|
extract_addresses
|
|
|
|
|
bootstrap_liquidity_manager
|
|
|
|
|
prepare_application_state
|
2025-09-23 19:24:05 +02:00
|
|
|
prime_chain_for_indexing
|
2025-09-23 16:57:49 +02:00
|
|
|
start_ponder # Re-enabled with PostgreSQL
|
2025-09-23 19:24:05 +02:00
|
|
|
start_txnbot
|
2025-09-23 14:18:04 +02:00
|
|
|
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"
|
|
|
|
|
|
2025-09-23 19:24:05 +02:00
|
|
|
wait "$(cat "$ANVIL_PID_FILE")" "$(cat "$PONDER_PID_FILE")" "$(cat "$WEBAPP_PID_FILE")" "$(cat "$TXNBOT_PID_FILE")"
|
2025-09-23 14:18:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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"
|
2025-09-23 16:57:49 +02:00
|
|
|
service_status "Frontend" "$WEBAPP_PID_FILE" "$WEBAPP_LOG"
|
2025-09-23 19:24:05 +02:00
|
|
|
service_status "TxnBot" "$TXNBOT_PID_FILE" "$TXNBOT_LOG"
|
2025-09-23 14:18:04 +02:00
|
|
|
|
|
|
|
|
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
|