Skip to content

Commit

Permalink
Improve MultiMessageSender
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 committed Sep 19, 2023
1 parent 032c11a commit c37a573
Show file tree
Hide file tree
Showing 18 changed files with 492 additions and 247 deletions.
312 changes: 191 additions & 121 deletions src/MultiMessageSender.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 @@ -84,17 +84,6 @@ contract AxelarSenderAdapter is BaseSenderAdapter {
}
}

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

/// @inheritdoc IMessageSenderAdapter
function getMessageFee(uint256 _toChainId, 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, gac.getGlobalMsgDeliveryGasLimit());
}
}
3 changes: 0 additions & 3 deletions src/interfaces/IMessageSenderAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,4 @@ interface IMessageSenderAdapter is SingleMessageDispatcher {

/// @dev returns name of the message bridge wrapped by the adapter
function name() external view returns (string memory);

/// @dev return native token amount in wei required by this message bridge for sending a message
function getMessageFee(uint256 toChainId, address to, bytes calldata data) external view returns (uint256);
}
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 is 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
8 changes: 4 additions & 4 deletions test/Setup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,8 @@ abstract contract Setup is Test {
vm.startPrank(owner);

address[] memory _senderAdapters = new address[](2);
_senderAdapters[0] = contractAddress[1][bytes("WORMHOLE_SENDER_ADAPTER")];
_senderAdapters[1] = contractAddress[1][bytes("AXELAR_SENDER_ADAPTER")];
_senderAdapters[0] = contractAddress[SRC_CHAIN_ID][bytes("AXELAR_SENDER_ADAPTER")];
_senderAdapters[1] = contractAddress[SRC_CHAIN_ID][bytes("WORMHOLE_SENDER_ADAPTER")];

MultiMessageSender(contractAddress[1][bytes("MMA_SENDER")]).addSenderAdapters(_senderAdapters);

Expand All @@ -287,8 +287,8 @@ abstract contract Setup is Test {
vm.selectFork(fork[chainId]);

address[] memory _receiverAdapters = new address[](2);
_receiverAdapters[0] = contractAddress[chainId][bytes("WORMHOLE_RECEIVER_ADAPTER")];
_receiverAdapters[1] = contractAddress[chainId][bytes("AXELAR_RECEIVER_ADAPTER")];
_receiverAdapters[0] = contractAddress[chainId][bytes("AXELAR_RECEIVER_ADAPTER")];
_receiverAdapters[1] = contractAddress[chainId][bytes("WORMHOLE_RECEIVER_ADAPTER")];

bool[] memory _operations = new bool[](2);
_operations[0] = true;
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] = 0.01 ether;
fees[1] = wormholeFee;
MultiMessageSender(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 MultiMessageAggregationTest 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] = 0.01 ether;
fees[1] = wormholeFee;
MultiMessageSender(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] = 0.01 ether;
fees[1] = wormholeFee;
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,
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] = 0.01 ether;
fees[1] = wormholeFee;
MultiMessageSender(contractAddress[SRC_CHAIN_ID][bytes("MMA_SENDER")]).remoteCall{value: 2 ether}(
DST_CHAIN_ID,
address(contractAddress[DST_CHAIN_ID][bytes("MMA_RECEIVER")]),
Expand All @@ -69,7 +75,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] = 0.01 ether;
fees[1] = wormholeFee;
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.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] = 0.01 ether;
fees[1] = wormholeFee;
MultiMessageSender(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] = 0.01 ether;
fees[1] = wormholeFee;
MultiMessageSender(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/MultiMessageReceiver.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ contract MultiMessageReceiverTest is Setup {
);

MultiMessageReceiver receiver;
address wormholeAdapterAddr;
address axelarAdapterAddr;
address wormholeAdapterAddr;
address timelockAddr;

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

vm.selectFork(fork[DST_CHAIN_ID]);
receiver = MultiMessageReceiver(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 MultiMessageReceiverTest 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 c37a573

Please sign in to comment.