Update router implementations for v3 (#2749)
parent
03c92e1e34
commit
fcfecdf250
@ -1,168 +0,0 @@ |
||||
// SPDX-License-Identifier: MIT OR Apache-2.0 |
||||
pragma solidity >=0.6.11; |
||||
|
||||
// ============ Internal Imports ============ |
||||
import {IInterchainGasPaymaster} from "./interfaces/IInterchainGasPaymaster.sol"; |
||||
import {IInterchainSecurityModule} from "./interfaces/IInterchainSecurityModule.sol"; |
||||
import {IHyperlaneConnectionClient} from "./interfaces/IHyperlaneConnectionClient.sol"; |
||||
import {IMailbox} from "./interfaces/IMailbox.sol"; |
||||
|
||||
// ============ External Imports ============ |
||||
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; |
||||
import {Address} from "@openzeppelin/contracts/utils/Address.sol"; |
||||
|
||||
abstract contract HyperlaneConnectionClient is |
||||
OwnableUpgradeable, |
||||
IHyperlaneConnectionClient |
||||
{ |
||||
// ============ Mutable Storage ============ |
||||
|
||||
IMailbox public mailbox; |
||||
// Interchain Gas Paymaster contract. The relayer associated with this contract |
||||
// must be willing to relay messages dispatched from the current Mailbox contract, |
||||
// otherwise payments made to the paymaster will not result in relayed messages. |
||||
IInterchainGasPaymaster public interchainGasPaymaster; |
||||
|
||||
IInterchainSecurityModule public interchainSecurityModule; |
||||
|
||||
uint256[48] private __GAP; // gap for upgrade safety |
||||
|
||||
// ============ Events ============ |
||||
/** |
||||
* @notice Emitted when a new mailbox is set. |
||||
* @param mailbox The address of the mailbox contract |
||||
*/ |
||||
event MailboxSet(address indexed mailbox); |
||||
|
||||
/** |
||||
* @notice Emitted when a new Interchain Gas Paymaster is set. |
||||
* @param interchainGasPaymaster The address of the Interchain Gas Paymaster. |
||||
*/ |
||||
event InterchainGasPaymasterSet(address indexed interchainGasPaymaster); |
||||
|
||||
event InterchainSecurityModuleSet(address indexed module); |
||||
|
||||
// ============ Modifiers ============ |
||||
|
||||
/** |
||||
* @notice Only accept messages from an Hyperlane Mailbox contract |
||||
*/ |
||||
modifier onlyMailbox() { |
||||
require(msg.sender == address(mailbox), "!mailbox"); |
||||
_; |
||||
} |
||||
|
||||
/** |
||||
* @notice Only accept addresses that at least have contract code |
||||
*/ |
||||
modifier onlyContract(address _contract) { |
||||
require(Address.isContract(_contract), "!contract"); |
||||
_; |
||||
} |
||||
|
||||
// ======== Initializer ========= |
||||
|
||||
function __HyperlaneConnectionClient_initialize(address _mailbox) |
||||
internal |
||||
onlyInitializing |
||||
{ |
||||
_setMailbox(_mailbox); |
||||
__Ownable_init(); |
||||
} |
||||
|
||||
function __HyperlaneConnectionClient_initialize( |
||||
address _mailbox, |
||||
address _interchainGasPaymaster |
||||
) internal onlyInitializing { |
||||
_setInterchainGasPaymaster(_interchainGasPaymaster); |
||||
__HyperlaneConnectionClient_initialize(_mailbox); |
||||
} |
||||
|
||||
function __HyperlaneConnectionClient_initialize( |
||||
address _mailbox, |
||||
address _interchainGasPaymaster, |
||||
address _interchainSecurityModule |
||||
) internal onlyInitializing { |
||||
_setInterchainSecurityModule(_interchainSecurityModule); |
||||
__HyperlaneConnectionClient_initialize( |
||||
_mailbox, |
||||
_interchainGasPaymaster |
||||
); |
||||
} |
||||
|
||||
function __HyperlaneConnectionClient_initialize( |
||||
address _mailbox, |
||||
address _interchainGasPaymaster, |
||||
address _interchainSecurityModule, |
||||
address _owner |
||||
) internal onlyInitializing { |
||||
_setMailbox(_mailbox); |
||||
_setInterchainGasPaymaster(_interchainGasPaymaster); |
||||
_setInterchainSecurityModule(_interchainSecurityModule); |
||||
_transferOwnership(_owner); |
||||
} |
||||
|
||||
// ============ External functions ============ |
||||
|
||||
/** |
||||
* @notice Sets the address of the application's Mailbox. |
||||
* @param _mailbox The address of the Mailbox contract. |
||||
*/ |
||||
function setMailbox(address _mailbox) external virtual onlyOwner { |
||||
_setMailbox(_mailbox); |
||||
} |
||||
|
||||
/** |
||||
* @notice Sets the address of the application's InterchainGasPaymaster. |
||||
* @param _interchainGasPaymaster The address of the InterchainGasPaymaster contract. |
||||
*/ |
||||
function setInterchainGasPaymaster(address _interchainGasPaymaster) |
||||
external |
||||
virtual |
||||
onlyOwner |
||||
{ |
||||
_setInterchainGasPaymaster(_interchainGasPaymaster); |
||||
} |
||||
|
||||
function setInterchainSecurityModule(address _module) |
||||
external |
||||
virtual |
||||
onlyOwner |
||||
{ |
||||
_setInterchainSecurityModule(_module); |
||||
} |
||||
|
||||
// ============ Internal functions ============ |
||||
|
||||
/** |
||||
* @notice Sets the address of the application's InterchainGasPaymaster. |
||||
* @param _interchainGasPaymaster The address of the InterchainGasPaymaster contract. |
||||
*/ |
||||
function _setInterchainGasPaymaster(address _interchainGasPaymaster) |
||||
internal |
||||
onlyContract(_interchainGasPaymaster) |
||||
{ |
||||
interchainGasPaymaster = IInterchainGasPaymaster( |
||||
_interchainGasPaymaster |
||||
); |
||||
emit InterchainGasPaymasterSet(_interchainGasPaymaster); |
||||
} |
||||
|
||||
/** |
||||
* @notice Modify the contract the Application uses to validate Mailbox contracts |
||||
* @param _mailbox The address of the mailbox contract |
||||
*/ |
||||
function _setMailbox(address _mailbox) internal onlyContract(_mailbox) { |
||||
mailbox = IMailbox(_mailbox); |
||||
emit MailboxSet(_mailbox); |
||||
} |
||||
|
||||
function _setInterchainSecurityModule(address _module) internal { |
||||
require( |
||||
_module == address(0) || Address.isContract(_module), |
||||
"!contract" |
||||
); |
||||
interchainSecurityModule = IInterchainSecurityModule(_module); |
||||
emit InterchainSecurityModuleSet(_module); |
||||
} |
||||
} |
@ -1,84 +0,0 @@ |
||||
// SPDX-License-Identifier: MIT OR Apache-2.0 |
||||
pragma solidity >=0.8.0; |
||||
|
||||
/*@@@@@@@ @@@@@@@@@ |
||||
@@@@@@@@@ @@@@@@@@@ |
||||
@@@@@@@@@ @@@@@@@@@ |
||||
@@@@@@@@@ @@@@@@@@@ |
||||
@@@@@@@@@@@@@@@@@@@@@@@@@ |
||||
@@@@@ HYPERLANE @@@@@@@ |
||||
@@@@@@@@@@@@@@@@@@@@@@@@@ |
||||
@@@@@@@@@ @@@@@@@@@ |
||||
@@@@@@@@@ @@@@@@@@@ |
||||
@@@@@@@@@ @@@@@@@@@ |
||||
@@@@@@@@@ @@@@@@@@*/ |
||||
|
||||
import {Message} from "../libs/Message.sol"; |
||||
import {StandardHookMetadata} from "../libs/hooks/StandardHookMetadata.sol"; |
||||
import {AbstractPostDispatchHook} from "./AbstractPostDispatchHook.sol"; |
||||
import {MailboxClient} from "../client/MailboxClient.sol"; |
||||
import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol"; |
||||
import {IMailbox} from "../interfaces/IMailbox.sol"; |
||||
|
||||
contract ConfigFallbackDomainRoutingHook is |
||||
AbstractPostDispatchHook, |
||||
MailboxClient |
||||
{ |
||||
using Message for bytes; |
||||
using StandardHookMetadata for bytes; |
||||
|
||||
// ============ Public Storage ============ |
||||
|
||||
/// @notice message sender => destination => recipient => hook |
||||
mapping(address => mapping(uint32 => mapping(bytes32 => IPostDispatchHook))) |
||||
public customHooks; |
||||
|
||||
constructor(address _mailbox) MailboxClient(_mailbox) {} |
||||
|
||||
// ============ External Functions ============ |
||||
|
||||
function setHook( |
||||
uint32 destinationDomain, |
||||
bytes32 recipient, |
||||
IPostDispatchHook hook |
||||
) external { |
||||
customHooks[msg.sender][destinationDomain][recipient] = hook; |
||||
} |
||||
|
||||
// ============ Internal Functions ============ |
||||
|
||||
/// @inheritdoc AbstractPostDispatchHook |
||||
function _postDispatch(bytes calldata metadata, bytes calldata message) |
||||
internal |
||||
override |
||||
{ |
||||
_getConfiguredHook(message).postDispatch{value: msg.value}( |
||||
metadata, |
||||
message |
||||
); |
||||
} |
||||
|
||||
/// @inheritdoc AbstractPostDispatchHook |
||||
function _quoteDispatch(bytes calldata metadata, bytes calldata message) |
||||
internal |
||||
view |
||||
override |
||||
returns (uint256) |
||||
{ |
||||
return _getConfiguredHook(message).quoteDispatch(metadata, message); |
||||
} |
||||
|
||||
function _getConfiguredHook(bytes calldata message) |
||||
internal |
||||
view |
||||
returns (IPostDispatchHook) |
||||
{ |
||||
IPostDispatchHook configuredHook = customHooks[message.senderAddress()][ |
||||
message.destination() |
||||
][message.recipient()]; |
||||
if (address(configuredHook) == address(0)) { |
||||
configuredHook = mailbox.defaultHook(); |
||||
} |
||||
return configuredHook; |
||||
} |
||||
} |
@ -0,0 +1,54 @@ |
||||
// SPDX-License-Identifier: MIT OR Apache-2.0 |
||||
pragma solidity >=0.8.0; |
||||
|
||||
/*@@@@@@@ @@@@@@@@@ |
||||
@@@@@@@@@ @@@@@@@@@ |
||||
@@@@@@@@@ @@@@@@@@@ |
||||
@@@@@@@@@ @@@@@@@@@ |
||||
@@@@@@@@@@@@@@@@@@@@@@@@@ |
||||
@@@@@ HYPERLANE @@@@@@@ |
||||
@@@@@@@@@@@@@@@@@@@@@@@@@ |
||||
@@@@@@@@@ @@@@@@@@@ |
||||
@@@@@@@@@ @@@@@@@@@ |
||||
@@@@@@@@@ @@@@@@@@@ |
||||
@@@@@@@@@ @@@@@@@@*/ |
||||
|
||||
import {Message} from "../libs/Message.sol"; |
||||
import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol"; |
||||
import {DomainRoutingHook} from "./DomainRoutingHook.sol"; |
||||
|
||||
contract DestinationRecipientRoutingHook is DomainRoutingHook { |
||||
using Message for bytes; |
||||
|
||||
/// @notice destination => recipient =>custom hook |
||||
mapping(uint32 => mapping(bytes32 => address)) public customHooks; |
||||
|
||||
constructor(address mailbox, address owner) |
||||
DomainRoutingHook(mailbox, owner) |
||||
{} |
||||
|
||||
function _postDispatch(bytes calldata metadata, bytes calldata message) |
||||
internal |
||||
override |
||||
{ |
||||
address customHookPreset = customHooks[message.destination()][ |
||||
message.recipient() |
||||
]; |
||||
if (customHookPreset != address(0)) { |
||||
IPostDispatchHook(customHookPreset).postDispatch{value: msg.value}( |
||||
metadata, |
||||
message |
||||
); |
||||
} else { |
||||
super._postDispatch(metadata, message); |
||||
} |
||||
} |
||||
|
||||
function configCustomHook( |
||||
uint32 destinationDomain, |
||||
bytes32 recipient, |
||||
address hook |
||||
) external onlyOwner { |
||||
customHooks[destinationDomain][recipient] = hook; |
||||
} |
||||
} |
@ -0,0 +1,52 @@ |
||||
// SPDX-License-Identifier: MIT OR Apache-2.0 |
||||
pragma solidity >=0.8.0; |
||||
|
||||
/*@@@@@@@ @@@@@@@@@ |
||||
@@@@@@@@@ @@@@@@@@@ |
||||
@@@@@@@@@ @@@@@@@@@ |
||||
@@@@@@@@@ @@@@@@@@@ |
||||
@@@@@@@@@@@@@@@@@@@@@@@@@ |
||||
@@@@@ HYPERLANE @@@@@@@ |
||||
@@@@@@@@@@@@@@@@@@@@@@@@@ |
||||
@@@@@@@@@ @@@@@@@@@ |
||||
@@@@@@@@@ @@@@@@@@@ |
||||
@@@@@@@@@ @@@@@@@@@ |
||||
@@@@@@@@@ @@@@@@@@*/ |
||||
|
||||
import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol"; |
||||
import {IMailbox} from "../interfaces/IMailbox.sol"; |
||||
import {DomainRoutingHook} from "./DomainRoutingHook.sol"; |
||||
import {Message} from "../libs/Message.sol"; |
||||
|
||||
/** |
||||
* @title FallbackDomainRoutingHook |
||||
* @notice Delegates to a hook based on the destination domain of the message. |
||||
* If no hook is configured for the destination domain, delegates to a fallback hook. |
||||
*/ |
||||
contract FallbackDomainRoutingHook is DomainRoutingHook { |
||||
using Message for bytes; |
||||
|
||||
IPostDispatchHook public immutable fallbackHook; |
||||
|
||||
constructor( |
||||
address _mailbox, |
||||
address _owner, |
||||
address _fallback |
||||
) DomainRoutingHook(_mailbox, _owner) { |
||||
fallbackHook = IPostDispatchHook(_fallback); |
||||
} |
||||
|
||||
// ============ Internal Functions ============ |
||||
|
||||
function _getConfiguredHook(bytes calldata message) |
||||
internal |
||||
view |
||||
override |
||||
returns (IPostDispatchHook hook) |
||||
{ |
||||
hook = hooks[message.destination()]; |
||||
if (address(hook) == address(0)) { |
||||
hook = fallbackHook; |
||||
} |
||||
} |
||||
} |
@ -1,20 +0,0 @@ |
||||
// SPDX-License-Identifier: MIT OR Apache-2.0 |
||||
pragma solidity >=0.8.0; |
||||
import {IInterchainGasPaymaster} from "./IInterchainGasPaymaster.sol"; |
||||
import {ISpecifiesInterchainSecurityModule} from "./IInterchainSecurityModule.sol"; |
||||
import {IMailbox} from "./IMailbox.sol"; |
||||
|
||||
interface IHyperlaneConnectionClient is ISpecifiesInterchainSecurityModule { |
||||
function mailbox() external view returns (IMailbox); |
||||
|
||||
function interchainGasPaymaster() |
||||
external |
||||
view |
||||
returns (IInterchainGasPaymaster); |
||||
|
||||
function setMailbox(address) external; |
||||
|
||||
function setInterchainGasPaymaster(address) external; |
||||
|
||||
function setInterchainSecurityModule(address) external; |
||||
} |
@ -0,0 +1,17 @@ |
||||
// SPDX-License-Identifier: MIT OR Apache-2.0 |
||||
pragma solidity >=0.8.0; |
||||
|
||||
import {IInterchainSecurityModule} from "../interfaces/IInterchainSecurityModule.sol"; |
||||
|
||||
contract NoopIsm is IInterchainSecurityModule { |
||||
uint8 public constant override moduleType = uint8(Types.NULL); |
||||
|
||||
function verify(bytes calldata, bytes calldata) |
||||
public |
||||
pure |
||||
override |
||||
returns (bool) |
||||
{ |
||||
return true; |
||||
} |
||||
} |
@ -1,18 +0,0 @@ |
||||
// SPDX-License-Identifier: MIT OR Apache-2.0 |
||||
pragma solidity >=0.6.11; |
||||
import {HyperlaneConnectionClient} from "../HyperlaneConnectionClient.sol"; |
||||
import {IMailbox} from "../interfaces/IMailbox.sol"; |
||||
|
||||
contract TestHyperlaneConnectionClient is HyperlaneConnectionClient { |
||||
constructor() { |
||||
_transferOwnership(msg.sender); |
||||
} |
||||
|
||||
function initialize(address _mailbox) external initializer { |
||||
__HyperlaneConnectionClient_initialize(_mailbox); |
||||
} |
||||
|
||||
function localDomain() external view returns (uint32) { |
||||
return mailbox.localDomain(); |
||||
} |
||||
} |
@ -0,0 +1,165 @@ |
||||
// SPDX-License-Identifier: Apache-2.0 |
||||
pragma solidity ^0.8.13; |
||||
|
||||
import "forge-std/Test.sol"; |
||||
|
||||
import {TypeCasts} from "../../contracts/libs/TypeCasts.sol"; |
||||
import {DomainRoutingHook} from "../../contracts/hooks/DomainRoutingHook.sol"; |
||||
import {FallbackDomainRoutingHook} from "../../contracts/hooks/FallbackDomainRoutingHook.sol"; |
||||
import {TestPostDispatchHook} from "../../contracts/test/TestPostDispatchHook.sol"; |
||||
import {TestMailbox} from "../../contracts/test/TestMailbox.sol"; |
||||
|
||||
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; |
||||
|
||||
contract DomainRoutingHookTest is Test { |
||||
using TypeCasts for address; |
||||
using Strings for uint32; |
||||
|
||||
DomainRoutingHook public hook; |
||||
TestPostDispatchHook public noopHook; |
||||
TestMailbox public mailbox; |
||||
|
||||
function setUp() public virtual { |
||||
address owner = address(this); |
||||
uint32 origin = 0; |
||||
mailbox = new TestMailbox(origin); |
||||
hook = new DomainRoutingHook(address(mailbox), owner); |
||||
noopHook = new TestPostDispatchHook(); |
||||
} |
||||
|
||||
function test_quoteDispatch( |
||||
uint32 destination, |
||||
bytes32 recipient, |
||||
bytes memory body, |
||||
bytes memory metadata, |
||||
uint256 fee |
||||
) public { |
||||
noopHook.setFee(fee); |
||||
|
||||
hook.setHook(destination, address(noopHook)); |
||||
|
||||
bytes memory testMessage = mailbox.buildOutboundMessage( |
||||
destination, |
||||
recipient, |
||||
body |
||||
); |
||||
|
||||
vm.expectCall( |
||||
address(noopHook), |
||||
abi.encodeCall(noopHook.quoteDispatch, (metadata, testMessage)) |
||||
); |
||||
assertEq(hook.quoteDispatch(metadata, testMessage), fee); |
||||
} |
||||
|
||||
function test_quoteDispatch_whenDestinationUnenrolled( |
||||
uint32 destination, |
||||
bytes32 recipient, |
||||
bytes memory body, |
||||
bytes memory metadata, |
||||
uint256 |
||||
) public virtual { |
||||
bytes memory testMessage = mailbox.buildOutboundMessage( |
||||
destination, |
||||
recipient, |
||||
body |
||||
); |
||||
// dynamic reason cannot be checked? |
||||
vm.expectRevert(); |
||||
hook.quoteDispatch(metadata, testMessage); |
||||
} |
||||
|
||||
function test_postDispatch( |
||||
uint32 destination, |
||||
bytes32 recipient, |
||||
bytes memory body, |
||||
bytes memory metadata |
||||
) public { |
||||
hook.setHook(destination, address(noopHook)); |
||||
|
||||
bytes memory testMessage = mailbox.buildOutboundMessage( |
||||
destination, |
||||
recipient, |
||||
body |
||||
); |
||||
|
||||
vm.expectCall( |
||||
address(noopHook), |
||||
abi.encodeCall(noopHook.postDispatch, (metadata, testMessage)) |
||||
); |
||||
hook.postDispatch(metadata, testMessage); |
||||
} |
||||
|
||||
function test_postDispatch_whenDestinationUnenrolled( |
||||
uint32 destination, |
||||
bytes32 recipient, |
||||
bytes memory body, |
||||
bytes memory metadata |
||||
) public virtual { |
||||
bytes memory testMessage = mailbox.buildOutboundMessage( |
||||
destination, |
||||
recipient, |
||||
body |
||||
); |
||||
// dynamic reason cannot be checked? |
||||
vm.expectRevert(); |
||||
hook.postDispatch(metadata, testMessage); |
||||
} |
||||
} |
||||
|
||||
contract FallbackDomainRoutingHookTest is DomainRoutingHookTest { |
||||
TestPostDispatchHook public fallbackHook; |
||||
|
||||
function setUp() public override { |
||||
address owner = address(this); |
||||
uint32 origin = 0; |
||||
mailbox = new TestMailbox(origin); |
||||
fallbackHook = new TestPostDispatchHook(); |
||||
noopHook = new TestPostDispatchHook(); |
||||
hook = new FallbackDomainRoutingHook( |
||||
address(mailbox), |
||||
owner, |
||||
address(fallbackHook) |
||||
); |
||||
} |
||||
|
||||
function test_quoteDispatch_whenDestinationUnenrolled( |
||||
uint32 destination, |
||||
bytes32 recipient, |
||||
bytes memory body, |
||||
bytes memory metadata, |
||||
uint256 fee |
||||
) public override { |
||||
fallbackHook.setFee(fee); |
||||
|
||||
bytes memory testMessage = mailbox.buildOutboundMessage( |
||||
destination, |
||||
recipient, |
||||
body |
||||
); |
||||
|
||||
vm.expectCall( |
||||
address(fallbackHook), |
||||
abi.encodeCall(fallbackHook.quoteDispatch, (metadata, testMessage)) |
||||
); |
||||
assertEq(hook.quoteDispatch(metadata, testMessage), fee); |
||||
} |
||||
|
||||
function test_postDispatch_whenDestinationUnenrolled( |
||||
uint32 destination, |
||||
bytes32 recipient, |
||||
bytes memory body, |
||||
bytes memory metadata |
||||
) public override { |
||||
bytes memory testMessage = mailbox.buildOutboundMessage( |
||||
destination, |
||||
recipient, |
||||
body |
||||
); |
||||
|
||||
vm.expectCall( |
||||
address(fallbackHook), |
||||
abi.encodeCall(fallbackHook.postDispatch, (metadata, testMessage)) |
||||
); |
||||
hook.postDispatch(metadata, testMessage); |
||||
} |
||||
} |
@ -1,136 +0,0 @@ |
||||
// SPDX-License-Identifier: Apache-2.0 |
||||
pragma solidity ^0.8.13; |
||||
|
||||
import "forge-std/Test.sol"; |
||||
|
||||
import {TypeCasts} from "../../contracts/libs/TypeCasts.sol"; |
||||
import {MessageUtils} from "../isms/IsmTestUtils.sol"; |
||||
import {TestMailbox} from "../../contracts/test/TestMailbox.sol"; |
||||
import {ConfigFallbackDomainRoutingHook} from "../../contracts/hooks/ConfigFallbackDomainRoutingHook.sol"; |
||||
import {TestPostDispatchHook} from "../../contracts/test/TestPostDispatchHook.sol"; |
||||
import {TestRecipient} from "../../contracts/test/TestRecipient.sol"; |
||||
|
||||
contract FallbackDomainRoutingHookTest is Test { |
||||
using TypeCasts for address; |
||||
|
||||
ConfigFallbackDomainRoutingHook internal fallbackHook; |
||||
TestPostDispatchHook internal configuredTestPostDispatchHook; |
||||
TestPostDispatchHook internal mailboxDefaultHook; |
||||
TestRecipient internal testRecipient; |
||||
TestMailbox internal mailbox; |
||||
|
||||
uint32 internal constant TEST_ORIGIN_DOMAIN = 1; |
||||
uint32 internal constant TEST_DESTINATION_DOMAIN = 2; |
||||
bytes internal testMessage; |
||||
|
||||
event PostDispatchHookCalled(); |
||||
|
||||
function setUp() public { |
||||
mailbox = new TestMailbox(TEST_ORIGIN_DOMAIN); |
||||
configuredTestPostDispatchHook = new TestPostDispatchHook(); |
||||
mailboxDefaultHook = new TestPostDispatchHook(); |
||||
testRecipient = new TestRecipient(); |
||||
fallbackHook = new ConfigFallbackDomainRoutingHook(address(mailbox)); |
||||
mailbox.setDefaultHook(address(mailboxDefaultHook)); |
||||
} |
||||
|
||||
function _setUpTestMessage(uint32 originDomain, uint32 destinationDomain) |
||||
public |
||||
{ |
||||
// vm.assume(originDomain != 0 && destinationDomain != 0); |
||||
|
||||
testMessage = _encodeTestMessage(originDomain, destinationDomain); |
||||
} |
||||
|
||||
/* ============ hook.quoteDispatch ============ */ |
||||
|
||||
function test_quoteDispatchHook_configured( |
||||
uint32 originDomain, |
||||
uint32 destinationDomain |
||||
) public { |
||||
testMessage = _encodeTestMessage(originDomain, destinationDomain); |
||||
|
||||
fallbackHook.setHook( |
||||
destinationDomain, |
||||
address(testRecipient).addressToBytes32(), |
||||
configuredTestPostDispatchHook |
||||
); |
||||
|
||||
vm.expectCall( |
||||
address(configuredTestPostDispatchHook), |
||||
abi.encodeCall( |
||||
configuredTestPostDispatchHook.quoteDispatch, |
||||
("", testMessage) |
||||
) |
||||
); |
||||
assertEq(fallbackHook.quoteDispatch("", testMessage), 25000); |
||||
} |
||||
|
||||
function test_quoteDispatch_default( |
||||
uint32 originDomain, |
||||
uint32 destinationDomain |
||||
) public payable { |
||||
testMessage = _encodeTestMessage(originDomain, destinationDomain); |
||||
|
||||
vm.expectCall( |
||||
address(mailboxDefaultHook), |
||||
abi.encodeCall(mailboxDefaultHook.quoteDispatch, ("", testMessage)) |
||||
); |
||||
fallbackHook.quoteDispatch("", testMessage); |
||||
} |
||||
|
||||
/* ============ hook.postDispatch ============ */ |
||||
|
||||
function test_postDispatchHook_configured( |
||||
uint32 originDomain, |
||||
uint32 destinationDomain |
||||
) public payable { |
||||
testMessage = _encodeTestMessage(originDomain, destinationDomain); |
||||
|
||||
fallbackHook.setHook( |
||||
destinationDomain, |
||||
address(testRecipient).addressToBytes32(), |
||||
configuredTestPostDispatchHook |
||||
); |
||||
|
||||
vm.expectCall( |
||||
address(configuredTestPostDispatchHook), |
||||
abi.encodeCall( |
||||
configuredTestPostDispatchHook.postDispatch, |
||||
("", testMessage) |
||||
) |
||||
); |
||||
fallbackHook.postDispatch{value: msg.value}("", testMessage); |
||||
} |
||||
|
||||
function test_postDispatch_default( |
||||
uint32 originDomain, |
||||
uint32 destinationDomain |
||||
) public payable { |
||||
testMessage = _encodeTestMessage(originDomain, destinationDomain); |
||||
|
||||
vm.expectCall( |
||||
address(mailboxDefaultHook), |
||||
abi.encodeCall(mailboxDefaultHook.postDispatch, ("", testMessage)) |
||||
); |
||||
|
||||
fallbackHook.postDispatch{value: msg.value}("", testMessage); |
||||
} |
||||
|
||||
function _encodeTestMessage(uint32 originDomain, uint32 destinationDomain) |
||||
internal |
||||
view |
||||
returns (bytes memory) |
||||
{ |
||||
return |
||||
MessageUtils.formatMessage( |
||||
uint8(0), // version |
||||
uint32(1), // nonce |
||||
originDomain, |
||||
address(this).addressToBytes32(), |
||||
destinationDomain, |
||||
address(testRecipient).addressToBytes32(), |
||||
abi.encodePacked("Hello from the other chain!") |
||||
); |
||||
} |
||||
} |
@ -1,93 +0,0 @@ |
||||
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; |
||||
import { expect } from 'chai'; |
||||
import { ethers } from 'hardhat'; |
||||
|
||||
import { |
||||
InterchainGasPaymaster, |
||||
Mailbox, |
||||
Mailbox__factory, |
||||
TestHyperlaneConnectionClient, |
||||
TestHyperlaneConnectionClient__factory, |
||||
TestInterchainGasPaymaster__factory, |
||||
} from '../types'; |
||||
|
||||
const ONLY_OWNER_REVERT_MSG = 'Ownable: caller is not the owner'; |
||||
|
||||
describe('HyperlaneConnectionClient', async () => { |
||||
let connectionClient: TestHyperlaneConnectionClient, |
||||
mailbox: Mailbox, |
||||
newMailbox: Mailbox, |
||||
signer: SignerWithAddress, |
||||
nonOwner: SignerWithAddress; |
||||
|
||||
before(async () => { |
||||
[signer, nonOwner] = await ethers.getSigners(); |
||||
}); |
||||
|
||||
beforeEach(async () => { |
||||
const mailboxFactory = new Mailbox__factory(signer); |
||||
const domain = 1000; |
||||
// TODO: fix
|
||||
mailbox = await mailboxFactory.deploy(domain); |
||||
newMailbox = await mailboxFactory.deploy(domain); |
||||
|
||||
const connectionClientFactory = new TestHyperlaneConnectionClient__factory( |
||||
signer, |
||||
); |
||||
connectionClient = await connectionClientFactory.deploy(); |
||||
await connectionClient.initialize(mailbox.address); |
||||
}); |
||||
|
||||
it('Cannot be initialized twice', async () => { |
||||
await expect( |
||||
connectionClient.initialize(mailbox.address), |
||||
).to.be.revertedWith('Initializable: contract is already initialized'); |
||||
}); |
||||
|
||||
it('owner can set mailbox', async () => { |
||||
expect(await connectionClient.mailbox()).to.not.equal(newMailbox.address); |
||||
await expect(connectionClient.setMailbox(newMailbox.address)).to.emit( |
||||
connectionClient, |
||||
'MailboxSet', |
||||
); |
||||
expect(await connectionClient.mailbox()).to.equal(newMailbox.address); |
||||
}); |
||||
|
||||
it('non-owner cannot set mailbox', async () => { |
||||
await expect( |
||||
connectionClient.connect(nonOwner).setMailbox(newMailbox.address), |
||||
).to.be.revertedWith(ONLY_OWNER_REVERT_MSG); |
||||
}); |
||||
|
||||
describe('#setInterchainGasPaymaster', () => { |
||||
let newPaymaster: InterchainGasPaymaster; |
||||
|
||||
before(async () => { |
||||
const paymasterFactory = new TestInterchainGasPaymaster__factory(signer); |
||||
newPaymaster = await paymasterFactory.deploy(); |
||||
}); |
||||
|
||||
it('Allows owner to set the interchainGasPaymaster', async () => { |
||||
await connectionClient.setInterchainGasPaymaster(newPaymaster.address); |
||||
expect(await connectionClient.interchainGasPaymaster()).to.equal( |
||||
newPaymaster.address, |
||||
); |
||||
}); |
||||
|
||||
it('Emits the SetInterchainGasPaymaster event', async () => { |
||||
await expect( |
||||
connectionClient.setInterchainGasPaymaster(newPaymaster.address), |
||||
) |
||||
.to.emit(connectionClient, 'InterchainGasPaymasterSet') |
||||
.withArgs(newPaymaster.address); |
||||
}); |
||||
|
||||
it('Reverts a call from non-owner', async () => { |
||||
await expect( |
||||
connectionClient |
||||
.connect(nonOwner) |
||||
.setInterchainGasPaymaster(newPaymaster.address), |
||||
).to.be.revertedWith(ONLY_OWNER_REVERT_MSG); |
||||
}); |
||||
}); |
||||
}); |
Loading…
Reference in new issue