This piece of code is generated for a contract with a non-payable constructor to ensure that it is called with value 0, otherwise REVERT 0 0
. It originates in ContractCompiler::appendCallValueCheck
which generates the CALLVALUE
and then does CompilerContext::appendInlineAssembly("{ if condition { revert(0, 0) } }", {"condition"});
CompilerContext::appendInlineAssembly
is quite complex (barely readable, frankly) but long story short, "condition"
here refers to the value on top of the stack, and since the stack machine must consume the value in order to test it, as a courtesy to other code generators, appendInlineAssembly duplicates it before testing it so that it leaves the state of the stack unchanged. Which is indeed not necessary here since no following code actually reads the value, but it is basically a convenience to keep the compiler code as generic as possible (taking any assembly you can throw at it and spitting out machine code is about as generic as it gets).
But you are right, in this case it doesn't really accomplish anything, and it and the redundant POP
could be optimized out for a savings of 6 gas on running the constructor and 16 deployment gas (or indeed if you really decided you wanted a copy of the call value on the stack you could simply CALLVALUE
again at any time for only 2 gas!). If you write everything in assembly there are quite a few optimizations like this you can make, but in most cases they're premature optimizations and you can save more gas by writing better Solidity.
I rewrote one 80-line Solidity contract that cost 151,000 deployment gas into assembly with Yul and got it down to 101,000 gas (about 1100 bytes down to 300). It was obviously pretty simple, but had a lot of operations on large arrays from calldata, and since all array accesses are bounds checked in Solidity even if it is impossible for them to be out of bounds, rewriting without the checks and optimizing the calldata to memory copying saved a huge amount of boilerplate utility code that took half the generated code (run solc
with the --asm
flag and that is all the #utility.yul
stuff at the end). In the big picture one DUP
and a POP
aren't too big a deal! (Also obviously there are extreme security and correctness implications involved with bypassing the compiler's safety checks which are indeed there for a reason. Not recommended on production code for anyone without extensive experience writing stack machine assembly and EVM code in general, or maybe not anyone at all...but certainly fun to play around with on the testnets!)
006 DUP1
might be a compiler issue or non-optimized byte code.