🌌
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. Build
  3. Toolkit
  4. Wormhole SDK

Data Layouts

O SDK Wormhole e o Sistema de Layout

O Wormhole SDK utiliza o pacote layout para definir, serializar e desserializar estruturas de dados de maneira eficiente. Esse sistema modular garante formatação consistente de dados e compatibilidade entre ambientes, sendo ideal para projetos que exigem um gerenciamento robusto de dados estruturados.

Benefícios do Sistema de Layout

Compreender o mecanismo de layout permite que você:

  1. Defina estruturas de dados: como números, arrays e tipos personalizados.

  2. Serialização e desserialização eficientes: converta dados estruturados para binário e vice-versa.

  3. Lide com layouts específicos do protocolo: ideal para aplicações que envolvam comunicação cross-chain.

Essa abordagem é particularmente útil para desenvolvedores que desejam integrar o Wormhole em suas aplicações, especialmente ao trabalhar com payloads complexos ou comunicação entre blockchains.


Conceitos-Chave

Itens de Layout

Um layout define como estruturas de dados devem ser:

  • Serializadas: convertidas para um formato binário.

  • Desserializadas: reconstruídas para sua estrutura original.

Um layout é composto por itens de layout, que especificam campos individuais ou conjuntos de campos na sua estrutura de dados. Cada item de layout inclui:

  • name: Nome do campo.

  • binary: Tipo de dado (e.g., uint, bytes).

  • size: Tamanho fixo em bytes (para tipos como uint e bytes).

Exemplo de Layout

Abaixo, um exemplo de layout para serializar uma mensagem no protocolo Wormhole:

const exampleLayout = [
  { name: 'sourceChain', binary: 'uint', size: 2 },
  { name: 'orderSender', binary: 'bytes', size: 32 },
  { name: 'redeemer', binary: 'bytes', size: 32 },
  { name: 'redeemerMessage', binary: 'bytes', lengthSize: 4 },
] as const;

Neste exemplo:

  • sourceChain: Inteiro não assinado de 2 bytes, identifica a blockchain de origem.

  • orderSender: Array de bytes fixo (32 bytes), representa o endereço do remetente.

  • redeemer: Outro array de 32 bytes, usado para o endereço do recebedor.

  • redeemerMessage: Sequência de bytes de comprimento variável, especificada por um inteiro de 4 bytes.

Essa definição garante que todos os campos necessários sejam codificados de maneira consistente e interpretados corretamente.


Serialização e Desserialização

Serialização

Converte dados estruturados em formato binário. Use a função serializeLayout:

const examplePayload = {
  sourceChain: 6,
  orderSender: new Uint8Array(32),
  redeemer: new Uint8Array(32),
  redeemerMessage: new Uint8Array([0x01, 0x02, 0x03]),
};

const serializedData = serializeLayout(exampleLayout, examplePayload);

Resultado: serializedData será um Uint8Array representando os dados em binário.

Desserialização

Reconstrói objetos estruturados a partir de binário. Use a função deserializeLayout:

const deserializedPayload = deserializeLayout(exampleLayout, serializedData);

Resultado: Um objeto reconstruído, facilitando o trabalho com dados transmitidos entre blockchains.


Conversões Personalizadas

Os layouts permitem mapeamentos personalizados para tipos complexos. Isso é útil ao lidar com dados que não se encaixam em tipos simples como inteiros ou arrays de bytes.

Exemplo: Conversão personalizada para um Chain ID:

const chainCustomConversion = {
  to: (chainId: number) => toChain(chainId),
  from: (chain: Chain) => chainToChainId(chain),
} satisfies CustomConversion<number, Chain>;

Essa configuração permite conversões entre formatos legíveis e dados codificados usados em payloads.


Tratamento de Erros

O sistema de layout realiza verificações durante a serialização/desserialização. Erros são lançados se os dados estiverem no formato ou tamanho errado.

Exemplo de tratamento de erro:

try {
  deserializeLayout(fillLayout, corruptedData);
} catch (error) {
  console.error('Erro durante a desserialização:', error.message);
}

Aplicações de Layouts

Definição de Layouts

Estruturas são definidas como listas de campos:

const exampleLayout = [
  { name: 'sourceChain', binary: 'uint', size: 2 },
  { name: 'orderSender', binary: 'bytes', size: 32 },
  { name: 'redeemer', binary: 'bytes', size: 32 },
  { name: 'redeemerMessage', binary: 'bytes', lengthSize: 4 },
] as const;

Campos de Comprimento Variável

O layout suporta arrays e sequências de bytes com comprimento variável, usando lengthSize para especificar o tamanho do campo:

{ name: 'message', binary: 'bytes', lengthSize: 4 }

O sistema de layout do Wormhole SDK oferece uma maneira eficiente de gerenciar dados estruturados para comunicação cross-chain, garantindo consistência e suporte a casos de uso complexos, como payloads de comprimento variável e conversões personalizadas.

Layouts Aninhados e Tipagem Forte O Wormhole SDK simplifica o manuseio de estruturas complexas ao adotar layouts aninhados e tipagem forte. Enquanto os layouts aninhados organizam dados hierárquicos de forma clara, a tipagem forte garante consistência dos dados e identifica erros durante o desenvolvimento.

Layouts Aninhados

Em protocolos complexos, estruturas aninhadas são comuns. Esses layouts permitem organizar dados hierárquicos (como transações ou mensagens multipartes) em formatos estruturados.

Exemplo de layout aninhado onde uma mensagem possui campos aninhados:

const nestedLayout = [
  {
    name: 'source',
    binary: 'bytes',
    layout: [
      { name: 'chainId', binary: 'uint', size: 2 },
      { name: 'sender', binary: 'bytes', size: 32 },
    ],
  },
  {
    name: 'redeemer',
    binary: 'bytes',
    layout: [
      { name: 'address', binary: 'bytes', size: 32 },
      { name: 'message', binary: 'bytes', lengthSize: 4 },
    ],
  },
] as const satisfies Layout;

Aqui:

  • source contém dois campos: chainId e sender.

  • redeemer possui address e uma mensagem com comprimento prefixado.

Tipagem Forte

Ao usar TypeScript, o Wormhole SDK implementa tipagem forte para garantir que dados serializados/deserializados sigam a estrutura esperada, minimizando erros.

Com o utilitário LayoutToType, cria-se uma estrutura tipada a partir de um layout:

type NestedMessage = LayoutToType<typeof nestedLayout>;
const message: NestedMessage = {
  source: {
    chainId: 6,
    sender: new Uint8Array(32),
  },
  redeemer: {
    address: new Uint8Array(32),
    message: new Uint8Array([0x01, 0x02, 0x03]),
  },
};

Qualquer dado com tipo incorreto resulta em erro em tempo de compilação.

Serialização e Desserialização

Com layouts aninhados, a serialização e desserialização seguem o mesmo padrão:

const serializedNested = serializeLayout(nestedLayout, message);
const deserializedNested = deserializeLayout(nestedLayout, serializedNested);

A tipagem forte em TypeScript assegura que os objetos de mensagem estejam em conformidade com o layout.


Layouts Usados Comumente

O SDK inclui layouts otimizados para campos padrão como IDs de blockchain, endereços e assinaturas.

Chain ID Layouts

Os layouts de ID de blockchain derivam do chainItemBase, que define a representação binária de um ID como um inteiro não assinado de 2 bytes:

const chainItemBase = { binary: 'uint', size: 2 } as const;

Dynamic Chain ID Layout: Permite validação em tempo de execução e suporte a valores nulos.

export const chainItem = <const C extends readonly Chain[] = typeof chains>(
  opts?: { allowedChains?: C; allowNull?: boolean },
) => ({
  ...chainItemBase,
  custom: {
    to: (val: number): AllowNull<C[number], N> => { ... },
    from: (val: AllowNull<C[number], N>): number => { ... },
  },
});

Fixed Chain ID Layout: Rígido, ideal para um único ID de blockchain.

export const fixedChainItem = <const C extends Chain>(chain: C) => ({
  ...chainItemBase,
  custom: {
    to: chain,
    from: chainToChainId(chain),
  },
});

Layouts de Assinatura

O layout de assinatura no Wormhole define como serializar/desserializar assinaturas criptográficas.

const signatureLayout = [
  { name: 'r', binary: 'uint', size: 32 },
  { name: 's', binary: 'uint', size: 32 },
  { name: 'v', binary: 'uint', size: 1 },
] as const satisfies Layout;

Custom Conversion: Integra dados binários brutos com objetos de assinatura de alto nível.

export const signatureItem = {
  binary: 'bytes',
  layout: signatureLayout,
  custom: {
    to: (val: LayoutToType<typeof signatureLayout>) =>
      new Signature(val.r, val.s, val.v),
    from: (val: Signature) => ({ r: val.r, s: val.s, v: val.v }),
  },
} satisfies BytesLayoutItem;

Essa integração melhora a confiabilidade no processamento de dados de assinatura.

VAAs e Protocolos Wormhole

As VAAs (Verified Action Approval) são mensagens assinadas que conectam cadeias distintas. O layout para VAAs organiza dados em três componentes principais:

  • Header: Metadados como índice do conjunto de guardiões e assinaturas.

  • Envelope: Detalhes específicos da cadeia, como ID de emissor e sequência.

  • Payload: Dados de aplicação.

Header Example:

export const headerLayout = [
  { name: 'version', binary: 'uint', size: 1, custom: 1, omit: true },
  { name: 'guardianSet', ...guardianSetItem },
  {
    name: 'signatures',
    binary: 'array',
    lengthSize: 1,
    layout: guardianSignatureLayout,
  },
] as const satisfies Layout;

A arquitetura modular do Wormhole SDK facilita a interoperabilidade entre cadeias, garantindo consistência nos dados.

Como funciona?

Internamente, a função serialize combina dinamicamente o baseLayout (cabeçalho e envelope) com o layout do payload definido pelo payloadLiteral. O layout completo é então passado para a função serializeLayout, que converte os dados no formato binário.

const layout = [
  ...baseLayout, // Layout do cabeçalho e envelope
  payloadLiteralToPayloadItemLayout(vaa.payloadLiteral), // Layout do payload
] as const;

return serializeLayout(layout, vaa as LayoutToType<typeof layout>);

Desserializando os dados do VAA

O SDK Wormhole fornece a função deserialize para analisar um VAA no formato binário de volta para um objeto estruturado. Esta função usa o baseLayout e a lógica do discriminador de payload para garantir que o VAA seja interpretado corretamente.

import { deserialize } from '@wormhole-foundation/sdk-core/vaa/functions';

const serializedVAA = new Uint8Array([
  /* Dados binários do VAA serializado */
]);

const vaaPayloadType = 'SomePayloadType'; // Tipo de payload esperado para este VAA
const deserializedVAA = deserialize(vaaPayloadType, serializedVAA);

Como funciona?

Internamente, a função deserialize usa o baseLayout (cabeçalho e envelope) para analisar a estrutura principal do VAA. Ela então identifica o layout do payload apropriado usando o tipo de payload ou discriminador fornecido.

const [header, envelopeOffset] = deserializeLayout(headerLayout, data, {
  consumeAll: false,
});

const [envelope, payloadOffset] = deserializeLayout(envelopeLayout, data, {
  offset: envelopeOffset,
  consumeAll: false,
});

const [payloadLiteral, payload] =
  typeof payloadDet === 'string'
    ? [
        payloadDet as PayloadLiteral,
        deserializePayload(payloadDet as PayloadLiteral, data, payloadOffset),
      ]
    : deserializePayload(
        payloadDet as PayloadDiscriminator,
        data,
        payloadOffset
      );

return {
  ...header,
  ...envelope,
  payloadLiteral,
  payload,
} satisfies VAA;

Registrando Payloads Personalizados

No SDK Wormhole, os payloads dependem de layouts para definir sua estrutura binária, garantindo consistência e segurança de tipo entre os protocolos. Payloads personalizados expandem essa funcionalidade, permitindo que os desenvolvedores tratem características específicas de protocolo ou casos de uso únicos.

Para aprender como definir e registrar payloads usando layouts, consulte a página "Building Protocols and Payloads" para um guia detalhado.

Erros Comuns e Melhores Práticas

Ao trabalhar com o sistema de layout do SDK Wormhole, é importante estar ciente de alguns problemas comuns que podem surgir. Abaixo estão alguns erros a evitar e melhores práticas para garantir uma integração tranquila.

Erros a Evitar

Definindo Tamanhos para Tipos de Dados

Ao definir os tamanhos para cada tipo de dado, certifique-se de que o comprimento real dos dados corresponda ao tamanho especificado para evitar erros de serialização e desserialização:

  • uint e int - o tamanho especificado deve ser grande o suficiente para acomodar o valor dos dados. Por exemplo, armazenar um valor maior que 255 em um único byte (uint8) falhará, pois excede a capacidade do byte. Da mesma forma, um inteiro subdimensionado (por exemplo, especificar 2 bytes para um inteiro de 4 bytes) pode resultar em perda de dados ou falha na desserialização.

  • bytes - os dados devem corresponder ao comprimento de byte especificado no layout. Por exemplo, definir um campo como 32 bytes (tamanho: 32) exige que os dados fornecidos tenham exatamente 32 bytes de comprimento; caso contrário, a serialização falhará.

// Erro: Descompasso entre o tamanho dos dados e o tamanho definido no layout
{ name: 'orderSender', binary: 'bytes', size: 32 }
// Se os dados fornecidos não tiverem exatamente 32 bytes, isso falhará

Arrays Definidos Incorretamente

Arrays podem ser de comprimento fixo ou prefixados por comprimento, então é importante defini-los corretamente. Arrays de comprimento fixo devem corresponder ao comprimento especificado, enquanto arrays prefixados por comprimento necessitam de um campo lengthSize.

// Erro: O comprimento do array não corresponde ao tamanho esperado
{ name: 'redeemerMessage', binary: 'bytes', lengthSize: 4 }

Melhores Práticas

Essas melhores práticas e erros comuns podem ajudar a prevenir bugs e melhorar a confiabilidade de sua implementação ao trabalhar com layouts no SDK Wormhole.

Reutilize Itens de Layout Predefinidos

Em vez de definir manualmente os tamanhos ou tipos, reutilize os itens de layout predefinidos fornecidos pelo SDK Wormhole. Esses itens garantem formatação consistente e aplicam tipagem forte.

Por exemplo, use o layout chainItem para IDs de cadeia ou universalAddressItem para endereços de blockchain:

import {
  chainItem,
  universalAddressItem,
} from '@wormhole-foundation/sdk-core/layout-items';

const exampleLayout = [
  { name: 'sourceChain', ...chainItem() }, // Use o layout predefinido para ID da cadeia
  { name: 'senderAddress', ...universalAddressItem }, // Use o layout para endereço universal
] as const;

Ao aproveitar os itens de layout predefinidos, você reduz redundâncias, mantém a consistência e garante compatibilidade com os padrões do Wormhole.

Use Instâncias de Classe

Sempre que possível, converta os dados desserializados em instâncias de classes de nível superior. Isso facilita a validação, manipulação e interação com dados estruturados. Por exemplo, a classe UniversalAddress garante o manuseio consistente de endereços:

import { UniversalAddress } from '@wormhole-foundation/sdk-core';

const deserializedAddress = new UniversalAddress(someBinaryData);

Focar em reutilizar itens de layout predefinidos e converter dados desserializados em abstrações de nível superior pode garantir uma implementação mais robusta e de fácil manutenção.

Tratamento Consistente de Erros

Sempre trate os erros durante as operações de serialização e desserialização. Capturar exceções permite registrar ou resolver problemas de forma elegante ao trabalhar com dados potencialmente corrompidos ou inválidos.

try {
  const deserialized = deserializeLayout(fillLayout, data);
} catch (error) {
  console.error('Falha na desserialização:', error);
}

Aproveite Layouts Reutilizáveis

Criar layouts reutilizáveis para estruturas comumente repetidas melhora a manutenção do código e reduz a duplicação. Esses layouts podem representar campos ou combinações de campos frequentemente encontrados em comunicação cross-chain, como IDs de cadeia, endereços e assinaturas.

Por exemplo, defina um layout reutilizável para IDs de cadeia e endereços:

const commonLayout = [
  { name: 'chainId', binary: 'uint', size: 2 },
  { name: 'address', binary: 'bytes', size: 32 },
] as const satisfies Layout;

// Reutilize o layout comum em diferentes contextos
const exampleLayout = [
  ...commonLayout,
  { name: 'sequence', binary: 'uint', size: 8 },
];

Ao abstrair elementos comuns em um único layout, você garante consistência em diferentes partes da aplicação e simplifica futuras atualizações.

Considerações de Performance

A serialização e desserialização eficientes são cruciais ao lidar com grandes quantidades de dados cross-chain. Abaixo estão algumas estratégias e melhores práticas para garantir o desempenho ideal ao usar layouts do SDK Wormhole.

Instanciação Preguiçosa

A criação de um discriminador pode ser intensiva em recursos para conjuntos de dados complexos ou grandes. As estruturas de layout não acarretam custos significativos de forma antecipada, mas adiar a criação de discriminadores até que sejam necessários pode melhorar a eficiência.

const lazyDiscriminator = lazyInstantiate(() => layoutDiscriminator(layouts));

Essa abordagem garante que os discriminadores sejam criados apenas quando necessário, ajudando a otimizar o desempenho, especialmente para layouts complexos ou condicionais.

Recursos

Para mais aprendizado e experiência prática, explore os seguintes recursos:

  • Wormhole TypeScript SDK - O repositório do SDK Wormhole contém a implementação central dos layouts, incluindo itens de layout predefinidos e utilitários como serializeLayout e deserializeLayout.

  • Repositório de testes de layouts - Para experimentação prática, confira o repositório do pacote de layouts, que fornece exemplos e testes unitários para ajudar a entender melhor a serialização, desserialização e o mecanismo de tipagem forte. Executar esses testes localmente é uma ótima maneira de aprofundar seu entendimento de como os layouts funcionam em cenários do mundo real.

PreviousTypeScript SDKNextConstruindo Protocolos e Payloads

Last updated 4 months ago