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/OPL2ToL1Ism.sol

110 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 {TypeCasts} from "../../libs/TypeCasts.sol";
import {Message} from "../../libs/Message.sol";
import {OPL2ToL1Metadata} from "../../libs/OPL2ToL1Metadata.sol";
import {AbstractMessageIdAuthorizedIsm} from "./AbstractMessageIdAuthorizedIsm.sol";
// ============ External Imports ============
import {ICrossDomainMessenger} from "../../interfaces/optimism/ICrossDomainMessenger.sol";
import {IOptimismPortal} from "../../interfaces/optimism/IOptimismPortal.sol";
import {CrossChainEnabledOptimism} from "@openzeppelin/contracts/crosschain/optimism/CrossChainEnabledOptimism.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
/**
* @title OPL2ToL1Ism
* @notice Uses the native Optimism bridge to verify interchain messages from L2 to L1.
*/
contract OPL2ToL1Ism is
CrossChainEnabledOptimism,
AbstractMessageIdAuthorizedIsm
{
using TypeCasts for address;
using Message for bytes;
using OPL2ToL1Metadata for bytes;
// ============ Constants ============
// module type for the ISM
uint8 public constant moduleType =
uint8(IInterchainSecurityModule.Types.OP_L2_TO_L1);
// OptimismPortal contract on L1 to finalize withdrawal from L1
IOptimismPortal public immutable portal;
// ============ Constructor ============
constructor(address _messenger) CrossChainEnabledOptimism(_messenger) {
address _portal = ICrossDomainMessenger(_messenger).PORTAL();
require(
Address.isContract(_portal),
"OPL2ToL1Ism: invalid OptimismPortal contract"
);
portal = IOptimismPortal(_portal);
}
// ============ External Functions ============
/// @inheritdoc IInterchainSecurityModule
function verify(
bytes calldata metadata,
bytes calldata message
) external override returns (bool) {
if (!isVerified(message)) {
_verifyWithPortalCall(metadata, message);
require(isVerified(message), "OPL2ToL1Ism: message not verified");
}
releaseValueToRecipient(message);
return true;
}
// ============ Internal function ============
/**
* @notice Verify message directly using the portal.finalizeWithdrawal function.
* @dev This is a fallback in case the message is not verified by the stateful verify function first.
*/
function _verifyWithPortalCall(
bytes calldata metadata,
bytes calldata message
) internal {
require(
metadata.checkCalldataLength(),
"OPL2ToL1Ism: invalid data length"
);
require(
metadata.messageId() == message.id(),
"OPL2ToL1Ism: invalid message id"
);
IOptimismPortal.WithdrawalTransaction memory withdrawal = abi.decode(
metadata,
(IOptimismPortal.WithdrawalTransaction)
);
// if the finalizeWithdrawalTransaction call is successful, the message is verified
portal.finalizeWithdrawalTransaction(withdrawal);
}
/// @inheritdoc AbstractMessageIdAuthorizedIsm
function _isAuthorized() internal view override returns (bool) {
return
_crossChainSender() == TypeCasts.bytes32ToAddress(authorizedHook);
}
}