The assertion is true, which is exactly why it's there. You use assert()
to declare things that you think will always hold. If they turn out to be false, you have a bug in your contract.
An assertion is not just a fancy if
. While it does perform a runtime check, it's also one of the ways to provide a target for formal verification. Tools such as the SMTChecker built into the Solidity compiler can detect bugs by trying to prove various statements about your code. The thing is - how can such a tool tell that the result you got is not the result you wanted? Documenting your assumptions with assertions is a very straightforward way to give the tool extra information needed to discern expected behavior from a buggy one.
Also, while the contract is simple now and it's easy to see that it won't fail, code does not stay simple forever. The condition is only true under the assumption that the contract has no other payable functions. Will you remember to revise this function every time when you add a payable function? What if the contract grows and the function is buried at the bottom of the file under several other functions? Most importantly - what about other people modifying the code in the future? Will they even notice this limitation? An assertion is a good way not to have to rely on anyone noticing this and turn it into an automated check.
Finally, the assertion is true but is it obvious? There are actually quite a few assumptions that go into it:
- Contract can receive ether only in a few specific ways:
- through a call of its payable function -
sendHalf()
is the only one here
- call of its
receive()
or fallback()
function - there are none
- being the recipient of
selfdestruct
of another contract
- being the recipient of the ether mined in a block
- The callee of
transfer()
cannot call back sendHalf()
because transfer()
forwards only 2300 gas and an external call costs more.
- The callee of
transfer()
cannot execute selfdestruct
because it costs 5000 gas.
- Reverts inside
transfer()
are not silenced in any way so even if the cost of selfdestruct
changed the future to be <= 2300 gas, issuing it would terminate the execution anyway.
- Transactions on Ethereum are only executed sequentially and mined ether cannot be transferred in the middle of a contract execution.
There are enough assumptions here that the author of the code might simply not have been 100% certain that he did not miss some obscure corner case that might turn into a security hole. An assertion can be a simple and effective way to definitively exclude such a possibility.