🌌
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
  1. Materiais
  2. Infraestrutura

Run a Relayer

PreviousInfraestruturaNextRun a Spy

Last updated 4 months ago

Os relayers desempenham um papel crucial na comunicação cross-chain, garantindo que as mensagens sejam transferidas de forma contínua entre diferentes blockchains. Embora os relayers do Wormhole forneçam uma maneira confiável de gerenciar essas transferências, eles podem não atender a todos os requisitos exclusivos de cada aplicação.

Relayers personalizados abordam essas limitações ao oferecer soluções sob medida que atendem às necessidades distintas de sua aplicação. Desenvolver um relayer personalizado proporciona controle total sobre o processamento das mensagens, mecanismos de entrega e integração com sistemas existentes. Essa personalização permite otimizar o desempenho e implementar recursos específicos que os relayers implantados pelo Wormhole podem não oferecer.

Um relayer personalizado pode ser tão simples quanto um processo no navegador que consulta a API para verificar a disponibilidade de um VAA após a submissão de uma transação e entregá-lo à cadeia de destino. Também pode ser implementado com um Spy combinado a um daemon que escuta por VAAs de um ID de cadeia e emissor relevantes, tomando ações quando um VAA é observado.

Este guia ensina como configurar e configurar um relayer personalizado para o manuseio eficiente de mensagens. Você começará entendendo como identificar de forma única um VAA utilizando o endereço do emissor, ID de sequência e ID de cadeia. Em seguida, explorará o Relayer Engine, um pacote que fornece uma estrutura para a construção de relayers personalizados, e aprenderá como buscar e processar VAAs utilizando o SDK do Wormhole.

Iniciando com um Relayer Personalizado Para começar a construir um relayer personalizado, é essencial entender os componentes que você estará gerenciando como parte do seu serviço de relaying. Seu relayer deve ser capaz de recuperar e entregar VAAs.

Como Identificar de Forma Exclusiva um VAA

Independentemente do ambiente, para obter o VAA que você deseja retransmitir, é necessário ter:

  • O endereço do emissor

  • O ID de sequência da mensagem de interesse

  • O chainId da cadeia que emitiu a mensagem

Com esses três componentes, é possível identificar de forma única um VAA e processá-lo.

Usando o Relayer Engine

O relayer-engine é um pacote que fornece a estrutura e o ponto de partida para um relayer personalizado.

Com o Relayer Engine, um desenvolvedor pode escrever uma lógica específica para filtrar e receber apenas as mensagens que interessam.

Após receber uma mensagem do Wormhole, o desenvolvedor pode aplicar lógica adicional para analisar payloads personalizados ou enviar as Verifiable Action Approvals (VAA) para uma ou várias cadeias de destino.

Para usar o Relayer Engine, o desenvolvedor pode especificar como retransmitir mensagens do Wormhole para sua aplicação, utilizando uma API inspirada em middleware Express/Koa, deixando que a biblioteca cuide dos detalhes.

Instalar o Relayer Engine

Primeiro, instale o pacote relayer-engine com o gerenciador de pacotes de sua preferência:

npm i @wormhole-foundation/relayer-engine

Começando com o Relayer Engine

No exemplo a seguir, você:

  1. Configura um StandardRelayerApp, passando as opções de configuração para o relayer.

  2. Adiciona um filtro para capturar apenas as mensagens que a aplicação se importa, com um callback para processar o VAA assim que for recebido.

  3. Inicia o aplicativo do relayer.

import {
  Environment,
  StandardRelayerApp,
  StandardRelayerContext,
} from '@wormhole-foundation/relayer-engine';
import { CHAIN_ID_SOLANA } from '@certusone/wormhole-sdk';

(async function main() {
  // Inicializa o aplicativo do relayer e passa as opções de configuração
  const app = new StandardRelayerApp<StandardRelayerContext>(
    Environment.TESTNET,
    {
      name: 'ExampleRelayer',
    }
  );

  // Adiciona um filtro com callback que será invocado ao encontrar um VAA que atenda ao filtro
  app.chain(CHAIN_ID_SOLANA).address(
    'DZnkkTmCiFWfYTfT41X3Rd1kDgozqzxWaHqsw6W4x2oe',
    async (ctx, next) => {
      const vaa = ctx.vaa;
      const hash = ctx.sourceTxHash;
      console.log(`Got a VAA with sequence: ${vaa.sequence} from with txhash: ${hash}`);
    }
  );

  // Inicia o aplicativo
  await app.listen();
})();

A primeira linha significativa instancia o StandardRelayerApp, uma subclasse de RelayerApp com padrões padrão.

Configurações do StandardRelayerAppOpts

Outras opções podem ser passadas ao construtor do StandardRelayerApp para configurar ainda mais o aplicativo:

  • wormholeRpcs: Lista de URLs de onde consultar VAAs perdidos

  • concurrency: Quantidade de requisições simultâneas para workflows

  • spyEndpoint: Endereço do Spy

  • logger: Logger personalizado

  • privateKeys: Conjunto de chaves para assinar e enviar transações

  • tokensByChain: Lista de tokens relevantes

  • workflows: Como configurar tentativas de workflows

  • redisClusterEndpoints: Configuração do Redis

Adicionando Filtros e Middleware

A última linha no exemplo simples executa await app.listen(), que inicia o motor do relayer. Uma vez iniciado, o Relayer Engine envia solicitações de assinatura para o Spy e começa outros workflows, como o rastreamento de VAAs perdidos.

O processo continuará até ser interrompido ou até encontrar um erro irrecuperável. Para desligar o relayer de forma ordenada, chame app.stop().

O código-fonte para este exemplo está disponível no repositório relayer-engine.

Iniciar Processos em Segundo Plano

Estes processos precisam estar em execução para que o aplicativo do relayer funcione corretamente.

A seguir, você deve iniciar um Spy para escutar os VAAs disponíveis publicados na rede Guardian. Também é necessário uma camada de persistência; este exemplo usa Redis.

Mais detalhes sobre o Spy estão disponíveis na documentação do Spy.

Spy da Rede Wormhole

Para que o aplicativo do relayer receba mensagens, um Spy local deve estar em execução, monitorando a rede Guardian. O aplicativo do relayer receberá atualizações desse Spy.

  • Mainnet Spy

docker run --pull=always --platform=linux/amd64 \
-p 7073:7073 \
--entrypoint /guardiand ghcr.io/wormhole-foundation/guardiand:latest \
spy \
--nodeKey /node.key \
--spyRPC "[::]:7073" \
--env mainnet
  • Testnet Spy

docker run --pull=always --platform=linux/amd64 \
-p 7073:7073 \
--entrypoint /guardiand ghcr.io/wormhole-foundation/guardiand:latest \
spy \
--nodeKey /node.key \
--spyRPC "[::]:7073" \
--env testnet

Persistência Redis

Nota

Embora esteja utilizando o Redis aqui, a camada de persistência pode ser trocada por outro banco de dados, implementando a interface apropriada.

Uma instância do Redis também deve estar disponível para persistir os dados das tarefas de recuperação de VAAs do Spy.

docker run --rm -p 6379:6379 --name redis-docker -d redis

Usando o Wormhole SDK

Você também pode usar o Wormhole SDK para fazer polling no RPC do Guardian até que um VAA assinado esteja pronto, utilizando a função getSignedVAAWithRetry do SDK.

import {
  getSignedVAAWithRetry,
  parseVAA,
  CHAIN_ID_SOLANA,
  CHAIN_ID_ETH,
} from '@certusone/wormhole-sdk';

const RPC_HOSTS = [
  /* ...*/
];

async function getVAA(
  emitter: string,
  sequence: string,
  chainId: number
): Promise<Uint8Array> {
  // Aguarda o VAA ficar pronto e o recupera da rede Guardian
  const { vaaBytes } = await getSignedVAAWithRetry(
    RPC_HOSTS,
    chainId,
    emitter,
    sequence
  );
  return vaaBytes;
}

const vaaBytes = await getVAA('INSIRA_ENDEREÇO_EMISSOR', 1, CHAIN_ID_ETH);

Uma vez que você tenha o VAA, o método de entrega depende da cadeia.

  • EVM

Em cadeias EVM, os bytes do VAA podem ser passados diretamente como um argumento para um método ABI.

// Configurar carteira ETH
const ethProvider = new ethers.providers.StaticJsonRpcProvider(
  'INSIRA_URL_RPC'
);
const ethWallet = new ethers.Wallet('INSIRA_CHAVE_PRIVADA', ethProvider);

// Criar cliente para interagir com nosso aplicativo de destino
const ethHelloWorld = HelloWorld__factory.connect(
  'INSIRA_ENDEREÇO_CONTRATO',
  ethWallet
);

// Invocar o método receiveMessage no contrato ETH e aguardar a confirmação
const receipt = await ethHelloWorld
  .receiveMessage(vaaBytes)
  .then((tx: ethers.ContractTransaction) => tx.wait())
  .catch((msg: any) => {
    console.error(msg);
    return null;
  });
  • SOLANA

No Solana, o VAA é primeiro postado no core bridge, e então uma transação personalizada é preparada para processar e validar o VAA.

import { CONTRACTS } from '@certusone/wormhole-sdk';

export const WORMHOLE_CONTRACTS = CONTRACTS[NETWORK];
export const CORE_BRIDGE_PID = new PublicKey(WORMHOLE_CONTRACTS.solana.core);

// Primeiro, poste o VAA no core bridge
await postVaaSolana(
  connection,
  wallet.signTransaction,
  CORE_BRIDGE_PID,
  wallet.key(),
  vaaBytes
);

const program = createHelloWorldProgramInterface(connection, programId);
const parsed = isBytes(wormholeMessage)
  ? parseVaa(wormholeMessage)
  : wormholeMessage;

const ix = program.methods
  .receiveMessage([...parsed.hash])
  .accounts({
    payer: new PublicKey(payer),
    config: deriveConfigKey(programId),
    wormholeProgram: new PublicKey(wormholeProgramId),
    posted: derivePostedVaaKey(wormholeProgramId, parsed.hash),
    foreignEmitter: deriveForeignEmitterKey(programId, parsed.emitterChain),
    received: deriveReceivedKey(
      programId,
      parsed.emitterChain,
      parsed.sequence
    ),
  })
  .instruction();

const transaction = new Transaction().add(ix);
const { blockhash } = await connection.getLatestBlockhash(commitment);
transaction.recentBlockhash = blockhash;
transaction.feePayer = new PublicKey(payerAddress);

const signed = await wallet.signTxn(transaction);
const txid = await connection.sendRawTransaction(signed);

await connection.confirmTransaction(txid);