Análisis de smart contracts
07.08.2024

Comprender los errores comunes de los contratos inteligentes ERC-20 en Solidity

Los contratos inteligentes en el ecosistema de Ethereum a menudo contienen lógica compleja para asegurar la seguridad y funcionalidad de las aplicaciones descentralizadas. Sin embargo, pueden surgir errores debido a varias razones, lo que puede llevar a transacciones fallidas y vulnerabilidades potenciales. Esta entrada del blog explorará algunos errores comunes encontrados en transacciones de tokens ERC-20, sus causas y soluciones.

1. Pancake: K

El error ocurre si se viola el invariante 'el producto de las reservas debe ser constante'. Esta condición garantiza que después de que se ejecute el intercambio, el nuevo producto de las reservas (incluidas las comisiones) no será menor que el producto antiguo. Una violación de esta condición puede ocurrir si:

  1. Los tokens ingresados en el pool (amount0In o amount1In) no coinciden con los valores esperados según la fórmula del invariante. Hubo un error en el cálculo de los saldos después del intercambio.
  2. Alguien intentó manipular el pool, lo que causó que los saldos de tokens cambiaran, eludiendo el proceso estándar de intercambio.
  3. Si no se cumple esta condición, se genera el error 'Pancake: K', señalando una violación del invariante matemático.

Una violación del invariante significa que una de las condiciones fundamentales que aseguran el funcionamiento correcto del sistema ya no se cumple. En el contexto de intercambios descentralizados como PancakeSwap, el invariante generalmente se asocia con una ecuación matemática que debe mantenerse verdadera para mantener el equilibrio entre las reservas de tokens en el pool de liquidez.


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

Solución:
Verifique que el pool tenga suficiente liquidez para realizar el intercambio y que los valores no superen las reservas (_reserve0, _reserve1). Supervise continuamente las reservas del pool y tome medidas para reabastecerlas si es necesario.

2. TransferHelper: TRANSFER_FROM_FAILED

Este error indica una falla en la transferencia de tokens usando el método safeTransferFrom, que es un patrón común para transferir tokens ERC-20 de forma segura.


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. La dirección desde la cual se está transfiriendo (from) no ha otorgado o no ha otorgado suficientes permisos a la dirección msg.sender para transferir tokens (allowance, approve).
  2. La dirección desde la cual se está transfiriendo no tiene suficientes tokens para realizar la transferencia. Esto también puede deberse a que los administradores del token tienen la capacidad de modificar el saldo del token y, en el momento de la retirada, el usuario que realizó la inversión puede no tener suficiente saldo para completar la retirada.
  3. El contrato del token puede no implementar la función transferFrom o implementarla incorrectamente.
  4. El contrato del token puede contener verificaciones adicionales o restricciones en la función transferFrom, lo que puede causar que la transacción se cancele.
  5. Si no hay suficiente gas para ejecutar la transacción, la transacción puede fallar.

Solución:

  1. Asegúrese de que la dirección desde la cual se está transfiriendo haya otorgado suficiente permiso a msg.sender. Esto se puede hacer llamando a la función allowance del contrato del token.
  2. Asegúrese de que la dirección desde la cual se está transfiriendo tenga suficientes tokens para realizar la transferencia.
  3. Verifique que la dirección del contrato del token sea correcta y que el contrato cumpla con el estándar ERC-20.
  4. Verifique la implementación de la función transferFrom en el contrato del token. Asegúrese de que esté implementada correctamente y que no tenga restricciones adicionales que puedan causar un fallo.
  5. Intente aumentar el límite de gas al llamar a la transacción para asegurarse de que el problema no sea una falta de gas.

3. INSUFFICIENT_LIQUIDITY

Este error ocurre cuando se intenta retirar más liquidez de la disponible en las reservas de un pool de liquidez.


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

Causa:

  1. El pool de liquidez no contiene suficiente cantidad de uno o ambos tokens para cumplir con el intercambio solicitado.
  2. La cantidad solicitada de tokens para retirar (amount0Out o amount1Out) excede el número disponible de tokens en el pool.
  3. Desajuste entre las reservas en el contrato y el estado real del pool. Las reservas almacenadas en el contrato pueden no coincidir con el saldo real de tokens en el pool debido a errores o manipulación.

Solución:

  1. Verifique las reservas del pool y asegúrese de que sean suficientes para cumplir con el intercambio solicitado. Esto se puede hacer usando la función getReserves.
  2. Asegúrese de que los parámetros amount0Out y amount1Out sean correctos y no superen el número disponible de tokens en el pool.
  3. Asegúrese de que las reservas en el contrato coincidan con el saldo real de tokens. Para ello, puede agregar la verificación y actualización de reservas antes de realizar el intercambio.

INSUFFICIENT_LIQUIDITY

4. APPROVE_FAILED

El error `APPROVE_FAILED` ocurre durante la ejecución de la función `safeApprove`. Esta función está diseñada para establecer la cantidad de tokens que un propietario permite que un gastador use en su nombre.


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. El contrato del token podría no tener la función `approve` o podría estar implementada incorrectamente.
  2. Puede haber un problema con el estado del contrato del token, como saldo o permiso insuficiente.
  3. La función `approve` podría revertir por razones de seguridad implementadas en el contrato del token.

Solución:

  1. Asegúrese de que el contrato del token cumpla con el estándar ERC-20 e incluya una función `approve` correctamente implementada.
  2. Verifique el estado del contrato del token y asegúrese de que la cuenta tenga suficientes tokens para aprobar.
  3. Verifique que la dirección y la cantidad pasadas a la función `safeApprove` sean correctas.
  4. Depure más a fondo verificando el mensaje de error específico o la razón del revert en el contrato del token.

5. Fallo con el error 'ds-math-sub-underflow'

El error `ds-math-sub-underflow` se produce cuando una operación de resta subdesborda, es decir, cuando el resultado de la resta es menor que cero.


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

Causa:
Este error ocurre porque la operación de resta `x - y` resulta en un número negativo, lo cual no está permitido para enteros sin signo en Solidity.

Solución:

  1. Asegúrese de que el valor de `y` sea siempre menor o igual a `x` antes de realizar la resta.
  2. Implemente verificaciones en su código para manejar casos en los que `y` pueda ser mayor que `x` y tome las medidas adecuadas, como revertir la transacción o ajustar la lógica.

6. Error 'ERC20: la cantidad de transferencia excede el permiso'

El error `ERC20: la cantidad de transferencia excede el permiso` ocurre cuando se intenta transferir tokens en nombre de otro usuario, pero la cantidad transferida excede el límite de permiso que el propietario del token ha establecido para el gastador.

Causa:
Este error es lanzado por la función `transferFrom` del contrato ERC-20 cuando la cantidad a transferir es mayor que el límite permitido establecido por el propietario del token.

Solución:

  1. Asegúrese de que el propietario del token haya establecido un permiso adecuado para el gastador utilizando la función `approve`.
  2. Verifique el permiso actual antes de intentar la operación `transferFrom`.
  3. Si es necesario, pida al propietario del token que aumente el permiso llamando a la función `approve` con un valor mayor.

7. TRANSFER_FAILED

Este error ocurre cuando la transferencia de tokens de una dirección a otra falla. La función `_safeTransfer` asegura que la operación de transferencia sea exitosa y que los datos devueltos, si los hay, se decodifiquen en `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. La función `token.call` no se ejecuta con éxito (es decir, `success` es `false`).
  2. La llamada devuelve datos que no se decodifican en `true`, lo que indica un fallo en la función `transfer` del contrato del token.

Solución:

  1. Asegúrese de que el contrato del token cumpla con el estándar ERC-20, lo que incluye implementar correctamente la función `transfer`.
  2. Verifique los parámetros: Asegúrese de que la dirección `to` sea válida y que el `value` esté dentro de los límites permitidos, considerando el saldo del remitente y la lógica del contrato.
  3. Condiciones adicionales: Verifique si el contrato del token requiere condiciones adicionales como la preaprobación del monto de gasto (`approve` y `allowance`).

INSUFFICIENT_LIQUIDITY

8. INSUFFICIENT_OUTPUT_AMOUNT

Este error ocurre en un contexto de intercambio descentralizado cuando la cantidad de salida de un intercambio de tokens es menor que la cantidad mínima especificada por el usuario. Esto es una medida de seguridad para asegurar que los usuarios no reciban menos tokens de los esperados debido a deslizamientos o cambios de precio durante la transacción.


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

Causa:

  1. La volatilidad del mercado afecta los precios de los tokens entre el momento en que se inicia la transacción y cuando se ejecuta.
  2. Configuraciones de deslizamiento alto, que permiten desviaciones significativas en los resultados esperados.
  3. Liquidez insuficiente en el pool de negociación, causando impactos mayores en el precio.

Solución:

  1. Asegúrese de que el contrato del token cumpla con el estándar ERC-20, lo que incluye implementar correctamente la función `transfer`.
  2. Verifique los parámetros: Asegúrese de que la dirección `to` sea válida y que el `value` esté dentro de los límites permitidos, considerando el saldo del remitente y la lógica del contrato.
  3. Condiciones adicionales: Verifique si el contrato del token requiere condiciones adicionales como la preaprobación del monto de gasto (`approve` y `allowance`).

9. INSUFFICIENT_INPUT_AMOUNT

Este error ocurre cuando ninguna de las cantidades de entrada para un intercambio de tokens es mayor que cero. Asegura que al menos una de las cantidades de entrada (`amount0In` o `amount1In`) sea positiva para proceder con el intercambio.


    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 incorrectos para la función de intercambio.
  2. Fondos insuficientes en la cuenta del usuario.
  3. Errores en la lógica que calcula las cantidades de entrada.

Solución:

  1. Valide las cantidades de entrada: Asegúrese de que las cantidades de entrada estén correctamente establecidas antes de invocar la función de intercambio. Esto implica una validación adecuada de la entrada del usuario y la configuración de parámetros.
  2. Verifique los saldos del usuario: Asegúrese de que el usuario tenga suficiente saldo de los tokens destinados al intercambio. Esto se puede hacer llamando a la función `balanceOf` de los contratos de token respectivos.

10. Fallo

Causa:

  1. Una transacción no se puede ejecutar porque carece de suficiente gas para completar todas las operaciones.
  2. La lógica o condiciones incorrectas dentro de un contrato inteligente pueden causar que la ejecución falle (por ejemplo, una llamada a `require` o `assert` que falla).
  3. Intentar ejecutar una transacción de token o criptomoneda cuando el saldo de la cuenta es insuficiente.
  4. En el caso de trabajar con tokens estándar ERC-20 y ERC-721, la transacción puede fallar debido a permisos insuficientes.
  5. Una llamada a un contrato inteligente puede ser revertida debido a la falta de cumplimiento de las condiciones dentro de él (por ejemplo, utilizando la función `revert`).

Solución:

  1. Aumente el límite de gas al enviar la transacción.
  2. Verifique las condiciones y la lógica dentro del contrato inteligente.
  3. Asegúrese de que la cuenta tenga suficientes fondos para completar la transacción.
  4. Llame a la función `approve` con un valor suficiente antes de llamar a `transferFrom`.
  5. Verifique las condiciones que causan que la transacción sea revertida.
  6. Verifique su saldo para comisiones.

Conclusión

Comprender y resolver estos errores comunes en contratos inteligentes ERC-20 requiere un sólido conocimiento de la programación en Solidity, el estándar ERC-20 y el funcionamiento interno de los intercambios descentralizados. Al revisar cuidadosamente las causas e implementar las soluciones sugeridas, los desarrolladores pueden crear contratos inteligentes más robustos y fiables, asegurando una experiencia fluida para los usuarios en el ecosistema descentralizado.

All posts

Connect to a wallet

Metamask