Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add upgrade scripts #94

Merged
merged 9 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from 8 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,5 @@ bin

# foundry
/out
/zkout
/cache-forge
Binary file modified bun.lockb
Binary file not shown.
2 changes: 1 addition & 1 deletion contracts/Disputer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
▓▓▓ ▓▓ ▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓
*/

pragma solidity 0.8.24;
pragma solidity ^0.8.24;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
Expand Down
28 changes: 15 additions & 13 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ auto_detect_solc = false
src = 'contracts'
out = 'out'
test = 'test'
libs = ["node_modules", "lib"]
libs = ["node_modules"]
script = 'scripts'
cache_path = 'cache-forge'
gas_reports = ["*"]
optimizer_runs = 100
fs_permissions = [{ access = "read", path = "./node_modules/@angleprotocol/sdk/dist/src/registry/registry.json" }]
solc = "0.8.24"
fs_permissions = [
{ access = "read", path = "./node_modules/@angleprotocol/sdk/dist/src/registry/registry.json" },
{ access = "write", path = "./transaction.json" },
{ access = "write", path = "./transactions.json" }
]
solc = "0.8.25"

ffi = true

Expand Down Expand Up @@ -39,7 +43,6 @@ bob = "${BOB_NODE_URI}"
linea = "${LINEA_NODE_URI}"
zksync = "${ZKSYNC_NODE_URI}"
mantle = "${MANTLE_NODE_URI}"
filecoin = "${FILECOIN_NODE_URI}"
blast = "${BLAST_NODE_URI}"
mode = "${MODE_NODE_URI}"
thundercore = "${THUNDERCORE_NODE_URI}"
Expand All @@ -66,13 +69,13 @@ swell = "${SWELL_NODE_URI}"
fork = "${ETH_NODE_URI_FORK}"

[etherscan]
localhost = { url = "http://localhost:4000", key = "${LOCALHOST_ETHERSCAN_API_KEY}" }
localhost = { url = "http://localhost:4000", key = "none" }
mainnet = { chainId = 1, key = "${MAINNET_ETHERSCAN_API_KEY}", url = "https://api.etherscan.io/api" }
polygon = { chainId = 137, key = "${POLYGON_ETHERSCAN_API_KEY}", url = "https://api.polygonscan.com/api" }
fantom = { chainId = 250, key = "${FANTOM_ETHERSCAN_API_KEY}", url = "https://api.ftmscan.com/api" }
optimism = { chainId = 10, key = "${OPTIMISM_ETHERSCAN_API_KEY}", url = "https://api-optimistic.etherscan.io/api" }
arbitrum = { chainId = 42161, key = "${ARBITRUM_ETHERSCAN_API_KEY}", url = "https://api.arbiscan.io/api" }
avalanche = { chainId = 43114, key = "${AVALANCHE_ETHERSCAN_API_KEY}", url = "api.avascan.info/v2/network/mainnet/evm/43114/etherscan" }
avalanche = { chainId = 43114, key = "${AVALANCHE_ETHERSCAN_API_KEY}", url = "https://api.avascan.info/v2/network/mainnet/evm/43114/etherscan" }
aurora = { chainId = 1313161554, key = "${AURORA_ETHERSCAN_API_KEY}", url = "http://localhost:4000" }
bsc = { chainId = 56, key = "${BSC_ETHERSCAN_API_KEY}", url = "https://api.bscscan.com/api" }
gnosis = { chainId = 100, key = "${GNOSIS_ETHERSCAN_API_KEY}", url = "https://api.gnosisscan.io/api" }
Expand All @@ -82,7 +85,6 @@ bob = { chainId = 60808, key = "${BOB_ETHERSCAN_API_KEY}", url = "https://explor
linea = { chainId = 59144, key = "${LINEA_ETHERSCAN_API_KEY}", url = "https://api.lineascan.build/api" }
zksync = { chainId = 324, key = "${ZKSYNC_ETHERSCAN_API_KEY}", url = "https://explorer.sepolia.era.zksync.dev/contract_verification" }
mantle = { chainId = 5000, key = "${MANTLE_ETHERSCAN_API_KEY}", url = "https://api.routescan.io/v2/network/mainnet/evm/43114/etherscan" }
filecoin = { chainId = 314, key = "${FILECOIN_ETHERSCAN_API_KEY}", url = "" }
blast = { chainId = 81457, key = "${BLAST_ETHERSCAN_API_KEY}", url = "https://api.blastscan.io/api" }
mode = { chainId = 34443, key = "${MODE_ETHERSCAN_API_KEY}", url = "https://api.routescan.io/v2/network/mainnet/evm/34443/etherscan/api" }
thundercore = { chainId = 108, key = "${THUNDERCORE_ETHERSCAN_API_KEY}", url = "" }
Expand All @@ -92,14 +94,14 @@ taiko = { chainId = 167000, key = "${TAIKO_ETHERSCAN_API_KEY}", url = "https://a
fuse = { chainId = 122, key = "${FUSE_ETHERSCAN_API_KEY}", url = "https://explorer.fuse.io/api" }
immutable = { chainId = 13371, key = "${IMMUTABLE_ETHERSCAN_API_KEY}", url = "https://immutable-mainnet.blockscout.com/api" }
scroll = { chainId = 534352, key = "${SCROLL_ETHERSCAN_API_KEY}", url = "https://api.scrollscan.com/api" }
manta = { chainId = 169, key = "${MANTA_ETHERSCAN_API_KEY}", url = "" }
sei = { chainId = 1329, key = "${SEI_ETHERSCAN_API_KEY}", url = "" }
manta = { chainId = 169, key = "${MANTA_ETHERSCAN_API_KEY}", url = "https://pacific-explorer.manta.network/api" }
sei = { chainId = 1329, key = "${SEI_ETHERSCAN_API_KEY}", url = "https://seitrace.com/pacific-1/api" }
celo = { chainId = 42220, key = "${CELO_ETHERSCAN_API_KEY}", url = "https://api.celoscan.io/api" }
fraxtal = { chainId = 252, key = "${FRAXTAL_ETHERSCAN_API_KEY}", url = "https://api.fraxscan.io/api" }
astar = { chainId = 592, key = "${ASTAR_ETHERSCAN_API_KEY}", url = "" }
astarzkevm = { chainId = 3776, key = "${ASTARZKEVM_ETHERSCAN_API_KEY}", url = "" }
rootstock = { chainId = 30, key = "${ROOTSTOCK_ETHERSCAN_API_KEY}", url = "" }
moonbeam = { chainId = 1284, key = "${MOONBEAM_ETHERSCAN_API_KEY}", url = "" }
astar = { chainId = 592, key = "${ASTAR_ETHERSCAN_API_KEY}", url = "https://astar.blockscout.com/api/" }
astarzkevm = { chainId = 3776, key = "${ASTARZKEVM_ETHERSCAN_API_KEY}", url = "https://astar-zkevm.explorer.startale.com/api" }
rootstock = { chainId = 30, key = "${ROOTSTOCK_ETHERSCAN_API_KEY}", url = "https://rootstock.blockscout.com/api/" }
moonbeam = { chainId = 1284, key = "${MOONBEAM_ETHERSCAN_API_KEY}", url = "https://api-moonbase.moonscan.io/api" }
skale = { chainId = 2046399126, key = "${SKALE_ETHERSCAN_API_KEY}", url = "https://internal-hubs.explorer.mainnet.skalenodes.com:10001/api" }
worldchain = { chainId = 480, key = "${WORLDCHAIN_ETHERSCAN_API_KEY}", url = "https://worldchain-mainnet.explorer.alchemy.com/api" }
lisk = { chainId = 1135, key = "${LISK_ETHERSCAN_API_KEY}", url = "https://blockscout.lisk.com/api/" }
Expand Down
228 changes: 228 additions & 0 deletions helpers/foundryMultiChainScript.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
#! /bin/bash

function usage {
echo "bash foundryMultiChainScript.sh <foundry-script-path>"
echo "Lists all chains where Merkl DistributionCreator is deployed and allows selection"
echo "Example: bash foundryMultiChainScript.sh ./scripts/DistributionCreator.s.sol:UpgradeAndBuildUpgradeToPayload"
echo ""
}

# Get list of chain IDs where DistributionCreator is deployed
function get_available_chains() {
local registry_file="node_modules/@angleprotocol/sdk/dist/src/registry/registry.json"
if [ ! -f "$registry_file" ]; then
echo "Registry file not found!"
exit 1
fi

jq -r 'to_entries | .[] | select(.value.Merkl.DistributionCreator != null) | .key' "$registry_file"
}

# Get list of chains to deploy to, handling exclusions
function get_selected_chains() {
local chain_ids=("$@")
local selected_chains=()
local exclude_chain_ids=(314) # Default exclusions: filecoin

read -p "Do you want to run the script on all chains? (y/n) -- Note: ChainIDs 314 and 324 is already excluded by default: " deploy_all

if [[ "$deploy_all" == "y" ]]; then
for chain_id in "${chain_ids[@]}"; do
if [[ ! " ${exclude_chain_ids[@]} " =~ " ${chain_id} " ]]; then
selected_chains+=("$chain_id")
fi
done
else
read -p "Enter chain IDs to exclude (space-separated), or press enter to continue: " -a additional_exclude
exclude_chain_ids+=("${additional_exclude[@]}")

for chain_id in "${chain_ids[@]}"; do
if [[ ! " ${exclude_chain_ids[@]} " =~ " ${chain_id} " ]]; then
selected_chains+=("$chain_id")
fi
done
fi

printf "%s " "${selected_chains[@]}"
}

# Get verification string for a specific chain
function get_verify_string() {
local chain_id=$1
local verify_string=""

local verifier_type_var="VERIFIER_TYPE_${chain_id}"
local verifier_type=$(eval "echo \$$verifier_type_var")

if [ ! -z "${verifier_type}" ]; then
verify_string="--verify --verifier ${verifier_type}"

# Add verifier URL if present
local verifier_url_var="VERIFIER_URL_${chain_id}"
local verifier_url=$(eval "echo \$$verifier_url_var")
if [ ! -z "${verifier_url}" ]; then
verify_string="${verify_string} --verifier-url ${verifier_url}"
fi

# Add API key if present
local verifier_api_key_var="VERIFIER_API_KEY_${chain_id}"
local verifier_api_key=$(eval "echo \$$verifier_api_key_var")
if [ ! -z "${verifier_api_key}" ]; then
if [ "${verifier_type}" == "etherscan" ]; then
verify_string="${verify_string} --etherscan-api-key ${verifier_api_key}"
else
verify_string="${verify_string} --verifier-api-key ${verifier_api_key}"
fi
fi
fi

echo "$verify_string"
}

# Get compilation flags for a specific chain
function get_compile_flags() {
local chain_id=$1

london_chain_ids=(30 122 592 1284 1923 10242 108 250 42220 59144)
legacy_chain_ids=(196 250 1329 3776 480 2046399126 42793)
zk_chain_ids=(324)
if [[ " ${london_chain_ids[@]} " =~ " ${chain_id} " ]]; then
echo "--evm-version london"
elif [[ " ${legacy_chain_ids[@]} " =~ " ${chain_id} " ]]; then
echo "--legacy"
elif [[ " ${zk_chain_ids[@]} " =~ " ${chain_id} " ]]; then
echo "--zksync"
else
echo ""
fi
}

function main {
# Check if script path is provided
if [ -z "$1" ]; then
usage
exit 1
fi

FOUNDRY_SCRIPT="$1"

# Verify the script exists
if [ ! -f "$FOUNDRY_SCRIPT" ]; then
echo "Error: Script file '$FOUNDRY_SCRIPT' not found!"
exit 1
fi

# Path to the registry file
registry_file="node_modules/@angleprotocol/sdk/dist/src/registry/registry.json"

if [ ! -f "$registry_file" ]; then
echo "Registry file not found!"
exit 1
fi


# Store chain IDs in an array
chain_ids=()
while IFS= read -r chain_id; do
chain_ids+=("$chain_id")
done <<< "$(jq -r 'to_entries | .[] | select(.value.Merkl.DistributionCreator != null) | .key' "$registry_file")"

# Display all chains
echo "Chain IDs where Merkl DistributionCreator is deployed: ${chain_ids[@]}"

echo ""
selected_chains=($(get_selected_chains "${chain_ids[@]}"))

source .env
rm -f ./transaction.json

# Initialize arrays for tracking deployment status
successful_chains=()
failed_chains=()

# Prompt user for broadcast and verify options
read -p "Do you want to broadcast the transaction? (y/n): " broadcast_choice

# Set flags based on user input
if [ "$broadcast_choice" == "y" ]; then
broadcast_flag="--broadcast"
read -p "Do you want to verify the transaction? (y/n): " verify_choice
else
broadcast_flag=""
fi

# Run forge script for each selected chain
for chain_id in "${selected_chains[@]}"; do
echo "Running forge script for chain ID: $chain_id"
rpc_url_var="ETH_NODE_URI_${chain_id}"
rpc_url=$(eval "echo \$$rpc_url_var")

# Check if chain ID already exists in transactions.json
if [ -f "./transactions.json" ] && jq -e "has(\"$chain_id\")" ./transactions.json > /dev/null; then
echo "Chain ID $chain_id already exists in transactions.json, skipping..."
continue
fi

# Verification string based on chain-specific environment variables
if [ "$verify_choice" == "y" ]; then
verify_string=$(get_verify_string "$chain_id")
else
verify_string=""
fi

# Compilation specific flags
compile_flags=$(get_compile_flags "$chain_id")

cmd="forge script $FOUNDRY_SCRIPT $broadcast_flag --rpc-url $rpc_url $compile_flags $verify_string --force"
echo "Running command: $cmd"
if eval $cmd && [ -f "./transaction.json" ]; then
successful_chains+=("$chain_id")
else
failed_chains+=("$chain_id")
fi

# Create a new JSON object with chain ID as key and transaction data as value
if [ -f "./transaction.json" ]; then
jq -s '.[0] * {("'$chain_id'"): .[1]}' \
./transactions.json \
./transaction.json > ./transactions.json.tmp

mv ./transactions.json.tmp ./transactions.json
rm -f ./transaction.json
fi

# Add verification step if verification was requested
if [ "$verify_choice" == "y" ]; then
echo "Attempting contract verification..."
# Extract contract address from the data field (removing 0x3659cfe6 prefix and any leading zeros)
contract_address=$(jq -r --arg chainid "$chain_id" '.[$chainid].data' ./transactions.json | sed 's/^0x3659cfe6000000000000000000000000//')

if [ ! -z "$contract_address" ] && [ "$contract_address" != "null" ]; then
# Get verification parameters from environment variables
verify_flag=$(get_verify_string "$chain_id" | sed 's/--verify //')
compile_flags=$(get_compile_flags "$chain_id" | sed 's/--legacy //')
verify_cmd="forge verify-contract --rpc-url $rpc_url 0x$contract_address contracts/DistributionCreator.sol:DistributionCreator $verify_flag $compile_flags --watch"
echo "Running verification command: $verify_cmd"
if eval $verify_cmd; then
echo "✅ Contract verification successful"
else
echo "❌ Contract verification failed"
fi
fi
fi

echo "Safe to cancel job for 5 seconds"
sleep 5
echo "Starting next chain"
done

# Display final deployment status
if [ ${#successful_chains[@]} -gt 0 ]; then
echo -e "\n✅ Deployment successful on chains: ${successful_chains[*]}"
fi
if [ ${#failed_chains[@]} -gt 0 ]; then
echo -e "\n❌ Deployment issues on chains: ${failed_chains[*]}"
fi
}

main "$@"
13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"foundry:run": "docker run -it --rm -v $(pwd):/app -w /app ghcr.io/foundry-rs/foundry sh",
"foundry:setup": "curl -L https://foundry.paradigm.xyz | bash && foundryup && git submodule update --init --recursive",
"foundry:size": "forge build --skip test --sizes",
"foundry:test": "FOUNDRY_PROFILE=dev forge test -vvv",
"foundry:test": "FOUNDRY_PROFILE=dev forge test -vvv forge --no-match-contract UpgradeDistributionCreatorTest",
"impersonate": "cast rpc anvil_impersonateAccount",
"impersonate:script": "FOUNDRY_PROFILE=dev forge script --skip test --fork-url fork --broadcast -vvvv --gas-price 0 --priority-gas-price 0 --unlocked --sender",
"impersonate:setBalance": "cast rpc anvil_setBalance 0x0022228a2cc5E7eF0274A7Baa600d44da5aB5776 1000000000000000000 && cast rpc anvil_setBalance 0x15775b23340C0f50E0428D674478B0e9D3D0a759 1000000000000000000 && cast rpc anvil_setBalance 0x19c41f6607b2c0e80e84baadaf886b17565f278e 1000000000000000000 && cast rpc anvil_setBalance 0xA9DdD91249DFdd450E81E1c56Ab60E1A62651701 1000000000000000000",
Expand All @@ -30,15 +30,16 @@
"url": "https://github.com/AngleProtocol/merkl-contracts/issues"
},
"devDependencies": {
"@angleprotocol/sdk": "2.34.5",
"@openzeppelin/contracts": "^4.8.1",
"@openzeppelin/contracts-upgradeable": "4.8.1",
"@angleprotocol/sdk": "2.34.7",
"@openzeppelin/contracts": "^4.9.0",
"@openzeppelin/contracts-upgradeable": "4.9.0",
"prettier": "^2.0.0",
"prettier-plugin-solidity": "^1.1.3",
"solhint": "^3.5.1",
"solhint-plugin-prettier": "^0.0.5",
"solidity-coverage": "^0.8.2",
"forge-std": "github:foundry-rs/forge-std#v1.9.4"
"forge-std": "github:foundry-rs/forge-std#v1.9.4",
"utils": "github:AngleProtocol/utils"
},
"dependencies": {}
}
}
2 changes: 1 addition & 1 deletion remappings.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@openzeppelin/=node_modules/@openzeppelin/
forge-std/=node_modules/forge-std/src
oz/=node_modules/@openzeppelin/contracts/
utils/=lib/utils
@utils/=node_modules/utils/src
2 changes: 1 addition & 1 deletion scripts/Disputer.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ pragma solidity ^0.8.17;

import { console } from "forge-std/console.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { JsonReader } from "@utils/JsonReader.sol";

import { BaseScript } from "./utils/Base.s.sol";
import { JsonReader } from "./utils/JsonReader.sol";
import { Disputer } from "../contracts/Disputer.sol";
import { Distributor } from "../contracts/Distributor.sol";

Expand Down
Loading
Loading