From e0df7f0f5bfc8320fdab1c6c7aecf397c4ad3675 Mon Sep 17 00:00:00 2001 From: Franco NG Date: Tue, 11 Jun 2024 14:38:13 +0200 Subject: [PATCH] Added L2VotingPowerPaused and L2GovernorPaused contract with test cases --- src/L2/paused/L2GovernorPaused.sol | 151 ++++++++++++++++++++++ src/L2/paused/L2VotingPowerPaused.sol | 38 ++++++ test/L2/paused/L2GovernorPaused.t.sol | 155 +++++++++++++++++++++++ test/L2/paused/L2VotingPowerPaused.t.sol | 110 ++++++++++++++++ 4 files changed, 454 insertions(+) create mode 100644 src/L2/paused/L2GovernorPaused.sol create mode 100644 src/L2/paused/L2VotingPowerPaused.sol create mode 100644 test/L2/paused/L2GovernorPaused.t.sol create mode 100644 test/L2/paused/L2VotingPowerPaused.t.sol diff --git a/src/L2/paused/L2GovernorPaused.sol b/src/L2/paused/L2GovernorPaused.sol new file mode 100644 index 00000000..b857f698 --- /dev/null +++ b/src/L2/paused/L2GovernorPaused.sol @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.23; + +import { Ownable2StepUpgradeable } from "@openzeppelin-upgradeable/contracts/access/Ownable2StepUpgradeable.sol"; +import { Initializable } from "@openzeppelin-upgradeable/contracts/proxy/utils/Initializable.sol"; +import { UUPSUpgradeable } from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; +import { TimelockControllerUpgradeable } from + "@openzeppelin-upgradeable/contracts/governance/extensions/GovernorTimelockControlUpgradeable.sol"; +import { L2Governor } from "../L2Governor.sol"; + +contract L2GovernorPaused is L2Governor { + /// @notice Setting global params. + function initializePaused() public reinitializer(2) { } + + function version() public pure virtual override returns (string memory) { + return string.concat(super.version(), "-paused"); + } + + /// @notice Override the cancel function to prevent staking from being processed. + function cancel( + address[] memory, + uint256[] memory, + bytes[] memory, + bytes32 + ) + public + virtual + override + returns (uint256) + { + revert("L2GovernorPaused: Governor is paused"); + } + + /// @notice Override the castVote function to prevent staking from being processed. + function castVote(uint256, uint8) public virtual override returns (uint256) { + revert("L2GovernorPaused: Governor is paused"); + } + + /// @notice Override the castVoteBySig function to prevent staking from being processed. + function castVoteBySig(uint256, uint8, address, bytes memory) public virtual override returns (uint256) { + revert("L2GovernorPaused: Governor is paused"); + } + + /// @notice Override the castVoteWithReason function to prevent staking from being processed. + function castVoteWithReason(uint256, uint8, string calldata) public virtual override returns (uint256) { + revert("L2GovernorPaused: Governor is paused"); + } + + /// @notice Override the castVoteWithReasonAndParams function to prevent staking from being processed. + function castVoteWithReasonAndParams( + uint256, + uint8, + string calldata, + bytes memory + ) + public + virtual + override + returns (uint256) + { + revert("L2GovernorPaused: Governor is paused"); + } + + /// @notice Override the castVoteWithReasonAndParamsBySig function to prevent staking from being processed. + function castVoteWithReasonAndParamsBySig( + uint256, + uint8, + address, + string calldata, + bytes memory, + bytes memory + ) + public + virtual + override + returns (uint256) + { + revert("L2GovernorPaused: Governor is paused"); + } + + /// @notice Override the execute function to prevent staking from being processed. + function execute( + address[] memory, + uint256[] memory, + bytes[] memory, + bytes32 + ) + public + payable + virtual + override + returns (uint256) + { + revert("L2GovernorPaused: Governor is paused"); + } + + /// @notice Override the propose function to prevent staking from being processed. + function propose( + address[] memory, + uint256[] memory, + bytes[] memory, + string memory + ) + public + virtual + override + returns (uint256) + { + revert("L2GovernorPaused: Governor is paused"); + } + + /// @notice Override the queue function to prevent staking from being processed. + function queue( + address[] memory, + uint256[] memory, + bytes[] memory, + bytes32 + ) + public + virtual + override + returns (uint256) + { + revert("L2GovernorPaused: Governor is paused"); + } + + /// @notice Override the relay function to prevent staking from being processed. + function relay(address, uint256, bytes memory) public payable virtual override { + revert("L2GovernorPaused: Governor is paused"); + } + + /// @notice Override the setProposalThreshold function to prevent staking from being processed. + function setProposalThreshold(uint256) public virtual override { + revert("L2GovernorPaused: Governor is paused"); + } + + /// @notice Override the setVotingDelay function to prevent staking from being processed. + function setVotingDelay(uint48) public virtual override { + revert("L2GovernorPaused: Governor is paused"); + } + + /// @notice Override the setVotingPeriod function to prevent staking from being processed. + function setVotingPeriod(uint32) public virtual override { + revert("L2GovernorPaused: Governor is paused"); + } + + /// @notice Override the updateTimelock function to prevent staking from being processed. + function updateTimelock(TimelockControllerUpgradeable) public virtual override { + revert("L2GovernorPaused: Governor is paused"); + } +} diff --git a/src/L2/paused/L2VotingPowerPaused.sol b/src/L2/paused/L2VotingPowerPaused.sol new file mode 100644 index 00000000..bcaacc85 --- /dev/null +++ b/src/L2/paused/L2VotingPowerPaused.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.23; + +import { Ownable2StepUpgradeable } from "@openzeppelin-upgradeable/contracts/access/Ownable2StepUpgradeable.sol"; +import { Initializable } from "@openzeppelin-upgradeable/contracts/proxy/utils/Initializable.sol"; +import { UUPSUpgradeable } from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; +import { IL2LockingPosition } from "../../interfaces/L2/IL2LockingPosition.sol"; +import { L2VotingPower } from "../L2VotingPower.sol"; + +contract L2VotingPowerPaused is L2VotingPower { + /// @notice Setting global params. + function initializePaused() public reinitializer(2) { + version = "1.0.0-paused"; + } + + /// @notice Override the modifyLockingPosition function to prevent staking from being processed. + function adjustVotingPower( + address, + IL2LockingPosition.LockingPosition memory, + IL2LockingPosition.LockingPosition memory + ) + public + virtual + override + { + revert("L2VotingPowerPaused: VotingPower is paused"); + } + + /// @notice Override the modifyLockingPosition function to prevent staking from being processed. + function delegate(address) public virtual override { + revert("L2VotingPowerPaused: VotingPower is paused"); + } + + /// @notice Override the modifyLockingPosition function to prevent staking from being processed. + function delegateBySig(address, uint256, uint256, uint8, bytes32, bytes32) public virtual override { + revert("L2VotingPowerPaused: VotingPower is paused"); + } +} diff --git a/test/L2/paused/L2GovernorPaused.t.sol b/test/L2/paused/L2GovernorPaused.t.sol new file mode 100644 index 00000000..932e6a4d --- /dev/null +++ b/test/L2/paused/L2GovernorPaused.t.sol @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.23; + +import { IVotes } from "@openzeppelin-upgradeable/contracts/governance/extensions/GovernorVotesUpgradeable.sol"; +import { OwnableUpgradeable } from "@openzeppelin-upgradeable/contracts/access/OwnableUpgradeable.sol"; +import { TimelockController } from "@openzeppelin/contracts/governance/TimelockController.sol"; +import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import { TimelockControllerUpgradeable } from + "@openzeppelin-upgradeable/contracts/governance/extensions/GovernorTimelockControlUpgradeable.sol"; +import { Test, console } from "forge-std/Test.sol"; +import { L2Governor } from "src/L2/L2Governor.sol"; +import { L2GovernorPaused } from "src/L2/paused/L2GovernorPaused.sol"; +import { Utils } from "script/contracts/Utils.sol"; + +contract MockL2GovernorV2 is L2Governor { + function version() public pure virtual override returns (string memory) { + return "2.0.0"; + } +} + +contract L2GovernorPausedTest is Test { + Utils public utils; + L2Governor public l2GovernorImplementation; + L2Governor public l2Governor; + + IVotes votingPower; + address[] executors; + TimelockController timelock; + address initialOwner; + + function setUp() public { + utils = new Utils(); + + // set initial values + votingPower = IVotes(address(0x1)); + executors.push(address(0)); // executor array contains address(0) such that anyone can execute proposals + timelock = new TimelockController(0, new address[](0), executors, address(this)); + initialOwner = address(this); + + console.log("L2GovernorTest address is: %s", address(this)); + + // deploy L2Governor Implementation contract + l2GovernorImplementation = new L2Governor(); + + // deploy L2Governor contract via proxy and initialize it at the same time + l2Governor = L2Governor( + payable( + address( + new ERC1967Proxy( + address(l2GovernorImplementation), + abi.encodeWithSelector(l2Governor.initialize.selector, votingPower, timelock, initialOwner) + ) + ) + ) + ); + + assertEq(l2Governor.name(), "Lisk Governor"); + assertEq(l2Governor.version(), "1.0.0"); + assertEq(l2Governor.votingDelay(), 0); + assertEq(l2Governor.votingPeriod(), 604800); + assertEq(l2Governor.proposalThreshold(), 300_000 * 10 ** 18); + assertEq(l2Governor.timelock(), address(timelock)); + assertEq(l2Governor.quorum(0), 24_000_000 * 10 ** 18); + assertEq(address(l2Governor.token()), address(votingPower)); + assertEq(l2Governor.owner(), initialOwner); + + // assure that address(0) is in executors role + assertEq(timelock.hasRole(timelock.EXECUTOR_ROLE(), address(0)), true); + + // Upgrade from L2Governor to L2GovernorPaused + L2GovernorPaused l2GovernorPausedImplementation = new L2GovernorPaused(); + l2Governor.upgradeToAndCall(address(l2GovernorPausedImplementation), ""); + assertEq(l2Governor.version(), "1.0.0-paused"); + } + + function test_Cancel_Paused() public { + vm.expectRevert("L2GovernorPaused: Governor is paused"); + l2Governor.cancel(new address[](1), new uint256[](1), new bytes[](1), 0); + } + + function test_CastVote_Paused() public { + vm.expectRevert("L2GovernorPaused: Governor is paused"); + l2Governor.castVote(0, 0); + } + + function test_CastVoteBySig_Paused() public { + vm.expectRevert("L2GovernorPaused: Governor is paused"); + l2Governor.castVoteBySig(0, 0, address(0), ""); + } + + function test_CastVoteWithReason_Paused() public { + vm.expectRevert("L2GovernorPaused: Governor is paused"); + l2Governor.castVoteWithReason(0, 0, ""); + } + + function test_CastVoteWithReasonAndParams_Paused() public { + vm.expectRevert("L2GovernorPaused: Governor is paused"); + l2Governor.castVoteWithReasonAndParams(0, 0, "", ""); + } + + function test_CastVoteWithReasonAndParamsBySig_Paused() public { + vm.expectRevert("L2GovernorPaused: Governor is paused"); + l2Governor.castVoteWithReasonAndParamsBySig(0, 0, address(0), "", "", ""); + } + + function test_Execute_Paused() public { + vm.expectRevert("L2GovernorPaused: Governor is paused"); + l2Governor.execute(new address[](1), new uint256[](1), new bytes[](1), 0); + } + + function test_Propose_Paused() public { + vm.expectRevert("L2GovernorPaused: Governor is paused"); + l2Governor.propose(new address[](1), new uint256[](1), new bytes[](1), ""); + } + + function test_Queue_Paused() public { + vm.expectRevert("L2GovernorPaused: Governor is paused"); + l2Governor.queue(new address[](1), new uint256[](1), new bytes[](1), ""); + } + + function test_Relay_Paused() public { + vm.expectRevert("L2GovernorPaused: Governor is paused"); + l2Governor.relay(address(0), 0, ""); + } + + function test_SetProposalThreshold_Paused() public { + vm.expectRevert("L2GovernorPaused: Governor is paused"); + l2Governor.setProposalThreshold(0); + } + + function test_SetVotingDelay_Paused() public { + vm.expectRevert("L2GovernorPaused: Governor is paused"); + l2Governor.setVotingDelay(0); + } + + function test_SetVotingPeriod_Paused() public { + vm.expectRevert("L2GovernorPaused: Governor is paused"); + l2Governor.setVotingPeriod(0); + } + + function test_UpdateTimelock_Paused() public { + vm.expectRevert("L2GovernorPaused: Governor is paused"); + l2Governor.updateTimelock(TimelockControllerUpgradeable(payable(address(0)))); + } + + function test_UpgradeToAndCall_CanUpgradeFromPausedContractToNewContract() public { + MockL2GovernorV2 mockL2GovernorV2Implementation = new MockL2GovernorV2(); + + // upgrade contract + l2Governor.upgradeToAndCall(address(mockL2GovernorV2Implementation), ""); + + // new version updated + assertEq(l2Governor.version(), "2.0.0"); + } +} diff --git a/test/L2/paused/L2VotingPowerPaused.t.sol b/test/L2/paused/L2VotingPowerPaused.t.sol new file mode 100644 index 00000000..45fb31fb --- /dev/null +++ b/test/L2/paused/L2VotingPowerPaused.t.sol @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.23; + +import { IVotes } from "@openzeppelin/contracts/governance/utils/IVotes.sol"; +import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import { OwnableUpgradeable } from "@openzeppelin-upgradeable/contracts/access/OwnableUpgradeable.sol"; +import { IAccessControl } from "@openzeppelin/contracts/access/IAccessControl.sol"; +import { IERC20Errors } from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { Test, console } from "forge-std/Test.sol"; +import { IL2LockingPosition } from "src/interfaces/L2/IL2LockingPosition.sol"; +import { L2VotingPower } from "src/L2/L2VotingPower.sol"; +import { L2VotingPowerPaused } from "src/L2/paused/L2VotingPowerPaused.sol"; +import { Utils } from "script/contracts/Utils.sol"; + +contract MockL2VotingPowerV2 is L2VotingPower { + uint256 public testNumber; + + function initializeV2(uint256 _testNumber) public reinitializer(3) { + version = "2.0.0"; + testNumber = _testNumber; + } + + function onlyV2() public pure returns (string memory) { + return "Only L2VotingPowerV2 have this function"; + } +} + +contract L2VotingPowerPausedTest is Test { + Utils public utils; + L2VotingPower public l2VotingPowerImplementation; + L2VotingPower public l2VotingPower; + + address lockingPositionContractAddress; + + function setUp() public { + utils = new Utils(); + + // set initial values + lockingPositionContractAddress = address(0xdeadbeefdeadbeefdeadbeef); + + console.log("L2VotingPowerTest address is: %s", address(this)); + + // deploy L2VotingPower Implementation contract + l2VotingPowerImplementation = new L2VotingPower(); + + // deploy L2VotingPower contract via proxy and initialize it at the same time + l2VotingPower = L2VotingPower( + address( + new ERC1967Proxy( + address(l2VotingPowerImplementation), + abi.encodeWithSelector(l2VotingPower.initialize.selector, lockingPositionContractAddress) + ) + ) + ); + + assertEq(l2VotingPower.lockingPositionAddress(), lockingPositionContractAddress); + assertEq(l2VotingPower.version(), "1.0.0"); + assertEq(l2VotingPower.name(), "Lisk Voting Power"); + assertEq(l2VotingPower.symbol(), "vpLSK"); + + // Upgrade from L2VotingPower to L2VotingPowerPaused + L2VotingPowerPaused l2VotingPowerPausedImplementation = new L2VotingPowerPaused(); + l2VotingPower.upgradeToAndCall(address(l2VotingPowerPausedImplementation), ""); + + // Call initializePaused with new interface + L2VotingPowerPaused(address(l2VotingPower)).initializePaused(); + + // l2VotingPower pointing to paused contract + assertEq(l2VotingPower.version(), "1.0.0-paused"); + } + + function test_adjustVotingPower_Paused() public { + IL2LockingPosition.LockingPosition memory positionBefore = + IL2LockingPosition.LockingPosition(address(this), 0, 0, 0); + IL2LockingPosition.LockingPosition memory positionAfter = + IL2LockingPosition.LockingPosition(address(this), 50, 0, 0); + + vm.expectRevert("L2VotingPowerPaused: VotingPower is paused"); + l2VotingPower.adjustVotingPower(address(0), positionBefore, positionAfter); + } + + function test_delegate_Paused() public { + vm.expectRevert("L2VotingPowerPaused: VotingPower is paused"); + l2VotingPower.delegate(address(0)); + } + + function test_delegateBySig_Paused() public { + vm.expectRevert("L2VotingPowerPaused: VotingPower is paused"); + l2VotingPower.delegateBySig(address(0), 0, 0, 0, "", ""); + } + + function test_UpgradeToAndCall_CanUpgradeFromPausedContractToNewContract() public { + MockL2VotingPowerV2 mockL2VotingPowerV2Implementation = new MockL2VotingPowerV2(); + + uint256 testNumber = 123; + + // upgrade contract, and also change some variables by reinitialize + l2VotingPower.upgradeToAndCall( + address(mockL2VotingPowerV2Implementation), + abi.encodeWithSelector(mockL2VotingPowerV2Implementation.initializeV2.selector, testNumber) + ); + + // new testNumber variable introduced + assertEq(MockL2VotingPowerV2(address(l2VotingPower)).testNumber(), testNumber); + + // new version updated + assertEq(l2VotingPower.version(), "2.0.0"); + } +}