Skip to content
Open
Show file tree
Hide file tree
Changes from 7 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
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@
"typescript": "4.8.4",
"wait-on": "6.0.1"
},
"dependencies": {
"@hyperlane-xyz/core": "^1.4.2"
},
"files": [
"src/**",
"types/**",
Expand Down
194 changes: 194 additions & 0 deletions src/hyperlane-adapter/HyperlaneReceiverAdapterV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.16;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IMailbox } from "./interfaces/IMailbox.sol";
import { IMessageRecipient } from "./interfaces/IMessageRecipient.sol";
import { IInterchainSecurityModule, ISpecifiesInterchainSecurityModule } from "./interfaces/IInterchainSecurityModule.sol";
import { TypeCasts } from "./libraries/TypeCasts.sol";
import { EncodeDecodeUtil } from "./libraries/EncodeDecodeUtil.sol";
import { Errors } from "./libraries/Errors.sol";
import { IMessageDispatcher } from "../interfaces/IMessageDispatcher.sol";
import { IMessageExecutor } from "../interfaces/IMessageExecutor.sol";
import "../libraries/MessageLib.sol";

/**
* @title HyperlaneReceiverAdapter implementation.
* @notice `IBridgeReceiverAdapter` implementation that uses Hyperlane as the bridge.
*/
contract HyperlaneReceiverAdapterV2 is
Copy link
Author

@degencodebeast degencodebeast Nov 7, 2023

Choose a reason for hiding this comment

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

updated to V2

Copy link

Choose a reason for hiding this comment

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

Should this be v3?

Copy link
Author

@degencodebeast degencodebeast Jan 23, 2024

Choose a reason for hiding this comment

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

I have updated the adapter to use hyperlane V3 and added some unit tests as well

IMessageRecipient,
ISpecifiesInterchainSecurityModule,
Ownable
{
/// @notice `Mailbox` contract reference.
IMailbox public immutable mailbox;

/// @notice `ISM` contract reference.
IInterchainSecurityModule public ism;

/**
* @notice Sender adapter address for each source chain.
* @dev srcChainId => senderAdapter address.
*/
mapping(uint256 => IMessageDispatcher) public senderAdapters;

/**
* @notice Ensure that messages cannot be replayed once they have been executed.
* @dev msgId => isExecuted.
*/
mapping(bytes32 => bool) public executedMessages;

/**
* @notice Emitted when the ISM is set.
* @param module The new ISM for this adapter/recipient.
*/
event IsmSet(address indexed module);

/**
* @notice Emitted when a message has successfully been executed.
* @param fromChainId ID of the chain that dispatched the message
* @param messageId ID uniquely identifying the message that was executed
*/
event MessageIdExecuted(uint256 indexed fromChainId, bytes32 indexed messageId);

/**
* @notice Emitted when a messageId has already been executed.
* @param messageId ID uniquely identifying the message or message batch that were re-executed
*/
error MessageIdAlreadyExecuted(bytes32 messageId);

/**
* @notice Emitted when a sender adapter for a source chain is updated.
* @param srcChainId Source chain identifier.
* @param senderAdapter Address of the sender adapter.
*/
event SenderAdapterUpdated(uint256 srcChainId, IMessageDispatcher senderAdapter);

/* Constructor */
/**
* @notice HyperlaneReceiverAdapter constructor.
* @param _mailbox Address of the Hyperlane `Mailbox` contract.
*/
constructor(address _mailbox) {
if (_mailbox == address(0)) {
revert Errors.InvalidMailboxZeroAddress();
}
mailbox = IMailbox(_mailbox);
}

/// @notice Restrict access to trusted `Mailbox` contract.
modifier onlyMailbox() {
if (msg.sender != address(mailbox)) {
revert Errors.UnauthorizedMailbox(msg.sender);
}
_;
}

/// @inheritdoc ISpecifiesInterchainSecurityModule
function interchainSecurityModule() external view returns (IInterchainSecurityModule) {
return ism;
}

/**
* @notice Sets the ISM for this adapter/recipient.
* @param _ism The ISM contract address.
*/
function setIsm(address _ism) external onlyOwner {
ism = IInterchainSecurityModule(_ism);
emit IsmSet(_ism);
}

function executeMessage(
address _to,
bytes memory _message,
bytes32 _messageId,
uint256 _fromChainId,
address _from,
bool _executedMessageId
) internal {
MessageLib.executeMessage(_to, _message, _messageId, _fromChainId, _from, _executedMessageId);

emit MessageIdExecuted(_fromChainId, _messageId);
}

function executeMessageBatch(
MessageLib.Message[] memory _messages,
bytes32 _messageId,
uint256 _fromChainId,
address _from,
bool _executedMessageId
) internal {
MessageLib.executeMessageBatch(_messages, _messageId, _fromChainId, _from, _executedMessageId);

emit MessageIdExecuted(_fromChainId, _messageId);
}

/**
* @notice Called by Hyperlane `Mailbox` contract on destination chain to receive cross-chain messages.
* @dev _origin Source chain domain identifier (not currently used).
* @param _sender Address of the sender on the source chain.
* @param _body Body of the message.
*/
function handle(
uint32 _origin,
bytes32 _sender,
bytes memory _body
) external onlyMailbox {
address adapter = TypeCasts.bytes32ToAddress(_sender);
bool _executedMessageId;
(
MessageLib.Message[] memory _messages,
bytes32 msgId,
uint256 srcChainId,
address srcSender
) = EncodeDecodeUtil.decode(_body);

if (_origin != srcChainId) {
Copy link
Author

Choose a reason for hiding this comment

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

comparing the origin and sender chain

revert Errors.UnauthorizedOrigin(_origin);
}

if (IMessageDispatcher(adapter) != senderAdapters[srcChainId]) {
revert Errors.UnauthorizedAdapter(srcChainId, adapter);
}

if (_messages.length < 1) {
revert Errors.NoMessagesSent(srcChainId);
}

if (executedMessages[msgId]) {
revert MessageIdAlreadyExecuted(msgId);
} else {
Copy link
Author

Choose a reason for hiding this comment

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

did all my revert conditions up front and did state changes after

_executedMessageId = executedMessages[msgId];
executedMessages[msgId] = true;
}

if (_messages.length == 1) {
MessageLib.Message memory _message = _messages[0];
executeMessage(_message.to, _message.data, msgId, srcChainId, srcSender, _executedMessageId);
} else {
executeMessageBatch(_messages, msgId, srcChainId, srcSender, _executedMessageId);
}
}

function updateSenderAdapter(
uint256[] calldata _srcChainIds,
IMessageDispatcher[] calldata _senderAdapters
) external onlyOwner {
if (_srcChainIds.length != _senderAdapters.length) {
revert Errors.MismatchChainsAdaptersLength(_srcChainIds.length, _senderAdapters.length);
}
for (uint256 i; i < _srcChainIds.length; ++i) {
senderAdapters[_srcChainIds[i]] = _senderAdapters[i];
emit SenderAdapterUpdated(_srcChainIds[i], _senderAdapters[i]);
}
}

function getSenderAdapter(uint256 _srcChainId)
public
view
returns (IMessageDispatcher _senderAdapter)
{
_senderAdapter = senderAdapters[_srcChainId];
}
}
Loading