The home for Hyperlane core contracts, sdk packages, and other infrastructure
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
hyperlane-monorepo/solidity/contracts/isms/hook/layer-zero/LayerZeroV2Ism.sol

115 lines
3.7 KiB

// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
/*@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@ HYPERLANE @@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/
// ============ Internal Imports ============
import {IInterchainSecurityModule} from "../../../interfaces/IInterchainSecurityModule.sol";
import {Message} from "../../../libs/Message.sol";
import {TypeCasts} from "../../../libs/TypeCasts.sol";
import {AbstractMessageIdAuthorizedIsm} from "../AbstractMessageIdAuthorizedIsm.sol";
// ============ External Imports ============
import {Origin} from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
/**
* @title LayerZeroV2Ism
* @notice Uses LayerZero V2 deliver and verify a messages Id
*/
contract LayerZeroV2Ism is AbstractMessageIdAuthorizedIsm {
using Message for bytes;
using TypeCasts for bytes32;
// Layerzero endpoint address
address public immutable endpoint;
// ============ Constants ============
uint8 public constant moduleType =
uint8(IInterchainSecurityModule.Types.NULL);
// @dev the offset of msg.data where the function parameters (as bytes) begins. 4 bytes is always used when encoding the function selector
uint8 constant FUNC_SELECTOR_OFFSET = 4;
// @dev the offset of msg.data where Origin.sender begins. 32 is always used since calldata comes in 32 bytes.
uint8 constant ORIGIN_SENDER_OFFSET = FUNC_SELECTOR_OFFSET + 32;
// ============ Constructor ============
constructor(address _endpoint) {
require(
_endpoint != address(0),
"LayerZeroV2Ism: invalid authorized endpoint"
);
endpoint = _endpoint;
}
/**
* @notice Entry point for receiving msg/packet from the LayerZero endpoint.
* @param _lzMessage The payload of the received message.
* @dev Authorization verifcation is done within verifyMessageId() -> _isAuthorized()
*/
function lzReceive(
Origin calldata,
bytes32,
bytes calldata _lzMessage,
address,
bytes calldata
) external payable {
verifyMessageId(_messageId(_lzMessage));
}
// ============ Internal function ============
/**
* @notice Slices the messageId from the message delivered from LayerZeroV2Hook
* @dev message is created as abi.encodeCall(AbstractMessageIdAuthorizedIsm.verifyMessageId, id)
* @dev _message will be 36 bytes (4 bytes for function selector, and 32 bytes for messageId)
*/
function _messageId(
bytes calldata _message
) internal pure returns (bytes32) {
return bytes32(_message[FUNC_SELECTOR_OFFSET:]);
}
/**
* @notice Validates criterias to verify a message
* @dev this is called by AbstractMessageIdAuthorizedIsm.verifyMessageId
* @dev parses msg.value to get parameters from lzReceive()
*/
function _isAuthorized() internal view override returns (bool) {
require(_isAuthorizedHook(), "LayerZeroV2Ism: hook is not authorized");
require(
_isAuthorizedEndPoint(),
"LayerZeroV2Ism: endpoint is not authorized"
);
return true;
}
/**
* @notice check if origin.sender is the authorized hook
*/
function _isAuthorizedHook() internal view returns (bool) {
return bytes32(msg.data[ORIGIN_SENDER_OFFSET:]) == authorizedHook;
}
/**
* @notice check if LayerZero endpoint is authorized
*/
function _isAuthorizedEndPoint() internal view returns (bool) {
return msg.sender == endpoint;
}
}