Diferencias entre require y assert en Solidity


En Solidity, las funciones require, assert son esenciales para manejar errores y asegurar que los contratos inteligentes funcionen de manera segura y confiable. Estas funciones fueron introducidas en la versión 0.4.10 de Solidity y están diseñadas para verificar condiciones específicas en tu código, lanzando excepciones si esas condiciones no se cumplen.

¿Qué es assert?

assert es una función en Solidity que se utiliza para verificar invariantes internas dentro de un contrato inteligente.

Su propósito es asegurar que ciertas condiciones, que no deberían fallar nunca si el código está bien implementado, se mantengan durante la ejecución del contrato. Si una condición evaluada con assert es falsa, el contrato lanza una excepción de tipo Panic(uint256), consume todo el gas restante y revierte todos los cambios realizados en el estado del contrato.

Uso típico de assert:

  • Verificar errores internos en la lógica del contrato.
  • Asegurar que se mantengan invariantes del contrato, como que un saldo nunca sea negativo.
  • Detectar bugs en el código que no deberían ocurrir bajo ninguna circunstancia.
function transfer(address recipient, uint amount) public {
    uint senderBalance = balances[msg.sender];
    uint recipientBalance = balances[recipient];
    
    // Realiza la transferencia
    balances[msg.sender] -= amount;
    balances[recipient] += amount;

    // Verifica que el saldo del remitente no sea negativo
    assert(balances[msg.sender] >= 0); 

    // Verifica que el balance total no haya cambiado
    assert(balances[msg.sender] + balances[recipient] == senderBalance + recipientBalance);
}

Después de realizar la transferencia, assert(balances[msg.sender] >= 0) se encarga para verificar que el saldo del remitente no sea negativo. Esta comprobación asegura que el saldo del remitente nunca sea negativo. Si esta condición no se cumple, significa que existe un error en la lógica interna del contrato que debe ser corregido para mantener la integridad del estado.

Además, assert(balances[msg.sender] + balances[recipient] == senderBalance + recipientBalance) verifica que la suma total de los balances del remitente y del destinatario después de la transferencia coincida con la suma total de sus balances antes de la transferencia. Garantizando que la transferencia se ha realizado correctamente sin alterar de manera inesperada el balance total.

¿Qué es require?

require es una función en Solidity que se utiliza para validar condiciones externas e inputs antes de ejecutar el código de un contrato. Si la condición evaluada por require es falsa, la ejecución se revierte y se puede proporcionar un mensaje de error personalizado. Además, require reembolsa el gas no utilizado al usuario, lo que lo hace más eficiente para validar entradas y asegurar que ciertas condiciones previas se cumplan antes de proceder con operaciones críticas.

Uso típico de require:

  • Validar parámetros de entrada a funciones.
  • Asegurar que las condiciones previas a la ejecución de una función sean correctas.
  • Verificar el resultado de llamadas a funciones externas.
function transfer(address recipient, uint amount) public {
 require(amount > 0, "Amount must be greater than 0");
 uint senderBalance = balances[msg.sender];
 require(senderBalance >= amount, "Insufficient balance");
 balances[msg.sender] -= amount;
 balances[recipient] += amount;
}

Aquí, require asegura que la cantidad a transferir sea mayor que 0. Si no se cumple esta condición, la transacción se revierte con el mensaje “Amount must be greater than 0”.

📰

A partir de Solidity v0.8.26, la función require también soporta custom errors, permitiendo revertir con un error personalizado en lugar de un string.

error InvalidAmount();
error InsufficientBalance(uint256 balance);

function transfer(address recipient, uint amount) public {
 require(amount > 0,InvalidAmount());
 uint senderBalance = balances[msg.sender];
 require(senderBalance >= amount, InsufficientBalance(senderBalance));
 balances[msg.sender] -= amount;
 balances[recipient] += amount;
}

Diferencias Principales

  • Propósito:
    • assert: Se utiliza para detectar errores internos y bugs en el contrato que no deberían ocurrir nunca.
    • require: Se emplea para validar condiciones externas, entradas de usuario y asegurar que se cumplan ciertas condiciones antes de la ejecución del código.
  • Comportamiento en Caso de Error:
    • assert: Lanza una excepción de tipo Panic(uint256), consume todo el gas restante y revierte los cambios en el contrato.
    • require: Lanza un error de tipo Error(string), revierte los cambios y reembolsa el gas no utilizado al usuario.
  • Casos de Uso:
    • assert: Verificar invariantes internas y errores lógicos que no deberían ocurrir.
    • require: Validar entradas y asegurar condiciones previas a la ejecución de funciones.

Conclusión

En resumen, tanto assert como require juegan papeles cruciales en la robustez y seguridad de los contratos inteligentes en Solidity, pero su aplicación es distinta.

assert debe reservarse para asegurar que la lógica interna del contrato funcione correctamente y detectar posibles bugs que no deberían suceder.

Por otro lado, require es más adecuado para validar entradas externas y asegurar que se cumplan las condiciones necesarias antes de proceder con la ejecución del contrato.

Usar estas funciones correctamente permite prevenir errores, ahorrar gas y garantizar que el contrato se comporte como se espera.