> ## Documentation Index
> Fetch the complete documentation index at: https://docs.metalntwx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Inbound Messages

> Proving and executing source-chain messages on Metal

## Send on the source chain

The source-chain `Outbox` exposes this interface:

```solidity theme={null}
interface IOutbox {
    event MessageSent(
        uint256 indexed nonce,
        uint64 indexed destinationChainId,
        address indexed target,
        address sender,
        bytes payload
    );

    function send(uint64 destinationChainId, address target, bytes calldata payload)
        external
        returns (uint256 nonce);

    function messages(uint256 nonce) external view returns (bytes32);
    function nextNonce() external view returns (uint256);
}
```

`send` writes the following commitment:

```text theme={null}
messages[nonce] = keccak256(abi.encode(
    sourceChainId,
    destinationChainId,
    nonce,
    msg.sender,
    target,
    payload
))
```

The message commitment binds both chain IDs, the sequential nonce, the source
sender, and the exact Metal call.

## Storage commitment

The `messages` mapping occupies storage slot `0`. A commitment for `nonce`
therefore resides at:

```text theme={null}
slot = keccak256(abi.encode(nonce, uint256(0)))
```

Each slot is written once. `ForeignInbox` verifies this storage value. Relayers
use `MessageSent` to reconstruct the execution call.

The inbox pins the source `Outbox`'s runtime bytecode hash as immutable
`foreignOutboxCodeHash`. Route providers must supply the approved runtime
bytecode and hash for a direct, immutable `Outbox`. This excludes proxies,
whose implementations can change while their runtime bytecode stays the same. A
new `Outbox` version requires a new inbox. Preserve application replay records
when replacing a route, since the new inbox starts with no consumed nonces.

## Foreign inbox

```solidity theme={null}
interface IForeignInbox {
    error CodeHashMismatch();
    error InvalidOutboxCodeHash();

    event MessageExecuted(
        uint256 indexed nonce,
        address indexed target,
        address sender
    );

    function execute(
        bytes32 checkpointBlockHash,
        uint256 nonce,
        address sender,
        address target,
        bytes calldata payload,
        bytes[] calldata accountProof,
        bytes[] calldata storageProof
    ) external;

    function foreignSender() external view returns (address);
    function consumed(uint256 nonce) external view returns (bool);
    function lightClient() external view returns (address);
    function foreignOutbox() external view returns (address);
    function foreignOutboxCodeHash() external view returns (bytes32);
    function foreignChainId() external view returns (uint256);
}
```

Deployment rejects a zero code hash or `keccak256("")` with
`InvalidOutboxCodeHash`. Calculate the configured hash from the outbox's
runtime bytecode.

## Build the proof

First select an accepted source-chain checkpoint on Metal. For Tempo and Arc, a
relayer can submit the checkpoint to the corresponding contract light client.
Metal block execution maintains Ethereum checkpoints. Call `eth_getProof` on the
source chain for the outbox at the accepted checkpoint's block number:

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "eth_getProof",
  "params": [
    "0xFOREIGN_OUTBOX",
    ["0xMESSAGE_STORAGE_SLOT"],
    "0xCHECKPOINT_BLOCK_NUMBER"
  ]
}
```

Pass the returned account proof as `accountProof` and the requested slot's proof
as `storageProof`. The inbox authenticates the account under the accepted state
root and compares its code hash to `foreignOutboxCodeHash` before using its
storage root. `CodeHashMismatch` indicates different runtime bytecode at that
checkpoint.

## Authenticate the source sender

The receiving contract authenticates the inbox through `msg.sender`, then
authorizes the source sender returned by `foreignSender()` before processing the
payload:

```solidity theme={null}
contract ForeignReceiver {
    address immutable inbox;

    constructor(address inbox_) {
        inbox = inbox_;
    }

    function receiveMessage(bytes calldata data) external {
        require(msg.sender == inbox, "only ForeignInbox");
        address sender = IForeignInbox(msg.sender).foreignSender();
        // Authorize `sender` and process `data`.
    }
}
```

> [card] [Receive messages on Metal](https://docs.metalntwx.com/build/interop/receive-messages-on-metal.md)
>
> Configure a route, collect matching proofs, and handle delivery results.

> [card] [Light clients](https://docs.metalntwx.com/reference/interop/light-clients.md)
>
> Review the checkpoints that authenticate source-chain state roots.

> [card] [Outbound messages](https://docs.metalntwx.com/reference/interop/messages.md)
>
> Send a cross-chain message from Metal to a destination chain.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.