2

Beginner question:

In one or two smart contracts samples, I've seen that a function is payable but the smart contract seems to hold its own state on the total it has received, e.g. total += msg.value.

I'm trying to learn and build a mental model of what is happening. In my mind, the blockchain has its native currency which provides the means for a payable function, which I assume is the transfer of value from an address like a wallet to another like a contract.

The blockchain holds the facts of these transfers. So why would a smart contract keep track of how much has been received?

This duplicates state and is WET code (latent bugs); I assume there's no built-in function to ask the blockchain what the balance is for a particular contract, else why would one track it and open up the chance of programmer error?

I'd be grateful if someone could explain what I've misunderstood, thank you.

1 Answer 1

4

TL;DR You could have a variable in your contract that keeps track of how much ether it has received in total through a function, if you're for example managing a payment channel and want to keep historical data in your contracts state, but you should never assume it's gonna be actually equal to your contracts balance, and favor using the native way of getting an the balance of an address instead

I assume there's no built-in function to ask the blockchain what the balance is for a particular contract

There is one, actually. It's address().balance in solidity, and more generally the BALANCE (0x31) or SELFBALANCE (0x47) opcodes.

You're not providing context, but maybe those smart contracts can have ether transferred out of them, at some point, and still want to keep track of the total amount they received regardless of what they had transferred out for, some reason? Now, you're perfectly right that maintaining a storage variable that is supposed to track the contracts ether balance shouldnt be done.That's because, as you mentionned it's storage duplication, of course, but mainly because it's dangerous to assume that that variable is always gonna be equal to the contracts balance, because it's possible to send ether to a smart contract without calling it to through the SELFDESTRUCT (that is to be deleted on ETH mainnet in an upcoming fork, but still) opcode, that deletes a smart contracts code and send all its balance to another address, without actually calling it. Let's take those 2 smart contracts.

contract VulnerableBank {
  uint balance;
  // Sending Ether to the contract will trigger that function
  receive() external payable {
    balance += msg.value;
  }
  function withdraw() external {
    // Left is our own variable that keeps track of the contracts balance, right is the native way of doing it
    require(balance == address(this).balance) 
    payable(msg.sender).transfer(balance);
    balance = 0;
  }
}

contract Griefer {
  function grief(address payable to) external payable {
    selfdestruct(to);
  }
}

If grief() is called with the VulnerableBank contract address as input, and Griefer's balance != 0, we're going to break the sync between VulnerableBank's balance variable and its actual balance, effectively bricking the contract (by making the withdraw() function uncallable)

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.