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

Polygon ZKevm hook and ism #3136

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
108 changes: 108 additions & 0 deletions solidity/contracts/hooks/PolygonZkevmHook.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;

/*@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@ HYPERLANE @@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/

// ============ Internal Imports ============
import {StandardHookMetadata} from "./libs/StandardHookMetadata.sol";
import {TypeCasts} from "../libs/TypeCasts.sol";
import {Message} from "../libs/Message.sol";
import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol";

// ============ External Imports ============
import {Address} from "@openzeppelin/contracts/utils/Address.sol";

import {IPolygonZkEVMBridge} from "../interfaces/polygonzkevm/IPolygonZkEVMBridge.sol";
import {MailboxClient} from "../client/MailboxClient.sol";

/**
* @title PolygonZkevmHook
* @notice Message hook to inform the {Polygon zkEVM chain Ism} of messages published through
* the native Polygon zkEVM bridge bridge.
*/
contract PolygonZkevmHook is IPostDispatchHook, MailboxClient {
using StandardHookMetadata for bytes;
using Message for bytes;
using TypeCasts for bytes32;

// ============ Immutable Variables ============
IPolygonZkEVMBridge public immutable zkEvmBridge;

// left-padded address for ISM to verify messages
address public immutable ism;
// Domain of chain on which the ISM is deployed
uint32 public immutable destinationDomain;
// Polygon ZkevmBridge uses networkId 0 for Mainnet and 1 for rollup
uint32 public immutable zkEvmBridgeDestinationNetId;

constructor(
address _mailbox,
uint32 _destinationDomain,
address _ism,
address _zkEvmBridge,
uint32 _zkEvmBridgeDestinationNetId
) MailboxClient(_mailbox) {
require(
Address.isContract(_zkEvmBridge),
"PolygonzkEVMHook: invalid PolygonZkEVMBridge contract"
);
require(
_destinationDomain != 0,
"PolygonzkEVMHook: invalid destination domain"
);
require(
_zkEvmBridgeDestinationNetId <= 1,
"PolygonZkevmIsm: invalid ZkEVMBridge destination network id"
);
ism = _ism;
destinationDomain = _destinationDomain;
zkEvmBridge = IPolygonZkEVMBridge(_zkEvmBridge);
zkEvmBridgeDestinationNetId = uint8(_zkEvmBridgeDestinationNetId);
}

/// @inheritdoc IPostDispatchHook
function supportsMetadata(
bytes calldata
) public pure virtual override returns (bool) {
return true;
}

/// @dev This value is hardcoded to 0 because the Polygon zkEVM bridge does not support fee quotes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how are fees calculated?

Copy link
Author

@curlypoint curlypoint Jan 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Users have to call the claim function on destination chain, we're calling that in verify(metadata,message) call,after fetching the smt proof needed using CCIP.

function quoteDispatch(
bytes calldata,
bytes calldata
) external pure override returns (uint256) {
return 0;
}

/// @inheritdoc IPostDispatchHook
function postDispatch(
bytes calldata metadata,
bytes calldata message
) external payable override {
require(
metadata.msgValue(0) < 2 ** 255,
"PolygonzkEVMHook: msgValue must be less than 2 ** 255"
);
bytes32 messageId = message.id();

zkEvmBridge.bridgeMessage{value: msg.value}(
zkEvmBridgeDestinationNetId,
address(ism),
true,
curlypoint marked this conversation as resolved.
Show resolved Hide resolved
abi.encodePacked(messageId)
);
}

function hookType() external view override returns (uint8) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: AGPL-3.0

pragma solidity >=0.8.0;

/**
* @dev Define interface for PolygonZkEVM Bridge message receiver
*/
interface IBridgeMessageReceiver {
function onMessageReceived(
address originAddress,
uint32 originNetwork,
bytes memory data
) external payable;
}
118 changes: 118 additions & 0 deletions solidity/contracts/interfaces/polygonZkevm/IPolygonZkEVMBridge.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// SPDX-License-Identifier: AGPL-3.0

pragma solidity ^0.8.0;

interface IPolygonZkEVMBridge {
/**
* @dev Thrown when sender is not the PolygonZkEVM address
*/
error OnlyPolygonZkEVM();

/**
* @dev Thrown when the destination network is invalid
*/
error DestinationNetworkInvalid();

/**
* @dev Thrown when the amount does not match msg.value
*/
error AmountDoesNotMatchMsgValue();

/**
* @dev Thrown when user is bridging tokens and is also sending a value
*/
error MsgValueNotZero();

/**
* @dev Thrown when the Ether transfer on claimAsset fails
*/
error EtherTransferFailed();

/**
* @dev Thrown when the message transaction on claimMessage fails
*/
error MessageFailed();

/**
* @dev Thrown when the global exit root does not exist
*/
error GlobalExitRootInvalid();

/**
* @dev Thrown when the smt proof does not match
*/
error InvalidSmtProof();

/**
* @dev Thrown when an index is already claimed
*/
error AlreadyClaimed();

/**
* @dev Thrown when the owner of permit does not match the sender
*/
error NotValidOwner();

/**
* @dev Thrown when the spender of the permit does not match this contract address
*/
error NotValidSpender();

/**
* @dev Thrown when the amount of the permit does not match
*/
error NotValidAmount();

/**
* @dev Thrown when the permit data contains an invalid signature
*/
error NotValidSignature();

function bridgeAsset(
uint32 destinationNetwork,
address destinationAddress,
uint256 amount,
address token,
bool forceUpdateGlobalExitRoot,
bytes calldata permitData
) external payable;

function bridgeMessage(
uint32 destinationNetwork,
address destinationAddress,
bool forceUpdateGlobalExitRoot,
bytes calldata metadata
) external payable;

function claimAsset(
bytes32[32] calldata smtProof,
uint32 index,
bytes32 mainnetExitRoot,
bytes32 rollupExitRoot,
uint32 originNetwork,
address originTokenAddress,
uint32 destinationNetwork,
address destinationAddress,
uint256 amount,
bytes calldata metadata
) external;

function claimMessage(
bytes32[32] calldata smtProof,
uint32 index,
bytes32 mainnetExitRoot,
bytes32 rollupExitRoot,
uint32 originNetwork,
address originAddress,
uint32 destinationNetwork,
address destinationAddress,
uint256 amount,
bytes calldata metadata
) external;

function updateGlobalExitRoot() external;

function activateEmergencyState() external;

function deactivateEmergencyState() external;
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ abstract contract AbstractMessageIdAuthorizedIsm is
bytes calldata,
/*_metadata*/
bytes calldata message
) external returns (bool) {
) external virtual returns (bool) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed to modify this function as relayer caller verify after the CCIP call

bytes32 messageId = message.id();

// check for the first bit (used for verification)
Expand All @@ -99,7 +99,7 @@ abstract contract AbstractMessageIdAuthorizedIsm is
* @dev Only callable by the authorized hook.
* @param messageId Hyperlane Id of the message.
*/
function verifyMessageId(bytes32 messageId) external payable virtual {
function verifyMessageId(bytes32 messageId) public payable virtual {
require(
_isAuthorized(),
"AbstractMessageIdAuthorizedIsm: sender is not the hook"
Expand Down
Loading