Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions packages/ethereum-contracts/ops-scripts/deploy-yield-backend.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#!/usr/bin/env bash
set -eu
set -o pipefail

# Usage:
# ops-scripts/deploy-yield-backend.sh <network> aave <underlyingToken> <aavePool> <surplusReceiver>
# ops-scripts/deploy-yield-backend.sh <network> spark <vault> <surplusReceiver> <referralId>
#
# Deploys a yield backend contract. Network is the canonical name from metadata (e.g. base-mainnet, eth-mainnet).
#
# For aave, <underlyingToken> is the SuperToken's underlying asset. Use address(0) for the native token
# (e.g. ETHx) to deploy AaveETHYieldBackend; any other address deploys AaveYieldBackend for that ERC-20.
#
# Examples:
# # AaveYieldBackend for USDC on Base
# ops-scripts/deploy-yield-backend.sh base-mainnet aave 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 0xA238Dd80C259a72e81d7e4664a9801593F98d1c5 0xac808840f02c47C05507f48165d2222FF28EF4e1
#
# # AaveETHYieldBackend for native underlying (ETHx) on Base
# ops-scripts/deploy-yield-backend.sh base-mainnet aave 0x0000000000000000000000000000000000000000 0xA238Dd80C259a72e81d7e4664a9801593F98d1c5 0xac808840f02c47C05507f48165d2222FF28EF4e1
#
# # SparkYieldBackend for USDC on Ethereum
# ops-scripts/deploy-yield-backend.sh eth-mainnet spark 0xBc65ad17c5C0a2A4D159fa5a503f4992c7B545FE 0xac808840f02c47C05507f48165d2222FF28EF4e1 42
#
# ENV: RPC_URL, PROVIDER_URL_OVERRIDE, or PROVIDER_URL_TEMPLATE (with {{NETWORK}}) for RPC; metadata publicRPCs as fallback.
# WALLET_NAME (default: sf-ops), ETHERSCAN_API_V2_KEY for verification.
# SIMULATE=1 to run without --broadcast (simulate only, no on-chain tx).
#
# Requires: jq, forge. Run from packages/ethereum-contracts or repo root.

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PKG_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
METADATA_JSON="${METADATA_JSON:-$PKG_ROOT/../metadata/networks.json}"

# shellcheck source=/dev/null
[[ -f "$PKG_ROOT/.env" ]] && source "$PKG_ROOT/.env"
# shellcheck source=/dev/null
[[ -f "$PKG_ROOT/../.env" ]] && source "$PKG_ROOT/../.env"
# shellcheck source=/dev/null
[[ -f "$SCRIPT_DIR/lib/network-config.sh" ]] && source "$SCRIPT_DIR/lib/network-config.sh"

network="${1:?Usage: $0 <network> aave|spark <args...>}"
backend_type="${2:?Usage: $0 <network> aave|spark <args...>}"

if [[ ! -f "$METADATA_JSON" ]]; then
echo "Metadata not found: $METADATA_JSON" >&2
exit 1
fi

rpc=$(get_rpc_url "$network") || exit 1
chain_id=$(get_chain_id "$network")
if [[ -z "$chain_id" || "$chain_id" == "null" ]]; then
echo "No chainId for network: $network" >&2
exit 1
fi

cd "$PKG_ROOT"

tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"' EXIT

forge_args=(--rpc-url "$rpc")
if [[ -n "${SIMULATE:-}" ]]; then
# Simulation: use well-known test key to avoid keystore access
forge_args+=(--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80)
else
forge_args+=(--account "${WALLET_NAME:-sf-ops}" --broadcast)
fi

if [[ -z "${SIMULATE:-}" && -n "${ETHERSCAN_API_V2_KEY:-}" ]]; then
echo "Verification: enabled"
forge_args+=(
--verify
--verifier etherscan
--etherscan-api-key "$ETHERSCAN_API_V2_KEY"
--chain-id "$chain_id"
--delay 10
--retries 15
)
else
echo "Verification: skipped (ETHERSCAN_API_V2_KEY not set)"
fi

case "$backend_type" in
aave)
underlying_token="${3:?Usage: $0 <network> aave <underlyingToken> <aavePool> <surplusReceiver>}"
aave_pool="${4:?Usage: $0 <network> aave <underlyingToken> <aavePool> <surplusReceiver>}"
surplus_receiver="${5:?Usage: $0 <network> aave <underlyingToken> <aavePool> <surplusReceiver>}"
zero_underlying=0x0000000000000000000000000000000000000000
echo "Network: $network"
echo "RPC: $rpc"
if [[ "${underlying_token,,}" == "${zero_underlying}" ]]; then
echo "Backend: AaveETHYieldBackend (underlying = native token)"
echo " aavePool: $aave_pool"
echo " surplusReceiver: $surplus_receiver"
echo ""
forge script scripts/DeployYieldBackend.s.sol:DeployYieldBackend \
"${forge_args[@]}" \
--sig "runAaveETH(address,address)" "$aave_pool" "$surplus_receiver" | tee "$tmpfile"
else
echo "Backend: AaveYieldBackend"
echo " underlyingToken: $underlying_token"
echo " aavePool: $aave_pool"
echo " surplusReceiver: $surplus_receiver"
echo ""
forge script scripts/DeployYieldBackend.s.sol:DeployYieldBackend \
"${forge_args[@]}" \
--sig "runAave(address,address,address)" "$underlying_token" "$aave_pool" "$surplus_receiver" | tee "$tmpfile"
fi
contract_addr=$(grep -oE '0x[a-fA-F0-9]{40}' "$tmpfile" | tail -n 1)
;;
spark)
vault="${3:?Usage: $0 <network> spark <vault> <surplusReceiver> <referralId>}"
surplus_receiver="${4:?Usage: $0 <network> spark <vault> <surplusReceiver> <referralId>}"
referral_id="${5:?Usage: $0 <network> spark <vault> <surplusReceiver> <referralId>}"
echo "Network: $network"
echo "Backend: SparkYieldBackend"
echo "RPC: $rpc"
echo ""
forge script scripts/DeployYieldBackend.s.sol:DeployYieldBackend \
"${forge_args[@]}" \
--sig "runSpark(address,address,uint16)" "$vault" "$surplus_receiver" "$referral_id" | tee "$tmpfile"
contract_addr=$(grep -oE '0x[a-fA-F0-9]{40}' "$tmpfile" | tail -n 1)
;;
*)
echo "Unknown backend type: $backend_type (use aave or spark)" >&2
exit 1
;;
esac

if [[ -z "${contract_addr:-}" ]]; then
echo "Could not determine deployed contract address from forge output" >&2
exit 1
fi

echo ""
echo "Deployed to $contract_addr"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
# Convenience wrapper for super-token-admin-action.sh disableYieldBackend.
# Usage: disable-yield-backend.sh <network> <superToken> [adminOverride]
# adminOverride: optional; use when on-chain admin not yet set (e.g. pending gov action)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
[ -n "${3:-}" ] && export SUPER_TOKEN_ADMIN_OVERRIDE="$3"
exec "$SCRIPT_DIR/super-token-admin-action.sh" "$1" disableYieldBackend "$2"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
# Convenience wrapper for super-token-admin-action.sh enableYieldBackend.
# Usage: enable-yield-backend.sh <network> <superToken> <yieldBackend> [adminOverride]
# adminOverride: optional; use when on-chain admin not yet set (e.g. pending gov action)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
[ -n "${4:-}" ] && export SUPER_TOKEN_ADMIN_OVERRIDE="$4"
exec "$SCRIPT_DIR/super-token-admin-action.sh" "$1" enableYieldBackend "$2" "$3"
236 changes: 236 additions & 0 deletions packages/ethereum-contracts/ops-scripts/gov-action.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
#!/bin/bash
#
# Governance actions via Foundry. When the governance admin is a Safe, outputs transaction
# payload and optionally proposes it via propose-safe-tx.ts (requires SAFE_PROPOSER_PK in .env).
# Optional: SAFE_API_KEY for higher rate limits, SAFE_ORIGIN for proposal label.
# SIMULATE=1 to skip broadcast and Safe proposal (encode only).
#
set -e
set -o pipefail

SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
PKG_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
METADATA_JSON="${METADATA_JSON:-$PKG_ROOT/../metadata/networks.json}"

# shellcheck source=/dev/null
[ -f "$PKG_ROOT/.env" ] && . "$PKG_ROOT/.env"
# shellcheck source=/dev/null
[ -f "$PKG_ROOT/../.env" ] && . "$PKG_ROOT/../.env"
# shellcheck source=/dev/null
[ -f "$SCRIPT_DIR/lib/network-config.sh" ] && . "$SCRIPT_DIR/lib/network-config.sh"
# shellcheck source=/dev/null
[ -f "$SCRIPT_DIR/lib/safe-tx.sh" ] && . "$SCRIPT_DIR/lib/safe-tx.sh"

# read the network name and action type from the command line
NETWORK=$1
ACTION_TYPE=$2

# name of the foundry wallet account to be used
WALLET_NAME=${WALLET_NAME:-sf-ops}

# Validate arguments
if [ -z "$NETWORK" ]; then
echo "Error: Network name is required"
echo "Usage: $0 <NETWORK> <ACTION_TYPE> [ACTION_ARGS...]"
echo ""
echo "Supported action types:"
echo " setTokenMinimumDeposit <TOKEN_ADDRESS> <MINIMUM_DEPOSIT>"
echo " set3PsConfig <TOKEN_ADDRESS> <LIQUIDATION_PERIOD> <PATRICIAN_PERIOD>"
echo " clear3PsConfig <TOKEN_ADDRESS>"
echo " setRewardAddress <TOKEN_ADDRESS> <REWARD_ADDRESS>"
echo " clearRewardAddress <TOKEN_ADDRESS>"
echo " enableTrustedForwarder <TOKEN_ADDRESS> <FORWARDER_ADDRESS>"
echo " disableTrustedForwarder <TOKEN_ADDRESS> <FORWARDER_ADDRESS>"
echo " changeSuperTokenAdmin <TOKEN_ADDRESS> <NEW_ADMIN>"
echo " batchChangeSuperTokenAdmin <TOKEN_ADDRESSES_JSON> <NEW_ADMINS_JSON>"
echo " registerAgreementClass <AGREEMENT_CLASS>"
echo " replaceGovernance <NEW_GOVERNANCE>"
exit 1
fi

if [ -z "$ACTION_TYPE" ]; then
echo "Error: Action type is required"
echo "Usage: $0 <NETWORK> <ACTION_TYPE> [ACTION_ARGS...]"
exit 1
fi

if ! jq -e --arg n "$NETWORK" 'any(.[]; .name == $n)' "$METADATA_JSON" >/dev/null 2>&1; then
echo "Network $NETWORK not found in networks.json" >&2
exit 1
fi

PROVIDER_URL=$(get_rpc_url "$NETWORK") || exit 1
echo "Using RPC: $PROVIDER_URL"

echo "Using WALLET: $WALLET_NAME"

HOST_ADDRESS=$(get_host "$NETWORK")
if [[ -z "$HOST_ADDRESS" || "$HOST_ADDRESS" == "null" || ! "$HOST_ADDRESS" =~ ^0x[a-fA-F0-9]{40}$ ]]; then
echo "HOST_ADDRESS is not a valid Ethereum address" >&2
exit 1
fi

echo "Using HOST_ADDRESS: $HOST_ADDRESS"
echo "Using ACTION_TYPE: $ACTION_TYPE"

# Parse action-specific arguments based on action type
case "$ACTION_TYPE" in
setTokenMinimumDeposit)
if [ $# -lt 4 ]; then
echo "Error: setTokenMinimumDeposit requires TOKEN_ADDRESS and MINIMUM_DEPOSIT"
exit 1
fi
TOKEN_ADDRESS=$3
MINIMUM_DEPOSIT=$4
if [[ ! "$TOKEN_ADDRESS" =~ ^0x[a-fA-F0-9]{40}$ ]]; then
echo "Error: TOKEN_ADDRESS is not a valid Ethereum address"
exit 1
fi
export TOKEN_ADDRESS
export MINIMUM_DEPOSIT
echo "Using TOKEN_ADDRESS: $TOKEN_ADDRESS"
echo "Using MINIMUM_DEPOSIT: $MINIMUM_DEPOSIT"
;;
set3PsConfig)
if [ $# -lt 5 ]; then
echo "Error: set3PsConfig requires TOKEN_ADDRESS, LIQUIDATION_PERIOD, and PATRICIAN_PERIOD"
exit 1
fi
TOKEN_ADDRESS=$3
LIQUIDATION_PERIOD=$4
PATRICIAN_PERIOD=$5
if [[ ! "$TOKEN_ADDRESS" =~ ^0x[a-fA-F0-9]{40}$ ]]; then
echo "Error: TOKEN_ADDRESS is not a valid Ethereum address"
exit 1
fi
export TOKEN_ADDRESS
export LIQUIDATION_PERIOD
export PATRICIAN_PERIOD
echo "Using TOKEN_ADDRESS: $TOKEN_ADDRESS"
echo "Using LIQUIDATION_PERIOD: $LIQUIDATION_PERIOD"
echo "Using PATRICIAN_PERIOD: $PATRICIAN_PERIOD"
;;
clear3PsConfig|clearRewardAddress)
if [ $# -lt 3 ]; then
echo "Error: $ACTION_TYPE requires TOKEN_ADDRESS"
exit 1
fi
TOKEN_ADDRESS=$3
if [[ ! "$TOKEN_ADDRESS" =~ ^0x[a-fA-F0-9]{40}$ ]]; then
echo "Error: TOKEN_ADDRESS is not a valid Ethereum address"
exit 1
fi
export TOKEN_ADDRESS
echo "Using TOKEN_ADDRESS: $TOKEN_ADDRESS"
;;
setRewardAddress|enableTrustedForwarder|disableTrustedForwarder)
if [ $# -lt 4 ]; then
echo "Error: $ACTION_TYPE requires TOKEN_ADDRESS and ADDRESS"
exit 1
fi
TOKEN_ADDRESS=$3
ADDRESS=$4
if [[ ! "$TOKEN_ADDRESS" =~ ^0x[a-fA-F0-9]{40}$ ]]; then
echo "Error: TOKEN_ADDRESS is not a valid Ethereum address"
exit 1
fi
if [[ ! "$ADDRESS" =~ ^0x[a-fA-F0-9]{40}$ ]]; then
echo "Error: ADDRESS is not a valid Ethereum address"
exit 1
fi
export TOKEN_ADDRESS
if [ "$ACTION_TYPE" = "setRewardAddress" ]; then
export REWARD_ADDRESS=$ADDRESS
echo "Using TOKEN_ADDRESS: $TOKEN_ADDRESS"
echo "Using REWARD_ADDRESS: $ADDRESS"
else
export FORWARDER_ADDRESS=$ADDRESS
echo "Using TOKEN_ADDRESS: $TOKEN_ADDRESS"
echo "Using FORWARDER_ADDRESS: $ADDRESS"
fi
;;
changeSuperTokenAdmin)
if [ $# -lt 4 ]; then
echo "Error: changeSuperTokenAdmin requires TOKEN_ADDRESS and NEW_ADMIN"
exit 1
fi
TOKEN_ADDRESS=$3
NEW_ADMIN=$4
if [[ ! "$TOKEN_ADDRESS" =~ ^0x[a-fA-F0-9]{40}$ ]]; then
echo "Error: TOKEN_ADDRESS is not a valid Ethereum address"
exit 1
fi
if [[ ! "$NEW_ADMIN" =~ ^0x[a-fA-F0-9]{40}$ ]]; then
echo "Error: NEW_ADMIN is not a valid Ethereum address"
exit 1
fi
export TOKEN_ADDRESS
export NEW_ADMIN
echo "Using TOKEN_ADDRESS: $TOKEN_ADDRESS"
echo "Using NEW_ADMIN: $NEW_ADMIN"
;;
batchChangeSuperTokenAdmin)
if [ $# -lt 4 ]; then
echo "Error: batchChangeSuperTokenAdmin requires TOKEN_ADDRESSES_JSON and NEW_ADMINS_JSON"
exit 1
fi
TOKEN_ADDRESSES_JSON=$3
NEW_ADMINS_JSON=$4
export TOKEN_ADDRESSES_JSON
export NEW_ADMINS_JSON
echo "Using TOKEN_ADDRESSES_JSON: $TOKEN_ADDRESSES_JSON"
echo "Using NEW_ADMINS_JSON: $NEW_ADMINS_JSON"
;;
registerAgreementClass|replaceGovernance)
if [ $# -lt 3 ]; then
echo "Error: $ACTION_TYPE requires ADDRESS"
exit 1
fi
ADDRESS=$3
if [[ ! "$ADDRESS" =~ ^0x[a-fA-F0-9]{40}$ ]]; then
echo "Error: ADDRESS is not a valid Ethereum address"
exit 1
fi
if [ "$ACTION_TYPE" = "registerAgreementClass" ]; then
export AGREEMENT_CLASS=$ADDRESS
echo "Using AGREEMENT_CLASS: $ADDRESS"
else
export NEW_GOVERNANCE=$ADDRESS
echo "Using NEW_GOVERNANCE: $ADDRESS"
fi
;;
*)
echo "Error: Unknown action type: $ACTION_TYPE"
exit 1
;;
esac

# Run the Forge script
export HOST_ADDRESS
export ACTION_TYPE

cd "$PKG_ROOT"

# Build forge command with conditional flags
forge_args=(script scripts/GovAction.s.sol:GovAction --rpc-url "$PROVIDER_URL")
if [[ -n "${SIMULATE:-}" ]]; then
forge_args+=(--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80)
else
forge_args+=(--account "$WALLET_NAME" --broadcast --verify)
if [[ -n "${ETHERSCAN_API_KEY:-}" ]]; then
echo "ETHERSCAN_API_KEY is set"
forge_args+=(--etherscan-api-key "$ETHERSCAN_API_KEY")
else
echo "ETHERSCAN_API_KEY is not set"
fi
fi

SCRIPT_LOG=$(mktemp)
trap 'rm -f "$SCRIPT_LOG"' EXIT

if ! forge "${forge_args[@]}" | tee "$SCRIPT_LOG"; then
echo "Forge script failed"
exit 1
fi

handle_safe_tx_payloads "$SCRIPT_LOG" "$SCRIPT_DIR" "$PROVIDER_URL"
Loading
Loading