1

try to call erc20 token's transferFrom() using assembly(to save some gas), code like this:

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
    }
}

the token is a standard ERC20 token that when some spender trying to call transferFrom() without enough allowance, it will revert, in our case, this line:

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

result will be 0.

The thing surprise me is that when this happens, the transaction will use up all the gasLimit. why is that?

I tried several other cases, none of them will use up the gas:

  1. if I comment out the whole assembly code block, or
  2. if I keep the assembly code block, but comment out: revert("TOKEN_TRANSFER_FROM_ERROR"), LineC
  3. if I comment out LineC, keep the assembly block and uncomment LineB
0

Turned out that its caused by messing up the memory, if we use the free memory pointer: let ptr := mload(0x40), the burn-all-gas problem will disappear.

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");
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.