Very great article, and very easy to follow along, despite how low level and in-depth the EVM opcodes are.
I just noticed a small typo:
"There we have it, after this opcode executes youβll be taken to the location of the store(uint156) bytecode and the execution of the function will continue as normal."
Where it should be 'store(uint256)' :)
Can I also ask which tool you used to create the green screenshot of the stack? :)
I started learning Solidity, to get into blockchain security but I had so many questions of how the code work's at a fundamental EVM level, this is the real deal. Only other thing that would have blown my mind is if you explained how each function() is a unit of energy, interacting at energy level with the EVM (but that's matrix stuff) :)
Hi. This is a great article! Thank you! Anyway, can I ask why do they need to go through the trouble of `PUSH4 0x2e64cec1 -> EQ -> PUSH2 0x003b -> JUMPI` to push the `retrieve()` (`0x2e64cec1`) function when it is clearly not needed/executed (after all, it is `store(uint256)` that is being called)?
That's a great question. It does not appear obvious in the first place, but here is the answer.
The reason why the `retrieve()` function selector comes first (and is compared first) is because of the way the selectors are stored in the smart contract bytecode.
Basically, the list of selectors in this "sequence of if / else opcodes" (I like to call that the 'dispatcher' or 'function dispatcher').
The order of the functions selectors depends on their "decimal value". They are listed from the "lowest" to the "highest" number.
- store(uint256) -> selector = 0x6057361d (in hex) = 1,616,328,221 (in decimal)
- retrieve() -> selector = 0x2e64cec1 (in hex) = 778,358,465 (in decimal)
(in hex) 0x2e64cec1 < 0x6057361d
(in decimal) 1,616,328,221 < 778,358,465
So in summary, because 778 millions (the decimal number representation of the retrieve()) is a lower number compared to 1,616 billion (the decimal number representation of the store(uint256) selector), the retrieve() will come first/before in the dispatcher list.
so we can be more careful with our function naming and arguments for gas optimizations? so no unneeded checks be executed for functions that are called not so often?
Top notch content
Very great article, and very easy to follow along, despite how low level and in-depth the EVM opcodes are.
I just noticed a small typo:
"There we have it, after this opcode executes youβll be taken to the location of the store(uint156) bytecode and the execution of the function will continue as normal."
Where it should be 'store(uint256)' :)
Can I also ask which tool you used to create the green screenshot of the stack? :)
Thanks for the great answer below, the green sreenshots are made in Figma
Finally had a chance to read this. Great stuff!!
Still the best EVM deep dive.
Interesting
Where are you? I dont see your twitter account..
Thank you so much for this, awesome article!!
Reminds me of my college hard days. Thank you
I started learning Solidity, to get into blockchain security but I had so many questions of how the code work's at a fundamental EVM level, this is the real deal. Only other thing that would have blown my mind is if you explained how each function() is a unit of energy, interacting at energy level with the EVM (but that's matrix stuff) :)
Hi. This is a great article! Thank you! Anyway, can I ask why do they need to go through the trouble of `PUSH4 0x2e64cec1 -> EQ -> PUSH2 0x003b -> JUMPI` to push the `retrieve()` (`0x2e64cec1`) function when it is clearly not needed/executed (after all, it is `store(uint256)` that is being called)?
@Jin Xing Lim
That's a great question. It does not appear obvious in the first place, but here is the answer.
The reason why the `retrieve()` function selector comes first (and is compared first) is because of the way the selectors are stored in the smart contract bytecode.
Basically, the list of selectors in this "sequence of if / else opcodes" (I like to call that the 'dispatcher' or 'function dispatcher').
The order of the functions selectors depends on their "decimal value". They are listed from the "lowest" to the "highest" number.
- store(uint256) -> selector = 0x6057361d (in hex) = 1,616,328,221 (in decimal)
- retrieve() -> selector = 0x2e64cec1 (in hex) = 778,358,465 (in decimal)
(in hex) 0x2e64cec1 < 0x6057361d
(in decimal) 1,616,328,221 < 778,358,465
So in summary, because 778 millions (the decimal number representation of the retrieve()) is a lower number compared to 1,616 billion (the decimal number representation of the store(uint256) selector), the retrieve() will come first/before in the dispatcher list.
@CJ42
Thank you for the great answer! Learnt something new today thanks to you!
so we can be more careful with our function naming and arguments for gas optimizations? so no unneeded checks be executed for functions that are called not so often?