From 637f1bb9630a3f5d1c307c61b4886deb2f7f7f40 Mon Sep 17 00:00:00 2001 From: CodeSandwich Date: Thu, 26 Sep 2024 01:06:47 +0200 Subject: [PATCH] Create a new deployment mechanism --- deployments/filecoin-test.json | 47 +-- foundry.toml | 2 +- .../DeployAxelarBridgedGovernor.s.sol | 0 .../DeployLZBridgedGovernor.s.sol | 0 script/FilecoinDeploy.sol | 90 ++++ script/modules/AddressDriver.sol | 53 +++ script/modules/AxelarBridgedGovernor.sol | 58 +++ script/modules/Caller.sol | 40 ++ script/modules/Drips.sol | 57 +++ script/modules/GiversRegistry.sol | 55 +++ script/modules/ImmutableSplitsDriver.sol | 59 +++ script/modules/LZBridgedGovernor.sol | 54 +++ script/modules/NFTDriver.sol | 53 +++ script/modules/NativeTokenUnwrapper.sol | 57 +++ script/modules/RepoDriver.sol | 67 +++ script/utils/Create3Factory.sol | 94 +++++ script/utils/Create3Helpers.sol | 35 ++ script/utils/DeploymentJson.sol | 116 ++++++ script/utils/ModulesDeployer.sol | 68 +++ src/DripsDeployer.sol | 388 ------------------ 20 files changed, 971 insertions(+), 422 deletions(-) rename {scripts => script}/DeployAxelarBridgedGovernor.s.sol (100%) rename {scripts => script}/DeployLZBridgedGovernor.s.sol (100%) create mode 100644 script/FilecoinDeploy.sol create mode 100644 script/modules/AddressDriver.sol create mode 100644 script/modules/AxelarBridgedGovernor.sol create mode 100644 script/modules/Caller.sol create mode 100644 script/modules/Drips.sol create mode 100644 script/modules/GiversRegistry.sol create mode 100644 script/modules/ImmutableSplitsDriver.sol create mode 100644 script/modules/LZBridgedGovernor.sol create mode 100644 script/modules/NFTDriver.sol create mode 100644 script/modules/NativeTokenUnwrapper.sol create mode 100644 script/modules/RepoDriver.sol create mode 100644 script/utils/Create3Factory.sol create mode 100644 script/utils/Create3Helpers.sol create mode 100644 script/utils/DeploymentJson.sol create mode 100644 script/utils/ModulesDeployer.sol delete mode 100644 src/DripsDeployer.sol diff --git a/deployments/filecoin-test.json b/deployments/filecoin-test.json index fc1b026f..07c39409 100644 --- a/deployments/filecoin-test.json +++ b/deployments/filecoin-test.json @@ -1,35 +1,16 @@ { - "Chain": "filecoin", - "Deployment time": "2024-08-12T22:50:45Z", - "Commit hash": "0b04137dff43f83789f4d10169d9a4d78e323332", - "Wallet": "0xEF4B994f97092fD22B8a880c47F053910788471b", - "Safe singleton factory": "0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7", - "CREATE3 factory": "0xe9BE461efaB6f9079741da3b180249F81e66A461", - "DripsDeployer salt": "DripsDeployerTest1", - "DripsDeployer": "0xCD29c13A0f40C6E1a1783f1b0B69D72529CE183F", - "Drips": "0x29252acF5a3dA105CB3aC245B7758F6e50281ba7", - "Drips cycle seconds": "604800 [6.048e5]", - "Drips logic": "0x1ADE6713A488A8AE1DA66b452aCb3DD1821aeB7D", - "Drips admin": "0xEF4B994f97092fD22B8a880c47F053910788471b", - "Caller": "0x6171a47dDc84AF3e138D6d84c5b5D1bFD35615a3", - "AddressDriver": "0xE13A4f3671ee451F81Df3aa1AEb6653e4c33D5e0", - "AddressDriver ID": "0", - "AddressDriver logic": "0x3288D4C292080643E9041602cf0960bfe8fB44F0", - "AddressDriver admin": "0xEF4B994f97092fD22B8a880c47F053910788471b", - "NFTDriver": "0xE03d510d927816f3482C3C0204F14203403c0ee2", - "NFTDriver ID": "1", - "NFTDriver logic": "0xe349116Ab1F17819b080d519B51914DD1D5b6057", - "NFTDriver admin": "0xEF4B994f97092fD22B8a880c47F053910788471b", - "ImmutableSplitsDriver": "0xf07bF47a4a118792f5392560C27EE6530Ca36cA3", - "ImmutableSplitsDriver ID": "2", - "ImmutableSplitsDriver logic": "0x01Ca3Edd523913A7402F682eD1f758C2Ee47e72f", - "ImmutableSplitsDriver admin": "0xEF4B994f97092fD22B8a880c47F053910788471b", - "RepoDriver": "0x249e35aC49ccC4B1F0688Bc4c0bFA866a1b1E3fE", - "RepoDriver ID": "3", - "Gelato Automate": "0x83444851B04Af09e3F2dA745F1AC5F580bB775C6", - "RepoDriver IPFS CID": "", - "RepoDriver max requests per block": "0", - "RepoDriver max requests per 31 days": "0", - "RepoDriver logic": "0xB47AFebbf18Bb7126289eF068097E559E0cC76e4", - "RepoDriver admin": "0xEF4B994f97092fD22B8a880c47F053910788471b" + "AddressDriver": "0xEFcd912a5a67C3a7Cc70a2Fb9aa17781bf1cE68F", + "Caller": "0x7f2457421718A541B9Ee01E2F77F8BA749055F3b", + "Deployer": "0xEF4B994f97092fD22B8a880c47F053910788471b", + "Drips": "0x0B71C2a08d27E86d3841A6772332DEde0bc8DCa5", + "Drips cycle seconds": 86400, + "DripsDeployer": "0x79f3e7Aa55D39bBD42de31677B715B2e4D6e8ab0", + "GiversRegistry": "0x1F83E99813941a9bd701342560BFFaC794D707d1", + "ImmutableSplitsDriver": "0x990806c7EFC1C26be162a46F821ca281618F28AA", + "LZBridgedGovernor": "0x6bC5C5D7dA80dC1c30677852ce9AA01df368E76f", + "NFTDriver": "0x1397579E87AB255C8474907183B074947eBa7338", + "NativeTokenUnwrapper": "0x8A388BE3fb93C28b66365DCbf3eAc344690BD1C4", + "RepoDriver": "0xf3aE6ADDeEE195e91380F5F9Ce73698460BAdf79", + "RepoDriver tasks owner": "0x6D3A0fAE4FBa0f543b9e3280E85F4e4f59ba3D54", + "Salt": "test5" } diff --git a/foundry.toml b/foundry.toml index c44afa13..25a87c4b 100644 --- a/foundry.toml +++ b/foundry.toml @@ -4,7 +4,7 @@ evm_version = 'shanghai' optimizer_runs = 7_700 verbosity = 1 fuzz_runs = 5 -script = 'scripts' +fs_permissions = [{ access = "read-write", path = "./"}] [fmt] line_length = 100 [fuzz] diff --git a/scripts/DeployAxelarBridgedGovernor.s.sol b/script/DeployAxelarBridgedGovernor.s.sol similarity index 100% rename from scripts/DeployAxelarBridgedGovernor.s.sol rename to script/DeployAxelarBridgedGovernor.s.sol diff --git a/scripts/DeployLZBridgedGovernor.s.sol b/script/DeployLZBridgedGovernor.s.sol similarity index 100% rename from scripts/DeployLZBridgedGovernor.s.sol rename to script/DeployLZBridgedGovernor.s.sol diff --git a/script/FilecoinDeploy.sol b/script/FilecoinDeploy.sol new file mode 100644 index 00000000..11436e7c --- /dev/null +++ b/script/FilecoinDeploy.sol @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.20; + +import {console, Script} from "forge-std/Script.sol"; + +import {addressDriverModuleData} from "script/modules/AddressDriver.sol"; +import { + axelarBridgedGovernorModule, + axelarBridgedGovernorModuleData, + IAxelarGMPGateway +} from "script/modules/AxelarBridgedGovernor.sol"; +import {callerModuleData} from "script/modules/Caller.sol"; +import {dripsModuleData} from "script/modules/Drips.sol"; +import {giversRegistryModuleData, IWrappedNativeToken} from "script/modules/GiversRegistry.sol"; +import {immutableSplitsDriverModuleData} from "script/modules/ImmutableSplitsDriver.sol"; +import {nativeTokenUnwrapperModuleData} from "script/modules/NativeTokenUnwrapper.sol"; +import {nftDriverModuleData} from "script/modules/NFTDriver.sol"; +import {IAutomate, repoDriverModuleData} from "script/modules/RepoDriver.sol"; +import {deployCreate3Factory, ICreate3Factory} from "script/utils/Create3Factory.sol"; +import {writeDeploymentJson} from "script/utils/DeploymentJson.sol"; +import { + deployModulesDeployer, ModulesDeployer, ModuleData +} from "script/utils/ModulesDeployer.sol"; + +/// @dev As of 09.10.2024 Foundry badly estimates Filecoin gas usage. +/// To avoid the out of gas errors, pass `--gas-estimate-multiplier 80000` to `forge script`. +contract FilecoinDeploy is Script { + function run() public { + require(block.chainid == 314, "Must be run on Filecoin"); + string memory salt = vm.envString("SALT"); + + vm.startBroadcast(); + ICreate3Factory create3Factory = deployCreate3Factory(); + ModulesDeployer modulesDeployer = + deployModulesDeployer(create3Factory, bytes32(bytes(salt)), msg.sender); + + ModuleData[] memory modules = new ModuleData[](1); + modules[0] = axelarBridgedGovernorModuleData({ + modulesDeployer: modulesDeployer, + // Taken from https://docs.axelar.dev/dev/reference/mainnet-contract-addresses/ + gateway: IAxelarGMPGateway(0xe432150cce91c13a887f7D836923d5597adD8E31), + ownerChain: "Ethereum", + // Radworks governance on Ethereum controlling the bridge. + owner: 0x8dA8f82d2BbDd896822de723F55D6EdF416130ba + }); + modulesDeployer.deployModules(modules); + + address admin = + address(axelarBridgedGovernorModule(modulesDeployer).axelarBridgedGovernor()); + + modules = new ModuleData[](2); + modules[0] = callerModuleData(modulesDeployer); + modules[1] = + dripsModuleData({modulesDeployer: modulesDeployer, admin: admin, cycleSecs: 1 days}); + modulesDeployer.deployModules(modules); + + modules = new ModuleData[](2); + modules[0] = addressDriverModuleData(modulesDeployer, admin); + modules[1] = nftDriverModuleData(modulesDeployer, admin); + modulesDeployer.deployModules(modules); + + modules = new ModuleData[](2); + modules[0] = immutableSplitsDriverModuleData(modulesDeployer, admin); + modules[1] = repoDriverModuleData({ + modulesDeployer: modulesDeployer, + admin: admin, + // Taken from https://docs.gelato.network/web3-services/web3-functions/contract-addresses + gelatoAutomate: IAutomate(0x2A6C106ae13B558BB9E2Ec64Bd2f1f7BEFF3A5E0), + // Deployed from https://github.com/drips-network/contracts-gelato-web3-function + ipfsCid: "QmeP5ETCt7bZLMtQeFRmJNm5mhYaGgM3GNvExQ4PP12whD", + // Calculated to saturate the Gelato free tier giving 200K GU. + // Assumes that each requests costs up to 11 GU (5 seconds of CPU + 1 transaction). + // The penalty-free throughput is 1 request per 3 minutes. + maxRequestsPerBlock: 80, + maxRequestsPer31Days: 18000 + }); + modulesDeployer.deployModules(modules); + + // Take from https://docs.filecoin.io/smart-contracts/advanced/wrapped-fil + IWrappedNativeToken wfil = IWrappedNativeToken(0x60E1773636CF5E4A227d9AC24F20fEca034ee25A); + modules = new ModuleData[](2); + modules[0] = giversRegistryModuleData(modulesDeployer, admin, wfil); + modules[1] = nativeTokenUnwrapperModuleData(modulesDeployer, wfil); + modulesDeployer.deployModules(modules); + + vm.stopBroadcast(); + + writeDeploymentJson(vm, modulesDeployer, salt); + } +} diff --git a/script/modules/AddressDriver.sol b/script/modules/AddressDriver.sol new file mode 100644 index 00000000..a0e8a1f7 --- /dev/null +++ b/script/modules/AddressDriver.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.20; + +import {create3ManagedProxy} from "script/utils/Create3Helpers.sol"; +import {callerModule} from "script/modules/Caller.sol"; +import {Drips, dripsModule, DripsModule} from "script/modules/Drips.sol"; +import { + isModuleDeployed, + ModulesDeployer, + getModule, + Module, + ModuleData +} from "script/utils/ModulesDeployer.sol"; +import {AddressDriver} from "src/AddressDriver.sol"; + +bytes32 constant ADDRESS_DRIVER_MODULE_SALT = "AddressDriverModule"; + +function isAddressDriverModuleDeployed(ModulesDeployer modulesDeployer) view returns (bool yes) { + return isModuleDeployed(modulesDeployer, ADDRESS_DRIVER_MODULE_SALT); +} + +function addressDriverModule(ModulesDeployer modulesDeployer) view returns (AddressDriverModule) { + return AddressDriverModule(getModule(modulesDeployer, ADDRESS_DRIVER_MODULE_SALT)); +} + +function addressDriverModuleData(ModulesDeployer modulesDeployer, address admin) + pure + returns (ModuleData memory) +{ + bytes memory args = abi.encode(modulesDeployer, admin); + return ModuleData({ + salt: ADDRESS_DRIVER_MODULE_SALT, + initCode: abi.encodePacked(type(AddressDriverModule).creationCode, args), + value: 0 + }); +} + +contract AddressDriverModule is Module { + AddressDriver public immutable addressDriver; + + constructor(ModulesDeployer modulesDeployer, address admin) + Module(modulesDeployer, ADDRESS_DRIVER_MODULE_SALT) + { + DripsModule dripsModule_ = dripsModule(modulesDeployer); + Drips drips = dripsModule_.drips(); + address forwarder = address(callerModule(modulesDeployer).caller()); + uint32 driverId = 0; + AddressDriver logic = new AddressDriver(drips, forwarder, driverId); + address proxy = create3ManagedProxy(modulesDeployer, "AddressDriver", logic, admin, ""); + addressDriver = AddressDriver(proxy); + dripsModule_.claimDriverId(ADDRESS_DRIVER_MODULE_SALT, driverId, proxy); + } +} diff --git a/script/modules/AxelarBridgedGovernor.sol b/script/modules/AxelarBridgedGovernor.sol new file mode 100644 index 00000000..3c019fe0 --- /dev/null +++ b/script/modules/AxelarBridgedGovernor.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.20; + +import {create3GovernorProxy} from "script/utils/Create3Helpers.sol"; +import { + isModuleDeployed, + ModulesDeployer, + getModule, + Module, + ModuleData +} from "script/utils/ModulesDeployer.sol"; +import {AxelarBridgedGovernor, IAxelarGMPGateway} from "src/BridgedGovernor.sol"; + +bytes32 constant AXELAR_BRIDGED_GOVERNOR_MODULE_SALT = "AxelarBridgedGovernorModule"; + +function isAxelarBridgedGovernorModuleDeployed(ModulesDeployer modulesDeployer) + view + returns (bool yes) +{ + return isModuleDeployed(modulesDeployer, AXELAR_BRIDGED_GOVERNOR_MODULE_SALT); +} + +function axelarBridgedGovernorModule(ModulesDeployer modulesDeployer) + view + returns (AxelarBridgedGovernorModule) +{ + return + AxelarBridgedGovernorModule(getModule(modulesDeployer, AXELAR_BRIDGED_GOVERNOR_MODULE_SALT)); +} + +function axelarBridgedGovernorModuleData( + ModulesDeployer modulesDeployer, + IAxelarGMPGateway gateway, + string memory ownerChain, + address owner +) pure returns (ModuleData memory) { + bytes memory args = abi.encode(modulesDeployer, gateway, ownerChain, owner); + return ModuleData({ + salt: AXELAR_BRIDGED_GOVERNOR_MODULE_SALT, + initCode: abi.encodePacked(type(AxelarBridgedGovernorModule).creationCode, args), + value: 0 + }); +} + +contract AxelarBridgedGovernorModule is Module { + AxelarBridgedGovernor public immutable axelarBridgedGovernor; + + constructor( + ModulesDeployer modulesDeployer, + IAxelarGMPGateway gateway, + string memory ownerChain, + address owner + ) Module(modulesDeployer, AXELAR_BRIDGED_GOVERNOR_MODULE_SALT) { + AxelarBridgedGovernor logic = new AxelarBridgedGovernor(gateway, ownerChain, owner); + address proxy = create3GovernorProxy(modulesDeployer, "AxelarBridgedGovernor", logic); + axelarBridgedGovernor = AxelarBridgedGovernor(payable(proxy)); + } +} diff --git a/script/modules/Caller.sol b/script/modules/Caller.sol new file mode 100644 index 00000000..88edffc1 --- /dev/null +++ b/script/modules/Caller.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.20; + +import {create3} from "script/utils/Create3Helpers.sol"; +import { + isModuleDeployed, + ModulesDeployer, + getModule, + Module, + ModuleData +} from "script/utils/ModulesDeployer.sol"; +import {Caller} from "src/Caller.sol"; + +bytes32 constant CALLER_MODULE_SALT = "CallerModule"; + +function isCallerModuleDeployed(ModulesDeployer modulesDeployer) view returns (bool yes) { + return isModuleDeployed(modulesDeployer, CALLER_MODULE_SALT); +} + +function callerModule(ModulesDeployer modulesDeployer) view returns (CallerModule) { + return CallerModule(getModule(modulesDeployer, CALLER_MODULE_SALT)); +} + +function callerModuleData(ModulesDeployer modulesDeployer) pure returns (ModuleData memory) { + bytes memory args = abi.encode(modulesDeployer); + return ModuleData({ + salt: CALLER_MODULE_SALT, + initCode: abi.encodePacked(type(CallerModule).creationCode, args), + value: 0 + }); +} + +contract CallerModule is Module { + Caller public immutable caller; + + constructor(ModulesDeployer modulesDeployer) Module(modulesDeployer, CALLER_MODULE_SALT) { + // slither-disable-next-line too-many-digits + caller = Caller(create3(modulesDeployer, "Caller", type(Caller).creationCode, "")); + } +} diff --git a/script/modules/Drips.sol b/script/modules/Drips.sol new file mode 100644 index 00000000..63cb3e6c --- /dev/null +++ b/script/modules/Drips.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.20; + +import {create3ManagedProxy} from "script/utils/Create3Helpers.sol"; +import { + isModuleDeployed, + ModulesDeployer, + getModule, + Module, + ModuleData +} from "script/utils/ModulesDeployer.sol"; +import {Drips} from "src/Drips.sol"; + +bytes32 constant DRIPS_MODULE_SALT = "DripsModule"; + +function isDripsModuleDeployed(ModulesDeployer modulesDeployer) view returns (bool yes) { + return isModuleDeployed(modulesDeployer, DRIPS_MODULE_SALT); +} + +function dripsModule(ModulesDeployer modulesDeployer) view returns (DripsModule) { + return DripsModule(getModule(modulesDeployer, DRIPS_MODULE_SALT)); +} + +function dripsModuleData(ModulesDeployer modulesDeployer, address admin, uint32 cycleSecs) + pure + returns (ModuleData memory) +{ + bytes memory args = abi.encode(modulesDeployer, admin, cycleSecs); + return ModuleData({ + salt: DRIPS_MODULE_SALT, + initCode: abi.encodePacked(type(DripsModule).creationCode, args), + value: 0 + }); +} + +contract DripsModule is Module { + Drips public immutable drips; + + constructor(ModulesDeployer modulesDeployer, address admin, uint32 cycleSecs) + Module(modulesDeployer, DRIPS_MODULE_SALT) + { + Drips logic = new Drips(cycleSecs); + address proxy = create3ManagedProxy(modulesDeployer, "Drips", logic, admin, ""); + drips = Drips(proxy); + for (uint256 i = 0; i < 100; i++) { + // slither-disable-next-line calls-loop,unused-return + drips.registerDriver(address(this)); + } + } + + function claimDriverId(bytes32 senderSalt, uint32 driverId, address driver) + public + onlyModule(senderSalt) + { + drips.updateDriverAddress(driverId, driver); + } +} diff --git a/script/modules/GiversRegistry.sol b/script/modules/GiversRegistry.sol new file mode 100644 index 00000000..efa25f3d --- /dev/null +++ b/script/modules/GiversRegistry.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.20; + +import {AddressDriver, addressDriverModule} from "script/modules/AddressDriver.sol"; +import {create3ManagedProxy} from "script/utils/Create3Helpers.sol"; +import { + isModuleDeployed, + ModulesDeployer, + getModule, + Module, + ModuleData +} from "script/utils/ModulesDeployer.sol"; +import {GiversRegistry} from "src/Giver.sol"; +import {IWrappedNativeToken} from "src/IWrappedNativeToken.sol"; + +bytes32 constant GIVERS_REGISTRY_MODULE_SALT = "GiversRegistryModule"; + +function isGiversRegistryModuleDeployed(ModulesDeployer modulesDeployer) view returns (bool yes) { + return isModuleDeployed(modulesDeployer, GIVERS_REGISTRY_MODULE_SALT); +} + +function giversRegistryModule(ModulesDeployer modulesDeployer) + view + returns (GiversRegistryModule) +{ + return GiversRegistryModule(getModule(modulesDeployer, GIVERS_REGISTRY_MODULE_SALT)); +} + +function giversRegistryModuleData( + ModulesDeployer modulesDeployer, + address admin, + IWrappedNativeToken wrappedNativeToken +) pure returns (ModuleData memory) { + bytes memory args = abi.encode(modulesDeployer, admin, wrappedNativeToken); + return ModuleData({ + salt: GIVERS_REGISTRY_MODULE_SALT, + initCode: abi.encodePacked(type(GiversRegistryModule).creationCode, args), + value: 0 + }); +} + +contract GiversRegistryModule is Module { + GiversRegistry public immutable giversRegistry; + + constructor( + ModulesDeployer modulesDeployer, + address admin, + IWrappedNativeToken wrappedNativeToken + ) Module(modulesDeployer, GIVERS_REGISTRY_MODULE_SALT) { + AddressDriver addressDriver = addressDriverModule(modulesDeployer).addressDriver(); + GiversRegistry logic = new GiversRegistry(addressDriver, wrappedNativeToken); + address proxy = create3ManagedProxy(modulesDeployer, "GiversRegistry", logic, admin, ""); + giversRegistry = GiversRegistry(proxy); + } +} diff --git a/script/modules/ImmutableSplitsDriver.sol b/script/modules/ImmutableSplitsDriver.sol new file mode 100644 index 00000000..a847bc0b --- /dev/null +++ b/script/modules/ImmutableSplitsDriver.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.20; + +import {create3ManagedProxy} from "script/utils/Create3Helpers.sol"; +import { + isModuleDeployed, + ModulesDeployer, + getModule, + Module, + ModuleData +} from "script/utils/ModulesDeployer.sol"; +import {Drips, dripsModule, DripsModule} from "script/modules/Drips.sol"; +import {ImmutableSplitsDriver} from "src/ImmutableSplitsDriver.sol"; + +bytes32 constant IMMUTABLE_SPLITS_DRIVER_MODULE_SALT = "ImmutableSplitsDriverModule"; + +function isImmutableSplitsDriverModuleDeployed(ModulesDeployer modulesDeployer) + view + returns (bool yes) +{ + return isModuleDeployed(modulesDeployer, IMMUTABLE_SPLITS_DRIVER_MODULE_SALT); +} + +function immutableSplitsDriverModule(ModulesDeployer modulesDeployer) + view + returns (ImmutableSplitsDriverModule) +{ + return + ImmutableSplitsDriverModule(getModule(modulesDeployer, IMMUTABLE_SPLITS_DRIVER_MODULE_SALT)); +} + +function immutableSplitsDriverModuleData(ModulesDeployer modulesDeployer, address admin) + pure + returns (ModuleData memory) +{ + bytes memory args = abi.encode(modulesDeployer, admin); + return ModuleData({ + salt: IMMUTABLE_SPLITS_DRIVER_MODULE_SALT, + initCode: abi.encodePacked(type(ImmutableSplitsDriverModule).creationCode, args), + value: 0 + }); +} + +contract ImmutableSplitsDriverModule is Module { + ImmutableSplitsDriver public immutable immutableSplitsDriver; + + constructor(ModulesDeployer modulesDeployer, address admin) + Module(modulesDeployer, IMMUTABLE_SPLITS_DRIVER_MODULE_SALT) + { + DripsModule dripsModule_ = dripsModule(modulesDeployer); + Drips drips = dripsModule_.drips(); + uint32 driverId = 2; + ImmutableSplitsDriver logic = new ImmutableSplitsDriver(drips, driverId); + address proxy = + create3ManagedProxy(modulesDeployer, "ImmutableSplitsDriver", logic, admin, ""); + immutableSplitsDriver = ImmutableSplitsDriver(proxy); + dripsModule_.claimDriverId(IMMUTABLE_SPLITS_DRIVER_MODULE_SALT, driverId, proxy); + } +} diff --git a/script/modules/LZBridgedGovernor.sol b/script/modules/LZBridgedGovernor.sol new file mode 100644 index 00000000..2df0947b --- /dev/null +++ b/script/modules/LZBridgedGovernor.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.20; + +import {create3GovernorProxy} from "script/utils/Create3Helpers.sol"; +import { + isModuleDeployed, + ModulesDeployer, + getModule, + Module, + ModuleData +} from "script/utils/ModulesDeployer.sol"; +import {LZBridgedGovernor} from "src/BridgedGovernor.sol"; + +bytes32 constant LZ_BRIDGED_GOVERNOR_MODULE_SALT = "LZBridgedGovernorModule"; + +function isLZBridgedGovernorModuleDeployed(ModulesDeployer modulesDeployer) + view + returns (bool yes) +{ + return isModuleDeployed(modulesDeployer, LZ_BRIDGED_GOVERNOR_MODULE_SALT); +} + +function lzBridgedGovernorModule(ModulesDeployer modulesDeployer) + view + returns (LZBridgedGovernorModule) +{ + return LZBridgedGovernorModule(getModule(modulesDeployer, LZ_BRIDGED_GOVERNOR_MODULE_SALT)); +} + +function lzBridgedGovernorModuleData( + ModulesDeployer modulesDeployer, + address endpoint, + uint32 ownerEid, + bytes32 owner +) pure returns (ModuleData memory) { + bytes memory args = abi.encode(modulesDeployer, endpoint, ownerEid, owner); + return ModuleData({ + salt: LZ_BRIDGED_GOVERNOR_MODULE_SALT, + initCode: abi.encodePacked(type(LZBridgedGovernorModule).creationCode, args), + value: 0 + }); +} + +contract LZBridgedGovernorModule is Module { + LZBridgedGovernor public immutable lzBridgedGovernor; + + constructor(ModulesDeployer modulesDeployer, address endpoint, uint32 ownerEid, bytes32 owner) + Module(modulesDeployer, LZ_BRIDGED_GOVERNOR_MODULE_SALT) + { + LZBridgedGovernor logic = new LZBridgedGovernor(endpoint, ownerEid, owner); + address proxy = create3GovernorProxy(modulesDeployer, "LZBridgedGovernor", logic); + lzBridgedGovernor = LZBridgedGovernor(payable(proxy)); + } +} diff --git a/script/modules/NFTDriver.sol b/script/modules/NFTDriver.sol new file mode 100644 index 00000000..5530973b --- /dev/null +++ b/script/modules/NFTDriver.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.20; + +import {callerModule} from "script/modules/Caller.sol"; +import {Drips, dripsModule, DripsModule} from "script/modules/Drips.sol"; +import {create3ManagedProxy} from "script/utils/Create3Helpers.sol"; +import { + isModuleDeployed, + ModulesDeployer, + getModule, + Module, + ModuleData +} from "script/utils/ModulesDeployer.sol"; +import {NFTDriver} from "src/NFTDriver.sol"; + +bytes32 constant NFT_DRIVER_MODULE_SALT = "NFTDriverModule"; + +function isNFTDriverModuleDeployed(ModulesDeployer modulesDeployer) view returns (bool yes) { + return isModuleDeployed(modulesDeployer, NFT_DRIVER_MODULE_SALT); +} + +function nftDriverModule(ModulesDeployer modulesDeployer) view returns (NFTDriverModule) { + return NFTDriverModule(getModule(modulesDeployer, NFT_DRIVER_MODULE_SALT)); +} + +function nftDriverModuleData(ModulesDeployer modulesDeployer, address admin) + pure + returns (ModuleData memory) +{ + bytes memory args = abi.encode(modulesDeployer, admin); + return ModuleData({ + salt: NFT_DRIVER_MODULE_SALT, + initCode: abi.encodePacked(type(NFTDriverModule).creationCode, args), + value: 0 + }); +} + +contract NFTDriverModule is Module { + NFTDriver public immutable nftDriver; + + constructor(ModulesDeployer modulesDeployer, address admin) + Module(modulesDeployer, NFT_DRIVER_MODULE_SALT) + { + DripsModule dripsModule_ = dripsModule(modulesDeployer); + Drips drips = dripsModule_.drips(); + address forwarder = address(callerModule(modulesDeployer).caller()); + uint32 driverId = 1; + NFTDriver logic = new NFTDriver(drips, forwarder, driverId); + address proxy = create3ManagedProxy(modulesDeployer, "NFTDriver", logic, admin, ""); + nftDriver = NFTDriver(proxy); + dripsModule_.claimDriverId(NFT_DRIVER_MODULE_SALT, driverId, proxy); + } +} diff --git a/script/modules/NativeTokenUnwrapper.sol b/script/modules/NativeTokenUnwrapper.sol new file mode 100644 index 00000000..69f2fe8e --- /dev/null +++ b/script/modules/NativeTokenUnwrapper.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.20; + +import {create3} from "script/utils/Create3Helpers.sol"; +import { + isModuleDeployed, + ModulesDeployer, + getModule, + Module, + ModuleData +} from "script/utils/ModulesDeployer.sol"; +import {IWrappedNativeToken} from "src/IWrappedNativeToken.sol"; +import {NativeTokenUnwrapper} from "src/NativeTokenUnwrapper.sol"; + +bytes32 constant NATIVE_TOKEN_UNWRAPPER_MODULE_SALT = "NativeTokenUnwrapperModule"; + +function isNativeTokenUnwrapperModuleDeployed(ModulesDeployer modulesDeployer) + view + returns (bool yes) +{ + return isModuleDeployed(modulesDeployer, NATIVE_TOKEN_UNWRAPPER_MODULE_SALT); +} + +function nativeTokenUnwrapperModule(ModulesDeployer modulesDeployer) + view + returns (NativeTokenUnwrapperModule) +{ + return + NativeTokenUnwrapperModule(getModule(modulesDeployer, NATIVE_TOKEN_UNWRAPPER_MODULE_SALT)); +} + +function nativeTokenUnwrapperModuleData( + ModulesDeployer modulesDeployer, + IWrappedNativeToken wrappedNativeToken +) pure returns (ModuleData memory) { + bytes memory args = abi.encode(modulesDeployer, wrappedNativeToken); + return ModuleData({ + salt: NATIVE_TOKEN_UNWRAPPER_MODULE_SALT, + initCode: abi.encodePacked(type(NativeTokenUnwrapperModule).creationCode, args), + value: 0 + }); +} + +contract NativeTokenUnwrapperModule is Module { + NativeTokenUnwrapper public immutable nativeTokenUnwrapper; + + constructor(ModulesDeployer modulesDeployer, IWrappedNativeToken wrappedNativeToken) + Module(modulesDeployer, NATIVE_TOKEN_UNWRAPPER_MODULE_SALT) + { + bytes memory args = abi.encode(wrappedNativeToken); + // slither-disable-next-line too-many-digits + address deployment = create3( + modulesDeployer, "NativeTokenUnwrapper", type(NativeTokenUnwrapper).creationCode, args + ); + nativeTokenUnwrapper = NativeTokenUnwrapper(payable(deployment)); + } +} diff --git a/script/modules/RepoDriver.sol b/script/modules/RepoDriver.sol new file mode 100644 index 00000000..c7305836 --- /dev/null +++ b/script/modules/RepoDriver.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.20; + +import {callerModule} from "script/modules/Caller.sol"; +import {Drips, dripsModule, DripsModule} from "script/modules/Drips.sol"; +import {create3ManagedProxy} from "script/utils/Create3Helpers.sol"; +import { + isModuleDeployed, + ModulesDeployer, + getModule, + Module, + ModuleData +} from "script/utils/ModulesDeployer.sol"; +import {IAutomate, RepoDriver} from "src/RepoDriver.sol"; + +bytes32 constant REPO_DRIVER_MODULE_SALT = "RepoDriverModule"; + +function isRepoDriverModuleDeployed(ModulesDeployer modulesDeployer) view returns (bool yes) { + return isModuleDeployed(modulesDeployer, REPO_DRIVER_MODULE_SALT); +} + +function repoDriverModule(ModulesDeployer modulesDeployer) view returns (RepoDriverModule) { + return RepoDriverModule(getModule(modulesDeployer, REPO_DRIVER_MODULE_SALT)); +} + +function repoDriverModuleData( + ModulesDeployer modulesDeployer, + address admin, + IAutomate gelatoAutomate, + string memory ipfsCid, + uint32 maxRequestsPerBlock, + uint32 maxRequestsPer31Days +) pure returns (ModuleData memory) { + bytes memory args = abi.encode( + modulesDeployer, admin, gelatoAutomate, ipfsCid, maxRequestsPerBlock, maxRequestsPer31Days + ); + return ModuleData({ + salt: REPO_DRIVER_MODULE_SALT, + initCode: abi.encodePacked(type(RepoDriverModule).creationCode, args), + value: 0 + }); +} + +contract RepoDriverModule is Module { + RepoDriver public immutable repoDriver; + + constructor( + ModulesDeployer modulesDeployer, + address admin, + IAutomate gelatoAutomate, + string memory ipfsCid, + uint32 maxRequestsPerBlock, + uint32 maxRequestsPer31Days + ) Module(modulesDeployer, REPO_DRIVER_MODULE_SALT) { + DripsModule dripsModule_ = dripsModule(modulesDeployer); + Drips drips = dripsModule_.drips(); + address forwarder = address(callerModule(modulesDeployer).caller()); + uint32 driverId = 3; + RepoDriver logic = new RepoDriver(drips, forwarder, driverId, gelatoAutomate); + bytes memory data = abi.encodeCall( + RepoDriver.updateGelatoTask, (ipfsCid, maxRequestsPerBlock, maxRequestsPer31Days) + ); + address proxy = create3ManagedProxy(modulesDeployer, "RepoDriver", logic, admin, data); + repoDriver = RepoDriver(payable(proxy)); + dripsModule_.claimDriverId(REPO_DRIVER_MODULE_SALT, driverId, proxy); + } +} diff --git a/script/utils/Create3Factory.sol b/script/utils/Create3Factory.sol new file mode 100644 index 00000000..61e4d414 --- /dev/null +++ b/script/utils/Create3Factory.sol @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.20; + +import {console} from "forge-std/Script.sol"; +import {Address} from "openzeppelin-contracts/utils/Address.sol"; + +/// @dev The singleton factory for deterministic deployment. +/// Deployed by Safe, addresses taken from https://github.com/safe-global/safe-singleton-factory. +address constant SINGLETON_FACTORY = 0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7; + +function deployCreate3Factory() returns (ICreate3Factory deployment) { + deployment = ICreate3Factory(0xe9BE461efaB6f9079741da3b180249F81e66A461); + if (Address.isContract(address(deployment))) { + console.log("Create3Factory already deployed"); + return deployment; + } + + /// @notice The creation code of Create3Factory. + /// It's reused verbatim to keep it byte-for-byte identical across all deployments and chains, + /// so the Safe singleton factory always deploys it under the same address + /// regardless of the currently used compiler version and configuration. + /// Taken from https://github.com/ZeframLou/create3-factory, + /// originally deployed on Ethereum as `0x9fBB3DF7C40Da2e5A0dE984fFE2CCB7C47cd0ABf` + /// in transaction `0xb05de371a18fc4f02753b34a689939cee69b93a043b926732043780959b7c4e3`. + bytes memory creationCode = + hex"608060405234801561001057600080fd5b5061063b806100206000396000f3fe6080604052600436106100" + hex"295760003560e01c806350f1c4641461002e578063cdcb760a14610077575b600080fd5b34801561003a57" + hex"600080fd5b5061004e610049366004610489565b61008a565b60405173ffffffffffffffffffffffffffff" + hex"ffffffffffff909116815260200160405180910390f35b61004e6100853660046104fd565b6100ee565b60" + hex"40517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084901b166020" + hex"820152603481018290526000906054016040516020818303038152906040528051906020012091506100e7" + hex"8261014c565b9392505050565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000" + hex"0000000000003360601b166020820152603481018390526000906054016040516020818303038152906040" + hex"528051906020012092506100e78383346102b2565b604080518082018252601081527f67363d3d37363d34" + hex"f03d5260086018f30000000000000000000000000000000060209182015290517fff000000000000000000" + hex"00000000000000000000000000000000000000000000918101919091527fffffffffffffffffffffffffff" + hex"ffffffffffffff0000000000000000000000003060601b166021820152603581018290527f21c35dbe1b34" + hex"4a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f6055820152600090819061022890607501" + hex"5b6040516020818303038152906040528051906020012090565b6040517fd6940000000000000000000000" + hex"0000000000000000000000000000000000000060208201527fffffffffffffffffffffffffffffffffffff" + hex"ffff000000000000000000000000606083901b1660228201527f0100000000000000000000000000000000" + hex"00000000000000000000000000000060368201529091506100e79060370161020f565b6000806040518060" + hex"400160405280601081526020017f67363d3d37363d34f03d5260086018f300000000000000000000000000" + hex"00000081525090506000858251602084016000f5905073ffffffffffffffffffffffffffffffffffffffff" + hex"811661037d576040517f08c379a00000000000000000000000000000000000000000000000000000000081" + hex"5260206004820152601160248201527f4445504c4f594d454e545f4641494c454400000000000000000000" + hex"000000000060448201526064015b60405180910390fd5b6103868661014c565b925060008173ffffffffff" + hex"ffffffffffffffffffffffffffffff1685876040516103b091906105d6565b60006040518083038185875a" + hex"f1925050503d80600081146103ed576040519150601f19603f3d011682016040523d82523d600060208401" + hex"3e6103f2565b606091505b50509050808015610419575073ffffffffffffffffffffffffffffffffffffff" + hex"ff84163b15155b61047f576040517f08c379a0000000000000000000000000000000000000000000000000" + hex"00000000815260206004820152601560248201527f494e495449414c495a4154494f4e5f4641494c454400" + hex"000000000000000000006044820152606401610374565b5050509392505050565b60008060408385031215" + hex"61049c57600080fd5b823573ffffffffffffffffffffffffffffffffffffffff811681146104c057600080" + hex"fd5b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000" + hex"000000000000600052604160045260246000fd5b6000806040838503121561051057600080fd5b82359150" + hex"602083013567ffffffffffffffff8082111561052f57600080fd5b818501915085601f8301126105435760" + hex"0080fd5b813581811115610555576105556104ce565b604051601f82017fffffffffffffffffffffffffff" + hex"ffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561059b576105" + hex"9b6104ce565b816040528281528860208487010111156105b457600080fd5b826020860160208301376000" + hex"6020848301015280955050505050509250929050565b6000825160005b818110156105f757602081860181" + hex"015185830152016105dd565b50600092019182525091905056fea2646970667358221220fd377c185926b3" + hex"110b7e8a544f897646caf36a0e82b2629de851045e2a5f937764736f6c63430008100033"; + bytes32 salt = 0; + require(Address.isContract(SINGLETON_FACTORY), "Singleton factory not deployed"); + bytes memory addr = Address.functionCall( + SINGLETON_FACTORY, bytes.concat(salt, creationCode), "Create3Factory deployment failed" + ); + require(address(bytes20(addr)) == address(deployment), "Invalid Create3Factory address"); +} + +/// @title Factory for deploying contracts to deterministic addresses via CREATE3. +/// @author zefram.eth, taken from https://github.com/ZeframLou/create3-factory. +/// @notice Enables deploying contracts using CREATE3. +/// Each deployer (`msg.sender`) has its own namespace for deployed addresses. +interface ICreate3Factory { + /// @notice Deploys a contract using CREATE3. + /// @dev The provided salt is hashed together with msg.sender to generate the final salt. + /// @param salt The deployer-specific salt for determining the deployed contract's address. + /// @param creationCode The creation code of the contract to deploy. + /// @return deployed The address of the deployed contract. + function deploy(bytes32 salt, bytes memory creationCode) + external + payable + returns (address deployed); + + /// @notice Predicts the address of a deployed contract. + /// @dev The provided salt is hashed together + /// with the deployer address to generate the final salt. + /// @param deployer The deployer account that will call `deploy()`. + /// @param salt The deployer-specific salt for determining the deployed contract's address. + /// @return deployed The address of the contract that will be deployed. + function getDeployed(address deployer, bytes32 salt) external view returns (address deployed); +} diff --git a/script/utils/Create3Helpers.sol b/script/utils/Create3Helpers.sol new file mode 100644 index 00000000..bea3dcdb --- /dev/null +++ b/script/utils/Create3Helpers.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.20; + +import {ModulesDeployer} from "script/utils/ModulesDeployer.sol"; +import {Call, Governor, GovernorProxy} from "src/BridgedGovernor.sol"; +import {Managed, ManagedProxy} from "src/Managed.sol"; + +function create3ManagedProxy( + ModulesDeployer modulesDeployer, + bytes32 salt, + Managed logic, + address admin, + bytes memory data +) returns (address proxy) { + bytes memory args = abi.encode(logic, admin, data); + // slither-disable-next-line too-many-digits + return create3(modulesDeployer, salt, type(ManagedProxy).creationCode, args); +} + +function create3GovernorProxy(ModulesDeployer modulesDeployer, bytes32 salt, Governor logic) + returns (address proxy) +{ + bytes memory args = abi.encode(logic, new Call[](0)); + // slither-disable-next-line too-many-digits + return create3(modulesDeployer, salt, type(GovernorProxy).creationCode, args); +} + +function create3( + ModulesDeployer modulesDeployer, + bytes32 salt, + bytes memory creationCode, + bytes memory args +) returns (address deployment) { + return modulesDeployer.create3Factory().deploy(salt, abi.encodePacked(creationCode, args)); +} diff --git a/script/utils/DeploymentJson.sol b/script/utils/DeploymentJson.sol new file mode 100644 index 00000000..fa117f3b --- /dev/null +++ b/script/utils/DeploymentJson.sol @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.20; + +import {VmSafe} from "forge-std/Script.sol"; +import { + addressDriverModule, isAddressDriverModuleDeployed +} from "script/modules/AddressDriver.sol"; +import { + axelarBridgedGovernorModule, + isAxelarBridgedGovernorModuleDeployed +} from "script/modules/AxelarBridgedGovernor.sol"; +import {callerModule, isCallerModuleDeployed} from "script/modules/Caller.sol"; +import {Drips, dripsModule, isDripsModuleDeployed} from "script/modules/Drips.sol"; +import { + giversRegistryModule, isGiversRegistryModuleDeployed +} from "script/modules/GiversRegistry.sol"; +import { + immutableSplitsDriverModule, + isImmutableSplitsDriverModuleDeployed +} from "script/modules/ImmutableSplitsDriver.sol"; +import { + isLZBridgedGovernorModuleDeployed, + lzBridgedGovernorModule +} from "script/modules/LZBridgedGovernor.sol"; +import { + isNativeTokenUnwrapperModuleDeployed, + nativeTokenUnwrapperModule +} from "script/modules/NativeTokenUnwrapper.sol"; +import {isNFTDriverModuleDeployed, nftDriverModule} from "script/modules/NFTDriver.sol"; +import { + isRepoDriverModuleDeployed, RepoDriver, repoDriverModule +} from "script/modules/RepoDriver.sol"; +import {isModuleDeployed, ModulesDeployer} from "script/utils/ModulesDeployer.sol"; + +function writeDeploymentJson(VmSafe vm, ModulesDeployer modulesDeployer, string memory salt) { + string memory objectKey = "deployment JSON"; + + if (isAxelarBridgedGovernorModuleDeployed(modulesDeployer)) { + vm.serializeAddress( + objectKey, + "AxelarBridgedGovernor", + address(axelarBridgedGovernorModule(modulesDeployer).axelarBridgedGovernor()) + ); + } + + if (isLZBridgedGovernorModuleDeployed(modulesDeployer)) { + vm.serializeAddress( + objectKey, + "LZBridgedGovernor", + address(lzBridgedGovernorModule(modulesDeployer).lzBridgedGovernor()) + ); + } + + if (isCallerModuleDeployed(modulesDeployer)) { + vm.serializeAddress(objectKey, "Caller", address(callerModule(modulesDeployer).caller())); + } + + if (isDripsModuleDeployed(modulesDeployer)) { + Drips drips = dripsModule(modulesDeployer).drips(); + vm.serializeAddress(objectKey, "Drips", address(drips)); + vm.serializeUint(objectKey, "Drips cycle seconds", drips.cycleSecs()); + } + + if (isAddressDriverModuleDeployed(modulesDeployer)) { + vm.serializeAddress( + objectKey, + "AddressDriver", + address(addressDriverModule(modulesDeployer).addressDriver()) + ); + } + + if (isNFTDriverModuleDeployed(modulesDeployer)) { + vm.serializeAddress( + objectKey, "NFTDriver", address(nftDriverModule(modulesDeployer).nftDriver()) + ); + } + + if (isImmutableSplitsDriverModuleDeployed(modulesDeployer)) { + vm.serializeAddress( + objectKey, + "ImmutableSplitsDriver", + address(immutableSplitsDriverModule(modulesDeployer).immutableSplitsDriver()) + ); + } + + if (isRepoDriverModuleDeployed(modulesDeployer)) { + RepoDriver repoDriver = repoDriverModule(modulesDeployer).repoDriver(); + vm.serializeAddress(objectKey, "RepoDriver", address(repoDriver)); + vm.serializeAddress( + objectKey, "RepoDriver tasks owner", address(repoDriver.gelatoTasksOwner()) + ); + } + + if (isGiversRegistryModuleDeployed(modulesDeployer)) { + vm.serializeAddress( + objectKey, + "GiversRegistry", + address(giversRegistryModule(modulesDeployer).giversRegistry()) + ); + } + + if (isNativeTokenUnwrapperModuleDeployed(modulesDeployer)) { + vm.serializeAddress( + objectKey, + "NativeTokenUnwrapper", + address(nativeTokenUnwrapperModule(modulesDeployer).nativeTokenUnwrapper()) + ); + } + + vm.serializeAddress(objectKey, "ModulesDeployer", address(modulesDeployer)); + vm.serializeString(objectKey, "Salt", salt); + vm.serializeAddress(objectKey, "Deployer", msg.sender); + string memory json = vm.serializeUint(objectKey, "Chain ID", block.chainid); + + vm.writeJson(json, "deployment.json"); +} diff --git a/script/utils/ModulesDeployer.sol b/script/utils/ModulesDeployer.sol new file mode 100644 index 00000000..f7a55035 --- /dev/null +++ b/script/utils/ModulesDeployer.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.20; + +import {ICreate3Factory} from "script/utils/Create3Factory.sol"; +import {Ownable2Step} from "openzeppelin-contracts/access/Ownable2Step.sol"; +import {Address} from "openzeppelin-contracts/utils/Address.sol"; + +struct ModuleData { + bytes32 salt; + bytes initCode; + uint256 value; +} + +function deployModulesDeployer(ICreate3Factory create3Factory, bytes32 salt, address owner) + returns (ModulesDeployer deployment) +{ + bytes memory args = abi.encode(create3Factory, owner); + bytes memory creationCode = abi.encodePacked(type(ModulesDeployer).creationCode, args); + address modulesDeployer = create3Factory.deploy(salt, creationCode); + return ModulesDeployer(payable(modulesDeployer)); +} + +contract ModulesDeployer is Ownable2Step { + ICreate3Factory public immutable create3Factory; + + constructor(ICreate3Factory create3Factory_, address owner) { + create3Factory = create3Factory_; + _transferOwnership(owner); + } + + receive() external payable {} + + function deployModules(ModuleData[] calldata modules) public payable onlyOwner { + for (uint256 i = 0; i < modules.length; i++) { + ModuleData calldata module_ = modules[i]; + // slither-disable-next-line reentrancy-eth,reentrancy-no-eth + create3Factory.deploy{value: module_.value}(module_.salt, module_.initCode); + } + } + + function module(bytes32 salt) public view returns (address addr) { + return create3Factory.getDeployed(address(this), salt); + } +} + +function isModuleDeployed(ModulesDeployer modulesDeployer, bytes32 salt) view returns (bool yes) { + address module = modulesDeployer.module(salt); + return Address.isContract(module); +} + +function getModule(ModulesDeployer modulesDeployer, bytes32 salt) view returns (address module) { + module = modulesDeployer.module(salt); + require(Address.isContract(module), string.concat(string(bytes.concat(salt)), " not deployed")); +} + +abstract contract Module { + ModulesDeployer internal immutable _modulesDeployer; + + constructor(ModulesDeployer modulesDeployer, bytes32 moduleSalt) { + _modulesDeployer = modulesDeployer; + require(address(this) == modulesDeployer.module(moduleSalt), "Invalid module salt"); + } + + modifier onlyModule(bytes32 senderSalt) { + require(msg.sender == _modulesDeployer.module(senderSalt), "Callable only by a module"); + _; + } +} diff --git a/src/DripsDeployer.sol b/src/DripsDeployer.sol deleted file mode 100644 index 6674e3ec..00000000 --- a/src/DripsDeployer.sol +++ /dev/null @@ -1,388 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -pragma solidity ^0.8.20; - -import {AddressDriver} from "./AddressDriver.sol"; -import {Caller} from "./Caller.sol"; -import {Drips} from "./Drips.sol"; -import {ImmutableSplitsDriver} from "./ImmutableSplitsDriver.sol"; -import {Managed, ManagedProxy} from "./Managed.sol"; -import {NFTDriver} from "./NFTDriver.sol"; -import {IAutomate, RepoDriver} from "./RepoDriver.sol"; -import {Ownable2Step} from "openzeppelin-contracts/access/Ownable2Step.sol"; -import {Address} from "openzeppelin-contracts/utils/Address.sol"; - -struct Module { - bytes32 salt; - uint256 amount; - bytes initCode; -} - -contract DripsDeployer is Ownable2Step { - // slither-disable-next-line naming-convention - bytes32[] internal _moduleSalts; - address public immutable initialOwner; - - function args() public view returns (bytes memory) { - return abi.encode(initialOwner); - } - - constructor(address initialOwner_) { - // slither-disable-next-line missing-zero-check - initialOwner = initialOwner_; - _transferOwnership(initialOwner); - } - - function deployModules( - Module[] calldata modules1, - Module[] calldata modules2, - Module[] calldata modules3, - Module[] calldata modules4 - ) public onlyOwner { - _deployModules(modules1); - _deployModules(modules2); - _deployModules(modules3); - _deployModules(modules4); - } - - function _deployModules(Module[] calldata modules) internal { - for (uint256 i = 0; i < modules.length; i++) { - Module calldata module = modules[i]; - _moduleSalts.push(module.salt); - // slither-disable-next-line reentrancy-eth,reentrancy-no-eth - Create3Factory.deploy(module.amount, module.salt, module.initCode); - } - } - - function moduleSalts() public view returns (bytes32[] memory) { - return _moduleSalts; - } - - function moduleAddress(bytes32 salt) public view returns (address addr) { - return Create3Factory.getDeployed(salt); - } -} - -abstract contract BaseModule { - DripsDeployer public immutable dripsDeployer; - bytes32 public immutable moduleSalt; - - constructor(DripsDeployer dripsDeployer_, bytes32 moduleSalt_) { - dripsDeployer = dripsDeployer_; - moduleSalt = moduleSalt_; - require(address(this) == _moduleAddress(moduleSalt_), "Invalid module deployment salt"); - } - - function args() public view virtual returns (bytes memory); - - function _moduleAddress(bytes32 salt) internal view returns (address addr) { - return dripsDeployer.moduleAddress(salt); - } - - modifier onlyModule(bytes32 salt) { - require(msg.sender == _moduleAddress(bytes32(salt))); - _; - } -} - -abstract contract ContractDeployerModule is BaseModule { - bytes32 public immutable salt = "deployment"; - - function deployment() public view returns (address) { - return Create3Factory.getDeployed(salt); - } - - function deploymentArgs() public view virtual returns (bytes memory); - - function _deployContract(bytes memory creationCode) internal { - Create3Factory.deploy(0, salt, abi.encodePacked(creationCode, deploymentArgs())); - } -} - -abstract contract ProxyDeployerModule is BaseModule { - bytes32 public immutable proxySalt = "proxy"; - address public proxyAdmin; - address public logic; - bytes public proxyDelegateCalldata; - - function proxy() public view returns (address) { - return Create3Factory.getDeployed(proxySalt); - } - - function proxyArgs() public view returns (bytes memory) { - return abi.encode(logic, proxyAdmin, proxyDelegateCalldata); - } - - function logicArgs() public view virtual returns (bytes memory); - - function _deployProxy(address proxyAdmin_, bytes memory logicCreationCode) internal { - _deployProxy(proxyAdmin_, logicCreationCode, ""); - } - - // slither-disable-next-line reentrancy-benign - function _deployProxy( - address proxyAdmin_, - bytes memory logicCreationCode, - bytes memory proxyDelegateCalldata_ - ) internal { - // Deploy logic - address logic_; - bytes memory logicInitCode = abi.encodePacked(logicCreationCode, logicArgs()); - // slither-disable-next-line assembly - assembly ("memory-safe") { - logic_ := create(0, add(logicInitCode, 32), mload(logicInitCode)) - } - require(logic_ != address(0), "Logic deployment failed"); - logic = logic_; - // Deploy proxy - proxyAdmin = proxyAdmin_; - proxyDelegateCalldata = proxyDelegateCalldata_; - // slither-disable-next-line too-many-digits - bytes memory proxyInitCode = abi.encodePacked(type(ManagedProxy).creationCode, proxyArgs()); - Create3Factory.deploy(0, proxySalt, proxyInitCode); - } -} - -abstract contract DripsDependentModule is BaseModule { - // slither-disable-next-line naming-convention - bytes32 internal immutable _dripsModuleSalt = "Drips"; - - function _dripsModule() internal view returns (DripsModule) { - address module = _moduleAddress(_dripsModuleSalt); - require(Address.isContract(module), "Drips module not deployed"); - return DripsModule(module); - } -} - -contract DripsModule is DripsDependentModule, ProxyDeployerModule { - uint32 public immutable dripsCycleSecs; - uint32 public immutable claimableDriverIds = 100; - - function args() public view override returns (bytes memory) { - return abi.encode(dripsDeployer, dripsCycleSecs, proxyAdmin); - } - - constructor(DripsDeployer dripsDeployer_, uint32 dripsCycleSecs_, address proxyAdmin_) - BaseModule(dripsDeployer_, _dripsModuleSalt) - { - dripsCycleSecs = dripsCycleSecs_; - // slither-disable-next-line too-many-digits - _deployProxy(proxyAdmin_, type(Drips).creationCode); - Drips drips_ = drips(); - for (uint256 i = 0; i < claimableDriverIds; i++) { - // slither-disable-next-line calls-loop,unused-return - drips_.registerDriver(address(this)); - } - } - - function logicArgs() public view override returns (bytes memory) { - return abi.encode(dripsCycleSecs); - } - - function drips() public view returns (Drips) { - return Drips(proxy()); - } - - function claimDriverId(bytes32 moduleSalt_, uint32 driverId, address driverAddr) - public - onlyModule(moduleSalt_) - { - drips().updateDriverAddress(driverId, driverAddr); - } -} - -abstract contract CallerDependentModule is BaseModule { - // slither-disable-next-line naming-convention - bytes32 internal immutable _callerModuleSalt = "Caller"; - - function _callerModule() internal view returns (CallerModule) { - address module = _moduleAddress(_callerModuleSalt); - require(Address.isContract(module), "Caller module not deployed"); - return CallerModule(module); - } -} - -contract CallerModule is ContractDeployerModule, CallerDependentModule { - function args() public view override returns (bytes memory) { - return abi.encode(dripsDeployer); - } - - constructor(DripsDeployer dripsDeployer_) BaseModule(dripsDeployer_, _callerModuleSalt) { - // slither-disable-next-line too-many-digits - _deployContract(type(Caller).creationCode); - } - - function deploymentArgs() public pure override returns (bytes memory) { - return abi.encode(); - } - - function caller() public view returns (Caller) { - return Caller(deployment()); - } -} - -abstract contract DriverModule is DripsDependentModule, ProxyDeployerModule { - uint32 public immutable driverId; - - constructor(uint32 driverId_) { - driverId = driverId_; - _dripsModule().claimDriverId(moduleSalt, driverId, proxy()); - } -} - -contract AddressDriverModule is CallerDependentModule, DriverModule(0) { - function args() public view override returns (bytes memory) { - return abi.encode(dripsDeployer, proxyAdmin); - } - - constructor(DripsDeployer dripsDeployer_, address proxyAdmin_) - BaseModule(dripsDeployer_, "AddressDriver") - { - // slither-disable-next-line too-many-digits - _deployProxy(proxyAdmin_, type(AddressDriver).creationCode); - } - - function logicArgs() public view override returns (bytes memory) { - return abi.encode(_dripsModule().drips(), _callerModule().caller(), driverId); - } - - function addressDriver() public view returns (AddressDriver) { - return AddressDriver(proxy()); - } -} - -contract NFTDriverModule is CallerDependentModule, DriverModule(1) { - function args() public view override returns (bytes memory) { - return abi.encode(dripsDeployer, proxyAdmin); - } - - constructor(DripsDeployer dripsDeployer_, address proxyAdmin_) - BaseModule(dripsDeployer_, "NFTDriver") - { - // slither-disable-next-line too-many-digits - _deployProxy(proxyAdmin_, type(NFTDriver).creationCode); - } - - function logicArgs() public view override returns (bytes memory) { - return abi.encode(_dripsModule().drips(), _callerModule().caller(), driverId); - } - - function nftDriver() public view returns (NFTDriver) { - return NFTDriver(proxy()); - } -} - -contract ImmutableSplitsDriverModule is DriverModule(2) { - function args() public view override returns (bytes memory) { - return abi.encode(dripsDeployer, proxyAdmin); - } - - constructor(DripsDeployer dripsDeployer_, address proxyAdmin_) - BaseModule(dripsDeployer_, "ImmutableSplitsDriver") - { - // slither-disable-next-line too-many-digits - _deployProxy(proxyAdmin_, type(ImmutableSplitsDriver).creationCode); - } - - function logicArgs() public view override returns (bytes memory) { - return abi.encode(_dripsModule().drips(), driverId); - } - - function immutableSplitsDriver() public view returns (ImmutableSplitsDriver) { - return ImmutableSplitsDriver(proxy()); - } -} - -contract RepoDriverModule is CallerDependentModule, DriverModule(3) { - IAutomate public immutable gelatoAutomate; - string public ipfsCid; - uint32 public immutable maxRequestsPerBlock; - uint32 public immutable maxRequestsPer31Days; - - function args() public view override returns (bytes memory) { - return abi.encode( - dripsDeployer, - proxyAdmin, - gelatoAutomate, - ipfsCid, - maxRequestsPerBlock, - maxRequestsPer31Days - ); - } - - constructor( - DripsDeployer dripsDeployer_, - address proxyAdmin_, - IAutomate gelatoAutomate_, - string memory ipfsCid_, - uint32 maxRequestsPerBlock_, - uint32 maxRequestsPer31Days_ - ) BaseModule(dripsDeployer_, "RepoDriver") { - gelatoAutomate = gelatoAutomate_; - ipfsCid = ipfsCid_; - maxRequestsPerBlock = maxRequestsPerBlock_; - maxRequestsPer31Days = maxRequestsPer31Days_; - bytes memory data = abi.encodeCall( - RepoDriver.updateGelatoTask, (ipfsCid_, maxRequestsPerBlock, maxRequestsPer31Days) - ); - // slither-disable-next-line too-many-digits - _deployProxy(proxyAdmin_, type(RepoDriver).creationCode, data); - } - - function logicArgs() public view override returns (bytes memory) { - return - abi.encode(_dripsModule().drips(), _callerModule().caller(), driverId, gelatoAutomate); - } - - function repoDriver() public view returns (RepoDriver) { - return RepoDriver(payable(proxy())); - } -} - -/// @notice Deploys contracts using CREATE3. -/// Each deployer has its own namespace for deployed addresses. -library Create3Factory { - /// @notice The CREATE3 factory address. - /// It's always the same, see `deploy_create3_factory` in the deployment script. - ICreate3Factory private constant _CREATE3_FACTORY = - ICreate3Factory(0xe9BE461efaB6f9079741da3b180249F81e66A461); - - /// @notice Deploys a contract using CREATE3. - /// @param amount The amount to pass into the deployed contract's constructor. - /// @param salt The deployer-specific salt for determining the deployed contract's address. - /// @param creationCode The creation code of the contract to deploy. - function deploy(uint256 amount, bytes32 salt, bytes memory creationCode) internal { - // slither-disable-next-line unused-return - _CREATE3_FACTORY.deploy{value: amount}(salt, creationCode); - } - - /// @notice Predicts the address of a contract deployed by this contract. - /// @param salt The deployer-specific salt for determining the deployed contract's address. - /// @return deployed The address of the contract that will be deployed. - function getDeployed(bytes32 salt) internal view returns (address deployed) { - return _CREATE3_FACTORY.getDeployed(address(this), salt); - } -} - -/// @title Factory for deploying contracts to deterministic addresses via CREATE3. -/// @author zefram.eth, taken from https://github.com/ZeframLou/create3-factory. -/// @notice Enables deploying contracts using CREATE3. -/// Each deployer (`msg.sender`) has its own namespace for deployed addresses. -interface ICreate3Factory { - /// @notice Deploys a contract using CREATE3. - /// @dev The provided salt is hashed together with msg.sender to generate the final salt. - /// @param salt The deployer-specific salt for determining the deployed contract's address. - /// @param creationCode The creation code of the contract to deploy. - /// @return deployed The address of the deployed contract. - function deploy(bytes32 salt, bytes memory creationCode) - external - payable - returns (address deployed); - - /// @notice Predicts the address of a deployed contract. - /// @dev The provided salt is hashed together - /// with the deployer address to generate the final salt. - /// @param deployer The deployer account that will call `deploy()`. - /// @param salt The deployer-specific salt for determining the deployed contract's address. - /// @return deployed The address of the contract that will be deployed. - function getDeployed(address deployer, bytes32 salt) external view returns (address deployed); -}