OpenZeppelin
Skip to content

Fundamentals of Gas Tokens

Fundamentals of Gas Tokens

Warning: There are active discussions to remove the SELFDESTRUCT feature prior to the merge to Proof of Stake, as well as gas refunds in general. If this were to happen, then gas tokens would no longer be usable for gas, and hence impact their value.
This change could occur as early as the London network upgrade (July 2021).
You can read about this (potential) decision in the following articles:

How do gas tokens work?

Whenever any transaction on Ethereum is run, it requires “gas” in accordance with the amount of computational resources used to compute the operations performed by the transaction.

The amount of gas used determines how much of a fee, in ETH, the user pays to run their transaction. The fee amount is simply gasUsed * gasPrice, where gas price is typically specified in “gwei” (where 1 gwei = 10^-9 ETH). Currently on Ethereum, there are two ways to receive a gas refund, which is a discount on your fee, applied after the transaction is completed. The gas refund can be up to half of the gas spent by the transaction. The two methods of obtaining gas refunds are clearing storage within a smart contract, and destroying a contract. Destroying a contract, which refunds 24000 gas, is considered more efficient than clearing storage, and in practice it usually is. GST2 and CHI, currently the most popular gas tokens, provide tokenized versions of gas refunds which utilize the destruction of smart contracts.

Fee (in ETH) = (gasUsed - gasRefunded) * gasPrice

GasToken2 (GST2) and CHI both work very similarly; they create extremely simple contracts which are later destroyed, providing a gas refund. “Minting” gas tokens refers to expending gas to deploy contracts, while “freeing” (or burning) gas tokens refers to destroying these contracts to get a gas refund.

The trick to using gas tokens effectively is in minting gas tokens at a low gas price, and then burning them at a high gas price, thereby saving the user on their gas fees. Since it costs more gas to create a contract than destroy it, gas token usage actually results in consuming more gas. The savings actually come from the difference in gas price, which is how much ether per gas unit you pay. Recall that ether paid is gasUsed * gasPrice. When the difference in gas prices between minting and burning is high enough, you can save ether, even though you’re not saving gas.

Gas tokens save ether when:
gasPrice_mint * gasUsed_mint < gasPrice_burn * gasRefunded_burn >
Since gasUsed_mint > gasRefunded_burn, gasPrice_mint must be sufficiently low compared to gasPrice_burn for ether to be saved.

In this post, I’ll refer to the tokenized contracts which are created and destroyed by GST2 as “dummy” contracts. To save on gas costs for the actual creation and destruction of the contracts, deployment of the “dummy” contracts is done with bytecode and Solidity assembly. The purpose of this document is to explain exactly what those pieces of assembly and bytecode do, as it can be confusing on first glance, and there seem to be many people in the community wondering about it with few resources to learn from.

Minting gas tokens

In order to mint gas tokens, an account or contract simply needs to call the public mint function. If you look into the actual contract code of GasToken2, you’ll see the makeChild function. The makeChild() function is marked as internal, and gets called repeatedly in the public mint function to mint the specified number of tokens. The purpose of the makeChild function is to deploy a contract with a minimal amount of bytecode, which can later be destroyed by the GST2 contract for a refund.

Here they provide pseudocode describing these “dummy” contracts:

if (msg.sender != GST2) { throw; }
selfdestruct(msg.sender)

This is about as simple as it gets. Any call to the newly created contract coming from the GST2 token contract will destroy it, passing on the gas refund for use in the rest of the transaction. If it doesn’t come from the GST2 contract, then the call will revert (to prevent moochers from getting our gas refund).

Burning gas tokens

Gas tokens can be burned by calling the public free function. When gas tokens are destroyed, they trigger a gas refund. They are destroyed through calls to the internal destroyChildren function.

Within destroyChildren, the function mk_contract_address determines the address of the next contract to destroy, then it .calls it, destroying the contract and triggering our gas refund. The address of each contract is easily predictable based on the fact that all contracts are created via the create assembly operation.

The free, freeUpTo, freeFrom and freeFromUpTo functions all call destroyChildren with some value, which is the number of gas tokens to destroy. The functionality of each version is slightly different, burning tokens from another user’s balance in the “From” versions and any number up to the specified value in the “UpTo” versions.

Effectively using GasTokens

Attempting to calculate a fixed ratio for high-gas-price / low-gas-price, which is the threshold for profitability of minting and burning gas tokens, is nearly impossible to do 100% accurately. This is because many factors influence the cost of both actions, such as the number of tokens being minted or burned, which gas token is being used, whether a user’s entire balance is being burned or only a portion, or whether a user possesses gas tokens before minting. This article from the 1inch team includes a graph in which “efficiency” of 3 varieties of gas tokens is shown. In the graph, an efficiency of 1 means the amount of ETH paid for minting equals the amount of ETH saved by burning gas tokens. But, this is merely an estimate. Ideally, an efficiency of at least 1.5 should be aimed for, which typically comes from a ratio of high-gas-price / low-gas-price somewhere in the range of 3 to 4.

Possible Improvements for Gas tokens

  • Gas token delegation: When minting gas tokens, one may prefer to use multiple accounts. One reason may be that a user is attempting to mint gas tokens at a low gas price, so the account which they do this with broadcasts transactions which sit in the mempool until they are placed into a block. Since transactions in ethereum must occur in the correct nonce order per account, things can get messy here if a transaction is still “pending” and the account attempts to send another. Imagine a user has multiple accounts with pending transactions to mint gas tokens at different gas prices. They then need to transfer these tokens to a central account. One may wonder, “why not allow the central account to spend the mining account’s tokens?” – typically, gas tokens are burned by the contracts which the central account calls, so those contracts would need an allowance. You might not want to do this, however, since once those contracts are allowed to spend your tokens, they either may spend them without your consent, or, may be unable to spend them since the contract may be set up to only spend tokens belonging to msg.sender, which in this case would be the central account. Rather than modifying the allowance logic of the gas token contract, an extremely simple solution would be a to add a mintTo function, which would allow one account to mint tokens to the central account’s balance.

  • Gas price based logic: Currently, gas tokens must be burned manually, meaning the decision to burn tokens must happen off-chain. It is possible to access the gas price paid for a transaction during the transaction through tx.gasprice . Using this, logic can be created which only burns gas tokens at certain gas prices. Although this would consume slightly more gas, the tradeoff to improve user experience may make it worthwhile. New functionality of gas tokens at a contract level could be implemented as well, since users would no longer need to judge whether burning gas tokens makes sense before sending their transaction.

  • Purchase and sale cost: Gas tokens offer more obvious ETH savings when the minters are the same users burning these tokens. However, interested parties may not be willing to mint gas tokens, and simply prefer to purchase them from minters instead. Intuitively, after gas prices increases, there is a sale price which earns minters a profit AND provides value to gas token users, such that buying and burning gas tokens is cheaper than paying a full transaction fee. Unfortunately, the gas costs of purchasing gas tokens, plus slippage in exchanges, may make it inefficient to buy gas tokens versus simply not. Additionally, low liquidity of such exchanges for gas tokens may make slippage too high for large-scale gas token consumers. Liquid gas token seeks to lower the amount of friction present in the “buy and burn” case by integrating a uniswap-style liquidity pool into the gas token contract, and thus lowering the gas costs incurred in buying gas tokens.