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/hooks/layer-zero/LayerZeroV1Hook.sol

119 lines
4.5 KiB

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/*@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@ HYPERLANE @@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/
import {ILayerZeroEndpoint} from "@layerzerolabs/lz-evm-v1-0.7/contracts/interfaces/ILayerZeroEndpoint.sol";
import {Message} from "../../libs/Message.sol";
import {TypeCasts} from "../../libs/TypeCasts.sol";
import {MailboxClient} from "../../client/MailboxClient.sol";
import {Indexed} from "../../libs/Indexed.sol";
import {IPostDispatchHook} from "../../interfaces/hooks/IPostDispatchHook.sol";
import {AbstractPostDispatchHook} from "../libs/AbstractPostDispatchHook.sol";
import {StandardHookMetadata} from "../libs/StandardHookMetadata.sol";
struct LayerZeroMetadata {
/// @dev the destination chain identifier
uint16 dstChainId;
/// @dev the user app address on this EVM chain. Contract address that calls Endpoint.send(). Used for LZ user app config lookup
address userApplication;
/// @dev if the source transaction is cheaper than the amount of value passed, refund the additional amount to this address
address refundAddress;
/// @dev the custom message to send over LayerZero
bytes payload;
/// @dev the address on destination chain (in bytes). A 40 length byte with remote and local addresses concatenated.
bytes destination;
/// @dev parameters for the adapter service, e.g. send some dust native token to dstChain
bytes adapterParam;
}
contract LayerZeroV1Hook is AbstractPostDispatchHook, MailboxClient {
using StandardHookMetadata for bytes;
using Message for bytes;
using TypeCasts for bytes32;
ILayerZeroEndpoint public immutable lZEndpoint;
constructor(address _mailbox, address _lZEndpoint) MailboxClient(_mailbox) {
lZEndpoint = ILayerZeroEndpoint(_lZEndpoint);
}
// ============ External Functions ============
/// @inheritdoc IPostDispatchHook
function hookType() external pure override returns (uint8) {
return uint8(IPostDispatchHook.Types.LAYER_ZERO_V1);
}
/// @inheritdoc AbstractPostDispatchHook
function _postDispatch(
bytes calldata metadata,
bytes calldata message
) internal virtual override {
// ensure hook only dispatches messages that are dispatched by the mailbox
bytes32 id = message.id();
require(_isLatestDispatched(id), "message not dispatched by mailbox");
bytes calldata lZMetadata = metadata.getCustomMetadata();
LayerZeroMetadata memory layerZeroMetadata = parseLzMetadata(
lZMetadata
);
lZEndpoint.send{value: msg.value}(
layerZeroMetadata.dstChainId,
layerZeroMetadata.destination,
layerZeroMetadata.payload,
payable(layerZeroMetadata.refundAddress),
address(0), // _zroPaymentAddress is hardcoded to addr(0) because zro tokens should not be directly accepted
layerZeroMetadata.adapterParam
);
}
/// @inheritdoc AbstractPostDispatchHook
function _quoteDispatch(
bytes calldata metadata,
bytes calldata
) internal view virtual override returns (uint256 nativeFee) {
bytes calldata lZMetadata = metadata.getCustomMetadata();
LayerZeroMetadata memory layerZeroMetadata = parseLzMetadata(
lZMetadata
);
(nativeFee, ) = lZEndpoint.estimateFees(
layerZeroMetadata.dstChainId,
layerZeroMetadata.userApplication,
layerZeroMetadata.payload,
false, // _payInZRO is hardcoded to false because zro tokens should not be directly accepted
layerZeroMetadata.adapterParam
);
}
/**
* @notice Formats LayerZero metadata using default abi encoding
* @param layerZeroMetadata LayerZero specific metadata
* @return ABI encoded metadata
*/
function formatLzMetadata(
LayerZeroMetadata calldata layerZeroMetadata
) public pure returns (bytes memory) {
return abi.encode(layerZeroMetadata);
}
/**
* @notice Decodes LayerZero metadata. Should be used after formatLzMetadata()
* @param lZMetadata ABI encoded metadata
*/
function parseLzMetadata(
bytes calldata lZMetadata
) public pure returns (LayerZeroMetadata memory parsedLayerZeroMetadata) {
(parsedLayerZeroMetadata) = abi.decode(lZMetadata, (LayerZeroMetadata));
}
}