From 1595335c88f6f3f0c54b07fe7ecea223362e9147 Mon Sep 17 00:00:00 2001 From: smartcontracts Date: Tue, 4 Jun 2024 12:21:24 -0400 Subject: [PATCH] feat: introduce ManageDrippie script (#10708) Adds a new script called ManageDrippie that, surprise surprise, manages Drippie tasks. Replacement for the old stuff that existed inside of DeployPeriphery because the old stuff was too janky and relied on making a bunch of smart contract modifications every time you wanted to add a new drip. Drips can now be added easily by modifying a JSON configuration file. --- .../sepolia-faucet-bridges.json | 80 ++ .../drippie-config/sepolia-faucet-core.json | 57 ++ .../drippie-config/sepolia-ops.json | 58 ++ .../periphery-deploy-config/sepolia.json | 52 +- .../scripts/DeployPeriphery.s.sol | 859 ------------------ .../scripts/PeripheryDeployConfig.s.sol | 194 ---- .../periphery/deploy/DeployPeriphery.s.sol | 300 ++++++ .../deploy/PeripheryDeployConfig.s.sol | 76 ++ .../periphery/drippie/DrippieConfig.s.sol | 148 +++ .../periphery/drippie/ManageDrippie.s.sol | 182 ++++ 10 files changed, 904 insertions(+), 1102 deletions(-) create mode 100644 packages/contracts-bedrock/periphery-deploy-config/drippie-config/sepolia-faucet-bridges.json create mode 100644 packages/contracts-bedrock/periphery-deploy-config/drippie-config/sepolia-faucet-core.json create mode 100644 packages/contracts-bedrock/periphery-deploy-config/drippie-config/sepolia-ops.json delete mode 100644 packages/contracts-bedrock/scripts/DeployPeriphery.s.sol delete mode 100644 packages/contracts-bedrock/scripts/PeripheryDeployConfig.s.sol create mode 100644 packages/contracts-bedrock/scripts/periphery/deploy/DeployPeriphery.s.sol create mode 100644 packages/contracts-bedrock/scripts/periphery/deploy/PeripheryDeployConfig.s.sol create mode 100644 packages/contracts-bedrock/scripts/periphery/drippie/DrippieConfig.s.sol create mode 100644 packages/contracts-bedrock/scripts/periphery/drippie/ManageDrippie.s.sol diff --git a/packages/contracts-bedrock/periphery-deploy-config/drippie-config/sepolia-faucet-bridges.json b/packages/contracts-bedrock/periphery-deploy-config/drippie-config/sepolia-faucet-bridges.json new file mode 100644 index 000000000000..d48f8c053317 --- /dev/null +++ b/packages/contracts-bedrock/periphery-deploy-config/drippie-config/sepolia-faucet-bridges.json @@ -0,0 +1,80 @@ +{ + "drippie": "0xd6F935Bd272BEE05bD64096D82970482EF16D64b", + "gelato": "0x859E31b3848Ec384012EECc72C5c49821008296C", + + "note": "Object attributes below are prefixed with numbers because of how foundry parses JSON into structs in alphabetical order", + "drips": [ + { + "00__name": "FaucetBridgedDrip_opmainnet_V1", + "01__dripcheck": "CheckTrue", + "02__checkparams": {}, + "03__recipient": "0xFBb0621E0B23b5478B630BD55a5f21f67730B0F1", + "04__value": 34000000000000000000, + "05__interval": 86400, + "06__data": "0x000000000000000000000000f21d42203af9af1c86e1e8ac501b41f5bc004a0a0000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" + }, + { + "00__name": "FaucetBridgedDrip_base_V1", + "01__dripcheck": "CheckTrue", + "02__checkparams": {}, + "03__recipient": "0xfd0Bf71F60660E2f608ed56e1659C450eB113120", + "04__value": 34000000000000000000, + "05__interval": 86400, + "06__data": "0x000000000000000000000000f21d42203af9af1c86e1e8ac501b41f5bc004a0a0000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" + }, + { + "00__name": "FaucetBridgedDrip_zora_V1", + "01__dripcheck": "CheckTrue", + "02__checkparams": {}, + "03__recipient": "0x5376f1D543dcbB5BD416c56C189e4cB7399fCcCB", + "04__value": 34000000000000000000, + "05__interval": 86400, + "06__data": "0x000000000000000000000000f21d42203af9af1c86e1e8ac501b41f5bc004a0a0000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" + }, + { + "00__name": "FaucetBridgedDrip_pgn_V1", + "01__dripcheck": "CheckTrue", + "02__checkparams": {}, + "03__recipient": "0xFaE6abCAF30D23e233AC7faF747F2fC3a5a6Bfa3", + "04__value": 34000000000000000000, + "05__interval": 86400, + "06__data": "0x000000000000000000000000f21d42203af9af1c86e1e8ac501b41f5bc004a0a0000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" + }, + { + "00__name": "FaucetBridgedDrip_orderly_V1", + "01__dripcheck": "CheckTrue", + "02__checkparams": {}, + "03__recipient": "0x1Af0494040d6904A9F3EE21921de4b359C736333", + "04__value": 34000000000000000000, + "05__interval": 86400, + "06__data": "0x000000000000000000000000f21d42203af9af1c86e1e8ac501b41f5bc004a0a0000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" + }, + { + "00__name": "FaucetBridgedDrip_mode_V1", + "01__dripcheck": "CheckTrue", + "02__checkparams": {}, + "03__recipient": "0xbC5C679879B2965296756CD959C3C739769995E2", + "04__value": 34000000000000000000, + "05__interval": 86400, + "06__data": "0x000000000000000000000000f21d42203af9af1c86e1e8ac501b41f5bc004a0a0000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" + }, + { + "00__name": "FaucetBridgedDrip_lyra_V1", + "01__dripcheck": "CheckTrue", + "02__checkparams": {}, + "03__recipient": "0x915f179A77FB2e1AeA8b56Ebc0D75A7e1A8a7A17", + "04__value": 34000000000000000000, + "05__interval": 86400, + "06__data": "0x000000000000000000000000f21d42203af9af1c86e1e8ac501b41f5bc004a0a0000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" + }, + { + "00__name": "FaucetBridgedDrip_lisk_V1", + "01__dripcheck": "CheckTrue", + "02__checkparams": {}, + "03__recipient": "0x1Fb30e446eA791cd1f011675E5F3f5311b70faF5", + "04__value": 34000000000000000000, + "05__interval": 86400, + "06__data": "0x000000000000000000000000f21d42203af9af1c86e1e8ac501b41f5bc004a0a0000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" + } + ] +} diff --git a/packages/contracts-bedrock/periphery-deploy-config/drippie-config/sepolia-faucet-core.json b/packages/contracts-bedrock/periphery-deploy-config/drippie-config/sepolia-faucet-core.json new file mode 100644 index 000000000000..1a1ef010eca8 --- /dev/null +++ b/packages/contracts-bedrock/periphery-deploy-config/drippie-config/sepolia-faucet-core.json @@ -0,0 +1,57 @@ +{ + "drippie": "0xd6F935Bd272BEE05bD64096D82970482EF16D64b", + "gelato": "0x859E31b3848Ec384012EECc72C5c49821008296C", + + "note": "Object attributes below are prefixed with numbers because of how foundry parses JSON into structs in alphabetical order", + "drips": [ + { + "00__name": "FaucetDrip_V1", + "01__dripcheck": "CheckBalanceLow", + "02__checkparams": { + "01__target": "0xF21d42203AF9af1C86E1e8ac501B41f5bc004A0a", + "02__threshold": 100000000000000000000 + }, + "03__recipient": "0xF21d42203AF9af1C86E1e8ac501B41f5bc004A0a", + "04__value": 20000000000000000000, + "05__interval": 3600, + "06__data": "" + }, + { + "00__name": "FaucetDrip_V2", + "01__dripcheck": "CheckBalanceLow", + "02__checkparams": { + "01__target": "0xF21d42203AF9af1C86E1e8ac501B41f5bc004A0a", + "02__threshold": 20000000000000000000 + }, + "03__recipient": "0xF21d42203AF9af1C86E1e8ac501B41f5bc004A0a", + "04__value": 500000000000000000000, + "05__interval": 604800, + "06__data": "" + }, + { + "00__name": "FaucetAdminDrip_V1", + "01__dripcheck": "CheckBalanceLow", + "02__checkparams": { + "01__target": "0x212E789D4523D4BAF464f8Fb2A9B9dff2B36e5A6", + "02__threshold": 100000000000000000 + }, + "03__recipient": "0x212E789D4523D4BAF464f8Fb2A9B9dff2B36e5A6", + "04__value": 1000000000000000000, + "05__interval": 86400, + "06__data": "" + }, + { + "00__name": "FaucetGelatoDrip_V1", + "01__dripcheck": "CheckGelatoLow", + "02__checkparams": { + "00__treasury": "0x7506C12a824d73D9b08564d5Afc22c949434755e", + "01__threshold": 100000000000000000, + "02__recipient": "0x0E9b4649eB0760A4F01646636E032D68cFDe58FF" + }, + "03__recipient": "0x7506C12a824d73D9b08564d5Afc22c949434755e", + "04__value": 1000000000000000000, + "05__interval": 86400, + "06__data": "0x0000000000000000000000000e9b4649eb0760a4f01646636e032d68cfde58ff" + } + ] +} diff --git a/packages/contracts-bedrock/periphery-deploy-config/drippie-config/sepolia-ops.json b/packages/contracts-bedrock/periphery-deploy-config/drippie-config/sepolia-ops.json new file mode 100644 index 000000000000..6b38dd37b4a2 --- /dev/null +++ b/packages/contracts-bedrock/periphery-deploy-config/drippie-config/sepolia-ops.json @@ -0,0 +1,58 @@ +{ + "drippie": "0xd6F935Bd272BEE05bD64096D82970482EF16D64b", + "gelato": "0x2A6C106ae13B558BB9E2Ec64Bd2f1f7BEFF3A5E0", + + "note": "Object attributes below are prefixed with numbers because of how foundry parses JSON into structs in alphabetical order", + "drips": [ + { + "00__name": "OperationsSequencerDrip_V1", + "01__dripcheck": "CheckBalanceLow", + "02__checkparams": { + "01__target": "0x8F23BB38F531600e5d8FDDaAEC41F13FaB46E98c", + "02__threshold": 100000000000000000000 + }, + "03__recipient": "0x8F23BB38F531600e5d8FDDaAEC41F13FaB46E98c", + "04__value": 20000000000000000000, + "05__interval": 86400, + "06__data": "" + }, + { + "00__name": "OperationsProposerDrip_V1", + "01__dripcheck": "CheckBalanceLow", + "02__checkparams": { + "01__target": "0x49277EE36A024120Ee218127354c4a3591dc90A9", + "02__threshold": 100000000000000000000 + }, + "03__recipient": "0x49277EE36A024120Ee218127354c4a3591dc90A9", + "04__value": 20000000000000000000, + "05__interval": 86400, + "06__data": "" + }, + { + "00__name": "OperationsGelatoDrip_V1", + "01__dripcheck": "CheckGelatoLow", + "02__checkparams": { + "00__treasury": "0x7506C12a824d73D9b08564d5Afc22c949434755e", + "01__threshold": 1000000000000000000, + "02__recipient": "0x03C256F7Ae7518D0fe489F257ab4b928D37CBE16" + }, + "03__recipient": "0x7506C12a824d73D9b08564d5Afc22c949434755e", + "04__value": 1000000000000000000, + "05__interval": 86400, + "06__data": "0x00000000000000000000000003c256f7ae7518d0fe489f257ab4b928d37cbe16" + }, + { + "00__name": "OperationsSecretsDrip_V1", + "01__dripcheck": "CheckSecrets", + "02__checkparams": { + "00__delay": 43200, + "01__secretHashMustExist": "0x565fa8c7daa859353b5b328b97b12c7d66c5832b2a24d4e0f739a65ad266a46f", + "02__secretHashMustNotExist": "0xbc362b01d69a85dff1793803dde67df1f338f37a36fdc73dddf27283d215e614" + }, + "03__recipient": "0x03C256F7Ae7518D0fe489F257ab4b928D37CBE16", + "04__value": 1000000000000000000, + "05__interval": 3600, + "06__data": "" + } + ] +} diff --git a/packages/contracts-bedrock/periphery-deploy-config/sepolia.json b/packages/contracts-bedrock/periphery-deploy-config/sepolia.json index 7c2a8f18f50a..6bf925b7f090 100644 --- a/packages/contracts-bedrock/periphery-deploy-config/sepolia.json +++ b/packages/contracts-bedrock/periphery-deploy-config/sepolia.json @@ -1,43 +1,11 @@ { - "create2DeploymentSalt": "0.0.2", + "create2DeploymentSalt": "0.0.3", "gelatoAutomateContract": "0x2A6C106ae13B558BB9E2Ec64Bd2f1f7BEFF3A5E0", - "gelatoTreasuryContract": "0x7506C12a824d73D9b08564d5Afc22c949434755e", "operationsDrippieOwner": "0x03C256F7Ae7518D0fe489F257ab4b928D37CBE16", - "operationsSequencerDripV1Target": "0x8F23BB38F531600e5d8FDDaAEC41F13FaB46E98c", - "operationsSequencerDripV1Value": 20000000000000000000, - "operationsSequencerDripV1Interval": 86400, - "operationsSequencerDripV1Threshold": 100000000000000000000, - "operationsGelatoDripV1Recipient": "0x03C256F7Ae7518D0fe489F257ab4b928D37CBE16", - "operationsGelatoDripV1Value": 1000000000000000000, - "operationsGelatoDripV1Interval": 86400, - "operationsGelatoDripV1Threshold": 500000000000000000, - "operationsSecretsDripV1Delay": 43200, - "operationsSecretsDripV1MustExist": "0x565fa8c7daa859353b5b328b97b12c7d66c5832b2a24d4e0f739a65ad266a46f", - "operationsSecretsDripV1MustNotExist": "0xbc362b01d69a85dff1793803dde67df1f338f37a36fdc73dddf27283d215e614", - "operationsSecretsDripV1Target": "0x03C256F7Ae7518D0fe489F257ab4b928D37CBE16", - "operationsSecretsDripV1Value": 1000000000000000000, - "operationsSecretsDripV1Interval": 3600, - "faucetDrippieOwner": "0x10ab157483dd308f8B38aCF2ad823dfD255F56b5", - "faucetDripV1Value": 20000000000000000000, - "faucetDripV1Interval": 3600, - "faucetDripV1Threshold": 100000000000000000000, - "faucetDripV2Value": 500000000000000000000, - "faucetDripV2Interval": 604800, - "faucetDripV2Threshold": 20000000000000000000, - "faucetAdminDripV1Value": 1000000000000000000, - "faucetAdminDripV1Interval": 86400, - "faucetAdminDripV1Threshold": 100000000000000000, - "faucetGelatoRecipient": "0x0E9b4649eB0760A4F01646636E032D68cFDe58FF", - "faucetGelatoBalanceV1DripInterval": 86400, - "faucetGelatoBalanceV1Value": 1000000000000000000, - "faucetGelatoThreshold": 100000000000000000, - "smallOpChainFaucetDripValue": 34000000000000000000, - "smallOpChainFaucetDripInterval": 86400, - "largeOpChainFaucetDripValue": 34000000000000000000, - "largeOpChainFaucetDripInterval": 86400, + "opChainAdminWalletDripValue": 1000000000000000000, "opChainAdminWalletDripInterval": 2592000, @@ -49,21 +17,7 @@ "faucetOffchainAuthModuleTtl": 86400, "faucetOffchainAuthModuleAmount": 50000000000000000, - "opL1BridgeAddress": "0xFBb0621E0B23b5478B630BD55a5f21f67730B0F1", - "baseL1BridgeAddress": "0xfd0Bf71F60660E2f608ed56e1659C450eB113120", - "zoraL1BridgeAddress": "0x5376f1D543dcbB5BD416c56C189e4cB7399fCcCB", - "pgnL1BridgeAddress": "0xFaE6abCAF30D23e233AC7faF747F2fC3a5a6Bfa3", - "orderlyL1BridgeAddress": "0x1Af0494040d6904A9F3EE21921de4b359C736333", - "modeL1BridgeAddress": "0xbC5C679879B2965296756CD959C3C739769995E2", - "lyraL1BridgeAddress": "0x915f179A77FB2e1AeA8b56Ebc0D75A7e1A8a7A17", - "liskL1BridgeAddress": "0x1Fb30e446eA791cd1f011675E5F3f5311b70faF5", - "deployDripchecks": true, "deployFaucetContracts": false, - "deployOperationsContracts": true, - "installOpChainFaucetsDrips": false, - "archivePreviousOpChainFaucetsDrips": false, - - "dripVersion": 3, - "previousDripVersion": 2 + "deployOperationsContracts": true } diff --git a/packages/contracts-bedrock/scripts/DeployPeriphery.s.sol b/packages/contracts-bedrock/scripts/DeployPeriphery.s.sol deleted file mode 100644 index e0e8d0dbf596..000000000000 --- a/packages/contracts-bedrock/scripts/DeployPeriphery.s.sol +++ /dev/null @@ -1,859 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import { console2 as console } from "forge-std/console2.sol"; -import { Script } from "forge-std/Script.sol"; - -import { IAutomate as IGelato } from "gelato/interfaces/IAutomate.sol"; -import { LibDataTypes as GelatoDataTypes } from "gelato/libraries/LibDataTypes.sol"; -import { LibTaskId as GelatoTaskId } from "gelato/libraries/LibTaskId.sol"; -import { GelatoBytes } from "gelato/vendor/gelato/GelatoBytes.sol"; - -import { Config } from "scripts/Config.sol"; -import { Artifacts } from "scripts/Artifacts.s.sol"; -import { PeripheryDeployConfig } from "scripts/PeripheryDeployConfig.s.sol"; - -import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; -import { Proxy } from "src/universal/Proxy.sol"; -import { L1StandardBridge } from "src/L1/L1StandardBridge.sol"; -import { Faucet } from "src/periphery/faucet/Faucet.sol"; -import { Drippie } from "src/periphery/drippie/Drippie.sol"; -import { CheckGelatoLow } from "src/periphery/drippie/dripchecks/CheckGelatoLow.sol"; -import { CheckBalanceLow } from "src/periphery/drippie/dripchecks/CheckBalanceLow.sol"; -import { CheckTrue } from "src/periphery/drippie/dripchecks/CheckTrue.sol"; -import { CheckSecrets } from "src/periphery/drippie/dripchecks/CheckSecrets.sol"; -import { AdminFaucetAuthModule } from "src/periphery/faucet/authmodules/AdminFaucetAuthModule.sol"; - -import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; - -/// @title DeployPeriphery -/// @notice Script used to deploy periphery contracts. -contract DeployPeriphery is Script, Artifacts { - /// @notice Error emitted when an address mismatch is detected. - error AddressMismatch(string, address, address); - - /// @notice Struct that contains the data for a Gelato task. - struct GelatoTaskData { - address taskCreator; - address execAddress; - bytes execData; - GelatoDataTypes.ModuleData moduleData; - address feeToken; - } - - /// @notice Deployment configuration. - PeripheryDeployConfig cfg; - - /// @notice Sets up the deployment script. - function setUp() public override { - Artifacts.setUp(); - cfg = new PeripheryDeployConfig(Config.deployConfigPath()); - console.log("Config path: %s", Config.deployConfigPath()); - } - - /// @notice Deploy all of the periphery contracts. - function run() public { - console.log("Deploying periphery contracts"); - - // Optionally deploy the base dripcheck contracts. - if (cfg.deployDripchecks()) { - deployCheckTrue(); - deployCheckBalanceLow(); - deployCheckGelatoLow(); - deployCheckSecrets(); - } - - // Optionally deploy the faucet contracts. - if (cfg.deployFaucetContracts()) { - // Deploy faucet contracts. - deployProxyAdmin(); - deployFaucetProxy(); - deployFaucet(); - deployFaucetDrippie(); - deployOnChainAuthModule(); - deployOffChainAuthModule(); - - // Initialize the faucet. - initializeFaucet(); - installFaucetAuthModulesConfigs(); - - // Optionally install OP Chain drip configs. - if (cfg.installOpChainFaucetsDrips()) { - installOpChainFaucetsDrippieConfigs(); - } - - // Optionally archive old drip configs. - if (cfg.archivePreviousOpChainFaucetsDrips()) { - archivePreviousOpChainFaucetsDrippieConfigs(); - } - } - - // Optionally deploy the operations contracts. - if (cfg.deployOperationsContracts()) { - deployOperationsDrippie(); - } - } - - /// @notice Modifier that wraps a function in broadcasting. - modifier broadcast() { - vm.startBroadcast(); - _; - vm.stopBroadcast(); - } - - /// @notice Deploy ProxyAdmin. - function deployProxyAdmin() public broadcast returns (address addr_) { - addr_ = _deployCreate2({ - _name: "ProxyAdmin", - _creationCode: type(ProxyAdmin).creationCode, - _constructorParams: abi.encode(msg.sender) - }); - - ProxyAdmin admin = ProxyAdmin(addr_); - require(admin.owner() == msg.sender); - } - - /// @notice Deploy FaucetProxy. - function deployFaucetProxy() public broadcast returns (address addr_) { - addr_ = _deployCreate2({ - _name: "FaucetProxy", - _creationCode: type(Proxy).creationCode, - _constructorParams: abi.encode(mustGetAddress("ProxyAdmin")) - }); - - Proxy proxy = Proxy(payable(addr_)); - require(EIP1967Helper.getAdmin(address(proxy)) == mustGetAddress("ProxyAdmin")); - } - - /// @notice Deploy the Faucet contract. - function deployFaucet() public broadcast returns (address addr_) { - addr_ = _deployCreate2({ - _name: "Faucet", - _creationCode: type(Faucet).creationCode, - _constructorParams: abi.encode(cfg.faucetAdmin()) - }); - - Faucet faucet = Faucet(payable(addr_)); - require(faucet.ADMIN() == cfg.faucetAdmin()); - } - - /// @notice Deploy the Drippie contract. - function deployFaucetDrippie() public broadcast returns (address addr_) { - addr_ = _deployCreate2({ - _name: "FaucetDrippie", - _creationCode: type(Drippie).creationCode, - _constructorParams: abi.encode(cfg.faucetDrippieOwner()) - }); - - Drippie drippie = Drippie(payable(addr_)); - require(drippie.owner() == cfg.faucetDrippieOwner()); - } - - /// @notice Deploy the Drippie contract for standard operations. - function deployOperationsDrippie() public broadcast returns (address addr_) { - addr_ = _deployCreate2({ - _name: "OperationsDrippie", - _creationCode: type(Drippie).creationCode, - _constructorParams: abi.encode(cfg.operationsDrippieOwner()) - }); - - Drippie drippie = Drippie(payable(addr_)); - require(drippie.owner() == cfg.operationsDrippieOwner()); - } - - /// @notice Deploy On-Chain Authentication Module. - function deployOnChainAuthModule() public broadcast returns (address addr_) { - addr_ = _deployCreate2({ - _name: "OnChainAuthModule", - _creationCode: type(AdminFaucetAuthModule).creationCode, - _constructorParams: abi.encode(cfg.faucetOnchainAuthModuleAdmin(), "OnChainAuthModule", "1") - }); - - AdminFaucetAuthModule module = AdminFaucetAuthModule(addr_); - require(module.ADMIN() == cfg.faucetOnchainAuthModuleAdmin()); - } - - /// @notice Deploy Off-Chain Authentication Module. - function deployOffChainAuthModule() public broadcast returns (address addr_) { - addr_ = _deployCreate2({ - _name: "OffChainAuthModule", - _creationCode: type(AdminFaucetAuthModule).creationCode, - _constructorParams: abi.encode(cfg.faucetOffchainAuthModuleAdmin(), "OffChainAuthModule", "1") - }); - - AdminFaucetAuthModule module = AdminFaucetAuthModule(addr_); - require(module.ADMIN() == cfg.faucetOffchainAuthModuleAdmin()); - } - - /// @notice Deploy CheckTrue contract. - function deployCheckTrue() public broadcast returns (address addr_) { - addr_ = _deployCreate2({ - _name: "CheckTrue", - _creationCode: type(CheckTrue).creationCode, - _constructorParams: hex"" - }); - } - - /// @notice Deploy CheckBalanceLow contract. - function deployCheckBalanceLow() public broadcast returns (address addr_) { - addr_ = _deployCreate2({ - _name: "CheckBalanceLow", - _creationCode: type(CheckBalanceLow).creationCode, - _constructorParams: hex"" - }); - } - - /// @notice Deploy CheckGelatoLow contract. - function deployCheckGelatoLow() public broadcast returns (address addr_) { - addr_ = _deployCreate2({ - _name: "CheckGelatoLow", - _creationCode: type(CheckGelatoLow).creationCode, - _constructorParams: hex"" - }); - } - - /// @notice Deploy CheckSecrets contract. - function deployCheckSecrets() public broadcast returns (address addr_) { - addr_ = _deployCreate2({ - _name: "CheckSecrets", - _creationCode: type(CheckSecrets).creationCode, - _constructorParams: hex"" - }); - } - - /// @notice Initialize the Faucet. - function initializeFaucet() public broadcast { - ProxyAdmin proxyAdmin = ProxyAdmin(mustGetAddress("ProxyAdmin")); - address faucetProxy = mustGetAddress("FaucetProxy"); - address faucet = mustGetAddress("Faucet"); - address implementationAddress = proxyAdmin.getProxyImplementation(faucetProxy); - if (implementationAddress == faucet) { - console.log("Faucet proxy implementation already set"); - } else { - proxyAdmin.upgrade({ _proxy: payable(faucetProxy), _implementation: faucet }); - } - - require(Faucet(payable(faucetProxy)).ADMIN() == Faucet(payable(faucet)).ADMIN()); - } - - /// @notice Installs the drip configs in the operations Drippie contract. - function installOperationsDrippieConfigs() public { - Drippie drippie = Drippie(mustGetAddress("OperationsDrippie")); - console.log("Installing operations drips at %s", address(drippie)); - installOperationsSequencerDripV1(); - installOperationsGelatoDripV1(); - installOperationsSecretsDripV1(); - console.log("Operations drip configs successfully installed"); - } - - /// @notice Installs the drip configs in the faucet Drippie contract. - function installFaucetDrippieConfigs() public { - Drippie drippie = Drippie(mustGetAddress("FaucetDrippie")); - console.log("Installing faucet drips at %s", address(drippie)); - installFaucetDripV1(); - installFaucetDripV2(); - installFaucetAdminDripV1(); - installFaucetGelatoBalanceV2(); - console.log("Faucet drip configs successfully installed"); - } - - /// @notice Installs drip configs that deposit funds to all OP Chain faucets. This function - /// should only be called on an L1 testnet. - function installOpChainFaucetsDrippieConfigs() public { - uint256 drippieOwnerPrivateKey = Config.drippieOwnerPrivateKey(); - vm.startBroadcast(drippieOwnerPrivateKey); - - Drippie drippie = Drippie(mustGetAddress("FaucetDrippie")); - console.log("Installing OP Chain faucet drips at %s", address(drippie)); - installSmallOpChainFaucetsDrips(); - installLargeOpChainFaucetsDrips(); - installSmallOpChainAdminWalletDrips(); - installLargeOpChainAdminWalletDrips(); - console.log("OP chain faucet drip configs successfully installed"); - - vm.stopBroadcast(); - } - - /// @notice Installs drips that send funds to small OP chain faucets on the scheduled interval. - function installSmallOpChainFaucetsDrips() public { - for (uint256 i = 0; i < cfg.getSmallFaucetsL1BridgeAddressesCount(); i++) { - address l1BridgeAddress = cfg.smallFaucetsL1BridgeAddresses(i); - _installDepositEthToDrip({ - _gelato: IGelato(cfg.gelatoAutomateContract()), - _drippie: Drippie(mustGetAddress("FaucetDrippie")), - _name: _makeFaucetDripName(l1BridgeAddress, cfg.dripVersion()), - _bridge: l1BridgeAddress, - _target: mustGetAddress("FaucetProxy"), - _value: cfg.smallOpChainFaucetDripValue(), - _interval: cfg.smallOpChainFaucetDripInterval() - }); - } - } - - /// @notice Installs drips that send funds to large OP chain faucets on the scheduled interval. - function installLargeOpChainFaucetsDrips() public { - for (uint256 i = 0; i < cfg.getLargeFaucetsL1BridgeAddressesCount(); i++) { - address l1BridgeAddress = cfg.largeFaucetsL1BridgeAddresses(i); - _installDepositEthToDrip({ - _gelato: IGelato(cfg.gelatoAutomateContract()), - _drippie: Drippie(mustGetAddress("FaucetDrippie")), - _name: _makeFaucetDripName(l1BridgeAddress, cfg.dripVersion()), - _bridge: l1BridgeAddress, - _target: mustGetAddress("FaucetProxy"), - _value: cfg.largeOpChainFaucetDripValue(), - _interval: cfg.largeOpChainFaucetDripInterval() - }); - } - } - - /// @notice Installs drips that send funds to the admin wallets for small OP chain faucets - /// on the scheduled interval. - function installSmallOpChainAdminWalletDrips() public { - require( - cfg.faucetOnchainAuthModuleAdmin() == cfg.faucetOffchainAuthModuleAdmin(), - "installSmallOpChainAdminWalletDrips: Only handles identical admin wallet addresses" - ); - - for (uint256 i = 0; i < cfg.getSmallFaucetsL1BridgeAddressesCount(); i++) { - address l1BridgeAddress = cfg.smallFaucetsL1BridgeAddresses(i); - _installDepositEthToDrip({ - _gelato: IGelato(cfg.gelatoAutomateContract()), - _drippie: Drippie(mustGetAddress("FaucetDrippie")), - _name: _makeAdminWalletDripName(l1BridgeAddress, cfg.dripVersion()), - _bridge: l1BridgeAddress, - _target: cfg.faucetOnchainAuthModuleAdmin(), - _value: cfg.opChainAdminWalletDripValue(), - _interval: cfg.opChainAdminWalletDripInterval() - }); - } - } - - /// @notice Installs drips that send funds to the admin wallets for large OP chain faucets - /// on the scheduled interval. - function installLargeOpChainAdminWalletDrips() public { - require( - cfg.faucetOnchainAuthModuleAdmin() == cfg.faucetOffchainAuthModuleAdmin(), - "installLargeOpChainAdminWalletDrips: Only handles identical admin wallet addresses" - ); - - for (uint256 i = 0; i < cfg.getLargeFaucetsL1BridgeAddressesCount(); i++) { - address l1BridgeAddress = cfg.largeFaucetsL1BridgeAddresses(i); - _installDepositEthToDrip({ - _gelato: IGelato(cfg.gelatoAutomateContract()), - _drippie: Drippie(mustGetAddress("FaucetDrippie")), - _name: _makeAdminWalletDripName(l1BridgeAddress, cfg.dripVersion()), - _bridge: l1BridgeAddress, - _target: cfg.faucetOnchainAuthModuleAdmin(), - _value: cfg.opChainAdminWalletDripValue(), - _interval: cfg.opChainAdminWalletDripInterval() - }); - } - } - - /// @notice Installs the OperationsSequencerDripV1 drip on the operations drippie contract. - function installOperationsSequencerDripV1() public broadcast { - _installBalanceLowDrip({ - _gelato: IGelato(cfg.gelatoAutomateContract()), - _drippie: Drippie(mustGetAddress("OperationsDrippie")), - _name: "OperationsSequencerDripV1", - _target: cfg.operationsSequencerDripV1Target(), - _value: cfg.operationsSequencerDripV1Value(), - _interval: cfg.operationsSequencerDripV1Interval(), - _threshold: cfg.operationsSequencerDripV1Threshold() - }); - } - - /// @notice Installs the OperationsGelatoDripV1 drip on the operations drippie contract. - function installOperationsGelatoDripV1() public broadcast { - _installGelatoDrip({ - _gelato: IGelato(cfg.gelatoAutomateContract()), - _drippie: Drippie(mustGetAddress("OperationsDrippie")), - _name: "OperationsGelatoDripV1", - _treasury: cfg.gelatoTreasuryContract(), - _recipient: cfg.operationsGelatoDripV1Recipient(), - _value: cfg.operationsGelatoDripV1Value(), - _interval: cfg.operationsGelatoDripV1Interval(), - _threshold: cfg.operationsGelatoDripV1Threshold() - }); - } - - /// @notice Installs the OperationsSecretsDripV1 drip on the operations drippie contract. - function installOperationsSecretsDripV1() public broadcast { - _installSecretsDrip({ - _gelato: IGelato(cfg.gelatoAutomateContract()), - _drippie: Drippie(mustGetAddress("OperationsDrippie")), - _name: "OperationsSecretsDripV1", - _delay: cfg.operationsSecretsDripV1Delay(), - _secretHashMustExist: cfg.operationsSecretsDripV1MustExist(), - _secretHashMustNotExist: cfg.operationsSecretsDripV1MustNotExist(), - _target: cfg.operationsSecretsDripV1Target(), - _value: cfg.operationsSecretsDripV1Value(), - _interval: cfg.operationsSecretsDripV1Interval() - }); - } - - /// @notice Installs the FaucetDripV1 drip on the faucet drippie contract. - function installFaucetDripV1() public broadcast { - _installBalanceLowDrip({ - _gelato: IGelato(cfg.gelatoAutomateContract()), - _drippie: Drippie(mustGetAddress("FaucetDrippie")), - _name: "FaucetDripV1", - _target: mustGetAddress("FaucetProxy"), - _value: cfg.faucetDripV1Value(), - _interval: cfg.faucetDripV1Interval(), - _threshold: cfg.faucetDripV1Threshold() - }); - } - - /// @notice Installs the FaucetDripV2 drip on the faucet drippie contract. - function installFaucetDripV2() public broadcast { - _installBalanceLowDrip({ - _gelato: IGelato(cfg.gelatoAutomateContract()), - _drippie: Drippie(mustGetAddress("FaucetDrippie")), - _name: "FaucetDripV2", - _target: mustGetAddress("FaucetProxy"), - _value: cfg.faucetDripV2Value(), - _interval: cfg.faucetDripV2Interval(), - _threshold: cfg.faucetDripV2Threshold() - }); - } - - /// @notice Installs the FaucetAdminDripV1 drip on the faucet drippie contract. - function installFaucetAdminDripV1() public broadcast { - _installBalanceLowDrip({ - _gelato: IGelato(cfg.gelatoAutomateContract()), - _drippie: Drippie(mustGetAddress("FaucetDrippie")), - _name: "FaucetAdminDripV1", - _target: mustGetAddress("FaucetProxy"), - _value: cfg.faucetAdminDripV1Value(), - _interval: cfg.faucetAdminDripV1Interval(), - _threshold: cfg.faucetAdminDripV1Threshold() - }); - } - - /// @notice Installs the GelatoBalanceV2 drip on the faucet drippie contract. - function installFaucetGelatoBalanceV2() public broadcast { - _installGelatoDrip({ - _gelato: IGelato(cfg.gelatoAutomateContract()), - _drippie: Drippie(mustGetAddress("FaucetDrippie")), - _name: "GelatoBalanceV2", - _treasury: cfg.gelatoTreasuryContract(), - _recipient: cfg.faucetGelatoRecipient(), - _value: cfg.faucetGelatoBalanceV1Value(), - _interval: cfg.faucetGelatoBalanceV1DripInterval(), - _threshold: cfg.faucetGelatoThreshold() - }); - } - - /// @notice Archives the previous OP Chain drip configs. - function archivePreviousOpChainFaucetsDrippieConfigs() public { - uint256 drippieOwnerPrivateKey = Config.drippieOwnerPrivateKey(); - vm.startBroadcast(drippieOwnerPrivateKey); - - Drippie drippie = Drippie(mustGetAddress("FaucetDrippie")); - console.log("Archiving OP Chain faucet drips at %s", address(drippie)); - archivePreviousSmallOpChainFaucetsDrips(); - archivePreviousLargeOpChainFaucetsDrips(); - - vm.stopBroadcast(); - - console.log("OP chain faucet drip configs successfully installed"); - } - - /// @notice Archives the previous small OP Chain faucet drips. - function archivePreviousSmallOpChainFaucetsDrips() public { - Drippie drippie = Drippie(mustGetAddress("FaucetDrippie")); - uint256 arrayLength = cfg.getSmallFaucetsL1BridgeAddressesCount(); - for (uint256 i = 0; i < arrayLength; i++) { - address l1BridgeAddress = cfg.smallFaucetsL1BridgeAddresses(i); - drippie.status(_makeFaucetDripName(l1BridgeAddress, cfg.previousDripVersion()), Drippie.DripStatus.PAUSED); - drippie.status( - _makeAdminWalletDripName(l1BridgeAddress, cfg.previousDripVersion()), Drippie.DripStatus.PAUSED - ); - drippie.status(_makeFaucetDripName(l1BridgeAddress, cfg.previousDripVersion()), Drippie.DripStatus.ARCHIVED); - drippie.status( - _makeAdminWalletDripName(l1BridgeAddress, cfg.previousDripVersion()), Drippie.DripStatus.ARCHIVED - ); - } - } - - /// @notice Archives the previous large OP Chain faucet drips. - function archivePreviousLargeOpChainFaucetsDrips() public { - Drippie drippie = Drippie(mustGetAddress("FaucetDrippie")); - uint256 arrayLength = cfg.getLargeFaucetsL1BridgeAddressesCount(); - for (uint256 i = 0; i < arrayLength; i++) { - address l1BridgeAddress = cfg.largeFaucetsL1BridgeAddresses(i); - drippie.status(_makeFaucetDripName(l1BridgeAddress, cfg.previousDripVersion()), Drippie.DripStatus.PAUSED); - drippie.status( - _makeAdminWalletDripName(l1BridgeAddress, cfg.previousDripVersion()), Drippie.DripStatus.PAUSED - ); - drippie.status(_makeFaucetDripName(l1BridgeAddress, cfg.previousDripVersion()), Drippie.DripStatus.ARCHIVED); - drippie.status( - _makeAdminWalletDripName(l1BridgeAddress, cfg.previousDripVersion()), Drippie.DripStatus.ARCHIVED - ); - } - } - - /// @notice Installs the OnChain AuthModule on the Faucet contract. - function installOnChainAuthModule() public broadcast { - _installAuthModule({ - _faucet: Faucet(mustGetAddress("FaucetProxy")), - _name: "OnChainAuthModule", - _config: Faucet.ModuleConfig({ - name: "OnChainAuthModule", - enabled: true, - ttl: cfg.faucetOnchainAuthModuleTtl(), - amount: cfg.faucetOnchainAuthModuleAmount() - }) - }); - } - - /// @notice Installs the OffChain AuthModule on the Faucet contract. - function installOffChainAuthModule() public broadcast { - _installAuthModule({ - _faucet: Faucet(mustGetAddress("FaucetProxy")), - _name: "OffChainAuthModule", - _config: Faucet.ModuleConfig({ - name: "OffChainAuthModule", - enabled: true, - ttl: cfg.faucetOffchainAuthModuleTtl(), - amount: cfg.faucetOffchainAuthModuleAmount() - }) - }); - } - - /// @notice Installs all of the auth modules in the faucet contract. - function installFaucetAuthModulesConfigs() public { - Faucet faucet = Faucet(mustGetAddress("FaucetProxy")); - console.log("Installing auth modules at %s", address(faucet)); - installOnChainAuthModule(); - installOffChainAuthModule(); - console.log("Faucet Auth Module configs successfully installed"); - } - - /// @notice Generates a drip name for a chain/faucet drip. - /// @param _l1Bridge The address of the L1 bridge. - /// @param _version The version of the drip. - function _makeFaucetDripName(address _l1Bridge, uint256 _version) internal pure returns (string memory) { - string memory dripNamePrefixWithBridgeAddress = string.concat("faucet-drip-", vm.toString(_l1Bridge)); - string memory versionSuffix = string.concat("-", vm.toString(_version)); - return string.concat(dripNamePrefixWithBridgeAddress, versionSuffix); - } - - /// @notice Generates a drip name for a chain/admin wallet drip. - /// @param _l1Bridge The address of the L1 bridge. - /// @param _version The version of the drip. - function _makeAdminWalletDripName(address _l1Bridge, uint256 _version) internal pure returns (string memory) { - string memory dripNamePrefixWithBridgeAddress = string.concat("faucet-admin-drip-", vm.toString(_l1Bridge)); - string memory versionSuffix = string.concat("-", vm.toString(_version)); - return string.concat(dripNamePrefixWithBridgeAddress, versionSuffix); - } - - /// @notice Deploys a contract using the CREATE2 opcode. - /// @param _name The name of the contract. - /// @param _creationCode The contract creation code. - /// @param _constructorParams The constructor parameters. - function _deployCreate2( - string memory _name, - bytes memory _creationCode, - bytes memory _constructorParams - ) - internal - returns (address addr_) - { - bytes32 salt = keccak256(abi.encodePacked(bytes(_name), cfg.create2DeploymentSalt())); - bytes memory initCode = abi.encodePacked(_creationCode, _constructorParams); - address preComputedAddress = vm.computeCreate2Address(salt, keccak256(initCode)); - if (preComputedAddress.code.length > 0) { - console.log("%s already deployed at %s", _name, preComputedAddress); - address savedAddress = getAddress(_name); - if (savedAddress == address(0)) { - save(_name, preComputedAddress); - } else if (savedAddress != preComputedAddress) { - revert AddressMismatch(_name, preComputedAddress, savedAddress); - } - addr_ = preComputedAddress; - } else { - assembly { - addr_ := create2(0, add(initCode, 0x20), mload(initCode), salt) - } - require(addr_ != address(0), "deployment failed"); - save(_name, addr_); - console.log("%s deployed at %s", _name, addr_); - } - } - - /// @notice Installs an auth module in the faucet. - /// @param _faucet The faucet contract. - /// @param _name The name of the auth module. - /// @param _config The configuration of the auth module. - function _installAuthModule(Faucet _faucet, string memory _name, Faucet.ModuleConfig memory _config) internal { - AdminFaucetAuthModule module = AdminFaucetAuthModule(mustGetAddress(_name)); - if (_faucet.isModuleEnabled(module)) { - console.log("%s already installed.", _name); - } else { - console.log("Installing %s", _name); - _faucet.configure(module, _config); - console.log("%s installed successfully", _name); - } - } - - /// @notice Generates the data for a Gelato task that would trigger a drip. - /// @param _drippie The drippie contract. - /// @param _name The name of the drip. - /// @return _taskData Gelato task data. - function _makeGelatoDripTaskData( - Drippie _drippie, - string memory _name - ) - internal - view - returns (GelatoTaskData memory _taskData) - { - // Get the drip interval. - uint256 dripInterval = _drippie.getDripInterval(_name); - - // Set up module types. - GelatoDataTypes.Module[] memory modules = new GelatoDataTypes.Module[](2); - modules[0] = GelatoDataTypes.Module.PROXY; - modules[1] = GelatoDataTypes.Module.TRIGGER; - - // Create arguments for the PROXY and TRIGGER modules. - bytes[] memory args = new bytes[](2); - args[0] = abi.encode(_name); - args[1] = abi.encode( - GelatoDataTypes.TriggerModuleData({ - triggerType: GelatoDataTypes.TriggerType.TIME, - triggerConfig: abi.encode(GelatoDataTypes.Time({ nextExec: 0, interval: uint128(dripInterval) })) - }) - ); - - // Create the task data. - _taskData = GelatoTaskData({ - taskCreator: msg.sender, - execAddress: address(_drippie), - execData: abi.encodeCall(Drippie.drip, (_name)), - moduleData: GelatoDataTypes.ModuleData({ modules: modules, args: args }), - feeToken: address(0) - }); - } - - /// @notice Starts a gelato drip task. - /// @param _gelato The gelato contract. - /// @param _drippie The drippie contract. - /// @param _name The name of the drip being triggered. - function _startGelatoDripTask(IGelato _gelato, Drippie _drippie, string memory _name) internal { - GelatoTaskData memory taskData = _makeGelatoDripTaskData({ _drippie: _drippie, _name: _name }); - _gelato.createTask({ - execAddress: taskData.execAddress, - execData: taskData.execData, - moduleData: taskData.moduleData, - feeToken: taskData.feeToken - }); - } - - /// @notice Pauses a gelato drip task. - /// @param _gelato The gelato contract. - /// @param _drippie The drippie contract. - /// @param _name The name of the drip being triggered. - function _pauseGelatoDripTask(IGelato _gelato, Drippie _drippie, string memory _name) internal { - GelatoTaskData memory taskData = _makeGelatoDripTaskData({ _drippie: _drippie, _name: _name }); - _gelato.cancelTask( - GelatoTaskId.getTaskId({ - taskCreator: taskData.taskCreator, - execAddress: taskData.execAddress, - execSelector: GelatoBytes.memorySliceSelector(taskData.execData), - moduleData: taskData.moduleData, - feeToken: taskData.feeToken - }) - ); - } - - /// @notice Installs a drip in the drippie contract. - /// @param _gelato The gelato contract. - /// @param _drippie The drippie contract. - /// @param _name The name of the drip. - /// @param _config The configuration of the drip. - function _installDrip( - IGelato _gelato, - Drippie _drippie, - string memory _name, - Drippie.DripConfig memory _config - ) - internal - { - if (_drippie.getDripStatus(_name) == Drippie.DripStatus.NONE) { - console.log("installing %s", _name); - _drippie.create(_name, _config); - _startGelatoDripTask(_gelato, _drippie, _name); - console.log("%s installed successfully", _name); - } else { - console.log("%s already installed", _name); - } - - // Attempt to activate the drip. - _drippie.status(_name, Drippie.DripStatus.ACTIVE); - } - - /// @notice Installs a drip that sends ETH to an address if the balance is below a threshold. - /// @param _gelato The gelato contract. - /// @param _drippie The drippie contract. - /// @param _name The name of the drip. - /// @param _target The target address. - /// @param _value The amount of ETH to send. - /// @param _interval The interval that must elapse between drips. - /// @param _threshold The balance threshold. - function _installBalanceLowDrip( - IGelato _gelato, - Drippie _drippie, - string memory _name, - address _target, - uint256 _value, - uint256 _interval, - uint256 _threshold - ) - internal - { - Drippie.DripAction[] memory actions = new Drippie.DripAction[](1); - actions[0] = Drippie.DripAction({ target: payable(_target), data: "", value: _value }); - _installDrip({ - _gelato: _gelato, - _drippie: _drippie, - _name: _name, - _config: Drippie.DripConfig({ - reentrant: false, - interval: _interval, - dripcheck: CheckBalanceLow(mustGetAddress("CheckBalanceLow")), - checkparams: abi.encode(CheckBalanceLow.Params({ target: _target, threshold: _threshold })), - actions: actions - }) - }); - } - - /// @notice Installs a drip that sends ETH through the L1StandardBridge on an interval. - /// @param _gelato The gelato contract. - /// @param _drippie The drippie contract. - /// @param _name The name of the drip. - /// @param _bridge The address of the bridge. - /// @param _target The target address. - /// @param _value The amount of ETH to send. - function _installDepositEthToDrip( - IGelato _gelato, - Drippie _drippie, - string memory _name, - address _bridge, - address _target, - uint256 _value, - uint256 _interval - ) - internal - { - Drippie.DripAction[] memory actions = new Drippie.DripAction[](1); - actions[0] = Drippie.DripAction({ - target: payable(_bridge), - data: abi.encodeCall(L1StandardBridge.depositETHTo, (_target, 200000, "")), - value: _value - }); - _installDrip({ - _gelato: _gelato, - _drippie: _drippie, - _name: _name, - _config: Drippie.DripConfig({ - reentrant: false, - interval: _interval, - dripcheck: CheckTrue(mustGetAddress("CheckTrue")), - checkparams: abi.encode(""), - actions: actions - }) - }); - } - - /// @notice Installs a drip that sends ETH to the Gelato treasury if the balance is below a - /// threshold. Balance gets deposited into the account of the recipient. - /// @param _gelato The gelato contract. - /// @param _drippie The drippie contract. - /// @param _name The name of the drip. - /// @param _treasury The address of the Gelato treasury. - /// @param _recipient The address of the recipient. - /// @param _value The amount of ETH to send. - /// @param _interval The interval that must elapse between drips. - function _installGelatoDrip( - IGelato _gelato, - Drippie _drippie, - string memory _name, - address _treasury, - address _recipient, - uint256 _value, - uint256 _interval, - uint256 _threshold - ) - internal - { - Drippie.DripAction[] memory actions = new Drippie.DripAction[](1); - actions[0] = Drippie.DripAction({ - target: payable(_treasury), - data: abi.encodeWithSignature("depositNative(address)", _recipient), - value: _value - }); - _installDrip({ - _gelato: _gelato, - _drippie: _drippie, - _name: _name, - _config: Drippie.DripConfig({ - reentrant: false, - interval: _interval, - dripcheck: CheckGelatoLow(mustGetAddress("CheckGelatoLow")), - checkparams: abi.encode( - CheckGelatoLow.Params({ recipient: _recipient, threshold: _threshold, treasury: _treasury }) - ), - actions: actions - }) - }); - } - - /// @notice Installs a drip that sends ETH to an account if one given secret is revealed and - /// another is not. Drip will stop if the second secret is revealed. - /// @param _gelato The gelato contract. - /// @param _drippie The drippie contract. - /// @param _name The name of the drip. - /// @param _delay The delay before the drip starts after the first secret is revealed. - /// @param _secretHashMustExist The hash of the secret that must exist. - /// @param _secretHashMustNotExist The hash of the secret that must not exist. - /// @param _target The target address. - /// @param _value The amount of ETH to send. - /// @param _interval The interval that must elapse between drips. - function _installSecretsDrip( - IGelato _gelato, - Drippie _drippie, - string memory _name, - uint256 _delay, - bytes32 _secretHashMustExist, - bytes32 _secretHashMustNotExist, - address _target, - uint256 _value, - uint256 _interval - ) - internal - { - Drippie.DripAction[] memory actions = new Drippie.DripAction[](1); - actions[0] = Drippie.DripAction({ target: payable(_target), data: "", value: _value }); - _installDrip({ - _gelato: _gelato, - _drippie: _drippie, - _name: _name, - _config: Drippie.DripConfig({ - reentrant: false, - interval: _interval, - dripcheck: CheckSecrets(mustGetAddress("CheckSecrets")), - checkparams: abi.encode( - CheckSecrets.Params({ - delay: _delay, - secretHashMustExist: _secretHashMustExist, - secretHashMustNotExist: _secretHashMustNotExist - }) - ), - actions: actions - }) - }); - } -} diff --git a/packages/contracts-bedrock/scripts/PeripheryDeployConfig.s.sol b/packages/contracts-bedrock/scripts/PeripheryDeployConfig.s.sol deleted file mode 100644 index 044806404e40..000000000000 --- a/packages/contracts-bedrock/scripts/PeripheryDeployConfig.s.sol +++ /dev/null @@ -1,194 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import { Script } from "forge-std/Script.sol"; -import { console2 as console } from "forge-std/console2.sol"; -import { stdJson } from "forge-std/StdJson.sol"; - -/// @title PeripheryDeployConfig -/// @notice Represents the configuration required to deploy the periphery contracts. It is expected -/// to read the file from JSON. A future improvement would be to have fallback -/// values if they are not defined in the JSON themselves. -contract PeripheryDeployConfig is Script { - string internal _json; - - // General configuration. - string public create2DeploymentSalt; - - // Configuration for Gelato. - address public gelatoAutomateContract; - address public gelatoTreasuryContract; - - // Configuration for standard operations Drippie contract. - address public operationsDrippieOwner; - address public operationsSequencerDripV1Target; - uint256 public operationsSequencerDripV1Value; - uint256 public operationsSequencerDripV1Interval; - uint256 public operationsSequencerDripV1Threshold; - address public operationsGelatoDripV1Recipient; - uint256 public operationsGelatoDripV1Value; - uint256 public operationsGelatoDripV1Interval; - uint256 public operationsGelatoDripV1Threshold; - uint256 public operationsSecretsDripV1Delay; - bytes32 public operationsSecretsDripV1MustExist; - bytes32 public operationsSecretsDripV1MustNotExist; - address public operationsSecretsDripV1Target; - uint256 public operationsSecretsDripV1Value; - uint256 public operationsSecretsDripV1Interval; - - // Configuration for the faucet Drippie contract. - address public faucetDrippieOwner; - uint256 public faucetDripV1Value; - uint256 public faucetDripV1Interval; - uint256 public faucetDripV1Threshold; - uint256 public faucetDripV2Value; - uint256 public faucetDripV2Interval; - uint256 public faucetDripV2Threshold; - uint256 public faucetAdminDripV1Value; - uint256 public faucetAdminDripV1Interval; - uint256 public faucetAdminDripV1Threshold; - address public faucetGelatoRecipient; - uint256 public faucetGelatoBalanceV1DripInterval; - uint256 public faucetGelatoBalanceV1Value; - uint256 public faucetGelatoThreshold; - uint256 public smallOpChainFaucetDripValue; - uint256 public smallOpChainFaucetDripInterval; - uint256 public largeOpChainFaucetDripValue; - uint256 public largeOpChainFaucetDripInterval; - uint256 public opChainAdminWalletDripValue; - uint256 public opChainAdminWalletDripInterval; - - // Configuration for the Faucet contract. - address public faucetAdmin; - address public faucetOnchainAuthModuleAdmin; - uint256 public faucetOnchainAuthModuleTtl; - uint256 public faucetOnchainAuthModuleAmount; - address public faucetOffchainAuthModuleAdmin; - uint256 public faucetOffchainAuthModuleTtl; - uint256 public faucetOffchainAuthModuleAmount; - - // Configuration for the L1 bridges. - address public opL1BridgeAddress; - address public baseL1BridgeAddress; - address public zoraL1BridgeAddress; - address public pgnL1BridgeAddress; - address public orderlyL1BridgeAddress; - address public modeL1BridgeAddress; - address public lyraL1BridgeAddress; - address public liskL1BridgeAddress; - address[6] public smallFaucetsL1BridgeAddresses; - address[2] public largeFaucetsL1BridgeAddresses; - - // Configuration booleans. - bool public deployDripchecks; - bool public deployFaucetContracts; - bool public deployOperationsContracts; - bool public installOpChainFaucetsDrips; - bool public archivePreviousOpChainFaucetsDrips; - - // Configuration for the drip version. - uint256 public dripVersion; - uint256 public previousDripVersion; - - constructor(string memory _path) { - console.log("PeripheryDeployConfig: reading file %s", _path); - try vm.readFile(_path) returns (string memory data) { - _json = data; - } catch { - console.log("Warning: unable to read config. Do not deploy unless you are not using config."); - return; - } - - // General configuration. - create2DeploymentSalt = stdJson.readString(_json, "$.create2DeploymentSalt"); - - // Configuration for Gelato. - gelatoAutomateContract = stdJson.readAddress(_json, "$.gelatoAutomateContract"); - gelatoTreasuryContract = stdJson.readAddress(_json, "$.gelatoTreasuryContract"); - - // Configuration for the standard operations Drippie contract. - operationsDrippieOwner = stdJson.readAddress(_json, "$.operationsDrippieOwner"); - operationsSequencerDripV1Target = stdJson.readAddress(_json, "$.operationsSequencerDripV1Target"); - operationsSequencerDripV1Value = stdJson.readUint(_json, "$.operationsSequencerDripV1Value"); - operationsSequencerDripV1Interval = stdJson.readUint(_json, "$.operationsSequencerDripV1Interval"); - operationsSequencerDripV1Threshold = stdJson.readUint(_json, "$.operationsSequencerDripV1Threshold"); - operationsGelatoDripV1Recipient = stdJson.readAddress(_json, "$.operationsGelatoDripV1Recipient"); - operationsGelatoDripV1Value = stdJson.readUint(_json, "$.operationsGelatoDripV1Value"); - operationsGelatoDripV1Interval = stdJson.readUint(_json, "$.operationsGelatoDripV1Interval"); - operationsGelatoDripV1Threshold = stdJson.readUint(_json, "$.operationsGelatoDripV1Threshold"); - operationsSecretsDripV1Delay = stdJson.readUint(_json, "$.operationsSecretsDripV1Delay"); - operationsSecretsDripV1MustExist = stdJson.readBytes32(_json, "$.operationsSecretsDripV1MustExist"); - operationsSecretsDripV1MustNotExist = stdJson.readBytes32(_json, "$.operationsSecretsDripV1MustNotExist"); - operationsSecretsDripV1Target = stdJson.readAddress(_json, "$.operationsSecretsDripV1Target"); - operationsSecretsDripV1Value = stdJson.readUint(_json, "$.operationsSecretsDripV1Value"); - operationsSecretsDripV1Interval = stdJson.readUint(_json, "$.operationsSecretsDripV1Interval"); - - // Configuration for the faucet Drippie contract. - faucetDrippieOwner = stdJson.readAddress(_json, "$.faucetDrippieOwner"); - faucetDripV1Value = stdJson.readUint(_json, "$.faucetDripV1Value"); - faucetDripV1Interval = stdJson.readUint(_json, "$.faucetDripV1Interval"); - faucetDripV1Threshold = stdJson.readUint(_json, "$.faucetDripV1Threshold"); - faucetDripV2Value = stdJson.readUint(_json, "$.faucetDripV2Value"); - faucetDripV2Interval = stdJson.readUint(_json, "$.faucetDripV2Interval"); - faucetDripV2Threshold = stdJson.readUint(_json, "$.faucetDripV2Threshold"); - faucetAdminDripV1Value = stdJson.readUint(_json, "$.faucetAdminDripV1Value"); - faucetAdminDripV1Interval = stdJson.readUint(_json, "$.faucetAdminDripV1Interval"); - faucetAdminDripV1Threshold = stdJson.readUint(_json, "$.faucetAdminDripV1Threshold"); - faucetGelatoRecipient = stdJson.readAddress(_json, "$.faucetGelatoRecipient"); - faucetGelatoBalanceV1DripInterval = stdJson.readUint(_json, "$.faucetGelatoBalanceV1DripInterval"); - faucetGelatoBalanceV1Value = stdJson.readUint(_json, "$.faucetGelatoBalanceV1Value"); - faucetGelatoThreshold = stdJson.readUint(_json, "$.faucetGelatoThreshold"); - smallOpChainFaucetDripValue = stdJson.readUint(_json, "$.smallOpChainFaucetDripValue"); - smallOpChainFaucetDripInterval = stdJson.readUint(_json, "$.smallOpChainFaucetDripInterval"); - largeOpChainFaucetDripValue = stdJson.readUint(_json, "$.largeOpChainFaucetDripValue"); - largeOpChainFaucetDripInterval = stdJson.readUint(_json, "$.largeOpChainFaucetDripInterval"); - opChainAdminWalletDripValue = stdJson.readUint(_json, "$.opChainAdminWalletDripValue"); - opChainAdminWalletDripInterval = stdJson.readUint(_json, "$.opChainAdminWalletDripInterval"); - - // Configuration for the Faucet contract. - faucetAdmin = stdJson.readAddress(_json, "$.faucetAdmin"); - faucetOnchainAuthModuleAdmin = stdJson.readAddress(_json, "$.faucetOnchainAuthModuleAdmin"); - faucetOnchainAuthModuleTtl = stdJson.readUint(_json, "$.faucetOnchainAuthModuleTtl"); - faucetOnchainAuthModuleAmount = stdJson.readUint(_json, "$.faucetOnchainAuthModuleAmount"); - faucetOffchainAuthModuleAdmin = stdJson.readAddress(_json, "$.faucetOffchainAuthModuleAdmin"); - faucetOffchainAuthModuleTtl = stdJson.readUint(_json, "$.faucetOffchainAuthModuleTtl"); - faucetOffchainAuthModuleAmount = stdJson.readUint(_json, "$.faucetOffchainAuthModuleAmount"); - - // Configuration for the L1 bridges. - opL1BridgeAddress = stdJson.readAddress(_json, "$.opL1BridgeAddress"); - baseL1BridgeAddress = stdJson.readAddress(_json, "$.baseL1BridgeAddress"); - zoraL1BridgeAddress = stdJson.readAddress(_json, "$.zoraL1BridgeAddress"); - pgnL1BridgeAddress = stdJson.readAddress(_json, "$.pgnL1BridgeAddress"); - orderlyL1BridgeAddress = stdJson.readAddress(_json, "$.orderlyL1BridgeAddress"); - liskL1BridgeAddress = stdJson.readAddress(_json, "$.liskL1BridgeAddress"); - modeL1BridgeAddress = stdJson.readAddress(_json, "$.modeL1BridgeAddress"); - lyraL1BridgeAddress = stdJson.readAddress(_json, "$.lyraL1BridgeAddress"); - largeFaucetsL1BridgeAddresses[0] = opL1BridgeAddress; - largeFaucetsL1BridgeAddresses[1] = baseL1BridgeAddress; - smallFaucetsL1BridgeAddresses[0] = zoraL1BridgeAddress; - smallFaucetsL1BridgeAddresses[1] = pgnL1BridgeAddress; - smallFaucetsL1BridgeAddresses[2] = orderlyL1BridgeAddress; - smallFaucetsL1BridgeAddresses[3] = modeL1BridgeAddress; - smallFaucetsL1BridgeAddresses[4] = lyraL1BridgeAddress; - smallFaucetsL1BridgeAddresses[5] = liskL1BridgeAddress; - - // Configuration booleans. - deployDripchecks = stdJson.readBool(_json, "$.deployDripchecks"); - deployFaucetContracts = stdJson.readBool(_json, "$.deployFaucetContracts"); - deployOperationsContracts = stdJson.readBool(_json, "$.deployOperationsContracts"); - installOpChainFaucetsDrips = stdJson.readBool(_json, "$.installOpChainFaucetsDrips"); - archivePreviousOpChainFaucetsDrips = stdJson.readBool(_json, "$.archivePreviousOpChainFaucetsDrips"); - - // Configuration for the drip version. - dripVersion = stdJson.readUint(_json, "$.dripVersion"); - previousDripVersion = stdJson.readUint(_json, "$.previousDripVersion"); - } - - function getSmallFaucetsL1BridgeAddressesCount() public view returns (uint256 count) { - return smallFaucetsL1BridgeAddresses.length; - } - - function getLargeFaucetsL1BridgeAddressesCount() public view returns (uint256 count) { - return largeFaucetsL1BridgeAddresses.length; - } -} diff --git a/packages/contracts-bedrock/scripts/periphery/deploy/DeployPeriphery.s.sol b/packages/contracts-bedrock/scripts/periphery/deploy/DeployPeriphery.s.sol new file mode 100644 index 000000000000..834af21231f1 --- /dev/null +++ b/packages/contracts-bedrock/scripts/periphery/deploy/DeployPeriphery.s.sol @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { console2 as console } from "forge-std/console2.sol"; +import { Script } from "forge-std/Script.sol"; + +import { Config } from "scripts/Config.sol"; +import { Artifacts } from "scripts/Artifacts.s.sol"; +import { PeripheryDeployConfig } from "scripts/periphery/deploy/PeripheryDeployConfig.s.sol"; + +import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; +import { Proxy } from "src/universal/Proxy.sol"; +import { L1StandardBridge } from "src/L1/L1StandardBridge.sol"; +import { Faucet } from "src/periphery/faucet/Faucet.sol"; +import { Drippie } from "src/periphery/drippie/Drippie.sol"; +import { CheckGelatoLow } from "src/periphery/drippie/dripchecks/CheckGelatoLow.sol"; +import { CheckBalanceLow } from "src/periphery/drippie/dripchecks/CheckBalanceLow.sol"; +import { CheckTrue } from "src/periphery/drippie/dripchecks/CheckTrue.sol"; +import { CheckSecrets } from "src/periphery/drippie/dripchecks/CheckSecrets.sol"; +import { AdminFaucetAuthModule } from "src/periphery/faucet/authmodules/AdminFaucetAuthModule.sol"; + +import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; + +/// @title DeployPeriphery +/// @notice Script used to deploy periphery contracts. +contract DeployPeriphery is Script, Artifacts { + /// @notice Error emitted when an address mismatch is detected. + error AddressMismatch(string, address, address); + + /// @notice Deployment configuration. + PeripheryDeployConfig cfg; + + /// @notice Sets up the deployment script. + function setUp() public override { + Artifacts.setUp(); + cfg = new PeripheryDeployConfig(Config.deployConfigPath()); + console.log("Config path: %s", Config.deployConfigPath()); + } + + /// @notice Deploy all of the periphery contracts. + function run() public { + console.log("Deploying periphery contracts"); + + // Optionally deploy the base dripcheck contracts. + if (cfg.deployDripchecks()) { + deployCheckTrue(); + deployCheckBalanceLow(); + deployCheckGelatoLow(); + deployCheckSecrets(); + } + + // Optionally deploy the faucet contracts. + if (cfg.deployFaucetContracts()) { + // Deploy faucet contracts. + deployProxyAdmin(); + deployFaucetProxy(); + deployFaucet(); + deployFaucetDrippie(); + deployOnChainAuthModule(); + deployOffChainAuthModule(); + + // Initialize the faucet. + initializeFaucet(); + installFaucetAuthModulesConfigs(); + } + + // Optionally deploy the operations contracts. + if (cfg.deployOperationsContracts()) { + deployOperationsDrippie(); + } + } + + /// @notice Modifier that wraps a function in broadcasting. + modifier broadcast() { + vm.startBroadcast(); + _; + vm.stopBroadcast(); + } + + /// @notice Deploy ProxyAdmin. + function deployProxyAdmin() public broadcast returns (address addr_) { + addr_ = _deployCreate2({ + _name: "ProxyAdmin", + _creationCode: type(ProxyAdmin).creationCode, + _constructorParams: abi.encode(msg.sender) + }); + + ProxyAdmin admin = ProxyAdmin(addr_); + require(admin.owner() == msg.sender); + } + + /// @notice Deploy FaucetProxy. + function deployFaucetProxy() public broadcast returns (address addr_) { + addr_ = _deployCreate2({ + _name: "FaucetProxy", + _creationCode: type(Proxy).creationCode, + _constructorParams: abi.encode(mustGetAddress("ProxyAdmin")) + }); + + Proxy proxy = Proxy(payable(addr_)); + require(EIP1967Helper.getAdmin(address(proxy)) == mustGetAddress("ProxyAdmin")); + } + + /// @notice Deploy the Faucet contract. + function deployFaucet() public broadcast returns (address addr_) { + addr_ = _deployCreate2({ + _name: "Faucet", + _creationCode: type(Faucet).creationCode, + _constructorParams: abi.encode(cfg.faucetAdmin()) + }); + + Faucet faucet = Faucet(payable(addr_)); + require(faucet.ADMIN() == cfg.faucetAdmin()); + } + + /// @notice Deploy the Drippie contract. + function deployFaucetDrippie() public broadcast returns (address addr_) { + addr_ = _deployCreate2({ + _name: "FaucetDrippie", + _creationCode: type(Drippie).creationCode, + _constructorParams: abi.encode(cfg.faucetDrippieOwner()) + }); + + Drippie drippie = Drippie(payable(addr_)); + require(drippie.owner() == cfg.faucetDrippieOwner()); + } + + /// @notice Deploy the Drippie contract for standard operations. + function deployOperationsDrippie() public broadcast returns (address addr_) { + addr_ = _deployCreate2({ + _name: "OperationsDrippie", + _creationCode: type(Drippie).creationCode, + _constructorParams: abi.encode(cfg.operationsDrippieOwner()) + }); + + Drippie drippie = Drippie(payable(addr_)); + require(drippie.owner() == cfg.operationsDrippieOwner()); + } + + /// @notice Deploy On-Chain Authentication Module. + function deployOnChainAuthModule() public broadcast returns (address addr_) { + addr_ = _deployCreate2({ + _name: "OnChainAuthModule", + _creationCode: type(AdminFaucetAuthModule).creationCode, + _constructorParams: abi.encode(cfg.faucetOnchainAuthModuleAdmin(), "OnChainAuthModule", "1") + }); + + AdminFaucetAuthModule module = AdminFaucetAuthModule(addr_); + require(module.ADMIN() == cfg.faucetOnchainAuthModuleAdmin()); + } + + /// @notice Deploy Off-Chain Authentication Module. + function deployOffChainAuthModule() public broadcast returns (address addr_) { + addr_ = _deployCreate2({ + _name: "OffChainAuthModule", + _creationCode: type(AdminFaucetAuthModule).creationCode, + _constructorParams: abi.encode(cfg.faucetOffchainAuthModuleAdmin(), "OffChainAuthModule", "1") + }); + + AdminFaucetAuthModule module = AdminFaucetAuthModule(addr_); + require(module.ADMIN() == cfg.faucetOffchainAuthModuleAdmin()); + } + + /// @notice Deploy CheckTrue contract. + function deployCheckTrue() public broadcast returns (address addr_) { + addr_ = _deployCreate2({ + _name: "CheckTrue", + _creationCode: type(CheckTrue).creationCode, + _constructorParams: hex"" + }); + } + + /// @notice Deploy CheckBalanceLow contract. + function deployCheckBalanceLow() public broadcast returns (address addr_) { + addr_ = _deployCreate2({ + _name: "CheckBalanceLow", + _creationCode: type(CheckBalanceLow).creationCode, + _constructorParams: hex"" + }); + } + + /// @notice Deploy CheckGelatoLow contract. + function deployCheckGelatoLow() public broadcast returns (address addr_) { + addr_ = _deployCreate2({ + _name: "CheckGelatoLow", + _creationCode: type(CheckGelatoLow).creationCode, + _constructorParams: hex"" + }); + } + + /// @notice Deploy CheckSecrets contract. + function deployCheckSecrets() public broadcast returns (address addr_) { + addr_ = _deployCreate2({ + _name: "CheckSecrets", + _creationCode: type(CheckSecrets).creationCode, + _constructorParams: hex"" + }); + } + + /// @notice Initialize the Faucet. + function initializeFaucet() public broadcast { + ProxyAdmin proxyAdmin = ProxyAdmin(mustGetAddress("ProxyAdmin")); + address faucetProxy = mustGetAddress("FaucetProxy"); + address faucet = mustGetAddress("Faucet"); + address implementationAddress = proxyAdmin.getProxyImplementation(faucetProxy); + if (implementationAddress == faucet) { + console.log("Faucet proxy implementation already set"); + } else { + proxyAdmin.upgrade({ _proxy: payable(faucetProxy), _implementation: faucet }); + } + + require(Faucet(payable(faucetProxy)).ADMIN() == Faucet(payable(faucet)).ADMIN()); + } + + /// @notice Installs the OnChain AuthModule on the Faucet contract. + function installOnChainAuthModule() public broadcast { + _installAuthModule({ + _faucet: Faucet(mustGetAddress("FaucetProxy")), + _name: "OnChainAuthModule", + _config: Faucet.ModuleConfig({ + name: "OnChainAuthModule", + enabled: true, + ttl: cfg.faucetOnchainAuthModuleTtl(), + amount: cfg.faucetOnchainAuthModuleAmount() + }) + }); + } + + /// @notice Installs the OffChain AuthModule on the Faucet contract. + function installOffChainAuthModule() public broadcast { + _installAuthModule({ + _faucet: Faucet(mustGetAddress("FaucetProxy")), + _name: "OffChainAuthModule", + _config: Faucet.ModuleConfig({ + name: "OffChainAuthModule", + enabled: true, + ttl: cfg.faucetOffchainAuthModuleTtl(), + amount: cfg.faucetOffchainAuthModuleAmount() + }) + }); + } + + /// @notice Installs all of the auth modules in the faucet contract. + function installFaucetAuthModulesConfigs() public { + Faucet faucet = Faucet(mustGetAddress("FaucetProxy")); + console.log("Installing auth modules at %s", address(faucet)); + installOnChainAuthModule(); + installOffChainAuthModule(); + console.log("Faucet Auth Module configs successfully installed"); + } + + /// @notice Deploys a contract using the CREATE2 opcode. + /// @param _name The name of the contract. + /// @param _creationCode The contract creation code. + /// @param _constructorParams The constructor parameters. + function _deployCreate2( + string memory _name, + bytes memory _creationCode, + bytes memory _constructorParams + ) + internal + returns (address addr_) + { + bytes32 salt = keccak256(abi.encodePacked(bytes(_name), cfg.create2DeploymentSalt())); + bytes memory initCode = abi.encodePacked(_creationCode, _constructorParams); + address preComputedAddress = vm.computeCreate2Address(salt, keccak256(initCode)); + if (preComputedAddress.code.length > 0) { + console.log("%s already deployed at %s", _name, preComputedAddress); + address savedAddress = getAddress(_name); + if (savedAddress == address(0)) { + save(_name, preComputedAddress); + } else if (savedAddress != preComputedAddress) { + revert AddressMismatch(_name, preComputedAddress, savedAddress); + } + addr_ = preComputedAddress; + } else { + assembly { + addr_ := create2(0, add(initCode, 0x20), mload(initCode), salt) + } + require(addr_ != address(0), "deployment failed"); + save(_name, addr_); + console.log("%s deployed at %s", _name, addr_); + } + } + + /// @notice Installs an auth module in the faucet. + /// @param _faucet The faucet contract. + /// @param _name The name of the auth module. + /// @param _config The configuration of the auth module. + function _installAuthModule(Faucet _faucet, string memory _name, Faucet.ModuleConfig memory _config) internal { + AdminFaucetAuthModule module = AdminFaucetAuthModule(mustGetAddress(_name)); + if (_faucet.isModuleEnabled(module)) { + console.log("%s already installed.", _name); + } else { + console.log("Installing %s", _name); + _faucet.configure(module, _config); + console.log("%s installed successfully", _name); + } + } +} diff --git a/packages/contracts-bedrock/scripts/periphery/deploy/PeripheryDeployConfig.s.sol b/packages/contracts-bedrock/scripts/periphery/deploy/PeripheryDeployConfig.s.sol new file mode 100644 index 000000000000..a4a6e2bfa379 --- /dev/null +++ b/packages/contracts-bedrock/scripts/periphery/deploy/PeripheryDeployConfig.s.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { Script } from "forge-std/Script.sol"; +import { console2 as console } from "forge-std/console2.sol"; +import { stdJson } from "forge-std/StdJson.sol"; + +/// @title PeripheryDeployConfig +/// @notice Represents the configuration required to deploy the periphery contracts. It is expected +/// to read the file from JSON. A future improvement would be to have fallback +/// values if they are not defined in the JSON themselves. +contract PeripheryDeployConfig is Script { + string internal _json; + + // General configuration. + string public create2DeploymentSalt; + + // Configuration for Gelato. + address public gelatoAutomateContract; + + // Configuration for standard operations Drippie contract. + address public operationsDrippieOwner; + + // Configuration for the faucet Drippie contract. + address public faucetDrippieOwner; + + // Configuration for the Faucet contract. + address public faucetAdmin; + address public faucetOnchainAuthModuleAdmin; + uint256 public faucetOnchainAuthModuleTtl; + uint256 public faucetOnchainAuthModuleAmount; + address public faucetOffchainAuthModuleAdmin; + uint256 public faucetOffchainAuthModuleTtl; + uint256 public faucetOffchainAuthModuleAmount; + + // Configuration booleans. + bool public deployDripchecks; + bool public deployFaucetContracts; + bool public deployOperationsContracts; + + constructor(string memory _path) { + console.log("PeripheryDeployConfig: reading file %s", _path); + try vm.readFile(_path) returns (string memory data) { + _json = data; + } catch { + console.log("Warning: unable to read config. Do not deploy unless you are not using config."); + return; + } + + // General configuration. + create2DeploymentSalt = stdJson.readString(_json, "$.create2DeploymentSalt"); + + // Configuration for Gelato. + gelatoAutomateContract = stdJson.readAddress(_json, "$.gelatoAutomateContract"); + + // Configuration for the standard operations Drippie contract. + operationsDrippieOwner = stdJson.readAddress(_json, "$.operationsDrippieOwner"); + + // Configuration for the faucet Drippie contract. + faucetDrippieOwner = stdJson.readAddress(_json, "$.faucetDrippieOwner"); + + // Configuration for the Faucet contract. + faucetAdmin = stdJson.readAddress(_json, "$.faucetAdmin"); + faucetOnchainAuthModuleAdmin = stdJson.readAddress(_json, "$.faucetOnchainAuthModuleAdmin"); + faucetOnchainAuthModuleTtl = stdJson.readUint(_json, "$.faucetOnchainAuthModuleTtl"); + faucetOnchainAuthModuleAmount = stdJson.readUint(_json, "$.faucetOnchainAuthModuleAmount"); + faucetOffchainAuthModuleAdmin = stdJson.readAddress(_json, "$.faucetOffchainAuthModuleAdmin"); + faucetOffchainAuthModuleTtl = stdJson.readUint(_json, "$.faucetOffchainAuthModuleTtl"); + faucetOffchainAuthModuleAmount = stdJson.readUint(_json, "$.faucetOffchainAuthModuleAmount"); + + // Configuration booleans. + deployDripchecks = stdJson.readBool(_json, "$.deployDripchecks"); + deployFaucetContracts = stdJson.readBool(_json, "$.deployFaucetContracts"); + deployOperationsContracts = stdJson.readBool(_json, "$.deployOperationsContracts"); + } +} diff --git a/packages/contracts-bedrock/scripts/periphery/drippie/DrippieConfig.s.sol b/packages/contracts-bedrock/scripts/periphery/drippie/DrippieConfig.s.sol new file mode 100644 index 000000000000..32b1310d42c1 --- /dev/null +++ b/packages/contracts-bedrock/scripts/periphery/drippie/DrippieConfig.s.sol @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { Script } from "forge-std/Script.sol"; +import { console2 as console } from "forge-std/console2.sol"; +import { stdJson } from "forge-std/StdJson.sol"; + +import { IAutomate as IGelato } from "gelato/interfaces/IAutomate.sol"; + +import { Artifacts } from "scripts/Artifacts.s.sol"; + +import { Drippie } from "src/periphery/drippie/Drippie.sol"; +import { CheckBalanceLow } from "src/periphery/drippie/dripchecks/CheckBalanceLow.sol"; +import { CheckGelatoLow } from "src/periphery/drippie/dripchecks/CheckGelatoLow.sol"; +import { CheckSecrets } from "src/periphery/drippie/dripchecks/CheckSecrets.sol"; + +/// @title DrippieConfig +/// @notice Loads Drippie configuration from a JSON file. +contract DrippieConfig is Script, Artifacts { + /// @notice Error emitted when an unknown drip check is encountered. + error UnknownDripCheck(string name); + + /// @notice Drip configuration with only name and dripcheck. + struct CoreDripConfig { + string name; + string dripcheck; + } + + /// @notice Full drip configuration. + struct FullDripConfig { + string name; + string dripcheck; + bytes checkparams; + address recipient; + uint256 value; + uint256 interval; + bytes data; + } + + /// @notice JSON configuration file represented as string. + string internal _json; + + /// @notice Drippie contract. + Drippie public drippie; + + /// @notice Gelato automation contract. + IGelato public gelato; + + /// @notice Drip configuration array. + FullDripConfig[] public drips; + + /// @param _path Path to the configuration file. + constructor(string memory _path) { + // Make sure artifacts are set up. + Artifacts.setUp(); + + // Load the configuration file. + console.log("DrippieConfig: reading file %s", _path); + try vm.readFile(_path) returns (string memory data) { + _json = data; + } catch { + console.log("WARNING: unable to read config, do not deploy unless you are not using config"); + return; + } + + // Load the Drippie contract address. + drippie = Drippie(payable(stdJson.readAddress(_json, "$.drippie"))); + + // Load the Gelato contract address. + gelato = IGelato(stdJson.readAddress(_json, "$.gelato")); + + // Determine the number of drips. + // In an ideal world we'd be able to load this array in one go by parsing it as an array + // of structs that include the checkparams as bytes. Unfortunately, Foundry parses the + // checkparams as a tuple which can't be parsed in a generic way (since Solidity does not + // support generics). As a result, we first parse the array as a simplified struct that + // only includes the first two fields so that we can determine the number of drips. We then + // iterate over the array and parse the full struct for each drip somewhat manually. + CoreDripConfig[] memory corecfg = abi.decode(stdJson.parseRaw(_json, "$.drips"), (CoreDripConfig[])); + console.log("DrippieConfig: found %d drips", corecfg.length); + + // Iterate and parse all of the drips. + for (uint256 i = 0; i < corecfg.length; i++) { + // Log so we know what's being loaded. + string memory name = corecfg[i].name; + console.log("DrippieConfig: attempting to load config for %s", name); + + // Make sure the dripcheck is deployed. + string memory dripcheck = corecfg[i].dripcheck; + console.log("DrippieConfig: attempting to get address for %s", dripcheck); + mustGetAddress(dripcheck); + + // Generate the base JSON path string. + string memory p = string.concat("$.drips[", vm.toString(i), "]"); + + // Load the checkparams as bytes. + bytes memory checkparams = stdJson.parseRaw(_json, string.concat(p, ".02__checkparams")); + + // Determine if the parameters are decodable. + console.log("DrippieConfig: attempting to decode check parameters for %s", name); + if (strcmp(dripcheck, "CheckBalanceLow")) { + abi.decode(checkparams, (CheckBalanceLow.Params)); + } else if (strcmp(dripcheck, "CheckGelatoLow")) { + abi.decode(checkparams, (CheckGelatoLow.Params)); + } else if (strcmp(dripcheck, "CheckSecrets")) { + abi.decode(checkparams, (CheckSecrets.Params)); + } else if (strcmp(dripcheck, "CheckTrue")) { + // No parameters to decode. + } else { + console.log("ERROR: unknown drip configuration %s", dripcheck); + revert UnknownDripCheck(dripcheck); + } + + // Parse all the easy stuff first. + console.log("DrippieConfig: attempting to load core configuration for %s", name); + FullDripConfig memory dripcfg = FullDripConfig({ + name: name, + dripcheck: dripcheck, + checkparams: checkparams, + recipient: stdJson.readAddress(_json, string.concat(p, ".03__recipient")), + value: stdJson.readUint(_json, string.concat(p, ".04__value")), + interval: stdJson.readUint(_json, string.concat(p, ".05__interval")), + data: stdJson.parseRaw(_json, string.concat(p, ".06__data")) + }); + + // Ok we're good to go. + drips.push(dripcfg); + } + } + + /// @notice Returns the number of drips in the configuration. + function dripsLength() public view returns (uint256) { + return drips.length; + } + + /// @notice Returns the drip configuration at the given index as ABI-encoded bytes. + function drip(uint256 _index) public view returns (bytes memory) { + return abi.encode(drips[_index]); + } + + /// @notice Check if two strings are equal. + /// @param _a First string. + /// @param _b Second string. + /// @return True if the strings are equal, false otherwise. + function strcmp(string memory _a, string memory _b) internal pure returns (bool) { + return keccak256(bytes(_a)) == keccak256(bytes(_b)); + } +} diff --git a/packages/contracts-bedrock/scripts/periphery/drippie/ManageDrippie.s.sol b/packages/contracts-bedrock/scripts/periphery/drippie/ManageDrippie.s.sol new file mode 100644 index 000000000000..dee96d689ffb --- /dev/null +++ b/packages/contracts-bedrock/scripts/periphery/drippie/ManageDrippie.s.sol @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { console2 as console } from "forge-std/console2.sol"; +import { Script } from "forge-std/Script.sol"; + +import { IAutomate as IGelato } from "gelato/interfaces/IAutomate.sol"; +import { LibDataTypes as GelatoDataTypes } from "gelato/libraries/LibDataTypes.sol"; +import { LibTaskId as GelatoTaskId } from "gelato/libraries/LibTaskId.sol"; +import { GelatoBytes } from "gelato/vendor/gelato/GelatoBytes.sol"; + +import { Config } from "scripts/Config.sol"; +import { Artifacts } from "scripts/Artifacts.s.sol"; +import { DrippieConfig } from "scripts/periphery/drippie/DrippieConfig.s.sol"; + +import { Drippie } from "src/periphery/drippie/Drippie.sol"; +import { IDripCheck } from "src/periphery/drippie/IDripCheck.sol"; + +/// @title ManageDrippie +/// @notice Script for managing drips in the Drippie contract. +contract ManageDrippie is Script, Artifacts { + /// @notice Struct that contains the data for a Gelato task. + struct GelatoTaskData { + address taskCreator; + address execAddress; + bytes execData; + GelatoDataTypes.ModuleData moduleData; + address feeToken; + } + + /// @notice Drippie configuration. + DrippieConfig public cfg; + + /// @notice Modifier that wraps a function in broadcasting. + modifier broadcast() { + vm.startBroadcast(msg.sender); + _; + vm.stopBroadcast(); + } + + /// @notice Sets up the deployment script. + function setUp() public override { + Artifacts.setUp(); + cfg = new DrippieConfig(Config.deployConfigPath()); + console.log("Config path: %s", Config.deployConfigPath()); + } + + /// @notice Runs the management script. + function run() public { + console.log("ManageDrippie: running"); + installDrips(); + } + + /// @notice Installs drips in the drippie contract. + function installDrips() public broadcast { + console.log("ManageDrippie: installing Drippie config"); + for (uint256 i = 0; i < cfg.dripsLength(); i++) { + DrippieConfig.FullDripConfig memory drip = abi.decode(cfg.drip(i), (DrippieConfig.FullDripConfig)); + Drippie.DripAction[] memory actions = new Drippie.DripAction[](1); + actions[0] = Drippie.DripAction({ target: payable(drip.recipient), data: drip.data, value: drip.value }); + _installDrip({ + _gelato: cfg.gelato(), + _drippie: cfg.drippie(), + _name: drip.name, + _config: Drippie.DripConfig({ + reentrant: false, + interval: drip.interval, + dripcheck: IDripCheck(mustGetAddress(drip.dripcheck)), + checkparams: drip.checkparams, + actions: actions + }) + }); + } + } + + /// @notice Generates the data for a Gelato task that would trigger a drip. + /// @param _drippie The drippie contract. + /// @param _name The name of the drip. + /// @return _taskData Gelato task data. + function _makeGelatoDripTaskData( + Drippie _drippie, + string memory _name + ) + internal + view + returns (GelatoTaskData memory _taskData) + { + // Get the drip interval. + uint256 dripInterval = _drippie.getDripInterval(_name); + + // Set up module types. + GelatoDataTypes.Module[] memory modules = new GelatoDataTypes.Module[](2); + modules[0] = GelatoDataTypes.Module.PROXY; + modules[1] = GelatoDataTypes.Module.TRIGGER; + + // Create arguments for the PROXY and TRIGGER modules. + bytes[] memory args = new bytes[](2); + args[0] = abi.encode(_name); + args[1] = abi.encode( + GelatoDataTypes.TriggerModuleData({ + triggerType: GelatoDataTypes.TriggerType.TIME, + triggerConfig: abi.encode(GelatoDataTypes.Time({ nextExec: 0, interval: uint128(dripInterval) })) + }) + ); + + // Create the task data. + _taskData = GelatoTaskData({ + taskCreator: msg.sender, + execAddress: address(_drippie), + execData: abi.encodeCall(Drippie.drip, (_name)), + moduleData: GelatoDataTypes.ModuleData({ modules: modules, args: args }), + feeToken: address(0) + }); + } + + /// @notice Starts a gelato drip task. + /// @param _gelato The gelato contract. + /// @param _drippie The drippie contract. + /// @param _name The name of the drip being triggered. + function _startGelatoDripTask(IGelato _gelato, Drippie _drippie, string memory _name) internal { + GelatoTaskData memory taskData = _makeGelatoDripTaskData({ _drippie: _drippie, _name: _name }); + _gelato.createTask({ + execAddress: taskData.execAddress, + execData: taskData.execData, + moduleData: taskData.moduleData, + feeToken: taskData.feeToken + }); + } + + /// @notice Pauses a gelato drip task. + /// @param _gelato The gelato contract. + /// @param _drippie The drippie contract. + /// @param _name The name of the drip being triggered. + function _pauseGelatoDripTask(IGelato _gelato, Drippie _drippie, string memory _name) internal { + GelatoTaskData memory taskData = _makeGelatoDripTaskData({ _drippie: _drippie, _name: _name }); + _gelato.cancelTask( + GelatoTaskId.getTaskId({ + taskCreator: taskData.taskCreator, + execAddress: taskData.execAddress, + execSelector: GelatoBytes.memorySliceSelector(taskData.execData), + moduleData: taskData.moduleData, + feeToken: taskData.feeToken + }) + ); + } + + /// @notice Installs a drip in the drippie contract. + /// @param _gelato The gelato contract. + /// @param _drippie The drippie contract. + /// @param _name The name of the drip. + /// @param _config The configuration of the drip. + function _installDrip( + IGelato _gelato, + Drippie _drippie, + string memory _name, + Drippie.DripConfig memory _config + ) + internal + { + if (_drippie.getDripStatus(_name) == Drippie.DripStatus.NONE) { + console.log("installing %s", _name); + _drippie.create(_name, _config); + _startGelatoDripTask(_gelato, _drippie, _name); + console.log("%s installed successfully", _name); + } else { + console.log("%s already installed", _name); + } + + // Grab the status again now that we've installed the drip. + Drippie.DripStatus status = _drippie.getDripStatus(_name); + if (status == Drippie.DripStatus.PAUSED) { + console.log("activating %s", _name); + _drippie.status(_name, Drippie.DripStatus.ACTIVE); + console.log("%s activated successfully", _name); + } else if (status == Drippie.DripStatus.ACTIVE) { + console.log("%s already active", _name); + } else { + // TODO: Better way to handle this? + console.log("WARNING: % could not be activated", _name); + } + } +}