Criação de Contratos de Mensagens Cross-Chain
As mensagens cross-chain do Wormhole permite que contratos inteligentes interajam de forma fluida entre múltiplas blockchains. Isso possibilita que os desenvolvedores criem aplicações descentralizadas que aproveitam as forças de diferentes redes, seja Avalanche, Celo, Ethereum ou outras. Neste tutorial, exploraremos o uso do SDK Solidity do Wormhole para criar contratos cross-chain, com a capacidade de enviar e receber mensagens entre diferentes blockchains.
A infraestrutura de mensageria do Wormhole simplifica a transmissão de dados, a ativação de eventos e a iniciação de transações entre blockchains. Neste tutorial, conduziremos uma demonstração prática, simples, mas poderosa, que mostra essa capacidade. Faremos o deploy de contratos em duas Testnets — Avalanche Fuji e Celo Alfajores — e enviaremos mensagens de uma blockchain para outra. Este tutorial é ideal para quem está começando no desenvolvimento cross-chain e deseja adquirir experiência prática com as poderosas ferramentas do Wormhole.
Ao final deste tutorial, você não apenas terá criado um sistema funcional de envio e recebimento de mensagens cross-chain utilizando Solidity, mas também terá desenvolvido um entendimento completo de como interagir com o relayer do Wormhole, gerenciar os custos de transações cross-chain e garantir que seus contratos inteligentes estejam corretamente configurados tanto nas blockchains de origem quanto nas de destino.
Este tutorial pressupõe uma compreensão básica de Solidity e desenvolvimento de contratos inteligentes. Antes de começar, pode ser útil revisar os conceitos básicos do Wormhole para se familiarizar com o protocolo.
Visão Geral do Wormhole
Interagiremos com dois componentes-chave do Wormhole: o relayer do Wormhole e os contratos principais do Wormhole. O relayer é responsável pela entrega de mensagens cross-chain e garante que a mensagem seja recebida corretamente na blockchain de destino. Isso permite que contratos inteligentes se comuniquem entre blockchains sem que os desenvolvedores precisem se preocupar com a complexidade subjacente.
Além disso, contaremos com o relayer do Wormhole para determinar automaticamente os custos das transações cross-chain e facilitar os pagamentos. Essa funcionalidade simplifica o desenvolvimento cross-chain ao permitir que você especifique apenas a blockchain de destino e a mensagem. O relayer cuida do restante, garantindo que a mensagem seja transmitida com a taxa apropriada.

Pré-requisitos
Antes de iniciar este tutorial, certifique-se de que você tem o seguinte:
Node.js e npm instalados em sua máquina
Foundry para fazer o deploy de contratos
Tokens de Testnet para Avalanche-Fuji e Celo-Alfajores para cobrir as taxas de gas
Chave privada da carteira
Construção de Contratos de Cross-Chain Messaging
Nesta seção, faremos o deploy de dois contratos inteligentes: um para enviar uma mensagem de Avalanche Fuji e outro para recebê-la em Celo Alfajores. Os contratos interagem com o relayer do Wormhole para transmitir mensagens entre as blockchains.
De forma geral, nossos contratos irão:
Enviar uma mensagem de Avalanche para Celo utilizando o relayer do Wormhole
Receber e processar a mensagem em Celo, registrando o conteúdo da mensagem
Antes de mergulharmos nos passos de deploy, vamos primeiro analisar as partes principais dos contratos.
Contrato de Envio: MessageSender
O contrato MessageSender
é responsável por calcular o custo de envio de uma mensagem entre blockchains e, em seguida, enviar essa mensagem.
As funções principais incluem:
quoteCrossChainCost
: calcula o custo de entrega de uma mensagem para a blockchain de destino utilizando o relayer do Wormhole.sendMessage
: codifica a mensagem e a envia para a blockchain de destino e o endereço do contrato utilizando o relayer do Wormhole.
Aqui está o core conctract:
function sendMessage(
uint16 targetChain,
address targetAddress,
string memory message
) external payable {
uint256 cost = quoteCrossChainCost(targetChain);
require(
msg.value >= cost,
"Insufficient funds for cross-chain delivery"
);
wormholeRelayer.sendPayloadToEvm{value: cost}(
targetChain,
targetAddress,
abi.encode(message, msg.sender),
0,
GAS_LIMIT
);
}
Aqui está o código completo para o contrato MessageSender.sol
:
// SPDX-License-Identifier: MIT pragma solidity ^0.8.18;
import "lib/wormhole-solidity-sdk/src/interfaces/IWormholeRelayer.sol";
contract MessageSender { IWormholeRelayer public wormholeRelayer; uint256 constant GAS_LIMIT = 50000;
constructor(address _wormholeRelayer) {
wormholeRelayer = IWormholeRelayer(_wormholeRelayer);
}
function quoteCrossChainCost(
uint16 targetChain
) public view returns (uint256 cost) {
(cost, ) = wormholeRelayer.quoteEVMDeliveryPrice(
targetChain,
0,
GAS_LIMIT
);
}
function sendMessage(
uint16 targetChain,
address targetAddress,
string memory message
) external payable {
uint256 cost = quoteCrossChainCost(targetChain);
require(
msg.value >= cost,
"Insufficient funds for cross-chain delivery"
);
wormholeRelayer.sendPayloadToEvm{value: cost}(
targetChain,
targetAddress,
abi.encode(message, msg.sender),
0,
GAS_LIMIT
);
}
}
Contrato Receptor: MessageReceiver
O contrato MessageReceiver
gerencia as mensagens recebidas entre cadeias. Quando uma mensagem chega, ele decodifica a carga útil e registra o conteúdo da mensagem. O contrato garante que apenas contratos autorizados possam enviar e processar mensagens, adicionando uma camada extra de segurança na comunicação entre cadeias.
Validação e Registro do Emissor
Na comunicação entre cadeias, a validação do emissor é essencial para prevenir que contratos não autorizados enviem mensagens. O modificadorisRegisteredSender
assegura que as mensagens só possam ser processadas se forem provenientes de um contrato registrado na cadeia de origem. Isso protege contra mensagens maliciosas e aumenta a segurança.
Detalhes principais da implementação incluem:
registeredSender
- armazena o endereço do contrato emissor registrado.setRegisteredSender
- registra o endereço do contrato emissor na cadeia de origem. Isso garante que apenas contratos registrados possam enviar mensagens, prevenindo emissores não autorizados.isRegisteredSender
- restringe o processamento de mensagens apenas àquelas provenientes de emissores registrados, prevenindo comunicação entre cadeias não autorizada.
mapping(uint16 => bytes32) public registeredSenders;
modifier isRegisteredSender(uint16 sourceChain, bytes32 sourceAddress) {
require(
registeredSenders[sourceChain] == sourceAddress,
"Not registered sender"
);
_;
}
function setRegisteredSender(
uint16 sourceChain,
bytes32 sourceAddress
) public {
require(
msg.sender == registrationOwner,
"Not allowed to set registered sender"
);
registeredSenders[sourceChain] = sourceAddress;
}
Processamento de Mensagens
A função receiveWormholeMessages
é a função central que processa a mensagem recebida. Ela verifica se o relayer Wormhole enviou a mensagem, decodifica a carga útil e emite um evento com o conteúdo da mensagem. É fundamental validar o emissor da mensagem para evitar mensagens não autorizadas.
function receiveWormholeMessages(
bytes memory payload,
bytes[] memory,
bytes32 sourceAddress,
uint16 sourceChain,
bytes32
) public payable override isRegisteredSender(sourceChain, sourceAddress) {
require(
msg.sender == address(wormholeRelayer),
"Only the Wormhole relayer can call this function"
);
// Decode the payload to extract the message
string memory message = abi.decode(payload, (string));
// Example use of sourceChain for logging
if (sourceChain != 0) {
emit SourceChainLogged(sourceChain);
}
// Emit an event with the received message
emit MessageReceived(message);
}
Abaixo temos MessageReceiver.sol:
// SPDX-License-Identifier: MIT pragma solidity ^0.8.18;
import "lib/wormhole-solidity-sdk/src/interfaces/IWormholeRelayer.sol"; import "lib/wormhole-solidity-sdk/src/interfaces/IWormholeReceiver.sol";
contract MessageReceiver is IWormholeReceiver { IWormholeRelayer public wormholeRelayer; address public registrationOwner;
// Mapping to store registered senders for each chain
mapping(uint16 => bytes32) public registeredSenders;
event MessageReceived(string message);
event SourceChainLogged(uint16 sourceChain);
constructor(address _wormholeRelayer) {
wormholeRelayer = IWormholeRelayer(_wormholeRelayer);
registrationOwner = msg.sender; // Set contract deployer as the owner
}
modifier isRegisteredSender(uint16 sourceChain, bytes32 sourceAddress) {
require(
registeredSenders[sourceChain] == sourceAddress,
"Not registered sender"
);
_;
}
function setRegisteredSender(
uint16 sourceChain,
bytes32 sourceAddress
) public {
require(
msg.sender == registrationOwner,
"Not allowed to set registered sender"
);
registeredSenders[sourceChain] = sourceAddress;
}
// Update receiveWormholeMessages to include the source address check
function receiveWormholeMessages(
bytes memory payload,
bytes[] memory,
bytes32 sourceAddress,
uint16 sourceChain,
bytes32
) public payable override isRegisteredSender(sourceChain, sourceAddress) {
require(
msg.sender == address(wormholeRelayer),
"Only the Wormhole relayer can call this function"
);
// Decode the payload to extract the message
string memory message = abi.decode(payload, (string));
// Example use of sourceChain for logging
if (sourceChain != 0) {
emit SourceChainLogged(sourceChain);
}
// Emit an event with the received message
emit MessageReceived(message);
}
}
Implantação dos Contratos
Esta seção orienta você sobre como implantar os contratos de mensagens cross-chain nas Testnets Avalanche Fuji e Celo Alfajores. Siga os passos abaixo para colocar seus contratos em funcionamento.
Ferramentas de Implantação
Usamos o Foundry para implantar nossos contratos inteligentes. No entanto, você pode usar qualquer ferramenta com a qual se sinta confortável, como:
Remix para um IDE baseado em navegador
Hardhat para um fluxo de trabalho mais extenso com JavaScript/TypeScript
Foundry para uma experiência focada em CLI, com recursos embutidos de script e testes
Os contratos e os passos de implantação permanecem os mesmos, independentemente da ferramenta preferida. O mais importante é garantir que você tenha os fundos necessários nas Testnets e que está implantando nas redes corretas.
Para começar a trabalhar com mensagens cross-chain utilizando Wormhole, clone primeiro o repositório no GitHub. Este repositório contém tudo o que você precisa para implantar, interagir e testar o fluxo de mensagens entre as blockchains.
Esta demonstração foca no uso dos scripts, por isso, é recomendável analisar os seguintes arquivos: deploySender.js
, deployReceiver.js
e sendMessage.js
.
Para configurar as dependências corretamente, execute o seguinte comando:
npm install
O repositório inclui:
Dois contratos Solidity:
MessageSender.sol
- contrato que envia a mensagem cross-chain de AvalancheMessageReceiver.sol
- contrato que recebe a mensagem cross-chain em Celo
Scripts de Implantação localizados no diretório
scripts
:deploySender.js
- implanta o contrato MessageSender em AvalanchedeployReceiver.js
- implanta o contrato MessageReceiver em CelosendMessage.js
- envia uma mensagem de Avalanche para Celo
Arquivos de configuração e ABI JSON para fácil implantação e interação:
chains.json
- arquivo de configuração que armazena informações essenciais para as Testnets suportadas, incluindo os endereços do relayer Wormhole, URLs RPC e IDs de cadeia. Normalmente, você não precisará modificar este arquivo, a menos que esteja trabalhando com redes diferentes.
Passos Importantes para Configuração
Adicione sua chave privada Crie um arquivo
.env
no diretório raiz do projeto e adicione sua chave privada da seguinte forma:bashCopiar códigotouch .env
Dentro do arquivo
.env
, adicione sua chave privada no seguinte formato:envCopiar códigoPRIVATE_KEY=INSIRA_SUA_CHAVE_PRIVADA
Compile os contratos Garanta que tudo esteja configurado corretamente compilando os contratos:
bashCopiar códigoforge build
A saída esperada deve ser semelhante a esta:

Processo de Implantação
Os dois scripts de implantação, deploySender.js
e deployReceiver.js
, realizam as seguintes tarefas principais:
Carregar configuração e detalhes do contrato Cada script começa carregando as informações de configuração necessárias, como a URL RPC da rede e o ABI (Application Binary Interface) e o bytecode do contrato. Essas informações são essenciais para garantir que o contrato seja implantado na rede de blockchain correta.
Chain.json
{
"chains": [
{
"description": "Avalanche testnet fuji",
"chainId": 6,
"rpc": "https://api.avax-test.network/ext/bc/C/rpc",
"tokenBridge": "0x61E44E506Ca5659E6c0bba9b678586fA2d729756",
"wormholeRelayer": "0xA3cF45939bD6260bcFe3D66bc73d60f19e49a8BB",
"wormhole": "0x7bbcE28e64B3F8b84d876Ab298393c38ad7aac4C"
},
{
"description": "Celo Testnet",
"chainId": 14,
"rpc": "https://alfajores-forno.celo-testnet.org",
"tokenBridge": "0x05ca6037eC51F8b712eD2E6Fa72219FEaE74E153",
"wormholeRelayer": "0x306B68267Deb7c5DfCDa3619E22E9Ca39C374f84",
"wormhole": "0x88505117CA88e7dd2eC6EA1E13f0948db2D50D56"
}
]
}
deploysender.sol
// Load the chain configuration from JSON
const chains = JSON.parse(
fs.readFileSync(path.resolve(__dirname, '../deploy-config/chains.json'))
);
// Get the Avalanche Fuji configuration
const avalancheChain = chains.chains.find((chain) =>
chain.description.includes('Avalanche testnet')
);
deployreceiver.sol
// Load the chain configuration from the JSON file
const chains = JSON.parse(
fs.readFileSync(path.resolve(__dirname, '../deploy-config/chains.json'))
);
// Get the Celo Testnet configuration
const celoChain = chains.chains.find((chain) =>
chain.description.includes('Celo Testnet')
);
Os scripts estabelecem uma conexão com a blockchain utilizando um provedor e criam uma instância de carteira usando uma chave privada. Esta carteira é responsável por assinar a transação de implantação.
// deploysender.sol
const provider = new ethers.JsonRpcProvider(avalancheChain.rpc);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
// deployreceiver.sol
const provider = new ethers.JsonRpcProvider(celoChain.rpc);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
O contrato é implantado na rede especificada na configuração. Após a implantação bem-sucedida, o endereço do contrato é retornado, o que é crucial para interagir com o contrato posteriormente.
// deploysender.sol
const senderContract = await MessageSender.deploy(
avalancheChain.wormholeRelayer
);
await senderContract.waitForDeployment();
// deployreceiver.sol
const receiverContract = await MessageReceiver.deploy(
celoChain.wormholeRelayer
);
await receiverContract.waitForDeployment();
Register the
MessageSender
on the target chain - after you deploy theMessageReceiver
contract on the Celo Alfajores network, the sender contract address from Avalanche Fuji needs to be registered. This ensures that only messages from the registeredMessageSender
contract are processed. This additional step is essential to enforce emitter validation, preventing unauthorized senders from delivering messages to theMessageReceiver
contract.
// Retrieve the address of the MessageSender from the deployedContracts.json file
const avalancheSenderAddress = deployedContracts.avalanche.MessageSender;
// Define the source chain ID for Avalanche Fuji
const sourceChainId = 6;
// Call setRegisteredSender on the MessageReceiver contract
const tx = await receiverContract.setRegisteredSender(
sourceChainId,
ethers.zeroPadValue(avalancheSenderAddress, 32)
);
await tx.wait();
Você pode encontrar o código completo para o deploySender.js
e deployReceiver.js
abaixo:
// deploySender.js
const { ethers } = require('ethers');
const fs = require('fs');
const path = require('path');
require('dotenv').config();
async function main() {
// Load the chain configuration from JSON
const chains = JSON.parse(
fs.readFileSync(path.resolve(__dirname, '../deploy-config/chains.json'))
);
// Get the Avalanche Fuji configuration
const avalancheChain = chains.chains.find((chain) =>
chain.description.includes('Avalanche testnet')
);
// Set up the provider and wallet
const provider = new ethers.JsonRpcProvider(avalancheChain.rpc);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
// Load the ABI and bytecode of the MessageSender contract
const messageSenderJson = JSON.parse(
fs.readFileSync(
path.resolve(__dirname, '../out/MessageSender.sol/MessageSender.json'),
'utf8'
)
);
const abi = messageSenderJson.abi;
const bytecode = messageSenderJson.bytecode;
// Create a ContractFactory for MessageSender
const MessageSender = new ethers.ContractFactory(abi, bytecode, wallet);
// Deploy the contract using the Wormhole Relayer address for Avalanche Fuji
const senderContract = await MessageSender.deploy(
avalancheChain.wormholeRelayer
);
await senderContract.waitForDeployment();
console.log('MessageSender deployed to:', senderContract.target);
// Update the deployedContracts.json file
const deployedContractsPath = path.resolve(
__dirname,
'../deploy-config/deployedContracts.json'
);
const deployedContracts = JSON.parse(
fs.readFileSync(deployedContractsPath, 'utf8')
);
deployedContracts.avalanche = {
MessageSender: senderContract.target,
deployedAt: new Date().toISOString(),
};
fs.writeFileSync(
deployedContractsPath,
JSON.stringify(deployedContracts, null, 2)
);
}
main().catch((error) => {
console.error(error);
process.exit(1);
});
// deployReceiver.js
const { ethers } = require('ethers');
const fs = require('fs');
const path = require('path');
require('dotenv').config();
async function main() {
// Load the chain configuration from the JSON file
const chains = JSON.parse(
fs.readFileSync(path.resolve(__dirname, '../deploy-config/chains.json'))
);
// Get the Celo Testnet configuration
const celoChain = chains.chains.find((chain) =>
chain.description.includes('Celo Testnet')
);
// Set up the provider and wallet
const provider = new ethers.JsonRpcProvider(celoChain.rpc);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
// Load the ABI and bytecode of the MessageReceiver contract
const messageReceiverJson = JSON.parse(
fs.readFileSync(
path.resolve(
__dirname,
'../out/MessageReceiver.sol/MessageReceiver.json'
),
'utf8'
)
);
const abi = messageReceiverJson.abi;
const bytecode = messageReceiverJson.bytecode;
// Create a ContractFactory for MessageReceiver
const MessageReceiver = new ethers.ContractFactory(abi, bytecode, wallet);
// Deploy the contract using the Wormhole Relayer address for Celo Testnet
const receiverContract = await MessageReceiver.deploy(
celoChain.wormholeRelayer
);
await receiverContract.waitForDeployment();
console.log('MessageReceiver deployed to:', receiverContract.target); // `target` is the contract address in ethers.js v6
// Update the deployedContracts.json file
const deployedContractsPath = path.resolve(
__dirname,
'../deploy-config/deployedContracts.json'
);
const deployedContracts = JSON.parse(
fs.readFileSync(deployedContractsPath, 'utf8')
);
// Retrieve the address of the MessageSender from the deployedContracts.json file
const avalancheSenderAddress = deployedContracts.avalanche.MessageSender;
// Define the source chain ID for Avalanche Fuji
const sourceChainId = 6;
// Call setRegisteredSender on the MessageReceiver contract
const tx = await receiverContract.setRegisteredSender(
sourceChainId,
ethers.zeroPadValue(avalancheSenderAddress, 32)
);
await tx.wait();
console.log(
`Registered MessageSender (${avalancheSenderAddress}) for Avalanche chain (${sourceChainId})`
);
deployedContracts.celo = {
MessageReceiver: receiverContract.target,
deployedAt: new Date().toISOString(),
};
fs.writeFileSync(
deployedContractsPath,
JSON.stringify(deployedContracts, null, 2)
);
}
main().catch((error) => {
console.error(error);
process.exit(1);
});
Deployando o Sender Contract
O Sender será responsável por cotar e enviar mensagens entre as blockchains.
Execute o seguinte comando para implantar o contrato enviador:
npm run deploy:sender
Após a implantação, o endereço do contrato será exibido. Você pode verificar o contrato no Avalanche Fuji Explorer.
Deployando o Receiver Contract
O receiver fica responsável por escutar as mensagens entre cadeias e registrá-las quando recebidas.
Para implantar o contrato receptor, execute o seguinte comando:
npm run deploy:receiver
Após a implantação, anote o endereço do contrato. Você pode verificar o contrato no Celo Alfajores Explorer.
Agora que ambos os contratos, o de envio e o de recepção, estão implantados, vamos para o próximo passo empolgante: enviar uma mensagem cross-chain de Avalanche Fuji para Celo Alfajores.
Neste exemplo, usaremos o script sendMessage.js
para transmitir uma mensagem do contrato de envio em Avalanche para o contrato de recepção em Celo. O script utiliza o Ethers.js para interagir com os contratos implantados, calcular o custo do cross-chain dinamicamente e gerenciar a transação.
Vamos analisar o script passo a passo.
Carregar arquivos de configuração:
chains.json
- contém detalhes sobre as chains de Testnet suportadas, como URLs de RPC e endereços de relayers.deployedContracts.json
- armazena os endereços dos contratos de envio e recepção implantados. Este arquivo é atualizado dinamicamente quando os contratos são implantados, mas os usuários também podem adicionar manualmente os endereços de seus contratos implantados, se necessário.
const chains = JSON.parse(
fs.readFileSync(path.resolve(__dirname, '../deploy-config/chains.json'))
);
const deployedContracts = JSON.parse(
fs.readFileSync(
path.resolve(__dirname, '../deploy-config/deployedContracts.json')
)
);
Configurar o provedor e o assinante - o script começa lendo as configurações da chain e extraindo os endereços dos contratos. Um passo essencial para interagir com uma blockchain é configurar o provedor. O provedor é sua conexão com a rede blockchain. Ele permite que o seu script interaja com a blockchain, recupere dados e envie transações. Neste caso, estamos usando um provedor JSON-RPC. Em seguida, configuramos a carteira, que será usada para assinar as transações. A carteira é criada utilizando a chave privada e o provedor. Isso garante que todas as transações enviadas dessa carteira sejam transmitidas para a rede Avalanche Fuji.
const provider = new ethers.JsonRpcProvider(avalancheChain.rpc);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provideconst messageSenderJson = JSON.parse(
Após configurar a carteira, o script carrega o ABI do contrato MessageSender.sol e cria uma instância dele:
fs.readFileSync(
path.resolve(__dirname, '../out/MessageSender.sol/MessageSender.json'),
'utf8'
)
);
Configurar os detalhes da mensagem - a próxima parte do script define a cadeia de destino (Celo) e o endereço de destino (o contrato receptor na Celo):
const targetChain = 14; // Wormhole chain ID for Celo Alfajores
const targetAddress = deployedContracts.celo.MessageReceiver;
Você pode personalizar a mensagem que será enviada entre as blockchains:
const message = 'Hello from Avalanche to Celo!';
Estimar o custo cross-chain - antes de enviar a mensagem, calculamos dinamicamente o custo da transação entre as blockchains usando a função
quoteCrossChainCost
:
const txCost = await MessageSender.quoteCrossChainCost(targetChain);
Isso garante que a transação inclua fundos suficientes para cobrir as taxas de gás da mensagem cross-chain.
Enviar uma mensagem - com tudo configurado, a mensagem é enviada utilizando a função
sendMessage
:
const tx = await MessageSender.sendMessage(
targetChain,
targetAddress,
message,
{
value: txCost,
}
);
Após o envio, o script aguarda a confirmação da transação:
await tx.wait();
Execute o script - para enviar a mensagem, execute o seguinte comando:
npm run send:message
Se tudo estiver configurado corretamente, a mensagem será enviada do Avalanche Fuji Testnet para o Celo Alfajores Testnet. Você pode monitorar a transação e verificar se a mensagem foi recebida no Celo usando o Wormhole Explorer.
O console deve gerar algo semelhante a isto:

Você pode encontrar o código completo para sendMessage.js
abaixo:
const { ethers } = require('ethers');
const fs = require('fs');
const path = require('path');
require('dotenv').config();
async function main() {
// Load the chain configuration and deployed contract addresses
const chains = JSON.parse(
fs.readFileSync(path.resolve(__dirname, '../deploy-config/chains.json'))
);
const deployedContracts = JSON.parse(
fs.readFileSync(
path.resolve(__dirname, '../deploy-config/deployedContracts.json')
)
);
console.log(
'Sender Contract Address: ',
deployedContracts.avalanche.MessageSender
);
console.log(
'Receiver Contract Address: ',
deployedContracts.celo.MessageReceiver
);
console.log('...');
// Get the Avalanche Fuji configuration
const avalancheChain = chains.chains.find((chain) =>
chain.description.includes('Avalanche testnet')
);
// Set up the provider and wallet
const provider = new ethers.JsonRpcProvider(avalancheChain.rpc);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
// Load the ABI of the MessageSender contract
const messageSenderJson = JSON.parse(
fs.readFileSync(
path.resolve(__dirname, '../out/MessageSender.sol/MessageSender.json'),
'utf8'
)
);
const abi = messageSenderJson.abi;
// Create a contract instance for MessageSender
const MessageSender = new ethers.Contract(
deployedContracts.avalanche.MessageSender,
abi,
wallet
);
// Define the target chain and target address (the Celo receiver contract)
const targetChain = 14; // Wormhole chain ID for Celo Alfajores
const targetAddress = deployedContracts.celo.MessageReceiver;
// The message you want to send
const message = 'Hello from Avalanche to Celo!';
// Dynamically quote the cross-chain cost
const txCost = await MessageSender.quoteCrossChainCost(targetChain);
// Send the message (make sure to send enough gas in the transaction)
const tx = await MessageSender.sendMessage(
targetChain,
targetAddress,
message,
{
value: txCost,
}
);
console.log('Transaction sent, waiting for confirmation...');
await tx.wait();
console.log('...');
console.log('Message sent! Transaction hash:', tx.hash);
console.log(
`You may see the transaction status on the Wormhole Explorer: https://wormholescan.io/#/tx/${tx.hash}?network=TESTNET`
);
}
main().catch((error) => {
console.error(error);
process.exit(1);
});
Conclusão
Agora você está totalmente equipado para construir contratos cross-chain usando o protocolo Wormhole! Com este tutorial, você aprendeu como:
Implementar contratos de remetente e destinatário em diferentes Testnets
Enviar uma mensagem cross-chain de um blockchain para outro
Monitorar o status de suas transações cross-chain usando o Wormhole Explorer e o Wormhole-Solidity-SDK
Last updated