{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-pages/developers/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["admonition"]},"type":"markdown"},"seo":{"title":"Send Cross-Chain Messages with Wormhole","siteUrl":"https://docs.xrplevm.org/","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"send-cross-chain-messages-with-wormhole","__idx":0},"children":["Send Cross-Chain Messages with Wormhole"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Wormhole's ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Core Contract"]}," lets any smart contract on the XRPL EVM publish arbitrary messages that can be verified and consumed on 40+ connected chains. This guide shows the raw messaging flow in Solidity: publishing a message on the source chain and verifying its ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["VAA"]}," (Verifiable Action Approval) on the destination chain."]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning","name":"Delivery on Mainnet uses the Executor"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["On ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["XRPL EVM Mainnet"]}," the legacy ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["IWormholeRelayer"]}," is not part of the official deployment and no delivery provider services it, so tutorials built on ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sendPayloadToEvm"]}," only work on ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Testnet"]},". For production delivery, publish through the Core Contract and request delivery through the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://wormhole.com/docs/protocol/infrastructure/relayers/executor-framework/"},"children":["Executor framework"]},". The ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://wormhole.com/docs/products/messaging/get-started/"},"children":["Wormhole TypeScript SDK"]}," manages Executor quoting and execution for you."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"prerequisites","__idx":1},"children":["Prerequisites"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["A funded deployer account on ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/pages/users/getting-started/connect-to-the-xrpl-evm"},"children":["XRPL EVM Mainnet or Testnet"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://getfoundry.sh/"},"children":["Foundry"]}," (or Hardhat) for contract development"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The Wormhole Solidity SDK:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"forge install wormhole-foundation/wormhole-solidity-sdk@v1.0.0\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use tagged releases of the SDK; the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["main"]}," branch is a nightly build. In SDK ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["v1.0.0"]}," the Core Contract interface is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ICoreBridge"]}," (defined in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["interfaces/ICoreBridge.sol"]},", together with the file-level ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["CoreBridgeVM"]}," struct). Older Wormhole tutorials that import ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["IWormhole.sol"]}," will not compile against this release."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"publish-a-message-source-chain","__idx":2},"children":["Publish a Message (Source Chain)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Call ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["publishMessage"]}," on the Core Contract (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["0xaBf89de706B583424328B54dD05a8fC986750Da8"]}," on both XRPL EVM Mainnet and Testnet), paying the current message fee:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"solidity","header":{"controls":{"copy":{}}},"source":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\nimport \"wormhole-solidity-sdk/interfaces/ICoreBridge.sol\";\n\ncontract HelloWormhole {\n    ICoreBridge public immutable wormhole;\n\n    constructor(address wormholeCore) {\n        wormhole = ICoreBridge(wormholeCore);\n    }\n\n    function sendMessage(\n        string memory message\n    ) external payable returns (uint64 sequence) {\n        uint256 wormholeFee = wormhole.messageFee();\n        require(msg.value >= wormholeFee, \"insufficient message fee\");\n\n        sequence = wormhole.publishMessage{value: wormholeFee}(\n            0,                  // nonce: free integer field\n            abi.encode(message),\n            1                   // consistencyLevel: finality required before attestation\n        );\n    }\n}\n","lang":"solidity"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Once the transaction finalizes, the Guardian network observes the emitted message and produces a signed VAA. You can fetch it from the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://docs.wormholescan.io"},"children":["WormholeScan API"]}," at ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://api.wormholescan.io/v1/signed_vaa/57/<emitterAddress>/<sequence>"]}," (use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["api.testnet.wormholescan.io"]}," for Testnet; ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["emitterAddress"]}," is your contract address left-padded to 32 bytes), or track it at ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://wormholescan.io/#/tx/<txHash>"]}," (append ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["?network=Testnet"]}," for Testnet)."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"verify-and-consume-the-vaa-destination-chain","__idx":3},"children":["Verify and Consume the VAA (Destination Chain)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["On the destination chain, pass the encoded VAA to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["parseAndVerifyVM"]}," on that chain's Core Contract:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"solidity","header":{"controls":{"copy":{}}},"source":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\nimport \"wormhole-solidity-sdk/interfaces/ICoreBridge.sol\";\n\ncontract HelloWormholeReceiver {\n    ICoreBridge public immutable wormhole;\n    // Trusted emitter (source contract) per Wormhole chain ID\n    mapping(uint16 => bytes32) public registeredEmitters;\n    // Replay protection\n    mapping(bytes32 => bool) public consumedMessages;\n\n    constructor(address wormholeCore) {\n        wormhole = ICoreBridge(wormholeCore);\n    }\n\n    function receiveMessage(bytes memory encodedVaa) public {\n        (\n            CoreBridgeVM memory vm,\n            bool valid,\n            string memory reason\n        ) = wormhole.parseAndVerifyVM(encodedVaa);\n\n        require(valid, reason);\n        require(\n            registeredEmitters[vm.emitterChainId] == vm.emitterAddress,\n            \"unknown emitter\"\n        );\n        require(!consumedMessages[vm.hash], \"message already consumed\");\n        consumedMessages[vm.hash] = true;\n\n        string memory message = abi.decode(vm.payload, (string));\n        // Your application logic here\n    }\n}\n","lang":"solidity"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"safety-checks","__idx":4},"children":["Safety Checks"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Wormhole verifies Guardian signatures, but your contract is responsible for application-level checks:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Emitter validation"]},": Only accept VAAs whose ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["emitterChainId"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["emitterAddress"]}," match contracts you trust. XRPL EVM's Wormhole chain ID is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["57"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Replay protection"]},": Store the VAA digest (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["vm.hash"]},") and reject duplicates."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Finality"]},": Choose a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["consistencyLevel"]}," appropriate for your use case when publishing."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"message-delivery","__idx":5},"children":["Message Delivery"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Publishing a message does not deliver it: someone must submit the VAA to the destination chain. Your options on XRPL EVM:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Executor (recommended)"]},": Request permissionless execution with an off-chain quote; independent relay providers deliver the VAA. The ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://wormhole.com/docs/products/messaging/get-started/"},"children":["Wormhole SDK"]}," handles quotes and execution requests. The Executor contract on XRPL EVM is listed in ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/pages/bridge/wormhole/deployed-contracts"},"children":["Deployed Contracts"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Self-relay"]},": Fetch the signed VAA from the WormholeScan API and submit it to your destination contract yourself. This is useful for testing and for flows where your backend already submits transactions."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Legacy Wormhole Relayer (Testnet only)"]},": ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["IWormholeRelayer.sendPayloadToEvm"]}," works on XRPL EVM ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Testnet"]}," (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["0x362fca37E45fe1096b42021b543f462D49a5C8df"]},") and is the model used by the official ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://wormhole.com/docs/products/messaging/tutorials/cross-chain-contracts/"},"children":["cross-chain contracts tutorial"]},". Do not ship Mainnet integrations against it."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"next-steps","__idx":6},"children":["Next Steps"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://wormhole.com/docs/products/messaging/guides/core-contracts/"},"children":["Wormhole Core Contracts guide"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://wormhole.com/docs/protocol/infrastructure/relayers/executor-framework/"},"children":["Executor framework"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/pages/developers/interacting-with-evm/advanced-guides/cross-chain-transactions/wormhole/transfer-tokens"},"children":["Transfer Tokens with Wormhole"]}]}]}]},"headings":[{"value":"Send Cross-Chain Messages with Wormhole","id":"send-cross-chain-messages-with-wormhole","depth":1},{"value":"Prerequisites","id":"prerequisites","depth":2},{"value":"Publish a Message (Source Chain)","id":"publish-a-message-source-chain","depth":2},{"value":"Verify and Consume the VAA (Destination Chain)","id":"verify-and-consume-the-vaa-destination-chain","depth":2},{"value":"Safety Checks","id":"safety-checks","depth":3},{"value":"Message Delivery","id":"message-delivery","depth":2},{"value":"Next Steps","id":"next-steps","depth":2}],"frontmatter":{"seo":{"title":"Send Cross-Chain Messages with Wormhole"}},"editPage":{"to":"https://github.com/ripple/docs.xrplevm.org/tree/main/pages/developers/interacting-with-evm/advanced-guides/cross-chain-transactions/wormhole/send-messages.md"},"lastModified":"2026-08-07T10:03:44.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/pages/developers/interacting-with-evm/advanced-guides/cross-chain-transactions/wormhole/send-messages","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}