parent
f6ad1e9a95
commit
943b0162fa
@ -0,0 +1,137 @@ |
||||
// SPDX-License-Identifier: MIT |
||||
|
||||
pragma solidity >=0.5.0; |
||||
|
||||
import "./ILayerZeroUserApplicationConfig.sol"; |
||||
|
||||
interface ILayerZeroEndpoint is ILayerZeroUserApplicationConfig { |
||||
// @notice send a LayerZero message to the specified address at a LayerZero endpoint. |
||||
// @param _dstChainId - the destination chain identifier |
||||
// @param _destination - the address on destination chain (in bytes). address length/format may vary by chains |
||||
// @param _payload - a custom bytes payload to send to the destination contract |
||||
// @param _refundAddress - if the source transaction is cheaper than the amount of value passed, refund the additional amount to this address |
||||
// @param _zroPaymentAddress - the address of the ZRO token holder who would pay for the transaction |
||||
// @param _adapterParams - parameters for custom functionality. e.g. receive airdropped native gas from the relayer on destination |
||||
function send( |
||||
uint16 _dstChainId, |
||||
bytes calldata _destination, |
||||
bytes calldata _payload, |
||||
address payable _refundAddress, |
||||
address _zroPaymentAddress, |
||||
bytes calldata _adapterParams |
||||
) external payable; |
||||
|
||||
// @notice used by the messaging library to publish verified payload |
||||
// @param _srcChainId - the source chain identifier |
||||
// @param _srcAddress - the source contract (as bytes) at the source chain |
||||
// @param _dstAddress - the address on destination chain |
||||
// @param _nonce - the unbound message ordering nonce |
||||
// @param _gasLimit - the gas limit for external contract execution |
||||
// @param _payload - verified payload to send to the destination contract |
||||
function receivePayload( |
||||
uint16 _srcChainId, |
||||
bytes calldata _srcAddress, |
||||
address _dstAddress, |
||||
uint64 _nonce, |
||||
uint256 _gasLimit, |
||||
bytes calldata _payload |
||||
) external; |
||||
|
||||
// @notice get the inboundNonce of a lzApp from a source chain which could be EVM or non-EVM chain |
||||
// @param _srcChainId - the source chain identifier |
||||
// @param _srcAddress - the source chain contract address |
||||
function getInboundNonce(uint16 _srcChainId, bytes calldata _srcAddress) |
||||
external |
||||
view |
||||
returns (uint64); |
||||
|
||||
// @notice get the outboundNonce from this source chain which, consequently, is always an EVM |
||||
// @param _srcAddress - the source chain contract address |
||||
function getOutboundNonce(uint16 _dstChainId, address _srcAddress) |
||||
external |
||||
view |
||||
returns (uint64); |
||||
|
||||
// @notice gets a quote in source native gas, for the amount that send() requires to pay for message delivery |
||||
// @param _dstChainId - the destination chain identifier |
||||
// @param _userApplication - the user app address on this EVM chain |
||||
// @param _payload - the custom message to send over LayerZero |
||||
// @param _payInZRO - if false, user app pays the protocol fee in native token |
||||
// @param _adapterParam - parameters for the adapter service, e.g. send some dust native token to dstChain |
||||
function estimateFees( |
||||
uint16 _dstChainId, |
||||
address _userApplication, |
||||
bytes calldata _payload, |
||||
bool _payInZRO, |
||||
bytes calldata _adapterParam |
||||
) external view returns (uint256 nativeFee, uint256 zroFee); |
||||
|
||||
// @notice get this Endpoint's immutable source identifier |
||||
function getChainId() external view returns (uint16); |
||||
|
||||
// @notice the interface to retry failed message on this Endpoint destination |
||||
// @param _srcChainId - the source chain identifier |
||||
// @param _srcAddress - the source chain contract address |
||||
// @param _payload - the payload to be retried |
||||
function retryPayload( |
||||
uint16 _srcChainId, |
||||
bytes calldata _srcAddress, |
||||
bytes calldata _payload |
||||
) external; |
||||
|
||||
// @notice query if any STORED payload (message blocking) at the endpoint. |
||||
// @param _srcChainId - the source chain identifier |
||||
// @param _srcAddress - the source chain contract address |
||||
function hasStoredPayload(uint16 _srcChainId, bytes calldata _srcAddress) |
||||
external |
||||
view |
||||
returns (bool); |
||||
|
||||
// @notice query if the _libraryAddress is valid for sending msgs. |
||||
// @param _userApplication - the user app address on this EVM chain |
||||
function getSendLibraryAddress(address _userApplication) |
||||
external |
||||
view |
||||
returns (address); |
||||
|
||||
// @notice query if the _libraryAddress is valid for receiving msgs. |
||||
// @param _userApplication - the user app address on this EVM chain |
||||
function getReceiveLibraryAddress(address _userApplication) |
||||
external |
||||
view |
||||
returns (address); |
||||
|
||||
// @notice query if the non-reentrancy guard for send() is on |
||||
// @return true if the guard is on. false otherwise |
||||
function isSendingPayload() external view returns (bool); |
||||
|
||||
// @notice query if the non-reentrancy guard for receive() is on |
||||
// @return true if the guard is on. false otherwise |
||||
function isReceivingPayload() external view returns (bool); |
||||
|
||||
// @notice get the configuration of the LayerZero messaging library of the specified version |
||||
// @param _version - messaging library version |
||||
// @param _chainId - the chainId for the pending config change |
||||
// @param _userApplication - the contract address of the user application |
||||
// @param _configType - type of configuration. every messaging library has its own convention. |
||||
function getConfig( |
||||
uint16 _version, |
||||
uint16 _chainId, |
||||
address _userApplication, |
||||
uint256 _configType |
||||
) external view returns (bytes memory); |
||||
|
||||
// @notice get the send() LayerZero messaging library version |
||||
// @param _userApplication - the contract address of the user application |
||||
function getSendVersion(address _userApplication) |
||||
external |
||||
view |
||||
returns (uint16); |
||||
|
||||
// @notice get the lzReceive() LayerZero messaging library version |
||||
// @param _userApplication - the contract address of the user application |
||||
function getReceiveVersion(address _userApplication) |
||||
external |
||||
view |
||||
returns (uint16); |
||||
} |
@ -0,0 +1,17 @@ |
||||
// SPDX-License-Identifier: MIT |
||||
|
||||
pragma solidity >=0.5.0; |
||||
|
||||
interface ILayerZeroReceiver { |
||||
// @notice LayerZero endpoint will invoke this function to deliver the message on the destination |
||||
// @param _srcChainId - the source endpoint identifier |
||||
// @param _srcAddress - the source sending contract address from the source chain |
||||
// @param _nonce - the ordered message nonce |
||||
// @param _payload - the signed payload is the UA bytes has encoded to be sent |
||||
function lzReceive( |
||||
uint16 _srcChainId, |
||||
bytes calldata _srcAddress, |
||||
uint64 _nonce, |
||||
bytes calldata _payload |
||||
) external; |
||||
} |
@ -0,0 +1,31 @@ |
||||
// SPDX-License-Identifier: MIT |
||||
|
||||
pragma solidity >=0.5.0; |
||||
|
||||
interface ILayerZeroUserApplicationConfig { |
||||
// @notice set the configuration of the LayerZero messaging library of the specified version |
||||
// @param _version - messaging library version |
||||
// @param _chainId - the chainId for the pending config change |
||||
// @param _configType - type of configuration. every messaging library has its own convention. |
||||
// @param _config - configuration in the bytes. can encode arbitrary content. |
||||
function setConfig( |
||||
uint16 _version, |
||||
uint16 _chainId, |
||||
uint256 _configType, |
||||
bytes calldata _config |
||||
) external; |
||||
|
||||
// @notice set the send() LayerZero messaging library version to _version |
||||
// @param _version - new messaging library version |
||||
function setSendVersion(uint16 _version) external; |
||||
|
||||
// @notice set the lzReceive() LayerZero messaging library version to _version |
||||
// @param _version - new messaging library version |
||||
function setReceiveVersion(uint16 _version) external; |
||||
|
||||
// @notice Only when the UA needs to resume the message flow in blocking mode and clear the stored payload |
||||
// @param _srcChainId - the chainId of the source chain |
||||
// @param _srcAddress - the contract address of the source contract at the source chain |
||||
function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) |
||||
external; |
||||
} |
@ -0,0 +1,350 @@ |
||||
// SPDX-License-Identifier: Apache-2.0 |
||||
pragma solidity ^0.8.13; |
||||
|
||||
import {Router} from "../../Router.sol"; |
||||
import {TypeCasts} from "../../libs/TypeCasts.sol"; |
||||
import {IMessageRecipient} from "../../interfaces/IMessageRecipient.sol"; |
||||
import {ILayerZeroEndpoint} from "../../interfaces/middleware/layerzero/ILayerZeroEndpoint.sol"; |
||||
import {ILayerZeroReceiver} from "../../interfaces/middleware/layerzero/ILayerZeroReceiver.sol"; |
||||
|
||||
/** |
||||
* @title LayerZeroRouter |
||||
* @notice Example of middleware to use hyperlane in a layerzero app on layerZero |
||||
* @dev Implemented send() and a virtual lzReceive(). |
||||
* @dev Please make sure to edit lzReceive() and setEstGasAmount() to match gas usage of lzReceive() in your app |
||||
* @dev Run `forge test --match-contract LayerZeroRouterTest` to see tests |
||||
*/ |
||||
|
||||
abstract contract LayerZeroRouter is Router, ILayerZeroEndpoint { |
||||
mapping(uint16 => uint32) layerZeroToHyperlaneDomain; |
||||
mapping(uint32 => uint16) hyperlaneToLayerZeroDomain; |
||||
|
||||
ILayerZeroReceiver public layerZeroReceiver; |
||||
|
||||
error LayerZeroDomainNotMapped(uint16); |
||||
error HyperlaneDomainNotMapped(uint32); |
||||
|
||||
uint256 estGasAmount; |
||||
|
||||
function initialize(address _owner, address _mailbox) public initializer { |
||||
_transferOwnership(_owner); |
||||
__Router_initialize(_mailbox); |
||||
} |
||||
|
||||
function initialize( |
||||
address _owner, |
||||
address _mailbox, |
||||
address _interchainGasPaymaster |
||||
) public initializer { |
||||
_transferOwnership(_owner); |
||||
__Router_initialize(_mailbox, _interchainGasPaymaster); |
||||
} |
||||
|
||||
function initialize( |
||||
address _owner, |
||||
address _mailbox, |
||||
address _interchainGasPaymaster, |
||||
address _interchainSecurityModule |
||||
) public initializer { |
||||
_transferOwnership(_owner); |
||||
__Router_initialize( |
||||
_mailbox, |
||||
_interchainGasPaymaster, |
||||
_interchainSecurityModule |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* @notice Adds a domain ID mapping from layerZeroDomain/hyperlaneDomain domain IDs and vice versa |
||||
* @param _layerZeroDomains An array of layerZeroDomain domain IDs |
||||
* @param _hyperlaneDomains An array of hyperlaneDomain domain IDs |
||||
*/ |
||||
function mapDomains( |
||||
uint16[] calldata _layerZeroDomains, |
||||
uint32[] calldata _hyperlaneDomains |
||||
) external onlyOwner { |
||||
for (uint256 i = 0; i < _layerZeroDomains.length; i += 1) { |
||||
layerZeroToHyperlaneDomain[ |
||||
_layerZeroDomains[i] |
||||
] = _hyperlaneDomains[i]; |
||||
hyperlaneToLayerZeroDomain[ |
||||
_hyperlaneDomains[i] |
||||
] = _layerZeroDomains[i]; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @notice Gets layerZero domain ID from hyperlane domain ID |
||||
* @param _hyperlaneDomain The hyperlane domain ID |
||||
*/ |
||||
function getLayerZeroDomain(uint32 _hyperlaneDomain) |
||||
public |
||||
view |
||||
returns (uint16 layerZeroDomain) |
||||
{ |
||||
layerZeroDomain = hyperlaneToLayerZeroDomain[_hyperlaneDomain]; |
||||
if (layerZeroDomain == 0) { |
||||
revert HyperlaneDomainNotMapped(_hyperlaneDomain); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @notice Gets hyperlane domain ID from layerZero domain ID |
||||
* @param _layerZeroDomain The layerZero domain ID |
||||
*/ |
||||
function getHyperlaneDomain(uint16 _layerZeroDomain) |
||||
public |
||||
view |
||||
returns (uint32 hyperlaneDomain) |
||||
{ |
||||
hyperlaneDomain = layerZeroToHyperlaneDomain[_layerZeroDomain]; |
||||
if (hyperlaneDomain == 0) { |
||||
revert LayerZeroDomainNotMapped(_layerZeroDomain); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @notice handles the version Adapter Parameters for LayerZero |
||||
* @param _adapterParams The adapter params used in LayerZero sends |
||||
*/ |
||||
function _interpretAdapterParamsV1(bytes memory _adapterParams) |
||||
internal |
||||
pure |
||||
returns (uint256 gasAmount) |
||||
{ |
||||
uint16 version; |
||||
require(_adapterParams.length == 34, "Please check your adapterparams"); |
||||
(version, gasAmount) = abi.decode(_adapterParams, (uint16, uint256)); |
||||
} |
||||
|
||||
/** |
||||
* @notice handles the version Adapter Parameters for LayerZero |
||||
* @param _adapterParams The adapter params used in LayerZero sends |
||||
*/ |
||||
function _interpretAdapterParamsV2(bytes memory _adapterParams) |
||||
internal |
||||
pure |
||||
returns ( |
||||
uint256 gasAmount, |
||||
uint256 nativeForDst, |
||||
address addressOnDst |
||||
) |
||||
{ |
||||
require(_adapterParams.length == 86, "Please check your adapterparams"); |
||||
uint16 version; |
||||
(version, gasAmount, nativeForDst, addressOnDst) = abi.decode( |
||||
_adapterParams, |
||||
(uint16, uint256, uint256, address) |
||||
); |
||||
} |
||||
|
||||
function splitAddress(bytes memory hexString) |
||||
public |
||||
pure |
||||
returns (address, address) |
||||
{ |
||||
// bytes memory byteArray = bytes(hexString); |
||||
require( |
||||
hexString.length == 40, |
||||
"Input string must be 40 characters long" |
||||
); |
||||
|
||||
bytes20 firstAddress; |
||||
bytes20 secondAddress; |
||||
|
||||
assembly { |
||||
firstAddress := mload(add(hexString, 0x20)) |
||||
secondAddress := mload(add(hexString, 0x30)) |
||||
} |
||||
|
||||
return (address(firstAddress), address(secondAddress)); |
||||
} |
||||
|
||||
/** |
||||
* @notice Sends a hyperlane message using LayerZero endpoint interface |
||||
* @dev NOTE: Layerzero's documentation is inconsistent in github vs docs. Following: https://layerzero.gitbook.io/docs/evm-guides/master/how-to-send-a-message |
||||
* @param _dstChainId - the destination chain identifier |
||||
* @param _remoteAndLocalAddresses - remote address concated with local address packed into 40 bytes |
||||
* @param _payload - the payload to be sent to the destination chain |
||||
* @param _refundAddress - the address to refund the gas fees to |
||||
* @param _zroPaymentAddress - not used (only for LayerZero) |
||||
* @param _adapterParams - the adapter params used in LayerZero sends |
||||
*/ |
||||
function send( |
||||
uint16 _dstChainId, |
||||
bytes memory _remoteAndLocalAddresses, |
||||
bytes calldata _payload, |
||||
address payable _refundAddress, |
||||
address _zroPaymentAddress, |
||||
bytes memory _adapterParams |
||||
) external payable override { |
||||
uint32 dstChainId32 = layerZeroToHyperlaneDomain[_dstChainId]; |
||||
_mustHaveRemoteRouter(dstChainId32); |
||||
address remoteAddr; |
||||
address localAddr; |
||||
if (_remoteAndLocalAddresses.length == 40) { |
||||
(remoteAddr, localAddr) = splitAddress(_remoteAndLocalAddresses); |
||||
} else if (_remoteAndLocalAddresses.length == 32) { |
||||
remoteAddr = abi.decode(_remoteAndLocalAddresses, (address)); |
||||
} else { |
||||
revert("Invalid remote and local addresses"); |
||||
} |
||||
|
||||
bytes memory adapterParams; |
||||
uint256 gasFees; |
||||
if (_adapterParams.length > 0) { |
||||
if (_adapterParams.length == 33) { |
||||
gasFees = _interpretAdapterParamsV1(_adapterParams); |
||||
} else if (_adapterParams.length == 86) { |
||||
uint256 nativeForDst; |
||||
address addressOnDst; |
||||
( |
||||
gasFees, |
||||
nativeForDst, |
||||
addressOnDst |
||||
) = _interpretAdapterParamsV2(_adapterParams); |
||||
} else { |
||||
revert("Invalid adapter params"); |
||||
} |
||||
} else { |
||||
(gasFees, ) = estimateFees( |
||||
_dstChainId, |
||||
msg.sender, |
||||
_payload, |
||||
_zroPaymentAddress != address(0x0), |
||||
adapterParams |
||||
); |
||||
} |
||||
|
||||
require(msg.value >= gasFees, "Not enough fee for gas"); |
||||
|
||||
bytes32 _messageId = mailbox.dispatch( |
||||
dstChainId32, |
||||
TypeCasts.addressToBytes32(remoteAddr), |
||||
_payload |
||||
); |
||||
|
||||
interchainGasPaymaster.payForGas{value: msg.value}( |
||||
_messageId, |
||||
dstChainId32, |
||||
gasFees, |
||||
_refundAddress |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* @notice The internal Router `handle` function which extracts the true recipient of the message and passes the translated hyperlane domain ID to lzReceive |
||||
* @param _originHyperlaneDomain the origin domain as specified by Hyperlane |
||||
* @param _sender The sender address |
||||
* @param _message The wrapped message to include sender and recipient |
||||
*/ |
||||
function handle( |
||||
uint32 _originHyperlaneDomain, |
||||
bytes32 _sender, |
||||
bytes calldata _message |
||||
) |
||||
public |
||||
override |
||||
onlyMailbox |
||||
onlyRemoteRouter(_originHyperlaneDomain, _sender) |
||||
{ |
||||
_handle(_originHyperlaneDomain, _sender, _message); |
||||
} |
||||
|
||||
function _handle( |
||||
uint32 _originHyperlaneDomain, |
||||
bytes32 _sender, |
||||
bytes calldata _message |
||||
) internal override { |
||||
uint16 srcChainId = getLayerZeroDomain(_originHyperlaneDomain); |
||||
lzReceive(srcChainId, _sender, 0, _message); //Note nonce does not exist in hyperlane on the destination chain |
||||
} |
||||
|
||||
/** |
||||
* @notice Originally LayerZero endpoint which will be evoked by this contract's handle function |
||||
* @dev override from ILayerZeroEndpoint.sol |
||||
* @param _srcChainId - the source endpoint identifier |
||||
* @param _srcAddress - the source sending contract address from the source chain |
||||
* @param _nonce - the ordered message nonce (not used in Hyperlane) |
||||
* @param _payload - the signed payload is the UA bytes has encoded to be sent |
||||
*/ |
||||
function lzReceive( |
||||
uint16 _srcChainId, |
||||
bytes32 _srcAddress, |
||||
uint64 _nonce, |
||||
bytes memory _payload |
||||
) public virtual {} |
||||
|
||||
/** |
||||
* @notice Gets a quote in source native gas, for the amount that send() requires to pay for message delivery |
||||
* @dev override from ILayerZeroEndpoint.sol |
||||
* @param _dstChainId - the destination chain identifier |
||||
* @param _userApplication - the user app address on this EVM chain |
||||
* @param _payload - the custom message to send over LayerZero |
||||
* @param _payInZRO - if false, user app pays the protocol fee in native token |
||||
* @param _adapterParams - parameters for the adapter service, e.g. send some dust native token to dstChain |
||||
*/ |
||||
function estimateFees( |
||||
uint16 _dstChainId, |
||||
address _userApplication, |
||||
bytes memory _payload, |
||||
bool _payInZRO, |
||||
bytes memory _adapterParams |
||||
) public view override returns (uint256 nativeFee, uint256 zroFee) { |
||||
require(estGasAmount > 0, "Please set gas amount"); |
||||
return ( |
||||
interchainGasPaymaster.quoteGasPayment( |
||||
layerZeroToHyperlaneDomain[_dstChainId], |
||||
estGasAmount |
||||
), |
||||
0 |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* @notice Sets the gas amount for the estimateFees function since this will depend upon gas your lzreceive() uses |
||||
* @dev Used for showcase and testing suggest editing getGasAmount |
||||
* @param _gas The amount of gas to set |
||||
*/ |
||||
|
||||
function setEstGasAmount(uint256 _gas) external onlyOwner { |
||||
estGasAmount = _gas; |
||||
} |
||||
|
||||
/** |
||||
* @notice Gets the gas amount for the estimateFees function |
||||
* @dev Please override this to however you wish to calculate your gas usage on destiniation chain |
||||
* @param _payload The payload to be sent to the destination chain |
||||
*/ |
||||
|
||||
function getEstGasAmount(bytes memory _payload) |
||||
public |
||||
view |
||||
returns (uint256) |
||||
{ |
||||
return estGasAmount; |
||||
} |
||||
|
||||
/** |
||||
* @notice Gets the chain ID of the current chain |
||||
* @dev override from ILayerZeroEndpoint.sol -- NOTE OVERFLOW RISK |
||||
*/ |
||||
function getChainId() external view override returns (uint16) { |
||||
return hyperlaneToLayerZeroDomain[mailbox.localDomain()]; |
||||
} |
||||
|
||||
/** |
||||
* @notice Gets the mailbox count this source chain since hyperlane does not have nonce |
||||
* @dev override from ILayerZeroEndpoint.sol |
||||
* @param _dstChainId - the destination chain identifier |
||||
* @param _srcAddress - the source chain contract address |
||||
* |
||||
*/ |
||||
function getOutboundNonce(uint16 _dstChainId, address _srcAddress) |
||||
external |
||||
view |
||||
returns (uint64) |
||||
{ |
||||
return uint64(mailbox.count()); |
||||
} |
||||
} |
@ -0,0 +1,161 @@ |
||||
// SPDX-License-Identifier: Apache-2.0 |
||||
pragma solidity ^0.8.13; |
||||
|
||||
import {Router} from "../../Router.sol"; |
||||
import {TypeCasts} from "../../libs/TypeCasts.sol"; |
||||
import {IMessageRecipient} from "../../interfaces/IMessageRecipient.sol"; |
||||
import {ILayerZeroEndpoint} from "../../interfaces/middleware/layerzero/ILayerZeroEndpoint.sol"; |
||||
import {ILayerZeroReceiver} from "../../interfaces/middleware/layerzero/ILayerZeroReceiver.sol"; |
||||
import {LayerZeroRouter} from "./LayerZeroRouter.sol"; |
||||
|
||||
/** |
||||
* @title MockLayerZeroRouter |
||||
* @dev Used for testing LayerZeroRouter |
||||
*/ |
||||
|
||||
contract MockLayerZeroRouter is LayerZeroRouter { |
||||
/** |
||||
* @notice Originally LayerZero endpoint which will be evoked by this contract's handle function |
||||
* @dev override from ILayerZeroEndpoint.sol -- NEED UPDATING |
||||
* @param _srcChainId - the source endpoint identifier |
||||
* @param _srcAddress - the source sending contract address from the source chain |
||||
* @param _nonce - the ordered message nonce (not used in Hyperlane) |
||||
* @param _payload - the signed payload is the UA bytes has encoded to be sent |
||||
*/ |
||||
function lzReceive( |
||||
uint16 _srcChainId, |
||||
bytes32 _srcAddress, |
||||
uint64 _nonce, |
||||
bytes memory _payload |
||||
) public override {} |
||||
|
||||
/** |
||||
* @dev Below are the functions that are not supported in the interface "ILayerZeroEndpoint" due to way hyperlane is structured |
||||
*/ |
||||
|
||||
function receivePayload( |
||||
uint16 _srcChainId, |
||||
bytes calldata _srcAddress, |
||||
address _dstAddress, |
||||
uint64 _nonce, |
||||
uint256 _gasLimit, |
||||
bytes calldata _payload |
||||
) external override { |
||||
//Not supported |
||||
} |
||||
|
||||
function getInboundNonce(uint16 _srcChainId, bytes calldata _srcAddress) |
||||
external |
||||
view |
||||
override |
||||
returns (uint64) |
||||
{ |
||||
//Not supported |
||||
return 0; |
||||
} |
||||
|
||||
function retryPayload( |
||||
uint16 _srcChainId, |
||||
bytes calldata _srcAddress, |
||||
bytes calldata _payload |
||||
) external override { |
||||
//Not supported |
||||
} |
||||
|
||||
function hasStoredPayload(uint16 _srcChainId, bytes calldata _srcAddress) |
||||
external |
||||
view |
||||
override |
||||
returns (bool) |
||||
{ |
||||
//Not supported |
||||
return false; |
||||
} |
||||
|
||||
function getSendLibraryAddress(address _userApplication) |
||||
external |
||||
view |
||||
override |
||||
returns (address) |
||||
{ |
||||
//Not supported |
||||
return address(0); |
||||
} |
||||
|
||||
function getReceiveLibraryAddress(address _userApplication) |
||||
external |
||||
view |
||||
override |
||||
returns (address) |
||||
{ |
||||
//Not supported |
||||
return address(0); |
||||
} |
||||
|
||||
function isSendingPayload() external view override returns (bool) { |
||||
//Not supported |
||||
return false; |
||||
} |
||||
|
||||
function isReceivingPayload() external view override returns (bool) { |
||||
//Not supported |
||||
return false; |
||||
} |
||||
|
||||
function getConfig( |
||||
uint16 _version, |
||||
uint16 _chainId, |
||||
address _userApplication, |
||||
uint256 _configType |
||||
) external view returns (bytes memory) { |
||||
//Not supported |
||||
return ""; |
||||
} |
||||
|
||||
function getSendVersion(address _userApplication) |
||||
external |
||||
view |
||||
override |
||||
returns (uint16) |
||||
{ |
||||
//Not supported |
||||
return 0; |
||||
} |
||||
|
||||
function getReceiveVersion(address _userApplication) |
||||
external |
||||
view |
||||
override |
||||
returns (uint16) |
||||
{ |
||||
//Not supported |
||||
return 0; |
||||
} |
||||
|
||||
/** |
||||
* @dev Below are the functions that are not supported in the interface "ILayerZeroUserApplicationConfig" due to way hyperlane is structured |
||||
*/ |
||||
function setConfig( |
||||
uint16 _version, |
||||
uint16 _chainId, |
||||
uint256 _configType, |
||||
bytes calldata _config |
||||
) external override { |
||||
//Not supported |
||||
} |
||||
|
||||
function setSendVersion(uint16 _version) external override { |
||||
//Not supported |
||||
} |
||||
|
||||
function setReceiveVersion(uint16 _version) external override { |
||||
//Not supported |
||||
} |
||||
|
||||
function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) |
||||
external |
||||
override |
||||
{ |
||||
//Not supported |
||||
} |
||||
} |
@ -0,0 +1,195 @@ |
||||
// SPDX-License-Identifier: Apache-2.0 |
||||
pragma solidity ^0.8.13; |
||||
|
||||
import "forge-std/Test.sol"; |
||||
import "../../../contracts/test/TestRecipient.sol"; |
||||
import {MockLayerZeroRouter} from "../../../contracts/middleware/layerzero/MockLayerZeroRouter.sol"; |
||||
import {MockHyperlaneEnvironment, MockMailbox} from "../../../contracts/mock/MockHyperlaneEnvironment.sol"; |
||||
|
||||
import {TypeCasts} from "../../../contracts/libs/TypeCasts.sol"; |
||||
|
||||
contract LayerZeroRouterTest is Test { |
||||
MockHyperlaneEnvironment testEnvironment; |
||||
|
||||
MockLayerZeroRouter originRouter; |
||||
MockLayerZeroRouter destinationRouter; |
||||
|
||||
TestRecipient recipient; |
||||
|
||||
uint16 lzOriginDomain = 123; |
||||
uint16 lzDestinationDomain = 321; |
||||
|
||||
uint32 hlOriginDomain = 456; |
||||
uint32 hlDestinationDomain = 654; |
||||
|
||||
address owner = vm.addr(123); |
||||
|
||||
function setUp() public { |
||||
console.log("Owner Address: %s", owner); |
||||
|
||||
originRouter = new MockLayerZeroRouter(); |
||||
destinationRouter = new MockLayerZeroRouter(); |
||||
|
||||
testEnvironment = new MockHyperlaneEnvironment( |
||||
hlOriginDomain, |
||||
hlDestinationDomain |
||||
); |
||||
|
||||
console.log("Origin Router Address: %s", address(originRouter)); |
||||
console.log( |
||||
"Origin Mailbox: %s", |
||||
address(testEnvironment.mailboxes(hlOriginDomain)) |
||||
); |
||||
console.log( |
||||
"Destination Router Address: %s", |
||||
address(destinationRouter) |
||||
); |
||||
console.log( |
||||
"Destination Mailbox: %s", |
||||
address(testEnvironment.mailboxes(hlDestinationDomain)) |
||||
); |
||||
|
||||
originRouter.initialize( |
||||
owner, |
||||
address(testEnvironment.mailboxes(hlOriginDomain)), |
||||
address(testEnvironment.igps(hlOriginDomain)), |
||||
address(testEnvironment.isms(hlOriginDomain)) |
||||
); |
||||
|
||||
destinationRouter.initialize( |
||||
owner, |
||||
address(testEnvironment.mailboxes(hlDestinationDomain)), |
||||
address(testEnvironment.igps(hlDestinationDomain)), |
||||
address(testEnvironment.isms(hlDestinationDomain)) |
||||
); |
||||
|
||||
uint16[] memory lzDomains = new uint16[](2); |
||||
lzDomains[0] = lzOriginDomain; |
||||
lzDomains[1] = lzDestinationDomain; |
||||
|
||||
uint32[] memory hlDomains = new uint32[](2); |
||||
hlDomains[0] = hlOriginDomain; |
||||
hlDomains[1] = hlDestinationDomain; |
||||
|
||||
originRouter.mapDomains(lzDomains, hlDomains); |
||||
destinationRouter.mapDomains(lzDomains, hlDomains); |
||||
|
||||
originRouter.enrollRemoteRouter( |
||||
hlDestinationDomain, |
||||
TypeCasts.addressToBytes32(address(destinationRouter)) |
||||
); |
||||
destinationRouter.enrollRemoteRouter( |
||||
hlOriginDomain, |
||||
TypeCasts.addressToBytes32(address(originRouter)) |
||||
); |
||||
|
||||
recipient = new TestRecipient(); |
||||
|
||||
//Set expected gas usage |
||||
uint256 gasEstimate = 0.01 ether; |
||||
originRouter.setEstGasAmount(gasEstimate); |
||||
destinationRouter.setEstGasAmount(gasEstimate); |
||||
} |
||||
|
||||
function testCanSendMessage(bytes calldata _messageBody) public { |
||||
uint256 gasPrice = 100000000000000000000; //Need fix here |
||||
|
||||
address a = address(recipient); |
||||
address b = msg.sender; |
||||
bytes memory destination = abi.encodePacked(a, b); |
||||
|
||||
console.log("Recipient: %s", a); |
||||
console.log("Sender: %s", b); |
||||
|
||||
bytes memory payload = abi.encode("abc"); |
||||
|
||||
originRouter.send{value: gasPrice}( |
||||
lzDestinationDomain, |
||||
destination, |
||||
payload, |
||||
payable(address(0)), |
||||
address(0), |
||||
"" |
||||
); |
||||
|
||||
bytes32 senderAsBytes32 = TypeCasts.addressToBytes32( |
||||
address(originRouter) |
||||
); |
||||
|
||||
vm.expectCall( |
||||
address(recipient), |
||||
abi.encodeWithSelector( |
||||
recipient.handle.selector, |
||||
hlOriginDomain, |
||||
senderAsBytes32, |
||||
payload |
||||
) |
||||
); |
||||
testEnvironment.processNextPendingMessage(); |
||||
|
||||
assertEq(recipient.lastData(), payload); |
||||
assertEq(recipient.lastSender(), senderAsBytes32); |
||||
|
||||
bytes memory destinationAs32 = abi.encode(address(recipient)); |
||||
|
||||
originRouter.send{value: gasPrice}( |
||||
lzDestinationDomain, |
||||
destinationAs32, |
||||
payload, |
||||
payable(address(0)), |
||||
address(0), |
||||
"" |
||||
); |
||||
|
||||
vm.expectCall( |
||||
address(recipient), |
||||
abi.encodeWithSelector( |
||||
recipient.handle.selector, |
||||
hlOriginDomain, |
||||
senderAsBytes32, |
||||
payload |
||||
) |
||||
); |
||||
testEnvironment.processNextPendingMessage(); |
||||
|
||||
assertEq(recipient.lastData(), payload); |
||||
assertEq(recipient.lastSender(), senderAsBytes32); |
||||
} |
||||
|
||||
function testCanReceiveMessage(bytes calldata _messageBody) public { |
||||
uint256 gasPrice = 100000000000000000000; //Need fix here |
||||
|
||||
address a = address(destinationRouter); |
||||
address b = msg.sender; |
||||
bytes memory destination = abi.encodePacked(a, b); |
||||
|
||||
console.log("Recipient: %s", a); |
||||
console.log("Sender: %s", b); |
||||
|
||||
bytes memory payload = abi.encode("abc"); |
||||
|
||||
originRouter.send{value: gasPrice}( |
||||
lzDestinationDomain, |
||||
destination, |
||||
payload, |
||||
payable(address(0)), |
||||
address(0), |
||||
"" |
||||
); |
||||
|
||||
bytes32 senderAsBytes32 = TypeCasts.addressToBytes32( |
||||
address(originRouter) |
||||
); |
||||
|
||||
vm.expectCall( |
||||
address(destinationRouter), |
||||
abi.encodeWithSelector( |
||||
destinationRouter.handle.selector, |
||||
hlOriginDomain, |
||||
senderAsBytes32, |
||||
payload |
||||
) |
||||
); |
||||
testEnvironment.processNextPendingMessage(); |
||||
} |
||||
} |
Loading…
Reference in new issue