diff --git a/contracts/scripts/foundry/DeployL1ScrollOwner.s.sol b/contracts/scripts/foundry/DeployL1ScrollOwner.s.sol new file mode 100644 index 0000000000..a2f5e724ce --- /dev/null +++ b/contracts/scripts/foundry/DeployL1ScrollOwner.s.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.10; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; + +import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol"; + +import {ScrollOwner} from "../../src/misc/ScrollOwner.sol"; + +// solhint-disable state-visibility +// solhint-disable var-name-mixedcase + +contract DeployL1ScrollOwner is Script { + string NETWORK = vm.envString("NETWORK"); + + uint256 L1_DEPLOYER_PRIVATE_KEY = vm.envUint("L1_DEPLOYER_PRIVATE_KEY"); + + address SCROLL_MULTISIG_ADDR = vm.envAddress("L1_SCROLL_MULTISIG_ADDR"); + + address SECURITY_COUNCIL_ADDR = vm.envAddress("L1_SECURITY_COUNCIL_ADDR"); + + address L1_PROPOSAL_EXECUTOR_ADDR = vm.envAddress("L1_PROPOSAL_EXECUTOR_ADDR"); + + function run() external { + vm.startBroadcast(L1_DEPLOYER_PRIVATE_KEY); + + deployScrollOwner(); + + if (keccak256(abi.encodePacked(NETWORK)) == keccak256(abi.encodePacked("sepolia"))) { + // for sepolia + deployTimelockController("1D", 1 minutes); + deployTimelockController("7D", 7 minutes); + deployTimelockController("14D", 14 minutes); + } else if (keccak256(abi.encodePacked(NETWORK)) == keccak256(abi.encodePacked("mainnet"))) { + // for mainnet + deployTimelockController("1D", 1 days); + deployTimelockController("7D", 7 days); + deployTimelockController("14D", 14 days); + } + + vm.stopBroadcast(); + } + + function deployScrollOwner() internal { + ScrollOwner owner = new ScrollOwner(); + + logAddress("L1_SCROLL_OWNER_ADDR", address(owner)); + } + + function deployTimelockController(string memory label, uint256 delay) internal { + address[] memory proposers = new address[](1); + address[] memory executors = new address[](1); + + proposers[0] = SCROLL_MULTISIG_ADDR; + executors[0] = L1_PROPOSAL_EXECUTOR_ADDR; + + TimelockController timelock = new TimelockController(delay, proposers, executors, SECURITY_COUNCIL_ADDR); + + logAddress(string(abi.encodePacked("L1_", label, "_TIMELOCK_ADDR")), address(timelock)); + } + + function logAddress(string memory name, address addr) internal view { + console.log(string(abi.encodePacked(name, "=", vm.toString(address(addr))))); + } +} diff --git a/contracts/scripts/foundry/DeployL2RateLimiter.s.sol b/contracts/scripts/foundry/DeployL2RateLimiter.s.sol new file mode 100644 index 0000000000..d1f500c053 --- /dev/null +++ b/contracts/scripts/foundry/DeployL2RateLimiter.s.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.10; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; + +import {ScrollGatewayBase} from "../../src/libraries/gateway/ScrollGatewayBase.sol"; +import {ScrollMessengerBase} from "../../src/libraries/ScrollMessengerBase.sol"; + +import {ETHRateLimiter} from "../../src/rate-limiter/ETHRateLimiter.sol"; +import {TokenRateLimiter} from "../../src/rate-limiter/TokenRateLimiter.sol"; + +contract DeployL2RateLimiter is Script { + uint256 L2_DEPLOYER_PRIVATE_KEY = vm.envUint("L2_DEPLOYER_PRIVATE_KEY"); + + address L2_SCROLL_MESSENGER_PROXY_ADDR = vm.envAddress("L2_SCROLL_MESSENGER_PROXY_ADDR"); + + uint256 RATE_LIMITER_PERIOD_LENGTH = vm.envUint("RATE_LIMITER_PERIOD_LENGTH"); + uint104 ETH_TOTAL_LIMIT = uint104(vm.envUint("ETH_TOTAL_LIMIT")); + + function run() external { + vm.startBroadcast(L2_DEPLOYER_PRIVATE_KEY); + + deployETHRateLimiter(); + deployTokenRateLimiter(); + + vm.stopBroadcast(); + } + + function deployETHRateLimiter() internal { + ETHRateLimiter limiter = new ETHRateLimiter( + RATE_LIMITER_PERIOD_LENGTH, + L2_SCROLL_MESSENGER_PROXY_ADDR, + ETH_TOTAL_LIMIT + ); + + logAddress("L2_ETH_RATE_LIMITER_ADDR", address(limiter)); + } + + function deployTokenRateLimiter() internal { + TokenRateLimiter limiter = new TokenRateLimiter(RATE_LIMITER_PERIOD_LENGTH); + + logAddress("L2_TOKEN_RATE_LIMITER_ADDR", address(limiter)); + } + + function logAddress(string memory name, address addr) internal view { + console.log(string(abi.encodePacked(name, "=", vm.toString(address(addr))))); + } +} diff --git a/contracts/scripts/foundry/DeployL2ScrollOwner.s.sol b/contracts/scripts/foundry/DeployL2ScrollOwner.s.sol new file mode 100644 index 0000000000..11380ae5aa --- /dev/null +++ b/contracts/scripts/foundry/DeployL2ScrollOwner.s.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.10; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; + +import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol"; + +import {ScrollOwner} from "../../src/misc/ScrollOwner.sol"; + +// solhint-disable state-visibility +// solhint-disable var-name-mixedcase + +contract DeployL2ScrollOwner is Script { + string NETWORK = vm.envString("NETWORK"); + + uint256 L2_DEPLOYER_PRIVATE_KEY = vm.envUint("L2_DEPLOYER_PRIVATE_KEY"); + + address SCROLL_MULTISIG_ADDR = vm.envAddress("L2_SCROLL_MULTISIG_ADDR"); + + address SECURITY_COUNCIL_ADDR = vm.envAddress("L2_SECURITY_COUNCIL_ADDR"); + + address L2_PROPOSAL_EXECUTOR_ADDR = vm.envAddress("L2_PROPOSAL_EXECUTOR_ADDR"); + + function run() external { + vm.startBroadcast(L2_DEPLOYER_PRIVATE_KEY); + + deployScrollOwner(); + + if (keccak256(abi.encodePacked(NETWORK)) == keccak256(abi.encodePacked("sepolia"))) { + // for sepolia + deployTimelockController("1D", 1 minutes); + deployTimelockController("7D", 7 minutes); + deployTimelockController("14D", 14 minutes); + } else if (keccak256(abi.encodePacked(NETWORK)) == keccak256(abi.encodePacked("mainnet"))) { + // for mainnet + deployTimelockController("1D", 1 days); + deployTimelockController("7D", 7 days); + deployTimelockController("14D", 14 days); + } + + vm.stopBroadcast(); + } + + function deployScrollOwner() internal { + ScrollOwner owner = new ScrollOwner(); + + logAddress("L2_SCROLL_OWNER_ADDR", address(owner)); + } + + function deployTimelockController(string memory label, uint256 delay) internal { + address[] memory proposers = new address[](1); + address[] memory executors = new address[](1); + + proposers[0] = SCROLL_MULTISIG_ADDR; + executors[0] = L2_PROPOSAL_EXECUTOR_ADDR; + + TimelockController timelock = new TimelockController(delay, proposers, executors, SECURITY_COUNCIL_ADDR); + + logAddress(string(abi.encodePacked("L2_", label, "_TIMELOCK_ADDR")), address(timelock)); + } + + function logAddress(string memory name, address addr) internal view { + console.log(string(abi.encodePacked(name, "=", vm.toString(address(addr))))); + } +} diff --git a/contracts/scripts/foundry/InitializeL1ScrollOwner.s.sol b/contracts/scripts/foundry/InitializeL1ScrollOwner.s.sol new file mode 100644 index 0000000000..8dfb4724f4 --- /dev/null +++ b/contracts/scripts/foundry/InitializeL1ScrollOwner.s.sol @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.10; + +import {Script} from "forge-std/Script.sol"; + +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; + +import {L1ScrollMessenger} from "../../src/L1/L1ScrollMessenger.sol"; +import {L1USDCGateway} from "../../src/L1/gateways/usdc/L1USDCGateway.sol"; +import {EnforcedTxGateway} from "../../src/L1/gateways/EnforcedTxGateway.sol"; +import {L1CustomERC20Gateway} from "../../src/L1/gateways/L1CustomERC20Gateway.sol"; +import {L1ERC1155Gateway} from "../../src/L1/gateways/L1ERC1155Gateway.sol"; +import {L1ERC721Gateway} from "../../src/L1/gateways/L1ERC721Gateway.sol"; +import {L1GatewayRouter} from "../../src/L1/gateways/L1GatewayRouter.sol"; +import {L1MessageQueue} from "../../src/L1/rollup/L1MessageQueue.sol"; +import {ScrollMessengerBase} from "../../src/libraries/ScrollMessengerBase.sol"; +import {L2GasPriceOracle} from "../../src/L1/rollup/L2GasPriceOracle.sol"; +import {MultipleVersionRollupVerifier} from "../../src/L1/rollup/MultipleVersionRollupVerifier.sol"; +import {ScrollChain} from "../../src/L1/rollup/ScrollChain.sol"; +import {ScrollOwner} from "../../src/misc/ScrollOwner.sol"; +import {Whitelist} from "../../src/L2/predeploys/Whitelist.sol"; + +// solhint-disable max-states-count +// solhint-disable state-visibility +// solhint-disable var-name-mixedcase + +contract InitializeL1ScrollOwner is Script { + uint256 L1_DEPLOYER_PRIVATE_KEY = vm.envUint("L1_DEPLOYER_PRIVATE_KEY"); + + bytes32 constant SECURITY_COUNCIL_NO_DELAY_ROLE = keccak256("SECURITY_COUNCIL_NO_DELAY_ROLE"); + bytes32 constant SCROLL_MULTISIG_NO_DELAY_ROLE = keccak256("SCROLL_MULTISIG_NO_DELAY_ROLE"); + + bytes32 constant TIMELOCK_1DAY_DELAY_ROLE = keccak256("TIMELOCK_1DAY_DELAY_ROLE"); + bytes32 constant TIMELOCK_7DAY_DELAY_ROLE = keccak256("TIMELOCK_7DAY_DELAY_ROLE"); + + address SCROLL_MULTISIG_ADDR = vm.envAddress("L1_SCROLL_MULTISIG_ADDR"); + address SECURITY_COUNCIL_ADDR = vm.envAddress("L1_SECURITY_COUNCIL_ADDR"); + + address L1_SCROLL_OWNER_ADDR = vm.envAddress("L1_SCROLL_OWNER_ADDR"); + address L1_1D_TIMELOCK_ADDR = vm.envAddress("L1_1D_TIMELOCK_ADDR"); + address L1_7D_TIMELOCK_ADDR = vm.envAddress("L1_7D_TIMELOCK_ADDR"); + address L1_14D_TIMELOCK_ADDR = vm.envAddress("L1_14D_TIMELOCK_ADDR"); + + address L1_PROXY_ADMIN_ADDR = vm.envAddress("L1_PROXY_ADMIN_ADDR"); + address L1_SCROLL_CHAIN_PROXY_ADDR = vm.envAddress("L1_SCROLL_CHAIN_PROXY_ADDR"); + address L1_MESSAGE_QUEUE_PROXY_ADDR = vm.envAddress("L1_MESSAGE_QUEUE_PROXY_ADDR"); + address L2_GAS_PRICE_ORACLE_PROXY_ADDR = vm.envAddress("L2_GAS_PRICE_ORACLE_PROXY_ADDR"); + address L1_SCROLL_MESSENGER_PROXY_ADDR = vm.envAddress("L1_SCROLL_MESSENGER_PROXY_ADDR"); + address L1_GATEWAY_ROUTER_PROXY_ADDR = vm.envAddress("L1_GATEWAY_ROUTER_PROXY_ADDR"); + address L1_CUSTOM_ERC20_GATEWAY_PROXY_ADDR = vm.envAddress("L1_CUSTOM_ERC20_GATEWAY_PROXY_ADDR"); + address L1_ETH_GATEWAY_PROXY_ADDR = vm.envAddress("L1_ETH_GATEWAY_PROXY_ADDR"); + address L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR = vm.envAddress("L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR"); + // address L1_USDC_GATEWAY_PROXY_ADDR = vm.envAddress("L1_USDC_GATEWAY_PROXY_ADDR"); + address L1_WETH_GATEWAY_PROXY_ADDR = vm.envAddress("L1_WETH_GATEWAY_PROXY_ADDR"); + address L1_ERC721_GATEWAY_PROXY_ADDR = vm.envAddress("L1_ERC721_GATEWAY_PROXY_ADDR"); + address L1_ERC1155_GATEWAY_PROXY_ADDR = vm.envAddress("L1_ERC1155_GATEWAY_PROXY_ADDR"); + address L1_MULTIPLE_VERSION_ROLLUP_VERIFIER_ADDR = vm.envAddress("L1_MULTIPLE_VERSION_ROLLUP_VERIFIER_ADDR"); + // address L1_ENFORCED_TX_GATEWAY_PROXY_ADDR = vm.envAddress("L1_ENFORCED_TX_GATEWAY_PROXY_ADDR"); + address L1_WHITELIST_ADDR = vm.envAddress("L1_WHITELIST_ADDR"); + + ScrollOwner owner; + + function run() external { + vm.startBroadcast(L1_DEPLOYER_PRIVATE_KEY); + + owner = ScrollOwner(payable(L1_SCROLL_OWNER_ADDR)); + + // @note we don't config 14D access, since the default admin is a 14D timelock which can access all methods. + configProxyAdmin(); + configScrollChain(); + configL1MessageQueue(); + configL1ScrollMessenger(); + configL2GasPriceOracle(); + configL1Whitelist(); + configMultipleVersionRollupVerifier(); + configL1GatewayRouter(); + configL1CustomERC20Gateway(); + configL1ERC721Gateway(); + configL1ERC1155Gateway(); + + // @note comments out for testnet + // configEnforcedTxGateway(); + // configL1USDCGateway(); + + grantRoles(); + transferOwnership(); + + vm.stopBroadcast(); + } + + function transferOwnership() internal { + Ownable(L1_PROXY_ADMIN_ADDR).transferOwnership(address(owner)); + Ownable(L1_SCROLL_CHAIN_PROXY_ADDR).transferOwnership(address(owner)); + Ownable(L1_MESSAGE_QUEUE_PROXY_ADDR).transferOwnership(address(owner)); + Ownable(L1_SCROLL_MESSENGER_PROXY_ADDR).transferOwnership(address(owner)); + // Ownable(L1_ENFORCED_TX_GATEWAY_PROXY_ADDR).transferOwnership(address(owner)); + Ownable(L2_GAS_PRICE_ORACLE_PROXY_ADDR).transferOwnership(address(owner)); + Ownable(L1_WHITELIST_ADDR).transferOwnership(address(owner)); + Ownable(L1_MULTIPLE_VERSION_ROLLUP_VERIFIER_ADDR).transferOwnership(address(owner)); + Ownable(L1_GATEWAY_ROUTER_PROXY_ADDR).transferOwnership(address(owner)); + Ownable(L1_CUSTOM_ERC20_GATEWAY_PROXY_ADDR).transferOwnership(address(owner)); + Ownable(L1_ETH_GATEWAY_PROXY_ADDR).transferOwnership(address(owner)); + Ownable(L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR).transferOwnership(address(owner)); + // Ownable(L1_USDC_GATEWAY_PROXY_ADDR).transferOwnership(address(owner)); + Ownable(L1_WETH_GATEWAY_PROXY_ADDR).transferOwnership(address(owner)); + Ownable(L1_ERC721_GATEWAY_PROXY_ADDR).transferOwnership(address(owner)); + Ownable(L1_ERC1155_GATEWAY_PROXY_ADDR).transferOwnership(address(owner)); + } + + function grantRoles() internal { + owner.grantRole(SECURITY_COUNCIL_NO_DELAY_ROLE, SECURITY_COUNCIL_ADDR); + owner.grantRole(SCROLL_MULTISIG_NO_DELAY_ROLE, SCROLL_MULTISIG_ADDR); + owner.grantRole(TIMELOCK_1DAY_DELAY_ROLE, L1_1D_TIMELOCK_ADDR); + owner.grantRole(TIMELOCK_7DAY_DELAY_ROLE, L1_7D_TIMELOCK_ADDR); + + owner.grantRole(owner.DEFAULT_ADMIN_ROLE(), L1_14D_TIMELOCK_ADDR); + owner.revokeRole(owner.DEFAULT_ADMIN_ROLE(), vm.addr(L1_DEPLOYER_PRIVATE_KEY)); + } + + function configProxyAdmin() internal { + bytes4[] memory _selectors; + + // no delay, security council + _selectors = new bytes4[](2); + _selectors[0] = ProxyAdmin.upgrade.selector; + _selectors[1] = ProxyAdmin.upgradeAndCall.selector; + owner.updateAccess(L1_PROXY_ADMIN_ADDR, _selectors, SECURITY_COUNCIL_NO_DELAY_ROLE, true); + } + + function configScrollChain() internal { + bytes4[] memory _selectors; + + // no delay, scroll multisig + _selectors = new bytes4[](5); + _selectors[0] = ScrollChain.revertBatch.selector; + _selectors[1] = ScrollChain.removeSequencer.selector; + _selectors[2] = ScrollChain.removeProver.selector; + _selectors[3] = ScrollChain.updateMaxNumTxInChunk.selector; + _selectors[4] = ScrollChain.setPause.selector; + owner.updateAccess(L1_SCROLL_CHAIN_PROXY_ADDR, _selectors, SCROLL_MULTISIG_NO_DELAY_ROLE, true); + + // delay 1 day, scroll multisig + _selectors = new bytes4[](2); + _selectors[0] = ScrollChain.addSequencer.selector; + _selectors[1] = ScrollChain.addProver.selector; + owner.updateAccess(L1_SCROLL_CHAIN_PROXY_ADDR, _selectors, TIMELOCK_1DAY_DELAY_ROLE, true); + } + + function configL1MessageQueue() internal { + bytes4[] memory _selectors; + + // delay 1 day, scroll multisig + _selectors = new bytes4[](2); + _selectors[0] = L1MessageQueue.updateGasOracle.selector; + _selectors[1] = L1MessageQueue.updateMaxGasLimit.selector; + owner.updateAccess(L1_MESSAGE_QUEUE_PROXY_ADDR, _selectors, TIMELOCK_1DAY_DELAY_ROLE, true); + } + + function configL1ScrollMessenger() internal { + bytes4[] memory _selectors; + + // no delay, scroll multisig + _selectors = new bytes4[](1); + _selectors[0] = ScrollMessengerBase.setPause.selector; + owner.updateAccess(L1_SCROLL_MESSENGER_PROXY_ADDR, _selectors, SCROLL_MULTISIG_NO_DELAY_ROLE, true); + + // delay 1 day, scroll multisig + _selectors = new bytes4[](1); + _selectors[0] = L1ScrollMessenger.updateMaxReplayTimes.selector; + owner.updateAccess(L1_SCROLL_MESSENGER_PROXY_ADDR, _selectors, TIMELOCK_1DAY_DELAY_ROLE, true); + } + + function configL2GasPriceOracle() internal { + bytes4[] memory _selectors; + + // no delay, scroll multisig + _selectors = new bytes4[](1); + _selectors[0] = L2GasPriceOracle.setIntrinsicParams.selector; + owner.updateAccess(L2_GAS_PRICE_ORACLE_PROXY_ADDR, _selectors, SCROLL_MULTISIG_NO_DELAY_ROLE, true); + } + + function configL1Whitelist() internal { + bytes4[] memory _selectors; + + // delay 1 day, scroll multisig + _selectors = new bytes4[](1); + _selectors[0] = Whitelist.updateWhitelistStatus.selector; + owner.updateAccess(L1_WHITELIST_ADDR, _selectors, TIMELOCK_1DAY_DELAY_ROLE, true); + } + + function configMultipleVersionRollupVerifier() internal { + bytes4[] memory _selectors; + + // no delay, security council + _selectors = new bytes4[](1); + _selectors[0] = MultipleVersionRollupVerifier.updateVerifier.selector; + owner.updateAccess(L1_MULTIPLE_VERSION_ROLLUP_VERIFIER_ADDR, _selectors, SECURITY_COUNCIL_NO_DELAY_ROLE, true); + + // delay 7 day, scroll multisig + _selectors = new bytes4[](1); + _selectors[0] = MultipleVersionRollupVerifier.updateVerifier.selector; + owner.updateAccess(L1_MULTIPLE_VERSION_ROLLUP_VERIFIER_ADDR, _selectors, TIMELOCK_7DAY_DELAY_ROLE, true); + } + + function configL1GatewayRouter() internal { + bytes4[] memory _selectors; + + // delay 1 day, scroll multisig + _selectors = new bytes4[](1); + _selectors[0] = L1GatewayRouter.setERC20Gateway.selector; + owner.updateAccess(L1_GATEWAY_ROUTER_PROXY_ADDR, _selectors, TIMELOCK_1DAY_DELAY_ROLE, true); + } + + function configL1CustomERC20Gateway() internal { + bytes4[] memory _selectors; + + // delay 1 day, scroll multisig + _selectors = new bytes4[](1); + _selectors[0] = L1CustomERC20Gateway.updateTokenMapping.selector; + owner.updateAccess(L1_CUSTOM_ERC20_GATEWAY_PROXY_ADDR, _selectors, TIMELOCK_1DAY_DELAY_ROLE, true); + } + + function configL1ERC721Gateway() internal { + bytes4[] memory _selectors; + + // delay 1 day, scroll multisig + _selectors = new bytes4[](1); + _selectors[0] = L1ERC721Gateway.updateTokenMapping.selector; + owner.updateAccess(L1_ERC721_GATEWAY_PROXY_ADDR, _selectors, TIMELOCK_1DAY_DELAY_ROLE, true); + } + + function configL1ERC1155Gateway() internal { + bytes4[] memory _selectors; + + // delay 1 day, scroll multisig + _selectors = new bytes4[](1); + _selectors[0] = L1ERC1155Gateway.updateTokenMapping.selector; + owner.updateAccess(L1_ERC1155_GATEWAY_PROXY_ADDR, _selectors, TIMELOCK_1DAY_DELAY_ROLE, true); + } + + /* + function configL1USDCGateway() internal { + bytes4[] memory _selectors; + + // delay 7 day, scroll multisig + _selectors = new bytes4[](3); + _selectors[0] = L1USDCGateway.updateCircleCaller.selector; + _selectors[1] = L1USDCGateway.pauseDeposit.selector; + _selectors[2] = L1USDCGateway.pauseWithdraw.selector; + owner.updateAccess(L1_USDC_GATEWAY_PROXY_ADDR, _selectors, TIMELOCK_7DAY_DELAY_ROLE, true); + } + + function configEnforcedTxGateway() internal { + bytes4[] memory _selectors; + + // no delay, scroll multisig + _selectors = new bytes4[](1); + _selectors[0] = EnforcedTxGateway.setPause.selector; + owner.updateAccess(L1_ENFORCED_TX_GATEWAY_PROXY_ADDR, _selectors, SCROLL_MULTISIG_NO_DELAY_ROLE, true); + } + */ +} diff --git a/contracts/scripts/foundry/InitializeL2RateLimiter.s.sol b/contracts/scripts/foundry/InitializeL2RateLimiter.s.sol new file mode 100644 index 0000000000..c6e5921393 --- /dev/null +++ b/contracts/scripts/foundry/InitializeL2RateLimiter.s.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.10; + +import {Script} from "forge-std/Script.sol"; + +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {AccessControlEnumerable} from "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; + +import {ScrollMessengerBase} from "../../src/libraries/ScrollMessengerBase.sol"; +import {ScrollGatewayBase} from "../../src/libraries/gateway/ScrollGatewayBase.sol"; +import {ETHRateLimiter} from "../../src/rate-limiter/ETHRateLimiter.sol"; +import {TokenRateLimiter} from "../../src/rate-limiter/TokenRateLimiter.sol"; + +// solhint-disable max-states-count +// solhint-disable state-visibility +// solhint-disable var-name-mixedcase + +contract InitializeL2RateLimiter is Script { + uint256 L2_DEPLOYER_PRIVATE_KEY = vm.envUint("L2_DEPLOYER_PRIVATE_KEY"); + + address L2_SCROLL_MESSENGER_PROXY_ADDR = vm.envAddress("L2_SCROLL_MESSENGER_PROXY_ADDR"); + address L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR = vm.envAddress("L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR"); + address L2_ETH_GATEWAY_PROXY_ADDR = vm.envAddress("L2_ETH_GATEWAY_PROXY_ADDR"); + address L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR = vm.envAddress("L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR"); + address L2_DAI_GATEWAY_PROXY_ADDR = vm.envAddress("L2_DAI_GATEWAY_PROXY_ADDR"); + // address L2_USDC_GATEWAY_PROXY_ADDR = vm.envAddress("L2_USDC_GATEWAY_PROXY_ADDR"); + + address L2_ETH_RATE_LIMITER_ADDR = vm.envAddress("L2_ETH_RATE_LIMITER_ADDR"); + address L2_TOKEN_RATE_LIMITER_ADDR = vm.envAddress("L2_TOKEN_RATE_LIMITER_ADDR"); + + function run() external { + vm.startBroadcast(L2_DEPLOYER_PRIVATE_KEY); + + ScrollMessengerBase(payable(L2_SCROLL_MESSENGER_PROXY_ADDR)).updateRateLimiter(L2_ETH_RATE_LIMITER_ADDR); + + bytes32 TOKEN_SPENDER_ROLE = TokenRateLimiter(L2_TOKEN_RATE_LIMITER_ADDR).TOKEN_SPENDER_ROLE(); + TokenRateLimiter(L2_TOKEN_RATE_LIMITER_ADDR).grantRole(TOKEN_SPENDER_ROLE, L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR); + TokenRateLimiter(L2_TOKEN_RATE_LIMITER_ADDR).grantRole( + TOKEN_SPENDER_ROLE, + L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR + ); + TokenRateLimiter(L2_TOKEN_RATE_LIMITER_ADDR).grantRole(TOKEN_SPENDER_ROLE, L2_DAI_GATEWAY_PROXY_ADDR); + + ScrollGatewayBase(payable(L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR)).updateRateLimiter(L2_TOKEN_RATE_LIMITER_ADDR); + ScrollGatewayBase(payable(L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR)).updateRateLimiter(L2_TOKEN_RATE_LIMITER_ADDR); + ScrollGatewayBase(payable(L2_DAI_GATEWAY_PROXY_ADDR)).updateRateLimiter(L2_TOKEN_RATE_LIMITER_ADDR); + + // @note comments out for now + // limiter.grantRole(TOKEN_SPENDER_ROLE, L2_USDC_GATEWAY_PROXY_ADDR); + // ScrollGatewayBase(payable(L2_USDC_GATEWAY_PROXY_ADDR)).updateRateLimiter(address(limiter)); + + vm.stopBroadcast(); + } +} diff --git a/contracts/scripts/foundry/InitializeL2ScrollOwner.s.sol b/contracts/scripts/foundry/InitializeL2ScrollOwner.s.sol new file mode 100644 index 0000000000..240daac77a --- /dev/null +++ b/contracts/scripts/foundry/InitializeL2ScrollOwner.s.sol @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.10; + +import {Script} from "forge-std/Script.sol"; + +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol"; +import {AccessControlEnumerable} from "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; + +import {L2USDCGateway} from "../../src/L2/gateways/usdc/L2USDCGateway.sol"; +import {L2CustomERC20Gateway} from "../../src/L2/gateways/L2CustomERC20Gateway.sol"; +import {L2CustomERC20Gateway} from "../../src/L2/gateways/L2CustomERC20Gateway.sol"; +import {L2ERC1155Gateway} from "../../src/L2/gateways/L2ERC1155Gateway.sol"; +import {L2ERC721Gateway} from "../../src/L2/gateways/L2ERC721Gateway.sol"; +import {L2GatewayRouter} from "../../src/L2/gateways/L2GatewayRouter.sol"; +import {ScrollMessengerBase} from "../../src/libraries/ScrollMessengerBase.sol"; +import {L1GasPriceOracle} from "../../src/L2/predeploys/L1GasPriceOracle.sol"; +import {L2TxFeeVault} from "../../src/L2/predeploys/L2TxFeeVault.sol"; +import {Whitelist} from "../../src/L2/predeploys/Whitelist.sol"; +import {ScrollOwner} from "../../src/misc/ScrollOwner.sol"; +import {ETHRateLimiter} from "../../src/rate-limiter/ETHRateLimiter.sol"; +import {TokenRateLimiter} from "../../src/rate-limiter/TokenRateLimiter.sol"; + +// solhint-disable max-states-count +// solhint-disable state-visibility +// solhint-disable var-name-mixedcase + +contract InitializeL2ScrollOwner is Script { + uint256 L2_DEPLOYER_PRIVATE_KEY = vm.envUint("L2_DEPLOYER_PRIVATE_KEY"); + + bytes32 constant SECURITY_COUNCIL_NO_DELAY_ROLE = keccak256("SECURITY_COUNCIL_NO_DELAY_ROLE"); + bytes32 constant SCROLL_MULTISIG_NO_DELAY_ROLE = keccak256("SCROLL_MULTISIG_NO_DELAY_ROLE"); + + bytes32 constant TIMELOCK_1DAY_DELAY_ROLE = keccak256("TIMELOCK_1DAY_DELAY_ROLE"); + bytes32 constant TIMELOCK_7DAY_DELAY_ROLE = keccak256("TIMELOCK_7DAY_DELAY_ROLE"); + + address SCROLL_MULTISIG_ADDR = vm.envAddress("L2_SCROLL_MULTISIG_ADDR"); + address SECURITY_COUNCIL_ADDR = vm.envAddress("L2_SECURITY_COUNCIL_ADDR"); + + address L2_SCROLL_OWNER_ADDR = vm.envAddress("L2_SCROLL_OWNER_ADDR"); + address L2_1D_TIMELOCK_ADDR = vm.envAddress("L2_1D_TIMELOCK_ADDR"); + address L2_7D_TIMELOCK_ADDR = vm.envAddress("L2_7D_TIMELOCK_ADDR"); + address L2_14D_TIMELOCK_ADDR = vm.envAddress("L2_14D_TIMELOCK_ADDR"); + + address L2_PROXY_ADMIN_ADDR = vm.envAddress("L2_PROXY_ADMIN_ADDR"); + address L2_TX_FEE_VAULT_ADDR = vm.envAddress("L2_TX_FEE_VAULT_ADDR"); + address L1_GAS_PRICE_ORACLE_ADDR = vm.envAddress("L1_GAS_PRICE_ORACLE_ADDR"); + address L2_WHITELIST_ADDR = vm.envAddress("L2_WHITELIST_ADDR"); + address L2_MESSAGE_QUEUE_ADDR = vm.envAddress("L2_MESSAGE_QUEUE_ADDR"); + + address L2_SCROLL_MESSENGER_PROXY_ADDR = vm.envAddress("L2_SCROLL_MESSENGER_PROXY_ADDR"); + address L2_GATEWAY_ROUTER_PROXY_ADDR = vm.envAddress("L2_GATEWAY_ROUTER_PROXY_ADDR"); + address L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR = vm.envAddress("L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR"); + address L2_ETH_GATEWAY_PROXY_ADDR = vm.envAddress("L2_ETH_GATEWAY_PROXY_ADDR"); + address L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR = vm.envAddress("L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR"); + // address L2_USDC_GATEWAY_PROXY_ADDR = vm.envAddress("L2_USDC_GATEWAY_PROXY_ADDR"); + address L2_WETH_GATEWAY_PROXY_ADDR = vm.envAddress("L2_WETH_GATEWAY_PROXY_ADDR"); + address L2_ERC721_GATEWAY_PROXY_ADDR = vm.envAddress("L2_ERC721_GATEWAY_PROXY_ADDR"); + address L2_ERC1155_GATEWAY_PROXY_ADDR = vm.envAddress("L2_ERC1155_GATEWAY_PROXY_ADDR"); + + address L2_ETH_RATE_LIMITER_ADDR = vm.envAddress("L2_ETH_RATE_LIMITER_ADDR"); + address L2_TOKEN_RATE_LIMITER_ADDR = vm.envAddress("L2_TOKEN_RATE_LIMITER_ADDR"); + + ScrollOwner owner; + + function run() external { + vm.startBroadcast(L2_DEPLOYER_PRIVATE_KEY); + + owner = ScrollOwner(payable(L2_SCROLL_OWNER_ADDR)); + + // @note we don't config 14D access, since the default admin is a 14D timelock which can access all methods. + configProxyAdmin(); + configL1GasPriceOracle(); + configL2TxFeeVault(); + configL2Whitelist(); + configL2ScrollMessenger(); + configL2GatewayRouter(); + configL2CustomERC20Gateway(); + configL2ERC721Gateway(); + configL2ERC1155Gateway(); + configETHRateLimiter(); + configTokenRateLimiter(); + + // @note comments out for testnet + // configL2USDCGateway(); + + grantRoles(); + transferOwnership(); + + vm.stopBroadcast(); + } + + function transferOwnership() internal { + Ownable(L2_PROXY_ADMIN_ADDR).transferOwnership(address(owner)); + Ownable(L2_MESSAGE_QUEUE_ADDR).transferOwnership(address(owner)); + Ownable(L1_GAS_PRICE_ORACLE_ADDR).transferOwnership(address(owner)); + Ownable(L2_TX_FEE_VAULT_ADDR).transferOwnership(address(owner)); + Ownable(L2_WHITELIST_ADDR).transferOwnership(address(owner)); + Ownable(L2_SCROLL_MESSENGER_PROXY_ADDR).transferOwnership(address(owner)); + Ownable(L2_GATEWAY_ROUTER_PROXY_ADDR).transferOwnership(address(owner)); + Ownable(L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR).transferOwnership(address(owner)); + Ownable(L2_ETH_GATEWAY_PROXY_ADDR).transferOwnership(address(owner)); + Ownable(L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR).transferOwnership(address(owner)); + Ownable(L2_WETH_GATEWAY_PROXY_ADDR).transferOwnership(address(owner)); + Ownable(L2_ERC721_GATEWAY_PROXY_ADDR).transferOwnership(address(owner)); + Ownable(L2_ERC1155_GATEWAY_PROXY_ADDR).transferOwnership(address(owner)); + + // Ownable(L2_USDC_GATEWAY_PROXY_ADDR).transferOwnership(address(owner)); + + Ownable(L2_ETH_RATE_LIMITER_ADDR).transferOwnership(address(owner)); + + TokenRateLimiter tokenRateLimiter = TokenRateLimiter(L2_TOKEN_RATE_LIMITER_ADDR); + tokenRateLimiter.grantRole(tokenRateLimiter.DEFAULT_ADMIN_ROLE(), address(owner)); + tokenRateLimiter.revokeRole(tokenRateLimiter.DEFAULT_ADMIN_ROLE(), vm.addr(L2_DEPLOYER_PRIVATE_KEY)); + } + + function grantRoles() internal { + owner.grantRole(SECURITY_COUNCIL_NO_DELAY_ROLE, SECURITY_COUNCIL_ADDR); + owner.grantRole(SCROLL_MULTISIG_NO_DELAY_ROLE, SCROLL_MULTISIG_ADDR); + owner.grantRole(TIMELOCK_1DAY_DELAY_ROLE, L2_1D_TIMELOCK_ADDR); + owner.grantRole(TIMELOCK_7DAY_DELAY_ROLE, L2_7D_TIMELOCK_ADDR); + + owner.grantRole(owner.DEFAULT_ADMIN_ROLE(), L2_14D_TIMELOCK_ADDR); + owner.revokeRole(owner.DEFAULT_ADMIN_ROLE(), vm.addr(L2_DEPLOYER_PRIVATE_KEY)); + } + + function configProxyAdmin() internal { + bytes4[] memory _selectors; + + // no delay, security council + _selectors = new bytes4[](2); + _selectors[0] = ProxyAdmin.upgrade.selector; + _selectors[1] = ProxyAdmin.upgradeAndCall.selector; + owner.updateAccess(L2_PROXY_ADMIN_ADDR, _selectors, SECURITY_COUNCIL_NO_DELAY_ROLE, true); + } + + function configL1GasPriceOracle() internal { + bytes4[] memory _selectors; + + // no delay, scroll multisig + _selectors = new bytes4[](2); + _selectors[0] = L1GasPriceOracle.setOverhead.selector; + _selectors[1] = L1GasPriceOracle.setScalar.selector; + owner.updateAccess(L1_GAS_PRICE_ORACLE_ADDR, _selectors, SCROLL_MULTISIG_NO_DELAY_ROLE, true); + } + + function configL2TxFeeVault() internal { + bytes4[] memory _selectors; + + // no delay, scroll multisig + _selectors = new bytes4[](1); + _selectors[0] = L2TxFeeVault.updateMinWithdrawAmount.selector; + owner.updateAccess(L2_TX_FEE_VAULT_ADDR, _selectors, SCROLL_MULTISIG_NO_DELAY_ROLE, true); + } + + function configL2Whitelist() internal { + bytes4[] memory _selectors; + + // delay 1 day, scroll multisig + _selectors = new bytes4[](1); + _selectors[0] = Whitelist.updateWhitelistStatus.selector; + owner.updateAccess(L2_WHITELIST_ADDR, _selectors, TIMELOCK_1DAY_DELAY_ROLE, true); + } + + function configL2ScrollMessenger() internal { + bytes4[] memory _selectors; + + // no delay, scroll multisig + _selectors = new bytes4[](1); + _selectors[0] = ScrollMessengerBase.setPause.selector; + owner.updateAccess(L2_SCROLL_MESSENGER_PROXY_ADDR, _selectors, SCROLL_MULTISIG_NO_DELAY_ROLE, true); + } + + function configL2GatewayRouter() internal { + bytes4[] memory _selectors; + + // delay 1 day, scroll multisig + _selectors = new bytes4[](1); + _selectors[0] = L2GatewayRouter.setERC20Gateway.selector; + owner.updateAccess(L2_GATEWAY_ROUTER_PROXY_ADDR, _selectors, TIMELOCK_1DAY_DELAY_ROLE, true); + } + + function configL2CustomERC20Gateway() internal { + bytes4[] memory _selectors; + + // delay 1 day, scroll multisig + _selectors = new bytes4[](1); + _selectors[0] = L2CustomERC20Gateway.updateTokenMapping.selector; + owner.updateAccess(L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR, _selectors, TIMELOCK_1DAY_DELAY_ROLE, true); + } + + function configL2ERC721Gateway() internal { + bytes4[] memory _selectors; + + // delay 1 day, scroll multisig + _selectors = new bytes4[](1); + _selectors[0] = L2ERC721Gateway.updateTokenMapping.selector; + owner.updateAccess(L2_ERC721_GATEWAY_PROXY_ADDR, _selectors, TIMELOCK_1DAY_DELAY_ROLE, true); + } + + function configL2ERC1155Gateway() internal { + bytes4[] memory _selectors; + + // delay 1 day, scroll multisig + _selectors = new bytes4[](1); + _selectors[0] = L2ERC1155Gateway.updateTokenMapping.selector; + owner.updateAccess(L2_ERC1155_GATEWAY_PROXY_ADDR, _selectors, TIMELOCK_1DAY_DELAY_ROLE, true); + } + + function configETHRateLimiter() internal { + bytes4[] memory _selectors; + + // no delay, scroll multisig + _selectors = new bytes4[](1); + _selectors[0] = ETHRateLimiter.updateTotalLimit.selector; + owner.updateAccess(L2_ETH_RATE_LIMITER_ADDR, _selectors, SCROLL_MULTISIG_NO_DELAY_ROLE, true); + } + + function configTokenRateLimiter() internal { + bytes4[] memory _selectors; + + // no delay, scroll multisig + _selectors = new bytes4[](2); + _selectors[0] = TokenRateLimiter.updateTotalLimit.selector; + _selectors[1] = AccessControl.grantRole.selector; + owner.updateAccess(L2_TOKEN_RATE_LIMITER_ADDR, _selectors, SCROLL_MULTISIG_NO_DELAY_ROLE, true); + + // delay 7 day, scroll multisig + _selectors = new bytes4[](1); + _selectors[0] = AccessControl.revokeRole.selector; + owner.updateAccess(L2_TOKEN_RATE_LIMITER_ADDR, _selectors, TIMELOCK_7DAY_DELAY_ROLE, true); + } + + /* + function configL2USDCGateway() internal { + bytes4[] memory _selectors; + + // delay 7 day, scroll multisig + _selectors = new bytes4[](3); + _selectors[0] = L2USDCGateway.updateCircleCaller.selector; + _selectors[1] = L2USDCGateway.pauseDeposit.selector; + _selectors[2] = L2USDCGateway.pauseWithdraw.selector; + owner.updateAccess(L2_USDC_GATEWAY_PROXY_ADDR, _selectors, TIMELOCK_7DAY_DELAY_ROLE, true); + } + */ +} diff --git a/contracts/src/External.sol b/contracts/src/External.sol index e445916858..c21b3831d1 100644 --- a/contracts/src/External.sol +++ b/contracts/src/External.sol @@ -2,6 +2,7 @@ pragma solidity =0.8.16; +import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol"; import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {MinimalForwarder} from "@openzeppelin/contracts/metatx/MinimalForwarder.sol"; diff --git a/contracts/src/L1/rollup/L2GasPriceOracle.sol b/contracts/src/L1/rollup/L2GasPriceOracle.sol index 52fd07fd3c..375848d77b 100644 --- a/contracts/src/L1/rollup/L2GasPriceOracle.sol +++ b/contracts/src/L1/rollup/L2GasPriceOracle.sol @@ -108,23 +108,7 @@ contract L2GasPriceOracle is OwnableUpgradeable, IL2GasPriceOracle { * Public Mutating Functions * *****************************/ - /// @notice Allows the owner to update parameters for intrinsic gas calculation. - /// @param _txGas The intrinsic gas for transaction. - /// @param _txGasContractCreation The intrinsic gas for contract creation. - /// @param _zeroGas The intrinsic gas for each zero byte. - /// @param _nonZeroGas The intrinsic gas for each nonzero byte. - function setIntrinsicParams( - uint64 _txGas, - uint64 _txGasContractCreation, - uint64 _zeroGas, - uint64 _nonZeroGas - ) external { - require(whitelist.isSenderAllowed(msg.sender), "Not whitelisted sender"); - - _setIntrinsicParams(_txGas, _txGasContractCreation, _zeroGas, _nonZeroGas); - } - - /// @notice Allows the owner to modify the l2 base fee. + /// @notice Allows whitelisted caller to modify the l2 base fee. /// @param _newL2BaseFee The new l2 base fee. function setL2BaseFee(uint256 _newL2BaseFee) external { require(whitelist.isSenderAllowed(msg.sender), "Not whitelisted sender"); @@ -149,6 +133,20 @@ contract L2GasPriceOracle is OwnableUpgradeable, IL2GasPriceOracle { emit UpdateWhitelist(_oldWhitelist, _newWhitelist); } + /// @notice Allows the owner to update parameters for intrinsic gas calculation. + /// @param _txGas The intrinsic gas for transaction. + /// @param _txGasContractCreation The intrinsic gas for contract creation. + /// @param _zeroGas The intrinsic gas for each zero byte. + /// @param _nonZeroGas The intrinsic gas for each nonzero byte. + function setIntrinsicParams( + uint64 _txGas, + uint64 _txGasContractCreation, + uint64 _zeroGas, + uint64 _nonZeroGas + ) external onlyOwner { + _setIntrinsicParams(_txGas, _txGasContractCreation, _zeroGas, _nonZeroGas); + } + /********************** * Internal Functions * **********************/ diff --git a/contracts/src/test/L2GasPriceOracle.t.sol b/contracts/src/test/L2GasPriceOracle.t.sol index bc0f16566e..2d7eebb57e 100644 --- a/contracts/src/test/L2GasPriceOracle.t.sol +++ b/contracts/src/test/L2GasPriceOracle.t.sol @@ -50,7 +50,7 @@ contract L2GasPriceOracleTest is DSTestPlus { function testSetIntrinsicParamsAccess() external { hevm.startPrank(address(4)); - hevm.expectRevert("Not whitelisted sender"); + hevm.expectRevert("Ownable: caller is not the owner"); oracle.setIntrinsicParams(1, 0, 0, 1); }