From 4e6cc418dbbe7e713ffd9dae3c7f37dfcebea39d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8r=E2=88=82=C2=A1?= Date: Mon, 18 Nov 2024 18:35:58 +0700 Subject: [PATCH 1/3] Backporting the makefile lifecycle improvements --- .env.example | 28 ++++-- .env.test.example | 14 +++ .gitignore | 1 + Makefile | 206 ++++++++++++++++++++++++++++++++++++++++---- README.md | 191 +++++++++++++++++++++++++++++++++++++--- TEST_TREE.md | 139 ++++++++++++++++-------------- script/Deploy.s.sol | 106 ++++++++++++++--------- 7 files changed, 546 insertions(+), 139 deletions(-) create mode 100644 .env.test.example diff --git a/.env.example b/.env.example index c506dcd..eb35073 100644 --- a/.env.example +++ b/.env.example @@ -1,9 +1,24 @@ -# NETWORK AND DEPLOYMENT WALLET -DEPLOYMENT_PRIVATE_KEY="..." -ALCHEMY_API_KEY="..." -ETHERSCAN_API_KEY="..." -NETWORK="holesky" -DEPLOY_AS_PRODUCTION=true # With false, the script will deploy mock helpers +# NETWORK AND ACCOUNT(s) +DEPLOYMENT_PRIVATE_KEY="0x..." +REFUND_ADDRESS="0x..." + +# The name of the networks to use for test/production +TESTNET_NETWORK="holesky" +PRODNET_NETWORK="mainnet" + +# The RPC of the networks to use for test/production +TESTNET_RPC_URL="https://holesky.drpc.org" +PRODNET_RPC_URL="https://eth.drpc.org" + +# API Keys (optional) +# Note that having these active will slow down unit tests even when not needed +# So recommended to only activate when needed +# ALCHEMY_API_KEY="..." +# ETHERSCAN_API_KEY="..." + +# MULTISIG PARAMETERS +# define a list of multisig members - said multisig will be assigned administrator roles of the ve contracts +MULTISIG_MEMBERS_JSON_FILE_NAME="/script/multisig-members.json" # GOVERNANCE PARAMETERS MIN_VETO_RATIO="300000" # 30% (base 1_000_000) @@ -24,6 +39,7 @@ TAIKO_BRIDGE_ADDRESS="0xA098b76a3Dd499D3F6D58D8AcCaFC8efBFd06807" # Address of t DAO_FACTORY="0xE640Da5AD169630555A86D9b6b9C145B4961b1EB" PLUGIN_SETUP_PROCESSOR="0xCe0B4124dea6105bfB85fB4461c4D39f360E9ef3" PLUGIN_REPO_FACTORY="0x95D563382BeD5AcB458759EE05b27DF2CB019Cc7" + GOVERNANCE_ERC20_BASE="0xC24188a73dc09aA7C721f96Ad8857B469C01dC9f" GOVERNANCE_WRAPPED_ERC20_BASE="0x7a62da7B56fB3bfCdF70E900787010Bc4c9Ca42e" diff --git a/.env.test.example b/.env.test.example new file mode 100644 index 0000000..9b3e753 --- /dev/null +++ b/.env.test.example @@ -0,0 +1,14 @@ +# If deploying against a fork, pass the address of a large token holder. +# This address will be impersonated to distribute tokens to addresses inside test cases. +# The whale needs to hold at least 3000 tokens +TEST_TOKEN_WHALE="" + +# If you are testing with `make test-fork-factory-*`, you need to define the address of the +# existing factory to use. Otherwise, you should use `make test-fork-*` +FACTORY_ADDRESS="" + +# The block number to run test forks against +# If left empty, the live onchain state will be used, which may +# consume API calls or cause rate limits +FORK_TESTNET_BLOCK_NUMBER=2643743 +FORK_PRODNET_BLOCK_NUMBER=21084855 diff --git a/.gitignore b/.gitignore index 8e7fe72..037dbf1 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ docs/ # Dotenv file .env +.env.test lcov.info .DS_Store diff --git a/Makefile b/Makefile index 9096670..60cda8c 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,24 @@ .DEFAULT_TARGET: help +# Import the .env files and export their values (ignore any error if missing) +-include .env +-include .env.test + +# RULE SPECIFIC ENV VARS [optional] + +# Override the verifier and block explorer parameters (network dependent) +deploy-testnet: export ETHERSCAN_API_KEY_PARAM = --etherscan-api-key $(ETHERSCAN_API_KEY) +deploy-prodnet: export ETHERSCAN_API_KEY_PARAM = --etherscan-api-key $(ETHERSCAN_API_KEY) +# deploy-testnet: export VERIFIER_TYPE_PARAM = --verifier blockscout +# deploy-testnet: export VERIFIER_URL_PARAM = --verifier-url "https://server/api\?" + +# CONSTANTS + +TEST_COVERAGE_SRC_FILES:=$(wildcard test/*.sol test/**/*.sol script/*.sol script/**/*.sol src/escrow/increasing/delegation/*.sol src/libs/ProxyLib.sol) +DEPLOY_SCRIPT:=script/Deploy.s.sol:Deploy +VERBOSITY:=-vvv +SHELL:=/bin/bash + SOLIDITY_VERSION=0.8.17 TEST_TREE_MARKDOWN=TEST_TREE.md SOURCE_FILES=$(wildcard test/*.t.yaml test/integration/*.t.yaml) @@ -8,15 +27,55 @@ TARGET_TEST_FILES = $(SOURCE_FILES:.tree=.t.sol) MAKE_TEST_TREE=deno run ./test/script/make-test-tree.ts MAKEFILE=Makefile +# TARGETS + .PHONY: help help: @echo "Available targets:" - @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE) \ - | sed -n 's/^\(.*\): \(.*\)##\(.*\)/- make \1 \3/p' + @grep -E '^[a-zA-Z0-9_-]*:.*?## .*$$' Makefile \ + | sed -n 's/^\(.*\): \(.*\)##\(.*\)/- make \1 \3/p' \ + | sed 's/^- make $$//g' + +: ## + +.PHONY: init +init: .env .env.test ## Check the dependencies and prompt to install if needed + @which deno > /dev/null && echo "Deno is available" || echo "Install Deno: curl -fsSL https://deno.land/install.sh | sh" + @which bulloak > /dev/null && echo "bulloak is available" || echo "Install bulloak: cargo install bulloak" + + @which forge > /dev/null || curl -L https://foundry.paradigm.xyz | bash + @forge build + @which lcov > /dev/null || echo "Note: lcov can be installed by running 'sudo apt install lcov'" + +.PHONY: clean +clean: ## Clean the build artifacts + rm -f $(TREE_FILES) + rm -f $(TEST_TREE_MARKDOWN) + rm -Rf ./out/* lcov.info* ./report/* + +: ## + +.PHONY: test +test: ## Run unit tests, locally + forge test $(VERBOSITY) +# forge test --no-match-path $(FORK_TEST_WILDCARD) $(VERBOSITY) + +test-coverage: report/index.html ## Generate an HTML coverage report under ./report + @which open > /dev/null && open report/index.html || echo -n + @which xdg-open > /dev/null && xdg-open report/index.html || echo -n + +report/index.html: lcov.info.pruned + genhtml $^ -o report --branch-coverage -all: sync markdown ## Builds all tree files and updates the test tree markdown +lcov.info.pruned: lcov.info + lcov --remove ./$< -o ./$@ $^ -sync: $(TREE_FILES) ## Scaffold or sync tree files into solidity tests +lcov.info: $(TEST_COVERAGE_SRC_FILES) + forge coverage --no-match-path $(FORK_TEST_WILDCARD) --report lcov + +: ## + +sync-tests: $(TREE_FILES) ## Scaffold or sync tree files into solidity tests @for file in $^; do \ if [ ! -f $${file%.tree}.t.sol ]; then \ echo "[Scaffold] $${file%.tree}.t.sol" ; \ @@ -27,10 +86,10 @@ sync: $(TREE_FILES) ## Scaffold or sync tree files into solidity tests fi \ done -check: $(TREE_FILES) ## Checks if solidity files are out of sync +check-tests: $(TREE_FILES) ## Checks if solidity files are out of sync bulloak check $^ -markdown: $(TEST_TREE_MARKDOWN) ## Generates a markdown file with the test definitions rendered as a tree +markdown-tests: $(TEST_TREE_MARKDOWN) ## Generates a markdown file with the test definitions rendered as a tree # Internal targets @@ -59,14 +118,131 @@ $(TREE_FILES): $(SOURCE_FILES) cat $$file | $(MAKE_TEST_TREE) > $${file%.t.yaml}.tree ; \ done -# Global +# Copy the .env files if not present +.env: + cp .env.example .env + @echo "NOTE: Edit the correct values of .env before you continue" -.PHONY: init -init: ## Check the dependencies and prompt to install if needed - @which deno > /dev/null && echo "Deno is available" || echo "Install Deno: curl -fsSL https://deno.land/install.sh | sh" - @which bulloak > /dev/null && echo "bulloak is available" || echo "Install bulloak: cargo install bulloak" +.env.test: + cp .env.test.example .env.test + @echo "NOTE: Edit the correct values of .env.test before you continue" -.PHONY: clean -clean: ## Clean the intermediary tree files - rm -f $(TREE_FILES) - rm -f $(TEST_TREE_MARKDOWN) +# : ## + +# #### Fork testing #### + +# test-fork-mint-testnet: export MINT_TEST_TOKENS = true +# test-fork-mint-prodnet: export MINT_TEST_TOKENS = true + +# test-fork-mint-testnet: test-fork-testnet ## Clean fork test, minting test tokens (testnet) +# test-fork-mint-prodnet: test-fork-prodnet ## Clean fork test, minting test tokens (production network) + +# : ## + +# test-fork-testnet: export RPC_URL = $(TESTNET_RPC_URL) +# test-fork-prodnet: export RPC_URL = $(PRODNET_RPC_URL) +# test-fork-testnet: export FORK_BLOCK_NUMBER = $(FORK_TESTNET_BLOCK_NUMBER) +# test-fork-prodnet: export FORK_BLOCK_NUMBER = $(FORK_PRODNET_BLOCK_NUMBER) + +# test-fork-testnet: test-fork ## Fork test using the existing token(s), new factory (testnet) +# test-fork-prodnet: test-fork ## Fork test using the existing token(s), new factory (production network) + +# : ## + +# # Override the fork test mode (existing factory) +# test-fork-factory-testnet: export FORK_TEST_MODE = existing-factory +# test-fork-factory-prodnet: export FORK_TEST_MODE = existing-factory + +# test-fork-factory-testnet: test-fork-testnet ## Fork test using an existing factory (testnet) +# test-fork-factory-prodnet: test-fork-prodnet ## Fork test using an existing factory (production network) + +# .PHONY: test-fork +# test-fork: +# @if [ -z "$(strip $(FORK_BLOCK_NUMBER))" ] ; then \ +# forge test --match-contract $(E2E_TEST_NAME) \ +# --rpc-url $(RPC_URL) \ +# $(VERBOSITY) ; \ +# else \ +# forge test --match-contract $(E2E_TEST_NAME) \ +# --rpc-url $(RPC_URL) \ +# --fork-block-number $(FORK_BLOCK_NUMBER) \ +# $(VERBOSITY) ; \ +# fi + +: ## + +#### Deployment targets #### + +pre-deploy-mint-testnet: export MINT_TEST_TOKENS = true +pre-deploy-testnet: export RPC_URL = $(TESTNET_RPC_URL) +pre-deploy-testnet: export NETWORK = $(TESTNET_NETWORK) +pre-deploy-prodnet: export RPC_URL = $(PRODNET_RPC_URL) +pre-deploy-prodnet: export NETWORK = $(PRODNET_NETWORK) + +pre-deploy-mint-testnet: pre-deploy-testnet ## Simulate a deployment to the testnet, minting test token(s) +pre-deploy-testnet: pre-deploy ## Simulate a deployment to the testnet +pre-deploy-prodnet: pre-deploy ## Simulate a deployment to the production network + +: ## + +deploy-testnet: export RPC_URL = $(TESTNET_RPC_URL) +deploy-testnet: export NETWORK = $(TESTNET_NETWORK) +deploy-prodnet: export RPC_URL = $(PRODNET_RPC_URL) +deploy-prodnet: export NETWORK = $(PRODNET_NETWORK) + +deploy-testnet: export DEPLOYMENT_LOG_FILE=./deployment-$(patsubst "%",%,$(TESTNET_NETWORK))-$(shell date +"%y-%m-%d-%H-%M").log +deploy-prodnet: export DEPLOYMENT_LOG_FILE=./deployment-$(patsubst "%",%,$(PRODNET_NETWORK))-$(shell date +"%y-%m-%d-%H-%M").log + +deploy-testnet: deploy ## Deploy to the testnet and verify +deploy-prodnet: deploy ## Deploy to the production network and verify + +.PHONY: pre-deploy +pre-deploy: + @echo "Simulating the deployment" + forge script $(DEPLOY_SCRIPT) \ + --chain $(NETWORK) \ + --rpc-url $(RPC_URL) \ + $(VERBOSITY) + +.PHONY: deploy +deploy: test + @echo "Starting the deployment" + forge script $(DEPLOY_SCRIPT) \ + --chain $(NETWORK) \ + --rpc-url $(RPC_URL) \ + --broadcast \ + --verify \ + $(VERIFIER_TYPE_PARAM) \ + $(VERIFIER_URL_PARAM) \ + $(ETHERSCAN_API_KEY_PARAM) \ + $(VERBOSITY) | tee $(DEPLOYMENT_LOG_FILE) + +: ## + +refund: export DEPLOYMENT_ADDRESS = $(shell cast wallet address --private-key $(DEPLOYMENT_PRIVATE_KEY)) + +.PHONY: refund +refund: ## Refund the remaining balance left on the deployment account + @echo "Refunding the remaining balance on $(DEPLOYMENT_ADDRESS)" + @if [ -z $(REFUND_ADDRESS) -o $(REFUND_ADDRESS) = "0x0000000000000000000000000000000000000000" ]; then \ + echo "- The refund address is empty" ; \ + exit 1; \ + fi + @BALANCE=$(shell cast balance $(DEPLOYMENT_ADDRESS) --rpc-url $(PRODNET_RPC_URL)) && \ + GAS_PRICE=$(shell cast gas-price --rpc-url $(PRODNET_RPC_URL)) && \ + REMAINING=$$(echo "$$BALANCE - $$GAS_PRICE * 21000" | bc) && \ + \ + ENOUGH_BALANCE=$$(echo "$$REMAINING > 0" | bc) && \ + if [ "$$ENOUGH_BALANCE" = "0" ]; then \ + echo -e "- No balance can be refunded: $$BALANCE wei\n- Minimum balance: $${REMAINING:1} wei" ; \ + exit 1; \ + fi ; \ + echo -n -e "Summary:\n- Refunding: $$REMAINING (wei)\n- Recipient: $(REFUND_ADDRESS)\n\nContinue? (y/N) " && \ + \ + read CONFIRM && \ + if [ "$$CONFIRM" != "y" ]; then echo "Aborting" ; exit 1; fi ; \ + \ + cast send --private-key $(DEPLOYMENT_PRIVATE_KEY) \ + --rpc-url $(PRODNET_RPC_URL) \ + --value $$REMAINING \ + $(REFUND_ADDRESS) diff --git a/README.md b/README.md index b18140d..75bdfae 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,9 @@ Another key difference is that the Emergency Multisig is designed such in a way See [Deploying the DAO](#deploying-the-dao) below and check out the [contract deployments](./DEPLOYMENTS.md). -## Optimistic Token Voting plugin +## Contracts overview + +### Optimistic Token Voting plugin This plugin is an adapted version of Aragon's [Optimistic Token Voting plugin](https://github.com/aragon/optimistic-token-voting-plugin). @@ -26,14 +28,14 @@ Proposals can only be executed when the veto threshold hasn't been reached after The governance settings need to be defined when the plugin is installed but the DAO can update them at any time. -### Permissions +#### Permissions - Only proposers can create proposals on the plugin - The plugin can execute actions on the DAO - The DAO can update the plugin settings - The DAO can upgrade the plugin -## Multisig (standard flow) +### Multisig (standard flow) It allows the Security Council members to create and approve proposals. After a certain minimum of approvals is met, proposals can be relayed to the [Optimistic Token Voting plugin](#optimistic-token-voting-plugin) only. @@ -43,14 +45,14 @@ The ability to relay proposals to the [Optimistic Token Voting plugin](#optimist ![Standard proposal flow](./img/std-proposal-flow.png) -### Permissions +#### Permissions - Only members can create proposals - Only members can approve - The plugin can only create proposals on the [Optimistic Token Voting plugin](#optimistic-token-voting-plugin) provided that the `duration` is equal or greater than the minimum defined - The DAO can update the plugin settings -## Emergency Multisig +### Emergency Multisig Like before, this plugin allows Security Council members to create and approve proposals. If a super majority approves, proposals can be relayed to the [Optimistic Token Voting plugin](#optimistic-token-voting-plugin) with a delay period of potentially 0. This is, being executed immediately. @@ -62,7 +64,7 @@ There are two key differences with the standard Multisig: ![Emergency proposal flow](./img/emergency-proposal-flow.png) -### Permissions +#### Permissions The Emergency Multisig settings are the same as for the standard Multisig. @@ -71,7 +73,7 @@ The Emergency Multisig settings are the same as for the standard Multisig. - The plugin can only create proposals on the [Optimistic Token Voting plugin](#optimistic-token-voting-plugin) provided that the `duration` is equal or greater than the minimum defined - The DAO can update the plugin settings -## Signer List +### Signer List Both multisigs relate to this contract to determine if an address was listed at a certain block. It allows to read the state and manage the address list given that the appropriate permissions are granted. @@ -99,7 +101,7 @@ What it means: - Can approve - Can execute (they can decrypt the actions and the metadata) -## Encryption Registry +### Encryption Registry This is a helper contract that allows Security Council members ([SignerList](#signer-list) addresses) to register the public key of their deterministic ephemeral wallet. The available public keys will be used to encrypt the proposal metadata and actions. @@ -107,17 +109,17 @@ Given that smart contracts cannot possibly sign or decrypt data, the encryption Refer to the UI repository for the encryption details. -## Delegation Wall +### Delegation Wall This is a very simple contract that serves the purpose of storing the IPFS URI's pointing to the delegation profile posted by all candidates. Profiles can be updated by the owner and read by everyone. -## Installing plugins to the DAO +### Installing plugins to the DAO -### Installing the initial set of plugins on the DAO +#### Installing the initial set of plugins on the DAO This is taken care by the [TaikoDAOFactory](src/factory/TaikoDaoFactory.sol) contract. It is invoked by [scripts/Deploy.s.sol](script/Deploy.s.sol) and it creates a holistic, immutable DAO deployment, given some settings. To create a new DAO with new settings, a new factory needs to be deployed. -### Installing plugins on the existing DAO +#### Installing plugins on the existing DAO Plugin changes need a proposal to be passed when the DAO already exists. @@ -133,6 +135,171 @@ See [OptimisticTokenVotingPluginSetup](src/setup/OptimisticTokenVotingPluginSetu [Learn more about plugin setup's](https://devs.aragon.org/docs/osx/how-it-works/framework/plugin-management/plugin-setup/) and [preparing installations](https://devs.aragon.org/docs/sdk/examples/client/prepare-installation). + +## Setup + +To get started, ensure that [Foundry](https://getfoundry.sh/) and [Make](https://www.gnu.org/software/make/) are installed on your computer. + +### Using the Makefile + +The `Makefile` is the target launcher of the project. It's the recommended way to work with it. It manages the env variables of common tasks and executes only the steps that require being run. + +``` +$ make +Available targets: + +- make init Check the dependencies and prompt to install if needed +- make clean Clean the build artifacts + +- make test Run unit tests, locally +- make test-coverage Generate an HTML coverage report under ./report + +- make sync-tests Scaffold or sync tree files into solidity tests +- make check-tests Checks if solidity files are out of sync +- make markdown-tests Generates a markdown file with the test definitions rendered as a tree + +- make pre-deploy-mint-testnet Simulate a deployment to the testnet, minting test token(s) +- make pre-deploy-testnet Simulate a deployment to the testnet +- make pre-deploy-prodnet Simulate a deployment to the production network + +- make deploy-testnet Deploy to the testnet and verify +- make deploy-prodnet Deploy to the production network and verify + +- make refund Refund the remaining balance left on the deployment account +``` + +Run `make init`: +- It ensures that Foundry is installed +- It runs a first compilation of the project +- It copies `.env.example` into `.env` and `.env.test.example` into `.env.test` + +Next, customize the values of `.env` and optionally `.env.test`. + +### Understanding `.env.example` + +The env.example file contains descriptions for all the initial settings. You don't need all of these right away but should review prior to fork tests and deployments + +## Deployment + +Deployments are done using the deployment factory. This is a singleton contract that will: + +- Deploy all contracts +- Set permissions +- Transfer ownership to a freshly deployed multisig +- Store the addresses of the deployment in a single source of truth that can be queried at any time. + +Check the available make targets to simulate and deploy the smart contracts: + +``` +- make pre-deploy-testnet Simulate a deployment to the defined testnet +- make pre-deploy-prodnet Simulate a deployment to the defined production network +- make deploy-testnet Deploy to the defined testnet network and verify +- make deploy-prodnet Deploy to the production network and verify +``` + +### Deployment Checklist + +- [ ] I have cloned the official repository on my computer and I have checked out the corresponding branch +- [ ] I am using the latest official docker engine, running a Debian Linux (stable) image + - [ ] I have run `docker run --rm -it -v .:/deployment debian:bookworm-slim` + - [ ] I have run `apt update && apt install -y make curl git vim neovim bc` + - [ ] I have run `curl -L https://foundry.paradigm.xyz | bash` + - [ ] I have run `source /root/.bashrc && foundryup` + - [ ] I have run `cd /deployment` + - [ ] I have run `make init` + - [ ] I have printed the contents of `.env` and `.env.test` on the screen +- [ ] I am opening an editor on the `/deployment` folder, within the Docker container +- [ ] The `.env` file contains the correct parameters for the deployment + - [ ] I have created a brand new burner wallet with `cast wallet new` and copied the private key to `DEPLOYMENT_PRIVATE_KEY` within `.env` + - [ ] I have reviewed the target network and RPC URL + - [ ] I have checked that the JSON file under `MULTISIG_MEMBERS_JSON_FILE_NAME` contains the correct list of signers + - [ ] I have ensured all multisig members have undergone a proper security review and are aware of the security implications of being on said multisig + - [ ] I have checked that `MIN_STD_APPROVALS`, `MIN_EMERGENCY_APPROVALS` and `MULTISIG_PROPOSAL_EXPIRATION_PERIOD` are correct + - [ ] I have verified that `TOKEN_ADDRESS` corresponds to an ERC20 contract on the target chain + - [ ] I have checked that `TAIKO_L1_ADDRESS` and `TAIKO_BRIDGE_ADDRESS` are correct + - The plugin ENS subdomain + - [ ] Contains a meaningful and unique value + - The given OSx addresses: + - [ ] Exist on the target network + - [ ] Contain the latest stable official version of the OSx DAO implementation, the Plugin Setup Processor and the Plugin Repo Factory + - [ ] I have verified the values on https://www.npmjs.com/package/@aragon/osx-commons-configs?activeTab=code > `/@aragon/osx-commons-configs/dist/deployments/json/` +- [ ] All my unit tests pass (`make test`) +- **Target test network** + - [ ] I have run a preview deployment on the testnet + - `make pre-deploy-mint-testnet` + - [ ] I have deployed my contracts successfully to the target testnet + - `make deploy-testnet` + - [ ] I have tested that these contracts work successfully on a UI +- **Target production network** + - [ ] I have updated `TOKEN_ADDRESS` to have the address of the testnet token deployed above + - [ ] I have checked that `TAIKO_L1_ADDRESS` and `TAIKO_BRIDGE_ADDRESS` are correct +- [ ] My deployment wallet is a newly created account, ready for safe production deploys. +- My computer: + - [ ] Is running in a safe physical location and a trusted network + - [ ] It exposes no services or ports + - [ ] The wifi or wired network used does does not have open ports to a WAN +- [ ] I have previewed my deploy without any errors + - `make pre-deploy-prodnet` +- [ ] My wallet has sufficient native token for gas + - At least, 15% more than the estimated simulation +- [ ] Unit tests still run clean +- [ ] I have run `git status` and it reports no local changes +- [ ] The current local git branch corresponds to its counterpart on `origin` + - [ ] I confirm that the rest of members of the ceremony pulled the last commit of my branch and reported the same commit hash as my output for `git log -n 1` +- [ ] I have initiated the production deployment with `make deploy-prodnet` + +### Post deployment checklist + +- [ ] The deployment process completed with no errors +- [ ] The deployed factory was deployed by the deployment address +- [ ] The reported contracts have been created created by the newly deployed factory +- [ ] The smart contracts are correctly verified on Etherscan or the corresponding block explorer +- [ ] The output of the latest `deployment-*.log` file corresponds to the console output +- [ ] I have transferred the remaining funds of the deployment wallet to the address that originally funded it + - `make refund` + +### Manual from the command line + +You can of course run all commands from the command line: + +```sh +# Load the env vars +source .env +``` + +```sh +# run unit tests +forge test --no-match-path "test/fork/**/*.sol" +``` + +```sh +# Set the right RPC URL +RPC_URL="https://eth-sepolia.g.alchemy.com/v2/${ALCHEMY_API_KEY}" +``` + +```sh +# Run the deployment script + +# If using Etherscan +forge script --chain "$NETWORK" script/DeployGauges.s.sol:Deploy --rpc-url "$RPC_URL" --broadcast --verify + +# If using BlockScout +forge script --chain "$NETWORK" script/DeployGauges.s.sol:Deploy --rpc-url "$RPC_URL" --broadcast --verify --verifier blockscout --verifier-url "https://sepolia.explorer.mode.network/api\?" +``` + +If you get the error Failed to get EIP-1559 fees, add `--legacy` to the command: + +```sh +forge script --chain "$NETWORK" script/DeployGauges.s.sol:Deploy --rpc-url "$RPC_URL" --broadcast --verify --legacy +``` + +If some contracts fail to verify on Etherscan, retry with this command: + +```sh +forge script --chain "$NETWORK" script/DeployGauges.s.sol:Deploy --rpc-url "$RPC_URL" --verify --legacy --private-key "$DEPLOYMENT_PRIVATE_KEY" --resume +``` + + ## OSx protocol overview OSx [DAO's](https://github.com/aragon/osx/blob/develop/packages/contracts/src/core/dao/DAO.sol) are designed to hold all the assets and rights by themselves. On the other hand, plugins are custom opt-in pieces of logic that can implement any type of governance. They are meant to eventually make the DAO execute a certain set of actions. diff --git a/TEST_TREE.md b/TEST_TREE.md index 7eabb0c..f3a233f 100644 --- a/TEST_TREE.md +++ b/TEST_TREE.md @@ -67,7 +67,8 @@ EmergencyMultisigTest │ │ └── It allows anyone to create │ ├── Given onlyListed is true │ │ ├── Given creation caller is not listed or appointed -│ │ │ └── It reverts +│ │ │ ├── It reverts +│ │ │ └── It reverts if listed before but not now │ │ ├── Given creation caller is appointed by a former signer │ │ │ └── It reverts │ │ ├── Given creation caller is listed and self appointed @@ -87,23 +88,23 @@ EmergencyMultisigTest ├── Given The proposal is not created │ ├── When calling getProposal being uncreated │ │ └── It should return empty values -│ ├── When calling canApprove and approve being uncreated -│ │ ├── It canApprove should return false (when currently listed and self appointed) -│ │ ├── It approve should revert (when currently listed and self appointed) -│ │ ├── It canApprove should return false (when currently listed, appointing someone else now) -│ │ ├── It approve should revert (when currently listed, appointing someone else now) +│ ├── When calling canApprove or approve being uncreated +│ │ ├── It canApprove should return false (when listed and self appointed) +│ │ ├── It approve should revert (when listed and self appointed) +│ │ ├── It canApprove should return false (when listed, appointing someone else now) +│ │ ├── It approve should revert (when listed, appointing someone else now) │ │ ├── It canApprove should return false (when appointed by a listed signer) │ │ ├── It approve should revert (when appointed by a listed signer) -│ │ ├── It canApprove should return false (when currently unlisted and unappointed) -│ │ └── It approve should revert (when currently unlisted and unappointed) +│ │ ├── It canApprove should return false (when unlisted and unappointed) +│ │ └── It approve should revert (when unlisted and unappointed) │ ├── When calling hasApproved being uncreated │ │ └── It hasApproved should always return false -│ └── When calling canExecute and execute being uncreated +│ └── When calling canExecute or execute being uncreated │ └── It canExecute should always return false ├── Given The proposal is open │ ├── When calling getProposal being open │ │ └── It should return the right values -│ ├── When calling canApprove and approve being open +│ ├── When calling canApprove or approve being open │ │ ├── It canApprove should return true (when listed on creation, self appointed now) │ │ ├── It approve should work (when listed on creation, self appointed now) │ │ ├── It approve should emit an event (when listed on creation, self appointed now) @@ -116,7 +117,7 @@ EmergencyMultisigTest │ │ └── It approve should revert (when unlisted on creation, unappointed now) │ ├── When calling hasApproved being open │ │ └── It hasApproved should return false until approved -│ └── When calling canExecute and execute being open +│ └── When calling canExecute or execute being open │ ├── It canExecute should return false (when listed on creation, self appointed now) │ ├── It execute should revert (when listed on creation, self appointed now) │ ├── It canExecute should return false (when listed on creation, appointing someone else now) @@ -128,14 +129,14 @@ EmergencyMultisigTest ├── Given The proposal was approved by the address │ ├── When calling getProposal being approved │ │ └── It should return the right values -│ ├── When calling canApprove and approve being approved +│ ├── When calling canApprove or approve being approved │ │ ├── It canApprove should return false (when listed on creation, self appointed now) │ │ ├── It approve should revert (when listed on creation, self appointed now) │ │ ├── It canApprove should return false (when currently appointed by a signer listed on creation) │ │ └── It approve should revert (when currently appointed by a signer listed on creation) │ ├── When calling hasApproved being approved │ │ └── It hasApproved should return false until approved -│ └── When calling canExecute and execute being approved +│ └── When calling canExecute or execute being approved │ ├── It canExecute should return false (when listed on creation, self appointed now) │ ├── It execute should revert (when listed on creation, self appointed now) │ ├── It canExecute should return false (when currently appointed by a signer listed on creation) @@ -143,7 +144,7 @@ EmergencyMultisigTest ├── Given The proposal passed │ ├── When calling getProposal being passed │ │ └── It should return the right values -│ ├── When calling canApprove and approve being passed +│ ├── When calling canApprove or approve being passed │ │ ├── It canApprove should return false (when listed on creation, self appointed now) │ │ ├── It approve should revert (when listed on creation, self appointed now) │ │ ├── It canApprove should return false (when listed on creation, appointing someone else now) @@ -154,11 +155,11 @@ EmergencyMultisigTest │ │ └── It approve should revert (when unlisted on creation, unappointed now) │ ├── When calling hasApproved being passed │ │ └── It hasApproved should return false until approved -│ ├── When calling canExecute and execute with modified data being passed +│ ├── When calling canExecute or execute with modified data being passed │ │ ├── It execute should revert with modified metadata │ │ ├── It execute should revert with modified actions │ │ └── It execute should work with matching data -│ ├── When calling canExecute and execute being passed +│ ├── When calling canExecute or execute being passed │ │ ├── It canExecute should return true, always │ │ ├── It execute should work, when called by anyone with the actions │ │ ├── It execute should emit an event, when called by anyone with the actions @@ -171,7 +172,7 @@ EmergencyMultisigTest ├── Given The proposal is already executed │ ├── When calling getProposal being executed │ │ └── It should return the right values -│ ├── When calling canApprove and approve being executed +│ ├── When calling canApprove or approve being executed │ │ ├── It canApprove should return false (when listed on creation, self appointed now) │ │ ├── It approve should revert (when listed on creation, self appointed now) │ │ ├── It canApprove should return false (when listed on creation, appointing someone else now) @@ -182,7 +183,7 @@ EmergencyMultisigTest │ │ └── It approve should revert (when unlisted on creation, unappointed now) │ ├── When calling hasApproved being executed │ │ └── It hasApproved should return false until approved -│ └── When calling canExecute and execute being executed +│ └── When calling canExecute or execute being executed │ ├── It canExecute should return false (when listed on creation, self appointed now) │ ├── It execute should revert (when listed on creation, self appointed now) │ ├── It canExecute should return false (when listed on creation, appointing someone else now) @@ -194,7 +195,7 @@ EmergencyMultisigTest └── Given The proposal expired ├── When calling getProposal being expired │ └── It should return the right values - ├── When calling canApprove and approve being expired + ├── When calling canApprove or approve being expired │ ├── It canApprove should return false (when listed on creation, self appointed now) │ ├── It approve should revert (when listed on creation, self appointed now) │ ├── It canApprove should return false (when listed on creation, appointing someone else now) @@ -205,7 +206,7 @@ EmergencyMultisigTest │ └── It approve should revert (when unlisted on creation, unappointed now) ├── When calling hasApproved being expired │ └── It hasApproved should return false until approved - └── When calling canExecute and execute being expired + └── When calling canExecute or execute being expired ├── It canExecute should return false (when listed on creation, self appointed now) ├── It execute should revert (when listed on creation, self appointed now) ├── It canExecute should return false (when listed on creation, appointing someone else now) @@ -283,7 +284,8 @@ MultisigTest │ │ └── It allows anyone to create │ ├── Given onlyListed is true │ │ ├── Given creation caller is not listed or appointed -│ │ │ └── It reverts +│ │ │ ├── It reverts +│ │ │ └── It reverts if listed before but not now │ │ ├── Given creation caller is appointed by a former signer │ │ │ └── It reverts │ │ ├── Given creation caller is listed and self appointed @@ -299,30 +301,30 @@ MultisigTest ├── Given The proposal is not created │ ├── When calling getProposal being uncreated │ │ └── It should return empty values -│ ├── When calling canApprove and approve being uncreated -│ │ ├── It canApprove should return false (when currently listed and self appointed) -│ │ ├── It approve should revert (when currently listed and self appointed) -│ │ ├── It canApprove should return false (when currently listed, appointing someone else now) -│ │ ├── It approve should revert (when currently listed, appointing someone else now) +│ ├── When calling canApprove or approve being uncreated +│ │ ├── It canApprove should return false (when listed and self appointed) +│ │ ├── It approve should revert (when listed and self appointed) +│ │ ├── It canApprove should return false (when listed, appointing someone else now) +│ │ ├── It approve should revert (when listed, appointing someone else now) │ │ ├── It canApprove should return false (when appointed by a listed signer) │ │ ├── It approve should revert (when appointed by a listed signer) -│ │ ├── It canApprove should return false (when currently unlisted and unappointed) -│ │ └── It approve should revert (when currently unlisted and unappointed) +│ │ ├── It canApprove should return false (when unlisted and unappointed) +│ │ └── It approve should revert (when unlisted and unappointed) │ ├── When calling hasApproved being uncreated │ │ └── It hasApproved should always return false -│ └── When calling canExecute and execute being uncreated -│ ├── It canExecute should return false (when currently listed and self appointed) -│ ├── It execute should revert (when currently listed and self appointed) -│ ├── It canExecute should return false (when currently listed, appointing someone else now) -│ ├── It execute should revert (when currently listed, appointing someone else now) +│ └── When calling canExecute or execute being uncreated +│ ├── It canExecute should return false (when listed and self appointed) +│ ├── It execute should revert (when listed and self appointed) +│ ├── It canExecute should return false (when listed, appointing someone else now) +│ ├── It execute should revert (when listed, appointing someone else now) │ ├── It canExecute should return false (when appointed by a listed signer) │ ├── It execute should revert (when appointed by a listed signer) -│ ├── It canExecute should return false (when currently unlisted and unappointed) -│ └── It execute should revert (when currently unlisted and unappointed) +│ ├── It canExecute should return false (when unlisted and unappointed) +│ └── It execute should revert (when unlisted and unappointed) ├── Given The proposal is open │ ├── When calling getProposal being open │ │ └── It should return the right values -│ ├── When calling canApprove and approve being open +│ ├── When calling canApprove or approve being open │ │ ├── It canApprove should return true (when listed on creation, self appointed now) │ │ ├── It approve should work (when listed on creation, self appointed now) │ │ ├── It approve should emit an event (when listed on creation, self appointed now) @@ -341,7 +343,7 @@ MultisigTest │ │ └── It A ProposalCreated event is emitted on the destination plugin │ ├── When calling hasApproved being open │ │ └── It hasApproved should return false until approved -│ └── When calling canExecute and execute being open +│ └── When calling canExecute or execute being open │ ├── It canExecute should return false (when listed on creation, self appointed now) │ ├── It execute should revert (when listed on creation, self appointed now) │ ├── It canExecute should return false (when listed on creation, appointing someone else now) @@ -353,14 +355,14 @@ MultisigTest ├── Given The proposal was approved by the address │ ├── When calling getProposal being approved │ │ └── It should return the right values -│ ├── When calling canApprove and approve being approved +│ ├── When calling canApprove or approve being approved │ │ ├── It canApprove should return false (when listed on creation, self appointed now) │ │ ├── It approve should revert (when listed on creation, self appointed now) │ │ ├── It canApprove should return false (when currently appointed by a signer listed on creation) │ │ └── It approve should revert (when currently appointed by a signer listed on creation) │ ├── When calling hasApproved being approved │ │ └── It hasApproved should return false until approved -│ └── When calling canExecute and execute being approved +│ └── When calling canExecute or execute being approved │ ├── It canExecute should return false (when listed on creation, self appointed now) │ ├── It execute should revert (when listed on creation, self appointed now) │ ├── It canExecute should return false (when currently appointed by a signer listed on creation) @@ -368,7 +370,7 @@ MultisigTest ├── Given The proposal passed │ ├── When calling getProposal being passed │ │ └── It should return the right values -│ ├── When calling canApprove and approve being passed +│ ├── When calling canApprove or approve being passed │ │ ├── It canApprove should return false (when listed on creation, self appointed now) │ │ ├── It approve should revert (when listed on creation, self appointed now) │ │ ├── It canApprove should return false (when listed on creation, appointing someone else now) @@ -379,7 +381,7 @@ MultisigTest │ │ └── It approve should revert (when unlisted on creation, unappointed now) │ ├── When calling hasApproved being passed │ │ └── It hasApproved should return false until approved -│ ├── When calling canExecute and execute being passed +│ ├── When calling canExecute or execute being passed │ │ ├── It canExecute should return true, always │ │ ├── It execute should work, when called by anyone │ │ ├── It execute should emit an event, when called by anyone @@ -392,7 +394,7 @@ MultisigTest ├── Given The proposal is already executed │ ├── When calling getProposal being executed │ │ └── It should return the right values -│ ├── When calling canApprove and approve being executed +│ ├── When calling canApprove or approve being executed │ │ ├── It canApprove should return false (when listed on creation, self appointed now) │ │ ├── It approve should revert (when listed on creation, self appointed now) │ │ ├── It canApprove should return false (when listed on creation, appointing someone else now) @@ -403,7 +405,7 @@ MultisigTest │ │ └── It approve should revert (when unlisted on creation, unappointed now) │ ├── When calling hasApproved being executed │ │ └── It hasApproved should return false until approved -│ └── When calling canExecute and execute being executed +│ └── When calling canExecute or execute being executed │ ├── It canExecute should return false (when listed on creation, self appointed now) │ ├── It execute should revert (when listed on creation, self appointed now) │ ├── It canExecute should return false (when listed on creation, appointing someone else now) @@ -415,7 +417,7 @@ MultisigTest └── Given The proposal expired ├── When calling getProposal being expired │ └── It should return the right values - ├── When calling canApprove and approve being expired + ├── When calling canApprove or approve being expired │ ├── It canApprove should return false (when listed on creation, self appointed now) │ ├── It approve should revert (when listed on creation, self appointed now) │ ├── It canApprove should return false (when listed on creation, appointing someone else now) @@ -426,7 +428,7 @@ MultisigTest │ └── It approve should revert (when unlisted on creation, unappointed now) ├── When calling hasApproved being expired │ └── It hasApproved should return false until approved - └── When calling canExecute and execute being expired + └── When calling canExecute or execute being expired ├── It canExecute should return false (when listed on creation, self appointed now) ├── It execute should revert (when listed on creation, self appointed now) ├── It canExecute should return false (when listed on creation, appointing someone else now) @@ -506,33 +508,42 @@ SignerListTest │ │ └── It returns false │ └── Given the member is enlisted now │ └── It returns false -├── When calling resolveEncryptionAccountStatus +├── When calling isListedOrAppointedByListed │ ├── Given the caller is a listed signer -│ │ ├── It ownerIsListed should be true -│ │ └── It isAppointed should be false +│ │ └── It listedOrAppointedByListed should be true │ ├── Given the caller is appointed by a signer -│ │ ├── It ownerIsListed should be true -│ │ └── It isAppointed should be true +│ │ └── It listedOrAppointedByListed should be true │ └── Given the caller is not listed or appointed -│ ├── It ownerIsListed should be false -│ └── It isAppointed should be false -├── When calling resolveEncryptionOwner -│ ├── Given the resolved owner is listed on resolveEncryptionOwner +│ └── It listedOrAppointedByListed should be false +├── When calling getListedEncryptionOwnerAtBlock +│ ├── Given the resolved owner is listed on getListedEncryptionOwnerAtBlock │ │ ├── When the given address is the owner │ │ │ └── It should return the given address │ │ └── When the given address is appointed by the owner │ │ └── It should return the resolved owner -│ └── Given the resolved owner is not listed on resolveEncryptionOwner +│ ├── Given the resolved owner was listed on getListedEncryptionOwnerAtBlock +│ │ ├── When the given address is the owner 2 +│ │ │ └── It should return the given address +│ │ └── When the given address is appointed by the owner 2 +│ │ └── It should return the resolved owner +│ └── Given the resolved owner was not listed on getListedEncryptionOwnerAtBlock │ └── It should return a zero value -├── When calling resolveEncryptionAccount -│ ├── Given the resolved owner is listed on resolveEncryptionAccount -│ │ ├── When the given address is appointed -│ │ │ ├── It owner should be the resolved owner -│ │ │ └── It appointedWallet should be the caller -│ │ └── When the given address is not appointed -│ │ ├── It owner should be the caller -│ │ └── It appointedWallet should be resolved appointed wallet -│ └── Given the resolved owner is not listed on resolveEncryptionAccount +├── When calling resolveEncryptionAccountAtBlock +│ ├── Given the resolved owner is listed on resolveEncryptionAccountAtBlock +│ │ ├── When the given address is owner +│ │ │ ├── It owner should be itself +│ │ │ └── It votingWallet should be the appointed address +│ │ └── When the given address is appointed +│ │ ├── It owner should be the resolved owner +│ │ └── It votingWallet should be the given address +│ ├── Given the resolved owner was listed on resolveEncryptionAccountAtBlock +│ │ ├── When the given address is owner 2 +│ │ │ ├── It owner should be itself +│ │ │ └── It votingWallet should be the appointed address +│ │ └── When the given address is appointed 2 +│ │ ├── It owner should be the resolved owner +│ │ └── It votingWallet should be the given address +│ └── Given the resolved owner was not listed on resolveEncryptionAccountAtBlock │ ├── It should return a zero owner │ └── It should return a zero appointedWallet └── When calling getEncryptionRecipients diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index 1d09918..1ec9cff 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -21,9 +21,20 @@ contract Deploy is Script { EmergencyMultisigPluginSetup emergencyMultisigPluginSetup; OptimisticTokenVotingPluginSetup optimisticTokenVotingPluginSetup; - function run() public { - vm.startBroadcast(vm.envUint("DEPLOYMENT_PRIVATE_KEY")); + /// @dev Thrown when attempting to deploy a multisig with no members + error EmptyMultisig(); + modifier broadcast() { + uint256 privKey = vm.envUint("DEPLOYMENT_PRIVATE_KEY"); + vm.startBroadcast(privKey); + console.log("Deploying from:", vm.addr(privKey)); + + _; + + vm.stopBroadcast(); + } + + function run() public broadcast { // NOTE: Deploying the plugin setup's separately because of the code size limit // EncryptionRegistry and DelegationWall are deployed by the TaikoDaoFactory @@ -36,51 +47,23 @@ contract Deploy is Script { ); console.log("Chain ID:", block.chainid); - console.log("Deploying from:", vm.addr(vm.envUint("DEPLOYMENT_PRIVATE_KEY"))); TaikoDaoFactory.DeploymentSettings memory settings; - if (vm.envBool("DEPLOY_AS_PRODUCTION")) { - settings = getProductionSettings(); + if (vm.envOr("MINT_TEST_TOKENS", false)) { + settings = getTestTokenSettings(); } else { - settings = getInternalTestingSettings(); + settings = getProductionSettings(); } console.log(""); TaikoDaoFactory factory = new TaikoDaoFactory(settings); factory.deployOnce(); - TaikoDaoFactory.Deployment memory daoDeployment = factory.getDeployment(); - address delegationWall = address(new DelegationWall()); - - vm.stopBroadcast(); - - // Print summary - console.log("Factory:", address(factory)); - console.log(""); - console.log("DAO:", address(daoDeployment.dao)); - console.log("Voting token:", address(settings.tokenAddress)); - console.log("Taiko Bridge:", settings.taikoBridgeAddress); - console.log(""); - console.log("Plugins"); - console.log("- Multisig plugin:", address(daoDeployment.multisigPlugin)); - console.log("- Emergency multisig plugin:", address(daoDeployment.emergencyMultisigPlugin)); - console.log("- Optimistic token voting plugin:", address(daoDeployment.optimisticTokenVotingPlugin)); - console.log(""); - - console.log("Helpers"); - console.log("- Signer list", address(daoDeployment.signerList)); - console.log("- Encryption registry", address(daoDeployment.encryptionRegistry)); - console.log("- Delegation wall", address(delegationWall)); - - console.log(""); + address delegationWall = address(new DelegationWall()); - console.log("Plugin repositories"); - console.log("- Multisig plugin repository:", address(daoDeployment.multisigPluginRepo)); - console.log("- Emergency multisig plugin repository:", address(daoDeployment.emergencyMultisigPluginRepo)); - console.log( - "- Optimistic token voting plugin repository:", address(daoDeployment.optimisticTokenVotingPluginRepo) - ); + // Done + printDeploymentSummary(factory, delegationWall); } function getProductionSettings() internal view returns (TaikoDaoFactory.DeploymentSettings memory settings) { @@ -117,8 +100,8 @@ contract Deploy is Script { }); } - function getInternalTestingSettings() internal returns (TaikoDaoFactory.DeploymentSettings memory settings) { - console.log("Using internal testing settings"); + function getTestTokenSettings() internal returns (TaikoDaoFactory.DeploymentSettings memory settings) { + console.log("Using test token settings"); address taikoBridgeAddress = address(0x1234567890); address[] memory multisigMembers = readMultisigMembers(); @@ -155,11 +138,50 @@ contract Deploy is Script { }); } - function readMultisigMembers() internal view returns (address[] memory) { + function printDeploymentSummary(TaikoDaoFactory factory, address delegationWall) internal view { + TaikoDaoFactory.DeploymentSettings memory settings = factory.getSettings(); + TaikoDaoFactory.Deployment memory daoDeployment = factory.getDeployment(); + + console.log("Factory:", address(factory)); + console.log(""); + console.log("DAO:", address(daoDeployment.dao)); + console.log("Voting token:", address(settings.tokenAddress)); + console.log("Taiko Bridge:", settings.taikoBridgeAddress); + console.log(""); + + console.log("Plugins"); + console.log("- Multisig plugin:", address(daoDeployment.multisigPlugin)); + console.log("- Emergency multisig plugin:", address(daoDeployment.emergencyMultisigPlugin)); + console.log("- Optimistic token voting plugin:", address(daoDeployment.optimisticTokenVotingPlugin)); + console.log(""); + + console.log("Helpers"); + console.log("- Signer list", address(daoDeployment.signerList)); + console.log("- Encryption registry", address(daoDeployment.encryptionRegistry)); + console.log("- Delegation wall", address(delegationWall)); + + console.log(""); + + console.log("Plugin repositories"); + console.log("- Multisig plugin repository:", address(daoDeployment.multisigPluginRepo)); + console.log("- Emergency multisig plugin repository:", address(daoDeployment.emergencyMultisigPluginRepo)); + console.log( + "- Optimistic token voting plugin repository:", address(daoDeployment.optimisticTokenVotingPluginRepo) + ); + } + + function readMultisigMembers() public view returns (address[] memory result) { // JSON list of members - string memory path = string.concat(vm.projectRoot(), "/script/multisig-members.json"); - string memory json = vm.readFile(path); - return vm.parseJsonAddressArray(json, "$.members"); + string memory membersFilePath = vm.envString("MULTISIG_MEMBERS_JSON_FILE_NAME"); + string memory path = string.concat(vm.projectRoot(), membersFilePath); + string memory strJson = vm.readFile(path); + + bool exists = vm.keyExistsJson(strJson, "$.members"); + if (!exists) revert EmptyMultisig(); + + result = vm.parseJsonAddressArray(strJson, "$.members"); + + if (result.length == 0) revert EmptyMultisig(); } function createTestToken(address[] memory members, address taikoBridge) internal returns (address) { From d658c013494760d9ca70b79ceed9b991b04bf490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8r=E2=88=82=C2=A1?= Date: Mon, 18 Nov 2024 18:53:00 +0700 Subject: [PATCH 2/3] Minor tweaks --- .gitignore | 2 ++ Makefile | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 037dbf1..80e3a6d 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ lcov.info .DS_Store *.tree + +logs/* diff --git a/Makefile b/Makefile index 60cda8c..2cf2ab1 100644 --- a/Makefile +++ b/Makefile @@ -185,6 +185,7 @@ pre-deploy-prodnet: pre-deploy ## Simulate a deployment to the production n : ## +deploy-mint-testnet: export MINT_TEST_TOKENS = true deploy-testnet: export RPC_URL = $(TESTNET_RPC_URL) deploy-testnet: export NETWORK = $(TESTNET_NETWORK) deploy-prodnet: export RPC_URL = $(PRODNET_RPC_URL) @@ -193,6 +194,7 @@ deploy-prodnet: export NETWORK = $(PRODNET_NETWORK) deploy-testnet: export DEPLOYMENT_LOG_FILE=./deployment-$(patsubst "%",%,$(TESTNET_NETWORK))-$(shell date +"%y-%m-%d-%H-%M").log deploy-prodnet: export DEPLOYMENT_LOG_FILE=./deployment-$(patsubst "%",%,$(PRODNET_NETWORK))-$(shell date +"%y-%m-%d-%H-%M").log +deploy-mint-testnet: deploy-testnet ## Deploy to the testnet, mint a token and verify deploy-testnet: deploy ## Deploy to the testnet and verify deploy-prodnet: deploy ## Deploy to the production network and verify @@ -207,6 +209,7 @@ pre-deploy: .PHONY: deploy deploy: test @echo "Starting the deployment" + @mkdir -p logs/ forge script $(DEPLOY_SCRIPT) \ --chain $(NETWORK) \ --rpc-url $(RPC_URL) \ @@ -215,7 +218,7 @@ deploy: test $(VERIFIER_TYPE_PARAM) \ $(VERIFIER_URL_PARAM) \ $(ETHERSCAN_API_KEY_PARAM) \ - $(VERBOSITY) | tee $(DEPLOYMENT_LOG_FILE) + $(VERBOSITY) | tee logs/$(DEPLOYMENT_LOG_FILE) : ## From ace49cbf525a034a76a0ba2cc62f1a2e1ddfbc59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8r=E2=88=82=C2=A1?= Date: Mon, 18 Nov 2024 22:45:29 +0700 Subject: [PATCH 3/3] Deployment --- DEPLOYMENTS.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/DEPLOYMENTS.md b/DEPLOYMENTS.md index b5ea6e7..e45d5bf 100644 --- a/DEPLOYMENTS.md +++ b/DEPLOYMENTS.md @@ -4,6 +4,43 @@ ## Holesly +### November 18th 2024 + +Deployment for internal testing: +- Exit window of 7 days +- L2 disabled +- Using a pre-release voting token + +Deployment with the encryption registry available + +``` + Deploying from: 0x424797Ed6d902E17b9180BFcEF452658e148e0Ab + Chain ID: 17000 + Using test token settings + Minting test tokens for the multisig members and the bridge + + Factory: 0x7D3dA38E856f002f4623B0D32a494E358f72adC9 + + DAO: 0x8caD8b62769710233f319611d064462b633Bbb8C + Voting token: 0x18EE0C13EC97a60fc190bABB348FD87421368920 + Taiko Bridge: 0x0000000000000000000000000000001234567890 + + Plugins + - Multisig plugin: 0xc880dB28A9105e6D69d30E93d99E38eFE84c54CB + - Emergency multisig plugin: 0x3bE6294EB67A3501bF091fD229282F2A51c532d2 + - Optimistic token voting plugin: 0xBB249c027c5De908288104F665A605ceC88ad6CE + + Helpers + - Signer list 0x7716DcB9B83f5f9fB5266767841c3F29555cE2d5 + - Encryption registry 0x94224B656D7D174B2Aa97FFCB188A847E6EA4511 + - Delegation wall 0xb8D78b40014D36F83dDc8219c0621d35E8043167 + + Plugin repositories + - Multisig plugin repository: 0x2d870FCedF2C1204839C3b8bca2Bf6e632b4E602 + - Emergency multisig plugin repository: 0xe069Ae1DCB19A9DE9C516097FaC20ea070311D48 + - Optimistic token voting plugin repository: 0xd774b0976C67832C84848dC6fdadE6189B297A71 +``` + ### October 16th 2024 Deployment for internal testing: