From 6f4e482fbcae2616080deb3137a5dac7653dceda Mon Sep 17 00:00:00 2001 From: Petrovska Date: Mon, 7 Oct 2024 22:26:05 +0700 Subject: [PATCH 1/3] feat: foundry support for missing test and deployment script --- .gitignore | 18 +++++++- .gitmodules | 3 ++ Makefile | 9 ++++ foundry.toml | 12 +++++ foundry_scripts/InjectorInfraDeployment.s.sol | 28 ++++++++++++ foundry_test/BaseFixture.sol | 36 +++++++++++++++ foundry_test/FactoryTest.t.sol | 45 +++++++++++++++++++ foundry_test/UncoveredLinesTest.t.sol | 27 +++++++++++ lib/forge-std | 1 + 9 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 .gitmodules create mode 100644 Makefile create mode 100644 foundry.toml create mode 100644 foundry_scripts/InjectorInfraDeployment.s.sol create mode 100644 foundry_test/BaseFixture.sol create mode 100644 foundry_test/FactoryTest.t.sol create mode 100644 foundry_test/UncoveredLinesTest.t.sol create mode 160000 lib/forge-std diff --git a/.gitignore b/.gitignore index 4ba749a..11a59c9 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,6 @@ dist/ downloads/ eggs/ .eggs/ -lib/ lib64/ parts/ sdist/ @@ -145,3 +144,20 @@ node_modules # Hardhat Ignition default folder for deployments against a local node ignition/deployments/chain-31337 + +# Foundry +cache/ +out/ + +# Ignores development broadcast logs +!/broadcast +/broadcast/**/dry-run/ + +# Codecov +lcov.info + +# Testing +.gas-snapshot + +# vscode - audit docs +.vscode/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f67502a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/forge-std"] + path = lib/forge-std + url = https://github.com/foundry-rs/forge-std.git diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..78e5d3f --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +-include .env + +# command: test the whole script without broadcasting the transaction into the chain to spot early errors +deployDry: + forge script foundry_scripts/InjectorInfraDeployment.s.sol \ + --rpc-url polygon \ + --slow \ + -vvvv + \ No newline at end of file diff --git a/foundry.toml b/foundry.toml new file mode 100644 index 0000000..3cc58fc --- /dev/null +++ b/foundry.toml @@ -0,0 +1,12 @@ +[profile.default] +src = 'contracts' +out = "out" +libs = ['node_modules','lib'] +test = 'foundry_test' +solc = '0.8.25' + +[fmt] +ignore = ['./contracts/**/*'] + +[rpc_endpoints] +polygon = "${POLYGON_RPC_KEY}" \ No newline at end of file diff --git a/foundry_scripts/InjectorInfraDeployment.s.sol b/foundry_scripts/InjectorInfraDeployment.s.sol new file mode 100644 index 0000000..ce02a03 --- /dev/null +++ b/foundry_scripts/InjectorInfraDeployment.s.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.25; + +import {Script} from "forge-std/Script.sol"; + +import {ChildChainGaugeInjectorV2} from "../contracts/ChildChainGaugeInjectorV2.sol"; +import {ChildChainGaugeInjectorV2Factory} from "../contracts/injectorFactoryV2.sol"; + +/// @notice Deploys the v2 infrastructure for the injectors in the following order: +/// 1. {ChildChainGaugeInjectorV2} -> singleton/implementation purposes (helps verifying in etherscan etc) +/// 2. {ChildChainGaugeInjectorV2Factory} +contract InjectorInfraDeployment is Script { + // injector infrastructure + ChildChainGaugeInjectorV2 injectorImpl; + ChildChainGaugeInjectorV2Factory injectorFactory; + + function run() public { + // read pk from `.env` + uint256 pk = vm.envUint("PRIVATE_KEY"); + vm.startBroadcast(pk); + + // 1. {ChildChainGaugeInjectorV2} + injectorImpl = new ChildChainGaugeInjectorV2(); + + // 2. {ChildChainGaugeInjectorV2Factory} + injectorFactory = new ChildChainGaugeInjectorV2Factory(address(injectorImpl)); + } +} \ No newline at end of file diff --git a/foundry_test/BaseFixture.sol b/foundry_test/BaseFixture.sol new file mode 100644 index 0000000..88f25b2 --- /dev/null +++ b/foundry_test/BaseFixture.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.25; + +import "forge-std/Test.sol"; + +import {ChildChainGaugeInjectorV2} from "../contracts/ChildChainGaugeInjectorV2.sol"; +import {ChildChainGaugeInjectorV2Factory} from "../contracts/injectorFactoryV2.sol"; + +contract BaseFixture is Test { + // injector instance + ChildChainGaugeInjectorV2 injector; + + // factory instance + ChildChainGaugeInjectorV2Factory factory; + + // constants + address constant GAUGE = 0x3Eae4a1c2E36870A006E816930d9f55DF0a72a13; + address constant GAUGE_2 = 0xc7e5FE004416A96Cb2C7D6440c28aE92262f7695; + address constant LM_MULTISIG = 0xc38c5f97B34E175FFd35407fc91a937300E33860; + address constant AUTHORIZER_ADAPTER = 0xAB093cd16e765b5B23D34030aaFaF026558e0A19; + address constant TEST_TOKEN_WHALE = 0xF977814e90dA44bFA03b6295A0616a897441aceC; + + // token address constants + address constant USDT = 0xc2132D05D31c914a87C6611C10748AEb04B58e8F; + address constant USDC = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174; + + // agents + address constant KEEPER = address(5); + + function setUp() public { + injector = new ChildChainGaugeInjectorV2(); + factory = new ChildChainGaugeInjectorV2Factory(address(injector)); + + assert(factory.implementation() == address(injector)); + } +} diff --git a/foundry_test/FactoryTest.t.sol b/foundry_test/FactoryTest.t.sol new file mode 100644 index 0000000..13fc65c --- /dev/null +++ b/foundry_test/FactoryTest.t.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.25; + +import {BaseFixture} from "./BaseFixture.sol"; + +import {ChildChainGaugeInjectorV2} from "../contracts/ChildChainGaugeInjectorV2.sol"; + +contract FactoryTest is BaseFixture { + // dummy constants + uint256 MIN_WAIT_PERIOD_SECONDS = 1 days; + uint256 MAX_INJECTION_AMOUNT = 1_000e18; + address OWNER = address(56565); + + event InjectorCreated( + address indexed injector, address[] keeperAddresses, address injectTokenAddress, address owner + ); + + ChildChainGaugeInjectorV2 injectorFactoryDeployed; + + function testCreateInjector() public { + address[] memory keeperAddresses = new address[](1); + keeperAddresses[0] = KEEPER; + + // check: event emitted + vm.expectEmit(false, true, true, true); // @note topic0 is not checkeds + emit InjectorCreated(address(0), keeperAddresses, USDT, OWNER); + + // 1. create a new injector via factory + address injectorDeployed = + factory.createInjector(keeperAddresses, MIN_WAIT_PERIOD_SECONDS, USDT, MAX_INJECTION_AMOUNT, OWNER); + injectorFactoryDeployed = ChildChainGaugeInjectorV2(injectorDeployed); + + // 2. asserts: + // 2.1. check `getDeployedInjectors` returns the correct number of injectors + address[] memory injectorsDeployed = factory.getDeployedInjectors(); + assertEq(injectorsDeployed.length, 1); + assertEq(injectorsDeployed[0], injectorDeployed); + + // 2.2. check params of the injector correctness at deployment time + assertEq(injectorFactoryDeployed.owner(), OWNER); + assertEq(injectorFactoryDeployed.getKeeperAddresses()[0], KEEPER); + assertEq(injectorFactoryDeployed.MinWaitPeriodSeconds(), MIN_WAIT_PERIOD_SECONDS); + assertEq(injectorFactoryDeployed.InjectTokenAddress(), USDT); + } +} diff --git a/foundry_test/UncoveredLinesTest.t.sol b/foundry_test/UncoveredLinesTest.t.sol new file mode 100644 index 0000000..6941352 --- /dev/null +++ b/foundry_test/UncoveredLinesTest.t.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.25; + +import {BaseFixture} from "./BaseFixture.sol"; + +/// @notice Scope of the file is to test uncovered reverts, setters and getters: +/// 1. revert: `InjectorNotDistributor` +/// 2. revert: `ExceedsTotalInjectorProgramBudget` +/// 3. revert: `OnlyKeepers` +/// 4. getter: `getBalanceDelta` (cases-> deficit, exact balance and surplus) +/// 5. getter: `getFullSchedule` (check: expected values) +// @audit https://github.com/BalancerMaxis/ChildGaugeInjectorV2/issues/31 ? +contract UncoveredLinesTest is BaseFixture { + function test_revertWhen_InjectorNotDistributor() public {} + + function test_revertWhen_ExceedsTotalInjectorProgramBudget() public {} + + function test_revertWhen_NotKeepers() public {} + + function testGetBalance_When_Deficit() public {} + + function testGetBalance_When_ExactBalance() public {} + + function testGetBalance_When_Surplus() public {} + + function testGetFullSchedule() public {} +} diff --git a/lib/forge-std b/lib/forge-std new file mode 160000 index 0000000..035de35 --- /dev/null +++ b/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 035de35f5e366c8d6ed142aec4ccb57fe2dd87d4 From d5ba352eda2f1199cbd73e8212a81248b955f239 Mon Sep 17 00:00:00 2001 From: Petrovska Date: Wed, 9 Oct 2024 23:59:23 +0700 Subject: [PATCH 2/3] test: cover 1-5 scope scenarios defined in `UncoveredLinesTest` file --- foundry_test/BaseFixture.sol | 36 +++++++ foundry_test/FactoryTest.t.sol | 23 +---- foundry_test/UncoveredLinesTest.t.sol | 141 ++++++++++++++++++++++++-- 3 files changed, 172 insertions(+), 28 deletions(-) diff --git a/foundry_test/BaseFixture.sol b/foundry_test/BaseFixture.sol index 88f25b2..cec942f 100644 --- a/foundry_test/BaseFixture.sol +++ b/foundry_test/BaseFixture.sol @@ -3,6 +3,8 @@ pragma solidity ^0.8.25; import "forge-std/Test.sol"; +import {IChildChainGauge} from "../contracts/interfaces/balancer/IChildChainGauge.sol"; + import {ChildChainGaugeInjectorV2} from "../contracts/ChildChainGaugeInjectorV2.sol"; import {ChildChainGaugeInjectorV2Factory} from "../contracts/injectorFactoryV2.sol"; @@ -27,10 +29,44 @@ contract BaseFixture is Test { // agents address constant KEEPER = address(5); + address[] KEEPER_ADDRESSES = new address[](1); + + // dummy constants + uint256 MIN_WAIT_PERIOD_SECONDS = 1 days; + uint256 MAX_INJECTION_AMOUNT = 1_000e18; + address OWNER = address(56565); + + event InjectorCreated( + address indexed injector, address[] keeperAddresses, address injectTokenAddress, address owner + ); + function setUp() public { + vm.createSelectFork("polygon"); + injector = new ChildChainGaugeInjectorV2(); factory = new ChildChainGaugeInjectorV2Factory(address(injector)); assert(factory.implementation() == address(injector)); } + + function _deployDummyInjector() internal returns (address injectorDeployed_) { + KEEPER_ADDRESSES[0] = KEEPER; + + // check: event emitted + vm.expectEmit(false, true, true, true); // @note topic0 is not checkeds + emit InjectorCreated(address(0), KEEPER_ADDRESSES, USDT, OWNER); + + injectorDeployed_ = + factory.createInjector(KEEPER_ADDRESSES, MIN_WAIT_PERIOD_SECONDS, USDT, MAX_INJECTION_AMOUNT, OWNER); + } + + function _enableInjectorAsDistributor(address _injector) internal { + IChildChainGauge gaugeFirst = IChildChainGauge(GAUGE); + IChildChainGauge gaugeSecond = IChildChainGauge(GAUGE_2); + + vm.prank(gaugeFirst.authorizer_adaptor()); + gaugeFirst.add_reward(USDT, _injector); + vm.prank(gaugeSecond.authorizer_adaptor()); + gaugeSecond.add_reward(USDT, _injector); + } } diff --git a/foundry_test/FactoryTest.t.sol b/foundry_test/FactoryTest.t.sol index 13fc65c..8afb756 100644 --- a/foundry_test/FactoryTest.t.sol +++ b/foundry_test/FactoryTest.t.sol @@ -6,29 +6,10 @@ import {BaseFixture} from "./BaseFixture.sol"; import {ChildChainGaugeInjectorV2} from "../contracts/ChildChainGaugeInjectorV2.sol"; contract FactoryTest is BaseFixture { - // dummy constants - uint256 MIN_WAIT_PERIOD_SECONDS = 1 days; - uint256 MAX_INJECTION_AMOUNT = 1_000e18; - address OWNER = address(56565); - - event InjectorCreated( - address indexed injector, address[] keeperAddresses, address injectTokenAddress, address owner - ); - - ChildChainGaugeInjectorV2 injectorFactoryDeployed; - function testCreateInjector() public { - address[] memory keeperAddresses = new address[](1); - keeperAddresses[0] = KEEPER; - - // check: event emitted - vm.expectEmit(false, true, true, true); // @note topic0 is not checkeds - emit InjectorCreated(address(0), keeperAddresses, USDT, OWNER); - // 1. create a new injector via factory - address injectorDeployed = - factory.createInjector(keeperAddresses, MIN_WAIT_PERIOD_SECONDS, USDT, MAX_INJECTION_AMOUNT, OWNER); - injectorFactoryDeployed = ChildChainGaugeInjectorV2(injectorDeployed); + address injectorDeployed = _deployDummyInjector(); + ChildChainGaugeInjectorV2 injectorFactoryDeployed = ChildChainGaugeInjectorV2(injectorDeployed); // 2. asserts: // 2.1. check `getDeployedInjectors` returns the correct number of injectors diff --git a/foundry_test/UncoveredLinesTest.t.sol b/foundry_test/UncoveredLinesTest.t.sol index 6941352..30b7268 100644 --- a/foundry_test/UncoveredLinesTest.t.sol +++ b/foundry_test/UncoveredLinesTest.t.sol @@ -3,6 +3,12 @@ pragma solidity ^0.8.25; import {BaseFixture} from "./BaseFixture.sol"; +import "../node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import {IChildChainGauge} from "../contracts/interfaces/balancer/IChildChainGauge.sol"; + +import {ChildChainGaugeInjectorV2} from "../contracts/ChildChainGaugeInjectorV2.sol"; + /// @notice Scope of the file is to test uncovered reverts, setters and getters: /// 1. revert: `InjectorNotDistributor` /// 2. revert: `ExceedsTotalInjectorProgramBudget` @@ -11,17 +17,138 @@ import {BaseFixture} from "./BaseFixture.sol"; /// 5. getter: `getFullSchedule` (check: expected values) // @audit https://github.com/BalancerMaxis/ChildGaugeInjectorV2/issues/31 ? contract UncoveredLinesTest is BaseFixture { - function test_revertWhen_InjectorNotDistributor() public {} + function test_revertWhen_InjectorNotDistributor() public { + ChildChainGaugeInjectorV2 inj = ChildChainGaugeInjectorV2(_deployDummyInjector()); + + address[] memory recipients = new address[](1); + recipients[0] = GAUGE; + + vm.prank(inj.owner()); + vm.expectRevert(abi.encodeWithSelector(ChildChainGaugeInjectorV2.InjectorNotDistributor.selector, GAUGE, USDT)); + inj.addRecipients(recipients, 50e18, 4, uint56(block.timestamp + 1 days)); + } + + function test_revertWhen_ExceedsTotalInjectorProgramBudget() public { + ChildChainGaugeInjectorV2 inj = ChildChainGaugeInjectorV2(_deployDummyInjector()); + + _enableInjectorAsDistributor(address(inj)); + + uint256 dummyMaxTotalDue = 100e18; + vm.startPrank(inj.owner()); + inj.setMaxTotalDue(dummyMaxTotalDue); + assertEq(inj.MaxTotalDue(), dummyMaxTotalDue); + + address[] memory recipients = new address[](2); + recipients[0] = GAUGE; + recipients[1] = GAUGE_2; + + uint256 amountPerPeriod = 250e18; + vm.expectRevert( + abi.encodeWithSelector( + ChildChainGaugeInjectorV2.ExceedsTotalInjectorProgramBudget.selector, amountPerPeriod + ) + ); + inj.addRecipients(recipients, 250e18, 1, uint56(block.timestamp + 1 days)); + } + + function test_revertWhen_NotKeepers() public { + address NOT_KEEPER_AGENT = address(543485484845); + + ChildChainGaugeInjectorV2 inj = ChildChainGaugeInjectorV2(_deployDummyInjector()); + + address[] memory needsFunding = new address[](1); + needsFunding[0] = GAUGE; + + vm.prank(NOT_KEEPER_AGENT); + vm.expectRevert(abi.encodeWithSelector(ChildChainGaugeInjectorV2.OnlyKeepers.selector, NOT_KEEPER_AGENT)); + inj.performUpkeep(abi.encode(needsFunding)); + } + + function testGetBalance_When_Deficit() public { + ChildChainGaugeInjectorV2 inj = ChildChainGaugeInjectorV2(_deployDummyInjector()); + + _enableInjectorAsDistributor(address(inj)); + + address[] memory recipients = new address[](1); + recipients[0] = GAUGE; + uint256 amountPerPeriod = 250e18; + vm.prank(inj.owner()); + inj.addRecipients(recipients, amountPerPeriod, 1, uint56(block.timestamp + 1 days)); + + // send partially + deal(USDT, address(inj), 50e18); + + int256 expectedDeficit = -1 * int256(amountPerPeriod - IERC20(USDT).balanceOf(address(inj))); + + // should encounter DEFICIT + assertEq(inj.getBalanceDelta(), expectedDeficit); + } + + function testGetBalance_When_ExactBalance() public { + ChildChainGaugeInjectorV2 inj = ChildChainGaugeInjectorV2(_deployDummyInjector()); + + _enableInjectorAsDistributor(address(inj)); + + address[] memory recipients = new address[](1); + recipients[0] = GAUGE; + uint256 amountPerPeriod = 250e18; + vm.prank(inj.owner()); + inj.addRecipients(recipients, amountPerPeriod, 1, uint56(block.timestamp + 1 days)); + + // send full `amountPerPeriod` + deal(USDT, address(inj), amountPerPeriod); + + // should encounter EXACT + assertEq(inj.getBalanceDelta(), 0); + } + + function testGetBalance_When_Surplus() public { + ChildChainGaugeInjectorV2 inj = ChildChainGaugeInjectorV2(_deployDummyInjector()); + + _enableInjectorAsDistributor(address(inj)); + + address[] memory recipients = new address[](1); + recipients[0] = GAUGE; + uint256 amountPerPeriod = 250e18; + vm.prank(inj.owner()); + inj.addRecipients(recipients, amountPerPeriod, 1, uint56(block.timestamp + 1 days)); + + // send full `amountPerPeriod` * 3 + deal(USDT, address(inj), amountPerPeriod * 3); + + int256 expectedSurplus = int256(IERC20(USDT).balanceOf(address(inj)) - amountPerPeriod); + // should encounter SURPLUS + assertEq(inj.getBalanceDelta(), expectedSurplus); + } - function test_revertWhen_ExceedsTotalInjectorProgramBudget() public {} + function testGetFullSchedule() public { + ChildChainGaugeInjectorV2 inj = ChildChainGaugeInjectorV2(_deployDummyInjector()); - function test_revertWhen_NotKeepers() public {} + _enableInjectorAsDistributor(address(inj)); - function testGetBalance_When_Deficit() public {} + address[] memory recipients = new address[](2); + recipients[0] = GAUGE; + recipients[1] = GAUGE_2; - function testGetBalance_When_ExactBalance() public {} + vm.prank(inj.owner()); + inj.addRecipients(recipients, 250e18, 5, uint56(block.timestamp + 1 days)); - function testGetBalance_When_Surplus() public {} + ( + address[] memory gauges, + uint256[] memory amountsPerPeriod, + uint8[] memory maxPeriods, + uint8[] memory currentPeriods, + uint56[] memory lastTimestamps, + uint56[] memory doNotStartBeforeTimestamps + ) = inj.getFullSchedule(); - function testGetFullSchedule() public {} + for (uint256 i = 0; i < gauges.length; i++) { + assertEq(gauges[i], recipients[i]); + assertEq(amountsPerPeriod[i], 250e18); + assertEq(maxPeriods[i], 5); + assertEq(currentPeriods[i], 0); + assertEq(lastTimestamps[i], uint56(0)); + assertEq(doNotStartBeforeTimestamps[i], uint56(block.timestamp + 1 days)); + } + } } From e503f1737465851decd8731123ddb191b43d03c9 Mon Sep 17 00:00:00 2001 From: Petrovska Date: Fri, 11 Oct 2024 23:59:31 +0700 Subject: [PATCH 3/3] feat: add multichain deployment script --- .env.example | 20 +++++ Makefile | 27 +++++- foundry.toml | 8 +- .../InjectorInfraMultiChainDeployment.s.sol | 88 +++++++++++++++++++ 4 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 .env.example create mode 100644 foundry_scripts/InjectorInfraMultiChainDeployment.s.sol diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..0085342 --- /dev/null +++ b/.env.example @@ -0,0 +1,20 @@ +# Deployer private key +PRIVATE_KEY= + +# RPCs +ARBITRUM_RPC_URL= +BASE_RPC_URL= +ETHEREUM_RPC_URL= +FRAXTAL_RPC_URL= +GNOSIS_RPC_URL= +OPTIMISM_RPC_URL= +POLYGON_RPC_URL= + +# Etherscan APIs +ETHERSCAN_ARBITRUM_API_KEY= +ETHERSCAN_BASE_API_KEY= +ETHERSCAN_ETHEREUM_API_KEY= +ETHERSCAN_FRAXTAL_API_KEY= +ETHERSCAN_GNOSIS_API_KEY= +ETHERSCAN_OPTIMISM_API_KEY= +ETHERSCAN_POLYGON_API_KEY= diff --git a/Makefile b/Makefile index 78e5d3f..6d08f85 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,34 @@ -include .env # command: test the whole script without broadcasting the transaction into the chain to spot early errors -deployDry: +deployPolygonDry: forge script foundry_scripts/InjectorInfraDeployment.s.sol \ --rpc-url polygon \ --slow \ -vvvv + +deployPolygonBroadcastAndVerify: + forge script foundry_scripts/InjectorInfraDeployment.s.sol \ + --rpc-url polygon \ + --slow \ + --broadcast \ + --verify \ + -vvvv + +deployMultiChainDry: + forge script foundry_scripts/InjectorInfraMultiChainDeployment.s.sol \ + --rpc-url polygon \ + --slow \ + -- multi \ + -vvvv + +deployMultiChainBroadcastAndVerify: + forge script foundry_scripts/InjectorInfraMultiChainDeployment.s.sol \ + --rpc-url polygon \ + --slow \ + -- multi \ + --broadcast \ + --verify \ + -vvvv + \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 3cc58fc..78e643e 100644 --- a/foundry.toml +++ b/foundry.toml @@ -9,4 +9,10 @@ solc = '0.8.25' ignore = ['./contracts/**/*'] [rpc_endpoints] -polygon = "${POLYGON_RPC_KEY}" \ No newline at end of file +arbitrum = "${ARBITRUM_RPC_URL}" +base = "${BASE_RPC_URL}" +ethereum = "${ETHEREUM_RPC_URL}" +fraxtal = "${FRAXTAL_RPC_URL}" +gnosis = "${GNOSIS_RPC_URL}" +optimism = "${OPTIMISM_RPC_URL}" +polygon = "${POLYGON_RPC_URL}" \ No newline at end of file diff --git a/foundry_scripts/InjectorInfraMultiChainDeployment.s.sol b/foundry_scripts/InjectorInfraMultiChainDeployment.s.sol new file mode 100644 index 0000000..79bdae7 --- /dev/null +++ b/foundry_scripts/InjectorInfraMultiChainDeployment.s.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.25; + +import {Script} from "forge-std/Script.sol"; + +import {ChildChainGaugeInjectorV2} from "../contracts/ChildChainGaugeInjectorV2.sol"; +import {ChildChainGaugeInjectorV2Factory} from "../contracts/injectorFactoryV2.sol"; + +/// @notice Deploys the v2 infrastructure for the injectors in all chains in `foundry.toml` in the following order: +/// 1. {ChildChainGaugeInjectorV2} -> singleton/implementation purposes (helps verifying in etherscan etc) +/// 2. {ChildChainGaugeInjectorV2Factory} +contract InjectorInfraMultiChainDeployment is Script { + enum Chains { + ARBITRUM, + BASE, + ETHEREUM, + FRAXTAL, + GNOSIS, + OPTIMISM, + POLYGON + } + + // injector infrastructure + ChildChainGaugeInjectorV2 injectorImpl; + ChildChainGaugeInjectorV2Factory injectorFactory; + + mapping(Chains chain => string rpcAlias) public availableChains; + + constructor() { + availableChains[Chains.ARBITRUM] = "arbitrum"; + availableChains[Chains.BASE] = "base"; + availableChains[Chains.ETHEREUM] = "ethereum"; + availableChains[Chains.FRAXTAL] = "fraxtal"; + availableChains[Chains.GNOSIS] = "gnosis"; + availableChains[Chains.OPTIMISM] = "optimism"; + availableChains[Chains.POLYGON] = "polygon"; + } + + /// @dev broadcast transaction modifier + /// @param pk private key to broadcast transaction + modifier broadcast(uint256 pk) { + vm.startBroadcast(pk); + + _; + + vm.stopBroadcast(); + } + + function run() public { + // read pk from `.env` + uint256 pk = vm.envUint("PRIVATE_KEY"); + + // @note the array can be updated depending on your target chains to deploy + // @note by default the script will deploy in all chains available in the toml file + Chains[] memory targetDeploymentChains = new Chains[](6); + + targetDeploymentChains[0] = Chains.ARBITRUM; + targetDeploymentChains[1] = Chains.BASE; + targetDeploymentChains[2] = Chains.ETHEREUM; + targetDeploymentChains[3] = Chains.GNOSIS; + targetDeploymentChains[4] = Chains.OPTIMISM; + targetDeploymentChains[5] = Chains.POLYGON; + // @note fraxtal rpc gives sometimes problems + + for (uint256 i = 0; i < targetDeploymentChains.length; i++) { + _deploy(targetDeploymentChains[i], pk); + } + } + + /// @dev Helper to point into a specific chain + /// @param _targetChain chain to deploy + /// @param _pk private key to broadcast transaction + function _deploy(Chains _targetChain, uint256 _pk) internal { + vm.createSelectFork(availableChains[_targetChain]); + + _infraDeployment(_pk); + } + + /// @dev Helper to deploy the factory and singleton + /// @param _pk private key to broadcast transaction + function _infraDeployment(uint256 _pk) internal broadcast(_pk) { + // 1. {ChildChainGaugeInjectorV2} + injectorImpl = new ChildChainGaugeInjectorV2(); + + // 2. {ChildChainGaugeInjectorV2Factory} + injectorFactory = new ChildChainGaugeInjectorV2Factory(address(injectorImpl)); + } +} \ No newline at end of file