diff --git a/src/MultiMessageReceiver.sol b/src/MultiMessageReceiver.sol index f26fafa..38ef68c 100644 --- a/src/MultiMessageReceiver.sol +++ b/src/MultiMessageReceiver.sol @@ -58,27 +58,14 @@ contract MultiMessageReceiver is IMultiMessageReceiver, ExecutorAware, Initializ ////////////////////////////////////////////////////////////////*/ /// @notice sets the initial parameters - function initialize(address[] calldata _receiverAdapters, uint64 _quorum, address _governanceTimelock) - external - initializer - { - uint256 len = _receiverAdapters.length; - - if (len == 0) { - revert Error.ZERO_RECEIVER_ADAPTER(); - } - - for (uint256 i; i < len;) { - if (_receiverAdapters[i] == address(0)) { - revert Error.ZERO_ADDRESS_INPUT(); - } - - _updateReceiverAdapter(_receiverAdapters[i], true); - - unchecked { - ++i; - } - } + function initialize( + address[] calldata _receiverAdapters, + bool[] calldata _operations, + uint64 _quorum, + address _governanceTimelock + ) external initializer { + /// @dev adds the new receiver adapters before setting quorum and validations + _updateReceiverAdapters(_receiverAdapters, _operations); if (_quorum > trustedExecutor.length || _quorum == 0) { revert Error.INVALID_QUORUM_THRESHOLD(); @@ -180,35 +167,30 @@ contract MultiMessageReceiver is IMultiMessageReceiver, ExecutorAware, Initializ /// @notice Update bridge receiver adapters. /// @dev called by admin to update receiver bridge adapters on all other chains - function updateReceiverAdapter(address[] calldata _receiverAdapters, bool[] calldata _operations) + function updateReceiverAdapters(address[] calldata _receiverAdapters, bool[] calldata _operations) external onlyGovernanceTimelock { - uint256 len = _receiverAdapters.length; - - if (len != _operations.length) { - revert Error.ARRAY_LENGTH_MISMATCHED(); - } - - for (uint256 i; i < len;) { - _updateReceiverAdapter(_receiverAdapters[i], _operations[i]); + _updateReceiverAdapters(_receiverAdapters, _operations); + } - unchecked { - ++i; - } - } + /// @notice Update bridge receiver adapters after quorum update + /// @dev called by admin to update receiver bridge adapters on all other chains + function updateQuorumAndReceiverAdapter( + uint64 _newQuorum, + address[] calldata _receiverAdapters, + bool[] calldata _operations + ) external onlyGovernanceTimelock { + /// @dev updates quorum here + _updateQuorum(_newQuorum); + + /// @dev updates receiver adapter here + _updateReceiverAdapters(_receiverAdapters, _operations); } /// @notice Update power quorum threshold of message execution. function updateQuorum(uint64 _quorum) external onlyGovernanceTimelock { - /// NOTE: should check 2/3 ? - if (_quorum > trustedExecutor.length || _quorum == 0) { - revert Error.INVALID_QUORUM_THRESHOLD(); - } - uint64 oldValue = quorum; - - quorum = _quorum; - emit QuorumUpdated(oldValue, _quorum); + _updateQuorum(_quorum); } /*///////////////////////////////////////////////////////////////// @@ -241,6 +223,40 @@ contract MultiMessageReceiver is IMultiMessageReceiver, ExecutorAware, Initializ PRIVATE/INTERNAL FUNCTIONS ////////////////////////////////////////////////////////////////*/ + function _updateQuorum(uint64 _quorum) internal { + if (_quorum > trustedExecutor.length || _quorum == 0) { + revert Error.INVALID_QUORUM_THRESHOLD(); + } + uint64 oldValue = quorum; + + quorum = _quorum; + emit QuorumUpdated(oldValue, _quorum); + } + + function _updateReceiverAdapters(address[] memory _receiverAdapters, bool[] memory _operations) internal { + uint256 len = _receiverAdapters.length; + + if (len == 0) { + revert Error.ZERO_RECEIVER_ADAPTER(); + } + + if (len != _operations.length) { + revert Error.ARRAY_LENGTH_MISMATCHED(); + } + + for (uint256 i; i < len;) { + if (_receiverAdapters[i] == address(0)) { + revert Error.ZERO_ADDRESS_INPUT(); + } + + _updateReceiverAdapter(_receiverAdapters[i], _operations[i]); + + unchecked { + ++i; + } + } + } + function _updateReceiverAdapter(address _receiverAdapter, bool _add) private { if (_add) { _addTrustedExecutor(_receiverAdapter); diff --git a/src/MultiMessageSender.sol b/src/MultiMessageSender.sol index d9fa25b..92198ab 100644 --- a/src/MultiMessageSender.sol +++ b/src/MultiMessageSender.sol @@ -14,8 +14,14 @@ import "./libraries/Error.sol"; /// @title MultiMessageSender /// @dev handles the routing of message from external sender to bridge adapters contract MultiMessageSender { + /*/////////////////////////////////////////////////////////////// + CONSTANTS + //////////////////////////////////////////////////////////////*/ IGAC public immutable gac; + uint256 public constant MINIMUM_EXPIRATION = 2 days; + uint256 public constant MAXIMUM_EXPIRATION = 30 days; + /*///////////////////////////////////////////////////////////////// STATE VARIABLES ////////////////////////////////////////////////////////////////*/ @@ -70,6 +76,18 @@ contract MultiMessageSender { _; } + /// @dev validates the expiration provided by the user + modifier validateExpiration(uint256 _expiration) { + if (_expiration < MINIMUM_EXPIRATION) { + revert Error.INVALID_EXPIRATION_MIN(); + } + + if (_expiration > MAXIMUM_EXPIRATION) { + revert Error.INVALID_EXPIRATION_MAX(); + } + _; + } + /*///////////////////////////////////////////////////////////////// CONSTRUCTOR ////////////////////////////////////////////////////////////////*/ @@ -98,13 +116,16 @@ contract MultiMessageSender { /// @param _target is the target execution point on dst chain /// @param _callData is the data to be sent to _target by low-level call(eg. address(_target).call(_callData)) /// @param _nativeValue is the value to be sent to _target by low-level call (eg. address(_target).call{value: _nativeValue}(_callData)) - function remoteCall(uint256 _dstChainId, address _target, bytes calldata _callData, uint256 _nativeValue) - external - payable - onlyCaller - { + /// @param _expiration refers to the number of days that a message remains valid before it is considered stale and can no longer be executed. + function remoteCall( + uint256 _dstChainId, + address _target, + bytes calldata _callData, + uint256 _nativeValue, + uint256 _expiration + ) external payable onlyCaller { address[] memory excludedAdapters; - _remoteCall(_dstChainId, _target, _callData, _nativeValue, excludedAdapters); + _remoteCall(_dstChainId, _target, _callData, _nativeValue, _expiration, excludedAdapters); } /// @param _dstChainId is the destination chainId @@ -112,14 +133,16 @@ contract MultiMessageSender { /// @param _callData is the data to be sent to _target by low-level call(eg. address(_target).call(_callData)) /// @param _nativeValue is the value to be sent to _target by low-level call (eg. address(_target).call{value: _nativeValue}(_callData)) /// @param _excludedAdapters are the sender adapters to be excluded from relaying the message + /// @param _expiration refers to the number of days that a message remains valid before it is considered stale and can no longer be executed. function remoteCall( uint256 _dstChainId, address _target, bytes calldata _callData, uint256 _nativeValue, + uint256 _expiration, address[] calldata _excludedAdapters ) external payable onlyCaller { - _remoteCall(_dstChainId, _target, _callData, _nativeValue, _excludedAdapters); + _remoteCall(_dstChainId, _target, _callData, _nativeValue, _expiration, _excludedAdapters); } /// @notice Add bridge sender adapters @@ -183,7 +206,6 @@ contract MultiMessageSender { struct LocalCallVars { address[] adapters; uint256 adapterLength; - uint256 msgExpiration; bool[] adapterSuccess; bytes32 msgId; } @@ -193,6 +215,7 @@ contract MultiMessageSender { address _target, bytes calldata _callData, uint256 _nativeValue, + uint256 _expiration, address[] memory _excludedAdapters ) private { LocalCallVars memory v; @@ -247,10 +270,8 @@ contract MultiMessageSender { /// @dev increments nonce ++nonce; - v.msgExpiration = block.timestamp + gac.getMsgExpiryTime(); - MessageLibrary.Message memory message = - MessageLibrary.Message(block.chainid, _dstChainId, _target, nonce, _callData, _nativeValue, v.msgExpiration); + MessageLibrary.Message(block.chainid, _dstChainId, _target, nonce, _callData, _nativeValue, _expiration); v.adapterSuccess = new bool[](v.adapterLength); @@ -284,7 +305,7 @@ contract MultiMessageSender { } emit MultiMessageMsgSent( - v.msgId, nonce, _dstChainId, _target, _callData, _nativeValue, v.msgExpiration, v.adapters, v.adapterSuccess + v.msgId, nonce, _dstChainId, _target, _callData, _nativeValue, _expiration, v.adapters, v.adapterSuccess ); } diff --git a/src/controllers/GAC.sol b/src/controllers/GAC.sol index 3747ad5..e8b3705 100644 --- a/src/controllers/GAC.sol +++ b/src/controllers/GAC.sol @@ -15,7 +15,6 @@ contract GAC is IGAC, Ownable { STATE VARIABLES //////////////////////////////////////////////////////////////*/ uint256 public dstGasLimit; - uint256 public msgExpiration; /// @dev is the address to receive value refunds from remoteCall address public refundAddress; @@ -83,15 +82,6 @@ contract GAC is IGAC, Ownable { emit DstGasLimitUpdated(oldLimit, _gasLimit); } - /// @inheritdoc IGAC - function setMsgExpiryTime(uint256 _timeInSeconds) external override onlyOwner { - if (_timeInSeconds == 0) { - revert Error.ZERO_EXPIRATION_TIME(); - } - - msgExpiration = _timeInSeconds; - } - /// @inheritdoc IGAC function setRefundAddress(address _refundAddress) external override onlyOwner { if (_refundAddress == address(0)) { @@ -124,11 +114,6 @@ contract GAC is IGAC, Ownable { _gasLimit = dstGasLimit; } - /// @inheritdoc IGAC - function getMsgExpiryTime() external view override returns (uint256 _expiration) { - _expiration = msgExpiration; - } - /// @inheritdoc IGAC function getRefundAddress() external view override returns (address _refundAddress) { _refundAddress = refundAddress; diff --git a/src/interfaces/IGAC.sol b/src/interfaces/IGAC.sol index 0c36e53..d8b1b57 100644 --- a/src/interfaces/IGAC.sol +++ b/src/interfaces/IGAC.sol @@ -36,10 +36,6 @@ interface IGAC { /// @param _gasLimit is the limit to be set function setGlobalMsgDeliveryGasLimit(uint256 _gasLimit) external; - /// @dev sets the message expiry time - /// @param _timeInSeconds is the expiry time for the message on dst chain - function setMsgExpiryTime(uint256 _timeInSeconds) external; - /// @dev sets the refund address for gas refunds /// @param _refundAddress is the address to receive refunds from MMA sender function setRefundAddress(address _refundAddress) external; @@ -70,9 +66,6 @@ interface IGAC { /// @dev returns the multi message receiver on the chain function getMultiMessageReceiver(uint256 _chainId) external view returns (address _mmaReceiver); - /// @dev returns the expiry time of message from the time of dispatch - function getMsgExpiryTime() external view returns (uint256 _expiration); - /// @dev returns the refund address function getRefundAddress() external view returns (address _refundAddress); } diff --git a/src/libraries/Error.sol b/src/libraries/Error.sol index fca753f..94ba3b6 100644 --- a/src/libraries/Error.sol +++ b/src/libraries/Error.sol @@ -11,6 +11,9 @@ library Error { /// @dev is thrown when input is zero address error ZERO_ADDRESS_INPUT(); + /// @dev is thrown when quorum is zero + error ZERO_QUORUM(); + /// @dev is thrown if the length of two arrays are mismatched error ARRAY_LENGTH_MISMATCHED(); @@ -35,9 +38,6 @@ library Error { /// @dev is thrown if bridge adapter already delivered the message to multi message receiver error DUPLICATE_MESSAGE_DELIVERY_BY_ADAPTER(); - /// @dev is thrown if expiration time is zero - error ZERO_EXPIRATION_TIME(); - /// @dev is thrown if quorum threshold is greater than receiver adapters error INVALID_QUORUM_THRESHOLD(); @@ -62,6 +62,12 @@ library Error { /// @dev is thrown if caller is not admin of timelock error CALLER_NOT_ADMIN(); + /// @dev is thrown if the expiration is less than minimum expiration + error INVALID_EXPIRATION_MIN(); + + /// @dev is thrown if the delay is more than maximum delay + error INVALID_EXPIRATION_MAX(); + /*///////////////////////////////////////////////////////////////// ADAPTER ERRORS ////////////////////////////////////////////////////////////////*/ diff --git a/test/Setup.t.sol b/test/Setup.t.sol index 59256fa..a264d6a 100644 --- a/test/Setup.t.sol +++ b/test/Setup.t.sol @@ -16,11 +16,11 @@ import {WormholeReceiverAdapter} from "src/adapters/Wormhole/WormholeReceiverAda import {AxelarSenderAdapter} from "src/adapters/axelar/AxelarSenderAdapter.sol"; import {AxelarReceiverAdapter} from "src/adapters/axelar/AxelarReceiverAdapter.sol"; -import {GAC} from "../src/controllers/GAC.sol"; -import {GovernanceTimelock} from "../src/controllers/GovernanceTimelock.sol"; +import {GAC} from "src/controllers/GAC.sol"; +import {GovernanceTimelock} from "src/controllers/GovernanceTimelock.sol"; -import {MultiMessageSender} from "../src/MultiMessageSender.sol"; -import {MultiMessageReceiver} from "../src/MultiMessageReceiver.sol"; +import {MultiMessageSender} from "src/MultiMessageSender.sol"; +import {MultiMessageReceiver} from "src/MultiMessageReceiver.sol"; /// @dev can inherit the setup in tests abstract contract Setup is Test { @@ -32,6 +32,8 @@ abstract contract Setup is Test { /// @dev simulated caller address constant caller = address(10); address constant owner = address(420); + address constant refundAddress = address(420420); + uint256 constant EXPIRATION_CONSTANT = 5 days; /// @dev constants for axelar address constant ETH_GATEWAY = 0x4F4495243837681061C4743b74B3eEdf548D56A5; @@ -123,7 +125,6 @@ abstract contract Setup is Test { vm.selectFork(fork[chainId]); GAC gac = new GAC{salt: _salt}(); - gac.setMsgExpiryTime(2 days); gac.setMultiMessageCaller(caller); contractAddress[chainId][bytes("GAC")] = address(gac); @@ -280,8 +281,12 @@ abstract contract Setup is Test { _recieverAdapters[0] = contractAddress[chainId][bytes("WORMHOLE_RECEIVER_ADAPTER")]; _recieverAdapters[1] = contractAddress[chainId][bytes("AXELAR_RECEIVER_ADAPTER")]; + bool[] memory _operations = new bool[](2); + _operations[0] = true; + _operations[1] = true; + MultiMessageReceiver(contractAddress[DST_CHAINS[i]][bytes("MMA_RECEIVER")]).initialize( - _recieverAdapters, 2, contractAddress[chainId]["TIMELOCK"] + _recieverAdapters, _operations, 2, contractAddress[chainId]["TIMELOCK"] ); unchecked { @@ -308,7 +313,7 @@ abstract contract Setup is Test { GAC(contractAddress[chainId][bytes("GAC")]).setMultiMessageReceiver( ALL_CHAINS[j], contractAddress[ALL_CHAINS[j]][bytes("MMA_RECEIVER")] ); - GAC(contractAddress[chainId][bytes("GAC")]).setRefundAddress(caller); + GAC(contractAddress[chainId][bytes("GAC")]).setRefundAddress(refundAddress); } unchecked { diff --git a/test/failing-tests/SimpleMMA.GracePeriodExpiry.t.sol b/test/integration-tests/GracePeriodExpiry.t.sol similarity index 83% rename from test/failing-tests/SimpleMMA.GracePeriodExpiry.t.sol rename to test/integration-tests/GracePeriodExpiry.t.sol index d4dd023..906ece6 100644 --- a/test/failing-tests/SimpleMMA.GracePeriodExpiry.t.sol +++ b/test/integration-tests/GracePeriodExpiry.t.sol @@ -5,15 +5,16 @@ pragma solidity >=0.8.9; import {Vm} from "forge-std/Test.sol"; /// local imports -import "../Setup.t.sol"; -import "../contracts-mock/MockUniswapReceiver.sol"; +import "test/Setup.t.sol"; +import "test/contracts-mock/MockUniswapReceiver.sol"; import {MultiMessageSender} from "src/MultiMessageSender.sol"; import {MultiMessageReceiver} from "src/MultiMessageReceiver.sol"; import {Error} from "src/libraries/Error.sol"; import {GovernanceTimelock} from "src/controllers/GovernanceTimelock.sol"; -contract GracePeriodExpiry is Setup { +/// @dev scenario: tries to execute the txId after grace period ends +contract GracePeriodExpiryTest is Setup { MockUniswapReceiver target; /// @dev intializes the setup @@ -24,7 +25,6 @@ contract GracePeriodExpiry is Setup { target = new MockUniswapReceiver(); } - /// @dev just sends a message function test_timelockCheck() public { vm.selectFork(fork[1]); vm.startPrank(caller); @@ -32,7 +32,11 @@ contract GracePeriodExpiry is Setup { /// send cross-chain message using MMA infra vm.recordLogs(); MultiMessageSender(contractAddress[1][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}( - 137, address(target), abi.encode(MockUniswapReceiver.setValue.selector, ""), 0 + 137, + address(target), + abi.encode(MockUniswapReceiver.setValue.selector, ""), + 0, + block.timestamp + EXPIRATION_CONSTANT ); Vm.Log[] memory logs = vm.getRecordedLogs(); diff --git a/test/success-tests/SimpleMMA.t.sol b/test/integration-tests/MultiMessageAggregation.t.sol similarity index 83% rename from test/success-tests/SimpleMMA.t.sol rename to test/integration-tests/MultiMessageAggregation.t.sol index f5c8799..c4ac948 100644 --- a/test/success-tests/SimpleMMA.t.sol +++ b/test/integration-tests/MultiMessageAggregation.t.sol @@ -5,15 +5,15 @@ pragma solidity >=0.8.9; import {Vm} from "forge-std/Test.sol"; /// local imports -import "../Setup.t.sol"; -import "../contracts-mock/MockUniswapReceiver.sol"; +import "test/Setup.t.sol"; +import "test/contracts-mock/MockUniswapReceiver.sol"; import {MultiMessageSender} from "src/MultiMessageSender.sol"; import {MultiMessageReceiver} from "src/MultiMessageReceiver.sol"; import {Error} from "src/libraries/Error.sol"; import {GovernanceTimelock} from "src/controllers/GovernanceTimelock.sol"; -contract MMA is Setup { +contract MultiMessageAggregationTest is Setup { MockUniswapReceiver target; /// @dev intializes the setup @@ -25,14 +25,18 @@ contract MMA is Setup { } /// @dev just sends a message - function test_mma_send_receive() public { + function test_mmaSendMessage() public { vm.selectFork(fork[1]); vm.startPrank(caller); /// send cross-chain message using MMA infra vm.recordLogs(); MultiMessageSender(contractAddress[1][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}( - 137, address(target), abi.encode(MockUniswapReceiver.setValue.selector, ""), 0 + 137, + address(target), + abi.encode(MockUniswapReceiver.setValue.selector, ""), + 0, + block.timestamp + EXPIRATION_CONSTANT ); Vm.Log[] memory logs = vm.getRecordedLogs(); diff --git a/test/integration-tests/RemoteAdapterAdd.t.sol b/test/integration-tests/RemoteAdapterAdd.t.sol new file mode 100644 index 0000000..246d85c --- /dev/null +++ b/test/integration-tests/RemoteAdapterAdd.t.sol @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity >=0.8.9; + +/// library imports +import {Vm} from "forge-std/Test.sol"; + +/// local imports +import "test/Setup.t.sol"; + +import {MultiMessageSender} from "src/MultiMessageSender.sol"; +import {MultiMessageReceiver} from "src/MultiMessageReceiver.sol"; +import {Error} from "src/libraries/Error.sol"; +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 + function setUp() public override { + super.setUp(); + } + + /// @dev just add one adapter and assert + function test_remoteAddReceiverAdapterSingle() public { + address[] memory adaptersToAdd = new address[](1); + adaptersToAdd[0] = address(420421422); + + /// true = add + /// false = remove + bool[] memory operation = new bool[](1); + operation[0] = true; + + _adapterAdd(adaptersToAdd, operation); + } + + /// @dev add multiple adapters and assert + function test_remoteAddReceiverAdapterMulti() public { + address[] memory adaptersToAdd = new address[](3); + adaptersToAdd[0] = address(42042142232313); + adaptersToAdd[1] = address(22132131); + adaptersToAdd[2] = address(22132132131); + + /// true = add + /// false = remove + bool[] memory operation = new bool[](3); + operation[0] = true; + operation[1] = true; + operation[2] = true; + + _adapterAdd(adaptersToAdd, operation); + } + + function _adapterAdd(address[] memory adaptersToAdd, bool[] memory operation) internal { + vm.selectFork(fork[1]); + vm.startPrank(caller); + + /// 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")]), + abi.encodeWithSelector(MultiMessageReceiver.updateReceiverAdapters.selector, adaptersToAdd, operation), + 0, + block.timestamp + EXPIRATION_CONSTANT + ); + + Vm.Log[] memory logs = vm.getRecordedLogs(); + vm.stopPrank(); + + vm.recordLogs(); + /// simulate off-chain actors + _simulatePayloadDelivery(1, 137, logs); + bytes32 msgId = _getMsgId(vm.getRecordedLogs()); + + vm.selectFork(fork[137]); + vm.recordLogs(); + /// execute the message and move it to governance timelock contract + MultiMessageReceiver(contractAddress[137][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( + txId, finalTarget, value, data, eta + ); + + for (uint256 j; j < adaptersToAdd.length; ++j) { + bool isTrusted = + MultiMessageReceiver(contractAddress[137][bytes("MMA_RECEIVER")]).isTrustedExecutor(adaptersToAdd[j]); + assert(isTrusted); + } + } +} diff --git a/test/integration-tests/RemoteAdapterRemove.t.sol b/test/integration-tests/RemoteAdapterRemove.t.sol new file mode 100644 index 0000000..818d7e4 --- /dev/null +++ b/test/integration-tests/RemoteAdapterRemove.t.sol @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity >=0.8.9; + +/// library imports +import {Vm} from "forge-std/Test.sol"; + +/// local imports +import "test/Setup.t.sol"; + +import {MultiMessageSender} from "src/MultiMessageSender.sol"; +import {MultiMessageReceiver} from "src/MultiMessageReceiver.sol"; +import {Error} from "src/libraries/Error.sol"; +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 + 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"]; + + /// true = add + /// false = remove + bool[] memory operation = new bool[](1); + operation[0] = false; + + uint256 newQuorum = 1; + + _adapterRemove(newQuorum, adaptersToRemove, operation); + } + + /// @dev add multiple adapters and assert + function test_remoteRemoveReceiverAdapterMulti() public { + /// @dev adds a dummy adapter since quorum threshold can never be 0 + _updateDummy(); + + address[] memory adaptersToRemove = new address[](2); + adaptersToRemove[0] = contractAddress[137]["AXELAR_RECEIVER_ADAPTER"]; + adaptersToRemove[1] = contractAddress[137]["WORMHOLE_RECEIVER_ADAPTER"]; + + /// true = add + /// false = remove + bool[] memory operation = new bool[](2); + operation[0] = false; + operation[1] = false; + + uint256 newQuorum = 1; + + _adapterRemove(newQuorum, adaptersToRemove, operation); + } + + function _adapterRemove(uint256 newQuorum, address[] memory adaptersToRemove, bool[] memory operation) internal { + vm.selectFork(fork[1]); + vm.startPrank(caller); + + /// 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")]), + abi.encodeWithSelector( + MultiMessageReceiver.updateQuorumAndReceiverAdapter.selector, newQuorum, adaptersToRemove, operation + ), + 0, + block.timestamp + EXPIRATION_CONSTANT + ); + + Vm.Log[] memory logs = vm.getRecordedLogs(); + vm.stopPrank(); + + vm.recordLogs(); + /// simulate off-chain actors + _simulatePayloadDelivery(1, 137, logs); + bytes32 msgId = _getMsgId(vm.getRecordedLogs()); + + vm.selectFork(fork[137]); + vm.recordLogs(); + /// execute the message and move it to governance timelock contract + MultiMessageReceiver(contractAddress[137][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( + txId, finalTarget, value, data, eta + ); + + /// @dev validates quorum post update + uint256 currQuorum = MultiMessageReceiver(contractAddress[137][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]); + assert(!isTrusted); + } + } + + function _updateDummy() internal { + address[] memory newDummyAdapter = new address[](1); + newDummyAdapter[0] = address(420); + + /// true = add + /// false = remove + bool[] memory operation = new bool[](1); + operation[0] = true; + + vm.startPrank(contractAddress[137]["TIMELOCK"]); + MultiMessageReceiver(contractAddress[137]["MMA_RECEIVER"]).updateReceiverAdapters(newDummyAdapter, operation); + vm.stopPrank(); + } +} diff --git a/test/integration-tests/RemoteSetQuorum.t.sol b/test/integration-tests/RemoteSetQuorum.t.sol new file mode 100644 index 0000000..18d089e --- /dev/null +++ b/test/integration-tests/RemoteSetQuorum.t.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity >=0.8.9; + +/// library imports +import {Vm} from "forge-std/Test.sol"; + +/// local imports +import "test/Setup.t.sol"; + +import {MultiMessageSender} from "src/MultiMessageSender.sol"; +import {MultiMessageReceiver} from "src/MultiMessageReceiver.sol"; +import {Error} from "src/libraries/Error.sol"; +import {GovernanceTimelock} from "src/controllers/GovernanceTimelock.sol"; + +/// @dev scenario: admin updates quorum on dst chain using message from source chain +contract RemoteQuorumUpdate is Setup { + /// @dev intializes the setup + function setUp() public override { + super.setUp(); + } + + /// @dev just set remote chain quorum to 1 from 2 (done in setup) + function test_remoteQuorumUpdate() public { + uint256 newQuorum = 1; + + vm.selectFork(fork[1]); + vm.startPrank(caller); + + /// 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")]), + abi.encodeWithSelector(MultiMessageReceiver.updateQuorum.selector, newQuorum), + 0, + block.timestamp + EXPIRATION_CONSTANT + ); + + Vm.Log[] memory logs = vm.getRecordedLogs(); + vm.stopPrank(); + + vm.recordLogs(); + + /// simulate off-chain actors + _simulatePayloadDelivery(1, 137, logs); + bytes32 msgId = _getMsgId(vm.getRecordedLogs()); + + vm.selectFork(fork[137]); + vm.recordLogs(); + /// execute the message and move it to governance timelock contract + MultiMessageReceiver(contractAddress[137][bytes("MMA_RECEIVER")]).executeMessage(msgId); + (uint256 txId, address finalTarget, uint256 value, bytes memory data, uint256 eta) = + _getExecParams(vm.getRecordedLogs()); + + uint256 oldQuorum = MultiMessageReceiver(contractAddress[137][bytes("MMA_RECEIVER")]).quorum(); + assertEq(oldQuorum, 2); + + /// increment the time by 2 day (delay time) + vm.warp(block.timestamp + 2 days); + GovernanceTimelock(contractAddress[137][bytes("TIMELOCK")]).executeTransaction( + txId, finalTarget, value, data, eta + ); + + uint256 currQuorum = MultiMessageReceiver(contractAddress[137][bytes("MMA_RECEIVER")]).quorum(); + assertEq(currQuorum, newQuorum); + } +} diff --git a/test/integration-tests/RemoteTimelockUpdate.t.sol b/test/integration-tests/RemoteTimelockUpdate.t.sol new file mode 100644 index 0000000..60eb9da --- /dev/null +++ b/test/integration-tests/RemoteTimelockUpdate.t.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity >=0.8.9; + +/// library imports +import {Vm} from "forge-std/Test.sol"; + +/// local imports +import "test/Setup.t.sol"; + +import {MultiMessageSender} from "src/MultiMessageSender.sol"; +import {MultiMessageReceiver} from "src/MultiMessageReceiver.sol"; +import {Error} from "src/libraries/Error.sol"; +import {GovernanceTimelock} from "src/controllers/GovernanceTimelock.sol"; + +/// @dev scenario: admin updates timelock delay on dst chain using message from source chain +contract RemoteTimelockUpdate is Setup { + /// @dev intializes the setup + function setUp() public override { + super.setUp(); + } + + /// @dev just set timelock delay to 19 days and assert + function test_remoteTimelockUpdate() public { + uint256 newDelay = 19 days; + + vm.selectFork(fork[1]); + vm.startPrank(caller); + + /// send cross-chain message using MMA infra + vm.recordLogs(); + MultiMessageSender(contractAddress[1][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}( + 137, + address(contractAddress[137][bytes("TIMELOCK")]), + abi.encodeWithSelector(GovernanceTimelock.setDelay.selector, newDelay), + 0, + block.timestamp + EXPIRATION_CONSTANT + ); + + Vm.Log[] memory logs = vm.getRecordedLogs(); + vm.stopPrank(); + + vm.recordLogs(); + + /// simulate off-chain actors + _simulatePayloadDelivery(1, 137, logs); + bytes32 msgId = _getMsgId(vm.getRecordedLogs()); + + vm.selectFork(fork[137]); + vm.recordLogs(); + /// execute the message and move it to governance timelock contract + MultiMessageReceiver(contractAddress[137][bytes("MMA_RECEIVER")]).executeMessage(msgId); + (uint256 txId, address finalTarget, uint256 value, bytes memory data, uint256 eta) = + _getExecParams(vm.getRecordedLogs()); + + uint256 oldDelay = GovernanceTimelock(contractAddress[137][bytes("TIMELOCK")]).delay(); + assertEq(oldDelay, GovernanceTimelock(contractAddress[137][bytes("TIMELOCK")]).MINIMUM_DELAY()); + + /// increment the time by 2 day (delay time) + vm.warp(block.timestamp + 2 days); + GovernanceTimelock(contractAddress[137][bytes("TIMELOCK")]).executeTransaction( + txId, finalTarget, value, data, eta + ); + + uint256 currDelay = GovernanceTimelock(contractAddress[137][bytes("TIMELOCK")]).delay(); + assertEq(currDelay, newDelay); + } +} diff --git a/test/failing-tests/SimpleMMA.TimelockCheck.t.sol b/test/integration-tests/TimelockCheck.t.sol similarity index 83% rename from test/failing-tests/SimpleMMA.TimelockCheck.t.sol rename to test/integration-tests/TimelockCheck.t.sol index 58fec46..5227504 100644 --- a/test/failing-tests/SimpleMMA.TimelockCheck.t.sol +++ b/test/integration-tests/TimelockCheck.t.sol @@ -5,15 +5,17 @@ pragma solidity >=0.8.9; import {Vm} from "forge-std/Test.sol"; /// local imports -import "../Setup.t.sol"; -import "../contracts-mock/MockUniswapReceiver.sol"; +import "test/Setup.t.sol"; +import "test/contracts-mock/MockUniswapReceiver.sol"; import {MultiMessageSender} from "src/MultiMessageSender.sol"; import {MultiMessageReceiver} from "src/MultiMessageReceiver.sol"; import {Error} from "src/libraries/Error.sol"; import {GovernanceTimelock} from "src/controllers/GovernanceTimelock.sol"; -contract TimelockCheck is Setup { +/// @dev scenario 1: tries to execute the txId before timelock ends +/// @dev scenario 2: tries to execute the txId post timelock ends and within expiry +contract TimelockCheckTest is Setup { MockUniswapReceiver target; /// @dev intializes the setup @@ -32,7 +34,11 @@ contract TimelockCheck is Setup { /// send cross-chain message using MMA infra vm.recordLogs(); MultiMessageSender(contractAddress[1][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}( - 137, address(target), abi.encode(MockUniswapReceiver.setValue.selector, ""), 0 + 137, + address(target), + abi.encode(MockUniswapReceiver.setValue.selector, ""), + 0, + block.timestamp + EXPIRATION_CONSTANT ); Vm.Log[] memory logs = vm.getRecordedLogs(); diff --git a/test/unit-tests/MultiMessageReceiver.t.sol b/test/unit-tests/MultiMessageReceiver.t.sol index ac2dda0..2d0f09e 100644 --- a/test/unit-tests/MultiMessageReceiver.t.sol +++ b/test/unit-tests/MultiMessageReceiver.t.sol @@ -5,8 +5,8 @@ pragma solidity >=0.8.9; import {Vm} from "forge-std/Test.sol"; /// local imports -import "../Setup.t.sol"; -import "../contracts-mock/MockUniswapReceiver.sol"; +import "test/Setup.t.sol"; +import "test/contracts-mock/MockUniswapReceiver.sol"; import "src/adapters/Wormhole/WormholeReceiverAdapter.sol"; import "src/libraries/Error.sol"; import "src/libraries/Message.sol"; @@ -43,8 +43,12 @@ contract MultiMessageReceiverTest is Setup { adapters[0] = wormholeAdapterAddr; adapters[1] = axelarAdapterAddr; + bool[] memory operation = new bool[](2); + operation[0] = true; + operation[1] = true; + MultiMessageReceiver dummyReceiver = new MultiMessageReceiver(); - dummyReceiver.initialize(adapters, 2, timelockAddr); + dummyReceiver.initialize(adapters, operation, 2, timelockAddr); assertEq(dummyReceiver.quorum(), 2); assertEq(dummyReceiver.trustedExecutor(0), wormholeAdapterAddr); @@ -56,7 +60,7 @@ contract MultiMessageReceiverTest is Setup { vm.startPrank(caller); vm.expectRevert("Initializable: contract is already initialized"); - receiver.initialize(new address[](0), 0, address(0)); + receiver.initialize(new address[](0), new bool[](0), 0, address(0)); } /// @dev cannot be called with zero adapter @@ -66,7 +70,7 @@ contract MultiMessageReceiverTest is Setup { MultiMessageReceiver dummyReceiver = new MultiMessageReceiver(); vm.expectRevert(Error.ZERO_RECEIVER_ADAPTER.selector); - dummyReceiver.initialize(new address[](0), 0, address(0)); + dummyReceiver.initialize(new address[](0), new bool[](0), 0, address(0)); } /// @dev cannot be called with zero address adapter @@ -77,8 +81,11 @@ contract MultiMessageReceiverTest is Setup { address[] memory adapters = new address[](1); adapters[0] = address(0); + bool[] memory operation = new bool[](1); + operation[0] = true; + vm.expectRevert(Error.ZERO_ADDRESS_INPUT.selector); - dummyReceiver.initialize(adapters, 1, timelockAddr); + dummyReceiver.initialize(adapters, operation, 1, timelockAddr); } /// @dev quorum cannot be larger than the number of receiver adapters @@ -89,8 +96,11 @@ contract MultiMessageReceiverTest is Setup { address[] memory adapters = new address[](1); adapters[0] = address(42); + bool[] memory operation = new bool[](1); + operation[0] = true; + vm.expectRevert(Error.INVALID_QUORUM_THRESHOLD.selector); - dummyReceiver.initialize(adapters, 2, timelockAddr); + dummyReceiver.initialize(adapters, operation, 2, timelockAddr); } /// @dev quorum cannot be larger than the number of unique receiver adapters @@ -102,8 +112,12 @@ contract MultiMessageReceiverTest is Setup { adapters[0] = address(42); adapters[1] = address(42); + bool[] memory operation = new bool[](2); + operation[0] = true; + operation[1] = true; + vm.expectRevert(Error.INVALID_QUORUM_THRESHOLD.selector); - dummyReceiver.initialize(adapters, 2, timelockAddr); + dummyReceiver.initialize(adapters, operation, 2, timelockAddr); } /// @dev initializer quorum cannot be zero @@ -114,8 +128,11 @@ contract MultiMessageReceiverTest is Setup { address[] memory adapters = new address[](1); adapters[0] = address(42); + bool[] memory operation = new bool[](1); + operation[0] = true; + vm.expectRevert(Error.INVALID_QUORUM_THRESHOLD.selector); - dummyReceiver.initialize(adapters, 0, timelockAddr); + dummyReceiver.initialize(adapters, operation, 0, timelockAddr); } /// @dev governance timelock cannot be zero address @@ -126,8 +143,11 @@ contract MultiMessageReceiverTest is Setup { address[] memory adapters = new address[](1); adapters[0] = address(42); + bool[] memory operation = new bool[](1); + operation[0] = true; + vm.expectRevert(Error.ZERO_GOVERNANCE_TIMELOCK.selector); - dummyReceiver.initialize(adapters, 1, address(0)); + dummyReceiver.initialize(adapters, operation, 1, address(0)); } /// @dev receives message from one adapter @@ -419,7 +439,7 @@ contract MultiMessageReceiverTest is Setup { vm.expectEmit(true, true, true, true, address(receiver)); emit ReceiverAdapterUpdated(address(42), true); - receiver.updateReceiverAdapter(updatedAdapters, operations); + receiver.updateReceiverAdapters(updatedAdapters, operations); assertEq(receiver.trustedExecutor(0), wormholeAdapterAddr); assertEq(receiver.trustedExecutor(1), axelarAdapterAddr); @@ -441,7 +461,7 @@ contract MultiMessageReceiverTest is Setup { vm.expectEmit(true, true, true, true, address(receiver)); emit ReceiverAdapterUpdated(wormholeAdapterAddr, false); - receiver.updateReceiverAdapter(updatedAdapters, operations); + receiver.updateReceiverAdapters(updatedAdapters, operations); assertEq(receiver.trustedExecutor(0), axelarAdapterAddr); } @@ -450,7 +470,7 @@ contract MultiMessageReceiverTest is Setup { vm.startPrank(caller); vm.expectRevert(Error.CALLER_NOT_GOVERNANCE_TIMELOCK.selector); - receiver.updateReceiverAdapter(new address[](0), new bool[](0)); + receiver.updateReceiverAdapters(new address[](0), new bool[](0)); } /// @dev adapters and operations length mismatched @@ -458,9 +478,12 @@ contract MultiMessageReceiverTest is Setup { vm.startPrank(timelockAddr); vm.expectRevert(Error.ARRAY_LENGTH_MISMATCHED.selector); - bool[] memory operations = new bool[](1); - operations[0] = true; - receiver.updateReceiverAdapter(new address[](0), operations); + address[] memory adapters = new address[](1); + adapters[0] = address(420); + + bool[] memory operations = new bool[](2); + + receiver.updateReceiverAdapters(adapters, operations); } /// @dev cannot remove one receiver adapter without reducing quorum first @@ -472,7 +495,7 @@ contract MultiMessageReceiverTest is Setup { bool[] memory operations = new bool[](1); operations[0] = false; vm.expectRevert(Error.INVALID_QUORUM_THRESHOLD.selector); - receiver.updateReceiverAdapter(updatedAdapters, operations); + receiver.updateReceiverAdapters(updatedAdapters, operations); } /// @dev updates quorum @@ -484,7 +507,7 @@ contract MultiMessageReceiverTest is Setup { updatedAdapters[0] = address(42); bool[] memory operations = new bool[](1); operations[0] = true; - receiver.updateReceiverAdapter(updatedAdapters, operations); + receiver.updateReceiverAdapters(updatedAdapters, operations); vm.expectEmit(true, true, true, true, address(receiver)); emit QuorumUpdated(2, 3); @@ -517,6 +540,38 @@ contract MultiMessageReceiverTest is Setup { receiver.updateQuorum(0); } + /// @dev valid quorum and receiver updater in one single call + function test_quorum_and_receiver_updater() public { + vm.startPrank(timelockAddr); + + address[] memory adapters = new address[](2); + adapters[0] = address(420); + adapters[1] = address(421); + + bool[] memory addOps = new bool[](2); + addOps[0] = true; + addOps[1] = true; + + /// @dev adds the adapters before removal + receiver.updateReceiverAdapters(adapters, addOps); + + /// @dev asserts the quorum and adapter lengths + assertEq(receiver.isTrustedExecutor(adapters[0]), true); + assertEq(receiver.isTrustedExecutor(adapters[1]), true); + + adapters = new address[](1); + adapters[0] = address(420); + + uint64 newQuorum = 1; + + /// @dev removes the newly updated adapter by reducing quorum by one + receiver.updateQuorumAndReceiverAdapter(newQuorum, adapters, new bool[](1)); + + /// @dev asserts the quorum and adapter lengths + assertEq(receiver.quorum(), newQuorum); + assertEq(receiver.isTrustedExecutor(adapters[0]), false); + } + /// @dev should get message info function test_get_message_info() public { vm.startPrank(wormholeAdapterAddr); diff --git a/test/unit-tests/MultiMessageSender.t.sol b/test/unit-tests/MultiMessageSender.t.sol index 58b6e5f..42c1e77 100644 --- a/test/unit-tests/MultiMessageSender.t.sol +++ b/test/unit-tests/MultiMessageSender.t.sol @@ -5,9 +5,9 @@ pragma solidity >=0.8.9; import {Vm} from "forge-std/Test.sol"; /// local imports -import "../Setup.t.sol"; -import "../contracts-mock/FailingSenderAdapter.sol"; -import "../contracts-mock/ZeroAddressReceiverGac.sol"; +import "test/Setup.t.sol"; +import "test/contracts-mock/FailingSenderAdapter.sol"; +import "test/contracts-mock/ZeroAddressReceiverGac.sol"; import "src/interfaces/IBridgeSenderAdapter.sol"; import "src/interfaces/IMultiMessageReceiver.sol"; import "src/interfaces/IGAC.sol"; @@ -75,7 +75,7 @@ contract MultiMessageSenderTest is Setup { adapterSuccess[0] = true; adapterSuccess[1] = true; - uint256 expiration = block.timestamp + gac.getMsgExpiryTime(); + uint256 expiration = block.timestamp + EXPIRATION_CONSTANT; MessageLibrary.Message memory message = MessageLibrary.Message({ srcChainId: SRC_CHAIN_ID, @@ -95,7 +95,7 @@ contract MultiMessageSenderTest is Setup { msgId, 1, DST_CHAIN_ID, address(42), bytes("42"), 0, expiration, senderAdapters, adapterSuccess ); - sender.remoteCall{value: fee}(DST_CHAIN_ID, address(42), bytes("42"), 0); + sender.remoteCall{value: fee}(DST_CHAIN_ID, address(42), bytes("42"), 0, expiration); assertEq(sender.nonce(), 1); } @@ -104,15 +104,15 @@ contract MultiMessageSenderTest is Setup { function test_remote_call_refund() public { vm.startPrank(caller); - uint256 expiration = block.timestamp + gac.getMsgExpiryTime(); + uint256 expiration = block.timestamp + EXPIRATION_CONSTANT; + uint256 nativeValue = 2 ether; - // NOTE: caller is also configured as the refund address in this test setup uint256 balanceBefore = gac.getRefundAddress().balance; - sender.remoteCall{value: 2 ether}(DST_CHAIN_ID, address(42), bytes("42"), 0); + sender.remoteCall{value: nativeValue}(DST_CHAIN_ID, address(42), bytes("42"), 0, expiration); uint256 balanceAfter = gac.getRefundAddress().balance; uint256 fee = sender.estimateTotalMessageFee(DST_CHAIN_ID, receiver, address(42), bytes("42"), 0); - assertEq(balanceBefore - balanceAfter, fee); + assertEq(balanceAfter - balanceBefore, nativeValue - fee); } /// @dev perform remote call with an excluded adapter @@ -128,7 +128,7 @@ contract MultiMessageSenderTest is Setup { address[] memory excludedAdapters = new address[](1); excludedAdapters[0] = axelarAdapterAddr; - uint256 expiration = block.timestamp + gac.getMsgExpiryTime(); + uint256 expiration = block.timestamp + EXPIRATION_CONSTANT; MessageLibrary.Message memory message = MessageLibrary.Message({ srcChainId: SRC_CHAIN_ID, @@ -149,7 +149,7 @@ contract MultiMessageSenderTest is Setup { msgId, 1, DST_CHAIN_ID, address(42), bytes("42"), 0, expiration, senderAdapters, adapterSuccess ); - sender.remoteCall{value: fee}(DST_CHAIN_ID, address(42), bytes("42"), 0, excludedAdapters); + sender.remoteCall{value: fee}(DST_CHAIN_ID, address(42), bytes("42"), 0, expiration, excludedAdapters); } /// @dev only caller can perform remote call @@ -157,7 +157,7 @@ contract MultiMessageSenderTest is Setup { vm.startPrank(owner); vm.expectRevert(Error.INVALID_PRIVILEGED_CALLER.selector); - sender.remoteCall(DST_CHAIN_ID, address(42), bytes("42"), 0); + sender.remoteCall(DST_CHAIN_ID, address(42), bytes("42"), 0, block.timestamp + EXPIRATION_CONSTANT); } /// @dev cannot call with dst chain ID of 0 @@ -165,7 +165,7 @@ contract MultiMessageSenderTest is Setup { vm.startPrank(caller); vm.expectRevert(Error.ZERO_CHAIN_ID.selector); - sender.remoteCall(0, address(42), bytes("42"), 0); + sender.remoteCall(0, address(42), bytes("42"), 0, block.timestamp + EXPIRATION_CONSTANT); } /// @dev cannot call with target address of 0 @@ -173,7 +173,7 @@ contract MultiMessageSenderTest is Setup { vm.startPrank(caller); vm.expectRevert(Error.INVALID_TARGET.selector); - sender.remoteCall(DST_CHAIN_ID, address(0), bytes("42"), 0); + sender.remoteCall(DST_CHAIN_ID, address(0), bytes("42"), 0, block.timestamp + EXPIRATION_CONSTANT); } /// @dev cannot call with receiver address of 0 @@ -183,7 +183,7 @@ contract MultiMessageSenderTest is Setup { MultiMessageSender dummySender = new MultiMessageSender(address(new ZeroAddressReceiverGac(caller))); vm.expectRevert(Error.ZERO_RECEIVER_ADAPTER.selector); - dummySender.remoteCall(DST_CHAIN_ID, address(42), bytes("42"), 0); + dummySender.remoteCall(DST_CHAIN_ID, address(42), bytes("42"), 0, block.timestamp + EXPIRATION_CONSTANT); } /// @dev cannot call with no sender adapter @@ -200,7 +200,7 @@ contract MultiMessageSenderTest is Setup { vm.startPrank(caller); vm.expectRevert(Error.NO_SENDER_ADAPTER_FOUND.selector); - sender.remoteCall(DST_CHAIN_ID, address(42), bytes("42"), 0); + sender.remoteCall(DST_CHAIN_ID, address(42), bytes("42"), 0, block.timestamp + EXPIRATION_CONSTANT); } /// @dev should proceed with the call despite one failing adapter, emitting an error message @@ -225,7 +225,7 @@ contract MultiMessageSenderTest is Setup { adapterSuccess[1] = true; adapterSuccess[2] = false; - uint256 expiration = block.timestamp + gac.getMsgExpiryTime(); + uint256 expiration = block.timestamp + EXPIRATION_CONSTANT; MessageLibrary.Message memory message = MessageLibrary.Message({ srcChainId: SRC_CHAIN_ID, @@ -248,7 +248,7 @@ contract MultiMessageSenderTest is Setup { msgId, 1, DST_CHAIN_ID, address(42), bytes("42"), 0, expiration, senderAdapters, adapterSuccess ); - sender.remoteCall{value: fee}(DST_CHAIN_ID, address(42), bytes("42"), 0); + sender.remoteCall{value: fee}(DST_CHAIN_ID, address(42), bytes("42"), 0, expiration); } /// @dev adds two sender adapters