0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

How Do I Make Cross-Chain Message Retries Safe?

0
Posted at

Make the destination handler idempotent before enabling automatic execution or manual retries; every attempt must either complete the intended state change once or leave it safe to try again. This matters whenever a cross-chain instruction can be re-submitted after a timeout, out-of-gas failure or temporary dependency outage.

What can happen when a message is retried?

A verified message may be executed more than once as an attempt, even when the protocol prevents a successfully completed message from executing twice. Verification says the message is authentic; execution can still fail and be retried. LayerZero V2, for example, separates verification from destination execution and allows a failed OApp delivery to be retried without sending a new source transaction.

That retry is useful only if the handler accounts for its previous effects. An EVM revert rolls back that transaction’s on-chain writes and contract calls, but a handler that catches an error and returns successfully can leave partial state behind. Off-chain work triggered by an event also cannot be rolled back with the transaction, so downstream consumers need their own deduplication.

Which identifier should the receiver remember?

Key completion by the protocol’s stable message identity, scoped to the receiving application. In a LayerZero OApp, the origin contains the source endpoint, sender and nonce, and the message includes a GUID; CCIP tracks execution by a message ID derived from the encoded message. Use the identifier the protocol supplies for that message rather than generating a fresh key each time the handler runs.

Do not use the payload hash alone as the deduplication key if users may intentionally submit the same instruction twice. Two identical “transfer 10” payloads can represent two valid transfers; two execution attempts for one message share a message identity. If the application supports multiple commands inside one message, add a stable command ID to the payload and key each command by message identity plus command ID.

How should the handler apply the operation?

Check the completion record, validate the payload and apply the state change in one atomic destination-chain transaction. A minimal pattern is: reject an already-completed key; mark the key complete; perform the state update and required contract calls. If any uncaught call reverts, the completion mark and state changes revert together, leaving a retry possible. The completion mapping needs no expiry if message identities never repeat within their protocol scope.

Consider two instructions side by side. “Set remote epoch to 42” is naturally idempotent: applying it twice leaves the same value. “Add 10 tokens to the balance” is not: repeating it mints or credits 20. For the additive command, store its processed identity before crediting and keep both writes in the same transaction; never treat a new transaction hash or executor address as a new command.

For an external action that cannot share the transaction, such as an off-chain payout, write an outbox record keyed by the message ID in the handler’s transaction. A worker can then retry delivery using that same key, while the payout provider or consumer deduplicates it. Marking the message complete before recording durable work risks losing the action; marking complete only after an uncertain external response risks repeating it.

When should messages execute in order?

Require nonce-ordered execution only when later state transitions depend on earlier ones. A balance snapshot or “set configuration version” message may tolerate reordering if the receiver rejects stale versions. A sequence such as “open position, then close position” may not: executing the close first can fail or produce a different result. LayerZero exposes ordered execution options, while its OApp design guidance also describes enforcing sequence requirements in application logic.

Ordering trades throughput for a stronger sequence guarantee. One failed message can hold later messages on that path until it is repaired, so partition independent work into separate channels or sequences where the protocol allows it. Do not skip a failed nonce merely to unblock traffic unless the application has a defined recovery transition; skipping verification and retrying verified execution are different recovery operations.

Before shipping, test duplicate delivery, a reverted handler followed by retry, two identical but distinct commands, and an out-of-order pair. The Chainlink CCIP and LayerZero V2 documentation describe their respective message identity and retry behavior; the application still has to make its business effects repeat-safe. If those effects need one coordinated state across chains, use the omnichain approach to coordinate application state, token supply and liquidity across the participating networks.

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?