{"id":"ERC-7786","name":"Cross-Chain Messaging Gateway","status":"Final","chain":"both","category":{"id":"cross-chain","name":"Cross-Chain","description":"Moving assets and messages between chains"},"journeyStages":[{"id":"executing","name":"Executing Transactions","description":"Performing on-chain actions"},{"id":"status","name":"Status & Confirmation","description":"Waiting for and confirming outcomes"}],"uxImpact":"ERC-7786 standardizes gateway source/recipient message calls and attributes. Applications can send a message through a compliant gateway, while route discovery, bridge health, status, post-processing, retry, and refunds remain adapter/provider-specific.","officialUrl":"https://ercs.ethereum.org/ERCS/erc-7786","lastReviewed":"2026-09-18","lastUpdated":"2026-09-18","kind":"proposal","hasDetailedContent":true,"content":{"id":"ERC-7786","summary":"ERC-7786 standardizes gateway source/recipient message calls and attributes. Applications can send a message through a compliant gateway, while route discovery, bridge health, status, post-processing, retry, and refunds remain adapter/provider-specific.","specConstraints":[{"id":"interoperable-message-addresses","constraint":"Source and recipient accounts must use ERC-7930 Interoperable Address binary format serialized according to CAIP-350; the recipient may be omitted or zeroed for an unspecified destination.","appliesTo":"application","normativity":"normative","specQuote":"The sender account (in the source chain) and recipient account (in the destination chain) MUST be input in an Interoperable Address binary format, specified in [ERC-7930](./eip-7930.md), which MUST be serialized according to CAIP-350. The recipient field MAY be omitted or zeroed (contain an Interoperable Address with all fields set to zero) to indicate an unspecified destination, such as when a broadcast mode is employed and the final recipients are determined by the protocol itself.","specSection":"Specification / Message Field Encoding / Sender and Recipient","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#sender-and-recipient","designImplication":"Validate the destination chain and address encoding before send, and clearly label an omitted or zeroed recipient as an unspecified or broadcast destination rather than a normal address."},{"id":"attributes-array-encoding","constraint":"The message attribute list must be encoded as bytes[] with each element concatenating its key and value.","appliesTo":"application","normativity":"normative","specQuote":"The list of attributes MUST be encoded as `bytes[]` (an array of `bytes`) where each element is the concatenation of a key and a value. (Note that in the case of keys defined as Solidity function selectors, each element of the array can be encoded by `abi.encodeWithSignature`.)","specSection":"Specification / Message Field Encoding / Attributes","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#attributes","designImplication":"Treat attribute support and encoding as route capabilities. Do not present an attribute as available until the selected provider can encode and accept it."},{"id":"empty-attributes-valid","constraint":"A message with an empty attribute list must be treated as valid.","appliesTo":"application","normativity":"normative","specQuote":"A message with no attributes (an empty attributes list) MUST be considered a valid message.","specSection":"Specification / Message Field Encoding / Attributes","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#attributes","designImplication":"Allow a basic cross-chain message to proceed without forcing the user to configure optional attributes."},{"id":"gateway-attribute-compatibility","constraint":"A gateway should not remove support for an attribute once that support is present.","appliesTo":"infrastructure","normativity":"normative","specQuote":"A gateway MAY be upgraded with support for additional attributes. Once present support for an attribute SHOULD NOT be removed to preserve backwards compatibility with users of the gateway.","specSection":"Specification / Sending Procedure / supportsAttribute","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#supportsattribute","designImplication":"Show provider-reported attribute support and handle a capability change as a route state; do not silently keep presenting a previously supported route as guaranteed."},{"id":"unsupported-attribute-rejection","constraint":"A gateway must revert when an unsupported attribute is included; it may also revert when an attribute value is invalid for its key.","appliesTo":"infrastructure","normativity":"normative","specQuote":"MUST revert with `UnsupportedAttribute` if an unsupported attribute is included. MAY revert if an attribute value is not valid for the key.","specSection":"Specification / Sending Procedure / sendMessage","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#sendmessage","designImplication":"Preflight attribute support where possible and distinguish an unsupported route from a provider-reported invalid value. Do not promise one universal error for every invalid attribute."},{"id":"unsupported-native-value","constraint":"A gateway may accept native call value and must revert when value is included but unsupported; its destination representation is unspecified.","appliesTo":"infrastructure","normativity":"normative","specQuote":"MAY accept call value (native token) to be sent with the message. MUST revert if call value is included but it is not a feature supported by the gateway. It is unspecified how this value is represented on the destination.","specSection":"Specification / Sending Procedure / sendMessage","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#sendmessage","designImplication":"Only expose a native-value field when the selected gateway reports support, and explain that the destination representation is provider-specific."},{"id":"message-sent-observability","constraint":"sendMessage must emit a MessageSent event containing the optional send identifier it returns.","appliesTo":"infrastructure","normativity":"normative","specQuote":"MUST emit a `MessageSent` event, including the optional send identifier that is returned by the function.","specSection":"Specification / Sending Procedure / sendMessage","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#sendmessage","designImplication":"Use the source event and send identifier for source-side activity, while keeping that record distinct from destination delivery."},{"id":"post-processing-step","constraint":"After sendMessage, gateway post-processing may be needed before the message is effective, including payment for destination execution gas.","appliesTo":"infrastructure","normativity":"normative","specQuote":"After a sender has invoked `sendMessage`, further action MAY be required by the gateways to make the message effective. This is called _post-processing_. For example, some payment is typically required to cover the gas of executing the message at the destination.","specSection":"Specification / Sending Procedure / Post-processing","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#post-processing","designImplication":"Model post-processing as a distinct pending step and disclose any provider-supplied destination-gas payment instead of labeling sendMessage as completed delivery."},{"id":"protocol-recipient-delivery","constraint":"The protocol must ensure delivery to the recipient through the IERC7786Recipient interface.","appliesTo":"infrastructure","normativity":"normative","specQuote":"The protocol MUST ensure delivery of a sent message to its **recipient** using the `IERC7786Recipient` interface (specified below), which the recipient MUST implement.","specSection":"Specification / Reception Procedure","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#reception-procedure","designImplication":"Keep source-sent, relaying, and destination-delivered states separate; the gateway's delivery promise is not proof that the recipient has already executed."},{"id":"recipient-interface","constraint":"A recipient must implement the IERC7786Recipient interface for protocol delivery.","appliesTo":"account-implementation","normativity":"normative","specQuote":"The protocol MUST ensure delivery of a sent message to its **recipient** using the `IERC7786Recipient` interface (specified below), which the recipient MUST implement.","specSection":"Specification / Reception Procedure","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#reception-procedure","designImplication":"Only offer this message flow for recipients that support the standard receive interface, and give unsupported-recipient failures their own state."},{"id":"delivery-invocation","constraint":"Once safe delivery is possible, the gateway must invoke receiveMessage unless the sender or recipient explicitly requested otherwise.","appliesTo":"infrastructure","normativity":"normative","specQuote":"Once the message can be safely delivered (see Properties), the gateway MUST invoke `receiveMessage` with the message identifier and contents, unless the sender or the recipient explicitly requested otherwise.","specSection":"Specification / Reception Procedure","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#reception-procedure","designImplication":"Do not show successful destination completion before the provider reports invocation, and make an explicitly requested non-delivery path visible."},{"id":"receive-id-uniqueness","constraint":"A receiveId must be empty or unique for the calling gateway and relayed message.","appliesTo":"infrastructure","normativity":"normative","specQuote":"The `receiveId` MUST be either empty or unique (for the calling gateway) to the message being relayed. The format of this identifier is not specified, and the gateway can use it at its own discretion. For example it can be an identifier of the `MessagePosted` event that created the message.","specSection":"Specification / Reception Procedure","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#reception-procedure","designImplication":"Use the provider's receive identifier for destination tracking when available, but label its format as gateway-specific and do not assume it equals sendId."},{"id":"receive-result-validation","constraint":"The gateway must verify the receiveMessage return value and revert when it is incorrect.","appliesTo":"infrastructure","normativity":"normative","specQuote":"The gateway MUST verify that `receiveMessage` returns the correct value, and MUST revert otherwise.","specSection":"Specification / Reception Procedure","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#reception-procedure","designImplication":"Treat a failed return-value check as failed destination delivery, not as a successfully executed message."},{"id":"known-gateway-validation","constraint":"A recipient must validate that the caller is a known trusted gateway.","appliesTo":"account-implementation","normativity":"normative","specQuote":"The recipient MUST validate that the caller of this function is a **known gateway**, i.e., one whose underlying cross-chain messaging protocol it trusts.","specSection":"Specification / Reception Procedure / receiveMessage","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#receivemessage","designImplication":"Make the trusted gateway or provider legible in recipient configuration and treat an unrecognized caller as an authenticity failure."},{"id":"message-safety-finality","constraint":"The underlying protocol must guarantee finalized, once-only delivery for each sent message, while allowing identical-parameter messages to remain distinct.","appliesTo":"infrastructure","normativity":"normative","specQuote":"- The protocol MUST guarantee Safety: A message is delivered at the destination if and only if it was sent at the source. The delivery process must ensure a message is only delivered once the sending transaction is finalized, and not delivered more than once. Note that there can be multiple messages with identical parameters that must be delivered separately.","specSection":"Specification / Properties","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#properties","designImplication":"Keep confirmation tied to source finality and destination execution, and do not deduplicate separate messages merely because their visible parameters match."},{"id":"message-liveness","constraint":"The underlying protocol must guarantee eventual delivery when both chains remain live and censorship-resistant and protocol costs are paid.","appliesTo":"infrastructure","normativity":"normative","specQuote":"- The protocol MUST guarantee Liveness: A sent message is eventually delivered to the destination, assuming Liveness and censorship-resistance of the source and destination chains and that any protocol costs are paid.","specSection":"Specification / Properties","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#properties","designImplication":"Explain the conditions behind an eventual-delivery promise and show a waiting or blocked state when chain liveness or required costs are unavailable."},{"id":"message-timeliness","constraint":"The underlying protocol should guarantee delivery within a documented bounded time.","appliesTo":"infrastructure","normativity":"normative","specQuote":"- The protocol SHOULD guarantee Timeliness: A sent message is delivered at the destination within a bounded delivery time, which should be documented.","specSection":"Specification / Properties","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#properties","designImplication":"Show an ETA only when the provider documents a bound; otherwise use an honest pending or unknown state."},{"id":"noncentralized-relaying","constraint":"Safety and liveness should not rely on a centralized actor; relaying should be decentralized or permissionless because a centralized relayer can halt the protocol.","appliesTo":"infrastructure","normativity":"normative","specQuote":"- The above properties SHOULD NOT rely on trust in some centralized actor. For example, safety should be guaranteed by some trustless mechanism such as a light client proof or attestations by an open, decentralized validator set. Relaying should be decentralized or permissionless to ensure liveness; a centralized relayer can fail and thus halt the protocol.","specSection":"Specification / Properties","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#properties","designImplication":"Name the provider and relay dependency behind delivery status; do not imply guaranteed liveness when the selected route depends on one operator."},{"id":"post-processing-required-call","constraint":"When additional parameters are not supplied as attributes, an additional gateway call is required for the message to be sent.","appliesTo":"infrastructure","normativity":"rationale","specQuote":"If these additional parameters are not provided through attributes, an additional call to the gateway is REQUIRED for the message to be sent.","specSection":"Rationale","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#rationale","designImplication":"Represent provider-required post-processing as an explicit step with its own fee, pending, and failure states instead of hiding it behind a single send action."},{"id":"permissionless-relaying","constraint":"Where possible, the gateway should let anyone with an incentive to deliver the message relay it.","appliesTo":"infrastructure","normativity":"rationale","specQuote":"If possible, the gateway SHOULD be designed so that anyone with an incentive for the message to be delivered can jump in.","specSection":"Rationale","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#rationale","designImplication":"Do not design recovery copy that assumes only the original operator can resume a stalled message; expose provider-supported relay or retry options."},{"id":"invalid-relayer-parameters","constraint":"A malicious actor providing invalid parameters should not prevent another actor from relaying the message successfully.","appliesTo":"infrastructure","normativity":"rationale","specQuote":"A malicious actor providing invalid parameters SHOULD NOT prevent the message from being successfully relayed by someone else.","specSection":"Rationale","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#rationale","designImplication":"Keep a bad-relayer attempt recoverable and distinguish it from permanent message failure when the provider offers another relay path."},{"id":"canonical-address-handling","constraint":"Gateways should reject non-canonical Interoperable Address encodings and may normalize them before emission.","appliesTo":"infrastructure","normativity":"security-considerations","specQuote":"Interoperable Addresses (ERC‑7930) rely on CAIP‑350 serialization. Using non‑canonical encodings can lead to silent delivery failures. Gateways SHOULD reject non‑canonical encodings and MAY normalize them before emission.","specSection":"Security Considerations / Handling addresses","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#handling-addresses","designImplication":"Validate canonical address encoding before submission and show a correction or rejection state rather than allowing a silent delivery failure."},{"id":"attribute-support-extensible","constraint":"A gateway may support any set of standard or custom message attributes.","appliesTo":"infrastructure","normativity":"normative","specQuote":"A gateway MAY support any set of standard or custom attributes.","specSection":"Specification / Message Field Encoding / Attributes","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#attributes","designImplication":"Treat attribute support as gateway-specific capability data: expose supported features and give unsupported attributes a clear correction path."},{"id":"attribute-key-labeling","constraint":"Attribute keys are recommended to use Solidity function selectors so they have human-readable names and standard ABI value encoding.","appliesTo":"application","normativity":"normative","specQuote":"An attribute key is a 4-byte value (`bytes4`). It is RECOMMENDED to define a key as the function selector for a function signature according to Solidity ABI (for example, `minGasLimit(uint256)` resulting in the key `39f87ba1`), so as to give the key a human-readable name and a standard value encoding corresponding to the ABI-encoding of the function arguments.","specSection":"Specification / Message Field Encoding / Attributes","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#attributes","designImplication":"Show a readable attribute label and value schema in advanced message review instead of exposing only an opaque bytes4 key."},{"id":"send-id-tracking","constraint":"A gateway may return a source-side send identifier, which can be zero or differ from the destination receive identifier.","appliesTo":"application","normativity":"normative","specQuote":"MAY generate and return a unique non-zero _send identifier_, otherwise returning zero. This identifier can be used to track the lifecycle of the message in the source gateway in events and for post-processing. _Note that this identifier MAY be different from the `receiveId` that is delivered to the recipient, since that identifier may preferably consist of values like transaction id and log index that are not available in the execution environment._","specSection":"Specification / Sending Procedure / sendMessage","specUrl":"https://ercs.ethereum.org/ERCS/erc-7786#sendmessage","designImplication":"Present sendId as source-side tracking only, allow an absent or zero value, and keep it separate from destination receipt or completion identifiers."}],"applicability":{"whenToUse":["Your product addresses: each bridge has different integration requirements.","Your users see inconsistent cross-chain UI across apps.","Your UI should send through a gateway while exposing provider-specific route and status details.","You are designing a bridge-agnostic transfer experience with visible states and recovery paths."],"whenToAvoid":["The product never sends a cross-chain message or changes state across chains.","No gateway source/recipient implementation is available for the flow.","The selected provider exposes no route, status, or recovery data to the application.","The flow only performs same-chain actions."]},"designerTakeaways":["Design around a gateway interface only for providers that implement the required source/recipient calls and attributes.","Use a common message presentation while keeping route, status, post-processing, and recovery provider-specific.","Swap gateway adapters only after verifying their attributes, recipient behavior, and actual application integration."],"problemsSolved":[{"problem":"Each bridge has different integration requirements","oldWay":"Custom code for LayerZero, different code for Wormhole, etc.","newWay":"A single gateway interface can reduce integration differences for compliant providers; route and behavior still require verification.","impact":"critical"},{"problem":"Users see inconsistent cross-chain UI across apps","oldWay":"Every app shows bridges differently, different confirmations","newWay":"A standard message shape can support consistent presentation while provider-specific status and post-processing remain visible.","impact":"high"},{"problem":"Switching bridges requires code changes","oldWay":"Hardcoded bridge dependency, risky to change","newWay":"The application can change a gateway adapter when the new provider supports the required interface and semantics.","impact":"high"},{"problem":"No standard way to track cross-chain message status","oldWay":"Each bridge has different tracking APIs","newWay":"MessageSent and send IDs provide a source-side record; status remains provider-specific","impact":"medium"},{"problem":"Bridge failures handled inconsistently","oldWay":"Some bridges auto-retry, some fail silently, some refund","newWay":"Provider-specific error, post-processing, retry, and refund handling","impact":"medium"}],"uxPatterns":[{"name":"Bridge-Agnostic Transfer","description":"User picks destination, not bridge","userFlow":["User enters amount and destination chain","Application/provider supplies available bridge routes","Shows routes sorted by user preference and provider data","User picks route (or accepts default)","Single interface sends regardless of bridge"]},{"name":"Cross-Chain Message Tracker","description":"Unified status view for provider data across selected bridge routes","userFlow":["User sends cross-chain message","Redirected to status page","Polls the provider or adapter for status updates","Progress bar advances through stages","Shows bridge-specific details where helpful"]},{"name":"Bridge Health Dashboard","description":"Show provider-supplied bridge reliability and status","userFlow":["User views bridge dashboard","Provider/adapter aggregates bridge health data","Shows status, speed, and reliability metrics","Alerts highlight any issues","Helps user choose appropriate bridge"]},{"name":"Failed Message Recovery","description":"Handle failed cross-chain messages when provider recovery exists","userFlow":["Message fails to deliver","User sees notification of issue","UI explains what happened clearly","Offers retry or refund options only when the provider supports them","Labels bridge-specific post-processing and recovery"]}],"uiComponents":[{"name":"CrossChainProgress","description":"Show provider/application-reported message progress; ERC-7786 does not define universal stages.","states":["pending","confirming","relaying","executing","complete","failed"],"props":["sourceChain","destChain","currentStage","estimatedTime"]},{"name":"BridgeSelector","description":"Choose among provider/application-supplied routes; ERC-7786 does not standardize route discovery.","states":["loading","available","comparing","selected"],"props":["routes[]","sortBy","onSelect"]},{"name":"MessageStatusPoller","description":"Poll provider/application status when available.","states":["polling","updated","complete","error"],"props":["messageId","pollInterval","provider"]},{"name":"BridgeHealthBadge","description":"Show provider-reported bridge health; ERC-7786 does not define health semantics.","states":["healthy","degraded","down","unknown"],"props":["bridgeName","status","lastCheck"]}],"antiPatterns":[{"pattern":"Hiding which gateway or provider is used","why":"Users need the provider context for trust and troubleshooting.","instead":"Show the selected gateway, route, and source for each estimate or guarantee.","severity":"critical"},{"pattern":"Promising status updates or ETA from the ERC","why":"ERC-7786 exposes message events and IDs but leaves status and post-processing out of scope.","instead":"Show provider-sourced progress, or pending/unknown when no feed exists.","severity":"critical"},{"pattern":"Offering retry or refund without provider support","why":"Recovery behavior is outside the gateway interface.","instead":"Offer only verified provider/application actions and label post-processing.","severity":"critical"},{"pattern":"Auto-selecting a route without explanation","why":"Cost, speed, health, and security differ by provider.","instead":"Show the route choice and its data source; let the user change it when supported.","severity":"high"},{"pattern":"Treating sendId as destination completion","why":"The send ID can be zero or differ from the receive ID and does not prove execution.","instead":"Separate source MessageSent from destination/provider observations.","severity":"high"}],"keyTakeaways":["ERC-7786 standardizes source/recipient gateway calls and message attributes for compatible providers.","Show provider, route, and source-side send ID separately from destination receipt.","Build progress and ETA only from provider data, with unknown/pending states when absent.","Build retry, refund, and post-processing UI only for verified provider/application paths."],"technicalNotes":"ERC-7786 defines IERC7786GatewaySource.supportsAttribute(bytes4) and sendMessage(bytes recipient, bytes payload, bytes[] attributes) payable returns (bytes32 sendId), plus a recipient receiveMessage(bytes32 receiveId, bytes sender, bytes payload). It defines a MessageSent source event; status, destination invocation/post-processing, route discovery, health, retry, and refunds are out of scope. The send ID may be zero or differ from the receive ID.","lastUpdated":"2026-09-18","sources":[{"label":"ERC-7786 official specification","url":"https://ercs.ethereum.org/ERCS/erc-7786","type":"official-spec"}],"lastReviewed":"2026-09-18"},"sources":[{"label":"Official specification","url":"https://ercs.ethereum.org/ERCS/erc-7786","type":"official-spec"},{"label":"Discussion search","url":"https://ethereum-magicians.org/search?q=ERC-7786","type":"discussion"}],"urls":{"canonical":"https://www.eipsfordesigners.com/standards/ERC-7786","markdown":"https://www.eipsfordesigners.com/standards/ERC-7786/content.md","agent":"https://www.eipsfordesigners.com/standards/ERC-7786/agent.md","api":"https://www.eipsfordesigners.com/api/standards/ERC-7786","official":"https://ercs.ethereum.org/ERCS/erc-7786","discussion":"https://ethereum-magicians.org/search?q=ERC-7786"},"freshness":{"lastReviewed":"2026-09-18","lastUpdated":"2026-09-18"}}