Skip to main content
A finalized state change on Metal can trigger a transaction on another chain. The sending side is one contract call; the receiving side verifies a fixed-depth Merkle path against the message root committed in a light-client checkpoint, and dispatches the committed call exactly once.

Send from Metal

Call send on the Metal outbox with the destination chain, the target contract, and the calldata to deliver:
send appends a commitment as leaf nonce of an append-only Merkle tree whose composite root every Metal block header carries (and consensus validates):
The commitment binds the full execution domain. A message queued for one chain cannot replay on another, cannot be redirected to a different target, and cannot be executed with altered calldata or a forged sender.
The message is not actionable until its block is finalized. Track finality with consensus_status, or wait for the destination-side execution event.

Execute on the destination chain

MetalInbox.execute takes the message fields plus the proof: the tree’s leaf count at the checkpoint and a 32-element sibling path, both derivable from MessageSent events alone — no state queries, no archival node:
The inbox recomputes the leaf from the submitted fields, folds it up the index-ordered path, and requires the count-bound result to equal the checkpoint’s message root before dispatching target.call(payload). The leaf count is bound into the committed root, so it cannot be overstated to reach past the last message. Execution semantics:
  • Exactly once. A consumed nonce can never execute again, under any checkpoint.
  • Retryable on failure. If the target reverts, the whole execution reverts and the nonce stays unconsumed, so the message can be retried. A target that always reverts simply never consumes its nonce.
  • Checkpoint-specific proofs. An accepted checkpoint at or after the message’s block can prove it. The sibling path and leaf count must match that checkpoint’s message root. A proof remains usable against its original accepted checkpoint while the message is unconsumed.
  • Unordered delivery. The inbox checks whether a nonce was consumed but does not require the previous nonce to execute first. Targets must enforce any application-level ordering requirement.

Authenticate the caller

The target contract sees the inbox as msg.sender. To know who sent the message on Metal, read back through the inbox during execution:
Always gate the target function on the inbox address and authorize against metalSender(). Without the gate, anyone can call your target directly with arbitrary arguments.

Relaying

Relayers rebuild the message tree from MessageSent events and select an accepted checkpoint at or after the message’s block. They build the proof against that checkpoint’s root and leaf count, then call execute on the inbox. If no accepted checkpoint covers the message, they fetch a finality certificate from a Metal node and submit update on the light client first. To use a later checkpoint that includes additional messages, rebuild the proof for its tree. Relaying is permissionless. Checkpoints and message proofs are verified onchain, so relayers need no registration or stake. Relayers pay destination- chain gas and use MessageSent events to construct the message proof. The current destination-side MetalLightClient accepts only epoch 0. It cannot checkpoint later-epoch certificates without a verifier update. Relayers submit checkpoints when messages need delivery. One checkpoint can cover a batch of earlier messages, provided each message has a proof matching that checkpoint’s root and leaf count. Certificate gas is paid once per checkpoint.

Send messages from Metal

Follow message submission, proof construction, and delivery retries.

Finality certificates

Fetching and verifying the witness a checkpoint is built from.

Light Clients and Messages

The end-to-end path and trust model.