Ir para o conteúdo

Incompatibilidades de casas decimais em oráculos

Uma incompatibilidade de casas decimais aplica a escala de unidade errada a uma resposta de oráculo, quantidade de token, taxa de wrapper ou valor do protocolo, causando erros de ordens de grandeza em empréstimos, emissões, resgates e liquidações.

Atualizado

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

Resposta direta

Uma incompatibilidade de casas decimais ocorre quando um contrato interpreta um inteiro bruto com o expoente de unidade, a direção do preço ou a escala interna errados. As casas decimais do feed, do token e do token de cotação, as escalas de taxa de wrappers ou cotas e as convenções WAD ou RAY do protocolo são independentes. Uma interface com aparência correta não prova que o contrato consumidor execute a mesma aritmética.

Para uma quantidade bruta do token-base A_raw com d_t casas e uma resposta positiva P_raw que cota um token-base em tokens de cotação com d_p casas, a quantidade bruta do token de cotação com d_q casas é V_raw = round(A_raw * P_raw * 10^d_q / 10^(d_t + d_p)). A fórmula só vale para essa direção e após validar feed, unidades, tipos e regra de arredondamento. Um adapter que já normalizou o valor não pode escalá-lo novamente.

No ERC-20, decimals() é metadado opcional de exibição, não garantia universal de 18 casas. O decimals() de um feed descreve sua resposta, não a escala do token nem do protocolo. A aritmética verificada pode interromper um overflow com revert, mas não detecta fórmula dimensionalmente errada e pode transformar um resultado representável em negação de serviço se um produto intermediário transbordar.

Como funciona

  1. Fixe rede, bloco, consumidor, adapter, proxy e agregador do feed, token ou vault, versões do compilador e da biblioteca matemática e estado do upgrade.
  2. Inventarie cada inteiro com tipo e unidade: quantidade bruta, casas do token, resposta assinada e casas do feed, orientação base/cotação, taxa de wrapper ou cota, escala interna e casas do token de saída.
  3. Valide a fonte antes da conversão: endereço e direção corretos, resposta positiva, timestamp e status, idade e faixa aceitáveis, semântica do fallback e regras de carência do sequenciador L2.
  4. Derive uma única fórmula dimensional das unidades brutas de entrada às de saída. Separe explicitamente aumento e redução de escala, limite cada expoente de dez e normalize cada parcela uma única vez.
  5. Use multiplicação-divisão de precisão total comprovada ou cancelamento limitado. Evite truncamento por dividir antes, overflow intermediário, wraparound unchecked e casts assinados ou estreitos inseguros.
  6. Defina arredondamento para baixo, para cima ou ao mais próximo em cada ação. Garantia, dívida, empréstimo, emissão, resgate, taxas, liquidação e conversão de cotas podem exigir direções conservadoras diferentes.
  7. Teste vetores conhecidos e propriedades com valores extremos, combinações de casas, recíprocos, feeds compostos, respostas zero, negativas e desatualizadas, upgrades e dust; compare onchain com modelo independente de alta precisão e limite a exposição.

Normalização é análise dimensional, não formatação. BTC/USD e USD/BTC exigem fórmulas recíprocas; trocar o rótulo não inverte o preço. Feeds compostos exigem escala e timestamp de cada parcela. A divisão inteira em Solidity trunca em direção a zero, portanto rearranjos algebricamente equivalentes podem produzir resultados onchain distintos. Um mulDiv de precisão total resolve o problema de largura intermediária, mas ainda exige numerador, denominador, unidades, limites e direção de arredondamento corretos.

Exemplos resolvidos

  • Oito contra dezoito casas. Um feed BTC/USD retorna 6,000,000,000,000 com d_p = 8, logo o preço é 60,000 USD/BTC. Tratar a resposta como 18 casas produz 0.000006 USD/BTC, subavaliação de 10^10, não 10^9. Escalar para WAD resulta em 6,000,000,000,000 * 10^(18 - 8) = 60,000,000,000,000,000,000,000.
  • Unidades de quantidade, preço e saída. A_raw = 2,500,000 representa 2.5 tokens com d_t = 6; P_raw = 200,000,000 representa 2 tokens de cotação com d_p = 8. Para valor interno de 18 casas, 2,500,000 * 200,000,000 * 10^18 / 10^(6 + 8) = 5,000,000,000,000,000,000, ou 5 quote tokens. Omitir o denominador do token superavalia a posição em 10^6.
  • Recíproco e truncamento. ETH/USD em WAD é 2,000 * 10^18. USD/ETH na mesma escala é floor(10^36 / (2,000 * 10^18)) = 500,000,000,000,000, ou 0.0005 ETH/USD. Separadamente, com A_raw = 999,999, d_t = 6 e preço 2 * 10^18, multiply-divide completo produz 1,999,998,000,000,000,000; dividir a quantidade por 10^6 primeiro produz 0 e perde todo o valor.
  • Overflow intermediário e arredondamento. Sejam x = 2^200, y = 2^100 e denominador 2^100. O resultado exato é 2^200, que cabe em uint256, mas x * y = 2^300 não cabe. A multiplicação verificada reverte e a unchecked faz wrap; mulDiv de precisão total retorna 2^200. A divisão inteira 5 / 2 arredonda para baixo em 2, enquanto teto retorna 3; o arredondamento integra o invariante econômico.

Riscos

  • Endereço errado de rede, feed, proxy, adapter, token, vault ou consumidor.
  • Orientação base/cotação invertida sem conversão recíproca.
  • Supor 18 casas para o token ou metadado opcional indisponível ou errado.
  • Supor 8 casas para o feed em vez de lê-las e fixá-las.
  • Confundir casas do token de cotação com escala WAD, RAY, de mercado ou contábil.
  • Omitir casas de wrapper, cota, índice ou taxa de câmbio.
  • Aplicar escala duas vezes após normalização pelo adapter.
  • Omitir fator de escala ou denominador necessário.
  • Converter resposta assinada para unsigned antes de validar positividade.
  • Aceitar resultado zero, desatualizado, incompleto, limitado ou inválido.
  • Expoente decimal ou potência de dez sofre underflow, overflow ou excede limites.
  • Produto intermediário transborda embora o quociente final caiba.
  • Aritmética unchecked, shifts ou casts estreitos fazem wrap ou truncam silenciosamente.
  • Dividir antes de multiplicar destrói precisão ou transforma dust em zero.
  • Arredondamento para baixo, cima ou próximo é inadequado para a ação.
  • Conversões repetidas acumulam perda ou vazamento sistemático de valor.
  • Preços, caps, proporções, percentuais, pontos-base, WAD e RAY são comparados em unidades diferentes.
  • Upgrades de feed, token, proxy, adapter ou vault invalidam premissas em cache.
  • Formatação de interface, wallet, RPC ou indexador esconde cálculo onchain diferente.
  • Avaliação errada amplifica empréstimos, emissões, resgates, liquidações, caps, dívida incobrável ou emissão injusta de cotas.

Equívocos comuns

  • “Todo token ERC-20 usa 18 casas decimais.” O método de metadados é opcional, e ativos implantados usam valores e comportamentos distintos.
  • “Todo feed de preço em USD usa 8 casas.” A precisão pertence à interface do deployment exato e deve ser lida e versionada.
  • “Um inteiro bruto grande prova manipulação.” A magnitude não significa nada sem unidades, direção, casas, timestamp e escala do consumidor.
  • “Solidity 0.8 torna a escala correta.” Overflow verificado pode reverter, mas não corrige unidades, truncamento, cast nem política de arredondamento.
  • “Multiplicar antes de dividir ou adicionar casas sempre melhora a precisão.” Pode transbordar, duplicar escala ou preservar a unidade errada; até aritmética de precisão total exige fórmula correta.

Tópicos relacionados

Fontes

Navegação

Pesquisar na wiki...