> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/circlefin/evm-cctp-contracts/llms.txt
> Use this file to discover all available pages before exploring further.

# Message Format

> Message structure and encoding for cross-chain communication

The Message library defines the format for cross-chain messages in CCTP. Messages use a fixed-size header with a dynamic message body, leveraging TypedMemView for efficient memory operations.

## Message Structure

Messages contain metadata about the cross-chain transfer and routing information:

<ParamField path="version" type="uint32" required>
  Message format version (4 bytes)
</ParamField>

<ParamField path="sourceDomain" type="uint32" required>
  Domain identifier of the source chain (4 bytes)
</ParamField>

<ParamField path="destinationDomain" type="uint32" required>
  Domain identifier of the destination chain (4 bytes)
</ParamField>

<ParamField path="nonce" type="uint64" required>
  Destination-specific nonce for replay protection (8 bytes)

  <Note>In V2, this is expanded to bytes32 (32 bytes)</Note>
</ParamField>

<ParamField path="sender" type="bytes32" required>
  Address of sender on source chain as bytes32 (32 bytes, starting at index 20)
</ParamField>

<ParamField path="recipient" type="bytes32" required>
  Address of recipient on destination chain as bytes32 (32 bytes, starting at index 52)
</ParamField>

<ParamField path="destinationCaller" type="bytes32" required>
  Address authorized to call receiveMessage on destination chain (32 bytes, starting at index 84)

  Set to bytes32(0) to allow any caller
</ParamField>

<ParamField path="messageBody" type="bytes" required>
  Dynamic bytes payload containing the burn message or custom data (starting at index 116)
</ParamField>

## Memory Layout (V1)

```
Field                 Bytes      Type       Index
-----------------------------------------------
version               4          uint32     0
sourceDomain          4          uint32     4
destinationDomain     4          uint32     8
nonce                 8          uint64     12
sender                32         bytes32    20
recipient             32         bytes32    52
destinationCaller     32         bytes32    84
messageBody           dynamic    bytes      116
```

<Info>
  Fields use specific padding: `uintNN` fields are left-padded, and `bytesNN` fields are right-padded. This ensures fixed-size encoding and prevents hash collisions.
</Info>

## Message V2 Format

Version 2 introduces additional fields for finality control:

```
Field                        Bytes      Type       Index
----------------------------------------------------------
version                      4          uint32     0
sourceDomain                 4          uint32     4
destinationDomain            4          uint32     8
nonce                        32         bytes32    12
sender                       32         bytes32    44
recipient                    32         bytes32    76
destinationCaller            32         bytes32    108
minFinalityThreshold         4          uint32     140
finalityThresholdExecuted    4          uint32     144
messageBody                  dynamic    bytes      148
```

### V2 Key Differences

* **nonce**: Expanded from `uint64` (8 bytes) to `bytes32` (32 bytes)
* **minFinalityThreshold**: Minimum finality level required for attestation
* **finalityThresholdExecuted**: Finality level at which message was executed

## Encoding Functions

### Format Message (V1)

Source: \~/workspace/source/src/messages/Message.sol:66-87

```solidity theme={null}
function _formatMessage(
    uint32 _msgVersion,
    uint32 _msgSourceDomain,
    uint32 _msgDestinationDomain,
    uint64 _msgNonce,
    bytes32 _msgSender,
    bytes32 _msgRecipient,
    bytes32 _msgDestinationCaller,
    bytes memory _msgRawBody
) internal pure returns (bytes memory)
```

Packs all message fields into a single bytes array using `abi.encodePacked`.

### Format Message for Relay (V2)

Source: \~/workspace/source/src/messages/v2/MessageV2.sol:78-101

```solidity theme={null}
function _formatMessageForRelay(
    uint32 _version,
    uint32 _sourceDomain,
    uint32 _destinationDomain,
    bytes32 _sender,
    bytes32 _recipient,
    bytes32 _destinationCaller,
    uint32 _minFinalityThreshold,
    bytes calldata _messageBody
) internal pure returns (bytes memory)
```

Formats a V2 message with empty nonce and finalityThresholdExecuted (set during attestation).

## Decoding Functions

All getter functions use TypedMemView for zero-copy operations:

### V1 Getters

```solidity theme={null}
// Extract version
function _version(bytes29 _message) internal pure returns (uint32)

// Extract source domain
function _sourceDomain(bytes29 _message) internal pure returns (uint32)

// Extract destination domain
function _destinationDomain(bytes29 _message) internal pure returns (uint32)

// Extract nonce
function _nonce(bytes29 _message) internal pure returns (uint64)

// Extract sender address
function _sender(bytes29 _message) internal pure returns (bytes32)

// Extract recipient address
function _recipient(bytes29 _message) internal pure returns (bytes32)

// Extract destination caller
function _destinationCaller(bytes29 _message) internal pure returns (bytes32)

// Extract message body
function _messageBody(bytes29 _message) internal pure returns (bytes29)
```

### V2 Getters

V2 uses `_get` prefix and includes additional fields:

```solidity theme={null}
// All V1 fields with _get prefix
function _getVersion(bytes29 _message) internal pure returns (uint32)
function _getSourceDomain(bytes29 _message) internal pure returns (uint32)
function _getDestinationDomain(bytes29 _message) internal pure returns (uint32)
function _getNonce(bytes29 _message) internal pure returns (bytes32) // Returns bytes32
function _getSender(bytes29 _message) internal pure returns (bytes32)
function _getRecipient(bytes29 _message) internal pure returns (bytes32)
function _getDestinationCaller(bytes29 _message) internal pure returns (bytes32)
function _getMessageBody(bytes29 _message) internal pure returns (bytes29)

// V2-specific fields
function _getMinFinalityThreshold(bytes29 _message) internal pure returns (uint32)
function _getFinalityThresholdExecuted(bytes29 _message) internal pure returns (uint32)
```

## Address Conversion

Convert between EVM addresses and bytes32:

### Address to Bytes32

Source: \~/workspace/source/src/messages/Message.sol:146-148

```solidity theme={null}
function addressToBytes32(address addr) external pure returns (bytes32) {
    return bytes32(uint256(uint160(addr)));
}
```

Left-pads the address with zeros to create a bytes32 value.

### Bytes32 to Address

Source: \~/workspace/source/src/messages/Message.sol:156-158

```solidity theme={null}
function bytes32ToAddress(bytes32 _buf) public pure returns (address) {
    return address(uint160(uint256(_buf)));
}
```

<Warning>
  Different `bytes32` values can map to the same address due to truncation. If uniqueness is required, validate that the first 12 bytes are zero-padding.
</Warning>

## Validation

### V1 Validation

Source: \~/workspace/source/src/messages/Message.sol:164-170

```solidity theme={null}
function _validateMessageFormat(bytes29 _message) internal pure {
    require(_message.isValid(), "Malformed message");
    require(
        _message.len() >= MESSAGE_BODY_INDEX,
        "Invalid message: too short"
    );
}
```

Ensures message is at least 116 bytes (minimum header size).

### V2 Validation

Source: \~/workspace/source/src/messages/v2/MessageV2.sol:170-176

```solidity theme={null}
function _validateMessageFormat(bytes29 _message) internal pure {
    require(_message.isValid(), "Malformed message");
    require(
        _message.len() >= MESSAGE_BODY_INDEX,
        "Invalid message: too short"
    );
}
```

Ensures message is at least 148 bytes (V2 minimum header size).

## TypedMemView Usage

Messages use the [TypedMemView library](https://github.com/summa-tx/memview-sol) for efficient, zero-copy memory operations:

```solidity theme={null}
using TypedMemView for bytes;
using TypedMemView for bytes29;
```

* `bytes29`: Efficient memory view type that avoids copying data
* `indexUint()`: Reads uint values at specific byte offsets
* `index()`: Reads bytes32 values at specific byte offsets
* `slice()`: Creates sub-views without copying data

## Example Usage

### Encoding a Message

```solidity theme={null}
import {Message} from "./messages/Message.sol";
import {AddressUtils} from "./messages/v2/AddressUtils.sol";

// Convert addresses to bytes32
bytes32 senderBytes32 = AddressUtils.toBytes32(msg.sender);
bytes32 recipientBytes32 = AddressUtils.toBytes32(recipientAddress);

// Format message
bytes memory message = Message._formatMessage(
    0,                          // version
    0,                          // source domain
    1,                          // destination domain
    12345,                      // nonce
    senderBytes32,              // sender
    recipientBytes32,           // recipient
    bytes32(0),                 // any destination caller
    burnMessageBytes            // message body
);
```

### Decoding a Message

```solidity theme={null}
import {TypedMemView} from "@memview-sol/contracts/TypedMemView.sol";
import {Message} from "./messages/Message.sol";

using TypedMemView for bytes;
using Message for bytes29;

// Convert to TypedMemView
bytes29 messageView = receivedMessage.ref(0);

// Validate format
messageView._validateMessageFormat();

// Extract fields
uint32 version = messageView._version();
uint32 sourceDomain = messageView._sourceDomain();
bytes32 sender = messageView._sender();
bytes29 body = messageView._messageBody();

// Convert bytes32 back to address
address senderAddress = Message.bytes32ToAddress(sender);
```

## See Also

* [BurnMessage Format](/api/messages/burn-message) - Message body format for token transfers
* [AddressUtils](/api/messages/address-utils) - Address conversion utilities
* [MessageTransmitter](/api/message-transmitter) - Contract that processes messages
