Introducción
El propósito de este artículo es describir el comportamiento de la función gasleft() de Solidity y sus usos.
Es una función incorporada que se utiliza para comprobar el gas restante durante la llamada a un contrato. Es una de las variables y funciones especiales que siempre existen en el espacio de nombres global y, por lo tanto, no necesita ser importada. gasleft() era conocida anteriormente como msg.gas antes de la versión 0.4.21 de Solidity (msg.gas).
Autoría
Este artículo fue coescrito por Jesse Raymond (LinkedIn, Twitter) como parte del RareSkills Technical Writing Program.
Por qué es importante gasleft()
La cantidad de gas utilizada por los contratos inteligentes depende de la complejidad del código que se ejecuta y de la cantidad de datos que se procesan durante la llamada al contrato.
Si el gas proporcionado no es suficiente, la transacción revierte con un error “out of gas”. El uso adecuado de la función gasleft() puede prevenir situaciones en las que las transacciones del contrato se queden sin gas. Veamos un ejemplo en la siguiente sección.
Ejemplo para prevenir el error out-of-gas
No quedarse sin gas al distribuir Ether
Enviar Ether a múltiples direcciones en contratos inteligentes a través de bucles puede ser muy costoso, especialmente cuando se trata de un array grande de direcciones.
Si la cantidad de gas utilizada para ejecutar la transacción no es suficiente, la función fallará con un error “out of gas”, como se indicó anteriormente.
Sin embargo, la función gasleft() se puede utilizar para asegurar que el gas restante sea suficiente para realizar la siguiente transferencia, y salir de forma anticipada en caso contrario.
El siguiente código demuestra esto:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract GasConsumer{
uint constant MINIMUM_AMOUNT = 10_000;
// this is for illustration purposes only, not production
function distributeEther(address[] calldata receivers) external {
for (uint i = 0; i < receivers.length; i++) {
payable(receivers[i]).transfer(1 ether);
if (gasleft() < MINIMUM_AMOUNT) {
return;
}
}
}
receive() external payable {}
}
La función “distributeEther” en el contrato anterior toma un array de direcciones, itera a través del array usando un bucle for y envía 1 Ether a cada dirección con la función “transfer”.
La declaración “if” comprueba si el gas restante después de cada transferencia de Ether en el bucle es suficiente para la siguiente transferencia verificando si el gas restante después de una transferencia es inferior a 10,000 (9,000 para transferir ETH y 1,000 adicionales para otros opcodes menores). Si es inferior, la transacción finaliza sin revertir las transferencias anteriores.
(Tenga en cuenta que no es una buena idea enviar Ether a menos que las direcciones sean de confianza. Un receptor malintencionado podría proporcionar una dirección de un contrato inteligente que revierte al recibir Ether).
Benchmarking de código
Uso de la función gasleft() para medir el coste de ejecución
Otro ejemplo es usar la función gasleft() para medir la cantidad total de gas utilizada por una sección de código.
Aquí hay un ejemplo en remix:

benchmark de código solidity con gasleft
En este caso, la función gasleft() se utiliza para averiguar cuánto gas se está utilizando cuando se añade un número al array “numArr” mediante la función “updateArray”. Esta no es la cantidad total de gas utilizada por la función, sino la cantidad de gas utilizada antes y después de que se añada un número al array en el código numArr.push(_num) en la línea 30.
Explicación de los números resaltados
Establecemos “gasUsed” como una variable pública, de modo que podamos ver fácilmente el contenido después de que se ejecute la función. Se realiza una prueba de esto desplegando el contrato “GasCalc” y llamando a la función “updateArray”.
La función devuelve una tupla, donde la primera entrada da como resultado 80,348, que es el “initialGas”.
La segunda entrada en la tupla es “finalGas” y fue de 35,923; esto incluye el coste de gas de la propia función gasleft().
Al restar el gas final del gas inicial, determinamos que el coste de ejecución de la línea 30 es de 44,425.
El opcode detrás de gasleft requiere “2 de gas” para ejecutarse
Solidity es un lenguaje de alto nivel que se compila en bytecodes que son ejecutados en la Ethereum Virtual Machine (EVM).
El opcode para la función gasleft() es GAS (bytecode 0x5A), que cuesta “2 de gas” según la documentación de ethereum.
Aplicaciones en el mundo real
Proxy de OpenZeppelin – usado para reenviar todo el gas al contrato de implementación
Uso de gasleft en yul
La función gasleft() también puede ser accedida en contratos inteligentes de Solidity a través de yul (inline assembly), como gas().
El contrato proxy de OpenZeppelin es un excelente ejemplo de cómo se hace esto. Se utiliza en la función “delegatecall” que el proxy utiliza para llamar al contrato de implementación. gas() es una forma conveniente de especificar el uso del máximo de gas disponible para esta operación.

reenviar todo el gas en delegate call con gasleft en Solidity
Enlace: OpenZeppelin Proxy Contract
Minimal Forwarder de OpenZeppelin – usado para validar que el relayer envió suficiente gas para ejecutar la transacción
Un “Relayer” es una entidad off-chain que paga por el gas de las transacciones de otro usuario y la transacción se envía a un contrato “Forwarder” que ejecuta la transacción.
Cuando un usuario envía una solicitud al relayer, el usuario especifica la cantidad de gas a incluir en la transacción y firma digitalmente su solicitud.
Sin embargo, el relayer podría no respetar el límite de gas solicitado por el usuario y enviar una cantidad menor. Este ataque está documentado en el SWC Registry como SWC-126.
Esto provoca un ataque de gas-griefing. Si la llamada del relayer al contrato forwarder tiene éxito, pero la subcall que el usuario desea realizar falla, entonces el relayer puede “culpar” al usuario de enviar una transacción que revierte, cuando la verdadera razón fue que la subcall se quedó sin gas debido a que el relayer no envió el suficiente.
Una subcall puede fallar debido a un revert o a un error de out-of-gas, pero generalmente no se proporciona el motivo del fallo, simplemente la variable booleana success devuelve false. Debido a esto, no sabemos si la subcall falló por gas insuficiente o por malas instrucciones del remitente original.
Podemos usar “gasleft” para determinar de qué caso se trata.
Cuando ocurre la ejecución de “call”, solo se reenvían 63/64 partes del gas. Este límite de 63/64 fue introducido en el EIP 150.
Después de que la subcall se completa, la cantidad de gas restante debería ser al menos 1/64 del límite original especificado por el usuario.
Si queda menos de 1/64 del gas solicitado después de la subcall, entonces sabemos que el relayer no envió todo el gas que se suponía que debía enviar.
El contrato forwarder aquí verifica si queda al menos 1/63 del límite original como margen de seguridad.
El código invalid se utiliza para provocar que la transacción del relayer falle, dejando en claro que la transacción falló debido al relayer, y no a la subcall. Puede leer más sobre el ataque de gas griefing aquí.

Enlace: OpenZeppelin Minimal Forwarder Contract
Contrato EthBalance Monitor de Chainlink – usado para prevenir que el error out-of-gas bloquee la distribución de Ether
Esta es una aplicación en la vida real del primer ejemplo de este artículo, donde distribuimos Ether en un bucle. Hay más lógica de negocio en este código en comparación con el anterior, pero si destacamos la comprobación de “gasleft()” que provoca una salida anticipada del bucle, vemos que fundamentalmente es el mismo diseño.

Contrato VRFCoordinatorV2 de Chainlink – usado para obtener la cantidad de gas utilizada en los cumplimientos del VRFCoordinatorV2 de Chainlink
El contrato inteligente “VRFCoordinatorV2” de Chainlink es un coordinador de “Función Aleatoria Verificable”, utilizado para generar criptográficamente números aleatorios seguros en la blockchain.
El contrato inteligente es el oráculo que permite a los contratos inteligentes solicitar y recibir aleatoriedad. (ver aquí: VRFCoordinatorV2).
La función gasleft() se utiliza en la función “calculatePaymentAmount” del contrato para cobrar al usuario una tarifa más alta si los nodos necesitan pagar más gas para cumplir con la aleatoriedad.
Cuanto más bajo sea gasleft() en esta circunstancia, mayor será la tarifa porque (startGas - gasleft()) aumenta a medida que se consume más gas.

Enlace: Chainlink VRFCoordinatorV2 Contract
Conclusión
En este artículo, hemos discutido varios casos de uso para gasleft(). Estos incluyen prevenir errores de out-of-gas, hacer benchmarking del coste de ejecución del código de Solidity, reenviar todo el gas a los contratos de implementación y prevenir ataques DOS a los relayers.
RareSkills Blockchain Bootcamp
Consulte nuestras ofertas de blockchain bootcamp avanzado para obtener más información sobre la formación para desarrolladores de nivel experto que ofrecemos.
Publicado originalmente el 4 de febrero de 2023