#!/usr/bin/env bash set -euo pipefail # Run one manager-backed Mainnet quote-push cycle: # flash quote-push -> harvest retained quote into manager -> distribute to configured recipients. # # Default: simulation only. Use --apply to broadcast. # # Required env: # PRIVATE_KEY, ETHEREUM_MAINNET_RPC # DODO_PMM_INTEGRATION_MAINNET # QUOTE_PUSH_TREASURY_MANAGER_MAINNET # AAVE_QUOTE_PUSH_RECEIVER_MAINNET optional; defaults to latest broadcast or canonical receiver # QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET optional; can be auto-picked from latest broadcast # FLASH_QUOTE_AMOUNT_RAW optional; defaults to 200000 # # Optional treasury env: # QUOTE_PUSH_TREASURY_HARVEST optional; defaults to 1 # QUOTE_PUSH_TREASURY_GAS_HOLDBACK_TARGET_RAW optional; defaults to 0 # # Usage: # bash scripts/deployment/run-mainnet-aave-quote-push-managed-cycle.sh --dry-run # QUOTE_PUSH_TREASURY_GAS_HOLDBACK_TARGET_RAW=0 bash scripts/deployment/run-mainnet-aave-quote-push-managed-cycle.sh --apply SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROXMOX_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" SMOM="${PROXMOX_ROOT}/smom-dbis-138" DEFAULT_AAVE_QUOTE_PUSH_RECEIVER_MAINNET="0x241cb416aaFC2654078b7E2376adED2bDeFbCBa2" DEFAULT_POOL_CWUSDC_USDC_MAINNET="0x69776fc607e9edA8042e320e7e43f54d06c68f0E" _qp_private_key="${PRIVATE_KEY-}" _qp_rpc="${ETHEREUM_MAINNET_RPC-}" _qp_receiver="${AAVE_QUOTE_PUSH_RECEIVER_MAINNET-}" _qp_manager="${QUOTE_PUSH_TREASURY_MANAGER_MAINNET-}" _qp_unwinder="${QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET-}" _qp_amount="${FLASH_QUOTE_AMOUNT_RAW-}" _qp_unwind_type="${QUOTE_PUSH_UNWINDER_TYPE-}" _qp_unwind_mode="${UNWIND_MODE-}" _qp_pool="${POOL_CWUSDC_USDC_MAINNET-}" _qp_integration="${DODO_PMM_INTEGRATION_MAINNET-}" _qp_pool_a="${UNWIND_TWO_HOP_POOL_A-}" _qp_pool_b="${UNWIND_TWO_HOP_POOL_B-}" _qp_mid_token="${UNWIND_TWO_HOP_MID_TOKEN-}" _qp_min_mid_out="${UNWIND_MIN_MID_OUT_RAW-}" _qp_min_out_pmm="${MIN_OUT_PMM-}" _qp_min_out_unwind="${MIN_OUT_UNWIND-}" _qp_min_out_unwind_buffer="${MIN_OUT_UNWIND_BUFFER_RAW-}" _qp_fee_u24="${UNWIND_V3_FEE_U24-}" _qp_dodo_pool="${UNWIND_DODO_POOL-}" _qp_v3_path="${UNWIND_V3_PATH_HEX-}" _qp_intermediate_token="${UNWIND_INTERMEDIATE_TOKEN-}" _qp_min_intermediate_out="${UNWIND_MIN_INTERMEDIATE_OUT_RAW-}" _qp_treasury_harvest="${QUOTE_PUSH_TREASURY_HARVEST-}" _qp_treasury_holdback="${QUOTE_PUSH_TREASURY_GAS_HOLDBACK_TARGET_RAW-}" # shellcheck disable=SC1091 source "${PROXMOX_ROOT}/scripts/lib/load-project-env.sh" 2>/dev/null || true # shellcheck disable=SC1091 source "${SMOM}/scripts/load-env.sh" >/dev/null 2>&1 || true [[ -n "$_qp_private_key" ]] && export PRIVATE_KEY="$_qp_private_key" [[ -n "$_qp_rpc" ]] && export ETHEREUM_MAINNET_RPC="$_qp_rpc" [[ -n "$_qp_receiver" ]] && export AAVE_QUOTE_PUSH_RECEIVER_MAINNET="$_qp_receiver" [[ -n "$_qp_manager" ]] && export QUOTE_PUSH_TREASURY_MANAGER_MAINNET="$_qp_manager" [[ -n "$_qp_unwinder" ]] && export QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET="$_qp_unwinder" [[ -n "$_qp_amount" ]] && export FLASH_QUOTE_AMOUNT_RAW="$_qp_amount" [[ -n "$_qp_unwind_type" ]] && export QUOTE_PUSH_UNWINDER_TYPE="$_qp_unwind_type" [[ -n "$_qp_unwind_mode" ]] && export UNWIND_MODE="$_qp_unwind_mode" [[ -n "$_qp_pool" ]] && export POOL_CWUSDC_USDC_MAINNET="$_qp_pool" [[ -n "$_qp_integration" ]] && export DODO_PMM_INTEGRATION_MAINNET="$_qp_integration" [[ -n "$_qp_pool_a" ]] && export UNWIND_TWO_HOP_POOL_A="$_qp_pool_a" [[ -n "$_qp_pool_b" ]] && export UNWIND_TWO_HOP_POOL_B="$_qp_pool_b" [[ -n "$_qp_mid_token" ]] && export UNWIND_TWO_HOP_MID_TOKEN="$_qp_mid_token" [[ -n "$_qp_min_mid_out" ]] && export UNWIND_MIN_MID_OUT_RAW="$_qp_min_mid_out" [[ -n "$_qp_min_out_pmm" ]] && export MIN_OUT_PMM="$_qp_min_out_pmm" [[ -n "$_qp_min_out_unwind" ]] && export MIN_OUT_UNWIND="$_qp_min_out_unwind" [[ -n "$_qp_min_out_unwind_buffer" ]] && export MIN_OUT_UNWIND_BUFFER_RAW="$_qp_min_out_unwind_buffer" [[ -n "$_qp_fee_u24" ]] && export UNWIND_V3_FEE_U24="$_qp_fee_u24" [[ -n "$_qp_dodo_pool" ]] && export UNWIND_DODO_POOL="$_qp_dodo_pool" [[ -n "$_qp_v3_path" ]] && export UNWIND_V3_PATH_HEX="$_qp_v3_path" [[ -n "$_qp_intermediate_token" ]] && export UNWIND_INTERMEDIATE_TOKEN="$_qp_intermediate_token" [[ -n "$_qp_min_intermediate_out" ]] && export UNWIND_MIN_INTERMEDIATE_OUT_RAW="$_qp_min_intermediate_out" [[ -n "$_qp_treasury_harvest" ]] && export QUOTE_PUSH_TREASURY_HARVEST="$_qp_treasury_harvest" [[ -n "$_qp_treasury_holdback" ]] && export QUOTE_PUSH_TREASURY_GAS_HOLDBACK_TARGET_RAW="$_qp_treasury_holdback" unset _qp_private_key _qp_rpc _qp_receiver _qp_manager _qp_unwinder _qp_amount _qp_unwind_type _qp_unwind_mode unset _qp_pool _qp_integration _qp_pool_a _qp_pool_b _qp_mid_token _qp_min_mid_out _qp_min_out_pmm unset _qp_min_out_unwind _qp_min_out_unwind_buffer _qp_fee_u24 _qp_dodo_pool _qp_v3_path unset _qp_intermediate_token _qp_min_intermediate_out unset _qp_treasury_harvest _qp_treasury_holdback require_cmd() { command -v "$1" >/dev/null 2>&1 || { echo "[fail] missing required command: $1" >&2 exit 1 } } BROADCAST=() if (($# == 0)); then : else for a in "$@"; do case "$a" in --apply) BROADCAST=(--broadcast) ;; --dry-run) BROADCAST=() ;; *) echo "[fail] unknown arg: $a" >&2 exit 2 ;; esac done fi require_cmd cast require_cmd forge require_cmd mktemp require() { local n="$1" if [[ -z "${!n:-}" ]]; then echo "[fail] missing required env: $n" >&2 exit 1 fi } pick_latest_create_address() { local script_name="$1" local contract_name="$2" local latest_json="${SMOM}/broadcast/${script_name}/1/run-latest.json" if [[ ! -f "$latest_json" ]]; then return 1 fi if ! command -v jq >/dev/null 2>&1; then return 1 fi jq -r --arg contract "$contract_name" \ '.transactions[]? | select(.transactionType == "CREATE" and .contractName == $contract) | .contractAddress' \ "$latest_json" | tail -n1 } pick_default_unwinder() { local addr="" PICK_DEFAULT_UNWINDER_ADDR="" PICK_DEFAULT_UNWINDER_MODE="" addr="$(pick_latest_create_address "DeployTwoHopDodoIntegrationUnwinder.s.sol" "TwoHopDodoIntegrationUnwinder" || true)" if [[ -n "$addr" && "$addr" != "null" ]]; then PICK_DEFAULT_UNWINDER_ADDR="$addr" PICK_DEFAULT_UNWINDER_MODE="4" return 0 fi addr="$(pick_latest_create_address "DeployDODOIntegrationExternalUnwinder.s.sol" "DODOIntegrationExternalUnwinder" || true)" if [[ -n "$addr" && "$addr" != "null" ]]; then PICK_DEFAULT_UNWINDER_ADDR="$addr" PICK_DEFAULT_UNWINDER_MODE="1" return 0 fi addr="$(pick_latest_create_address "DeployUniswapV3ExternalUnwinder.s.sol" "UniswapV3ExternalUnwinder" || true)" if [[ -n "$addr" && "$addr" != "null" ]]; then PICK_DEFAULT_UNWINDER_ADDR="$addr" PICK_DEFAULT_UNWINDER_MODE="0" return 0 fi addr="$(pick_latest_create_address "DeployDODOToUniswapV3MultiHopExternalUnwinder.s.sol" "DODOToUniswapV3MultiHopExternalUnwinder" || true)" if [[ -n "$addr" && "$addr" != "null" ]]; then PICK_DEFAULT_UNWINDER_ADDR="$addr" PICK_DEFAULT_UNWINDER_MODE="5" return 0 fi addr="$(pick_latest_create_address "DeployTwoHopDodoToUniswapV3MultiHopExternalUnwinder.s.sol" "TwoHopDodoToUniswapV3MultiHopExternalUnwinder" || true)" if [[ -n "$addr" && "$addr" != "null" ]]; then PICK_DEFAULT_UNWINDER_ADDR="$addr" PICK_DEFAULT_UNWINDER_MODE="6" return 0 fi return 1 } UNW="${QUOTE_PUSH_UNWINDER_TYPE:-}" if [[ -z "${AAVE_QUOTE_PUSH_RECEIVER_MAINNET:-}" ]]; then if [[ -n "${QUOTE_PUSH_TREASURY_MANAGER_MAINNET:-}" && -n "${ETHEREUM_MAINNET_RPC:-}" ]]; then manager_receiver="$(cast call "$QUOTE_PUSH_TREASURY_MANAGER_MAINNET" 'receiver()(address)' --rpc-url "$ETHEREUM_MAINNET_RPC" 2>/dev/null | awk '{print $1}' || true)" if [[ -n "$manager_receiver" && "$manager_receiver" != "0x0000000000000000000000000000000000000000" ]]; then export AAVE_QUOTE_PUSH_RECEIVER_MAINNET="$manager_receiver" fi fi fi if [[ -z "${AAVE_QUOTE_PUSH_RECEIVER_MAINNET:-}" ]]; then inferred_receiver="$(pick_latest_create_address "DeployAaveQuotePushFlashReceiver.s.sol" "AaveQuotePushFlashReceiver" || true)" export AAVE_QUOTE_PUSH_RECEIVER_MAINNET="${inferred_receiver:-$DEFAULT_AAVE_QUOTE_PUSH_RECEIVER_MAINNET}" fi if [[ -z "${POOL_CWUSDC_USDC_MAINNET:-}" || "${_qp_pool:-}" == "" ]]; then export POOL_CWUSDC_USDC_MAINNET="$DEFAULT_POOL_CWUSDC_USDC_MAINNET" fi if [[ -z "${FLASH_QUOTE_AMOUNT_RAW:-}" ]]; then export FLASH_QUOTE_AMOUNT_RAW=200000 fi if [[ -z "${MIN_OUT_PMM:-}" ]]; then export MIN_OUT_PMM=1 fi if [[ -z "${QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET:-}" && -n "$UNW" ]]; then unwind_script="" unwind_contract="" case "$UNW" in univ3) unwind_script="DeployUniswapV3ExternalUnwinder.s.sol" unwind_contract="UniswapV3ExternalUnwinder" export UNWIND_MODE="${UNWIND_MODE:-0}" ;; dodo) unwind_script="DeployDODOIntegrationExternalUnwinder.s.sol" unwind_contract="DODOIntegrationExternalUnwinder" export UNWIND_MODE="${UNWIND_MODE:-1}" ;; two_hop_dodo) unwind_script="DeployTwoHopDodoIntegrationUnwinder.s.sol" unwind_contract="TwoHopDodoIntegrationUnwinder" export UNWIND_MODE="${UNWIND_MODE:-4}" ;; dodo_univ3) unwind_script="DeployDODOToUniswapV3MultiHopExternalUnwinder.s.sol" unwind_contract="DODOToUniswapV3MultiHopExternalUnwinder" export UNWIND_MODE="${UNWIND_MODE:-5}" ;; two_hop_dodo_univ3) unwind_script="DeployTwoHopDodoToUniswapV3MultiHopExternalUnwinder.s.sol" unwind_contract="TwoHopDodoToUniswapV3MultiHopExternalUnwinder" export UNWIND_MODE="${UNWIND_MODE:-6}" ;; *) echo "[fail] QUOTE_PUSH_UNWINDER_TYPE must be univ3, dodo, two_hop_dodo, dodo_univ3, or two_hop_dodo_univ3 when set" >&2 exit 1 ;; esac inferred_unwinder="$(pick_latest_create_address "$unwind_script" "$unwind_contract" || true)" if [[ -n "$inferred_unwinder" && "$inferred_unwinder" != "null" ]]; then export QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET="$inferred_unwinder" fi fi if [[ -z "${QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET:-}" ]]; then if pick_default_unwinder; then if [[ -n "$PICK_DEFAULT_UNWINDER_ADDR" && "$PICK_DEFAULT_UNWINDER_ADDR" != "null" ]]; then export QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET="$PICK_DEFAULT_UNWINDER_ADDR" export UNWIND_MODE="$PICK_DEFAULT_UNWINDER_MODE" fi fi fi if [[ "${UNWIND_MODE:-}" == "4" ]]; then export UNWIND_TWO_HOP_POOL_A="${UNWIND_TWO_HOP_POOL_A:-0xe944b7Cb012A0820c07f54D51e92f0e1C74168DB}" export UNWIND_TWO_HOP_POOL_B="${UNWIND_TWO_HOP_POOL_B:-0x27f3aE7EE71Be3d77bAf17d4435cF8B895DD25D2}" export UNWIND_TWO_HOP_MID_TOKEN="${UNWIND_TWO_HOP_MID_TOKEN:-0xaF5017d0163ecb99d9B5D94e3b4D7b09Af44D8AE}" export UNWIND_MIN_MID_OUT_RAW="${UNWIND_MIN_MID_OUT_RAW:-1}" elif [[ "${UNWIND_MODE:-}" == "5" ]]; then export UNWIND_DODO_POOL="${UNWIND_DODO_POOL:-0xCC0fd27A40775c9AfcD2BBd3f7c902b0192c247A}" export UNWIND_INTERMEDIATE_TOKEN="${UNWIND_INTERMEDIATE_TOKEN:-0xdAC17F958D2ee523a2206206994597C13D831ec7}" export UNWIND_MIN_INTERMEDIATE_OUT_RAW="${UNWIND_MIN_INTERMEDIATE_OUT_RAW:-1}" elif [[ "${UNWIND_MODE:-}" == "6" ]]; then export UNWIND_TWO_HOP_POOL_A="${UNWIND_TWO_HOP_POOL_A:-0xe944b7Cb012A0820c07f54D51e92f0e1C74168DB}" export UNWIND_TWO_HOP_POOL_B="${UNWIND_TWO_HOP_POOL_B:-0x79156F6B7bf71a1B72D78189B540A89A6C13F6FC}" export UNWIND_TWO_HOP_MID_TOKEN="${UNWIND_TWO_HOP_MID_TOKEN:-0xaF5017d0163ecb99d9B5D94e3b4D7b09Af44D8AE}" export UNWIND_INTERMEDIATE_TOKEN="${UNWIND_INTERMEDIATE_TOKEN:-0xdAC17F958D2ee523a2206206994597C13D831ec7}" export UNWIND_MIN_MID_OUT_RAW="${UNWIND_MIN_MID_OUT_RAW:-1}" export UNWIND_MIN_INTERMEDIATE_OUT_RAW="${UNWIND_MIN_INTERMEDIATE_OUT_RAW:-1}" export MIN_OUT_UNWIND_BUFFER_RAW="${MIN_OUT_UNWIND_BUFFER_RAW:-0}" fi require ETHEREUM_MAINNET_RPC require PRIVATE_KEY require QUOTE_PUSH_TREASURY_MANAGER_MAINNET require AAVE_QUOTE_PUSH_RECEIVER_MAINNET require QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET require DODO_PMM_INTEGRATION_MAINNET require FLASH_QUOTE_AMOUNT_RAW manager_receiver="$(cast call "$QUOTE_PUSH_TREASURY_MANAGER_MAINNET" 'receiver()(address)' --rpc-url "$ETHEREUM_MAINNET_RPC" 2>/dev/null | awk '{print $1}' || true)" if [[ -z "$manager_receiver" ]]; then echo "[fail] could not resolve receiver() from manager $QUOTE_PUSH_TREASURY_MANAGER_MAINNET" >&2 exit 1 fi if [[ "${manager_receiver,,}" != "${AAVE_QUOTE_PUSH_RECEIVER_MAINNET,,}" ]]; then cat >&2 <&2 exit 1 fi echo "receiver=$AAVE_QUOTE_PUSH_RECEIVER_MAINNET" echo "manager=$QUOTE_PUSH_TREASURY_MANAGER_MAINNET" echo "unwinder=$QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET" echo "flash_quote_amount_raw=$FLASH_QUOTE_AMOUNT_RAW unwind_mode=$UM" echo "gas_holdback_target_raw=${QUOTE_PUSH_TREASURY_GAS_HOLDBACK_TARGET_RAW:-0}" tmp_output="$(mktemp)" set +e ( cd "$SMOM" forge script script/flash/RunManagedMainnetAaveCwusdcUsdcQuotePushCycle.s.sol:RunManagedMainnetAaveCwusdcUsdcQuotePushCycle \ --rpc-url "$ETHEREUM_MAINNET_RPC" \ "${BROADCAST[@]}" \ -vvvv ) 2>&1 | tee "$tmp_output" status=${PIPESTATUS[0]} set -e if [[ "$status" -ne 0 ]]; then rm -f "$tmp_output" exit "$status" fi managed_harvested_raw="$(sed -nE 's/.*managedCycleHarvestedRaw", ([0-9]+).*/\1/p' "$tmp_output" | tail -n1)" managed_gas_distribution_raw="$(sed -nE 's/.*managedCycleGasDistributionRaw", ([0-9]+).*/\1/p' "$tmp_output" | tail -n1)" managed_recycle_distribution_raw="$(sed -nE 's/.*managedCycleRecycleDistributionRaw", ([0-9]+).*/\1/p' "$tmp_output" | tail -n1)" estimated_eth_required="$(sed -nE 's/Estimated amount required: ([0-9.]+) ETH/\1/p' "$tmp_output" | tail -n1)" estimated_total_gas="$(sed -nE 's/Estimated total gas used for script: ([0-9]+)/\1/p' "$tmp_output" | tail -n1)" if [[ -n "$managed_recycle_distribution_raw" || -n "$estimated_eth_required" || -n "$estimated_total_gas" ]]; then echo "MANAGED_CYCLE_SUMMARY harvested_raw=${managed_harvested_raw:-0} gas_distribution_raw=${managed_gas_distribution_raw:-0} recycle_distribution_raw=${managed_recycle_distribution_raw:-0} estimated_eth_required=${estimated_eth_required:-0} estimated_total_gas=${estimated_total_gas:-0}" fi rm -f "$tmp_output"