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.
477 lines
13 KiB
477 lines
13 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; // 4 bytes domain + 32 bytes id
|
|
uint256 private constant IDENTIFIER_LEN = 1;
|
|
uint256 private constant TRANSFER_LEN = 65; // 1 byte identifier + 32 bytes recipient + 32 bytes amount
|
|
uint256 private constant DETAILS_LEN = 66; // 1 byte identifier + 32 bytes name + 32 bytes symbol + 1 byte decimals
|
|
uint256 private constant REQUEST_DETAILS_LEN = 1; // 1 byte identifier
|
|
|
|
// WARNING: do NOT re-write the numbers / order
|
|
// of message types in an upgrade;
|
|
// will cause in-flight messages to be mis-interpreted
|
|
enum Types {
|
|
Invalid, // 0
|
|
TokenId, // 1
|
|
Message, // 2
|
|
Transfer, // 3
|
|
Details, // 4
|
|
RequestDetails // 5
|
|
}
|
|
|
|
/**
|
|
* @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 Checks that Action is valid type
|
|
* @param _action The action
|
|
* @return TRUE if action is valid
|
|
*/
|
|
function isValidAction(bytes29 _action) internal pure returns (bool) {
|
|
return
|
|
isDetails(_action) ||
|
|
isRequestDetails(_action) ||
|
|
isTransfer(_action);
|
|
}
|
|
|
|
/**
|
|
* @notice Checks that view is a valid message length
|
|
* @param _view The bytes string
|
|
* @return TRUE if message is valid
|
|
*/
|
|
function isValidMessageLength(bytes29 _view) internal pure returns (bool) {
|
|
uint256 _len = _view.len();
|
|
return
|
|
_len == TOKEN_ID_LEN + TRANSFER_LEN ||
|
|
_len == TOKEN_ID_LEN + DETAILS_LEN ||
|
|
_len == TOKEN_ID_LEN + REQUEST_DETAILS_LEN;
|
|
}
|
|
|
|
/**
|
|
* @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(isValidAction(_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 _action The message
|
|
* @return True if the message is of type Transfer
|
|
*/
|
|
function isTransfer(bytes29 _action) internal pure returns (bool) {
|
|
return
|
|
actionType(_action) == uint8(Types.Transfer) &&
|
|
messageType(_action) == Types.Transfer;
|
|
}
|
|
|
|
/**
|
|
* @notice Checks that the message is of type Details
|
|
* @param _action The message
|
|
* @return True if the message is of type Details
|
|
*/
|
|
function isDetails(bytes29 _action) internal pure returns (bool) {
|
|
return
|
|
actionType(_action) == uint8(Types.Details) &&
|
|
messageType(_action) == Types.Details;
|
|
}
|
|
|
|
/**
|
|
* @notice Checks that the message is of type Details
|
|
* @param _action The message
|
|
* @return True if the message is of type Details
|
|
*/
|
|
function isRequestDetails(bytes29 _action) internal pure returns (bool) {
|
|
return
|
|
actionType(_action) == uint8(Types.RequestDetails) &&
|
|
messageType(_action) == Types.RequestDetails;
|
|
}
|
|
|
|
/**
|
|
* @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(Types.Transfer, _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(Types.Details, _name, _symbol, _decimals).ref(
|
|
0
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @notice Formats Request Details
|
|
* @return The Request Details message
|
|
*/
|
|
function formatRequestDetails() internal pure returns (bytes29) {
|
|
return
|
|
mustBeRequestDetails(abi.encodePacked(Types.RequestDetails).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 _tokenId The message
|
|
* @return The domain
|
|
*/
|
|
function domain(bytes29 _tokenId)
|
|
internal
|
|
pure
|
|
typeAssert(_tokenId, Types.TokenId)
|
|
returns (uint32)
|
|
{
|
|
return uint32(_tokenId.indexUint(0, 4));
|
|
}
|
|
|
|
/**
|
|
* @notice Retrieves the ID from a TokenID
|
|
* @param _tokenId The message
|
|
* @return The ID
|
|
*/
|
|
function id(bytes29 _tokenId)
|
|
internal
|
|
pure
|
|
typeAssert(_tokenId, Types.TokenId)
|
|
returns (bytes32)
|
|
{
|
|
// before = 4 bytes domain
|
|
return _tokenId.index(4, 32);
|
|
}
|
|
|
|
/**
|
|
* @notice Retrieves the EVM ID
|
|
* @param _tokenId The message
|
|
* @return The EVM ID
|
|
*/
|
|
function evmId(bytes29 _tokenId)
|
|
internal
|
|
pure
|
|
typeAssert(_tokenId, Types.TokenId)
|
|
returns (address)
|
|
{
|
|
// before = 4 bytes domain + 12 bytes empty to trim for address
|
|
return _tokenId.indexAddress(16);
|
|
}
|
|
|
|
/**
|
|
* @notice Retrieves the action identifier from message
|
|
* @param _message The action
|
|
* @return The message type
|
|
*/
|
|
function msgType(bytes29 _message) internal pure returns (uint8) {
|
|
return uint8(_message.indexUint(TOKEN_ID_LEN, 1));
|
|
}
|
|
|
|
/**
|
|
* @notice Retrieves the identifier from action
|
|
* @param _action The action
|
|
* @return The action type
|
|
*/
|
|
function actionType(bytes29 _action) internal pure returns (uint8) {
|
|
return uint8(_action.indexUint(0, 1));
|
|
}
|
|
|
|
/**
|
|
* @notice Retrieves the recipient from a Transfer
|
|
* @param _transferAction The message
|
|
* @return The recipient address as bytes32
|
|
*/
|
|
function recipient(bytes29 _transferAction)
|
|
internal
|
|
pure
|
|
typeAssert(_transferAction, Types.Transfer)
|
|
returns (bytes32)
|
|
{
|
|
// before = 1 byte identifier
|
|
return _transferAction.index(1, 32);
|
|
}
|
|
|
|
/**
|
|
* @notice Retrieves the EVM Recipient from a Transfer
|
|
* @param _transferAction The message
|
|
* @return The EVM Recipient
|
|
*/
|
|
function evmRecipient(bytes29 _transferAction)
|
|
internal
|
|
pure
|
|
typeAssert(_transferAction, Types.Transfer)
|
|
returns (address)
|
|
{
|
|
// before = 1 byte identifier + 12 bytes empty to trim for address
|
|
return _transferAction.indexAddress(13);
|
|
}
|
|
|
|
/**
|
|
* @notice Retrieves the amount from a Transfer
|
|
* @param _transferAction The message
|
|
* @return The amount
|
|
*/
|
|
function amnt(bytes29 _transferAction)
|
|
internal
|
|
pure
|
|
typeAssert(_transferAction, Types.Transfer)
|
|
returns (uint256)
|
|
{
|
|
// before = 1 byte identifier + 32 bytes ID
|
|
return _transferAction.indexUint(33, 32);
|
|
}
|
|
|
|
/**
|
|
* @notice Retrieves the name from Details
|
|
* @param _detailsAction The message
|
|
* @return The name
|
|
*/
|
|
function name(bytes29 _detailsAction)
|
|
internal
|
|
pure
|
|
typeAssert(_detailsAction, Types.Details)
|
|
returns (bytes32)
|
|
{
|
|
// before = 1 byte identifier
|
|
return _detailsAction.index(1, 32);
|
|
}
|
|
|
|
/**
|
|
* @notice Retrieves the symbol from Details
|
|
* @param _detailsAction The message
|
|
* @return The symbol
|
|
*/
|
|
function symbol(bytes29 _detailsAction)
|
|
internal
|
|
pure
|
|
typeAssert(_detailsAction, Types.Details)
|
|
returns (bytes32)
|
|
{
|
|
// before = 1 byte identifier + 32 bytes name
|
|
return _detailsAction.index(33, 32);
|
|
}
|
|
|
|
/**
|
|
* @notice Retrieves the decimals from Details
|
|
* @param _detailsAction The message
|
|
* @return The decimals
|
|
*/
|
|
function decimals(bytes29 _detailsAction)
|
|
internal
|
|
pure
|
|
typeAssert(_detailsAction, Types.Details)
|
|
returns (uint8)
|
|
{
|
|
// before = 1 byte identifier + 32 bytes name + 32 bytes symbol
|
|
return uint8(_detailsAction.indexUint(65, 1));
|
|
}
|
|
|
|
/**
|
|
* @notice Retrieves the token ID from a Message
|
|
* @param _message The message
|
|
* @return The ID
|
|
*/
|
|
function tokenId(bytes29 _message)
|
|
internal
|
|
pure
|
|
typeAssert(_message, Types.Message)
|
|
returns (bytes29)
|
|
{
|
|
return _message.slice(0, TOKEN_ID_LEN, uint40(Types.TokenId));
|
|
}
|
|
|
|
/**
|
|
* @notice Retrieves the action data from a Message
|
|
* @param _message The message
|
|
* @return The action
|
|
*/
|
|
function action(bytes29 _message)
|
|
internal
|
|
pure
|
|
typeAssert(_message, Types.Message)
|
|
returns (bytes29)
|
|
{
|
|
uint256 _actionLen = _message.len() - TOKEN_ID_LEN;
|
|
uint40 _type = uint40(msgType(_message));
|
|
return _message.slice(TOKEN_ID_LEN, _actionLen, _type);
|
|
}
|
|
|
|
/**
|
|
* @notice Converts to a Transfer
|
|
* @param _action The message
|
|
* @return The newly typed message
|
|
*/
|
|
function tryAsTransfer(bytes29 _action) internal pure returns (bytes29) {
|
|
if (_action.len() == TRANSFER_LEN) {
|
|
return _action.castTo(uint40(Types.Transfer));
|
|
}
|
|
return TypedMemView.nullView();
|
|
}
|
|
|
|
/**
|
|
* @notice Converts to a Details
|
|
* @param _action The message
|
|
* @return The newly typed message
|
|
*/
|
|
function tryAsDetails(bytes29 _action) internal pure returns (bytes29) {
|
|
if (_action.len() == DETAILS_LEN) {
|
|
return _action.castTo(uint40(Types.Details));
|
|
}
|
|
return TypedMemView.nullView();
|
|
}
|
|
|
|
/**
|
|
* @notice Converts to a Details
|
|
* @param _action The message
|
|
* @return The newly typed message
|
|
*/
|
|
function tryAsRequestDetails(bytes29 _action)
|
|
internal
|
|
pure
|
|
returns (bytes29)
|
|
{
|
|
if (_action.len() == REQUEST_DETAILS_LEN) {
|
|
return _action.castTo(uint40(Types.RequestDetails));
|
|
}
|
|
return TypedMemView.nullView();
|
|
}
|
|
|
|
/**
|
|
* @notice Converts to a TokenID
|
|
* @param _tokenId The message
|
|
* @return The newly typed message
|
|
*/
|
|
function tryAsTokenId(bytes29 _tokenId) internal pure returns (bytes29) {
|
|
if (_tokenId.len() == TOKEN_ID_LEN) {
|
|
return _tokenId.castTo(uint40(Types.TokenId));
|
|
}
|
|
return TypedMemView.nullView();
|
|
}
|
|
|
|
/**
|
|
* @notice Converts to a Message
|
|
* @param _message The message
|
|
* @return The newly typed message
|
|
*/
|
|
function tryAsMessage(bytes29 _message) internal pure returns (bytes29) {
|
|
if (isValidMessageLength(_message)) {
|
|
return _message.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 Details
|
|
* @param _view The message
|
|
* @return The message
|
|
*/
|
|
function mustBeRequestDetails(bytes29 _view)
|
|
internal
|
|
pure
|
|
returns (bytes29)
|
|
{
|
|
return tryAsRequestDetails(_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();
|
|
}
|
|
}
|
|
|