From c25075436f831b219ebe3cf62b04dd6fc16d459a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wei=C3=9Fer=20Hase?= Date: Wed, 24 Jul 2024 20:43:40 +0200 Subject: [PATCH] feat: adding deployment script with faucet erc20s (#114) * feat: adding deployment script with faucet erc20s * fix: missing files * feat: update appData to a valid one * fix: natspec in IFaucet * fix: final tweaks * refactor: deployment script * chore: testnet deployment (updated registry) * fix: rm solhint 1-contract-per-file rule in script dir * fix: update bDao address * fix: gas snapshots * fix: refresh snapshots * fix: extra line * chore: testnet redeployment * chore: add grafiti to factory * chore: deployment addresses * chore: rm grafiti from factory * refactor: reorganize deployment and scripting architecture * fix: linter warnings * fix: deprecate mainnet and testnet id constants * fix: contract comments * feat: adding explicit revert on unknown chains * feat: adding BCoWHelper to deployment script and registry --- README.md | 8 +++++ package.json | 10 +++--- script/.solhint.json | 6 ++++ script/Deploy.s.sol | 43 +++++++++++++++++++++++++ script/DeployBCoWFactory.s.sol | 17 ---------- script/DeployBFactory.s.sol | 17 ---------- script/Params.s.sol | 48 +++++++++++++++------------ script/Registry.s.sol | 36 +++++++++++++++++++++ script/Script.s.sol | 59 ++++++++++++++++++++++++++++++++++ src/interfaces/IFaucet.sol | 14 ++++++++ 10 files changed, 200 insertions(+), 58 deletions(-) create mode 100644 script/.solhint.json create mode 100644 script/Deploy.s.sol delete mode 100644 script/DeployBCoWFactory.s.sol delete mode 100644 script/DeployBFactory.s.sol create mode 100644 script/Registry.s.sol create mode 100644 script/Script.s.sol create mode 100644 src/interfaces/IFaucet.sol diff --git a/README.md b/README.md index 5a1eac83..1245d520 100644 --- a/README.md +++ b/README.md @@ -57,3 +57,11 @@ yarn test # run the tests - The weight represents the intended distribution of value between the tokens in the pool - Modify the pool's swap fee by calling `IBPool.setSwapFee(fee)` - Finalize the pool by calling `IBPool.finalize()` + +# Deployments +Ethereum Mainnet: + - BCoWFactory: (0x21Cd97D70f8475DF3d62917880aF9f41D9a9dCeF)[https://etherscan.io/address/0x21Cd97D70f8475DF3d62917880aF9f41D9a9dCeF#code] + +Ethereum Sepolia: + - BCoWFactory: (0xe8587525430fFC9193831e1113a672f3133C1B8A)[https://sepolia.etherscan.io/address/0xe8587525430fFC9193831e1113a672f3133C1B8A#code] + - BCoWPool: (0xFe1ce255D68B3Bff95E71DDef1c8fc55459aaCd7)[https://sepolia.etherscan.io/address/0xFe1ce255D68B3Bff95E71DDef1c8fc55459aaCd7#code] \ No newline at end of file diff --git a/package.json b/package.json index 3501dc8d..a704b568 100644 --- a/package.json +++ b/package.json @@ -16,15 +16,17 @@ "build": "forge build", "build:optimized": "FOUNDRY_PROFILE=optimized forge build", "coverage": "forge coverage --match-path 'test/unit/**'", - "deploy:bcowfactory:mainnet": "bash -c 'source .env && forge script DeployBCoWFactory -vvvvv --rpc-url $MAINNET_RPC --broadcast --chain mainnet --private-key $MAINNET_DEPLOYER_PK --verify --etherscan-api-key $ETHERSCAN_API_KEY'", - "deploy:bcowfactory:testnet": "bash -c 'source .env && forge script DeployBCoWFactory -vvvvv --rpc-url $SEPOLIA_RPC --broadcast --chain sepolia --private-key $SEPOLIA_DEPLOYER_PK --verify --etherscan-api-key $ETHERSCAN_API_KEY'", - "deploy:bfactory:mainnet": "bash -c 'source .env && forge script DeployBFactory -vvvvv --rpc-url $MAINNET_RPC --broadcast --chain mainnet --private-key $MAINNET_DEPLOYER_PK --verify --etherscan-api-key $ETHERSCAN_API_KEY'", - "deploy:bfactory:testnet": "bash -c 'source .env && forge script DeployBFactory -vvvvv --rpc-url $SEPOLIA_RPC --broadcast --chain sepolia --private-key $SEPOLIA_DEPLOYER_PK --verify --etherscan-api-key $ETHERSCAN_API_KEY'", + "deploy:bcowfactory:mainnet": "forge script DeployBCoWFactory -vvvvv --rpc-url $MAINNET_RPC --broadcast --chain mainnet --private-key $MAINNET_DEPLOYER_PK --verify", + "deploy:bcowfactory:testnet": "forge script DeployBCoWFactory -vvvvv --rpc-url $SEPOLIA_RPC --broadcast --chain sepolia --private-key $SEPOLIA_DEPLOYER_PK --verify", + "deploy:bfactory:mainnet": "forge script DeployBFactory -vvvvv --rpc-url $MAINNET_RPC --broadcast --chain mainnet --private-key $MAINNET_DEPLOYER_PK --verify", + "deploy:bfactory:testnet": "forge script DeployBFactory -vvvvv --rpc-url $SEPOLIA_RPC --broadcast --chain sepolia --private-key $SEPOLIA_DEPLOYER_PK --verify", "lint:bulloak": "find test/unit -name '*.tree' | xargs bulloak check", "lint:check": "solhint 'src/**/*.sol' 'test/**/*.sol' 'script/**/*.sol' && forge fmt --check", "lint:fix": "solhint --fix 'src/**/*.sol' 'test/**/*.sol' 'script/**/*.sol' && sort-package-json && forge fmt", "lint:natspec": "npx @defi-wonderland/natspec-smells --config natspec-smells.config.js", "prepare": "husky install", + "script:mainnet": "forge script MainnetScript -vvvvv --rpc-url $MAINNET_RPC --broadcast --chain mainnet --private-key $MAINNET_DEPLOYER_PK --verify", + "script:testnet": "forge script TestnetScript -vvvvv --rpc-url $SEPOLIA_RPC --broadcast --chain sepolia --private-key $SEPOLIA_DEPLOYER_PK --verify", "smock": "smock-foundry --contracts src/contracts", "test": "yarn test:integration && yarn test:unit", "test:integration": "forge test --ffi --match-path 'test/integration/**' -vvv --isolate", diff --git a/script/.solhint.json b/script/.solhint.json new file mode 100644 index 00000000..c82a6256 --- /dev/null +++ b/script/.solhint.json @@ -0,0 +1,6 @@ +{ + "rules": { + "one-contract-per-file": "off", + "custom-errors": "off" + } +} diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol new file mode 100644 index 00000000..c697f26a --- /dev/null +++ b/script/Deploy.s.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {BCoWFactory} from 'contracts/BCoWFactory.sol'; +import {BCoWHelper} from 'contracts/BCoWHelper.sol'; +import {BFactory} from 'contracts/BFactory.sol'; +import {IBFactory} from 'interfaces/IBFactory.sol'; + +import {Script} from 'forge-std/Script.sol'; +import {Params} from 'script/Params.s.sol'; + +/// @notice This base script is shared across `yarn script:{b|bcow}factory:{mainnet|testnet}` +abstract contract DeployBaseFactory is Script, Params { + constructor() Params(block.chainid) {} + + function run() public { + vm.startBroadcast(); + IBFactory bFactory = _deployFactory(); + bFactory.setBDao(_bFactoryDeploymentParams.bDao); + vm.stopBroadcast(); + } + + function _deployFactory() internal virtual returns (IBFactory); +} + +/// @notice This script will be executed by `yarn script:bfactory:{mainnet|testnet}` +contract DeployBFactory is DeployBaseFactory { + function _deployFactory() internal override returns (IBFactory bFactory) { + bFactory = new BFactory(); + } +} + +/// @notice This script will be executed by `yarn script:bcowfactory:{mainnet|testnet}` +contract DeployBCoWFactory is DeployBaseFactory { + function _deployFactory() internal override returns (IBFactory bFactory) { + bFactory = new BCoWFactory({ + solutionSettler: _bCoWFactoryDeploymentParams.settlement, + appData: _bCoWFactoryDeploymentParams.appData + }); + + new BCoWHelper(address(bFactory)); + } +} diff --git a/script/DeployBCoWFactory.s.sol b/script/DeployBCoWFactory.s.sol deleted file mode 100644 index 57905392..00000000 --- a/script/DeployBCoWFactory.s.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.25; - -import {BCoWFactory} from 'contracts/BCoWFactory.sol'; -import {Script} from 'forge-std/Script.sol'; -import {Params} from 'script/Params.s.sol'; - -contract DeployBCoWFactory is Script, Params { - function run() public { - BCoWFactoryDeploymentParams memory params = _bCoWFactoryDeploymentParams[block.chainid]; - - vm.startBroadcast(); - BCoWFactory bCoWFactory = new BCoWFactory(params.settlement, params.appData); - bCoWFactory.setBDao(params.bDao); - vm.stopBroadcast(); - } -} diff --git a/script/DeployBFactory.s.sol b/script/DeployBFactory.s.sol deleted file mode 100644 index 1d7a0157..00000000 --- a/script/DeployBFactory.s.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.25; - -import {BFactory} from 'contracts/BFactory.sol'; -import {Script} from 'forge-std/Script.sol'; -import {Params} from 'script/Params.s.sol'; - -contract DeployBFactory is Script, Params { - function run() public { - BFactoryDeploymentParams memory params = _bFactoryDeploymentParams[block.chainid]; - - vm.startBroadcast(); - BFactory bFactory = new BFactory(); - bFactory.setBDao(params.bDao); - vm.stopBroadcast(); - } -} diff --git a/script/Params.s.sol b/script/Params.s.sol index 9df9a8f0..330f60e1 100644 --- a/script/Params.s.sol +++ b/script/Params.s.sol @@ -1,36 +1,44 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -contract Params { +/// @notice Deployment parameters +abstract contract Params { struct BFactoryDeploymentParams { address bDao; } struct BCoWFactoryDeploymentParams { - address bDao; address settlement; bytes32 appData; } /// @notice Settlement address address internal constant _GPV2_SETTLEMENT = 0x9008D19f58AAbD9eD0D60971565AA8510560ab41; - - /// @notice AppData identifier - bytes32 internal constant _APP_DATA = bytes32('appData'); - - /// @notice BFactory deployment parameters for each chain - mapping(uint256 _chainId => BFactoryDeploymentParams _params) internal _bFactoryDeploymentParams; - - /// @notice BCoWFactory deployment parameters for each chain - mapping(uint256 _chainId => BCoWFactoryDeploymentParams _params) internal _bCoWFactoryDeploymentParams; - - constructor() { - // Mainnet - _bFactoryDeploymentParams[1] = BFactoryDeploymentParams(address(this)); - _bCoWFactoryDeploymentParams[1] = BCoWFactoryDeploymentParams(address(this), _GPV2_SETTLEMENT, _APP_DATA); - - // Sepolia - _bFactoryDeploymentParams[11_155_111] = BFactoryDeploymentParams(address(this)); - _bCoWFactoryDeploymentParams[11_155_111] = BCoWFactoryDeploymentParams(address(this), _GPV2_SETTLEMENT, _APP_DATA); + /// @notice Balancer DAO address (has controller permission to collect fees from BFactory pools) + address internal constant _B_DAO = 0xce88686553686DA562CE7Cea497CE749DA109f9F; + + /** + * @notice AppData identifier + * @dev Value obtained from https://explorer.cow.fi/appdata?tab=encode + * - appCode: "CoW AMM Balancer" + * - metadata:hooks:version: 0.1.0 + * - version: 1.1.0 + */ + bytes32 internal constant _APP_DATA = 0x362e5182440b52aa8fffe70a251550fbbcbca424740fe5a14f59bf0c1b06fe1d; + + /// @notice BFactory deployment parameters + BFactoryDeploymentParams internal _bFactoryDeploymentParams; + + /// @notice BCoWFactory deployment parameters + BCoWFactoryDeploymentParams internal _bCoWFactoryDeploymentParams; + + constructor(uint256 chainId) { + if (chainId == 1 || chainId == 11_155_111) { + // Ethereum Mainnet & Ethereum Sepolia [Testnet] + _bFactoryDeploymentParams = BFactoryDeploymentParams({bDao: _B_DAO}); + _bCoWFactoryDeploymentParams = BCoWFactoryDeploymentParams({settlement: _GPV2_SETTLEMENT, appData: _APP_DATA}); + } else { + revert('Params: unknown chain ID'); + } } } diff --git a/script/Registry.s.sol b/script/Registry.s.sol new file mode 100644 index 00000000..16334c1c --- /dev/null +++ b/script/Registry.s.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {BCoWFactory} from 'contracts/BCoWFactory.sol'; +import {BCoWHelper} from 'contracts/BCoWHelper.sol'; +import {BFactory} from 'contracts/BFactory.sol'; + +import {Params} from 'script/Params.s.sol'; + +/// @notice Registry of deployed contracts +abstract contract Registry is Params { + /// @notice Balancer Pool Factory + BFactory public bFactory; + /// @notice Balancer CoW Pool Factory + BCoWFactory public bCoWFactory; + /// @notice Balancer CoW Helper + BCoWHelper public bCoWHelper; + + constructor(uint256 chainId) Params(chainId) { + // TODO: redeploy + if (chainId == 1) { + // Ethereum Mainnet + bFactory = BFactory(0xaD0447be7BDC80cf2e6DA20B13599E5dc859b667); + bCoWFactory = BCoWFactory(0x21Cd97D70f8475DF3d62917880aF9f41D9a9dCeF); + bCoWHelper = BCoWHelper(0xE50481D88f147B8b4aaCdf9a1B7b7bA44F87823f); + } else if (chainId == 11_155_111) { + // Ethereum Sepolia [Testnet] + bFactory = BFactory(0x2bfA24B26B85DD812b2C69E3B1cb4C85C886C8E2); + bCoWFactory = BCoWFactory(0xe8587525430fFC9193831e1113a672f3133C1B8A); + bCoWHelper = BCoWHelper(0x0fd365F9Ed185512536E7dbfc7a8DaE43cD3CA09); + } else { + // TODO: add Gnosis chain + revert('Registry: unknown chain ID'); + } + } +} diff --git a/script/Script.s.sol b/script/Script.s.sol new file mode 100644 index 00000000..d21a54cc --- /dev/null +++ b/script/Script.s.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import {IBPool} from 'contracts/BPool.sol'; +import {IFaucet} from 'interfaces/IFaucet.sol'; + +import {Script} from 'forge-std/Script.sol'; +import {Registry} from 'script/Registry.s.sol'; + +/// @notice This base script is shared across `yarn script:{mainnet|testnet}` +abstract contract BaseScript is Registry, Script { + constructor() Registry(block.chainid) {} +} + +/// @notice This script will be executed by `yarn script:mainnet` +contract MainnetScript is BaseScript { + function run() public { + assert(block.chainid == 1); + vm.startBroadcast(); + + // script logic here + + vm.stopBroadcast(); + } +} + +/// @notice This script will be executed by `yarn script:testnet` +contract TestnetScript is BaseScript { + /// @notice ERC20 and Faucet addresses + address internal constant _SEPOLIA_FAUCET = 0x26bfAecAe4D5fa93eE1737ce1Ce7D53F2a0E9b2d; + address internal constant _SEPOLIA_BAL_TOKEN = 0xb19382073c7A0aDdbb56Ac6AF1808Fa49e377B75; + address internal constant _SEPOLIA_DAI_TOKEN = 0xB77EB1A70A96fDAAeB31DB1b42F2b8b5846b2613; + address internal constant _SEPOLIA_USDC_TOKEN = 0x80D6d3946ed8A1Da4E226aa21CCdDc32bd127d1A; + + /// @dev The following is an example of a script that deploys a Balancer CoW pool + function run() public { + assert(block.chainid == 11_155_111); + vm.startBroadcast(); + + // NOTE: dripping can be called by anyone but only once a day (per address) + IFaucet(_SEPOLIA_FAUCET).drip(_SEPOLIA_BAL_TOKEN); + IFaucet(_SEPOLIA_FAUCET).drip(_SEPOLIA_DAI_TOKEN); + IFaucet(_SEPOLIA_FAUCET).drip(_SEPOLIA_USDC_TOKEN); + + IBPool bPool = bCoWFactory.newBPool(); + + IERC20(_SEPOLIA_BAL_TOKEN).approve(address(bPool), type(uint256).max); + IERC20(_SEPOLIA_DAI_TOKEN).approve(address(bPool), type(uint256).max); + IERC20(_SEPOLIA_USDC_TOKEN).approve(address(bPool), type(uint256).max); + + bPool.bind(_SEPOLIA_BAL_TOKEN, 40e18, 1e18); + bPool.bind(_SEPOLIA_DAI_TOKEN, 10e18, 1e18); + bPool.bind(_SEPOLIA_USDC_TOKEN, 10e6, 1e18); + + bPool.finalize(); + vm.stopBroadcast(); + } +} diff --git a/src/interfaces/IFaucet.sol b/src/interfaces/IFaucet.sol new file mode 100644 index 00000000..84d507b3 --- /dev/null +++ b/src/interfaces/IFaucet.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.25; + +/** + * @title IFaucet + * @notice External interface of Sepolia's Faucet contract. + */ +interface IFaucet { + /** + * @notice Drips an amount of tokens to the caller. + * @param token The address of the token to drip. + */ + function drip(address token) external; +}