test(governance): add timelock tests
This commit is contained in:
1 parent
aed4bd47bb
commit
92b1018b0a
1 file changed
+129
@@ -0,0 +1,129 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.36;
|
||||
|
||||
import {Test} from "forge-std/Test.sol";
|
||||
import {Timelock} from "../src/Timelock.sol";
|
||||
|
||||
contract Target {
|
||||
address public immutable timelock;
|
||||
uint256 public value;
|
||||
|
||||
constructor(address timelockAddress) {
|
||||
timelock = timelockAddress;
|
||||
}
|
||||
|
||||
function setValue(uint256 newValue) external {
|
||||
require(msg.sender == timelock, "Target: FORBIDDEN");
|
||||
value = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
contract TimelockTest is Test {
|
||||
Timelock private timelock;
|
||||
Target private target;
|
||||
|
||||
uint256 private constant delay = 2 days;
|
||||
|
||||
function setUp() public {
|
||||
vm.warp(1_000_000);
|
||||
timelock = new Timelock(address(this), delay);
|
||||
target = new Target(address(timelock));
|
||||
}
|
||||
|
||||
function queueSetValue(uint256 newValue, uint256 eta) private returns (bytes32) {
|
||||
return timelock.queueTransaction(address(target), 0, "setValue(uint256)", abi.encode(newValue), eta);
|
||||
}
|
||||
|
||||
function testConstructorRejectsShortDelay() public {
|
||||
vm.expectRevert(bytes("Timelock: DELAY_SHORT"));
|
||||
new Timelock(address(this), 1 hours);
|
||||
}
|
||||
|
||||
function testQueueMarksTransaction() public {
|
||||
uint256 eta = block.timestamp + delay;
|
||||
bytes32 txHash = queueSetValue(42, eta);
|
||||
assertTrue(timelock.queuedTransactions(txHash));
|
||||
}
|
||||
|
||||
function testQueueRevertsWhenEtaTooSoon() public {
|
||||
vm.expectRevert(bytes("Timelock: ETA_TOO_SOON"));
|
||||
queueSetValue(42, block.timestamp + 1 hours);
|
||||
}
|
||||
|
||||
function testQueueOnlyAdmin() public {
|
||||
vm.prank(address(0xBAD));
|
||||
vm.expectRevert(bytes("Timelock: ONLY_ADMIN"));
|
||||
queueSetValue(42, block.timestamp + delay);
|
||||
}
|
||||
|
||||
function testExecuteRevertsBeforeEta() public {
|
||||
uint256 eta = block.timestamp + delay;
|
||||
queueSetValue(42, eta);
|
||||
vm.expectRevert(bytes("Timelock: NOT_READY"));
|
||||
timelock.executeTransaction(address(target), 0, "setValue(uint256)", abi.encode(42), eta);
|
||||
}
|
||||
|
||||
function testExecuteAppliesEffectAfterDelay() public {
|
||||
uint256 eta = block.timestamp + delay;
|
||||
queueSetValue(42, eta);
|
||||
vm.warp(eta);
|
||||
timelock.executeTransaction(address(target), 0, "setValue(uint256)", abi.encode(42), eta);
|
||||
assertEq(target.value(), 42);
|
||||
}
|
||||
|
||||
function testExecuteRevertsIfNotQueued() public {
|
||||
uint256 eta = block.timestamp + delay;
|
||||
vm.warp(eta);
|
||||
vm.expectRevert(bytes("Timelock: NOT_QUEUED"));
|
||||
timelock.executeTransaction(address(target), 0, "setValue(uint256)", abi.encode(42), eta);
|
||||
}
|
||||
|
||||
function testCancelPreventsExecution() public {
|
||||
uint256 eta = block.timestamp + delay;
|
||||
queueSetValue(42, eta);
|
||||
timelock.cancelTransaction(address(target), 0, "setValue(uint256)", abi.encode(42), eta);
|
||||
vm.warp(eta);
|
||||
vm.expectRevert(bytes("Timelock: NOT_QUEUED"));
|
||||
timelock.executeTransaction(address(target), 0, "setValue(uint256)", abi.encode(42), eta);
|
||||
}
|
||||
|
||||
function testExecuteRevertsAfterGracePeriod() public {
|
||||
uint256 eta = block.timestamp + delay;
|
||||
queueSetValue(42, eta);
|
||||
vm.warp(eta + 14 days + 1);
|
||||
vm.expectRevert(bytes("Timelock: STALE"));
|
||||
timelock.executeTransaction(address(target), 0, "setValue(uint256)", abi.encode(42), eta);
|
||||
}
|
||||
|
||||
function testExecuteBubblesTargetRevert() public {
|
||||
uint256 eta = block.timestamp + delay;
|
||||
bytes memory badData = abi.encodeWithSignature("setValue(uint256)", uint256(7));
|
||||
timelock.queueTransaction(address(target), 0, "", badData, eta);
|
||||
vm.warp(eta);
|
||||
vm.prank(address(0xBAD));
|
||||
vm.expectRevert(bytes("Timelock: ONLY_ADMIN"));
|
||||
timelock.executeTransaction(address(target), 0, "", badData, eta);
|
||||
}
|
||||
|
||||
function testSetDelayOnlyViaSelfCall() public {
|
||||
vm.expectRevert(bytes("Timelock: ONLY_SELF"));
|
||||
timelock.setDelay(3 days);
|
||||
}
|
||||
|
||||
function testTimelockCanRescheduleItselfThroughQueue() public {
|
||||
uint256 eta = block.timestamp + delay;
|
||||
timelock.queueTransaction(address(timelock), 0, "setDelay(uint256)", abi.encode(uint256(5 days)), eta);
|
||||
vm.warp(eta);
|
||||
timelock.executeTransaction(address(timelock), 0, "setDelay(uint256)", abi.encode(uint256(5 days)), eta);
|
||||
assertEq(timelock.delay(), 5 days);
|
||||
}
|
||||
|
||||
function testFuzzExecuteNeverSucceedsBeforeEta(uint256 wait, uint256 newValue) public {
|
||||
uint256 eta = block.timestamp + delay;
|
||||
queueSetValue(newValue, eta);
|
||||
wait = bound(wait, 0, delay - 1);
|
||||
vm.warp(block.timestamp + wait);
|
||||
vm.expectRevert(bytes("Timelock: NOT_READY"));
|
||||
timelock.executeTransaction(address(target), 0, "setValue(uint256)", abi.encode(newValue), eta);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user