diff --git a/src/MultiBridgeMessageSender.sol b/src/MultiBridgeMessageSender.sol index 5e6097f..0556fac 100644 --- a/src/MultiBridgeMessageSender.sol +++ b/src/MultiBridgeMessageSender.sol @@ -146,7 +146,7 @@ contract MultiBridgeMessageSender { /// @param _refundAddress refers to the refund address for any extra native tokens paid /// @param _fees refers to the fees to pay to each sender adapter that is not in the exclusion list specified by _excludedAdapters. /// The fees are in the same order as the sender adapters in the senderAdapters list, after the exclusion list is applied. - /// @param _successThreshold refers to minimum number of bridges required to relay the message + /// @param _successThreshold specifies the minimum number of bridges that must successfully dispatch the message for this call to succeed. /// @param _excludedAdapters are the sender adapters to be excluded from relaying the message, in ascending order by address function remoteCall( uint256 _dstChainId, @@ -248,7 +248,12 @@ contract MultiBridgeMessageSender { function _remoteCall(RemoteCallArgs memory _args) private { (address mmaReceiver, address[] memory adapters) = _checkAndProcessArgs( - _args.dstChainId, _args.target, _args.refundAddress, _args.fees, _args.excludedAdapters + _args.dstChainId, + _args.target, + _args.refundAddress, + _args.successThreshold, + _args.fees, + _args.excludedAdapters ); /// @dev increments nonce @@ -293,6 +298,7 @@ contract MultiBridgeMessageSender { uint256 _dstChainId, address _target, address _refundAddress, + uint256 _successThreshold, uint256[] memory _fees, address[] memory _excludedAdapters ) private view returns (address mmaReceiver, address[] memory adapters) { @@ -323,6 +329,11 @@ contract MultiBridgeMessageSender { if (adapters.length == 0) { revert Error.NO_SENDER_ADAPTER_FOUND(); } + + if (_successThreshold > adapters.length) { + revert Error.MULTI_MESSAGE_SEND_FAILED(); + } + if (adapters.length != _fees.length) { revert Error.INVALID_SENDER_ADAPTER_FEES(); } diff --git a/src/libraries/Error.sol b/src/libraries/Error.sol index 987f716..83e87c5 100644 --- a/src/libraries/Error.sol +++ b/src/libraries/Error.sol @@ -102,7 +102,7 @@ library Error { /// @dev is thrown if contract call is invalid (for axelar) error NOT_APPROVED_BY_GATEWAY(); - /// @dev is thrown if all message bridges fail + /// @dev is thrown if a message could not be sent through a sufficient number of bridges error MULTI_MESSAGE_SEND_FAILED(); /*///////////////////////////////////////////////////////////////// diff --git a/test/unit-tests/MultiBridgeMessageSender.t.sol b/test/unit-tests/MultiBridgeMessageSender.t.sol index 5dad296..3500bf3 100644 --- a/test/unit-tests/MultiBridgeMessageSender.t.sol +++ b/test/unit-tests/MultiBridgeMessageSender.t.sol @@ -94,7 +94,14 @@ contract MultiBridgeMessageSenderTest is Setup { ); sender.remoteCall{value: fees[0] + fees[1]}( - DST_CHAIN_ID, address(42), bytes("42"), 0, expiration, refundAddress, fees, DEFAULT_SUCCESS_THRESHOLD, + DST_CHAIN_ID, + address(42), + bytes("42"), + 0, + expiration, + refundAddress, + fees, + DEFAULT_SUCCESS_THRESHOLD, new address[](0) ); @@ -117,7 +124,14 @@ contract MultiBridgeMessageSenderTest is Setup { (, uint256[] memory fees) = _sortTwoAdaptersWithFees(axelarAdapterAddr, wormholeAdapterAddr, 0.01 ether, wormholeFee); sender.remoteCall{value: nativeValue}( - DST_CHAIN_ID, address(42), bytes("42"), 0, expiration, refundAddress, fees, DEFAULT_SUCCESS_THRESHOLD, + DST_CHAIN_ID, + address(42), + bytes("42"), + 0, + expiration, + refundAddress, + fees, + DEFAULT_SUCCESS_THRESHOLD, new address[](0) ); @@ -249,14 +263,28 @@ contract MultiBridgeMessageSenderTest is Setup { vm.startPrank(caller); vm.expectRevert(Error.INVALID_EXPIRATION_DURATION.selector); sender.remoteCall( - DST_CHAIN_ID, address(42), bytes("42"), 0, invalidExpMin, refundAddress, fees, DEFAULT_SUCCESS_THRESHOLD, - new address[](0) + DST_CHAIN_ID, + address(42), + bytes("42"), + 0, + invalidExpMin, + refundAddress, + fees, + DEFAULT_SUCCESS_THRESHOLD, + excludedAdapters ); vm.expectRevert(Error.INVALID_EXPIRATION_DURATION.selector); sender.remoteCall( - DST_CHAIN_ID, address(42), bytes("42"), 0, invalidExpMax, refundAddress, fees, DEFAULT_SUCCESS_THRESHOLD, - new address[](0) + DST_CHAIN_ID, + address(42), + bytes("42"), + 0, + invalidExpMax, + refundAddress, + fees, + DEFAULT_SUCCESS_THRESHOLD, + excludedAdapters ); // test expiration validation in remoteCall() which accepts excluded adapters @@ -299,8 +327,15 @@ contract MultiBridgeMessageSenderTest is Setup { fees[1] = 0.01 ether; vm.expectRevert(Error.INVALID_REFUND_ADDRESS.selector); sender.remoteCall( - DST_CHAIN_ID, address(42), bytes("42"), 0, EXPIRATION_CONSTANT, address(0), fees, DEFAULT_SUCCESS_THRESHOLD, - new address[](0) + DST_CHAIN_ID, + address(42), + bytes("42"), + 0, + EXPIRATION_CONSTANT, + address(0), + fees, + DEFAULT_SUCCESS_THRESHOLD, + excludedAdapters ); vm.expectRevert(Error.INVALID_REFUND_ADDRESS.selector); @@ -313,7 +348,7 @@ contract MultiBridgeMessageSenderTest is Setup { contractAddress[SRC_CHAIN_ID]["MMA_SENDER"], fees, DEFAULT_SUCCESS_THRESHOLD, - new address[](0) + excludedAdapters ); // test refund address validation in remoteCall() which accepts excluded adapters @@ -377,7 +412,14 @@ contract MultiBridgeMessageSenderTest is Setup { vm.expectRevert(Error.ZERO_CHAIN_ID.selector); sender.remoteCall( - 0, address(42), bytes("42"), 0, EXPIRATION_CONSTANT, refundAddress, fees, DEFAULT_SUCCESS_THRESHOLD, + 0, + address(42), + bytes("42"), + 0, + EXPIRATION_CONSTANT, + refundAddress, + fees, + DEFAULT_SUCCESS_THRESHOLD, new address[](0) ); } @@ -508,7 +550,14 @@ contract MultiBridgeMessageSenderTest is Setup { ); sender.remoteCall{value: 0.1 ether}( - DST_CHAIN_ID, address(42), bytes("42"), 0, expiration, refundAddress, fees, DEFAULT_SUCCESS_THRESHOLD, + DST_CHAIN_ID, + address(42), + bytes("42"), + 0, + expiration, + refundAddress, + fees, + DEFAULT_SUCCESS_THRESHOLD, new address[](0) ); } @@ -746,8 +795,8 @@ contract MultiBridgeMessageSenderTest is Setup { sender.removeSenderAdapters(adapters); } - /// @dev if all message bridges fail (not pay enough fees) - function test_all_MULTI_MESSAGE_SEND_FAILED() public { + /// @dev if the message could not be sent through a sufficient number of bridge + function test_revert_for_insufficient_number_of_bridges() public { vm.startPrank(caller); vm.expectRevert(Error.MULTI_MESSAGE_SEND_FAILED.selector);