WEB3DEV

Cover image for Criando uma Troca entre Cadeias com a LayerZero
Fatima Lima
Fatima Lima

Posted on

Criando uma Troca entre Cadeias com a LayerZero

Criaremos um DApp com a LayerZero que permita aos usuários fazer a ponte e trocar tokens MATIC da rede de teste Mumbai por tokens ETH na rede de teste Sepolia.

ÍNDICE

Como a LayerZero funciona?
Público-alvo
Pré-requisitos
Criação de uma Troca de Cadeia Cruzada entre as Redes de Teste Sepolia e Mumbai
Criação de um Novo Projeto Foundry
Implementação dos Contratos Inteligentes
Criação do Contrato Base
Adição das Variáveis
Implementação do Construtor
Interlúdio: Como os Contratos Inteligentes Interagem com o Protocolo LayerZero?
Implementação da Função swapTo_ETH
Implementação da Função _nonblockingLzReceive
O Contrato Completo para Mumbai
O Contrato Sepolia Completo
Criação dos Contratos
Criação do Arquivo .env
Implementação dos Scripts de Implantação
Implementação e Verificação dos Contratos
Conexão dos Dois Contratos
Problemas com esta Implementação
Conclusão

A comunicação entre cadeias (ou de cadeias cruzadas) é um dos tópicos mais quentes da Web3 atualmente. Há muitas blockchains na Web3, o que significa pools de liquidez divididos e contratos inteligentes isolados que não podem se comunicar em seus ambientes de blockchains. As equipes de engenharia de todo o mundo estão tentando fornecer um produto que permita a comunicação perfeita entre cadeias para resolver esse problema.

A LayerZero é uma dessas soluções, com um produto que já está funcionando e que possibilita a ponte entre cadeias em exchanges descentralizadas populares como Sushiswap e PancakeSwap.

Este artigo abordará a LayerZero e como ela ajuda a enviar mensagens entre cadeias.

Como Funciona a Layer Zero?

Qualquer protocolo de comunicação entre cadeias requer uma entidade off-chain para enviar mensagens de e para. No entanto, isso cria um risco de centralização.

Veja como a LayerZero organiza sua infraestrutura:

  • A LayerZero implantou um contrato inteligente como um endpoint (ponto de extremidade) em cada blockchain compatível. Esse endpoint serve como um ponto de montagem para todas as mensagens entre cadeias. Os endpoints são para a LayerZero o que os aeroportos são para as viagens aéreas.
  • Um relayer (retransmissor) off-chain escuta esses endpoints e coleta as mensagens à medida que elas chegam. A LayerZero nos permite executar nosso próprio relayer, reduzindo o risco de centralização.
  • No entanto, a LayerZero depende apenas parcialmente de um relayer para entregar essas mensagens. Na prática, um relayer trabalha com um oráculo que confirma ou nega a validade de uma transação.
  • As mensagens são entregues ao seu destino somente se ambas as entidades independentes concordarem com a validade de uma transação.
  • Um relayer geralmente é combinado com redes de oráculos descentralizadas, como a Chainlink, para garantir a confiabilidade, embora, em teoria, também possamos desenvolver nossas próprias redes.

Image description

Nota: Todas as informações técnicas contidas neste artigo sobre o funcionamento da LayerZero vieram de seu whitepaper.

Público-alvo

Este guia pressupõe um conhecimento do Foundry. Se você não se sentir à vontade com ele, consulte o livro Foundry.

Pré-requisitos

Este guia requer uma instalação atualizada do Foundry.

Criação de uma Troca de Cadeia Cruzada entre as Redes de teste Sepolia e Mumbain

Criaremos uma troca de cadeia cruzada entre a rede de teste Sepolia e a rede de teste Mumbai que permitirá aos usuários trocar ETH por MATIC e vice-versa com o clique de um botão.

Criação de um Novo projeto Foundry

Criamos um novo diretório e abrimos um terminal dentro dele para começar. Para inicializar um novo projeto Foundry, executamos o seguinte comando:

forge init
Enter fullscreen mode Exit fullscreen mode

Este repositório de exemplo da LayerZero contém todas as interfaces e contratos abstratos de que precisamos para conversar com os endpoints da LayerZero on-chain. Também precisamos da biblioteca Openzeppelin-contracts para trabalhar com o controle de acesso em nossos contratos inteligentes.

Para baixar esses repositórios em nosso projeto Foundry, executamos o seguinte:

forge install LayerZero-Labs/solidity-examples OpenZeppelin/openzeppelin-contracts
Enter fullscreen mode Exit fullscreen mode

Trabalharemos com a versão 0.8.19 do compilador do Solidity. Para garantir que o Forge use essa mesma versão para compilar todo o código do Solidity, adicionamos esta linha ao arquivo foundry.toml no diretório do nosso projeto

solc_version = '0.8.19'
# O arquivo TOML pode ser usado para configurar quase todos os aspectos de um
# projeto Foundry
Enter fullscreen mode Exit fullscreen mode

Por fim, como estaremos importando o código do Solidity de arquivos diferentes, devemos informar ao Forge sobre nossos remapeamentos. Adicione esse array ao arquivo foundry.toml:

remappings = [
    "@layerzero-contracts/=lib/solidity-examples/contracts/",
    "@openzeppelin/=lib/openzeppelin-contracts/"
]
Enter fullscreen mode Exit fullscreen mode

Implementação dos Contratos Inteligentes

Com nosso ambiente de desenvolvimento configurado, estamos finalmente prontos para começar.

Isenção de responsabilidade: este projeto será uma versão extremamente simplificada de como poderia ser uma troca de cadeia cruzada criada com base na LayerZero; ele serve apenas para fins educacionais e não deve ser considerado para nenhum projeto em produção.

Se você quiser se aprofundar mais, recomendo que dê uma olhada em Stargate Finance.

Dentro do diretório src, exclua o Counter.sol e crie dois novos arquivos.

  1. LayerZeroSwap_Mumbai.sol
  2. LayerZeroSwap_Sepolia.sol

O primeiro se tornará o ponto de extremidade na rede de teste Mumbai e o outro na Sepolia.

Criação do Contrato Base
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

import "@layerzero-contracts/lzApp/NonblockingLzApp.sol";

contract LayerZeroSwap_Mumbai is NonblockingLzApp {
  // A implementação será feita aqui
}
Enter fullscreen mode Exit fullscreen mode

NonblockingLzApp é um contrato abstrato criado com base nos contratos subjacentes da LayerZero. O objetivo desse contrato é facilitar a interação dos desenvolvedores com os contratos on-chain.

Adição da Variáveis

   // Variáveis de estado para o contrato    
    uint16 public destChainId;
    bytes payload;
    address payable deployer;
    address payable contractAddress = payable(address(this));

    // Instância do ponto de extremidade  LayerZero
    ILayerZeroEndpoint public immutable endpoint;

Enter fullscreen mode Exit fullscreen mode

A variável destChainId representa o endereço da cadeia de destino, não a cadeia na qual implementamos esse contrato. Ao enviar uma mensagem entre cadeias usando a LayerZero, precisamos especificar o endereço do destino pretendido.

Nota: esses não são os IDs de cadeia que você conhece. Para citar os documentos da LayerZero: "os valores de chainId não estão relacionados a IDs de EVM. Como a LayerZero abrangerá cadeias EVM e não EVM, os chainId são proprietários de nossos Endpoints". Ou seja, a LayerZero mantém seu próprio conjunto de IDs de cadeia para identificar blockchains que diferem dos números normalmente usados._ A referência completa pode ser encontrada em seus documentos._

Payload contém a mensagem que enviamos como bytes. Essa variável é um amálgama codificada pela ABI de tudo o que queremos enviar pela cadeia.

Nós inicializamos a variável deployer no construtor para o proprietário do contrato.

O contractAddress representa o endereço do contrato que conhecemos após a implantação. Também o inicializamos no construtor.

A variável endpoint é uma instância da interface ILayerZeroEndpoint; nós a usamos para interagir com os pontos de extremidade on-chain. Como um exemplo, verifique o ponto de extremidade Mumbai no Polygonscan.

Implementação do Construtor

 constructor(address _lzEndpoint) NonblockingLzApp(_lzEndpoint) {
    deployer = payable(msg.sender);
    endpoint = ILayerZeroEndpoint(_lzEndpoint);

    // Se Source == Sepolia, então Destination Chain = Mumbai
    if (_lzEndpoint == 0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1)
    destChainId = 10109;

    // se Source == Mumbai, então Destination Chain = Sepolia
    if (_lzEndpoint == 0xf69186dfBa60DdB133E91E9A4B5673624293d8F8)
    destChainId = 10161;
  }
Enter fullscreen mode Exit fullscreen mode

Devemos passar o endereço do endpoint on-chain para o contrato NonblockingLzApp para uma inicialização bem sucedida.

Neste exemplo, escrevemos duas declarações if que atribuem automaticamente o ID da cadeia com base no endereço do contrato do endpoint. Se tivermos implantado o contrato em Mumbai, a variável destChainId apontará para Sepolia e vice-versa.

Interlúdio: Como os Contratos Inteligentes Interagem com o Protocolo LayerZero?

Agora estamos prontos para escrever as duas principais funções de que precisamos para usar o protocolo LayerZero. Mas antes disso, vamos fazer um desvio conceitual.

Para qualquer contrato inteligente, a interação com o endpoint da LayerZero on-chain é um processo de duas partes:

Primeiro, chamamos a função send() no ponto de extremidade para enviar uma mensagem. Para deixar claro, essa é uma função que chamamos em um contrato já implantado, não algo que definimos.

Image description

Segundo, definimos a função_ _nonblockingLzReceive em nosso contrato. Qualquer contrato que queira receber mensagens entre cadeias deve ter essa função definida em seu contrato. O ponto de extremidade da LayerZero chama essa função em nosso contrato para entregar uma mensagem recebida. Para ser claro: não chamamos essa função; apenas a definimos!

Implementação da Função swapTo_ETH

Vamos agora definir a principal função de troca. Criamos uma nova função chamada swapTo_ETH e a definimos da seguinte forma:

function swapTo_ETH(address Receiver) public payable {
    require(msg.value >= 1 ether, "Please send at least 1 MATIC");
    uint value = msg.value;

    bytes memory trustedRemote = trustedRemoteLookup[destChainId];
    require(trustedRemote.length != 0, "LzApp: destination chain is not a trusted source");
    _checkPayloadSize(destChainId, payload.length);

    //A mensagem é codificada como bytes e armazenada na variável "payload".
    payload = abi.encode(Receiver, value);

    endpoint.send{value: 15 ether}(destChainId, trustedRemote, payload, contractAddress, address(0x0), bytes(""));
}
Enter fullscreen mode Exit fullscreen mode

Primeiro, garantimos que o usuário tenha enviado pelo menos 1 ETH em valor ao chamar a função.

Uma função chamada setTrustedRemoteAddress dentro do contrato que herdamos nos permite designar contratos confiáveis. Dessa forma, podemos garantir que nosso contrato interaja apenas com código confiável. O trustedRemoteLookup[destChainId]retorna o endereço do ponto de extremidade da outra cadeia em que confiamos. A função _checkPayloadSize garante que o tamanho da nossa payload esteja dentro dos limites aceitáveis.

Em seguida, empacotamos os dados que queremos enviar em uma única variável do tipo bytes usando abi.encode(). No nosso caso, pedimos ao usuário que nos informe o endereço de destino do outro lado.

Por fim, chamamos o send() e transferimos 15 ETH de nosso próprio contrato inteligente para pagar pelo gas.

Nota: já posso ouvi-lo gritando: "15 ETH! Que diabos está acontecendo aqui?"

Precisamos pagar algumas taxas de gas ao ponto de extremidade para a execução de nossa transação. A taxa real não é de 15 ETH. Entretanto, em minha experiência, as transações que foram acompanhadas por menos gas não foram executadas.

Embora tenhamos definido 15 ETH nessa implementação de swap, recebemos de volta todos os ETH não utilizados. Os 15 ETH são apenas um buffer para garantir que a transação seja realizada.

_A LayerZero tem funções como estimateGasFees(), que nos permite estimar a quantidade de gas que precisa ser enviada, mas achei que ela é imprecisa.

Implementação da Função _nonblockingLzReceive

Bem, é assim que enviamos uma mensagem para a rede de teste Sepolia. Mas e se recebermos uma mensagem de lá?

Para garantir que nosso contrato esteja pronto para lidar com as mensagens recebidas, precisamos implementar uma função chamada _nonblockingLzReceive:

function _nonblockingLzReceive(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload) internal override {

    (address Receiver , uint Value) = abi.decode(_payload, (address, uint));
    address payable recipient = payable(Receiver);        
    recipient.transfer(Value);
}
Enter fullscreen mode Exit fullscreen mode

Essa é a função que a LayerZero chama em nosso contrato para entregar uma mensagem. Sabemos o que codificamos do outro lado. Portanto, podemos decodificá-lo em um endereço de destinatário e em um valor inteiro que representa o valor em Wei que bloqueamos no contrato do outro lado.

Em seguida, transferimos esse valor para o destinatário chamando a função transfer(). MATIC e ETH são dois ativos muito diferentes com valores extremamente diferentes. Em qualquer implementação prática de uma troca entre cadeias, usaríamos um oráculo para coordenar a mediação de preços em tempo real entre os dois ativos. Para este exemplo, assumiremos uma taxa de câmbio de 1:1.

Por fim, encerraremos o código do contrato com duas funções simples:

   // Função Fallback para receber ether
    receive() external payable {}

    /**
     * @dev Permite que o proprietário retire todos os fundos do contrato.
     */
    function withdrawAll() external onlyOwner {
        deployer.transfer(address(this).balance);
    }
}
Enter fullscreen mode Exit fullscreen mode

O Contrato Completo para Mumbai

Depois de adicionar alguns comentários, assim deve ficar o LayerZeroSwap_Mumbai.sol:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

import "@layerzero-contracts/lzApp/NonblockingLzApp.sol";

/**
 * @title LayerZeroSwap_Mumbai
 * @dev Esse contrato envia uma mensagem de cadeia cruzada de Mumbai para Sepolia para transferir ETH em troca de MATIC depositado.
 */
contract LayerZeroSwap_Mumbai is NonblockingLzApp {

    // Variáveis de estado para o contrato    
    uint16 public destChainId;
    bytes payload;
    address payable deployer;
    address payable contractAddress = payable(address(this));

    // Instância do ponto de extremidade LayerZero
    ILayerZeroEndpoint public immutable endpoint;

    /**
     * @dev Construtor que inicializa o contrato com o ponto de extremidade LayerZero.
     * @param _lzEndpoint Endereço do ponto de extremidade LayerZero.
     */
    constructor(address _lzEndpoint) NonblockingLzApp(_lzEndpoint) {
        deployer = payable(msg.sender);
        endpoint = ILayerZeroEndpoint(_lzEndpoint);

        // Se Source == Sepolia, então Destination Chain = Mumbai
        if (_lzEndpoint == 0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1) destChainId = 10109;

        // Se Source == Mumbai, então Destination Chain = Sepolia
        if (_lzEndpoint == 0xf69186dfBa60DdB133E91E9A4B5673624293d8F8) destChainId = 10161;
    }

    /**
     * @dev Permite que os usuários troquem para ETH.
     * @param Receiver Endereço do destinatário.
     */
    function swapTo_ETH(address Receiver) public payable {
        require(msg.value >= 1 ether, "Please send at least 1 MATIC");
        uint value = msg.value;

        bytes memory trustedRemote = trustedRemoteLookup[destChainId];
        require(trustedRemote.length != 0, "LzApp: destination chain is not a trusted source");
        _checkPayloadSize(destChainId, payload.length);

        // A mensagem é codificada como bytes e armazenada na variável "payload".
        payload = abi.encode(Receiver, value);

        endpoint.send{value: 15 ether}(destChainId, trustedRemote, payload, contractAddress, address(0x0), bytes(""));
    }

    /**
     * @dev Função interna para lidar com mensagens LayerZero recebidas.
     */
    function _nonblockingLzReceive(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload) internal override {

        (address Receiver , uint Value) = abi.decode(_payload, (address, uint));
        address payable recipient = payable(Receiver);        
        recipient.transfer(Value);
    }

    // Função fallback para receber ether
    receive() external payable {}

    /**
     * @dev Permite que o proprietário retire todos os fundos do contrato.
     */
    function withdrawAll() external onlyOwner {
        deployer.transfer(address(this).balance);
    }
}
Enter fullscreen mode Exit fullscreen mode

O Contrato Sepolia Completo

A contraparte Sepolia desse contrato é quase a mesma, só que ao contrário.

Agora, dentro do LayerZeroSwap_Sepolia.sol, cole o seguinte código:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

import "@layerzero-contracts/lzApp/NonblockingLzApp.sol";

/**
 * @title LayerZeroSwap_Sepolia
 * @dev Esse contrato envia uma mensagem de cadeia cruzada da Sepolia para a Mumbai para transferir MATIC em troca de ETH depositado.
 */
contract LayerZeroSwap_Sepolia is NonblockingLzApp {

    // Variáveis de estado para o contrato
    address payable deployer;    
    uint16 public destChainId;
    bytes payload;    
    address payable contractAddress = payable(address(this));

    // Instância do ponto de extremidade LayerZero
    ILayerZeroEndpoint public immutable endpoint;

    /**
     * @dev Construtor que inicializa o contrato com o ponto de extremidade LayerZero.
     * @param _lzEndpoint Endereço do ponto de extremidade LayerZero.
     */
    constructor(address _lzEndpoint) NonblockingLzApp(_lzEndpoint) {
        deployer = payable(msg.sender);
        endpoint = ILayerZeroEndpoint(_lzEndpoint);

        // Se Source == Sepolia, então Destination Chain = Mumbai
        if (_lzEndpoint == 0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1) destChainId = 10109;

        // Se Source == Mumbai, então Destination Chain = Sepolia
        if (_lzEndpoint == 0xf69186dfBa60DdB133E91E9A4B5673624293d8F8) destChainId = 10161;
    }

    /**
     * @dev Permite que os usuários troquem para MATIC.
     * @param Receiver Endereço do Destinatário.
     */
    function swapTo_MATIC(address Receiver) public payable {
        require(msg.value >= 1 ether, "Please send at least 1 ETH");
        uint value = msg.value;

        bytes memory trustedRemote = trustedRemoteLookup[destChainId];
        require(trustedRemote.length != 0, "LzApp: destination chain is not a trusted source");
        _checkPayloadSize(destChainId, payload.length);

        // A mensagem é codificada como bytes e armazenada na variável "payload".
        payload = abi.encode(Receiver, value);

        endpoint.send{value: 15 ether}(destChainId, trustedRemote, payload, contractAddress, address(0x0), bytes(""));
    }

    /**
     * @dev Função interna para lidar com mensagens LayerZero recebidas.
     */
    function _nonblockingLzReceive(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload) internal override {
        (address Receiver , uint Value) = abi.decode(_payload, (address, uint));
        address payable recipient = payable(Receiver);        
        recipient.transfer(Value);
    }

    // Função fallback para receber ether
    receive() external payable {}

    /**
     * @dev Permite que o proprietário retire todos os fundos do contrato.
     */
    function withdrawAll() external onlyOwner {
        deployer.transfer(address(this).balance);
    }
}
Enter fullscreen mode Exit fullscreen mode

Criação de Contratos

Excluímos os arquivos padrão dentro do diretório script e test e executamos:

forge build
Enter fullscreen mode Exit fullscreen mode

Esse comando pode mostrar alguns avisos, mas podemos ignorá-los.

Criação do Arquivo .env

Precisamos adicionar nossas informações confidenciais em um arquivo .env para armazená-las com segurança. Portanto, criamos um arquivo na raiz do nosso projeto e o preenchemos com o seguinte:

SEPOLIA_RPC_URL=
MUMBAI_RPC_URL=

PRIVATE_KEY=

ETHERSCAN_API_KEY=
POLYGONSCAN_API_KEY=
Enter fullscreen mode Exit fullscreen mode

Podemos obter URLs de RPC dos serviços como Alchemy e Chainstack, ou você pode usar um URL de RPC público. Precisamos de 2 URLs de RPC, pois pretendemos implantar em duas redes.

Usamos uma chave privada correspondente a uma carteira que contém ETH e MATIC. Obtenha as chaves de API no Etherscan e Polygonscan. As chaves dos exploradores da rede principal também funcionarão em Sepolia e Mumbai.

Com todos os valores preenchidos, salve o arquivo .env. Execute este comando no terminal para obter essas variáveis no terminal:

source .env
Enter fullscreen mode Exit fullscreen mode

Implementação dos Scripts de Implantação

Implementaremos nossos contratos escrevendo scripts do Solidity.

Crie dois arquivos dentro do diretório de scripts, cada um implantando um contrato.

  • Deploy_Sepolia.s.sol
  • Deploy_Mumbai.s.sol

Dentro do Deploy_Sepolia.s.sol,adicione o seguinte código:

pragma solidity ^0.8.13;

import "forge-std/Script.sol";
import {LayerZeroSwap_Sepolia} from "../src/LayerZeroSwap_Sepolia.sol";

contract MyScript is Script {
  LayerZeroSwap_Sepolia layerZeroSwap_Sepolia;

  function run() external {        

    uint256 PrivateKey = vm.envUint("PRIVATE_KEY");
    vm.startBroadcast(PrivateKey);

    layerZeroSwap_Sepolia = new LayerZeroSwap_Sepolia(0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1);

    vm.stopBroadcast();
  }
}
Enter fullscreen mode Exit fullscreen mode

Para escrever um script de implantação usando o Foundry, precisamos escrever um contrato que herda do script.sol.

Primeiro, declaramos uma variável para o contrato layerZeroSwap_Sepolia. Em seguida, inicializamos layerZeroSwap_Sepolia, que representa uma implantação on-chain. Qualquer coisa que escrevermos entre startBroadcast() e stopBroadcast()será executada como uma transação on-chain.

Passamos o endereço do ponto de extremidade da Sepolia como um argumento do construtor.

Da mesma forma, colamos o seguinte código no Deploy_Mumbai.s.sol para o script de implantação Mumbai:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/Script.sol";
import {LayerZeroSwap_Mumbai} from "../src/LayerZeroSwap_Mumbai.sol";

contract MyScript is Script {
    LayerZeroSwap_Mumbai layerZeroSwap_Mumbai;

    function run() external {        

        uint256 PrivateKey = vm.envUint("PRIVATE_KEY");
        vm.startBroadcast(PrivateKey);

        layerZeroSwap_Mumbai = new LayerZeroSwap_Mumbai(0xf69186dfBa60DdB133E91E9A4B5673624293d8F8);

        vm.stopBroadcast();
    }
}
Enter fullscreen mode Exit fullscreen mode

Implantação e Verificação dos Contratos

Para executar o Deploy_Mumbai.s.sol, executamos esse comando no seu terminal:

forge script script/Deploy_Mumbai.s.sol:DeployMumbai \
--rpc-url $MUMBAI_RPC_URL \
--broadcast -vvvv
Enter fullscreen mode Exit fullscreen mode

Nota: Definimos o sinalizador de verbosidade (-v) com o máximo de 4. Isso nos dá um rastreamento completo da pilha de qualquer script que executamos no terminal, o que pode ser muito útil para a depuração.

Depois de implantar um contrato, o Foundry sempre retorna um endereço. Podemos usá-lo para verificar esse contrato com o seguinte comando:

forge verify-contract 0x5Bf61Ac6a7B63dDB5D80D125Bc7054916A50d99E \
--chain-id 80001 \
--num-of-optimizations 200 \
--watch --compiler-version v0.8.19+commit.7dd6d404 \
--constructor-args $(cast abi-encode "constructor(address)" 0xf69186dfBa60DdB133E91E9A4B5673624293d8F8)
src/LayerZeroSwap_Mumbai.sol:LayerZeroSwap_Mumbai \
--etherscan-api-key $POLYGONSCAN_API_KEY
Enter fullscreen mode Exit fullscreen mode

Esta é a aparência que seu terminal deve ter agora:

Image description

Da mesma forma, podemos executar Deploy_Sepolia.s.sol com este comando:

forge script script/Deploy_Sepolia.s.sol:DeploySepolia \
--rpc-url $SEPOLIA_RPC_URL \
--broadcast -vvvv
Enter fullscreen mode Exit fullscreen mode

Por fim, podemos verificar as implantações com este comando:

forge verify-contract 0xDA96bbe0B02e64D374bD98355a91405653b081E2 \
--chain-id 11155111 \
--num-of-optimizations 200 \
--watch --compiler-version v0.8.19+commit.7dd6d404 \
--constructor-args $(cast abi-encode "constructor(address)" 0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1) \
src/LayerZeroSwap_Sepolia.sol:LayerZeroSwap_Sepolia \
--etherscan-api-key $ETHERSCAN_API_KEY
Enter fullscreen mode Exit fullscreen mode

Conexão entre os dois Contratos

Você se lembra de como podemos usar o setTrustedRemoteAddress() para designar contratos confiáveis? Precisamos informar cada ponto de extremidade de suas contrapartes para que possam se comunicar entre si.

Na Sepolia, chamamos a função com os seguintes parâmetros:

  • _remoteChainId = 10109 (ID da Cadeia LayerZero para Mumbai)
  • _remoteAddress = Endereço do nosso contrato Mumbai

Na Mumbai, chame a função com os seguintes parâmetros:

  • _remoteChainId = 10161 (ID da Cadeia LayerZero para Sepolia)
  • _remoteAddress = Endereço do contrato Sepolia

Por fim, garantimos o envio de ETH e MATIC para cada contrato. Lembre-se de que são os contratos que estão pagando pelo gas, portanto, temos que enviar 30 para cada um dos contratos.

Problemas com essa Implementação

Antes de concluir, vamos discutir alguns problemas em nossa troca de cadeia cruzada:

  • ETH e MATIC não são ativos equivalentes. Qualquer implementação pronta para produção precisa de um ou mais feeds de dados confiáveis para a taxa de câmbio.
  • Não há tratamento de erros para transações com falha. Se a LayerZero não conseguir entregar uma mensagem, precisamos de verificações que assumam o controle nesse cenário.
  • Não cobramos nenhuma taxa dos usuários. Em uma implementação de produção, a troca entre cadeias deve cobrar algumas taxas para cobrir seus custos.

Considerando tudo isso, essa é uma implementação muito básica, mas o repositório do Github de Finanças Stargate oferece uma solução pronta para produção como um bom exemplo.

Conclusão

Com os recursos robustos da LayerZero, nossa solução está pronta para facilitar uma troca 1:1 perfeita entre ETH e MATIC.

Se surgir alguma dúvida ou pensamento, não hesite em entrar em contato comigo pelo Twitter. Estou sempre disposto a participar e ajudar.

Um brinde às possibilidades ilimitadas das inovações entre cadeias!

Esse artigo foi escrito por Priyank Gupta e traduzido por Fátima Lima. O original pode ser lido aqui.

Top comments (0)