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/optics-xapps/contracts/bridge/BridgeMessage.sol

370 lines
9.4 KiB

// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
// ============ External Imports ============
import {TypedMemView} from "@summa-tx/memview-sol/contracts/TypedMemView.sol";
library BridgeMessage {
using TypedMemView for bytes;
using TypedMemView for bytes29;
uint256 private constant TOKEN_ID_LEN = 36;
uint256 private constant TRANSFER_LEN = 64;
uint256 private constant DETAILS_LEN = 65;
enum Types {
Invalid, // 0
Transfer, // 1
Details, // 2
TokenId, // 3
Message // 4
}
/**
* @notice Asserts a message is of type `_t`
* @param _view The message
* @param _t The expected type
*/
modifier typeAssert(bytes29 _view, Types _t) {
_view.assertType(uint40(_t));
_;
}
/**
* @notice Formats an action message
* @param _tokenId The token ID
* @param _action The action
* @return The formatted message
*/
function formatMessage(bytes29 _tokenId, bytes29 _action)
internal
view
typeAssert(_tokenId, Types.TokenId)
returns (bytes memory)
{
require(isDetails(_action) || isTransfer(_action), "!action");
bytes29[] memory _views = new bytes29[](2);
_views[0] = _tokenId;
_views[1] = _action;
return TypedMemView.join(_views);
}
/**
* @notice Returns the type of the message
* @param _view The message
* @return The type of the message
*/
function messageType(bytes29 _view) internal pure returns (Types) {
return Types(uint8(_view.typeOf()));
}
/**
* @notice Checks that the message is of type Transfer
* @param _view The message
* @return True if the message is of type Transfer
*/
function isTransfer(bytes29 _view) internal pure returns (bool) {
return messageType(_view) == Types.Transfer;
}
/**
* @notice Checks that the message is of type Details
* @param _view The message
* @return True if the message is of type Details
*/
function isDetails(bytes29 _view) internal pure returns (bool) {
return messageType(_view) == Types.Details;
}
/**
* @notice Formats Transfer
* @param _to The recipient address as bytes32
* @param _amnt The transfer amount
* @return
*/
function formatTransfer(bytes32 _to, uint256 _amnt)
internal
pure
returns (bytes29)
{
return mustBeTransfer(abi.encodePacked(_to, _amnt).ref(0));
}
/**
* @notice Formats Details
* @param _name The name
* @param _symbol The symbol
* @param _decimals The decimals
* @return The Details message
*/
function formatDetails(
bytes32 _name,
bytes32 _symbol,
uint8 _decimals
) internal pure returns (bytes29) {
return
mustBeDetails(abi.encodePacked(_name, _symbol, _decimals).ref(0));
}
/**
* @notice Formats the Token ID
* @param _domain The domain
* @param _id The ID
* @return The formatted Token ID
*/
function formatTokenId(uint32 _domain, bytes32 _id)
internal
pure
returns (bytes29)
{
return mustBeTokenId(abi.encodePacked(_domain, _id).ref(0));
}
/**
* @notice Retrieves the domain from a TokenID
* @param _view The message
* @return The domain
*/
function domain(bytes29 _view)
internal
pure
typeAssert(_view, Types.TokenId)
returns (uint32)
{
return uint32(_view.indexUint(0, 4));
}
/**
* @notice Retrieves the ID from a TokenID
* @param _view The message
* @return The ID
*/
function id(bytes29 _view)
internal
pure
typeAssert(_view, Types.TokenId)
returns (bytes32)
{
return _view.index(4, 32);
}
/**
* @notice Retrieves the EVM ID
* @param _view The message
* @return The EVM ID
*/
function evmId(bytes29 _view)
internal
pure
typeAssert(_view, Types.TokenId)
returns (address)
{
// 4 bytes domain + 12 empty to trim for address
return _view.indexAddress(16);
}
/**
* @notice Retrieves the recipient from a Transfer
* @param _view The message
* @return The recipient address as bytes32
*/
function recipient(bytes29 _view)
internal
pure
typeAssert(_view, Types.Transfer)
returns (bytes32)
{
return _view.index(0, 32);
}
/**
* @notice Retrieves the EVM Recipient from a Transfer
* @param _view The message
* @return The EVM Recipient
*/
function evmRecipient(bytes29 _view)
internal
pure
typeAssert(_view, Types.Transfer)
returns (address)
{
return _view.indexAddress(12);
}
/**
* @notice Retrieves the amount from a Transfer
* @param _view The message
* @return The amount
*/
function amnt(bytes29 _view)
internal
pure
typeAssert(_view, Types.Transfer)
returns (uint256)
{
return _view.indexUint(32, 32);
}
/**
* @notice Retrieves the name from Details
* @param _view The message
* @return The name
*/
function name(bytes29 _view)
internal
pure
typeAssert(_view, Types.Details)
returns (bytes32)
{
return _view.index(0, 32);
}
/**
* @notice Retrieves the symbol from Details
* @param _view The message
* @return The symbol
*/
function symbol(bytes29 _view)
internal
pure
typeAssert(_view, Types.Details)
returns (bytes32)
{
return _view.index(32, 32);
}
/**
* @notice Retrieves the decimals from Details
* @param _view The message
* @return The decimals
*/
function decimals(bytes29 _view)
internal
pure
typeAssert(_view, Types.Details)
returns (uint8)
{
return uint8(_view.indexUint(64, 1));
}
/**
* @notice Retrieves the token ID from a Message
* @param _view The message
* @return The ID
*/
function tokenId(bytes29 _view)
internal
pure
typeAssert(_view, Types.Message)
returns (bytes29)
{
return _view.slice(0, TOKEN_ID_LEN, uint40(Types.TokenId));
}
/**
* @notice Retrieves the action data from a Message
* @param _view The message
* @return The action
*/
function action(bytes29 _view)
internal
pure
typeAssert(_view, Types.Message)
returns (bytes29)
{
if (_view.len() == TOKEN_ID_LEN + DETAILS_LEN) {
return
_view.slice(TOKEN_ID_LEN, DETAILS_LEN, uint40(Types.Details));
}
return _view.slice(TOKEN_ID_LEN, TRANSFER_LEN, uint40(Types.Transfer));
}
/**
* @notice Converts to a Transfer
* @param _view The message
* @return The newly typed message
*/
function tryAsTransfer(bytes29 _view) internal pure returns (bytes29) {
if (_view.len() == TRANSFER_LEN) {
return _view.castTo(uint40(Types.Transfer));
}
return TypedMemView.nullView();
}
/**
* @notice Converts to a Details
* @param _view The message
* @return The newly typed message
*/
function tryAsDetails(bytes29 _view) internal pure returns (bytes29) {
if (_view.len() == DETAILS_LEN) {
return _view.castTo(uint40(Types.Details));
}
return TypedMemView.nullView();
}
/**
* @notice Converts to a TokenID
* @param _view The message
* @return The newly typed message
*/
function tryAsTokenId(bytes29 _view) internal pure returns (bytes29) {
if (_view.len() == 36) {
return _view.castTo(uint40(Types.TokenId));
}
return TypedMemView.nullView();
}
/**
* @notice Converts to a Message
* @param _view The message
* @return The newly typed message
*/
function tryAsMessage(bytes29 _view) internal pure returns (bytes29) {
uint256 _len = _view.len();
if (
_len == TOKEN_ID_LEN + TRANSFER_LEN ||
_len == TOKEN_ID_LEN + DETAILS_LEN
) {
return _view.castTo(uint40(Types.Message));
}
return TypedMemView.nullView();
}
/**
* @notice Asserts that the message is of type Transfer
* @param _view The message
* @return The message
*/
function mustBeTransfer(bytes29 _view) internal pure returns (bytes29) {
return tryAsTransfer(_view).assertValid();
}
/**
* @notice Asserts that the message is of type Details
* @param _view The message
* @return The message
*/
function mustBeDetails(bytes29 _view) internal pure returns (bytes29) {
return tryAsDetails(_view).assertValid();
}
/**
* @notice Asserts that the message is of type TokenID
* @param _view The message
* @return The message
*/
function mustBeTokenId(bytes29 _view) internal pure returns (bytes29) {
return tryAsTokenId(_view).assertValid();
}
/**
* @notice Asserts that the message is of type Message
* @param _view The message
* @return The message
*/
function mustBeMessage(bytes29 _view) internal pure returns (bytes29) {
return tryAsMessage(_view).assertValid();
}
}