зачем израсходовать весь gasLimit, если использовать сборку и возврат вместе?

попробуйте вызвать токен erc20, transferFrom()используя сборку (чтобы сэкономить газ), такой код:

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

токен является стандартным токеном ERC20, который, когда какой-то транжира пытается позвонить transferFrom()без достаточного количества разрешений, в нашем случае вернется к этой строке:

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

resultбудет 0.

Меня удивляет то, что когда это произойдет, транзакция израсходует весь gasLimit. почему это?

Я пробовал несколько других случаев, ни один из них не израсходует газ:

  1. если я закомментирую весь блок ассемблерного кода или
  2. если я оставлю блок ассемблерного кода, но закомментирую: revert("TOKEN_TRANSFER_FROM_ERROR"), LineC
  3. если я закомментирую LineC, оставьте блок сборки и раскомментируйте LineB

Ответы (1)

Оказалось, что это вызвано испорченной памятью, если мы используем указатель свободной памяти: let ptr := mload(0x40)проблема сгоревшего газа исчезнет.

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