diff --git a/README.md b/README.md index fdcf20b..4071f9f 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,7 @@ This repository contains the smart contracts for verifying [SP1](https://github. ## Installation -> [!WARNING] -> [Foundry](https://github.com/foundry-rs/foundry) installs the latest release version initially, but subsequent `forge update` commands will use the `main` branch. This branch is the development branch and should be avoided in favor of tagged releases. The release process matches a specific SP1 version. +> [!WARNING] > [Foundry](https://github.com/foundry-rs/foundry) installs the latest release version initially, but subsequent `forge update` commands will use the `main` branch. This branch is the development branch and should be avoided in favor of tagged releases. The release process matches a specific SP1 version. To install the latest release version: @@ -14,6 +13,7 @@ forge install succinctlabs/sp1-contracts ``` To install a specific version: + ```bash forge install succinctlabs/sp1-contracts@ ``` @@ -33,6 +33,16 @@ contract MyContract is SP1Verifier { } ``` +### Deployments + +To deploy the contracts, you can use the `forge script` command and specify the specific contract you want to deploy. For example, to deploy the SP1 Verifier Gateway: + +```bash +forge script SP1VerifierGatewayScript --private-key $PRIVATE_KEY --verify --verifier etherscan --multi --broadcast +``` + +To re-verify already existing deployments, remove the `--broadcast` flag. + ## For Developers: Integrate SP1 Contracts This repository contains the EVM contracts for verifying SP1 PLONK EVM proofs. @@ -41,4 +51,4 @@ You can find more details on the contracts in the [`contracts`](./contracts/READ ## For Contributors -To update the SP1 contracts, please refer to the [`update`](./UPDATE_CONTRACTS.md) file. \ No newline at end of file +To update the SP1 contracts, please refer to the [`update`](./UPDATE_CONTRACTS.md) file. diff --git a/contracts/.env.example b/contracts/.env.example index 759017f..41062f3 100644 --- a/contracts/.env.example +++ b/contracts/.env.example @@ -1,7 +1,7 @@ -### The chains to deploy to +### The chains to deploy to, specified by chain ID (e.g. CHAINS=1,11155111,17000) CHAIN_IDS= -### RPCs for each chain +### RPCs for each chain ID RPC_1= RPC_11155111= RPC_17000= @@ -14,7 +14,7 @@ RPC_84532= RPC_534352= RPC_534351= -# Etherscan API keys for each chain +# Etherscan API keys for each chain ID ETHERSCAN_API_KEY_1= ETHERSCAN_API_KEY_11155111= ETHERSCAN_API_KEY_17000= @@ -25,4 +25,19 @@ ETHERSCAN_API_KEY_421614= ETHERSCAN_API_KEY_8453= ETHERSCAN_API_KEY_84532= ETHERSCAN_API_KEY_534352= -ETHERSCAN_API_KEY_534351= \ No newline at end of file +ETHERSCAN_API_KEY_534351= + +# Etherscan API URLs for each chain ID +ETHERSCAN_API_URL_1=https://api.etherscan.io/api +ETHERSCAN_API_URL_5=https://api-goerli.etherscan.io/api +ETHERSCAN_API_URL_17000=https://api-holesky.etherscan.io/api +ETHERSCAN_API_URL_11155111=https://api-sepolia.etherscan.io/api +ETHERSCAN_API_URL_100=https://api.gnosisscan.io/api +ETHERSCAN_API_URL_137=https://api.polygonscan.com/api +ETHERSCAN_API_URL_420=https://api-optimistic.etherscan.io/api +ETHERSCAN_API_URL_42161=https://api.arbiscan.io/api +ETHERSCAN_API_URL_421614=https://api-sepolia.arbiscan.io/api +ETHERSCAN_API_URL_8453=https://api.basescan.org/api +ETHERSCAN_API_URL_84532=https://api-sepolia.basescan.org/api +ETHERSCAN_API_URL_534352=https://api.scrollscan.com/api +ETHERSCAN_API_URL_534351=https://api-sepolia.scrollscan.com/api \ No newline at end of file diff --git a/contracts/deployments/11155111.json b/contracts/deployments/11155111.json index 3cca36a..33fea2f 100644 --- a/contracts/deployments/11155111.json +++ b/contracts/deployments/11155111.json @@ -1,5 +1,6 @@ { - "CREATE2_SALT": "0x0000000000000000000000000000000000000000000000000000000000000005", - "OWNER": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e", - "SP1_VERIFIER_GATEWAY": "0xE034625C1F2Fe7B8F3C8DC7C4d691b306c01590A" + "CREATE2_SALT": "0x0000000000000000000000000000000000000000000000000000000000000002", + "OWNER": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e", + "SP1_VERIFIER": "0x4588e29cbc2d715B7f8107140B9AA1Fd97Ba1083", + "SP1_VERIFIER_GATEWAY": "0x810995869591a7a98f319c9802f452dabcea9937" } \ No newline at end of file diff --git a/contracts/deployments/421614.json b/contracts/deployments/421614.json index 3cca36a..dea8856 100644 --- a/contracts/deployments/421614.json +++ b/contracts/deployments/421614.json @@ -1,5 +1,6 @@ { - "CREATE2_SALT": "0x0000000000000000000000000000000000000000000000000000000000000005", + "CREATE2_SALT": "0x0000000000000000000000000000000000000000000000000000000000000002", "OWNER": "0xDEd0000E32f8F40414d3ab3a830f735a3553E18e", - "SP1_VERIFIER_GATEWAY": "0xE034625C1F2Fe7B8F3C8DC7C4d691b306c01590A" + "SP1_VERIFIER": "0x4588e29cbc2d715B7f8107140B9AA1Fd97Ba1083", + "SP1_VERIFIER_GATEWAY": "0x810995869591a7a98f319c9802f452dabcea9937" } \ No newline at end of file diff --git a/contracts/foundry.toml b/contracts/foundry.toml index 0ce05c3..d3d76d3 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -18,15 +18,28 @@ ignore = ["lib/**"] # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options +[rpc_endpoints] +1 = "${RPC_1}" +11155111 = "${RPC_11155111}" +17000 = "${RPC_17000}" +100 = "${RPC_100}" +137 = "${RPC_137}" +42161 = "${RPC_42161}" +421614 = "${RPC_421614}" +8453 = "${RPC_8453}" +84532 = "${RPC_84532}" +534352 = "${RPC_534352}" +534351 = "${RPC_534351}" + [etherscan] -ethereum = { key = "${ETHERSCAN_API_KEY_1}", url = "${ETHERSCAN_API_URL_1}" } -sepolia = { key = "${ETHERSCAN_API_KEY_11155111}", url = "${ETHERSCAN_API_URL_11155111}" } -holesky = { key = "${ETHERSCAN_API_KEY_17000}", url = "${ETHERSCAN_API_URL_17000}" } -gnosis = { key = "${ETHERSCAN_API_KEY_100}", url = "${ETHERSCAN_API_URL_100}" } -polygon = { key = "${ETHERSCAN_API_KEY_137}", url = "${ETHERSCAN_API_URL_137}" } -arbitrum = { key = "${ETHERSCAN_API_KEY_42161}", url = "${ETHERSCAN_API_URL_42161}" } -arbitrum-sepolia = { key = "${ETHERSCAN_API_KEY_421614}", url = "${ETHERSCAN_API_URL_421614}" } -base = { key = "${ETHERSCAN_API_KEY_8453}", url = "${ETHERSCAN_API_URL_8453}" } -base-sepolia = { key = "${ETHERSCAN_API_KEY_84532}", url = "${ETHERSCAN_API_URL_84532}" } -scroll = { key = "${ETHERSCAN_API_KEY_534352}", url = "${ETHERSCAN_API_URL_534352}" } -scroll-sepolia = { key = "${ETHERSCAN_API_KEY_534351}", url = "${ETHERSCAN_API_URL_534351}" } \ No newline at end of file +1 = { key = "${ETHERSCAN_API_KEY_1}", url = "${ETHERSCAN_API_URL_1}" } +11155111 = { key = "${ETHERSCAN_API_KEY_11155111}", url = "${ETHERSCAN_API_URL_11155111}" } +17000 = { key = "${ETHERSCAN_API_KEY_17000}", url = "${ETHERSCAN_API_URL_17000}" } +100 = { key = "${ETHERSCAN_API_KEY_100}", url = "${ETHERSCAN_API_URL_100}" } +137 = { key = "${ETHERSCAN_API_KEY_137}", url = "${ETHERSCAN_API_URL_137}" } +42161 = { key = "${ETHERSCAN_API_KEY_42161}", url = "${ETHERSCAN_API_URL_42161}" } +421614 = { key = "${ETHERSCAN_API_KEY_421614}", url = "${ETHERSCAN_API_URL_421614}" } +8453 = { key = "${ETHERSCAN_API_KEY_8453}", url = "${ETHERSCAN_API_URL_8453}" } +84532 = { key = "${ETHERSCAN_API_KEY_84532}", url = "${ETHERSCAN_API_URL_84532}" } +534352 = { key = "${ETHERSCAN_API_KEY_534352}", url = "${ETHERSCAN_API_URL_534352}" } +534351 = { key = "${ETHERSCAN_API_KEY_534351}", url = "${ETHERSCAN_API_URL_534351}" } \ No newline at end of file diff --git a/contracts/script/deploy/SP1Verifier.s.sol b/contracts/script/deploy/SP1Verifier.s.sol index 14ed7f3..d083e13 100644 --- a/contracts/script/deploy/SP1Verifier.s.sol +++ b/contracts/script/deploy/SP1Verifier.s.sol @@ -7,10 +7,10 @@ import {SP1Verifier} from "../../src/v1.0.8-testnet/SP1Verifier.sol"; import {SP1VerifierGateway} from "../../src/SP1VerifierGateway.sol"; contract SP1VerifierScript is BaseScript { - function run() external multichain broadcaster { - console.log("Deploying SP1_VERIFIER on chain %s", vm.toString(block.chainid)); + string internal constant KEY = "SP1_VERIFIER"; - // Read env variables + function run() external multichain(KEY) broadcaster { + // Read config bytes32 CREATE2_SALT = readBytes32("CREATE2_SALT"); address SP1_VERIFIER_GATEWAY = readAddress("SP1_VERIFIER_GATEWAY"); @@ -22,6 +22,6 @@ contract SP1VerifierScript is BaseScript { gateway.addRoute(verifier); // Write address - writeAddress("SP1_VERIFIER", verifier); + writeAddress(KEY, verifier); } } diff --git a/contracts/script/deploy/SP1VerifierGateway.sol b/contracts/script/deploy/SP1VerifierGateway.sol index dcc2bc3..93cd37e 100644 --- a/contracts/script/deploy/SP1VerifierGateway.sol +++ b/contracts/script/deploy/SP1VerifierGateway.sol @@ -6,10 +6,10 @@ import {BaseScript} from "../utils/Base.s.sol"; import {SP1VerifierGateway} from "../../src/SP1VerifierGateway.sol"; contract SP1VerifierGatewayScript is BaseScript { - function run() external multichain broadcaster { - console.log("Deploying SP1_VERIFIER_GATEWAY on chain %s", vm.toString(block.chainid)); + string internal constant KEY = "SP1_VERIFIER_GATEWAY"; - // Read env variables + function run() external multichain(KEY) broadcaster { + // Read config bytes32 CREATE2_SALT = readBytes32("CREATE2_SALT"); address OWNER = readAddress("OWNER"); @@ -17,6 +17,6 @@ contract SP1VerifierGatewayScript is BaseScript { address gateway = address(new SP1VerifierGateway{salt: CREATE2_SALT}(OWNER)); // Write address - writeAddress("SP1_VERIFIER_GATEWAY", gateway); + writeAddress(KEY, gateway); } } diff --git a/contracts/script/utils/Base.s.sol b/contracts/script/utils/Base.s.sol index 45ec3e9..90b6039 100644 --- a/contracts/script/utils/Base.s.sol +++ b/contracts/script/utils/Base.s.sol @@ -6,7 +6,7 @@ import "forge-std/console.sol"; import {stdJson} from "forge-std/StdJson.sol"; import {Script} from "forge-std/Script.sol"; -/// @notice Script to inherit from to get access to helper functions +/// @notice Script to inherit from to get access to helper functions for deployments. abstract contract BaseScript is Script { using stdJson for string; @@ -20,38 +20,41 @@ abstract contract BaseScript is Script { /// @notice When used, runs the script on the chains specified in the `CHAIN_IDS` env variable. /// Must have a `RPC_${CHAIN_ID}` env variable set for each chain. - modifier multichain() { + modifier multichain(string memory KEY) { uint256[] memory chainIds = vm.envUint("CHAIN_IDS", ","); for (uint256 i = 0; i < chainIds.length; i++) { uint256 chainId = chainIds[i]; // Switch to the chain using the RPC - string memory rpc = vm.envString(string.concat("RPC_", vm.toString(chainId))); - vm.createSelectFork(rpc); + vm.createSelectFork(vm.toString(chainId)); - // or try vm.createSelectFork(vm.rpcUrl(chainName)); - // https://github.com/ourzora/zora-protocol/blob/87fd4a0f06cbabe9bb081eb01babc7222bd1db91/packages/frames/script/MultichainScript.sol#L35 + console.log("Deploying %s to chain %s", KEY, vm.toString(block.chainid)); _; } } + /// @notice Returns the directory of the deployments. function directory() internal view returns (string memory) { return string.concat(vm.projectRoot(), "/deployments/"); } + /// @notice Returns the file name for the current chain. function file() internal view returns (string memory) { return string.concat(vm.toString(block.chainid), ".json"); } + /// @notice Returns the path to the deployments file for the current chain. function path() internal view returns (string memory) { return string.concat(directory(), file()); } + /// @notice Returns the deployments file contents for the current chain. function deployments() internal view returns (string memory) { return vm.readFile(path()); } + /// @notice Ensures that the deployments file exists for the current chain. function ensureExists() internal { if (!vm.exists(directory())) { vm.createDir(directory(), true); @@ -62,14 +65,17 @@ abstract contract BaseScript is Script { } } + /// @notice Reads an address from the deployments file for the current chain. function readAddress(string memory key) internal view returns (address) { return deployments().readAddress(string.concat(".", key)); } + /// @notice Reads a bytes32 from the deployments file for the current chain. function readBytes32(string memory key) internal view returns (bytes32) { return deployments().readBytes32(string.concat(".", key)); } + /// @notice Writes an address to the deployments file for the current chain. function writeAddress(string memory key, address value) internal { ensureExists(); @@ -82,6 +88,7 @@ abstract contract BaseScript is Script { } } + /// @notice Writes a bytes32 to the deployments file for the current chain. function writeBytes32(string memory key, bytes32 value) internal { ensureExists(); diff --git a/contracts/script/verify.sh b/contracts/script/verify.sh deleted file mode 100755 index 2cc7a58..0000000 --- a/contracts/script/verify.sh +++ /dev/null @@ -1,71 +0,0 @@ -USAGE="Usage: ./verify.sh \n Example: ./verify.sh \"SP1VerifierGateway\" \"5 420 84531 421613\" \"true\" \"$(cast abi-encode "constructor(address)" "0x6e4f1e9ea315ebfd69d18c2db974eef6105fb803")\"" - -if [ -z "$1" ]; then - echo $USAGE - exit 1 -fi - -if [ -z "$2" ]; then - echo $USAGE - exit 1 -fi - -if [[ -z "$3" || ( "$3" != "true" && "$3" != "false" ) ]]; then - echo "$USAGE" - exit 1 -fi - -CONTRACT=$1 -IFS=' ' read -r -a CHAIN_IDS <<< "$2" -IS_PROXY=$3 -CONSTRUCTOR_ARGS=$4 - -# Load environment variables from .env -source .env - -# Create .env.deployments if it doesn't exist -if [ ! -f .env.deployments ]; then - touch .env.deployments -fi - -for chain_id in "${CHAIN_IDS[@]}"; do - set -a - source .env.deployments - set +a - - contract=$CONTRACT - constructor_args=${CONSTRUCTOR_ARGS[$i]} - is_proxy=${IS_PROXY[$i]} - snake_case=$(echo $contract | sed 's/\([a-z0-9]\)\([A-Z]\)/\1_\2/g' | tr '[:lower:]' '[:upper:]') - - address_var=$(echo "${snake_case}_${chain_id}") - address=$(echo $(eval echo "\$$address_var")) - - etherscan_key_var=$(echo 'ETHERSCAN_API_KEY_'"${chain_id}") - etherscan_key=$(echo $(eval echo "\$$etherscan_key_var")) - - # Proxy logic - if $is_proxy; then - rpc_url_var=$(echo 'RPC_'"${chain_id}") - rpc_url=$(echo $(eval echo "\$$rpc_url_var")) - # ERC1967Proxy implementation slot - impl=$(cast storage $address 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc --rpc-url $rpc_url) - impl_addr=$(cast abi-decode "a()(address)" $impl) - proxy_constructor_calldata=$(cast abi-encode "constructor(address,bytes)" $impl_addr "$constructor_args") - echo "Verifying Proxy_${chain_id} @ $address with CONSTRUCTOR_ARGS: ${constructor_args}" - forge verify-contract $address src/upgrade/Proxy.sol:Proxy --chain ${chain_id} --watch --constructor-args $proxy_constructor_calldata --verifier etherscan --etherscan-api-key $etherscan_key - address=$impl_addr - constructor_args="" - fi - - echo "Verifying ${contract}_${chain_id} @ $address with CONSTRUCTOR_ARGS: ${constructor_args}" - if [ -n "$constructor_args" ]; then - forge verify-contract $address $contract --chain ${chain_id} --watch --constructor-args $constructor_args --verifier etherscan --etherscan-api-key $etherscan_key - else - forge verify-contract $address $contract --chain ${chain_id} --watch --verifier etherscan --etherscan-api-key $etherscan_key - fi -done - - -// maybe can be done in forge: https://github.com/ethereum-optimism/optimism/blob/6062bc14aa887cbbb186377103b99ae5f2cf76e6/packages/contracts-bedrock/scripts/Artifacts.s.sol#L232 -sepollia, arb-sepolia, base-sepolia \ No newline at end of file