Skip to content

Commit

Permalink
Improve MultiMessageSender (#76)
Browse files Browse the repository at this point in the history
- Require the caller to specify fees to each adapter. Removing on-chain
  fee estimation which may not work with some adapters.
- Maintain a senderAdapters array sorted in ascending order by address.
  • Loading branch information
Dominator008 authored Sep 22, 2023
1 parent d7a877c commit 12f460e
Show file tree
Hide file tree
Showing 17 changed files with 510 additions and 247 deletions.
321 changes: 200 additions & 121 deletions src/MultiBridgeMessageSender.sol

Large diffs are not rendered by default.

11 changes: 0 additions & 11 deletions src/adapters/axelar/AxelarSenderAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,6 @@ contract AxelarSenderAdapter is BaseSenderAdapter {
}
}

/*/////////////////////////////////////////////////////////////////
EXTERNAL VIEW FUNCTIONS
////////////////////////////////////////////////////////////////*/

/// @inheritdoc IMessageSenderAdapter
function getMessageFee(uint256, address, bytes calldata) external view override returns (uint256) {
/// FIXME: axelar has no on-chain message fee estimate. should have to overpay and get refund
return 1 ether;
// return axelarChainRegistry.getFee(_toChainId, uint32(gac.getGlobalMsgDeliveryGasLimit()));
}

/*/////////////////////////////////////////////////////////////////
HELPER FUNCTIONS
////////////////////////////////////////////////////////////////*/
Expand Down
10 changes: 0 additions & 10 deletions src/adapters/wormhole/WormholeSenderAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,4 @@ contract WormholeSenderAdapter is BaseSenderAdapter {
}
}
}

/*/////////////////////////////////////////////////////////////////
EXTERNAL VIEW FUNCTIONS
////////////////////////////////////////////////////////////////*/

/// @inheritdoc IMessageSenderAdapter
function getMessageFee(uint256 _toChainId, address, bytes calldata) external view override returns (uint256 fee) {
/// note: 50000 GAS is commonly used across the MMA; move to some global contract
(fee,) = relayer.quoteEVMDeliveryPrice(chainIdMap[_toChainId], 0, senderGAC.getGlobalMsgDeliveryGasLimit());
}
}
6 changes: 0 additions & 6 deletions src/interfaces/adapters/IMessageSenderAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,6 @@ interface IMessageSenderAdapter is SingleMessageDispatcher {
/// @notice returns name of the message bridge wrapped by the adapter
function name() external view returns (string memory);

/// @notice return the fee (in native token wei) that would be charged by the bridge for the provided remote call
/// @param _toChainId is the destination chain id
/// @param _to is the destination address on the destination chain
/// @param _data is the data to be sent to the destination chain
function getMessageFee(uint256 _toChainId, address _to, bytes calldata _data) external view returns (uint256);

/// @notice returns the bridge receiver adapter address for a given destination chain id
/// @param _chainId is the destination chain whose receiver adapter address is to be returned
function getReceiverAdapter(uint256 _chainId) external view returns (address);
Expand Down
9 changes: 9 additions & 0 deletions src/libraries/Error.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ library Error {
/// @dev is thrown if sender adapter array has duplicates
error DUPLICATE_SENDER_ADAPTER();

/// @dev is thrown if the sender adapter does not exist
error SENDER_ADAPTER_NOT_EXIST();

/// @dev is thrown if sender adapter array is not in ascending order or has duplicates
error INVALID_SENDER_ADAPTER_ORDER();

/// @dev is thrown if deadline is lapsed
error MSG_EXECUTION_PASSED_DEADLINE();

Expand Down Expand Up @@ -72,6 +78,9 @@ library Error {
/// @dev is thrown if sender adapter is not allowed on receiver adapter
error INVALID_SENDER_ADAPTER();

/// @dev is thrown if the sender adapter fee array is invalid
error INVALID_SENDER_ADAPTER_FEES();

/// @dev is thrown if final destination is not mma receiver on receiver adapter
error INVALID_FINAL_DESTINATION();

Expand Down
4 changes: 0 additions & 4 deletions test/contracts-mock/FailingSenderAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,4 @@ contract FailingSenderAdapter {
function dispatchMessage(uint256, address, bytes calldata) external payable returns (bytes32) {
revert();
}

function getMessageFee(uint256, address, bytes calldata) external pure returns (uint256) {
return 0;
}
}
9 changes: 8 additions & 1 deletion test/integration-tests/GracePeriodExpiry.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity >=0.8.9;

/// library imports
import {Vm} from "forge-std/Test.sol";
import "wormhole-solidity-sdk/interfaces/IWormholeRelayer.sol";

/// local imports
import "test/Setup.t.sol";
Expand Down Expand Up @@ -31,13 +32,19 @@ contract GracePeriodExpiryTest is Setup {

/// send cross-chain message using MMA infra
vm.recordLogs();
uint256[] memory fees = new uint256[](2);
(uint256 wormholeFee,) =
IWormholeRelayer(POLYGON_RELAYER).quoteEVMDeliveryPrice(_wormholeChainId(DST_CHAIN_ID), 0, 0);
fees[0] = wormholeFee;
fees[1] = 0.01 ether;
MultiBridgeMessageSender(contractAddress[SRC_CHAIN_ID][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}(
DST_CHAIN_ID,
address(target),
abi.encode(MockUniswapReceiver.setValue.selector, ""),
0,
EXPIRATION_CONSTANT,
refundAddress
refundAddress,
fees
);

Vm.Log[] memory logs = vm.getRecordedLogs();
Expand Down
9 changes: 8 additions & 1 deletion test/integration-tests/MultiMessageAggregation.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity >=0.8.9;

/// library imports
import {Vm} from "forge-std/Test.sol";
import "wormhole-solidity-sdk/interfaces/IWormholeRelayer.sol";

/// local imports
import "test/Setup.t.sol";
Expand Down Expand Up @@ -31,13 +32,19 @@ contract MultiBridgeMessageAggregationTest is Setup {

/// send cross-chain message using MMA infra
vm.recordLogs();
uint256[] memory fees = new uint256[](2);
(uint256 wormholeFee,) =
IWormholeRelayer(POLYGON_RELAYER).quoteEVMDeliveryPrice(_wormholeChainId(DST_CHAIN_ID), 0, 0);
fees[0] = wormholeFee;
fees[1] = 0.01 ether;
MultiBridgeMessageSender(contractAddress[SRC_CHAIN_ID][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}(
DST_CHAIN_ID,
address(target),
abi.encode(MockUniswapReceiver.setValue.selector, ""),
0,
EXPIRATION_CONSTANT,
refundAddress
refundAddress,
fees
);

Vm.Log[] memory logs = vm.getRecordedLogs();
Expand Down
9 changes: 8 additions & 1 deletion test/integration-tests/RemoteAdapterAdd.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity >=0.8.9;

/// library imports
import {Vm} from "forge-std/Test.sol";
import "wormhole-solidity-sdk/interfaces/IWormholeRelayer.sol";

/// local imports
import "test/Setup.t.sol";
Expand Down Expand Up @@ -56,13 +57,19 @@ contract RemoteAdapterAdd is Setup {

/// send cross-chain message using MMA infra
vm.recordLogs();
uint256[] memory fees = new uint256[](2);
(uint256 wormholeFee,) =
IWormholeRelayer(POLYGON_RELAYER).quoteEVMDeliveryPrice(_wormholeChainId(DST_CHAIN_ID), 0, 0);
fees[0] = wormholeFee;
fees[1] = 0.01 ether;
MultiBridgeMessageSender(contractAddress[SRC_CHAIN_ID][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}(
DST_CHAIN_ID,
address(contractAddress[DST_CHAIN_ID][bytes("MMA_RECEIVER")]),
abi.encodeWithSelector(MultiBridgeMessageReceiver.updateReceiverAdapters.selector, adaptersToAdd, operation),
0,
EXPIRATION_CONSTANT,
refundAddress
refundAddress,
fees
);

Vm.Log[] memory logs = vm.getRecordedLogs();
Expand Down
9 changes: 8 additions & 1 deletion test/integration-tests/RemoteAdapterRemove.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity >=0.8.9;

/// library imports
import {Vm} from "forge-std/Test.sol";
import "wormhole-solidity-sdk/interfaces/IWormholeRelayer.sol";

/// local imports
import "test/Setup.t.sol";
Expand Down Expand Up @@ -61,6 +62,11 @@ contract RemoteAdapterRemove is Setup {

/// send cross-chain message using MMA infra
vm.recordLogs();
uint256[] memory fees = new uint256[](2);
(uint256 wormholeFee,) =
IWormholeRelayer(POLYGON_RELAYER).quoteEVMDeliveryPrice(_wormholeChainId(DST_CHAIN_ID), 0, 0);
fees[0] = wormholeFee;
fees[1] = 0.01 ether;
MultiBridgeMessageSender(contractAddress[SRC_CHAIN_ID][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}(
DST_CHAIN_ID,
address(contractAddress[DST_CHAIN_ID][bytes("MMA_RECEIVER")]),
Expand All @@ -72,7 +78,8 @@ contract RemoteAdapterRemove is Setup {
),
0,
EXPIRATION_CONSTANT,
refundAddress
refundAddress,
fees
);

Vm.Log[] memory logs = vm.getRecordedLogs();
Expand Down
9 changes: 8 additions & 1 deletion test/integration-tests/RemoteSetQuorum.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity >=0.8.9;

/// library imports
import {Vm} from "forge-std/Test.sol";
import "wormhole-solidity-sdk/interfaces/IWormholeRelayer.sol";

/// local imports
import "test/Setup.t.sol";
Expand All @@ -28,13 +29,19 @@ contract RemoteQuorumUpdate is Setup {

/// send cross-chain message using MMA infra
vm.recordLogs();
uint256[] memory fees = new uint256[](2);
(uint256 wormholeFee,) =
IWormholeRelayer(POLYGON_RELAYER).quoteEVMDeliveryPrice(_wormholeChainId(DST_CHAIN_ID), 0, 0);
fees[0] = wormholeFee;
fees[1] = 0.01 ether;
MultiBridgeMessageSender(contractAddress[SRC_CHAIN_ID][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}(
DST_CHAIN_ID,
address(contractAddress[DST_CHAIN_ID][bytes("MMA_RECEIVER")]),
abi.encodeWithSelector(MultiBridgeMessageReceiver.updateQuorum.selector, newQuorum),
0,
EXPIRATION_CONSTANT,
refundAddress
refundAddress,
fees
);

Vm.Log[] memory logs = vm.getRecordedLogs();
Expand Down
9 changes: 8 additions & 1 deletion test/integration-tests/RemoteTimelockUpdate.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity >=0.8.9;

/// library imports
import {Vm} from "forge-std/Test.sol";
import "wormhole-solidity-sdk/interfaces/IWormholeRelayer.sol";

/// local imports
import "test/Setup.t.sol";
Expand All @@ -28,13 +29,19 @@ contract RemoteTimelockUpdate is Setup {

/// send cross-chain message using MMA infra
vm.recordLogs();
uint256[] memory fees = new uint256[](2);
(uint256 wormholeFee,) =
IWormholeRelayer(POLYGON_RELAYER).quoteEVMDeliveryPrice(_wormholeChainId(DST_CHAIN_ID), 0, 0);
fees[0] = wormholeFee;
fees[1] = 0.01 ether;
MultiBridgeMessageSender(contractAddress[SRC_CHAIN_ID][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}(
POLYGON_CHAIN_ID,
address(contractAddress[POLYGON_CHAIN_ID][bytes("TIMELOCK")]),
abi.encodeWithSelector(GovernanceTimelock.setDelay.selector, newDelay),
0,
EXPIRATION_CONSTANT,
refundAddress
refundAddress,
fees
);

Vm.Log[] memory logs = vm.getRecordedLogs();
Expand Down
9 changes: 8 additions & 1 deletion test/integration-tests/TimelockCheck.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity >=0.8.9;

/// library imports
import {Vm} from "forge-std/Test.sol";
import "wormhole-solidity-sdk/interfaces/IWormholeRelayer.sol";

/// local imports
import "test/Setup.t.sol";
Expand Down Expand Up @@ -33,13 +34,19 @@ contract TimelockCheckTest is Setup {

/// send cross-chain message using MMA infra
vm.recordLogs();
uint256[] memory fees = new uint256[](2);
(uint256 wormholeFee,) =
IWormholeRelayer(POLYGON_RELAYER).quoteEVMDeliveryPrice(_wormholeChainId(DST_CHAIN_ID), 0, 0);
fees[0] = wormholeFee;
fees[1] = 0.01 ether;
MultiBridgeMessageSender(contractAddress[SRC_CHAIN_ID][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}(
DST_CHAIN_ID,
address(target),
abi.encode(MockUniswapReceiver.setValue.selector, ""),
0,
EXPIRATION_CONSTANT,
refundAddress
refundAddress,
fees
);

Vm.Log[] memory logs = vm.getRecordedLogs();
Expand Down
10 changes: 5 additions & 5 deletions test/unit-tests/MultiBridgeMessageReceiver.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ contract MultiBridgeMessageReceiverTest is Setup {
);

MultiBridgeMessageReceiver receiver;
address wormholeAdapterAddr;
address axelarAdapterAddr;
address wormholeAdapterAddr;
address timelockAddr;

/// @dev initializes the setup
Expand All @@ -32,16 +32,16 @@ contract MultiBridgeMessageReceiverTest is Setup {

vm.selectFork(fork[DST_CHAIN_ID]);
receiver = MultiBridgeMessageReceiver(contractAddress[DST_CHAIN_ID][bytes("MMA_RECEIVER")]);
wormholeAdapterAddr = contractAddress[DST_CHAIN_ID]["WORMHOLE_RECEIVER_ADAPTER"];
axelarAdapterAddr = contractAddress[DST_CHAIN_ID]["AXELAR_RECEIVER_ADAPTER"];
wormholeAdapterAddr = contractAddress[DST_CHAIN_ID]["WORMHOLE_RECEIVER_ADAPTER"];
timelockAddr = contractAddress[DST_CHAIN_ID]["TIMELOCK"];
}

/// @dev initializer
function test_initialize() public {
address[] memory adapters = new address[](2);
adapters[0] = wormholeAdapterAddr;
adapters[1] = axelarAdapterAddr;
adapters[0] = axelarAdapterAddr;
adapters[1] = wormholeAdapterAddr;

bool[] memory operation = new bool[](2);
operation[0] = true;
Expand All @@ -51,8 +51,8 @@ contract MultiBridgeMessageReceiverTest is Setup {
dummyReceiver.initialize(ETHEREUM_CHAIN_ID, adapters, operation, 2, timelockAddr);

assertEq(dummyReceiver.quorum(), 2);
assertTrue(dummyReceiver.isTrustedExecutor(wormholeAdapterAddr));
assertTrue(dummyReceiver.isTrustedExecutor(axelarAdapterAddr));
assertTrue(dummyReceiver.isTrustedExecutor(wormholeAdapterAddr));
}

/// @dev initializer cannot be called twice
Expand Down
Loading

0 comments on commit 12f460e

Please sign in to comment.