¿Por qué usar todo el límite de gas cuando se usa el ensamblaje y se revierten juntos?

intente llamar al token erc20 transferFrom()usando el ensamblaje (para ahorrar algo de gasolina), codifique como este:

pragma solidity ^0.4.24;

contract TestAssemblyAndRevert {
    function test(address from, address to, uint256 value) public {
        // a standard erc20 token
        address token = 0xedc2d4aca4f9b6a23904fbb0e513ea0668737643;

        // call transferFrom() of token using assembly
        assembly { // LineA
            // keccak256('transferFrom(address,address,uint256)') & 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000
            mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)

            // calldatacopy(t, f, s) copy s bytes from calldata at position f to mem at position t
            // copy from, to, value from calldata to memory
            calldatacopy(4, 4, 96)

            // call ERC20 Token contract transferFrom function
            let result := call(gas, token, 0, 0, 100, 0, 32)

            if eq(result, 1) {
                return(0, 0)
            }

            //revert(0, 0); // LineB
        }

        revert("TOKEN_TRANSFER_FROM_ERROR"); // LineC
    }
}

el token es un token ERC20 estándar que cuando algún gastador intenta llamar transferFrom()sin suficiente asignación, revertirá, en nuestro caso, esta línea:

let result := call(gas, token, 0, 0, 100, 0, 32)

resultserá 0.

Lo que me sorprende es que cuando esto sucede, la transacción consumirá todo el gasLimit. ¿porqué es eso?

Probé varios otros casos, ninguno de ellos consumirá el gas:

  1. si comento todo el bloque de código ensamblador, o
  2. si mantengo el bloque de código ensamblador, pero comento: revert("TOKEN_TRANSFER_FROM_ERROR"), LineC
  3. si comento LineC, mantengo el bloque de ensamblaje y descomento LineB

Respuestas (1)

Resultó que es causado por estropear la memoria, si usamos el puntero de memoria libre: let ptr := mload(0x40), el problema de quemar todo el gas desaparecerá.

pragma solidity ^0.4.24;

contract TestAssemblyAndRevert {
    function test(address from, address to, uint256 value) public {
        // a standard erc20 token
        address token = 0xedc2d4aca4f9b6a23904fbb0e513ea0668737643;

        // call transferFrom() of token using assembly
        assembly {
            let ptr := mload(0x40)

            // keccak256('transferFrom(address,address,uint256)') & 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000
            mstore(ptr, 0x23b872dd00000000000000000000000000000000000000000000000000000000)

            // calldatacopy(t, f, s) copy s bytes from calldata at position f to mem at position t
            // copy from, to, value from calldata to memory
            calldatacopy(add(ptr, 4), 4, 96)

            // call ERC20 Token contract transferFrom function
            let result := call(gas, token, 0, ptr, 100, ptr, 32)

            if eq(result, 1) {
                return(0, 0)
            }
        }

        revert("TOKEN_TRANSFER_FROM_ERROR");
    }
}