🌌
Wormhole Docs Brasil
  • Bem-vindo!
  • Materiais
    • Build
      • Começer a Buildar
        • Redes Suportadas
        • Testnet Faucets
        • Demos
      • Construindo Aplicações Frontend
        • Wormhole SDK
          • Wormhole TypeScript SDK
          • Data Layouts
          • Construção de Protocolos e Payloads
        • Queries
          • Overview
          • Usando Queries
          • F.A.Q.
        • Conexão
          • Overview
          • Routes
          • Features
          • Configuração
          • v1 Migration
          • F.A.Q.
      • Construindo Integrações de contratos
        • Wormhole Relayer
        • Core Contracts
        • CCTP
        • Transferências de Tokens Nativos
          • Processos de Deployment
        • Comandos NTT CLI
        • Configuração de NTT
          • Limitação de Taxas
          • Controle de Acesso
        • Managers e Transceivers
        • F.A.Q. Wormhole NTT
      • MultiGov
        • Deployment
        • Upgrades Contracts
        • F.A.Q. Tecnicas
      • Ambiente de Desenvolvimento
      • F.A.Q sobre Integrações de Contratos
      • Toolkit
        • Wormholescan
        • Wormhole CLI
        • Wormhole SDK
          • TypeScript SDK
          • Data Layouts
          • Construindo Protocolos e Payloads
        • Solidity SDK
        • Tilt
        • Toolkit F.A.Q.
      • Referências
    • Infraestrutura
      • Run a Relayer
      • Run a Spy
    • Tutoriais
      • Tutorial de Conexão
      • Tutorial de Contratos Cross-Chain
        • Criação de Contratos de Mensagens Cross-Chain
        • Criação de contratos de transferência de tokens Cross-Chain
      • Tutoriais de Transferências de Tokens Nativos (NTT - Native Token Transfers)
        • Crie Tokens Multichain
      • Tutorial MultiGov
        • Proposta Cross-Chain treasury management
      • Tutoriais do Wormhole SDK
        • Transferir USDC via CCTP
        • Transferir Tokens via a Token Bridge
    • Learn
      • Fundamentos
        • Introdução
        • Segurança
        • Overview de Arquitetura
        • Glossário
      • Componentes de Infraestrutura
        • Core Contracts
        • VAAs (Verified Action Approvals)
        • Guardians
        • Spy
        • Relayers
      • Messaging
        • Token Bridge
        • Circle's CCTP Bridge
        • Transferencias de Token Nativos
          • Overview
          • Arquitetura
          • Modelos de Deploy
          • Security
      • Multigov
        • MultiGov: Governança Cross-Chain com Wormhole
        • MultiGov Architecture
        • FAQs
    • Links úteis
Powered by GitBook
On this page
  • Deployando o Sender Contract
  • Deployando o Receiver Contract
  • Conclusão
  1. Materiais
  2. Tutoriais
  3. Tutorial de Contratos Cross-Chain

Criação de Contratos de Mensagens Cross-Chain

PreviousTutorial de Contratos Cross-ChainNextCriação de contratos de transferência de tokens Cross-Chain

Last updated 5 months ago

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:

  • 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 Avalanche

    • MessageReceiver.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 Avalanche

    • deployReceiver.js - implanta o contrato MessageReceiver em Celo

    • sendMessage.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

    1. 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
    2. 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:

  1. 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')
  );
  1. 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);
  1. 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();
  1. Register the MessageSender on the target chain - after you deploy the MessageReceiver contract on the Celo Alfajores network, the sender contract address from Avalanche Fuji needs to be registered. This ensures that only messages from the registered MessageSender contract are processed. This additional step is essential to enforce emitter validation, preventing unauthorized senders from delivering messages to the MessageReceiver 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

  1. 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

Deployando o Receiver Contract

  1. 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

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.

Vamos analisar o script passo a passo.

  1. 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')
    )
  );
  1. 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'
    )
  );
  1. 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!';
  1. 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.

  1. Enviar uma mensagem - com tudo configurado, a mensagem é enviada utilizando a função sendMessage:

  const tx = await MessageSender.sendMessage(
    targetChain,
    targetAddress,
    message,
    {
      value: txCost,
    }
  );
  1. Após o envio, o script aguarda a confirmação da transação:

await tx.wait();
  1. Execute o script - para enviar a mensagem, execute o seguinte comando:

npm run send:message

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

instalados em sua máquina

para fazer o deploy de contratos

Tokens de Testnet para e para cobrir as taxas de gas

Após a implantação, o endereço do contrato será exibido. Você pode verificar o contrato no

Após a implantação, anote o endereço do contrato. Você pode verificar o contrato no

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 para interagir com os contratos implantados, calcular o custo do cross-chain dinamicamente e gerenciar a transação.

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 .

Node.js e npm
Foundry
Avalanche-Fuji
Celo-Alfajores
Avalanche Fuji Explorer.
Celo Alfajores Explorer.
Ethers.js
Wormhole Explorer