How to use TON as a transaction fee on Titan

Implementation of fee token through L2 client modifications

Theo Lee
Tokamak Network
9 min readDec 27, 2023

--

Special Thanks to Suhyeon Lee, Steven Lee, Suah Kim, Aaron Lee, and Nam Pham for your help in writing the post.

You can check the Korean version of the article here.

Introduction

The term “Fee Token” pertains to tokens used for paying transaction processing gas fees, frequently employed in the execution of smart contracts on platforms such as Ethereum or for asset transfers.

Tokamak Network has studied using TON as a fee token on Titan. The users can pay transaction fees with TON instead of ETH in layer 2 where the fee token feature is supported.

Why is this important? It ensures a foundational demand for TON and operates as a persistent token burner within layer 2, allowing the platform to burn its tokens autonomously. Regardless of the blockchain application or service a user utilizes, transactions are an inevitable part of the process. Holding ETH on Titan is unnecessary to cover transaction fees.

As a benefit for users of the Tokamak Network, we can also offer a discount, making Layer 2 accessible at a significantly lower cost than using ETH. This ensures a consistent demand for TON on Titan, providing price stability, and enhancing user convenience by eliminating the need to manage both ETH and TON.

Moreover, it reduces the overall cost of accessing the Tokamak Network Layer 2 and using various decentralized applications (DApps).

source: unsplash

In this article, we’ll explain the principle of modifying Titan’s geth-based L2 client to pay transaction fees in TON instead of ETH and demonstrate with example code what assets change in transfer and withdrawal.

Customize L2 client for fee token

State Transition Model

To implement the fee token, we’ll start by looking at Ethereum’s transaction fee calculation logic.

In Ethereum, the process of the State Transition Model calculates the gas cost of executing a transaction and the sender pays the transaction fee accordingly. It checks the ETH balance of the sender, subtracts the calculated transaction fee, and confirms the changed state.

fig. State Transition Process

Calculate the fee as follows:

  1. First, calculate the maximum transaction fee (= Gas Limit * Gas Price)
  2. Checks the sender’s nonce and subtracts the maximum transaction fee (returns an Error if the maximum transaction fee is less than the sender’s ETH balance)
  3. If the receiver doesn’t exist, call evm.Create() to create a new contract
  4. Run evm.Call() to execute the transaction and return the result (if an error occurs during the execution of the function, it will restore any changes in State to the previous snapshot)
  5. If you’ve reached this point, calculate the actual transaction fee which the amount of Gas (= Gas Used) is multiplied by the Gas Price. The value is added to the miner’s balance, and the remaining Gas is returned to the sender.

Get the TON transaction fees

You can calculate the transaction fee in ETH and convert it to the TON transaction fee as follows.

Transaction Fee Based on TON = Transaction Fee Based on ETH * Price Ratio

We deploy the TON_FeeVault on the L2 network to obtain the Price Ratio, which is the ETH:TON exchange rate. The L2 client uses the Price Ratio stored in the storage of the TON_FeeVault. The main functions that make up the TON_FeeVault are as follows.

  • updatePriceRatio(): Updates the Price Ratio. This function can only be called by the owner of the contract.
  • updateGasPriceOracleAddress(): Updates the address of the OVM_GasPriceOracle in L2 . This function can only be called by the owner of the contract.
  • withdrawTON(): A function for withdrawing the transaction fee of the Sequencer that operates L2. Transaction fees (TON) generated on the L2 network are sent to the TON_FeeVault contract. The transaction fee collected in this way is withdrawn to the L1 Fee Wallet selected by the Sequencer.

The contract code can be found at https://github.com/tokamak-network/tokamak-titan/blob/feature/native-token/packages/tokamak/contracts/contracts/L2/fee-token/TON_FeeVault.sol

How to Calculate Titan Transaction Fees

Next, let’s learn how to calculate transaction fees on Titan.

The transaction fee in Titan, an L2 based on Optimistic Rollup can be expressed as the sum of the L1 Security Fee which is the rollup cost, and the L2 Execution Fee which is the transaction execution cost.

The L1 Security Fee and L2 Execution Fee are calculated as follows. (For explanations and detailed information, please refer to https://tokamaknetwork.gitbook.io/home/02-service-guide/titan/user-guide/l2-fee).

L1 Security Fee = L1 Base Fee * (Gas of Calldata + Overhead) * Scalar

L2 Execution Fee = L2 Gas Price * Gas Used

You can query L1 Base Fee, Overhead, Scalar, and L2 Gas Price through the OVM_GasPriceOracle contract deployed in L2. Gas of Calldata and Gas Used are calculated directly by the L2 client.

Implementation for the L2 client

Now let’s look at the logic of L2 client implementation for fee token.

The NewStateTranstion function below initializes the State Transition Object. The function evm.ChainConfig().IsFeeTokenUpdate(evm.BlockNumber) determines if the provided block number corresponds to the implementation of the fee token upgrade. It returns true if the fee token upgrade is activated and TON is used as a transaction fee in L2.

If isFeeTokenUpdate is true, call evm.StateDB.GetTonPriceRatio() to query the Price Ratio. For an L2 transaction that rolls up to L1, the applicable fee is determined by multiplying the L1 Security Fee with the Price Ratio. The L2 Gas Price is also multiplied by the Price Ratio to convert the L2 Gas Price into TON units.

The StateTransition structure is defined in https://github.com/tokamak-network/tokamak-titan/blob/feature/native-token/l2geth/core/state_transition.go#L58 and it is used for the transaction fee calculation in TransitionDb().

Let’s look at the main function within TransitionDb(). TransitionDb() returns the transaction execution result and Gas Used (the amount of gas used to execute the transaction).

  • preCheck(): Checks the sender’s nonce, calculates the maximum transaction fee, and deducts it from the sender’s TON balance in advance.
  • Create/Call(): Creates a contract or executes a transaction depending on the receiver and returns the result.
  • refundGas(): Calculate the fee to be refunded based on the remaining gas after executing the transaction and return it to the sender

Next, we calculate the transaction fee. Calculate l2Fee by multiplying st.gasUsed() with st.gasPrice. Combine l1Fee and l2Fee to get the transaction fee.

If st.isFeeTokenUpdate evaluates to true, the transaction fee contributes to an increment in the TON balance of the TON_FeeVault.

Consequently, the TransitionDb() function executes an L2 transaction and computes the transaction fee based on the gas consumed. The calculated amount is subtracted from the sender’s TON balance, and simultaneously, the TON_FeeVault contract undergoes an increase in its balance.

Demonstration

Environment

To test the fee token, a network with the same specifications as Titan is built in the local environment.

fig. Network architecture

Run a network

To test the fee token example, run a test network as shown below.

git clone -b feature/native-token - single-branch https://github.com/tokamak-network/tokamak-titan.git
cd tokamak-titan
# build
docker-compose -f ./ops/docker-compose-fee-token.yml build
# up
docker-compose -f ./ops/docker-compose-fee-token.yml up -d
# down
docker-compose -f ./ops/docker-compose-fee-token.yml down

Run example

Let’s check out the fee token with three examples.

  • ETH transfer

In the example code, 0.001 ETH is sent using the sendTransaction function in ether.js, and the change in the sender’s ETH and TON balance and the calculated transaction fee are output. Upon executing the example code, only the sent ETH amount is deducted from the sender’s balance, while the deduction of TON occurs as a consequence of the transaction fee.

...
Transaction hash: 0x2e16d471c56564b631fcb0b9ab6106628a1ff3a7d1ff8a1b9c504675accfbcfa
Change in ETH balance: 0.001
change in TON balance: 0.3537894186506904
L1Fee: 0.3537891592754904
L2Fee: 0.0000002593752
Total Fee: 0.3537894186506904
  • TON transfer

Execute the ERC20.transfer function to transfer 1 TON. Subsequently, display the resulting changes in the sender’s ETH and TON balances, along with the calculated transaction fee. When the example code is run, the observed outcomes are as follows: the sender’s ETH balance remains unchanged, and the TON balance is reduced by the transferred amount (1 TON) and the associated transaction fee.

...
Transaction hash: 0xca355143a677a194476da1d56250ae5a6c6d46bf49496c0a6f13906910c00b43
Change in ETH balance: 0.0
change in TON balance: 1.3987911467022376
L1Fee: 0.3987907573430088
L2Fee: 0.0000003893592288
Total Fee: 0.3987911467022376
  • ETH withdrawal

Proceed to withdraw ETH from L2 to L1 by initiating a withdrawal of 0.01 ETH through the L2StandardBridge’s withdraw function. The Batch Submitter identifies transactions in L2, aggregates the transaction batch, and submits both the batch and the state root to L1. Subsequently, the Message Relayer verifies the rolled-up state of the L2 transaction and executes the relay transaction to L1. Upon completion of the withdrawal, assess the ETH and TON balances before and after the transaction. Despite being an ETH withdrawal, note that the TON balance has been reduced due to the deduction incurred from the transaction fee.

---------------- Widthdraw 0.001 ETH ----------------
TX Hash: 0x8fcf24031721747f0fbd519a15b4f39dcac584000f1d1595e2c94b883402d2ed
Before L1: 0.198185314318598221 ETH L2: 0.061779738056659911 ETH
Before L2: 5.3517209343875952 TON
.............
After 14 seconds
After L1: 0.199185314318598221 ETH L2: 0.060779738056659911 ETH
After L2: 4.9270767220854416 TON

Full example code and guide can be found in Tokamak Network’s Repository: https://github.com/tokamak-network/tokamak-titan-example

Next Challenges

So far, we have implemented the fee token on Titan and confirmed the change in the sender’s TON and ETH balance according to the L2 transaction through an example. But there are still some problems we need to solve.

Reference and adjustment of Price Ratio

When updating the Price Ratio, the data on which it is based must be reliable and managed by consistent rules. The Price Ratio managed by the TON_FeeVault contract is closely related to the transaction cost charged to users. In this structure, the Price Ratio can only be adjusted by the owner of TON_FeeVault, and the Sequencer is mainly responsible for this role. This is a centralized structure in which transaction fees can be changed by the sequencer.

Issues with MetaMask

Even with a sender’s ETH balance at zero, transaction requests and result verification proceed as usual due to the application of the fee token, allowing the use of TON as a transaction fee. While there are no issues when requesting a transaction directly from the L2 client, a problem emerges with MetaMask. Specifically, an ‘insufficient balance’ error occurs when the sender’s ETH balance is zero. This becomes evident in the image below, where the user’s ETH balance falls short of the expected cost when comparing it to the predicted transaction fee. At the transaction approval stage, you can see that the transfer function is blocked.

fig. ‘insufficient balance’ issue in Metamask (Left: transaction fee estimation, Right: transaction confirmation)

To solve these problems and completely replace the role of ETH with TON in the Tokamak Network layer 2 network, we plan to provide a new layer 2 network using TON as the base currency. This not only solves the RPC problem but also allows EVM-based smart contracts to be deployed and used on Tokamak Network’s layer 2 network. Additionally, we have a plan to introduce an Oracle service that can dynamically update the Price Ratio according to changes in market prices to provide reasonable fees to users. The Price Ratio is automatically adjusted based on market conditions, eliminating the need for the Sequencer to manually request update transactions. Users can confidently trust and pay fees as the Price Ratio adapts to market prices, which are openly accessible to everyone.

Conclusion

In this article, we explored the implementation of a fee token utilizing TON as the transaction fee. The provided example code in the test network demonstrates the practical changes in balance, encompassing both ETH and TON. If you want to check the implementation code's details, please refer to https://github.com/tokamak-network/tokamak-titan/tree/feature/native-token.

Tokamak Network is actively researching solutions for the mentioned challenges and is considering incorporating the concept of ‘native TON’ into the new Titan. This involves utilizing TON not only as a transaction fee but also as the primary currency at Layer 2. The launch of the new Titan, scheduled for next year, will introduce native TON, completely supplanting the role of ETH. This transition will be supported by a core protocol based on Optimism Bedrock. In anticipation of the new Layer 2 network’s unveiling, we will be sharing our research findings and progress in real-time on Medium. We welcome your ongoing interest and engagement.

--

--