Análise de smart contracts
07.08.2024

Compreender os erros comuns dos contratos inteligentes ERC-20 no Solidity

Contratos inteligentes no ecossistema Ethereum frequentemente contêm lógica complexa para garantir a segurança e a funcionalidade de aplicações descentralizadas. No entanto, erros podem surgir devido a várias razões, levando a transações falhadas e potenciais vulnerabilidades. Este post do blog explorará alguns erros comuns encontrados em transações de tokens ERC-20, suas causas e soluções.

1. Pancake: K

O erro ocorre se a invariância ‘o produto das reservas deve ser constante’ for violada. Esta condição garante que, após a execução da troca, o novo produto das reservas (incluindo comissões) não será menor que o antigo produto. Uma violação desta condição pode ocorrer se:

  1. Os tokens inseridos na piscina (amount0In ou amount1In) não correspondem aos valores esperados de acordo com a fórmula da invariância. Houve um erro no cálculo dos saldos após a troca.
  2. Alguém tentou manipular a piscina, o que fez com que os saldos dos tokens mudassem, contornando o processo padrão de troca.
  3. Se essa condição não for atendida, o erro ‘Pancake: K’ é gerado, sinalizando uma violação da invariância matemática.

Uma violação da invariância significa que uma das condições fundamentais que garantem o funcionamento correto do sistema não é mais cumprida. No contexto de trocas descentralizadas como a PancakeSwap, a invariância geralmente está associada a uma equação matemática que deve permanecer verdadeira para manter o equilíbrio entre as reservas de tokens na piscina de liquidez.


require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(10000**2), 'Pancake: K');

Solução:
Verifique se a piscina tem liquidez suficiente para realizar a troca e se os valores não excedem as reservas (_reserve0, _reserve1). Monitore continuamente as reservas da piscina e tome medidas para reabastecê-las se necessário.

2. TransferHelper: TRANSFER_FROM_FAILED

Este erro significa uma falha na transferência de tokens usando o método safeTransferFrom, que é um padrão comum para transferir tokens ERC-20 com segurança.


function safeTransferFrom(address token, address from, address to, uint value) 
  internal {
    // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
    (bool success, bytes memory data) = 
token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
    require(success && (data.length == 0 || abi.decode(data, (bool))), 
'TransferHelper: TRANSFER_FROM_FAILED');
  }

Causa:

  1. O endereço de origem não concedeu ou não concedeu permissões suficientes para o endereço msg.sender transferir tokens (allowance, approve).
  2. O endereço de origem não possui tokens suficientes para realizar a transferência. Isso também pode ser devido ao fato de que os administradores do token têm a capacidade de modificar o saldo do token e, no momento da retirada, o usuário que fez o investimento pode não ter saldo suficiente para realizar a retirada.
  3. O contrato do token pode não implementar a função transferFrom ou implementá-la incorretamente.
  4. O contrato do token pode conter verificações ou restrições adicionais na função transferFrom, o que pode causar o cancelamento da transação.
  5. Se não houver gás suficiente para executar a transação, a transação pode falhar.

Solução:

  1. Certifique-se de que o endereço de origem concedeu uma allowance suficiente para o msg.sender. Isso pode ser feito chamando a função allowance do contrato do token.
  2. Certifique-se de que o endereço de origem tem tokens suficientes para realizar a transferência.
  3. Verifique se o endereço do contrato do token está correto e se o contrato é compatível com o ERC-20.
  4. Verifique a implementação da função transferFrom no contrato do token. Certifique-se de que está implementada corretamente e não possui restrições adicionais que possam causar uma falha.
  5. Tente aumentar o limite de gás ao chamar a transação para garantir que o problema não seja a falta de gás.

3. INSUFFICIENT_LIQUIDITY

Este erro ocorre ao tentar retirar mais liquidez do que está disponível nas reservas de uma piscina de liquidez.


(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
        require(amount0Out < _reserve0 && amount1Out < _reserve1, 'Pancake:
INSUFFICIENT_LIQUIDITY');

Causa:

  1. A piscina de liquidez não contém uma quantidade suficiente de um ou ambos os tokens para satisfazer a troca solicitada.
  2. A quantidade solicitada de tokens para retirada (amount0Out ou amount1Out) excede o número disponível de tokens na piscina.
  3. Desalinhamento entre as reservas no contrato e o estado real da piscina. As reservas armazenadas no contrato podem não corresponder ao estado real de saldo dos tokens na piscina devido a erros ou manipulações.

Solução:

  1. Verifique as reservas da piscina e certifique-se de que são suficientes para satisfazer a troca solicitada. Isso pode ser feito usando a função getReserves.
  2. Certifique-se de que os parâmetros amount0Out e amount1Out estão corretos e não excedem o número disponível de tokens na piscina.
  3. Certifique-se de que as reservas no contrato correspondem ao saldo real dos tokens. Para isso, você pode adicionar verificações e atualizações das reservas antes de realizar a troca.

INSUFFICIENT_LIQUIDITY

4. APPROVE_FAILED

O erro `APPROVE_FAILED` ocorre durante a execução da função `safeApprove`. Esta função é projetada para definir a quantidade de tokens que um proprietário permite que um gastador use em seu nome.


function safeApprove(address token, address to, uint value) internal {
  (bool success, bytes memory data) = 
token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
  require(success && (data.length == 0 || abi.decode(data, (bool))),
'TransferHelper: APPROVE_FAILED');
}

Causa:

  1. O contrato do token pode não ter a função `approve` ou ela pode estar implementada incorretamente.
  2. Pode haver um problema com o estado do contrato do token, como saldo ou allowance insuficientes.
  3. A função `approve` pode reverter por razões de segurança implementadas no contrato do token.

Solução:

  1. Certifique-se de que o contrato do token esteja conforme o padrão ERC-20 e inclua uma função `approve` corretamente implementada.
  2. Verifique o estado do contrato do token e certifique-se de que a conta tenha tokens suficientes para aprovar.
  3. Verifique se o endereço e a quantidade passados para a função `safeApprove` estão corretos.
  4. Depure mais verificando a mensagem de erro específica ou o motivo do revert no contrato do token.

5. Falha com erro 'ds-math-sub-underflow'

O erro `ds-math-sub-underflow` é gerado quando uma operação de subtração resulta em underflow, ou seja, quando o resultado da subtração é menor que zero.


    function sub(uint x, uint y) internal pure returns (uint z) {
      require((z = x - y) <= x, 'ds-math-sub-underflow');
    } 

Causa:
Este erro ocorre porque a operação de subtração `x - y` resulta em um número negativo, o que não é permitido para inteiros sem sinal em Solidity.

Solução:

  1. Certifique-se de que o valor de `y` seja sempre menor ou igual a `x` antes de realizar a subtração.
  2. Implemente verificações em seu código para lidar com casos onde `y` possa ser maior que `x` e tome as ações apropriadas, como reverter a transação ou ajustar a lógica.

6. Falha com erro 'ERC20: transfer amount exceeds allowance'

O erro `ERC20: transfer amount exceeds allowance` ocorre quando é feita uma tentativa de transferir tokens em nome de outro usuário, mas a quantidade a ser transferida excede o limite de allowance que o proprietário do token definiu para o gastador.

Causa:
Este erro é gerado pela função `transferFrom` do contrato ERC-20 quando a quantidade a ser transferida é maior que o limite permitido definido pelo proprietário do token.

Solução:

  1. Certifique-se de que o proprietário do token definiu um allowance adequado para o gastador usando a função `approve`.
  2. Verifique o allowance atual antes de tentar a operação `transferFrom`.
  3. Se necessário, peça ao proprietário do token para aumentar o allowance chamando a função `approve` com um valor maior.

7. TRANSFER_FAILED

Este erro ocorre quando a transferência de tokens de um endereço para outro falha. A função `_safeTransfer` garante que a operação de transferência seja bem-sucedida e que os dados retornados, se houver, sejam decodificados como `true`.


    `function _safeTransfer(address token, address to, uint value) private { (bool 
    success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to,
    value)); require(success && (data.length == 0 || abi.decode(data, (bool))),
    'Pancake: TRANSFER_FAILED'); }`

Causa:

  1. A função `token.call` não é executada com sucesso (ou seja, `success` é `false`).
  2. A chamada retorna dados que não decodificam para `true`, indicando uma falha na função `transfer` do contrato do token.

Solução:

  1. Garantir Conformidade com ERC-20: Verifique se o contrato do token adere ao padrão ERC-20, o que inclui implementar corretamente a função `transfer`.
  2. Parâmetros Correto: Certifique-se de que o endereço `to` é válido e que o `value` está dentro dos limites permitidos, considerando o saldo do remetente e a lógica do contrato.
  3. Condições Adicionais: Verifique se o contrato do token exige condições adicionais, como pré-aprovação do valor gasto (funções `approve` e `allowance`).

INSUFFICIENT_LIQUIDITY

8. INSUFFICIENT_OUTPUT_AMOUNT

Este erro ocorre em um contexto de troca descentralizada quando o valor de saída de uma troca de tokens é menor que a quantidade mínima especificada pelo usuário. Isso é uma proteção para garantir que os usuários não recebam menos tokens do que esperavam devido a slippage ou alterações de preço durante a transação.


    `require(amounts[amounts.length - 1] >= amountOutMin,
    'PancakeRouter: INSUFFICIENT_OUTPUT_AMOUNT');`

Causa:

  1. Volatilidade de mercado afetando os preços dos tokens entre o momento em que a transação é iniciada e quando é executada.
  2. Configurações de slippage altas, que permitem desvios significativos nos resultados esperados.
  3. Liquidez insuficiente na piscina de negociação, causando impactos maiores nos preços.

Solução:

  1. Garantir Conformidade com ERC-20: Verifique se o contrato do token adere ao padrão ERC-20, o que inclui implementar corretamente a função `transfer`.
  2. Parâmetros Correto: Certifique-se de que o endereço `to` é válido e que o `value` está dentro dos limites permitidos, considerando o saldo do remetente e a lógica do contrato.
  3. Condições Adicionais: Verifique se o contrato do token exige condições adicionais, como pré-aprovação do valor gasto (funções `approve` e `allowance`).

9. INSUFFICIENT_INPUT_AMOUNT

Este erro ocorre quando nenhum dos valores de entrada para uma troca de tokens é maior que zero. Garante que pelo menos um dos valores de entrada (`amount0In` ou `amount1In`) seja positivo para prosseguir com a troca.


    uint amount0In = ...; // Input amount of token0
    uint amount1In = ...; // Input amount of token1
    require(amount0In > 0 || amount1In > 0, 'Pancake: INSUFFICIENT_INPUT_AMOUNT');

Causa:

  1. Parâmetros de entrada incorretos para a função de troca.
  2. Fundos insuficientes na conta do usuário.
  3. Erros na lógica de cálculo das quantidades de entrada.

Solução:

  1. Validar Quantidades de Entrada: Certifique-se de que as quantidades de entrada estejam corretamente definidas antes de invocar a função de troca. Isso envolve validação adequada de entrada do usuário e definição de parâmetros.
  2. Verificar Saldos dos Usuários: Verifique se o usuário tem saldo suficiente dos tokens destinados à troca. Isso pode ser feito chamando a função `balanceOf` dos contratos de tokens respectivos.

10. Falha

Causa:

  1. Uma transação não pode ser executada porque falta gás suficiente para completar todas as operações.
  2. Lógica ou condições incorretas dentro de um contrato inteligente podem causar a falha na execução (por exemplo, uma chamada para require ou assert que falha).
  3. Tentativa de executar uma transação de token ou criptomoeda quando o saldo da conta é insuficiente.
  4. No caso de trabalhar com tokens dos padrões ERC-20 e ERC-721, a transação pode falhar devido a permissões insuficientes.
  5. Uma chamada a um contrato inteligente pode ser revertida devido à falha em cumprir as condições dentro dele (por exemplo, usando a função revert).

Solução:

  1. Aumente o limite de gás ao enviar a transação.
  2. Verifique as condições e a lógica dentro do contrato inteligente.
  3. Certifique-se de que a conta tem fundos suficientes para completar a transação.
  4. Chame a função approve com um valor suficiente antes de chamar transferFrom.
  5. Verifique as condições que causam a reversão da transação.
  6. Verifique seu saldo para comissões.

Conclusão

Compreender e resolver esses erros comuns em contratos inteligentes ERC-20 requer um sólido conhecimento da programação em Solidity, do padrão ERC-20 e do funcionamento interno das trocas descentralizadas. Ao revisar cuidadosamente as causas e implementar as soluções sugeridas, os desenvolvedores podem criar contratos inteligentes mais robustos e confiáveis, garantindo uma experiência contínua para os usuários no ecossistema descentralizado.

All posts

Connect to a wallet

Metamask