Fallback function: Capturando llamadas perdidas


En Solidity, la función fallback es una función especial que permite a los contratos manejar situaciones en las que se reciben llamadas inesperadas o Ether sin datos asociados. Esta función actúa como una especie de “captura todo” para interacciones no previstas con el contrato.

Definición y Propósito

La función fallback es una función sin nombre que se declara de la siguiente manera:

fallback() external payable {
    // Lógica de la función
}

Esta función se ejecuta en dos escenarios principales:

  1. Llamadas a funciones inexistentes: Cuando una llamada al contrato no coincide con ninguna de las funciones definidas.

  2. Recepción de Ether sin datos: Si el contrato recibe Ether sin datos y no tiene definida una función receive, se invoca la función fallback.

Diferencia entre receive y fallback

Desde Solidity 0.6.0, se introdujo la función receive para manejar específicamente la recepción de Ether sin datos:

receive() external payable {
    // Lógica para recibir Ether
}

La diferencia clave es que la función receive se ejecuta únicamente cuando se envía Ether sin datos al contrato. Por otro lado, la función fallback se ejecuta cuando no hay coincidencia con ninguna función existente o cuando se envía Ether con datos que no coinciden con ninguna función.

Los proxies y la función fallback

La aplicación más utilizada de la función fallback es en los contratos proxy.

Estos contratos sirven como intermediarios entre los usuarios y la lógica de negocio de otro contrato (el contrato de implementación). Para lograr esto, los proxies utilizan la función fallback para redirigir llamadas desconocidas a la dirección de la implementación mediante la instrucción delegatecall. Esto permite que la lógica de negocio pueda actualizarse sin cambiar la dirección del contrato con el que interactúan los usuarios.

Consideraciones Importantes

  • Gas Limitado: En la mayoría de los casos, las llamadas que activan la función fallback reciben un límite de gas bajo (generalmente 2300 unidades de gas), como cuando son activadas por transfer o send. Sin embargo, cuando se ejecuta mediante call o delegatecall, el gas disponible no está restringido, lo que permite realizar operaciones complejas, como en los contratos proxy.

  • Seguridad: Es recomendable implementar medidas de seguridad dentro de la función fallback para prevenir comportamientos inesperados o ataques, como los de reentrada. Por ejemplo, si no se desea que el contrato reciba Ether directamente, se puede hacer que la función fallback revierta la transacción:

fallback() external {
    revert("El contrato no acepta Ether directamente");
}

Ejemplo Práctico

A continuación, un ejemplo de un contrato que utiliza tanto la función receive como la función fallback:

contract EjemploFallback {
    event LogFallback(address sender, uint amount, bytes data);
    event LogReceive(address sender, uint amount);

    // Función que se ejecuta al recibir Ether sin datos
    receive() external payable {
        emit LogReceive(msg.sender, msg.value);
    }

    // Función que se ejecuta al recibir llamadas 
    // que no coinciden con ninguna función
    fallback() external payable {
        emit LogFallback(msg.sender, msg.value, msg.data);
    }
}

En este contrato, la función receive se ejecuta cuando se envía Ether sin datos, mientras que la función fallback se activa para llamadas que no coinciden con ninguna función definida o cuando se envía Ether con datos no reconocidos. Ambas funciones emiten eventos que registran la información de la transacción.