From f289a942cd9eb15646be7e47a30bf234b2d94f11 Mon Sep 17 00:00:00 2001 From: Rooh Afza Date: Tue, 23 Jan 2024 17:17:40 -0800 Subject: [PATCH] feat: add deployment scripts --- packages/splits-v2/.env.sample | 6 ++ packages/splits-v2/README.md | 59 ++++++++++++++----- packages/splits-v2/foundry.toml | 9 ++- packages/splits-v2/package.json | 6 +- packages/splits-v2/script/Base.s.sol | 49 ++++++++++++--- .../splits-v2/script/SplitFactoryV2.s.sol | 27 +++++++++ .../splits-v2/script/SplitsWarehouse.s.sol | 28 +++++++++ packages/splits-v2/script/config/1.json | 4 ++ .../splits-v2/script/config/11155111.json | 4 ++ 9 files changed, 167 insertions(+), 25 deletions(-) create mode 100644 packages/splits-v2/.env.sample create mode 100644 packages/splits-v2/script/SplitFactoryV2.s.sol create mode 100644 packages/splits-v2/script/SplitsWarehouse.s.sol create mode 100644 packages/splits-v2/script/config/1.json create mode 100644 packages/splits-v2/script/config/11155111.json diff --git a/packages/splits-v2/.env.sample b/packages/splits-v2/.env.sample new file mode 100644 index 0000000..c021723 --- /dev/null +++ b/packages/splits-v2/.env.sample @@ -0,0 +1,6 @@ +PRIVATE_KEY= + +MAINNET_RPC_URL= +SEPOLIA_RPC_URL= + +ETHERSCAN_API_KEY= \ No newline at end of file diff --git a/packages/splits-v2/README.md b/packages/splits-v2/README.md index 6882675..5b75729 100644 --- a/packages/splits-v2/README.md +++ b/packages/splits-v2/README.md @@ -1,27 +1,56 @@ ## Splits v2 -## Usage - ### Build -```shell -$ forge build -``` +`pnpm build` ### Test -```shell -$ forge test -``` +`pnpm test` + +#### Coverage + +`pnpm test:coverage` + +#### Coverage Report + +`pnpm test:coverage:report` + +### lint + +`pnpm lint` ### Format -```shell -$ forge fmt -``` +`pnpm format` + +### Deployment + +To deploy contracts, please ensure you have the environment variables set in `.env` file. Please refer to `.env.sample` +for the required environment variables. + +For a chain not present in .env.sample, add the rpc url and etherscan API key to the .env.sample file and +[foundry.toml](./foundry.toml) file. + +To understand how the configuration works, please refer to [foundry docs](https://book.getfoundry.sh/cheatcodes/rpc). + +Each contract has its own deployment script. A config file is present in the `config` folder for each chain. The config +files contains the input needed for the constructors of the contracts. + +To deploy contracts and verify contracts, run the following command: + +#### Splits Warehouse + +`pnpm deploy:SplitsWarehouse` + +For a test run, use the following command: + +`pnpm deploy:SplitsWarehouse:test` + +#### Split Factory V2 + +`pnpm deploy:SplitFactoryV2` -### Gas Snapshots +For a test run, use the following command: -```shell -$ forge snapshot -``` +`pnpm deploy:SplitFactoryV2:test` diff --git a/packages/splits-v2/foundry.toml b/packages/splits-v2/foundry.toml index c453026..2e5453f 100644 --- a/packages/splits-v2/foundry.toml +++ b/packages/splits-v2/foundry.toml @@ -9,6 +9,8 @@ solc = "0.8.18" src = "src" test = "test" allow_paths = ['../../node_modules'] +fs_permissions = [{ access = "read-write", path = "./" }] + [profile.default.fuzz] max_test_rejects = 1_000_000 @@ -62,9 +64,12 @@ targets = [ "src/splitters/SplitWalletV2.sol" = ["SplitWalletV2"] "src/splitters/SplitFactoryV2.sol" = ["SplitFactoryV2"] + [etherscan] -mainnet = { key = "${API_KEY_ETHERSCAN}" } +mainnet = { key = "${ETHERSCAN_API_KEY}" } +sepolia = { key = "${ETHERSCAN_API_KEY}" } [rpc_endpoints] localhost = "http://localhost:8545" -mainnet = "https://eth-mainnet.g.alchemy.com/v2/${API_KEY_ALCHEMY}" +mainnet = "${MAINNET_RPC_URL}" +sepolia = "${SEPOLIA_RPC_URL}" diff --git a/packages/splits-v2/package.json b/packages/splits-v2/package.json index e372db4..3947705 100644 --- a/packages/splits-v2/package.json +++ b/packages/splits-v2/package.json @@ -10,7 +10,11 @@ "format": "prettier --write \"**/*.{json,md,yml}\" --ignore-path=.prettierignore", "test": "forge test -vvv", "test:coverage": "forge coverage", - "test:coverage:report": "forge coverage --report lcov && genhtml lcov.info --branch-coverage --output-dir coverage" + "test:coverage:report": "forge coverage --report lcov && genhtml lcov.info --branch-coverage --output-dir coverage", + "deploy:SplitsWarehouse:test": "export DRY_RUN=true && source .env && forge script script/SplitsWarehouse.s.sol:SplitsWarehouseScript -vvvvv --rpc-url ", + "deploy:SplitsWarehouse": "export DRY_RUN=false && source .env && forge script script/SplitsWarehouse.s.sol:SplitsWarehouseScript --broadcast --verify -vvvvv --rpc-url ", + "deploy:SplitFactoryV2:test": "export DRY_RUN=true && source .env && forge script script/SplitFactoryV2.s.sol:SplitFactoryV2Script -vvvvv --rpc-url ", + "deploy:SplitFactoryV2": "export DRY_RUN=false && source .env && forge script script/SplitFactoryV2.s.sol:SplitFactoryV2Script --broadcast --verify -vvvvv --rpc-url " }, "keywords": [], "author": "@splits", diff --git a/packages/splits-v2/script/Base.s.sol b/packages/splits-v2/script/Base.s.sol index 9806560..594487f 100644 --- a/packages/splits-v2/script/Base.s.sol +++ b/packages/splits-v2/script/Base.s.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.18; import { ICreateX } from "./ICreateX.sol"; import { Script } from "forge-std/Script.sol"; import { stdJson } from "forge-std/stdJson.sol"; +import { LibString } from "solady/utils/LibString.sol"; contract BaseScript is Script { using stdJson for string; @@ -11,22 +12,21 @@ contract BaseScript is Script { ICreateX immutable CREATEX = ICreateX(0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed); function getConfig() internal view returns (string memory) { - string memory inputDir = string.concat(vm.projectRoot(), "/script/config/"); - string memory chainDir = string.concat(vm.toString(block.chainid), "/"); - string memory file = string.concat("config.json"); - return vm.readFile(string.concat(inputDir, chainDir, file)); + string memory dir = string.concat(vm.projectRoot(), "/script/config/"); + string memory file = string.concat(vm.toString(block.chainid), ".json"); + return vm.readFile(string.concat(dir, file)); } function getAddressFromConfig(string memory _key) internal view returns (address) { - return getConfig().readAddress(_key); + return getConfig().readAddress(string.concat(".", _key)); } function getStringFromConfig(string memory _key) internal view returns (string memory) { - return getConfig().readString(_key); + return getConfig().readString(string.concat(".", _key)); } function getUintFromConfig(string memory _key) internal view returns (uint256) { - return getConfig().readUint(_key); + return getConfig().readUint(string.concat(".", _key)); } function create3(bytes32 salt, bytes memory initCode) internal returns (address) { @@ -37,4 +37,39 @@ contract BaseScript is Script { // keccak256(abi.encodePacked(deployer, hex"01", _salt)) return bytes32(abi.encodePacked(deployer, hex"01", _salt)); } + + function updateDeployment(address _contract, string memory _name) internal { + if (isDryRun()) { + return; + } + string memory directory = string.concat(vm.projectRoot(), "/deployments/"); + if (!vm.exists(directory)) { + vm.createDir(directory, true); + } + + string memory file = string.concat(vm.projectRoot(), "/deployments/", vm.toString(block.chainid), ".json"); + bool exists = vm.exists(file); + if (!exists) { + vm.writeFile(file, "{}"); + } + + string memory json = vm.readFile(file); + if (vm.keyExists(json, string.concat(".", _name))) { + vm.writeJson(LibString.toHexStringChecksummed(_contract), file, string.concat(".", _name)); + } else { + string memory root = "root"; + vm.serializeJson(root, json); + vm.writeJson(vm.serializeAddress(root, _name, _contract), file); + } + } + + function computeCreate3Address(bytes32 salt, address deployer) public view returns (address) { + bytes32 guardedSalt = keccak256(abi.encode(deployer, block.chainid, salt)); + + return CREATEX.computeCreate3Address(guardedSalt); + } + + function isDryRun() internal view returns (bool) { + return vm.envBool("DRY_RUN"); + } } diff --git a/packages/splits-v2/script/SplitFactoryV2.s.sol b/packages/splits-v2/script/SplitFactoryV2.s.sol new file mode 100644 index 0000000..51818f1 --- /dev/null +++ b/packages/splits-v2/script/SplitFactoryV2.s.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +import { SplitFactoryV2 } from "../src/splitters/SplitFactoryV2.sol"; + +import { BaseScript } from "./Base.s.sol"; +import { console2 } from "forge-std/console2.sol"; + +contract SplitFactoryV2Script is BaseScript { + uint88 constant DEPLOYMENT_SALT = 1; + + function run() public { + address warehouse = getAddressFromConfig("splitsWarehouse"); + + bytes memory args = abi.encode(warehouse); + + uint256 privateKey = vm.envUint("PRIVATE_KEY"); + address deployer = vm.addr(privateKey); + + bytes32 salt = computeSalt(deployer, bytes11(DEPLOYMENT_SALT)); + + vm.startBroadcast(privateKey); + address factory = create3(salt, abi.encodePacked(type(SplitFactoryV2).creationCode, args)); + vm.stopBroadcast(); + updateDeployment(factory, "SplitFactoryV2"); + } +} diff --git a/packages/splits-v2/script/SplitsWarehouse.s.sol b/packages/splits-v2/script/SplitsWarehouse.s.sol new file mode 100644 index 0000000..1fd731b --- /dev/null +++ b/packages/splits-v2/script/SplitsWarehouse.s.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +import { SplitsWarehouse } from "../src/SplitsWarehouse.sol"; + +import { BaseScript } from "./Base.s.sol"; +import { console2 } from "forge-std/console2.sol"; + +contract SplitsWarehouseScript is BaseScript { + uint88 constant DEPLOYMENT_SALT = 0; + + function run() public { + string memory name = getStringFromConfig("nativeTokenName"); + string memory symbol = getStringFromConfig("nativeTokenSymbol"); + + bytes memory args = abi.encode(name, symbol); + + uint256 privateKey = vm.envUint("PRIVATE_KEY"); + address deployer = vm.addr(privateKey); + + bytes32 salt = computeSalt(deployer, bytes11(DEPLOYMENT_SALT)); + + vm.startBroadcast(privateKey); + address warehouse = create3(salt, abi.encodePacked(type(SplitsWarehouse).creationCode, args)); + vm.stopBroadcast(); + updateDeployment(warehouse, "SplitsWarehouse"); + } +} diff --git a/packages/splits-v2/script/config/1.json b/packages/splits-v2/script/config/1.json new file mode 100644 index 0000000..28ff2ff --- /dev/null +++ b/packages/splits-v2/script/config/1.json @@ -0,0 +1,4 @@ +{ + "nativeTokenName": "Splits Wrapped Ether", + "nativeTokenSymbol": "SplitsETH" +} diff --git a/packages/splits-v2/script/config/11155111.json b/packages/splits-v2/script/config/11155111.json new file mode 100644 index 0000000..28ff2ff --- /dev/null +++ b/packages/splits-v2/script/config/11155111.json @@ -0,0 +1,4 @@ +{ + "nativeTokenName": "Splits Wrapped Ether", + "nativeTokenSymbol": "SplitsETH" +}