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

feat: revert if all message bridges fail #97

Merged
merged 8 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from 4 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
71 changes: 64 additions & 7 deletions src/MultiBridgeMessageSender.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ contract MultiBridgeMessageSender {
uint256 expiration;
address refundAddress;
uint256[] fees;
uint256 successThreshold;
address[] excludedAdapters;
}

Expand Down Expand Up @@ -137,6 +138,44 @@ contract MultiBridgeMessageSender {
EXTERNAL FUNCTIONS
////////////////////////////////////////////////////////////////*/

/// @notice Call a remote function on a destination chain by sending multiple copies of a cross-chain message
/// via all available bridges. This function can only be called by the authorised called configured in the GAC.
/// @dev a fee in native token may be required by each message bridge to send messages. Any native token fee remained
/// will be refunded back to a refund address defined in the _refundAddress parameter.
/// Caller needs to specify fees for each adapter when calling this function.
/// @param _dstChainId is the destination chainId
/// @param _target is the target execution point on the destination 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))
/// @param _expiration refers to the number of seconds that a message remains valid before it is considered stale and can no longer be executed.
/// @param _refundAddress refers to the refund address for any extra native tokens paid
/// @param _fees refers to the fees to pay to each adapter
/// @param _successThreshold refers to minimum number of bridges required to relay the message
function remoteCall(
uint256 _dstChainId,
address _target,
bytes memory _callData,
uint256 _nativeValue,
uint256 _expiration,
address _refundAddress,
uint256[] memory _fees,
uint256 _successThreshold
) external payable onlyCaller validateExpiration(_expiration) {
_remoteCall(
RemoteCallArgs(
_dstChainId,
_target,
_callData,
_nativeValue,
_expiration,
_refundAddress,
_fees,
_successThreshold,
new address[](0)
)
);
}

ermyas marked this conversation as resolved.
Show resolved Hide resolved
/// @param _dstChainId is the destination chainId
/// @param _target is the target execution point on the destination chain
/// @param _callData is the data to be sent to _target by low-level call(eg. address(_target).call(_callData))
Expand All @@ -145,20 +184,30 @@ 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
ermyas marked this conversation as resolved.
Show resolved Hide resolved
/// @param _excludedAdapters are the sender adapters to be excluded from relaying the message, in ascending order by address
function remoteCall(
uint256 _dstChainId,
address _target,
bytes calldata _callData,
bytes memory _callData,
ermyas marked this conversation as resolved.
Show resolved Hide resolved
uint256 _nativeValue,
uint256 _expiration,
address _refundAddress,
uint256[] calldata _fees,
address[] calldata _excludedAdapters
uint256[] memory _fees,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
uint256[] memory _fees,
uint256[] calldata _fees,

uint256 _successThreshold,
address[] memory _excludedAdapters
) external payable onlyCaller validateExpiration(_expiration) {
_remoteCall(
RemoteCallArgs(
_dstChainId, _target, _callData, _nativeValue, _expiration, _refundAddress, _fees, _excludedAdapters
_dstChainId,
_target,
_callData,
_nativeValue,
_expiration,
_refundAddress,
_fees,
_successThreshold,
_excludedAdapters
)
);
}
Expand Down Expand Up @@ -253,7 +302,12 @@ contract MultiBridgeMessageSender {
block.timestamp + _args.expiration
);
bytes32 msgId = MessageLibrary.computeMsgId(message);
bool[] memory adapterSuccess = _dispatchMessages(adapters, mmaReceiver, _args.dstChainId, message, _args.fees);
(bool[] memory adapterSuccess, uint256 successCount) =
_dispatchMessages(adapters, mmaReceiver, _args.dstChainId, message, _args.fees);

if (successCount < _args.successThreshold) {
ermyas marked this conversation as resolved.
Show resolved Hide resolved
revert Error.MULTI_MESSAGE_SEND_FAILED();
}

emit MultiBridgeMessageSent(
msgId,
Expand Down Expand Up @@ -328,15 +382,18 @@ contract MultiBridgeMessageSender {
uint256 _dstChainId,
MessageLibrary.Message memory _message,
uint256[] memory _fees
) private returns (bool[] memory) {
) private returns (bool[] memory, uint256) {
uint256 len = _adapters.length;
bool[] memory successes = new bool[](len);
uint256 successCount;

for (uint256 i; i < len;) {
/// @dev if one bridge is paused, the flow shouldn't be broken
try IMessageSenderAdapter(_adapters[i]).dispatchMessage{value: _fees[i]}(
_dstChainId, _mmaReceiver, abi.encode(_message)
) {
successes[i] = true;
++successCount;
} catch {
successes[i] = false;
emit MessageSendFailed(_adapters[i], _message);
Expand All @@ -346,7 +403,7 @@ contract MultiBridgeMessageSender {
++i;
}
}
return successes;
return (successes, successCount);
}

function _filterAdapters(address[] memory _existings, address[] memory _removals)
Expand Down
3 changes: 3 additions & 0 deletions src/libraries/Error.sol
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ 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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// @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();

/*/////////////////////////////////////////////////////////////////
TIMELOCK ERRORS
////////////////////////////////////////////////////////////////*/
Expand Down
1 change: 1 addition & 0 deletions test/Setup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ abstract contract Setup is Test {
address constant owner = address(420);
address constant refundAddress = address(420420);
ermyas marked this conversation as resolved.
Show resolved Hide resolved
uint256 constant EXPIRATION_CONSTANT = 5 days;
uint256 constant DEFAULT_SUCCESS_THRESHOLD = 2;

/// @dev constants for axelar
address constant ETH_GATEWAY = 0x4F4495243837681061C4743b74B3eEdf548D56A5;
Expand Down
2 changes: 1 addition & 1 deletion test/integration-tests/GracePeriodExpiry.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ contract GracePeriodExpiryTest is Setup {
EXPIRATION_CONSTANT,
refundAddress,
fees,
new address[](0)
DEFAULT_SUCCESS_THRESHOLD
);

Vm.Log[] memory logs = vm.getRecordedLogs();
Expand Down
2 changes: 1 addition & 1 deletion test/integration-tests/MultiMessageAggregation.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ contract MultiBridgeMessageAggregationTest is Setup {
EXPIRATION_CONSTANT,
refundAddress,
fees,
new address[](0)
DEFAULT_SUCCESS_THRESHOLD
);

Vm.Log[] memory logs = vm.getRecordedLogs();
Expand Down
2 changes: 1 addition & 1 deletion test/integration-tests/RemoteAdapterAdd.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ contract RemoteAdapterAdd is Setup {
EXPIRATION_CONSTANT,
refundAddress,
fees,
new address[](0)
DEFAULT_SUCCESS_THRESHOLD
);

Vm.Log[] memory logs = vm.getRecordedLogs();
Expand Down
2 changes: 1 addition & 1 deletion test/integration-tests/RemoteAdapterRemove.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ contract RemoteAdapterRemove is Setup {
EXPIRATION_CONSTANT,
refundAddress,
fees,
new address[](0)
DEFAULT_SUCCESS_THRESHOLD
);

Vm.Log[] memory logs = vm.getRecordedLogs();
Expand Down
2 changes: 1 addition & 1 deletion test/integration-tests/RemoteSetQuorum.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ contract RemoteQuorumUpdate is Setup {
EXPIRATION_CONSTANT,
refundAddress,
fees,
new address[](0)
DEFAULT_SUCCESS_THRESHOLD
);

Vm.Log[] memory logs = vm.getRecordedLogs();
Expand Down
2 changes: 1 addition & 1 deletion test/integration-tests/RemoteTimelockUpdate.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ contract RemoteTimelockUpdate is Setup {
EXPIRATION_CONSTANT,
refundAddress,
fees,
new address[](0)
DEFAULT_SUCCESS_THRESHOLD
);

Vm.Log[] memory logs = vm.getRecordedLogs();
Expand Down
2 changes: 1 addition & 1 deletion test/integration-tests/TimelockCheck.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ contract TimelockCheckTest is Setup {
EXPIRATION_CONSTANT,
refundAddress,
fees,
new address[](0)
DEFAULT_SUCCESS_THRESHOLD
);

Vm.Log[] memory logs = vm.getRecordedLogs();
Expand Down
Loading
Loading