There have been many blockchain projects where the answer to ‘could it be done’ is yes, and ‘should it be done’, is no… this is another one. There isn’t much point to write your name directly onchain. But there are cases where writing exact bytecode to an address is more than just an exercise in understanding how contracts are deployed at a low level.
Background
This article came about while integrating a very cool library from Ledger into our contracts at Forum. The details of the library aren’t important here, but basically the trick they used was to ‘precompute’ some commonly used values and store them instead of calculating these values each time. In most cases we would just store the values on a contract and be done.
The problem is that the precomputed data includes 512 separate 32 byte values, and each run uses about 50 of them. That means it would take over 100,000 gas (50 x 2100 cold access SLOAD) to use this method, defeating the point of precomputing the values in the first place.
To get around this problem they employed a trick discussed in the SSTORE2 library form 0xsequence. Instead of writing the values to storage, they deploy an account where the bytecode is a concatenated version of all the values.
Account Structure
To understand where exactly this bytecode is written, it’s worth looking at the structure of an Ethereum account.
From this diagram you can see that the storage (all variables defined in the contract) and the code (EVM opcodes which execute when called) are stored in different places. The storage of an account can be updated, but the code is immutable… the perfect place to write your name!
Smart Contract
The next step is working out how to set the code field of an account. The best way to generate the bytecode needed to deploy an account, is to use some high level language like Solidity and write something like:
When a contract is compiled, the bytecode you see will be a combination of 2 things: The pink ‘initialization code’ and the blue ‘runtime code’. Initialization code (initcode), is executed in the context of the new account and it will setup any state variables, and also return the runtime bytecode. This runtime bytecode, the part we’re interested in, will be written in the code field for the account permanently. (check out here or here for a more in depth walkthrough of this)
The problem with the contract we’ve written is that is that the bytecode it would write onchain does not include our name. The account would include some storage locations which hold our name, but that’s not good enough. To write your exact name, and no other surrounding faff, you need to manipulate the initcode to return your name, and nothing else.
Setting the Bytecode
This is where the SSTORE2 library comes in handy, as it has a function which sets the initcode needed to deploy some specific bytecode. By making the initcode a set of instructions which return exactly what we need.
The above code takes our name in bytes and checks the length of it so that we know how many important bytes we need to copy later on. It then adds an offset of 11 (avoiding the 11 bytes of the instructions we’ve written) and copies the nameInBytes variable to memory at position 0. Finally we return the copied bytes, meaning that when this code is ran in the contenxt of a new account, the code field will be exactly the name!
Testing
We can now test this out by deploying an account with the initcode as seen above. By using a deployer like this you can see that the contract is deployed onchain, and the code field is exactly my name (in bytes)!