1

I was building a withdrawal function for a contract that was using msg.sender.call{value: balance}, after checking that the given msg.sender was registered in the contract and had the given balance; however, I then read a couple of threads

msg.sender 'spoofing' when function is public or external

https://stackoverflow.com/questions/51847788/msg-sender-does-not-work-inside-a-view-function-why-is-there-a-workaround

that made me wonder whether this is safe. The answers to both of those questions indicate that msg.sender will only be validated for on-chain transactions — but how do I know if a given function will result in an on-chain transaction?

My understanding (see, e.g., the response here) was that no withdrawal functions are executed on-chain. So then how do we authorize users trying to withdraw from a contract?

Here is the relevant portion of my withdrawal function:

function withdraw() public {
    Person storage personData = _people[msg.sender];
    require(personData.personAddress == msg.sender, "Not a person in hash map");

    uint256 balance = personData.balance;
    personData.balance = 0;
    (bool success, ) = msg.sender.call{value: balance}("");
    require(success, "Withdrawal failed");
  }
1
  • see figure 5 of arxiv.org/pdf/2101.01917.pdf — as well as the sentence: "For instance, the function shown in Figure 5 violates NW [no writes after call], although it is not subject to reentrancy vulnerability. It is because the external call msg.sender.call has no dependency on numWithdraw." — The non-reentrancy vulnerability vs. NW distinction is not as important to me, here, as the fact that the withdraw call in figure 5 is deemed secure.
    – user90019
    Commented Feb 8, 2022 at 18:59

2 Answers 2

2

I think you've misunderstood what on-chain and off-chain means. When you make a transaction, that changes the state of the EVM. It could be balance, variables in a smart contract and such. Every writes operation to the state of a contract, is executed on-chain.

But since the blockchain's state is public, anyone can read it. You can check any addresses balance or values of variables in contracts. And nodes have a feature called call which lets you call any function of a smart contract. You can think of it like simulating a contract call. Since its a simulation, you can pass in any address as msg.sender. But call won't change the state of the blockchain.

To summarise, any function can be called off-chain to simulate a transaction.

1

First of all: your function is correct as you implemented it.

Now, back to the main issue: there are two types of functions:

  1. Those that change the state.
  2. Those that do not. They are marked as view or pure functions in Solidity.

So, the thing is that you can use any msg.sender you want when accessing a function in read mode, and that's perfectly fine because the blockchain is not being updated, and there's absolutely no risk to loose funds.

When accessing in write mode a function you must provide YOUR address along with the corresponding signed data, which is of course signed with YOUR private key that only you are supposed to posses.

BUT, and here's the thing: you can also access functions that change the state in read mode, of course using any msg.sender you want. What will happen in this case is that the node will run a simulation locally and get back the returned value of the function, if any, as if it would have been executed with that msg.sender. However, no state changes will be performed at all, making the call completely safe.

And what's the point of accessing a non-view function in read mode?

The answer: to obtain the returned value of the function and/or check it doesn't actually revert. When programming smart contracts, you can run a contract's function that modifies the state and get back the returned value. For instance: when performing a swap on Uniswap you get the final amount of tokens you just purchased. However, if you submit a transaction to the blockchain, you will see the state modified, but you won't get that number. With this method it's possible to know in advance what would be the expected amount of tokens you'll get out of the swap if the transaction would have been included as the first transaction of the next block. That of course doesn't guarantee you much, but it's an estimation that depending on the case might be useful.

Your Answer

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