Bridge Message
Enable cross-chain smart contract communication and execution using Lxly.js
Overview
Message bridging enables smart contracts on different chains to communicate and execute functions across networks. Unlike asset bridging which transfers tokens, message bridging transfers executable data and triggers contract interactions on the destination network.
Key Features:
- Cross-Chain Communication: Smart contracts can call functions on other chains
- Data Transfer: Send structured data and parameters across networks
- Contract Execution: Trigger state changes on destination contracts
- IBridgeMessageReceiver: Target contracts must implement the message receiver interface
Basic Message Bridging
Send Cross-Chain Message
const { LxLyClient, use } = require('@maticnetwork/lxlyjs');
const { Web3ClientPlugin } = require('@maticnetwork/maticjs-web3');
// Initialize client (see Client Initialization guide)
const client = await initializeClient();
async function sendCrossChainMessage() {
try {
// Encode message data
const message = "Hello from Ethereum!";
const encodedData = client.client.providers[0].provider
.encodeParameters([message], ['string']);
// Send bridge message
const bridge = client.bridges[0]; // Source network bridge
const result = await bridge.bridgeMessage(
1, // Destination network
'0xReceiverContractAddress', // Target contract
true, // Force update global exit root
encodedData // Message data
);
const txHash = await result.getTransactionHash();
console.log('Message bridge transaction:', txHash);
return txHash;
} catch (error) {
console.error('Cross-chain message failed:', error);
throw error;
}
}Message with ETH Value
async function sendMessageWithETH() {
try {
const message = "Payment notification";
const encodedData = client.client.providers[0].provider
.encodeParameters([message, userAddress], ['string', 'address']);
const bridge = client.bridges[0];
const result = await bridge.bridgeMessage(
1, // Destination network
'0xPaymentContractAddress',
true,
encodedData,
{
value: '100000000000000000', // 0.1 ETH
gasLimit: 350000
}
);
const txHash = await result.getTransactionHash();
console.log('Message with ETH transaction:', txHash);
return txHash;
} catch (error) {
console.error('Message with ETH failed:', error);
throw error;
}
}Message Receiver Contract
Target contracts must implement the IBridgeMessageReceiver interface:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
interface IBridgeMessageReceiver {
function onMessageReceived(
address originAddress,
uint32 originNetwork,
bytes memory data
) external payable;
}
contract CrossChainReceiver is IBridgeMessageReceiver {
string public lastMessage;
address public lastSender;
uint32 public lastOriginNetwork;
uint256 public messageCount;
event MessageReceived(
address indexed originAddress,
uint32 originNetwork,
string message
);
function onMessageReceived(
address originAddress,
uint32 originNetwork,
bytes memory data
) external payable override {
// Decode the message
string memory message = abi.decode(data, (string));
// Store message details
lastMessage = message;
lastSender = originAddress;
lastOriginNetwork = originNetwork;
messageCount++;
emit MessageReceived(originAddress, originNetwork, message);
}
function getMessageDetails() external view returns (
string memory message,
address sender,
uint32 originNetwork,
uint256 count
) {
return (lastMessage, lastSender, lastOriginNetwork, messageCount);
}
}Claiming Messages
Claim Message
async function claimMessage(bridgeTransactionHash) {
try {
// Build claim payload
const payload = await client.bridgeUtil.buildPayloadForClaim(
bridgeTransactionHash,
0, // Source network
0 // Bridge index
);
// Claim message (this will execute onMessageReceived)
const bridge = client.bridges[1]; // Destination network bridge
const claimResult = await bridge.claimMessage(
payload.smtProof,
payload.smtProofRollup,
payload.globalIndex,
payload.mainnetExitRoot,
payload.rollupExitRoot,
payload.originNetwork,
payload.originTokenAddress,
payload.destinationNetwork,
payload.destinationAddress,
payload.amount,
payload.metadata,
{ gasLimit: 400000 }
);
const claimTxHash = await claimResult.getTransactionHash();
console.log('Message claim transaction:', claimTxHash);
return claimTxHash;
} catch (error) {
console.error('Message claim failed:', error);
throw error;
}
}Verify Message Execution
async function verifyMessageExecution(receiverContractAddress) {
try {
const receiverABI = [
{
"inputs": [],
"name": "lastMessage",
"outputs": [{"internalType": "string", "name": "", "type": "string"}],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getMessageDetails",
"outputs": [
{"internalType": "string", "name": "message", "type": "string"},
{"internalType": "address", "name": "sender", "type": "address"},
{"internalType": "uint32", "name": "originNetwork", "type": "uint32"},
{"internalType": "uint256", "name": "count", "type": "uint256"}
],
"stateMutability": "view",
"type": "function"
}
];
const receiverContract = client.contract(receiverABI, receiverContractAddress, 1);
// Check if message was received
const lastMessage = await receiverContract.read('lastMessage');
const messageDetails = await receiverContract.read('getMessageDetails');
console.log('Last received message:', lastMessage);
console.log('Message details:', messageDetails);
return { lastMessage, messageDetails };
} catch (error) {
console.error('Message verification failed:', error);
throw error;
}
}Edit on GitHub
Last updated on