Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unit tests for GovernanceTimelock #55

Merged
merged 3 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions src/controllers/GovernanceTimelock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,17 @@ contract GovernanceTimelock is IGovernanceTimelock {
////////////////////////////////////////////////////////////////*/

/// @param _admin is the address of admin contract that schedule txs
constructor(address _admin) {
/// @param _delay is the initial delay
constructor(address _admin, uint256 _delay) {
if (_admin == address(0)) {
revert Error.ZERO_ADDRESS_INPUT();
}
_checkDelay(_delay);

admin = _admin;
emit AdminUpdated(address(0), _admin);

delay = _delay;
}

/*/////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -130,13 +134,7 @@ contract GovernanceTimelock is IGovernanceTimelock {

/// @inheritdoc IGovernanceTimelock
function setDelay(uint256 _delay) external override onlySelf {
if (delay < MINIMUM_DELAY) {
revert Error.INVALID_DELAY_MIN();
}

if (delay > MAXIMUM_DELAY) {
revert Error.INVALID_DELAY_MAX();
}
_checkDelay(_delay);

uint256 oldDelay = delay;
delay = _delay;
Expand All @@ -155,4 +153,18 @@ contract GovernanceTimelock is IGovernanceTimelock {

emit AdminUpdated(oldAdmin, _newAdmin);
}

/*/////////////////////////////////////////////////////////////////
PRIVATE/INTERNAL FUNCTIONS
////////////////////////////////////////////////////////////////*/

function _checkDelay(uint256 _delay) internal pure {
if (_delay < MINIMUM_DELAY) {
revert Error.INVALID_DELAY_MIN();
}

if (_delay > MAXIMUM_DELAY) {
revert Error.INVALID_DELAY_MAX();
}
}
}
15 changes: 12 additions & 3 deletions test/Setup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ abstract contract Setup is Test {
/*///////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
/// @dev chain IDs
uint256 constant ETHEREUM_CHAIN_ID = 1;
uint256 constant BSC_CHAIN_ID = 56;
uint256 constant POLYGON_CHAIN_ID = 137;

/// @dev common src and dst chain IDs
uint256 constant SRC_CHAIN_ID = ETHEREUM_CHAIN_ID;
uint256 constant DST_CHAIN_ID = POLYGON_CHAIN_ID;

/// @dev simulated caller
address constant caller = address(10);
address constant owner = address(420);
Expand All @@ -54,10 +63,10 @@ abstract contract Setup is Test {
//////////////////////////////////////////////////////////////*/
/// @notice configure all the chain ids we use for the tests (including src chain)
/// id 0 represents src chain
uint256[] public ALL_CHAINS = [1, 56, 137];
uint256[] public ALL_CHAINS = [ETHEREUM_CHAIN_ID, BSC_CHAIN_ID, POLYGON_CHAIN_ID];

/// @notice configure any new dst chains here
uint256[] public DST_CHAINS = [56, 137];
uint256[] public DST_CHAINS = [BSC_CHAIN_ID, POLYGON_CHAIN_ID];

/// @notice configure all wormhole parameters in order of DST_CHAINS
address[] public WORMHOLE_RELAYERS = [BSC_RELAYER, POLYGON_RELAYER];
Expand Down Expand Up @@ -255,7 +264,7 @@ abstract contract Setup is Test {
address mmaReceiver = address(new MultiMessageReceiver{salt: _salt}());
contractAddress[chainId][bytes("MMA_RECEIVER")] = mmaReceiver;
contractAddress[chainId][bytes("TIMELOCK")] =
address(address(new GovernanceTimelock{salt: _salt}(mmaReceiver)));
address(address(new GovernanceTimelock{salt: _salt}(mmaReceiver, 3 days)));
}
}

Expand Down
22 changes: 11 additions & 11 deletions test/integration-tests/GracePeriodExpiry.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,22 @@ import {GovernanceTimelock} from "src/controllers/GovernanceTimelock.sol";
contract GracePeriodExpiryTest is Setup {
MockUniswapReceiver target;

/// @dev intializes the setup
/// @dev initializes the setup
function setUp() public override {
super.setUp();

vm.selectFork(fork[137]);
vm.selectFork(fork[DST_CHAIN_ID]);
target = new MockUniswapReceiver();
}

function test_timelockCheck() public {
vm.selectFork(fork[1]);
vm.selectFork(fork[SRC_CHAIN_ID]);
vm.startPrank(caller);

/// send cross-chain message using MMA infra
vm.recordLogs();
MultiMessageSender(contractAddress[1][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}(
137,
MultiMessageSender(contractAddress[SRC_CHAIN_ID][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}(
DST_CHAIN_ID,
address(target),
abi.encode(MockUniswapReceiver.setValue.selector, ""),
0,
Expand All @@ -44,21 +44,21 @@ contract GracePeriodExpiryTest is Setup {

vm.recordLogs();
/// simulate off-chain actors
_simulatePayloadDelivery(1, 137, logs);
_simulatePayloadDelivery(SRC_CHAIN_ID, DST_CHAIN_ID, logs);
bytes32 msgId = _getMsgId(vm.getRecordedLogs());

vm.selectFork(fork[137]);
vm.selectFork(fork[DST_CHAIN_ID]);
vm.recordLogs();
/// execute the message and move it to governance timelock contract
MultiMessageReceiver(contractAddress[137][bytes("MMA_RECEIVER")]).executeMessage(msgId);
MultiMessageReceiver(contractAddress[DST_CHAIN_ID][bytes("MMA_RECEIVER")]).executeMessage(msgId);
(uint256 txId, address finalTarget, uint256 value, bytes memory data, uint256 eta) =
_getExecParams(vm.getRecordedLogs());

/// increment the time by 20 day (beyond expiry, delay)
/// increment the time by 21 day (beyond expiry, delay)
/// @notice should revert here with TX_EXPIRED error
vm.warp(block.timestamp + 20 days);
vm.warp(block.timestamp + 21 days);
vm.expectRevert(Error.TX_EXPIRED.selector);
GovernanceTimelock(contractAddress[137][bytes("TIMELOCK")]).executeTransaction(
GovernanceTimelock(contractAddress[DST_CHAIN_ID][bytes("TIMELOCK")]).executeTransaction(
txId, finalTarget, value, data, eta
);
}
Expand Down
22 changes: 11 additions & 11 deletions test/integration-tests/MultiMessageAggregation.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,23 @@ import {GovernanceTimelock} from "src/controllers/GovernanceTimelock.sol";
contract MultiMessageAggregationTest is Setup {
MockUniswapReceiver target;

/// @dev intializes the setup
/// @dev initializes the setup
function setUp() public override {
super.setUp();

vm.selectFork(fork[137]);
vm.selectFork(fork[DST_CHAIN_ID]);
target = new MockUniswapReceiver();
}

/// @dev just sends a message
function test_mmaSendMessage() public {
vm.selectFork(fork[1]);
vm.selectFork(fork[SRC_CHAIN_ID]);
vm.startPrank(caller);

/// send cross-chain message using MMA infra
vm.recordLogs();
MultiMessageSender(contractAddress[1][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}(
137,
MultiMessageSender(contractAddress[SRC_CHAIN_ID][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}(
DST_CHAIN_ID,
address(target),
abi.encode(MockUniswapReceiver.setValue.selector, ""),
0,
Expand All @@ -44,19 +44,19 @@ contract MultiMessageAggregationTest is Setup {

vm.recordLogs();
/// simulate off-chain actors
_simulatePayloadDelivery(1, 137, logs);
_simulatePayloadDelivery(SRC_CHAIN_ID, DST_CHAIN_ID, logs);
bytes32 msgId = _getMsgId(vm.getRecordedLogs());

vm.selectFork(fork[137]);
vm.selectFork(fork[DST_CHAIN_ID]);
vm.recordLogs();
/// execute the message and move it to governance timelock contract
MultiMessageReceiver(contractAddress[137][bytes("MMA_RECEIVER")]).executeMessage(msgId);
MultiMessageReceiver(contractAddress[DST_CHAIN_ID][bytes("MMA_RECEIVER")]).executeMessage(msgId);
(uint256 txId, address finalTarget, uint256 value, bytes memory data, uint256 eta) =
_getExecParams(vm.getRecordedLogs());

/// increment the time by 2 day (delay time)
vm.warp(block.timestamp + 2 days);
GovernanceTimelock(contractAddress[137][bytes("TIMELOCK")]).executeTransaction(
/// increment the time by 7 days (delay time)
vm.warp(block.timestamp + 7 days);
GovernanceTimelock(contractAddress[DST_CHAIN_ID][bytes("TIMELOCK")]).executeTransaction(
txId, finalTarget, value, data, eta
);
assertEq(target.i(), type(uint256).max);
Expand Down
24 changes: 12 additions & 12 deletions test/integration-tests/RemoteAdapterAdd.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {GovernanceTimelock} from "src/controllers/GovernanceTimelock.sol";
/// @dev scenario: admin updates sender adapters on dst chain using message from source chain
/// @notice handles both single add and multiple add
contract RemoteAdapterAdd is Setup {
/// @dev intializes the setup
/// @dev initializes the setup
function setUp() public override {
super.setUp();
}
Expand Down Expand Up @@ -56,9 +56,9 @@ contract RemoteAdapterAdd is Setup {

/// send cross-chain message using MMA infra
vm.recordLogs();
MultiMessageSender(contractAddress[1][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}(
137,
address(contractAddress[137][bytes("MMA_RECEIVER")]),
MultiMessageSender(contractAddress[SRC_CHAIN_ID][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}(
DST_CHAIN_ID,
address(contractAddress[DST_CHAIN_ID][bytes("MMA_RECEIVER")]),
abi.encodeWithSelector(MultiMessageReceiver.updateReceiverAdapters.selector, adaptersToAdd, operation),
0,
block.timestamp + EXPIRATION_CONSTANT
Expand All @@ -69,25 +69,25 @@ contract RemoteAdapterAdd is Setup {

vm.recordLogs();
/// simulate off-chain actors
_simulatePayloadDelivery(1, 137, logs);
_simulatePayloadDelivery(SRC_CHAIN_ID, DST_CHAIN_ID, logs);
bytes32 msgId = _getMsgId(vm.getRecordedLogs());

vm.selectFork(fork[137]);
vm.selectFork(fork[DST_CHAIN_ID]);
vm.recordLogs();
/// execute the message and move it to governance timelock contract
MultiMessageReceiver(contractAddress[137][bytes("MMA_RECEIVER")]).executeMessage(msgId);
MultiMessageReceiver(contractAddress[DST_CHAIN_ID][bytes("MMA_RECEIVER")]).executeMessage(msgId);
(uint256 txId, address finalTarget, uint256 value, bytes memory data, uint256 eta) =
_getExecParams(vm.getRecordedLogs());

/// increment the time by 2 day (delay time)
vm.warp(block.timestamp + 2 days);
GovernanceTimelock(contractAddress[137][bytes("TIMELOCK")]).executeTransaction(
/// increment the time by 3 days (delay time)
vm.warp(block.timestamp + 3 days);
GovernanceTimelock(contractAddress[DST_CHAIN_ID][bytes("TIMELOCK")]).executeTransaction(
txId, finalTarget, value, data, eta
);

for (uint256 j; j < adaptersToAdd.length; ++j) {
bool isTrusted =
MultiMessageReceiver(contractAddress[137][bytes("MMA_RECEIVER")]).isTrustedExecutor(adaptersToAdd[j]);
bool isTrusted = MultiMessageReceiver(contractAddress[DST_CHAIN_ID][bytes("MMA_RECEIVER")])
.isTrustedExecutor(adaptersToAdd[j]);
assert(isTrusted);
}
}
Expand Down
38 changes: 20 additions & 18 deletions test/integration-tests/RemoteAdapterRemove.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ import {GovernanceTimelock} from "src/controllers/GovernanceTimelock.sol";
/// @dev scenario: admin updates sender adapters on dst chain using message from source chain
/// @notice handles both single add and multiple remove
contract RemoteAdapterRemove is Setup {
/// @dev intializes the setup
/// @dev initializes the setup
function setUp() public override {
super.setUp();
}

/// @dev just remove one adapter and assert
function test_remoteRemoveReceiverAdapterSingle() public {
address[] memory adaptersToRemove = new address[](1);
adaptersToRemove[0] = contractAddress[137]["AXELAR_RECEIVER_ADAPTER"];
adaptersToRemove[0] = contractAddress[DST_CHAIN_ID]["AXELAR_RECEIVER_ADAPTER"];

/// true = add
/// false = remove
Expand All @@ -41,8 +41,8 @@ contract RemoteAdapterRemove is Setup {
_updateDummy();

address[] memory adaptersToRemove = new address[](2);
adaptersToRemove[0] = contractAddress[137]["AXELAR_RECEIVER_ADAPTER"];
adaptersToRemove[1] = contractAddress[137]["WORMHOLE_RECEIVER_ADAPTER"];
adaptersToRemove[0] = contractAddress[DST_CHAIN_ID]["AXELAR_RECEIVER_ADAPTER"];
adaptersToRemove[1] = contractAddress[DST_CHAIN_ID]["WORMHOLE_RECEIVER_ADAPTER"];

/// true = add
/// false = remove
Expand All @@ -61,9 +61,9 @@ contract RemoteAdapterRemove is Setup {

/// send cross-chain message using MMA infra
vm.recordLogs();
MultiMessageSender(contractAddress[1][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}(
137,
address(contractAddress[137][bytes("MMA_RECEIVER")]),
MultiMessageSender(contractAddress[SRC_CHAIN_ID][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}(
DST_CHAIN_ID,
address(contractAddress[DST_CHAIN_ID][bytes("MMA_RECEIVER")]),
abi.encodeWithSelector(
MultiMessageReceiver.updateQuorumAndReceiverAdapter.selector, newQuorum, adaptersToRemove, operation
),
Expand All @@ -76,30 +76,30 @@ contract RemoteAdapterRemove is Setup {

vm.recordLogs();
/// simulate off-chain actors
_simulatePayloadDelivery(1, 137, logs);
_simulatePayloadDelivery(SRC_CHAIN_ID, DST_CHAIN_ID, logs);
bytes32 msgId = _getMsgId(vm.getRecordedLogs());

vm.selectFork(fork[137]);
vm.selectFork(fork[DST_CHAIN_ID]);
vm.recordLogs();
/// execute the message and move it to governance timelock contract
MultiMessageReceiver(contractAddress[137][bytes("MMA_RECEIVER")]).executeMessage(msgId);
MultiMessageReceiver(contractAddress[DST_CHAIN_ID][bytes("MMA_RECEIVER")]).executeMessage(msgId);
(uint256 txId, address finalTarget, uint256 value, bytes memory data, uint256 eta) =
_getExecParams(vm.getRecordedLogs());

/// increment the time by 2 day (delay time)
vm.warp(block.timestamp + 2 days);
GovernanceTimelock(contractAddress[137][bytes("TIMELOCK")]).executeTransaction(
/// increment the time by 7 days (delay time)
vm.warp(block.timestamp + 7 days);
GovernanceTimelock(contractAddress[DST_CHAIN_ID][bytes("TIMELOCK")]).executeTransaction(
txId, finalTarget, value, data, eta
);

/// @dev validates quorum post update
uint256 currQuorum = MultiMessageReceiver(contractAddress[137][bytes("MMA_RECEIVER")]).quorum();
uint256 currQuorum = MultiMessageReceiver(contractAddress[DST_CHAIN_ID][bytes("MMA_RECEIVER")]).quorum();
assertEq(currQuorum, newQuorum);

/// @dev validates adapters post update
for (uint256 j; j < adaptersToRemove.length; ++j) {
bool isTrusted =
MultiMessageReceiver(contractAddress[137][bytes("MMA_RECEIVER")]).isTrustedExecutor(adaptersToRemove[j]);
bool isTrusted = MultiMessageReceiver(contractAddress[DST_CHAIN_ID][bytes("MMA_RECEIVER")])
.isTrustedExecutor(adaptersToRemove[j]);
assert(!isTrusted);
}
}
Expand All @@ -113,8 +113,10 @@ contract RemoteAdapterRemove is Setup {
bool[] memory operation = new bool[](1);
operation[0] = true;

vm.startPrank(contractAddress[137]["TIMELOCK"]);
MultiMessageReceiver(contractAddress[137]["MMA_RECEIVER"]).updateReceiverAdapters(newDummyAdapter, operation);
vm.startPrank(contractAddress[DST_CHAIN_ID]["TIMELOCK"]);
MultiMessageReceiver(contractAddress[DST_CHAIN_ID]["MMA_RECEIVER"]).updateReceiverAdapters(
newDummyAdapter, operation
);
vm.stopPrank();
}
}
Loading
Loading