diff --git a/solidity/contracts/hooks/PolygonZkevmHook.sol b/solidity/contracts/hooks/PolygonZkevmHook.sol new file mode 100644 index 0000000000..7c984fe39d --- /dev/null +++ b/solidity/contracts/hooks/PolygonZkevmHook.sol @@ -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 + 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, + abi.encodePacked(messageId) + ); + } + + function hookType() external view override returns (uint8) {} +} diff --git a/solidity/contracts/interfaces/polygonZkevm/IBridgeMessageReceiver.sol b/solidity/contracts/interfaces/polygonZkevm/IBridgeMessageReceiver.sol new file mode 100644 index 0000000000..22c3f0b044 --- /dev/null +++ b/solidity/contracts/interfaces/polygonZkevm/IBridgeMessageReceiver.sol @@ -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; +} diff --git a/solidity/contracts/interfaces/polygonZkevm/IPolygonZkEVMBridge.sol b/solidity/contracts/interfaces/polygonZkevm/IPolygonZkEVMBridge.sol new file mode 100644 index 0000000000..cfef1c54d8 --- /dev/null +++ b/solidity/contracts/interfaces/polygonZkevm/IPolygonZkEVMBridge.sol @@ -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; +} diff --git a/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol b/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol index 2f9caf8a3f..a81bbdedf2 100644 --- a/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol +++ b/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol @@ -74,7 +74,7 @@ abstract contract AbstractMessageIdAuthorizedIsm is bytes calldata, /*_metadata*/ bytes calldata message - ) external returns (bool) { + ) external virtual returns (bool) { bytes32 messageId = message.id(); // check for the first bit (used for verification) @@ -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" diff --git a/solidity/contracts/isms/hook/PolygonZkevmIsm.sol b/solidity/contracts/isms/hook/PolygonZkevmIsm.sol new file mode 100644 index 0000000000..8ce046f523 --- /dev/null +++ b/solidity/contracts/isms/hook/PolygonZkevmIsm.sol @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity >=0.8.0; + +/*@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@ HYPERLANE @@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ +@@@@@@@@@ @@@@@@@@*/ + +// ============ Internal Imports ============ + +import {LibBit} from "../../libs/LibBit.sol"; +import {Message} from "../../libs/Message.sol"; +import {TypeCasts} from "../../libs/TypeCasts.sol"; + +import {AbstractCcipReadIsm} from "../ccip-read/AbstractCcipReadIsm.sol"; +import {AbstractMessageIdAuthorizedIsm} from "../hook/AbstractMessageIdAuthorizedIsm.sol"; + +import {IInterchainSecurityModule} from "../../interfaces/IInterchainSecurityModule.sol"; +import {IMailbox} from "../../interfaces/IMailbox.sol"; +import {ICcipReadIsm} from "../../interfaces/isms/ICcipReadIsm.sol"; + +// ============ External Imports ============ + +import {IPolygonZkEVMBridge} from "../../interfaces/polygonzkevm/IPolygonZkEVMBridge.sol"; +import {IBridgeMessageReceiver} from "../../interfaces/polygonzkevm/IBridgeMessageReceiver.sol"; +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; + +/** + * @title PolygonZkevmIsm + * @notice Polygon zkEVM chain Ism that uses the Polygon zkEVM bridge to verify messages + */ +contract PolygonZkevmIsm is + ICcipReadIsm, + AbstractMessageIdAuthorizedIsm, + IBridgeMessageReceiver +{ + using Message for bytes; + using LibBit for uint256; + using TypeCasts for bytes32; + using Address for address payable; + + IMailbox public mailbox; + string[] public offchainUrls; + + // ============ Constants ============ + IPolygonZkEVMBridge public immutable zkEvmBridge; + uint8 public constant override moduleType = + uint8(IInterchainSecurityModule.Types.CCIP_READ); + uint32 public immutable zkEvmBridgeDestinationNetId; + + // ============ Constructor ============ + constructor( + address _zkEvmBridge, + uint32 _zkEvmBridgeDestinationNetId, + address _mailbox, + string[] memory _offchainUrls + ) { + require( + Address.isContract(_zkEvmBridge), + "PolygonZkevmIsm: invalid ZkEVMBridge" + ); + require( + Address.isContract(_mailbox), + "PolygonZkevmIsm: invalid Mailbox" + ); + require( + _zkEvmBridgeDestinationNetId <= 1, + "PolygonZkevmIsm: invalid ZkEVMBridge destination network id" + ); + zkEvmBridgeDestinationNetId = _zkEvmBridgeDestinationNetId; + zkEvmBridge = IPolygonZkEVMBridge(_zkEvmBridge); + mailbox = IMailbox(_mailbox); + offchainUrls = _offchainUrls; + } + + /** + * @dev off-chain verification information for a given message. + * @param _message The message for which off-chain verification information is requested. + */ + function getOffchainVerifyInfo( + bytes calldata _message + ) external view override { + bytes memory messageId = abi.encodePacked(_message.id()); + revert OffchainLookup( + address(this), + offchainUrls, + messageId, + PolygonZkevmIsm.verify.selector, + _message + ); + } + + /** + * @dev Calls the Polygon zkEVM bridge to claim the message. + * @param _metadata from CCIP call + * @return A boolean indicating whether the message was successfully verified and processed. + */ + function verify( + bytes calldata _metadata, + bytes calldata _message + ) + external + override(AbstractMessageIdAuthorizedIsm, IInterchainSecurityModule) + returns (bool) + { + bytes32 messageId = _message.id(); + ( + bytes32[32] memory smtProof, + uint32 index, + bytes32 mainnetExitRoot, + bytes32 rollupExitRoot, + uint32 originNetwork, + address originAddress, + , + , + uint256 amount, + bytes memory payload + ) = abi.decode( + _metadata, + ( + bytes32[32], + uint32, + bytes32, + bytes32, + uint32, + address, + uint32, + address, + uint256, + bytes + ) + ); + require( + messageId == abi.decode(payload, (bytes32)), + "PolygonZkevmIsm: message id does not match payload" + ); + zkEvmBridge.claimMessage( + smtProof, + index, + mainnetExitRoot, + rollupExitRoot, + originNetwork, + originAddress, + zkEvmBridgeDestinationNetId, + address(this), + amount, + payload + ); + uint256 _msgValue = verifiedMessages[messageId].clearBit( + VERIFIED_MASK_INDEX + ); + if (_msgValue > 0) { + verifiedMessages[messageId] -= _msgValue; + payable(_message.recipientAddress()).sendValue(_msgValue); + } + + return true; + } + + /** + * @dev Callback function for Zkevm bridge. + * Verifies the received message. + * @inheritdoc IBridgeMessageReceiver + */ + function onMessageReceived( + address, + uint32, + bytes memory data + ) external payable override { + require( + msg.sender == address(zkEvmBridge), + "PolygonZkevmIsm: invalid sender" + ); + require(data.length == 32, "PolygonZkevmIsm: data must be 32 bytes"); + require( + _isAuthorized(), + "AbstractMessageIdAuthorizedIsm: sender is not the hook" + ); + require( + msg.value < 2 ** VERIFIED_MASK_INDEX, + "AbstractMessageIdAuthorizedIsm: msg.value must be less than 2^255" + ); + + bytes32 messageId = abi.decode(data, (bytes32)); + verifiedMessages[messageId] = msg.value.setBit(VERIFIED_MASK_INDEX); + + emit ReceivedMessage(messageId); + } + + /** + * @dev Checks if the origin chain message sender is the hook address. + * @inheritdoc AbstractMessageIdAuthorizedIsm + */ + function _isAuthorized() internal view override returns (bool) { + bytes32 originSender = abi.decode(msg.data[4:], (bytes32)); + return originSender == authorizedHook; + } +} diff --git a/solidity/test/hooks/PolygonZkevmHook.t.sol b/solidity/test/hooks/PolygonZkevmHook.t.sol new file mode 100644 index 0000000000..fc8b366d37 --- /dev/null +++ b/solidity/test/hooks/PolygonZkevmHook.t.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.13; + +import {Test} from "forge-std/Test.sol"; +import {TypeCasts} from "../../contracts/libs/TypeCasts.sol"; +import {StandardHookMetadata} from "../../contracts/hooks/libs/StandardHookMetadata.sol"; +import {TestMailbox} from "../../contracts/test/TestMailbox.sol"; +import {TestPostDispatchHook} from "../../contracts/test/TestPostDispatchHook.sol"; +import {TestIsm} from "../../contracts/test/TestIsm.sol"; +import {IPostDispatchHook} from "../../contracts/interfaces/hooks/IPostDispatchHook.sol"; +import {Message} from "../../contracts/libs/Message.sol"; +import {TestRecipient} from "../../contracts/test/TestRecipient.sol"; + +import {PolygonZkevmHook} from "../../contracts/hooks/PolygonZkevmHook.sol"; + +import "forge-std/console.sol"; + +contract PolygonZkEVMBridge { + function bridgeMessage( + uint32, + address, + bool, + bytes calldata + ) external payable {} +} + +contract PolygonZkevmHooktest is Test { + using TypeCasts for bytes32; + using StandardHookMetadata for bytes; + using Message for bytes; + + // Contracts + TestPostDispatchHook public requiredHook; + TestMailbox public mailbox; + TestIsm public ism; + PolygonZkevmHook public hook; + + TestRecipient internal testRecipient; + + PolygonZkEVMBridge internal polygonZkevmBridge; + + bytes internal testMessage = + abi.encodePacked("Hello from the other chain!"); + bytes internal testMetadata = + StandardHookMetadata.overrideRefundAddress(address(this)); + + function setUp() public { + // Setup Hyperlane + requiredHook = new TestPostDispatchHook(); + mailbox = new TestMailbox(0); + ism = new TestIsm(); + polygonZkevmBridge = new PolygonZkEVMBridge(); + hook = new PolygonZkevmHook( + address(mailbox), + 1, + address(ism), + address(polygonZkevmBridge), + 1 + ); + } + + function test_postDispatch() public { + vm.expectCall( + address(polygonZkevmBridge), + abi.encodeCall( + polygonZkevmBridge.bridgeMessage, + (uint32(1), address(ism), true, abi.encode(testMessage.id())) + ) + ); + + hook.postDispatch(testMetadata, testMessage); + } + + function test_postDispatch_msgValue() public { + vm.expectRevert( + "PolygonzkEVMHook: msgValue must be less than 2 ** 255" + ); + testMetadata = StandardHookMetadata.overrideMsgValue(2 ** 255); + hook.postDispatch(testMetadata, testMessage); + } + + function test_postDispatch_supportsMetadata() public { + assertTrue(hook.supportsMetadata(testMetadata)); + } +} diff --git a/solidity/test/isms/PolygonZkevmIsm.t.sol b/solidity/test/isms/PolygonZkevmIsm.t.sol new file mode 100644 index 0000000000..9de6f5b354 --- /dev/null +++ b/solidity/test/isms/PolygonZkevmIsm.t.sol @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.13; + +import {Test} from "forge-std/Test.sol"; +import {TypeCasts} from "../../contracts/libs/TypeCasts.sol"; +import {StandardHookMetadata} from "../../contracts/hooks/libs/StandardHookMetadata.sol"; +import {TestMailbox} from "../../contracts/test/TestMailbox.sol"; +import {TestPostDispatchHook} from "../../contracts/test/TestPostDispatchHook.sol"; +import {TestIsm} from "../../contracts/test/TestIsm.sol"; +import {IPostDispatchHook} from "../../contracts/interfaces/hooks/IPostDispatchHook.sol"; +import {Message} from "../../contracts/libs/Message.sol"; +import {TestRecipient} from "../../contracts/test/TestRecipient.sol"; +import {ICcipReadIsm} from "../../contracts/interfaces/isms/ICcipReadIsm.sol"; + +import {IInterchainSecurityModule} from "../../contracts/interfaces/IInterchainSecurityModule.sol"; +import {AbstractMessageIdAuthorizedIsm} from "../../contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol"; +import {PolygonZkevmHook} from "../../contracts/hooks/PolygonZkevmHook.sol"; +import {PolygonZkevmIsm} from "../../contracts/isms/hook/PolygonZkevmIsm.sol"; + +import "forge-std/console.sol"; + +contract PolygonZkEVMBridge { + PolygonZkevmIsm public ism; + bytes public returnData; + + function setIsm(PolygonZkevmIsm _ism) public { + ism = _ism; + } + + function setReturnData(bytes memory _returnData) public { + returnData = _returnData; + } + + function bridgeMessage( + uint32, + address, + bool, + bytes calldata + ) external payable {} + + function claimMessage( + bytes32[32] calldata, + uint32, + bytes32, + bytes32, + uint32, + address, + uint32, + address, + uint256, + bytes calldata + ) external payable { + ism.onMessageReceived(address(0x1), uint32(0), returnData); + } +} + +contract PolygonZkevmIsmtest is Test { + using TypeCasts for bytes32; + using StandardHookMetadata for bytes; + using Message for bytes; + + // Contracts + TestPostDispatchHook public requiredHook; + TestMailbox public mailbox; + PolygonZkevmIsm public ism; + + TestRecipient internal testRecipient; + + // address internal polygonZkevmBridge; + PolygonZkEVMBridge internal polygonZkevmBridge; + + address internal hook; + + bytes internal testMessage = + abi.encodePacked("Hello from the other chain!"); + bytes internal testMetadata = + StandardHookMetadata.overrideRefundAddress(address(this)); + + function setUp() public { + // Setup Hyperlane + requiredHook = new TestPostDispatchHook(); + mailbox = new TestMailbox(0); + polygonZkevmBridge = new PolygonZkEVMBridge(); + ism = new PolygonZkevmIsm( + address(polygonZkevmBridge), + uint32(0), + address(mailbox), + new string[](0) + ); + + hook = address(0x1); + + ism.setAuthorizedHook(TypeCasts.addressToBytes32(address(hook))); + testRecipient = new TestRecipient(); + + bytes memory messageId = abi.encodePacked(testMessage.id()); + polygonZkevmBridge.setIsm(ism); + polygonZkevmBridge.setReturnData(abi.encodePacked(messageId)); + } + + function test_moduleType() public { + assertEq( + ism.moduleType(), + uint8(IInterchainSecurityModule.Types.CCIP_READ) + ); + } + + function test_getOffchainVerifyInfo() external { + bytes memory messageId = abi.encodePacked(testMessage.id()); + + vm.expectRevert( + abi.encodeWithSelector( + ICcipReadIsm.OffchainLookup.selector, + address(ism), + new string[](0), + messageId, + PolygonZkevmIsm.verify.selector, + testMessage + ) + ); + + ism.getOffchainVerifyInfo(testMessage); + } + + // ================== NEED HELP ================== + // function test_verifyPolygonIsm() public { + // bytes32[32] memory smtProof = [ + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0), + // bytes32(0x0) + // ]; + // uint32 index = 0; + // bytes32 mainnetExitRoot = bytes32(0x0); + // bytes32 rollupExitRoot = bytes32(0x0); + // uint32 originNetwork = uint32(0); + // address originAddress = address(0x0); + // uint32 destinationNetwork = 1; + // address destinationAddress = address(0x0); + // uint256 amount = 0; + // bytes memory payload = abi.encodePacked(testMessage.id()); + + // bytes memory metadata = abi.encodePacked( + // smtProof, + // index, + // mainnetExitRoot, + // rollupExitRoot, + // originNetwork, + // originAddress, + // destinationNetwork, + // destinationAddress, + // amount, + // payload + // ); + // console.logBytes(metadata); + // ism.verify(metadata, testMessage); + + // } + + function test_onMessageReceived() public { + bytes32 messageId = testMessage.id(); + vm.prank(address(polygonZkevmBridge)); + ism.onMessageReceived(address(0x1), uint32(0), abi.encode(messageId)); + } + + function test_onMessageReceived_revertNotAuthBridge() public { + bytes32 messageId = testMessage.id(); + + vm.expectRevert("PolygonZkevmIsm: invalid sender"); + + ism.onMessageReceived(address(0x1), uint32(0), abi.encode(messageId)); + } + + function test_onMessageReceived_revertNot32Bytes() public { + vm.expectRevert("PolygonZkevmIsm: data must be 32 bytes"); + vm.prank(address(polygonZkevmBridge)); + + ism.onMessageReceived(address(0x1), uint32(0), abi.encode(testMessage)); + } + + function test_onMessageReceived_revertNoOriginHook() public { + bytes32 messageId = testMessage.id(); + vm.expectRevert( + "AbstractMessageIdAuthorizedIsm: sender is not the hook" + ); + vm.prank(address(polygonZkevmBridge)); + + ism.onMessageReceived(address(0x2), uint32(0), abi.encode(messageId)); + } + + function test_onMessageReceived_revertMsgTooBig() public { + bytes32 messageId = testMessage.id(); + hoax(address(polygonZkevmBridge), 2 ** 255); + + vm.expectRevert( + "AbstractMessageIdAuthorizedIsm: msg.value must be less than 2^255" + ); + + ism.onMessageReceived{value: 2 ** 255}( + address(0x1), + uint32(0), + abi.encode(messageId) + ); + } +}