diff --git a/.gitignore b/.gitignore index 61df11c..d18d846 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,5 @@ bin # foundry /out +/zkout /cache-forge diff --git a/bun.lockb b/bun.lockb index 744563c..1417aac 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/contracts/Disputer.sol b/contracts/Disputer.sol index 282fa5a..5ef17f4 100644 --- a/contracts/Disputer.sol +++ b/contracts/Disputer.sol @@ -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"; diff --git a/foundry.toml b/foundry.toml index 3c328cf..85357db 100644 --- a/foundry.toml +++ b/foundry.toml @@ -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 @@ -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}" @@ -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" } @@ -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 = "" } @@ -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/" } diff --git a/helpers/foundryMultiChainScript.sh b/helpers/foundryMultiChainScript.sh new file mode 100755 index 0000000..f200242 --- /dev/null +++ b/helpers/foundryMultiChainScript.sh @@ -0,0 +1,228 @@ +#! /bin/bash + +function usage { + echo "bash foundryMultiChainScript.sh " + 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 "$@" \ No newline at end of file diff --git a/package.json b/package.json index 3c47200..7b2744f 100644 --- a/package.json +++ b/package.json @@ -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 --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", @@ -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": {} -} \ No newline at end of file +} diff --git a/remappings.txt b/remappings.txt index 79003cf..9cd779e 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,4 +1,4 @@ @openzeppelin/=node_modules/@openzeppelin/ forge-std/=node_modules/forge-std/src oz/=node_modules/@openzeppelin/contracts/ -utils/=lib/utils \ No newline at end of file +@utils/=node_modules/utils/src \ No newline at end of file diff --git a/scripts/Disputer.s.sol b/scripts/Disputer.s.sol index 1b6d08a..ca90e80 100644 --- a/scripts/Disputer.s.sol +++ b/scripts/Disputer.s.sol @@ -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"; diff --git a/scripts/DistributionCreator.s.sol b/scripts/DistributionCreator.s.sol index 309b897..5b17e78 100644 --- a/scripts/DistributionCreator.s.sol +++ b/scripts/DistributionCreator.s.sol @@ -3,10 +3,12 @@ pragma solidity ^0.8.17; import { console } from "forge-std/console.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import { ITransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { JsonReader } from "@utils/JsonReader.sol"; +import { ContractType } from "@utils/Constants.sol"; import { BaseScript } from "./utils/Base.s.sol"; -import { JsonReader } from "./utils/JsonReader.sol"; import { DistributionCreator } from "../contracts/DistributionCreator.sol"; import { IAccessControlManager } from "../contracts/interfaces/IAccessControlManager.sol"; import { CampaignParameters } from "../contracts/struct/CampaignParameters.sol"; @@ -255,6 +257,19 @@ contract SetMessage is DistributionCreatorScript { } } +// GetMessage script +contract GetMessage is DistributionCreatorScript { + function run() external broadcast { + uint256 chainId = block.chainid; + address creatorAddress = readAddress(chainId, "Merkl.DistributionCreator"); + + console.log("Creator address:", creatorAddress); + string memory message = DistributionCreator(creatorAddress).message(); + + console.log("Message is:", message); + } +} + // ToggleSigningWhitelist script contract ToggleSigningWhitelist is DistributionCreatorScript { function run() external { @@ -506,3 +521,33 @@ contract SignAndCreateCampaign is DistributionCreatorScript { console.log("Message signed and campaign created with ID:", vm.toString(campaignId)); } } + +contract UpgradeAndBuildUpgradeToPayload is DistributionCreatorScript { + function run() external { + uint256 chainId = block.chainid; + address distributionCreator = readAddress(chainId, "Merkl.DistributionCreator"); + + address distributionCreatorImpl = address(new DistributionCreator()); + + bytes memory payload = abi.encodeWithSelector( + ITransparentUpgradeableProxy.upgradeTo.selector, + distributionCreatorImpl + ); + + try this.externalReadAddress(chainId, "Merkl.AngleLabs") returns (address safe) { + _serializeJson( + chainId, + distributionCreator, // target address (the proxy) + 0, // value + payload, // direct upgrade call + Operation.Call, // standard call (not delegate) + hex"", // signature + safe // safe address + ); + } catch {} + } + + function externalReadAddress(uint256 chainId, string memory key) external view returns (address) { + return readAddress(chainId, key); + } +} diff --git a/scripts/Distributor.s.sol b/scripts/Distributor.s.sol index 96caab5..d72aa32 100644 --- a/scripts/Distributor.s.sol +++ b/scripts/Distributor.s.sol @@ -4,9 +4,9 @@ pragma solidity ^0.8.17; import { console } from "forge-std/console.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.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 { Distributor, MerkleTree } from "../contracts/Distributor.sol"; import { IAccessControlManager } from "../contracts/interfaces/IAccessControlManager.sol"; diff --git a/scripts/MockToken.s.sol b/scripts/MockToken.s.sol index 699413e..acdaf39 100644 --- a/scripts/MockToken.s.sol +++ b/scripts/MockToken.s.sol @@ -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 { MockToken } from "../contracts/mock/MockToken.sol"; // Base contract with shared utilities @@ -45,9 +45,9 @@ contract Deploy is MockTokenScript { contract Mint is MockTokenScript { function run() external broadcast { // MODIFY THESE VALUES TO SET YOUR DESIRED MINT PARAMETERS - address token = address(0); - address recipient = address(0); - uint256 amount = 0; + address token = 0xb5eCAa1a867FeCCD6d87604bc16a2b6B53D706BF; + address recipient = 0x103eC7cF86CC6f1DAada07830C84f43B42Bf1eB3; + uint256 amount = 1e18; _run(token, recipient, amount); } diff --git a/scripts/merklDeploy.s.sol b/scripts/merklDeploy.s.sol index 8cfe1ce..4501cb7 100644 --- a/scripts/merklDeploy.s.sol +++ b/scripts/merklDeploy.s.sol @@ -7,9 +7,9 @@ import { ProxyAdmin } from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { console } from "forge-std/console.sol"; +import { JsonReader } from "@utils/JsonReader.sol"; import { CreateXConstants } from "./utils/CreateXConstants.sol"; -import { JsonReader } from "./utils/JsonReader.sol"; import { TokensUtils } from "./utils/TokensUtils.sol"; import { AccessControlManager } from "../contracts/AccessControlManager.sol"; @@ -157,7 +157,7 @@ contract MainDeployScript is Script, JsonReader, TokensUtils, CreateXConstants { vm.startBroadcast(DEPLOYER_PRIVATE_KEY); // Set params and transfer ownership - setDistributionCreatorParams(address(creator.proxy), aglaMerkl, KEEPER); + setDistributionCreatorParams(address(creator.proxy), aglaMerkl, DUMPER); setDistributorParams(address(distributor.proxy), DISPUTE_TOKEN, KEEPER); // Deploy Disputer @@ -354,7 +354,7 @@ contract MainDeployScript is Script, JsonReader, TokensUtils, CreateXConstants { SETTERS //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/ - function setDistributionCreatorParams(address _distributionCreator, address aglaMerkl, address keeper) public { + function setDistributionCreatorParams(address _distributionCreator, address aglaMerkl, address dumper) public { console.log("\n=== Setting DistributionCreator params ==="); DistributionCreator distributionCreator = DistributionCreator(_distributionCreator); @@ -368,8 +368,8 @@ contract MainDeployScript is Script, JsonReader, TokensUtils, CreateXConstants { distributionCreator.setRewardTokenMinAmounts(tokens, minAmounts); // Set keeper as fee recipient - console.log("Setting keeper as fee recipient:", keeper); - distributionCreator.setFeeRecipient(keeper); + console.log("Setting dumper as fee recipient:", dumper); + distributionCreator.setFeeRecipient(dumper); // Set campaign fees to 5% for airdrop campaigns console.log("Setting campaign fees to 5% for airdrop campaigns"); diff --git a/scripts/utils/Base.s.sol b/scripts/utils/Base.s.sol index fc8e829..87c3b9d 100644 --- a/scripts/utils/Base.s.sol +++ b/scripts/utils/Base.s.sol @@ -16,6 +16,11 @@ abstract contract BaseScript is Script { /// @dev Used to derive the broadcaster's address if $DEPLOYER_ADDRESS is not defined. string internal mnemonic; + enum Operation { + Call, + DelegateCall + } + /// @dev Initializes the transaction broadcaster like this: /// /// - If $DEPLOYER_ADDRESS is defined, use it. @@ -38,4 +43,38 @@ abstract contract BaseScript is Script { _; vm.stopBroadcast(); } + + function _serializeJson( + uint256 chainId, + address to, + uint256 value, + bytes memory data, + Operation operation, + bytes memory additionalData + ) internal { + _serializeJson(chainId, to, value, data, operation, additionalData, address(0)); + } + + function _serializeJson( + uint256 chainId, + address to, + uint256 value, + bytes memory data, + Operation operation, + bytes memory additionalData, + address safe + ) internal { + string memory json = ""; + vm.serializeUint(json, "chainId", chainId); + vm.serializeAddress(json, "to", to); + vm.serializeUint(json, "value", value); + vm.serializeUint(json, "operation", uint256(operation)); + vm.serializeBytes(json, "additionalData", additionalData); + if (safe != address(0)) { + vm.serializeAddress(json, "safe", safe); + } + string memory finalJson = vm.serializeBytes(json, "data", data); + + vm.writeJson(finalJson, "./transaction.json"); + } } diff --git a/scripts/utils/JsonReader.sol b/scripts/utils/JsonReader.sol deleted file mode 100644 index 6e5e81d..0000000 --- a/scripts/utils/JsonReader.sol +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.17; - -import { Script } from "forge-std/Script.sol"; -import { stdJson } from "forge-std/StdJson.sol"; - -contract JsonReader is Script { - using stdJson for string; - - error FileNotFound(string path); - error ValueNotFound(string path, string key); - error ChainNotSupported(uint256 chainId); - - // Mapping of chain IDs to their names - mapping(uint256 => string) internal chainNames; - - /// @notice Gets the network-specific config path - /// @return The full path to the network config file - function getPath() public view returns (string memory) { - string memory root = vm.projectRoot(); - return string.concat(root, "/node_modules/@angleprotocol/sdk/dist/src/registry/registry.json"); - } - - /// @notice Reads an address value from the network's JSON file - /// @param chainId The chain ID - /// @param key The JSON key to read - /// @return The address value - function readAddress(uint256 chainId, string memory key) public view returns (address) { - string memory path = getPath(); - return readAddressFromPath(path, string.concat(vm.toString(chainId), ".", key)); - } - - /// @notice Reads a string value from the network's JSON file - /// @param chainId The chain ID - /// @param key The JSON key to read - /// @return The string value - function readString(uint256 chainId, string memory key) public view returns (string memory) { - string memory path = getPath(); - return readStringFromPath(path, string.concat(vm.toString(chainId), ".", key)); - } - - /// @notice Reads a uint256 value from the network's JSON file - /// @param chainId The chain ID - /// @param key The JSON key to read - /// @return The uint256 value - function readUint(uint256 chainId, string memory key) public view returns (uint256) { - string memory path = getPath(); - return readUintFromPath(path, string.concat(vm.toString(chainId), ".", key)); - } - - /// @notice Reads a string array from the network's JSON file - /// @param chainId The chain ID - /// @param key The JSON key to read - /// @return The string array - function readStringArray(uint256 chainId, string memory key) public view returns (string[] memory) { - string memory path = getPath(); - return readStringArrayFromPath(path, string.concat(vm.toString(chainId), ".", key)); - } - - // Direct path reading functions - function readAddressFromPath(string memory path, string memory key) public view returns (address) { - string memory json = readJsonFile(path); - bytes memory raw = json.parseRaw(string.concat(".", key)); - if (raw.length == 0) revert ValueNotFound(path, key); - return bytesToAddress(raw); - } - - function readStringFromPath(string memory path, string memory key) public view returns (string memory) { - string memory json = readJsonFile(path); - bytes memory raw = json.parseRaw(string.concat(".", key)); - if (raw.length == 0) revert ValueNotFound(path, key); - return string(raw); - } - - function readUintFromPath(string memory path, string memory key) public view returns (uint256) { - string memory json = readJsonFile(path); - bytes memory raw = json.parseRaw(string.concat(".", key)); - if (raw.length == 0) revert ValueNotFound(path, key); - return abi.decode(raw, (uint256)); - } - - function readStringArrayFromPath(string memory path, string memory key) public view returns (string[] memory) { - string memory json = readJsonFile(path); - bytes memory raw = json.parseRaw(string.concat(".", key)); - if (raw.length == 0) revert ValueNotFound(path, key); - return abi.decode(raw, (string[])); - } - - /// @notice Reads a JSON file from the given path - /// @param path The path to the JSON file - /// @return The JSON content as a string - function readJsonFile(string memory path) public view returns (string memory) { - try vm.readFile(path) returns (string memory json) { - return json; - } catch { - revert FileNotFound(path); - } - } - - /// @notice Utility function to convert bytes to address - /// @param bys Bytes to convert - /// @return addr Resulting address - function bytesToAddress(bytes memory bys) private pure returns (address addr) { - assembly { - addr := mload(add(bys, 32)) - } - } -} diff --git a/test/DistributionCreator.t.sol b/test/DistributionCreator.t.sol index 84d57d8..5c1dbe2 100644 --- a/test/DistributionCreator.t.sol +++ b/test/DistributionCreator.t.sol @@ -4,13 +4,13 @@ pragma solidity ^0.8.17; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { Test } from "forge-std/Test.sol"; import { console } from "forge-std/console.sol"; +import { JsonReader } from "@utils/JsonReader.sol"; import { DistributionCreator, DistributionParameters, CampaignParameters } from "../contracts/DistributionCreator.sol"; import { Distributor, MerkleTree } from "../contracts/Distributor.sol"; import { Fixture, IERC20 } from "./Fixture.t.sol"; import { Errors } from "../contracts/utils/Errors.sol"; import { IAccessControlManager } from "../contracts/interfaces/IAccessControlManager.sol"; -import { JsonReader } from "../scripts/utils/JsonReader.sol"; import { MockToken } from "../contracts/mock/MockToken.sol"; contract DistributionCreatorCreateCampaignTest is Fixture { @@ -853,7 +853,7 @@ contract DistributionCreatorOverrideTest is Fixture { // Silo distrib address[] memory whitelist = new address[](1); whitelist[0] = 0x8095806d8753C0443C118D1C5e5eEC472e30BFeC; - bytes memory campaignData = abi.encode( + campaignData = abi.encode( 0x04C0599Ae5A44757c0af6F9eC3b93da8976c150A, 2, 0xa42001D6d2237d2c74108FE360403C4b796B7170, diff --git a/test/unit/DistributionCreator.t.sol b/test/unit/DistributionCreator.t.sol index 4d1f60c..0f67b16 100644 --- a/test/unit/DistributionCreator.t.sol +++ b/test/unit/DistributionCreator.t.sol @@ -3,12 +3,12 @@ pragma solidity ^0.8.17; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { Test } from "forge-std/Test.sol"; +import { JsonReader } from "@utils/JsonReader.sol"; import { DistributionCreator, DistributionParameters, CampaignParameters, RewardTokenAmounts } from "../../contracts/DistributionCreator.sol"; import { Errors } from "../../contracts/utils/Errors.sol"; import { Fixture, IERC20 } from "../Fixture.t.sol"; import { IAccessControlManager } from "../../contracts/interfaces/IAccessControlManager.sol"; -import { JsonReader } from "../../scripts/utils/JsonReader.sol"; contract DistributionCreatorTest is Fixture { using SafeERC20 for IERC20;