L1X Developer SDK
Multi-chain with X-Talk
Multi-chain with X-Talk
  • Start Building with X-Talk
    • Cross Chain Data and Message Passing (XCDP)
      • v1.1 (Latest)
        • System Overview
        • Your First Cross-Chain dApp with XTalk: A Step-by-Step Guide
      • v1.0 (Legacy )
        • Build your first XCDP - EVM Contract
        • Build your first XCDP - Solana to EVM Contract and XCDP - EVM to Solana Contract
          • Pre-Requisites for Solana
        • X-Talk Swap
          • Pre-Requisites
            • Pre-Requisites for Solana
          • Build your first X-Talk Swap Contract
            • Hardhat Installation & Deploy Liquidity Provision Contract that integrates with X-Talk Swap
          • Build your first X-Talk Swap Solana to EVM Contract and X-Talk Swap EVM to Solana Contract
            • Deploy Liquidity Provision Contract that integrates with Solana Swap
Powered by GitBook
On this page
  • Prerequisites
  • Goal
  • Step 1: Define the IXTalkBeacon Interface
  • Step 2: Setting Up Your Contract to Send Messages
  • Step 3: Setting Up Your Contract to Receive Messages
  • Step 4: Deployment and Interaction Flow
  1. Start Building with X-Talk
  2. Cross Chain Data and Message Passing (XCDP)
  3. v1.1 (Latest)

Your First Cross-Chain dApp with XTalk: A Step-by-Step Guide

Welcome, developer! This guide will walk you through integrating your Solidity smart contracts with the L1X XTalk Protocol to send and receive messages across different blockchains. We'll use a simplified example, similar to SimpleMessageTest.sol, to illustrate the core concepts.

Prerequisites

Before you start, you should have a basic understanding of:

  • Solidity and smart contract development.

  • The concept of cross-chain communication.

  • An XTalk environment set up (or access to a testnet where XTalk contracts like XTalkBeacon are deployed).

Known XTalkBeacon Testnet Addresses

For your convenience, here are some known deployed XTalkBeacon contract addresses on common testnets:

  • Sepolia Testnet:

    • Chain ID: 11155111

    • XTalkBeacon Address: 0x1a99f64254D998d9F6a2912Ca5b19c1DFE326eF8

  • BSC (Binance Smart Chain) Testnet:

    • Chain ID: 97

    • XTalkBeacon Address: 0x1a99f64254D998d9F6a2912Ca5b19c1DFE326eF8

Note: Always verify contract addresses from official project documentation or trusted sources before interacting with them on mainnet or for critical operations.

Goal

Our goal is to create a smart contract that can:

  1. Send a simple text message to a contract on another EVM-compatible chain using XTalk.

  2. Receive a simple text message from another chain via XTalk.

Step 1: Define the IXTalkBeacon Interface

To interact with the XTalk system on any EVM chain, your contract needs to know how to call the XTalkBeacon contract. This is done by defining an interface in your Solidity file.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IXTalkBeacon {
    // Function to initiate a cross-chain message
    function registerMessage(
        uint32 _destinationChainId,
        bytes32 _destinationAddress,
        bytes memory _messageData,
        uint32 _messageType,
        uint256 _messageNonce
    ) external;

    // Function to get the next valid nonce for your contract
    function getSenderRegistrationNextNonce(address _sender) external view returns (uint256);
}

// Your contract will go here
// contract MyXTalkDApp is ...
  • registerMessage: This is the function you'll call on the XTalkBeacon to send your message.

  • getSenderRegistrationNextNonce: You'll use this to get a unique nonce for each message your contract sends, which is important for security.

Step 2: Setting Up Your Contract to Send Messages

Let's create a contract MyXTalkDApp and add a function to send a message. For a production or testnet dApp, the XTalkBeacon address on a specific chain is fixed. You can store this as a constant or an immutable variable in your contract for clarity and safety.

contract MyXTalkDApp {
    // Address of the XTalkBeacon on the current chain.
    // For Sepolia Testnet, for example:
    address public immutable XTALK_BEACON_ADDRESS;

    // Event to log message sending attempts (optional)
    event MessageSentToXTalk(
        uint32 indexed destinationChainId,
        bytes32 indexed destinationContractAddress,
        string messageContent,
        uint256 nonceUsed
    );

    constructor(address beaconContractAddress) {
        XTALK_BEACON_ADDRESS = beaconContractAddress;
    }

    // Function to send a cross-chain message
    function sendMessageToOtherChain(
        uint32 destinationChainId,  // The ID of the chain you want to send to
        bytes32 destinationContractAddress, // The address of your target contract on the other chain (as bytes32)
        string memory myTextMessage    // The actual text you want to send
    ) public {
        // 1. Define a message type (application-specific)
        uint32 messageType = 1; // You can define different types for different actions

        // 2. Encode your payload (the string message)
        // The XTalk system transmits data as `bytes`. So, we ABI-encode our string.
        bytes memory encodedMessagePayload = abi.encode(myTextMessage);

        // 3. Get the next valid nonce for your contract
        // This makes each message unique from your contract's perspective on the source chain.
        IXTalkBeacon beacon = IXTalkBeacon(XTALK_BEACON_ADDRESS); // Use the stored beacon address
        uint256 nonce = beacon.getSenderRegistrationNextNonce(address(this));

        // 4. Call `registerMessage` on the XTalkBeacon
        beacon.registerMessage(
            destinationChainId,
            destinationContractAddress,
            encodedMessagePayload,
            messageType,
            nonce
        );

        // Optional: Emit an event in your contract to log that a message was sent
        emit MessageSentToXTalk(destinationChainId, destinationContractAddress, myTextMessage, nonce);
    }
}

Explanation:

  1. XTALK_BEACON_ADDRESS: This is now an immutable state variable, set in the constructor. When deploying MyXTalkDApp, you would pass the known XTalkBeacon address for the chain you are deploying to (e.g., 0x1a99f64254D998d9F6a2912Ca5b19c1DFE326eF8 for Sepolia or BSC Testnet as per our known addresses list). This makes it clear which beacon your contract is configured to use.

  2. sendMessageToOtherChain Parameters: The function now only takes parameters specific to the message itself, as the beaconAddress is already configured for the contract instance.

  3. Using the Stored Address: Inside sendMessageToOtherChain, IXTalkBeacon(XTALK_BEACON_ADDRESS) is used, referencing the immutable address.

  4. Important Security Note: In a production system, you might want to add a check to ensure msg.sender is the legitimate XTalkBeacon contract on this chain. For example: require(msg.sender == XTALK_BEACON_ADDRESS, "Unauthorized XTalk caller");

Step 3: Setting Up Your Contract to Receive Messages

To receive a message from another chain via XTalk, your contract (or a specific contract you deploy on the destination chain) must implement a special callback function: _xtalkMessageReceived.

// Continuing our MyXTalkDApp contract...
contract MyXTalkDApp {
    // ... (sendMessageToOtherChain function from above) ...

    // Data structure to store received message details
    struct ReceivedMessage {
        bytes32 messageId;          // Unique ID from XTalk system
        address sourceInvokerAddress; // Address of the contract that sent the message (on source chain)
        uint32 sourceChainId;       // ID of the chain the message came from
        uint32 messageType;         // Application-specific type from sender
        string actualMessage;       // The decoded text message
        bytes rawMessageData;       // The raw payload received
    }

    mapping(bytes32 => ReceivedMessage) public receivedXTalkMessages;
    event MessageReceived(bytes32 indexed messageId, address sourceInvoker, string message);

    // This is the REQUIRED callback function for XTalk
    function _xtalkMessageReceived(
        bytes32 _messageId,             // The unique XTalk message ID
        address _sourceInvokerAddress,  // Who sent it on the source chain
        uint32 _sourceChainId,          // Which chain it came from
        uint32 _messageType,            // The type defined by the sender
        bytes memory _messageData       // The raw payload (our ABI-encoded string)
    ) public {
        // Important Security Note: In a production system, you might want to add a check
        // to ensure `msg.sender` is the legitimate XTalkBeacon contract on this chain.
        // For example: require(msg.sender == XTALK_BEACON_ADDRESS, "Unauthorized XTalk caller");

        // 1. Decode the message payload if you know its type
        // Assuming the sender used `abi.encode(string)` like our `sendMessageToOtherChain` function
        string memory decodedTextMessage = abi.decode(_messageData, (string));

        // 2. Store the received message details
        receivedXTalkMessages[_messageId] = ReceivedMessage({
            messageId: _messageId,
            sourceInvokerAddress: _sourceInvokerAddress,
            sourceChainId: _sourceChainId,
            messageType: _messageType,
            actualMessage: decodedTextMessage,
            rawMessageData: _messageData
        });

        // 3. Emit an event to signal the message was received and processed
        emit MessageReceived(_messageId, _sourceInvokerAddress, decodedTextMessage);

        // 4. (Optional) Add any custom logic here to act upon the received message
        // For example, update state, call another function, etc.
    }
}

Explanation:

  1. _xtalkMessageReceived Signature: The function name and parameters (bytes32, address, uint32, uint32, bytes) must exactly match this signature. The XTalkBeacon on your contract's chain is programmed to call this specific function when it has a message for your contract.

  2. Security (Caller Verification - Optional but Recommended): Although not shown in the most basic SimpleMessageTest.sol, in a real-world scenario, you should verify that msg.sender of the _xtalkMessageReceived call is indeed the trusted XTalkBeacon contract address on the current chain. This prevents unauthorized contracts from spoofing XTalk messages directly to your callback.

  3. Decoding Payload: The _messageData arrives as bytes. Since our sendMessageToOtherChain function sent an ABI-encoded string, we use abi.decode(_messageData, (string)) to get the original text back.

  4. Storing Data: The example stores the message details in a mapping. You can adapt this to your dApp's needs.

  5. Custom Logic: After decoding and storing, you can add any logic your dApp needs to perform based on the received message content or type.

Step 4: Deployment and Interaction Flow

  1. Deploy MyXTalkDApp (or similar contracts) on both the source and destination chains.

    • The instance on the source chain will use sendMessageToOtherChain.

    • The instance on the destination chain will receive the message via _xtalkMessageReceived.

  2. Identify XTalkBeacon Addresses: You'll need the deployed addresses of the XTalkBeacon contracts on both chains. (See the "Known XTalkBeacon Testnet Addresses" section above for examples).

  3. Sending a Message:

    • Call sendMessageToOtherChain on the source chain with the appropriate parameters.

    • The message will be sent to the destination chain via XTalk.

    • The destination chain's _xtalkMessageReceived function will handle the message reception.

By following these steps, you'll be able to send and receive messages across different blockchains using the XTalk Protocol.

PreviousSystem OverviewNextv1.0 (Legacy )

Last updated 1 day ago