Extreme Optimization of GasToken
To fully grasp the content of this article, you need to have a basic understanding of blockchain, smart contracts, and how the Ethereum Virtual Machine (EVM) operates. This material targets WEB3 developers who are already familiar with optimizing gas costs for smart contracts.
In this article, we will delve into optimizing GasToken. However, it's important to note that it is no longer relevant to the Ethereum network. In August 2021, the London hard fork introduced EIP-1559 because the GasToken economy was inefficient. Nevertheless, the Binance Smart Chain (BSC) continues to use this mechanism.
First and foremost, let's discuss gas in the Ethereum network. The term "gas" represents a unit of measurement for the costs associated with executing transactions on the blockchain. This concept is an integral part of the network's economy and provides rewards to creators of new blocks. Each operation in the Ethereum network consumes a specific amount of gas, depending on the operation's complexity and the resources it requires for execution. In simpler terms, the more complex the task, the more gas it consumes.
GasToken is a unique mechanism designed to lower transaction costs and operations within a blockchain. The core idea behind GasToken is to provide a way to "package" gas into the form of tokens and temporarily "freeze" it within a specific contract. This enables users to store gas during periods of low gas prices and use it later when gas prices increase.
The original implementation of GasToken takes the form of an ERC-20 standard smart contract. When a user "mints" GasToken, they pay a certain amount of gas to create tokens. Later, when the user chooses to "redeem" the tokens, they do so by sending the tokens back to the contract, effectively refunding the gas they had previously expended.
Initially, there were two variants of GasToken, each employing its own approach: GST1 and GST2. The fundamental difference between them lay in the use of different EVM opcodes, which allowed for the return of some gas in the transaction.
By examining the Ethereum Yellow Paper and observing gas costs for operations, two intriguing opcodes come into view: SCLEAR and SELFDESTRUCT. The former rewards for freeing up storage slots within a smart contract, while the latter provides the ability to refund gas by destroying the contract.
- opcodes Rsclear
Name: Rsclear
Value: 4800
Description: Refund given (added into refund counter) when the storage value is set to zero from non-zero. The refund amount is defined as Gsreset + Gaccessliststorage.
- opcodes Gselfdestruct
Name: Gselfdestruct
Value: 5000
Description: Amount of gas to pay for a SELFDESTRUCT operation.
The aim behind these mechanisms was to motivate network participants to uphold the cleanliness and efficiency of the blockchain, rewarding them for actively eliminating unused data. Nevertheless, this approach, to some extent, incited "gas wars" among network participants, where economic gain surpassed the initial incentives. This illustrated that even efforts to enhance blockchain efficiency could lead to undesirable consequences due to economic factors.
A bit later, CHI GasToken emerged on the scene as an advanced version of GasToken, developed by the 1inch.exchange team. Its primary distinction from its predecessors lies in a more sophisticated optimization method, enabling even greater economic benefits for users. The core concept remains the same: packaging gas as tokens and temporarily storing it in a contract for later use.
However, the key feature lies in the utilization of the EVM opcode CREATE2. This opcode allows for the creation of smart contract addresses in advance based on deterministic logic. Let's take a closer look at the operation code that takes four parameters from the stack: endowment, Memory_start, Memory_length, and salt. Instead of the standard sender's hash and nonce, the generated address is determined as:
keccak256( 0xff ++ address ++ salt ++ keccak256(init_code))[12:]
And this makes it unique. It's worth noting that users have control over the salt parameter, allowing them to predefine the address.
From an economic perspective, CHI GasToken has become more efficient compared to previous versions. It provides savings of up to 1% of gas when minting tokens and up to 10% of gas when redeeming them.
As mentioned earlier, GasToken creates child smart contracts using the CREATE operation, and their code is represented below as a sequence of EVM opcodes:
[00] PUSH15
[10] CALLER
[11] XOR
[12] PC
[13] JUMPI
[14] CALLER
[15] SELFDESTRUCT
Each child contract implements the following simple functionality:
if (msg.sender == GST2.address){SELFDESTRUCT(msg.sender);}
The provided example of a child contract uses 22 bytes of code and costs approximately: 32,000 + 22 * 200 = 36,400 gas to create. Now, let's take a look at unique methods for optimizing the GasToken smart contract code.
Let us start by modifying the code in a way that reduces the number of operations while keeping the contract's functionality unchanged. For instance, we can replace the JUMPI opcode with an unconditional JUMP, which uses JUMPDEST as a reference point. Each operation has a specific position and sequential number in the code, and we use this to set the correct destination point.
[00] PUSH15
[10] CALLER
[11] XOR
[12] JUMP
[13] JUMPDEST
[14] SELFDESTRUCT
The main idea is to precompute the result of the bitwise XOR operation between the contract's GST address and the JUMPDEST instruction number and then place this value on the stack using PUSH15:
When the contract is called with the corresponding GST address, a XOR operation is performed between the number (0xRESULT) previously placed on the stack and the address of the calling contract. The result of this operation is stored at the top of the stack. This outcome should match the JUMPDEST operation number, which is 13 in this instance. It's important to note that the XOR operation possesses specific properties: when comparing identical data, it cancels out, leaving only the desired number. This number is then passed to the JUMP operation as the JUMPDEST destination point, enabling code execution to switch to the required instruction based on conditions:
With this optimization, we can achieve gas savings with each child contract. This is accomplished by reducing the code size to 21 bytes instead of the previous 22. Such an approach enables efficient control over contract operations while simultaneously optimizing gas costs.
Another method for optimizing the GasToken code involves the use of the bitwise right bitshift operation, denoted by the opcode SHR.
[00] PUSH15
[0b] CALLER
[0c] PUSH1
[0e] SHR
[0f] XOR
[10] PC
[11] JUMPI
[12] SELFDESTRUCT
The essence of this approach is to reduce the initially stacked number by several bytes through a right bitshift (SHR opcode).
When we place a number on the stack with leading zeros, these zeros can be shortened. This technique saves space in bytes. The creators of CHI GasToken applied this method to reduce the size of the address embedded in the smart contract code from 20 bytes to 15 bytes. This reduces gas costs and optimizes the contract while preserving its functionality.
when pushed onto the stack
PUSH15 0xB3F879CB30FE243B4DFEE438691C04
full address
0x0000000000B3F879CB30FE243B4DFEE438691C04
The SHR opcode performs a right bitshift operation on a number. This means that each bit of the number is shifted one position to the right. As a result of the bitshift, the rightmost bit becomes zero, and the leftmost bit (the sign bit) is preserved, determining the number's sign.
Let's now analyze the code snippet highlighted below:
In this case, it starts with the SHR operation on the caller's address (CALLER_ADDRESS), applying it to the 40 bits (assuming the embedded address also has leading zeros and, in total, occupies 15 bytes). This step is performed to align the numbers correctly for the XOR operation.
Then, the code proceeds as in the classical GasToken implementation. Optionally, you can combine this with the first optimization, which reduces the code size by using JUMP and JUMPDEST instead of JUMPI.
In the stack, it appears approximately as follows:
In this case, it starts with the SHR operation on the caller's address (CALLER_ADDRESS), applying it to the 40 bits (assuming the embedded address also has leading zeros and, in total, occupies 15 bytes). This step is performed to align the numbers correctly for the XOR operation.
Then, the code proceeds as in the classical GasToken implementation. Optionally, you can combine this with the first optimization, which reduces the code size by using JUMP and JUMPDEST instead of JUMPI.
It's worth noting that this reduction in PUSH operations can proportionally impact code security because in this case we only partially check the msg.sender's address. More concise code can imply less robust checks on the calling contract. Therefore, it is recommended to use this method cautiously, preferably only when creating your custom GasToken.
By applying this approach in our example, we were able to reduce the number of bytes to 19, saving gas and highlighting the importance of balance between optimization and security.
Delving deeper into optimization, let's consider the most compressed version:
[00] CALLER
[01] SELFDESTRUCT
It's tempting to use such a minimalist construction, but it's essential to remember that the contract will be automatically destroyed upon any call, regardless of the sender.
This elegant solution comes at just two bytes of code, achieving absolute compactness. However, such extreme reduction poses security risks, and its usage should be carefully evaluated.
A deep analysis of code and an understanding of EVM opcodes open doors to the most subtle and unique ways of simplification and optimization, applicable not only in the context of GasToken code but also in other aspects of blockchain development.
Presented methods serve as a showcase of opportunities, but their effectiveness is confirmed, and they can be successfully applied to your projects. At first, it may seem that the proposed optimization methods only bring minor gas savings. However, the idea that alterations in the code of child contracts can significantly diminish the overall GasToken minting cost underscores the potential for substantial gas cost optimization.
If our optimization methods have piqued your interest, or if you'd like us to optimize your own smart contracts, please feel free to reach out to us!
One click to start work with us