#!/usr/bin/env bash set -euo pipefail # ============================================ # AAXE / Furuombe Bash CLI # -------------------------------------------- # Builds and (optionally) submits a Furuombe plan # matching the user's block sequence. # # USAGE: # ./aaxe-cli.sh build-plan > plan.json # ./aaxe-cli.sh show-plan # ./aaxe-cli.sh send --rpc https://mainnet.infura.io/v3/KEY --router 0xRouterAddr # # ENV: # PRIVATE_KEY # hex private key (no 0x), ONLY required for `send` # # NOTES: # - The router ABI here assumes: function execute(bytes plan) # where `plan` is arbitrary bytes (we pass JSON bytes). # If your router expects a different ABI/format, adjust SEND section. # ============================================ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PLAN_FILE="${PLAN_FILE:-$SCRIPT_DIR/plan.json}" # ---------- Defaults (edit to your environment) ---------- CHAIN_ID_DEFAULT="${CHAIN_ID_DEFAULT:-1}" RPC_URL_DEFAULT="${RPC_URL_DEFAULT:-https://mainnet.infura.io/v3/YOUR_KEY}" AAXE_ROUTER_DEFAULT="${AAXE_ROUTER_DEFAULT:-0x0000000000000000000000000000000000000000}" # <- PUT REAL ROUTER # Common tokens (placeholders — replace with real addresses for your chain) ADDR_USDC="${ADDR_USDC:-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48}" # Mainnet USDC ADDR_USDT="${ADDR_USDT:-0xdAC17F958D2ee523a2206206994597C13D831ec7}" # Mainnet USDT # Aave V3 (example mainnet Pool; verify!) ADDR_AAVE_V3_POOL="${ADDR_AAVE_V3_POOL:-0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2}" # Paraswap v5 Augustus (placeholder — replace!) ADDR_PARASWAP_V5="${ADDR_PARASWAP_V5:-0xDEF1ABE32c034e558Cdd535791643C58a13aCC10}" # aToken placeholder (aEthUSDC-like) — replace with the correct aToken for your network ADDR_aEthUSDC="${ADDR_aEthUSDC:-0x0000000000000000000000000000000000000001}" # ---------- Helpers ---------- die() { echo "Error: $*" >&2; exit 1; } require_jq() { command -v jq >/dev/null 2>&1 || die "jq is required. Install: https://stedolan.github.io/jq/" } require_bc() { command -v bc >/dev/null 2>&1 || die "bc is required. Install: sudo apt-get install bc (or your package manager)" } require_cast() { command -v cast >/dev/null 2>&1 || die "Foundry's 'cast' is required for sending. Install: https://book.getfoundry.sh/" } to_wei_like() { # Convert decimal string to 6-decimal fixed (USDC/USDT style) integer string # e.g., "2001.033032" -> "2001033032" local amount="$1" # Use bc to multiply by 1000000 and truncate decimals (scale=0 truncates) echo "scale=0; ($amount * 1000000) / 1" | bc | tr -d '\n' } # ---------- Plan builder ---------- build_plan() { require_jq require_bc # Amounts as user provided (USDC/USDT both 6 decimals typical) USDC_4600=$(to_wei_like "4600") USDT_2500=$(to_wei_like "2500") USDT_2000_9=$(to_wei_like "2000.9") USDC_2001_033032=$(to_wei_like "2001.033032") USDC_1000=$(to_wei_like "1000") USDT_2300=$(to_wei_like "2300") USDT_2100_9=$(to_wei_like "2100.9") USDC_2100_628264=$(to_wei_like "2100.628264") USDC_4500=$(to_wei_like "4500") # Final flashloan repay is shown as -4600, we encode +4600 as repayment USDC_4600_POS=$(to_wei_like "4600") # The plan is a pure JSON array of steps, each step carries: # - blockType / protocol / display / tokens / amounts / addresses # - You can extend with slippage, deadline, referral, etc. jq -n --arg usdc "$ADDR_USDC" \ --arg usdt "$ADDR_USDT" \ --arg aave "$ADDR_AAVE_V3_POOL" \ --arg pswap "$ADDR_PARASWAP_V5" \ --arg aethusdc "$ADDR_aEthUSDC" \ --argjson amt_usdc_4600 "$USDC_4600" \ --argjson amt_usdt_2500 "$USDT_2500" \ --argjson amt_usdt_2000_9 "$USDT_2000_9" \ --argjson amt_usdc_2001_033032 "$USDC_2001_033032" \ --argjson amt_usdc_1000 "$USDC_1000" \ --argjson amt_usdt_2300 "$USDT_2300" \ --argjson amt_usdt_2100_9 "$USDT_2100_9" \ --argjson amt_usdc_2100_628264 "$USDC_2100_628264" \ --argjson amt_usdc_4500 "$USDC_4500" \ --argjson amt_usdc_4600 "$USDC_4600_POS" \ '[ { blockType:"Flashloan", protocol:"utility", display:"Utility flashloan", tokenIn:{symbol:"USDC", address:$usdc, amount:$amt_usdc_4600} }, { blockType:"Supply", protocol:"aavev3", display:"Aave V3", tokenIn:{symbol:"USDC", address:$usdc, amount:$amt_usdc_4600}, tokenOut:{symbol:"aEthUSDC", address:$aethusdc, amount:$amt_usdc_4600} }, { blockType:"Borrow", protocol:"aavev3", display:"Aave V3", tokenOut:{symbol:"USDT", address:$usdt, amount:$amt_usdt_2500} }, { blockType:"Swap", protocol:"paraswapv5", display:"Paraswap V5", tokenIn:{symbol:"USDT", address:$usdt, amount:$amt_usdt_2000_9}, tokenOut:{symbol:"USDC", address:$usdc, minAmount:$amt_usdc_2001_033032} }, { blockType:"Repay", protocol:"aavev3", display:"Aave V3", tokenIn:{symbol:"USDC", address:$usdc, amount:$amt_usdc_1000} }, { blockType:"Supply", protocol:"aavev3", display:"Aave V3", tokenIn:{symbol:"USDC", address:$usdc, amount:$amt_usdc_1000}, tokenOut:{symbol:"aEthUSDC", address:$aethusdc, amount:$amt_usdc_1000} }, { blockType:"Borrow", protocol:"aavev3", display:"Aave V3", tokenOut:{symbol:"USDT", address:$usdt, amount:$amt_usdt_2300} }, { blockType:"Swap", protocol:"paraswapv5", display:"Paraswap V5", tokenIn:{symbol:"USDT", address:$usdt, amount:$amt_usdt_2100_9}, tokenOut:{symbol:"USDC", address:$usdc, minAmount:$amt_usdc_2100_628264} }, { blockType:"Repay", protocol:"aavev3", display:"Aave V3", tokenIn:{symbol:"USDC", address:$usdc, amount:$amt_usdc_1000} }, { blockType:"Supply", protocol:"aavev3", display:"Aave V3", tokenIn:{symbol:"USDC", address:$usdc, amount:$amt_usdc_1000}, tokenOut:{symbol:"aEthUSDC", address:$aethusdc, amount:$amt_usdc_1000} }, { blockType:"Withdraw", protocol:"aavev3", display:"Aave V3", tokenIn:{symbol:"aEthUSDC", address:$aethusdc, amount:$amt_usdc_4500}, tokenOut:{symbol:"USDC", address:$usdc, amount:$amt_usdc_4500} }, { blockType:"FlashloanRepay", protocol:"utility", display:"Utility flashloan", tokenIn:{symbol:"USDC", address:$usdc, amount:$amt_usdc_4600} } ]' | jq '.' > "$PLAN_FILE" echo "Plan written to $PLAN_FILE" >&2 cat "$PLAN_FILE" } show_plan() { require_jq [[ -f "$PLAN_FILE" ]] || die "No plan file at $PLAN_FILE. Run: ./aaxe-cli.sh build-plan" jq '.' "$PLAN_FILE" } send_plan() { require_cast require_jq local RPC_URL="$RPC_URL_DEFAULT" local ROUTER="$AAXE_ROUTER_DEFAULT" local CHAIN_ID="$CHAIN_ID_DEFAULT" while [[ $# -gt 0 ]]; do case "$1" in --rpc) RPC_URL="$2"; shift 2;; --router) ROUTER="$2"; shift 2;; --chain-id) CHAIN_ID="$2"; shift 2;; *) die "Unknown arg: $1";; esac done [[ -n "${PRIVATE_KEY:-}" ]] || die "PRIVATE_KEY not set" [[ -f "$PLAN_FILE" ]] || die "No plan file at $PLAN_FILE. Run build-plan first." # Encode plan.json as bytes (hex) for execute(bytes) PLAN_JSON_MINIFIED="$(jq -c '.' "$PLAN_FILE")" PLAN_HEX="0x$(printf '%s' "$PLAN_JSON_MINIFIED" | xxd -p -c 100000 | tr -d '\n')" echo "Sending to router: $ROUTER" echo "RPC: $RPC_URL" echo "Chain ID: $CHAIN_ID" echo "Method: execute(bytes)" echo "Data bytes length: ${#PLAN_HEX}" # NOTE: Adjust function signature if your router differs. cast send \ --rpc-url "$RPC_URL" \ --private-key "$PRIVATE_KEY" \ --legacy \ "$ROUTER" \ "execute(bytes)" "$PLAN_HEX" } case "${1:-}" in build-plan) build_plan ;; show-plan) show_plan ;; send) shift; send_plan "$@" ;; ""|-h|--help) cat < plan.json ./aaxe-cli.sh show-plan PRIVATE_KEY=... ./aaxe-cli.sh send --rpc $RPC_URL_DEFAULT --router $AAXE_ROUTER_DEFAULT Edit addresses at the top of the script to match your network. EOF ;; *) die "Unknown command: ${1:-}. Try --help" ;; esac