From 7aab323e6d1de08d6bf2d5d0463f22158738ab8e Mon Sep 17 00:00:00 2001 From: Ryan Hall Date: Wed, 11 Dec 2024 11:27:25 -0500 Subject: [PATCH] write VerifyV1Deployments.s.sol script --- .github/workflows/test.yml | 4 + foundry.toml | 10 +- script/VerifyV1Deployments.s.sol | 197 ++++++++++++++++++ script/output/dev-0/v1-deployment.json | 6 +- script/output/holesky/v1-deployment.json | 2 +- script/output/holesky_dev/v1-deployment.json | 4 +- script/output/mainnet/v1-deployment.json | 2 +- .../output/scroll_sepolia/v1-deployment.json | 3 +- 8 files changed, 215 insertions(+), 13 deletions(-) create mode 100644 script/VerifyV1Deployments.s.sol diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f6e26c1..451e4a6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,3 +25,7 @@ jobs: - name: Run unit tests run: make test id: test + + - name: Run deployment tests + run: forge script script/VerifyV1Deployments.s.sol --ffi + id: deployment-tests diff --git a/foundry.toml b/foundry.toml index dd0373a..6523343 100644 --- a/foundry.toml +++ b/foundry.toml @@ -29,14 +29,14 @@ dev-2 = "https://holesky.gateway.tenderly.co" dev-3 = "https://holesky.gateway.tenderly.co" # used for `test` environment holesky = "https://holesky.gateway.tenderly.co" -sepolia = "${SEPOLIA_RPC_URL}" -base_sepolia = "${BASE_SEPOLIA_RPC_URL}" +sepolia = "https://ethereum-sepolia-rpc.publicnode.com" +base_sepolia = "https://base-sepolia-rpc.publicnode.com" fraxtal_testnet = "https://rpc.testnet.frax.com" mantle_sepolia = "https://rpc.sepolia.mantle.xyz" scroll_sepolia = "https://sepolia-rpc.scroll.io" # prod environment -mainnet = "${MAINNET_RPC_URL}" -base = "${BASE_RPC_URL}" +mainnet = "https://ethereum-rpc.publicnode.com" +base = "https://base-rpc.publicnode.com" fraxtal = "https://rpc.frax.com" mantle = "https://rpc.mantle.xyz" polygon_zkevm = "https://zkevm-rpc.com" @@ -47,7 +47,7 @@ holesky = { key = "${MAINNET_ETHERSCAN_API_KEY}" } base_sepolia = { key = "${BASESCAN_API_KEY}" } fraxtal_testnet = { key = "${FRAXSCAN_API_KEY}" } mantle_sepolia = { key = "${MANTLESCAN_API_KEY}" } -scroll-sepolia = { key = "${SCROLLSCAN_API_KEY}" } +scroll_sepolia = { key = "${SCROLLSCAN_API_KEY}" } mainnet = { key = "${MAINNET_ETHERSCAN_API_KEY}" } base = { key = "${BASESCAN_API_KEY}" } diff --git a/script/VerifyV1Deployments.s.sol b/script/VerifyV1Deployments.s.sol new file mode 100644 index 0000000..cbbd5ad --- /dev/null +++ b/script/VerifyV1Deployments.s.sol @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Script, console2 as console} from "forge-std/Script.sol"; +import {stdJson} from "forge-std/StdJson.sol"; +import {LPNQueryV1} from "../src/v1/client/LPNQueryV1.sol"; +import {ERC1967Proxy} from + "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import {LibString} from "solady/utils/LibString.sol"; +import { + MANTLE_MAINNET, + MANTLE_SEPOLIA, + isMainnet, + isTestnet +} from "../src/utils/Constants.sol"; + +contract VerifyV1Deployments is Script { + using stdJson for string; + using LibString for string; + + bytes32 constant IMPLEMENTATION_SLOT = + bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1); + + constructor() { + setChain( + "mantle", + ChainData("Mantle", MANTLE_MAINNET, "https://rpc.mantle.xyz") + ); + setChain( + "mantle_sepolia", + ChainData( + "Mantle Sepolia", + MANTLE_SEPOLIA, + "https://rpc.sepolia.mantle.xyz" + ) + ); + + setChain( + "polygon_zkevm", + ChainData("Polygon zkEVM", 1101, "https://zkevm-rpc.com") + ); + + setChain("scroll_sepolia", ChainData("Scroll Sepolia", 534351, "")); + } + + /// @notice entrypoint for the script; verifies the arrangement of all deployed v1 contracts + function run() public { + execute(false); + } + + /// @notice alternative entrypoint for the script; verifies the arrangement of all deployed v1 contracts + /// and attempts to fix any misaligned json files + function fix() public { + execute(true); + } + + /// @param shouldFix if true, will attempt to fix the misaligned json files + /// @dev this script does change anything about the deployed contracts; it sends no transactions; + /// it only verifies the correctness of the local json files + function execute(bool shouldFix) internal { + // Find all v1-deployment.json files + string[] memory shellCommandInputs = new string[](3); + shellCommandInputs[0] = "/bin/sh"; + shellCommandInputs[1] = "-c"; + shellCommandInputs[2] = "find . -name v1-deployment.json | sort"; + + bytes memory output = vm.ffi(shellCommandInputs); + string[] memory files = string(output).split("\n"); + + bool shouldFail; + + for (uint256 i = 0; i < files.length; i++) { + string memory jsonStr = vm.readFile(files[i]); + + // Extract chain ID and switch RPC + uint256 chainId = + abi.decode(jsonStr.parseRaw(".chainInfo.chainId"), (uint256)); + vm.createSelectFork(getChain(chainId).rpcUrl); + + // Extract addresses + address queryClientProxy = abi.decode( + jsonStr.parseRaw(".addresses.queryClientProxy"), (address) + ); + address queryClientImpl = abi.decode( + jsonStr.parseRaw(".addresses.queryClientImpl"), (address) + ); + address registryProxy = abi.decode( + jsonStr.parseRaw(".addresses.registryProxy"), (address) + ); + address registryImpl = abi.decode( + jsonStr.parseRaw(".addresses.registryImpl"), (address) + ); + + // Print deployment info + console.log("\nVerifying deployment in file:", files[i]); + console.log("Chain ID:", chainId); + console.log("QueryClient Proxy:", queryClientProxy); + console.log("QueryClient Implementation:", queryClientImpl); + console.log("Registry Proxy:", registryProxy); + console.log("Registry Implementation:", registryImpl); + console.log( + "--------------------- Assertions ---------------------" + ); + + // Verify query client proxy implementation + if (queryClientProxy != address(0)) { + address actualQueryImpl = + getProxyImplementation(queryClientProxy); + if (actualQueryImpl != queryClientImpl) { + shouldFail = true; + console.log( + unicode"✗ QueryClient Proxy points to incorrect implementation" + ); + if (shouldFix) { + jsonStr = vm.serializeString( + jsonStr, + ".addresses.queryClientImpl", + vm.toString(actualQueryImpl) + ); + } + } else { + console.log( + unicode"✓ QueryClient Proxy points to correct implementation" + ); + } + } else { + console.log( + unicode"✓ QueryClient Proxy not deployed, skipping check" + ); + } + + // Verify query client points to the correct registry + if (queryClientProxy != address(0)) { + address linkedRegistry = + address(LPNQueryV1(queryClientProxy).lpnRegistry()); + if (linkedRegistry != registryProxy) { + shouldFail = true; + console.log( + unicode"✗ QueryClient points to incorrect LPNRegistry" + ); + } else { + console.log( + unicode"✓ QueryClient points to correct LPNRegistry" + ); + } + } else { + console.log( + unicode"✓ QueryClient Proxy not deployed, skipping check" + ); + } + + // Verify registry proxy implementation + if (registryProxy != address(0)) { + address actualRegistryImpl = + getProxyImplementation(registryProxy); + if (actualRegistryImpl != registryImpl) { + shouldFail = true; + console.log( + unicode"✗ Registry Proxy points to incorrect implementation" + ); + if (shouldFix) { + jsonStr = vm.serializeString( + jsonStr, + ".addresses.registryImpl", + vm.toString(actualRegistryImpl) + ); + } + } else { + console.log( + unicode"✓ Registry proxy points to correct implementation" + ); + } + } else { + console.log( + unicode"✓ Registry proxy not deployed, skipping check" + ); + } + + vm.writeJson(jsonStr, files[i]); + } + + if (shouldFail) revert("verification failed"); + } + + function getProxyImplementation(address proxy) + internal + view + returns (address) + { + address implementation; + bytes32 result = vm.load(proxy, IMPLEMENTATION_SLOT); + assembly { + implementation := result + } + return implementation; + } +} diff --git a/script/output/dev-0/v1-deployment.json b/script/output/dev-0/v1-deployment.json index f472aae..521097a 100644 --- a/script/output/dev-0/v1-deployment.json +++ b/script/output/dev-0/v1-deployment.json @@ -1,8 +1,8 @@ { "addresses": { - "queryClient": "0x9e81a7a0A234E6Ede4604DD7823018D0efE53b4b", - "queryClientProxy": "0x5C189dC46c2c6136dBaFdAAa02Db46CD2dF683A3", - "registryImpl": "0x7a9C8a9af98119249F1b8f4786Fa9aE1B5fBB60A", + "queryClientImpl": "0x400e7aAEadcc134B0d8413092119221Ad736ed3e", + "queryClientProxy": "0x859fAB84a9EB80Ba45E1ACb90B75608ce484caa9", + "registryImpl": "0xd2CB2F1a106E5145D9aa882b8902cB0fA800Da6d", "registryProxy": "0x851Abc818275932215a345B7059d40a89939C074" }, "chainInfo": { diff --git a/script/output/holesky/v1-deployment.json b/script/output/holesky/v1-deployment.json index acd8206..52af998 100644 --- a/script/output/holesky/v1-deployment.json +++ b/script/output/holesky/v1-deployment.json @@ -1,6 +1,6 @@ { "addresses": { - "queryClientImpl": "0x80c0a42F808d6f35e83F4939482A485caE536e6a", + "queryClientImpl": "0xa0Ac691e90460Ac0fB5ADC8AC0Dd9b28CFAD69b4", "queryClientProxy": "0xcB170B5a3cf707b4dAc2AbdD6919A0c30A8ec145", "registryImpl": "0x12a628B8F31939bFa233E284862945870BBE649A", "registryProxy": "0xF8E0EfefAFfa31f9C85F175147d64753e4eC8157" diff --git a/script/output/holesky_dev/v1-deployment.json b/script/output/holesky_dev/v1-deployment.json index ef9daaa..360e234 100644 --- a/script/output/holesky_dev/v1-deployment.json +++ b/script/output/holesky_dev/v1-deployment.json @@ -1,8 +1,8 @@ { "addresses": { - "queryClientImpl": "0xE1b71B57Ea95ae274018aE844ae7013F39Cb06Dc", + "queryClientImpl": "0x9e81a7a0A234E6Ede4604DD7823018D0efE53b4b", "queryClientProxy": "0x5C189dC46c2c6136dBaFdAAa02Db46CD2dF683A3", - "registryImpl": "0x4015A66c1a8f4C6bD93507290f6C5304c38a4c56", + "registryImpl": "0xd0fA8Ea6981894687DeFc1AAebf5804CDa7e654F", "registryProxy": "0xC9135b51c4DD04B6E082e2cB56a5C26b3a53Ee47" }, "chainInfo": { diff --git a/script/output/mainnet/v1-deployment.json b/script/output/mainnet/v1-deployment.json index ae19a16..1a04690 100644 --- a/script/output/mainnet/v1-deployment.json +++ b/script/output/mainnet/v1-deployment.json @@ -14,4 +14,4 @@ "erc20ProportionateBalance": "0x0000000000000000000000000000000000000000", "erc721Enumerable": "0x0000000000000000000000000000000000000000" } -} +} \ No newline at end of file diff --git a/script/output/scroll_sepolia/v1-deployment.json b/script/output/scroll_sepolia/v1-deployment.json index ba8d8fe..c0e9385 100644 --- a/script/output/scroll_sepolia/v1-deployment.json +++ b/script/output/scroll_sepolia/v1-deployment.json @@ -1,6 +1,7 @@ { "addresses": { - "queryClient": "0x0000000000000000000000000000000000000000", + "queryClientImpl": "0x0000000000000000000000000000000000000000", + "queryClientProxy": "0x0000000000000000000000000000000000000000", "registryImpl": "0xBeAd7Fe75B567Ac0fc12526225451eb3F964d834", "registryProxy": "0xC9135b51c4DD04B6E082e2cB56a5C26b3a53Ee47" },