[ad_1]
The solidity compiler isn’t very efficient. Often for elementary operations, the Solidity compiler will add a ton of unnecessary opcodes. The compiler does this because it adds a lot of checks if the developer didn’t screw up, and if they did, the compiler would revert before doing any damage.
This is great when you start with solidity and just want to write something that works. But since gas on Ethereum can get very expensive, sometimes we need to optimize the contract as much as possible.
You can use inline assembly, but the solidity compiler still adds many checks and unnecessary opcodes that just cost more gas.
You can also use language like Huff, which is excellent. It gives you access to the stack, and you can do any optimization you want. But writing a complex contract in Huff is torture since you have to go opcode by opcode.
The best option, in my opinion, and the topic of this article, is to write the contract in solidity, leave out the functions that need more optimization, and just add the bytecode with verbatim later.
What is Verbatim?
Per the Solidity docs:
The set of verbatim… builtin functions lets you create bytecode for opcodes that are not known to the Yul compiler. It also allows you to create bytecode sequences that will not be modified by the optimizer.
In short, it will allow us to add our own bytecode to an already compiled contract. But there is a small catch. Verbatim doesn’t work in solidity, so we will first have to compile our contract down to YUL and then add the bytecode with verbatim.
How to generate the optimized bytecode?
Some tools let you do this, but they are pretty complicated, so I wrote a simple compiler that will generate the bytecode for you.
- Go to this Repo and clone it to your machine
git clone https://github.com/Kuly14/mnemonic.git
2. Cd into the folder
cd mnemonic
3. Create your .mn file like this
touch yourName.mn
4. Write your function in pure opcodes
Normally if you wrote in pure opcodes it should look something like this:
push1 0x04 calldataload
push1 0x00 mstore
push1 0x20
push1 0x00
return
Which would be annoying and time-consuming. So I added it to the compiler that the push opcodes are automatically added when you use 0x:
0x04 calldataload
0x00 mstore
0x20 0x00
return
So if you’ll try to use the push opcodes it won’t compile. Just write they bytes you want to push on stack with 0x.
5. Compile the .mn file with the compiler
cargo run -- /path/to/your/file/nameOfYourFile.mn
This will compile the contract and will save the bytecode to bytecode.txt
If you want the compiler to print the bytecode to the terminal use -p
flag like this:
cargo run -- /path/to/your/file/nameOfYourFile.mn -p
6. Create your solidity contract and leave the function you want to write in opcodes empty
function show() public view returns(uint) {}
7. Compile your solidity file to YUL with solc
solc nameOfYourContract.sol --ir > nameOfYourNewYulFile.yul
This will compile the contract to YUL and will save it in nameOfYourNewYulFile.yul
8. Open the YUL file with your favorite editor and delete the first line
When you first open the file, the first line should look like this
IR:
Delete this line.
9. Find the function you left empty
Just ctrl F and write the whole function
ctrl F — function show() public view returns(uint)
There you should see something like this
10. Copy the bytecode from your bytecode.txt file and add it to the YUL file like this:
If you look at the verbatim statement after the verbatim, there is _0i_0o. This means that we will take 0 items of the stack into the verbatim and return 0 items to the stack from the verbatim.
This can come in handy if you just need to optimize a part of a function and you want to take items off the stack into the verbatim.
But that can get really complex really quickly. You would have to go through the function with the debugger and figure out what is on the stack at that particular moment.
If you don’t know how to use the remix debugger, I wrote an article that you can check out here: https://medium.com/@kulman.david/how-to-use-remix-debugger-to-learn-solidity-assembly-5a745a22bb07
11. Compile the YUL contract to bytecode
solc --strict-assembly nameOfYourYulFile.yul --bin
This will output the bytecode of the entire contract. Copy it and move on to the next step.
12. Deploy the bytecode
Now that we have the bytecode, we can deploy the contract.
The best way to do this is with hardhat. Go to this repo: https://github.com/Kuly14/deploy-bytecode and clone it to your machine:
git clone https://github.com/Kuly14/deploy-bytecode.git
12.1 Install dependencies:
Install all the necessary dependencies that we will need to deploy the contract:
yarn
Add your solidity contract to src folder , this is important so hardhat can generate the ABI.
12.2 Compile the contracts:
yarn hardhat compile
This command will generate the ABI inside the artifacts folder.
Now go to `test/unit/Deploy2.test.ts`
12.3 Import the ABI
import * as abi from ../../artifacts/path/to/your/abi.json
And also, change the `const bytecode = “0x6000…”` to your bytecode.
Don’t forget to add `0x` at the start of the bytecode, or it won’t work.
Now inside the `it` block, write your own tests as you can see I did here: https://github.com/Kuly14/deploy-bytecode/blob/main/test/unit/Deploy2.test.ts
12.4 Run your tests:
yarn hardhat test test/unit/Deploy2.test.ts
Congrats!!!
Here are links to both repos we used in this article:
If you get lost don’t hesitate to raise an issue or just leave a comment in this article. Thanks for reading!
CAUTION
This is very risky to do. We are messing with the stack manually which can create many attack vectors and make the contracts vulnerable. This is pretty much just for fun. Unless absolutely necessary, I would never use this in actual main-net code.
New to trading? Try crypto trading bots or copy trading
[ad_2]
Source link