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

<figure><img src="https://3568400230-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FI0kU6STjXwuHPJEO5L0w%2Fuploads%2FYUYUdntFGtkRR08PM6d4%2Fimage.png?alt=media&#x26;token=30048641-9718-4ffd-a654-bbd2512899f9" alt=""><figcaption></figcaption></figure>

**Pré-requisitos**

Antes de iniciar este tutorial, certifique-se de que você tem o seguinte:

* [Node.js e npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) instalados em sua máquina
* [Foundry](https://book.getfoundry.sh/getting-started/installation) para fazer o deploy de contratos
* Tokens de Testnet para [Avalanche-Fuji](https://core.app/tools/testnet-faucet/?token=C) e [Celo-Alfajores](https://faucet.celo.org/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 modificado&#x72;**`isRegisteredSender`** 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:

     ```bash
     bashCopiar códigotouch .env
     ```

     Dentro do arquivo `.env`, adicione sua chave privada no seguinte formato:

     ```env
     envCopiar códigoPRIVATE_KEY=INSIRA_SUA_CHAVE_PRIVADA
     ```
  2. **Compile os contratos**\
     Garanta que tudo esteja configurado corretamente compilando os contratos:

     ```bash
     bashCopiar códigoforge build
     ```

  A saída esperada deve ser semelhante a esta:

<figure><img src="https://3568400230-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FI0kU6STjXwuHPJEO5L0w%2Fuploads%2FKoWhEAdI0WJlTMNjumBF%2Fimage.png?alt=media&#x26;token=4df0df3c-ef1a-46b6-a862-26b33fcadc11" alt=""><figcaption></figcaption></figure>

**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')
  );
```

2. 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);
```

3. 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();
```

4. **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
```

2. Após a implantação, o endereço do contrato será exibido. Você pode verificar o contrato no [Avalanche Fuji Explorer.](https://testnet.snowtrace.io/)
3.

```
<figure><img src="https://3568400230-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FI0kU6STjXwuHPJEO5L0w%2Fuploads%2FfJGDiL3KdiNrfFELPoh3%2Fimage.png?alt=media&#x26;token=ddb30ebf-e0fe-493a-94f6-09431fd85b43" alt=""><figcaption></figcaption></figure>
```

### **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
```

2. Após a implantação, anote o endereço do contrato. Você pode verificar o contrato no [Celo Alfajores Explorer.](https://alfajores.celoscan.io/)

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](https://docs.ethers.org/v6/) 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.

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')
    )
  );
```

2. 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'
    )
  );
```

3. 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!';
```

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

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

```
  const tx = await MessageSender.sendMessage(
    targetChain,
    targetAddress,
    message,
    {
      value: txCost,
    }
  );
```

6. Após o envio, o script aguarda a confirmação da transação:

```
await tx.wait();
```

7. 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](https://wormholescan.io/#/?network=TESTNET).

O console deve gerar algo semelhante a isto:

<figure><img src="https://3568400230-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FI0kU6STjXwuHPJEO5L0w%2Fuploads%2FbG4kHhgxEJahm61YnCqh%2Fimage.png?alt=media&#x26;token=5b7ab989-af37-45f4-a88d-3b84acf22757" alt=""><figcaption></figcaption></figure>

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&#x20;
* Enviar uma mensagem cross-chain de um blockchain para outro&#x20;
* Monitorar o status de suas transações cross-chain usando o Wormhole Explorer e o Wormhole-Solidity-SDK
