Ir para o conteúdo

Decodificação de calldata em uma carteira

Um guia orientado à verificação de calldata, palavras e offsets ABI, colisões de seletores, proxies, lotes, aprovações, permits com dados tipados, simulação e reconciliação pós-transação.

Atualizado

Somente para fins educacionais; não constitui recomendação nem aconselhamento de investimento. Investir pode causar perdas.

Resposta direta

Calldata é a sequência imutável de bytes fornecida como entrada para uma transação de nível superior do Ethereum ou uma chamada interna. Uma chamada convencional de função Solidity começa com um seletor de 4-byte, seguido por argumentos codificados em ABI, mas calldata não se descreve: os mesmos bytes podem ter significados diferentes para códigos em execução, implementações de proxy ou esquemas distintos. Funções fallback, assembly bruto e protocolos que não usam Solidity não precisam seguir a ABI convencional de funções.

Por isso, uma carteira deve mostrar mais do que um possível nome de função. Uma revisão segura vincula os bytes a chainId, um bloco, from, to, value nativo, codeHash em execução, implementação ativa e ABI confiável; decodifica estritamente cada chamada aninhada; distingue calldata on-chain de assinaturas EIP-712; simula em um estado explícito; e reconcilia o recibo e as mudanças de estado reais após a inclusão.

Decodificação de calldata
0 / 5
0 itens revisados; 5 itens ainda não resolvidos

A conclusão desta revisão não prova que um ativo, transação ou sistema seja seguro.

Como funciona

  1. Fixe o envelope de assinatura e o ponto de observação: chainId, número e hash do bloco, from, to, value nativo, bytes de entrada, nonce e campos de taxas. Preserve a fonte da carteira ou RPC; um payload decodificado em outra rede ou bloco não é a mesma afirmação.
  2. Classifique o objeto antes da decodificação. Transação, solicitação de dados tipados EIP-712, permit ERC-2612, UserOperation ERC-4337 e mensagem personal-sign bruta usam domínios e esquemas diferentes; não force todos a passar pela ABI de transações.
  3. Resolva o destino no bloco fixado. Leia o bytecode em execução e o codeHash; identifique proxy, beacon ou implementação quando aplicável; registre slots de implementação e administração; e obtenha uma ABI correspondente àquela versão exata do código. Um registro de seletores fornece candidatos, não autoridade.
  4. Decodifique estritamente. O seletor corresponde aos primeiros 4 bytes do Keccak-256 da assinatura canônica da função, excluindo tipos de retorno. Valores estáticos ocupam palavras de 32-byte; cabeçalhos dinâmicos contêm offsets medidos desde o bloco de argumentos posterior ao seletor. Rejeite dados truncados, offsets fora dos limites, comprimentos impossíveis, padding inválido e bytes finais sem explicação.
  5. Expanda recursivamente multicalls, calldata aninhada e execução delegada. Para cada chamada filha, liste destino, valor nativo, seletor, argumentos, tipo de chamada e eventual indicador allowFailure. Com delegatecall, o código da implementação roda no contexto de endereço, saldo e armazenamento do chamador, preservando msg.sender e msg.value.
  6. Construa registros separados de autoridade e valor e depois simule. Registre destinatários, spenders, operadores de NFT, unidades brutas de tokens, decimais, prazos, limites de slippage e valor nativo. Simule com bloco, remetente e valor exatos, mas trate o resultado como fotografia condicional, pois estado, preços, tempo, código e ordenação das transações podem mudar.
  7. Confirme cada campo relevante antes de assinar. Depois da inclusão, inspecione status do recibo, logs, traces disponíveis e variações de saldo, allowance e estado dos operadores; diferencie falhas filhas capturadas de sucesso no nível superior; contabilize gás mesmo em revert; aguarde a finalidade exigida; e pare em vez de assinar novamente uma falha sem explicação.

Exemplos resolvidos

  • Transferência ERC-20 estática. transfer(address,uint256) normalmente usa o seletor 0xa9059cbb. Um seletor e duas palavras ABI totalizam 4 + 2 * 32 = 68 bytes. Um valor bruto de 1,500,000 de um token cuja utilização de 6 decimals foi verificada independentemente aparece como 1.5 tokens. Os decimais são metadados externos do contrato, não estão codificados nesses argumentos, e o seletor sozinho não identifica exclusivamente o contrato ou a função.
  • Offset de bytes dinâmicos. Para f(address,bytes) com payload de 3-byte, o cabeçalho de duas palavras ocupa 64 bytes. O offset dinâmico é 0x40, medido desde o início do bloco de argumentos e sem contar o seletor. Sua cauda contém uma palavra de comprimento de 32-byte e uma palavra de dados com padding de 32-byte, portanto a calldata total é 4 + 64 + 32 + 32 = 132 bytes. Tratar o offset como absoluto desde o byte zero desloca o destino em quatro bytes.
  • O valor de um lote depende da implementação. Uma chamada externa leva 1.00 ETH; três chamadas filhas decodificadas solicitam explicitamente 0.20 ETH, 0.30 ETH e 0.10 ETH, totalizando 0.60 ETH. Os 0.40 ETH restantes podem ser devolvidos, retidos, encaminhados ou causar revert, conforme o código do lote. Se a terceira chamada falhar com allowFailure=true, as anteriores podem permanecer confirmadas; uma implementação atômica pode reverter tudo.
  • Um permit não é a calldata do relayer no momento da assinatura. Um proprietário com 1,000 USDC assina um permit ERC-2612 de 300 USDC no nonce 41. A assinatura isolada não altera saldo nem allowance. Depois que um relayer a envia com sucesso, o nonce passa a 42 e a allowance a 300; depois que o spender usa 180, o saldo é 820 e a allowance restante 120. Desconectar o site não revoga a permissão.

Riscos

  • Decodificar com rede, fork, block tag ou envelope de transação incorretos.
  • Assinar para domínio, endereço de destino ou destinatário falsificados.
  • Tratar um seletor de 4-byte como único, apesar de possíveis colisões.
  • Usar ABI presumida, obsoleta ou verificada incorretamente.
  • Confiar em um rótulo de fonte verificada sem comparar o codeHash atual.
  • Não perceber uma atualização de implementação, beacon ou administrador entre revisão e execução.
  • Ignorar uma função do proxy cujo seletor colide com a implementação.
  • Esquecer que delegatecall grava no contexto de armazenamento do chamador.
  • Aceitar offsets, comprimentos, padding ou bytes finais dinâmicos malformados.
  • Não expandir um lote aninhado que oculta destinos, valores ou permissões.
  • Presumir atomicidade quando a implementação captura ou permite falhas filhas.
  • Ignorar o value nativo de nível superior porque os argumentos de token parecem inofensivos.
  • Aplicar decimais errados ou presumir que tokens com taxa de transferência e rebasing são ERC-20 padrão.
  • Conceder allowance ERC-20 ilimitada ou tratar incorretamente a corrida na atualização da allowance.
  • Ignorar o alcance sobre toda a coleção de setApprovalForAll para NFT.
  • Confundir dados tipados EIP-712 ou permit ERC-2612 com calldata de transação.
  • Não verificar nonce, prazo, contrato verificador, domínio da rede ou limites de replay.
  • Tratar a simulação como estável apesar de mudanças de oráculo, timestamp, estado pendente, MEV ou código.
  • Tratar status do recibo, logs ou traces do provedor como prova completa do estado econômico.
  • Assinar novamente às cegas em uma UI comprometida ou ignorar riscos de inclusão, reorganização e finalidade.

Equívocos comuns

  • Um seletor de função identifica de forma única o que o contrato executará.
  • Um resumo verificado do frontend é idêntico aos bytes e à implementação atual assinados.
  • Uma transação com value=0 não pode mover tokens, NFTs ou ativos delegados.
  • Simulação bem-sucedida ou recibo de sucesso provam segurança e o resultado econômico pretendido.
  • Desconectar uma dapp revoga aprovações, permits e permissões de operadores de NFT.

Tópicos relacionados

Fontes

Navegação

Pesquisar na wiki...