2

I'm looking through the EIP-4337 account abstract sample repository and am not sure what the purpose of (success); is in the following function.

    function _payPrefund(uint256 missingAccountFunds) internal virtual {
        if (missingAccountFunds != 0) {
            (bool success, ) = payable(msg.sender).call{
                value: missingAccountFunds,
                gas: type(uint256).max
            }("");
            (success);
            //ignore failure (its EntryPoint's job to verify, not account.)
        }
    }

If failures are ignored, why is this value loaded into memory at all? Does adding (success); have a gas implication here?

Source: https://github.com/eth-infinitism/account-abstraction/blob/dae9733bf78bcb7576f572d67497c2d241ae4da1/contracts/core/BaseAccount.sol#L123

Edit: A lot of comments/answers are stating the code should be checking the value of success but that is strictly against the responsibilities of the contract, as acknowledged by the comment below the statement. The calling contract (the EIP-4337 EntryPoint) is required to check success and doing it here would duplicate effort without value.

3 Answers 3

3

Because the call function returns a true or false value and It does not revert the function, so you have to control the result yourself so that there is no mistake in changing the state variable.

4
  • What does (success); actually do then?
    – Milk
    Commented Sep 7, 2023 at 0:20
  • It returns the call result, which is true or false. If it is true, the transfer was done correctly and you make the desired changes in the state variables. If it is false, it means that the money transfer was not done correctly.
    – Mehran Ra
    Commented Sep 7, 2023 at 6:32
  • That's what the value of success represents, but that doesn't explain what the line (success); is doing. The line is accessing the value of the variable but why... It's not using the value for assignment or evaluation or calculation so what's the point of that line?
    – Milk
    Commented Sep 10, 2023 at 20:25
  • Yes, you are right, in fact, this line is wrong and it should be checked with a require, so that if it is correct, the desired operation will be performed.
    – Mehran Ra
    Commented Sep 11, 2023 at 4:01
2

The line is there to suppress a compiler warning. Without it you get:

Warning (9302): Return value of low-level calls not used.

The line (success); indicates to the compiler that you are "using" the variable. However, the compiler will optimise this line out of the compiled code so it actually does nothing.

(Adding this as the accepted answer as the other answers do not answer the question. They explain what the variable success does and why you should check it's value. They do not explain what the line (success); does or why it is there.)

Another possible way to suppress this warning is by wrapping the call statement in brackets. See this issue comment for details or as below.

(payable(msg.sender).call{
    value: missingAccountFunds,
    gas: type(uint256).max
(""));
1

The .call() pattern has 2 return values:

  • a bool signaling if the call failed or succeeded
  • and bytes that is the data returned by the call (if any).

The success variable is generally used in conjonction with a require as such: require(success, "call failed");

In the function you forwarded as the comment says they do not ensure for success or failure, hence there is no require. I guess it's just a choice of writing. Since they are not using any return value they could have just wrote :

if (missingAccountFunds != 0) {
            payable(msg.sender).call{
                value: missingAccountFunds,
                gas: type(uint256).max
            }("");
        }

Therefore i will assume (without more info) that it is just ,as you seemed to notice yourself, a bad practice.

edit: Having read the example implementation I will actually assume that it is meant to be overriden.

  • As said in the comment the failure case should not be handled (so there isn't a need for a require )
  • You might want to add more logic for the success case.
4
  • Would like some social proof on this. Seems like an odd statement to leave in what is otherwise very clean code.
    – Milk
    Commented Sep 7, 2023 at 0:22
  • Having read the call I actually assume that it is meant to be overriden and as said in the comment the failure case should not be handled (so there isn't a need for a require ) but you might want to add logic for the success case.
    – Torof
    Commented Sep 7, 2023 at 8:34
  • I don't think it's meant to be override, the line below states "//ignore failure (its EntryPoint's job to verify, not account.)" The return value should be ignored.
    – Milk
    Commented Sep 10, 2023 at 20:20
  • I read the contract again. 100% meant to be overriden. First it is stated as a base implementation: * This contract provides the basic logic for implementing the IAccount interface and it is actually written in the NatSpec of the function: * SubClass MAY override this method for better funds management
    – Torof
    Commented Sep 10, 2023 at 23:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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