Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hyperlane integration #10

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 143 additions & 0 deletions src/apps/hyperlane/IncentivizedHyperlaneEscrow.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import { IncentivizedMessageEscrow } from "../../IncentivizedMessageEscrow.sol";
import { ReplacementHook } from "./ReplacementHook.sol";

import {IInterchainSecurityModule, ISpecifiesInterchainSecurityModule} from "./interfaces/IInterchainSecurityModule.sol";
import { IMessageRecipient } from "./interfaces/IMessageRecipient.sol";
import { IPostDispatchHook } from "./interfaces/hooks/IPostDispatchHook.sol";
import { Message } from "./libs/Message.sol";
import { IMailbox } from "./interfaces/IMailbox.sol";

interface IVersioned {
function VERSION() view external returns(uint8);
}

/// @notice Hyperlane implementation of Generalised incentives.
contract IncentivizedHyperlaneEscrow is IncentivizedMessageEscrow, ReplacementHook, ISpecifiesInterchainSecurityModule, IMessageRecipient {
// ============ Libraries ============

using Message for bytes;


error BadChainIdentifier(); // 0x3c1e02c;
error BadMailboxVersion(uint8 VERSION, uint8 messageVersion); // 0x0d8b788f
error BadDomain(uint32 localDomain, uint32 messageDestination); // 0x2da4acb6
error WrongRecipient(address trueRecipient); // 0xb2d27e64
error ISMVerificationFailed(); // 0x902013b
error DeliverMessageDirectlyToGeneralisedIncentvies(); // 0xecb92632


IPostDispatchHook CUSTOM_HOOK = IPostDispatchHook(address(this));
bytes NOTHING = hex"";

uint32 public immutable localDomain;
IMailbox public immutable MAILBOX;
uint8 public immutable VERSION;
IInterchainSecurityModule immutable INTERCHAIN_SECURITY_MODULE;

function interchainSecurityModule() external view returns (IInterchainSecurityModule) {
return INTERCHAIN_SECURITY_MODULE;
}

/// @dev The hyperlane mailbox requires the call to not fail.
/// By always failing, the message cannot be delivered through the hyperlane mailbox.
function handle(
uint32 /* _origin */,
bytes32 /* _sender */,
bytes calldata /* _message */
) external payable {
revert DeliverMessageDirectlyToGeneralisedIncentvies();
}

constructor(address sendLostGasTo, address interchainSecurityModule_, address mailbox_) IncentivizedMessageEscrow(sendLostGasTo){
MAILBOX = IMailbox(mailbox_);

// Collect the chain identifier from the mailbox and store it here.
// localDomain is immutable on mailbox.
localDomain = MAILBOX.localDomain();
VERSION = IVersioned(mailbox_).VERSION();
INTERCHAIN_SECURITY_MODULE = IInterchainSecurityModule(interchainSecurityModule_);
}

/// @notice Get the required cost of the requiredHook. Calling the mailbox directly requires significantly
/// more gas as it would eventually also call this contract.
function _requiredHookQuote() internal view returns(uint256 amount) {
IPostDispatchHook requiredHook = MAILBOX.requiredHook();

bytes memory message = NOTHING;

return amount = requiredHook.quoteDispatch(NOTHING, message);
}

function estimateAdditionalCost() external view returns(address asset, uint256 amount) {
asset = address(0);
amount = _requiredHookQuote();
}

function _getMessageIdentifier(
bytes32 destinationIdentifier,
bytes calldata message
) internal override view returns(bytes32) {
return keccak256(
abi.encodePacked(
msg.sender,
bytes32(block.number),
localDomain,
destinationIdentifier,
message
)
);
}

/// @notice Verify a Hyperlane package. The heavy lifting is done by
/// the ism. This function is based on the function `process`
/// in the Hyperlane Mailbox with optimisations around the errors.
/// @dev Normally, it is enforced that this function is without side-effects.
/// However, the ISM interface allows .verify to modify state.
/// As a result, we cannot manage to promise that.
function _verifyPacket(bytes calldata _metadata, bytes calldata _message) internal override returns(bytes32 sourceIdentifier, bytes memory implementationIdentifier, bytes calldata message_) {
/// CHECKS ///

// Check that the message was intended for this "mailbox".
if (VERSION != _message.version()) revert BadMailboxVersion(VERSION, _message.version());
if (localDomain != _message.destination()) revert BadDomain(localDomain, _message.destination());

// Notice that there is no verification that a message hasn't been already delivered. That is because that is done elsewhere.

// Check if the recipient is this contract.
address recipient = _message.recipientAddress();
if (recipient != address(this)) revert WrongRecipient(recipient);
// We don't have to get the ism, since it is read anyway from this contract. The line would be MAILBOX.recipientIsm(recipient) but it would eventually call this contract anyway.

/// EFFECTS ///

// We are not emitting the events since that is not useful.

/// INTERACTIONS ///

// Verify the message via the interchain security module.
if (!INTERCHAIN_SECURITY_MODULE.verify(_metadata, _message)) revert ISMVerificationFailed();

// Get the application message.
sourceIdentifier = bytes32(uint256(_message.origin()));
implementationIdentifier = abi.encodePacked(_message.sender());
message_ = _message.body();
}

function _sendPacket(bytes32 destinationChainIdentifier, bytes memory destinationImplementation, bytes memory message) internal override returns(uint128 costOfsendPacketInNativeToken) {
// Get the cost of sending wormhole messages.
costOfsendPacketInNativeToken = uint128(_requiredHookQuote());
uint32 destinationDomain = uint32(uint256(destinationChainIdentifier));

// Handoff the message to hyperlane
MAILBOX.dispatch{value: costOfsendPacketInNativeToken}(
uint32(destinationDomain),
bytes32(destinationImplementation),
message,
hex"",
CUSTOM_HOOK
);
}
}
19 changes: 19 additions & 0 deletions src/apps/hyperlane/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Hyperlane Generalised Incentives

This is a Hyperlane implementation of Generalised Incentives. It works by using the Hyperlane Mailbox to emit messages but instead of using the Hyperlane mailbox to verify messages, messages are to be delivered directly to this contract for verification and execution.

The messages are still delivered to the Hyperlane Mailbox for emitting.


# Address encoding:

Hyperlane uses 32 bytes to represent addresses.

EVM -> EVM
```solidity

function _to_bytes32(address target) internal returns(bytes memory) {
return abi.encode(target);
}

```
55 changes: 55 additions & 0 deletions src/apps/hyperlane/ReplacementHook.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import { IncentivizedMessageEscrow } from "../../IncentivizedMessageEscrow.sol";

import { IPostDispatchHook } from "./interfaces/hooks/IPostDispatchHook.sol";

/// @notice Hyperlane implementation of Generalised incentives.
contract ReplacementHook is IPostDispatchHook {
/**
* @notice Returns an enum that represents the type of hook
*/
function hookType() external pure returns (uint8) {
return uint8(Types.INTERCHAIN_GAS_PAYMASTER);
}

/**
* @notice Returns whether the hook supports metadata
* @dev The following params aren't used:
* param metadata metadata
* @return Whether the hook supports metadata
*/
function supportsMetadata(
bytes calldata /* metadata */
) external pure returns (bool) {
return false;
}

/**
* @notice Post action after a message is dispatched via the Mailbox
* @dev The following params aren't used:
* param metadata The metadata required for the hook
* param message The message passed from the Mailbox.dispatch() call
*/
function postDispatch(
bytes calldata /* metadata */,
bytes calldata /* message */
) external payable {
return;
}

/**
* @notice Compute the payment required by the postDispatch call
* @dev The following params aren't used:
* param metadata The metadata required for the hook
* param message The message passed from the Mailbox.dispatch() call
* @return Quoted payment for the postDispatch call
*/
function quoteDispatch(
bytes calldata /* metadata */,
bytes calldata /* message */
) external pure returns (uint256) {
return 0;
}
}
15 changes: 15 additions & 0 deletions src/apps/hyperlane/interfaces/IGasOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;

interface IGasOracle {
struct RemoteGasData {
// The exchange rate of the remote native token quoted in the local native token.
// Scaled with 10 decimals, i.e. 1e10 is "one".
uint128 tokenExchangeRate;
uint128 gasPrice;
}

function getExchangeRateAndGasPrice(
uint32 _destinationDomain
) external view returns (uint128 tokenExchangeRate, uint128 gasPrice);
}
35 changes: 35 additions & 0 deletions src/apps/hyperlane/interfaces/IInterchainGasPaymaster.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;

/**
* @title IInterchainGasPaymaster
* @notice Manages payments on a source chain to cover gas costs of relaying
* messages to destination chains.
*/
interface IInterchainGasPaymaster {
/**
* @notice Emitted when a payment is made for a message's gas costs.
* @param messageId The ID of the message to pay for.
* @param destinationDomain The domain of the destination chain.
* @param gasAmount The amount of destination gas paid for.
* @param payment The amount of native tokens paid.
*/
event GasPayment(
bytes32 indexed messageId,
uint32 indexed destinationDomain,
uint256 gasAmount,
uint256 payment
);

function payForGas(
bytes32 _messageId,
uint32 _destinationDomain,
uint256 _gasAmount,
address _refundAddress
) external payable;

function quoteGasPayment(
uint32 _destinationDomain,
uint256 _gasAmount
) external view returns (uint256);
}
42 changes: 42 additions & 0 deletions src/apps/hyperlane/interfaces/IInterchainSecurityModule.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;

interface IInterchainSecurityModule {
enum Types {
UNUSED,
ROUTING,
AGGREGATION,
LEGACY_MULTISIG,
MERKLE_ROOT_MULTISIG,
MESSAGE_ID_MULTISIG,
NULL, // used with relayer carrying no metadata
CCIP_READ
}

/**
* @notice Returns an enum that represents the type of security model
* encoded by this ISM.
* @dev Relayers infer how to fetch and format metadata.
*/
function moduleType() external view returns (uint8);

/**
* @notice Defines a security model responsible for verifying interchain
* messages based on the provided metadata.
* @param _metadata Off-chain metadata provided by a relayer, specific to
* the security model encoded by the module (e.g. validator signatures)
* @param _message Hyperlane encoded interchain message
* @return True if the message was verified
*/
function verify(
bytes calldata _metadata,
bytes calldata _message
) external returns (bool);
}

interface ISpecifiesInterchainSecurityModule {
function interchainSecurityModule()
external
view
returns (IInterchainSecurityModule);
}
12 changes: 12 additions & 0 deletions src/apps/hyperlane/interfaces/ILiquidityLayerMessageRecipient.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;

interface ILiquidityLayerMessageRecipient {
function handleWithTokens(
uint32 _origin,
bytes32 _sender,
bytes calldata _message,
address _token,
uint256 _amount
) external;
}
13 changes: 13 additions & 0 deletions src/apps/hyperlane/interfaces/ILiquidityLayerRouter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;

interface ILiquidityLayerRouter {
function dispatchWithTokens(
uint32 _destinationDomain,
bytes32 _recipientAddress,
address _token,
uint256 _amount,
string calldata _bridge,
bytes calldata _messageBody
) external returns (bytes32);
}
Loading
Loading