Building a Feeless Chain: Why and How
Why Fees Exist
Applying fees to each transaction plays a vital role in tokenomics in the blockchain space. These fees, often called "gas," serve several purposes, one of which is crucial: securing the chain against spam attacks.
However, these fees also present some inconveniences depending on their calculation method. One major issue is their unpredictability, which can make users' transaction planning difficult. High fees may also deter certain usage cases where costs are a concern.
In the following discussion, I will focus on blockchain systems within Polkadot's Substrate-based chains ecosystem (proof-of-work based). I aim to explore how I devised a solution to eliminate transaction fees while maintaining network security for the Duniter blockchain. Additionally, we will examine the implementation of a wholly feeless yet secure blockchain.
Why No Fee
In the blockchain space, fees are essential and impact the end users. The calculation method for these fees aims to strike a balance between keeping the network secure and keeping user costs as low as possible. The other role of the fees is to incentivize users to contribute to the network by running validator nodes and finalizing blocks. Some of the transaction fees can be reversed to the validator that finalized the block, depending on the tokenomics.
The attractivity of a blockchain with no fee is very low and only applies to projects with another incentive to contribute to the network. However, such projects can arise in several contexts.
We will talk later about the Duniter project, where the blockchain is public but where the user needs to gain privileged access using a web of trust to become a member (non-members can still interact with the blockchain, but as the token is not accessible via DEX or exchange, one member should freely give tokens to a non-member before it can interact with the blockchain).
In this scenario, the number of members is restrained (and identified thanks to the web of trust), deterring system abuse. The validators known as Smiths are selected using another web of trust (no stacking necessary) and participate in the network by pure altruism. In this context, trust between users replaces the monetary incentive, and a no-fee chain makes sense.
Another scenario is for a private chain where the users participating on the network are known and selected at the entry. In this scenario, the blockchain provider (probably a centralized entity) provides and maintains the network; therefore, validators are not interested in monetary compensation. In this particular case, the no-fee chain also makes sense.
A chain with no fee can still be relevant for some chains that solve specific problems where the blockchain providers (centralized or decentralized) are not concerned about monetary incentives. For most blockchain applications, this concept is close to aliens, who will want to run a node, paying hardware and energy and stacking some capital to run a node with zero return.
The feeless chain concept will mainly attract small and collaborative projects where the incentive takes another form. It is important to note that, as we will see in the next section, it is possible to have a non-mandatory fee system where the fee is zero by default, but the transaction's sender can add a tip that will be reversed to the validator. In many chains, these tips increase the priority of the transaction, but if users are of good faith, it can, in the feeless case, compensate validators for their altruism to run the network.
Substrate Fee System
The fee computation is straightforward in a Substrate-based chain and can be subdivided into two conceptual units. The first is the concept of weights and benchmarks. Each interaction with the chain (extrinsic) triggers some operations, such as Alice transferring tokens to Bob. When Alice submits the transaction, the chain will check the transaction validity, verify that Alice has enough in its account, and then perform the state change. All these operations cost a certain amount of weight proportional to the computing power. These weights are computed using benchmarks, meaning that for every extrinsic, a benchmark is run on a reference machine targetting the most intensive computing path to determine the weights.
In Substrate, the concept of "weight" involves two primary components:
- ref_time refers to the computational time measured in picoseconds required to execute a transaction.
-
proof_size represents the size in bytes required to
execute a block of a parachain. The proof size is influenced by the
amount of data accessed during a transaction. It measures the amount
of data that must be processed to validate blocks'
correctness and integrity.
A Substrate parachain is a project-specific blockchain that can be validated by the validators of the relay chain (Polkadot). In contrast, a solochain operates independently with its own validators.
In addition to these components, the extrinsic length in bytes also plays a crucial role in resource management within Substrate.
The proof_size is only relevant for parachains, which tends to be more restrictive than the ref_time. For parachains, the maximum proof_size is set at 5 MB (5 * 1024 * 1024 bytes), as specified in the parachain runtime configuration. In contrast, this limitation does not apply to solochains, where the maximum proof_size can be removed to utilize the chain at its full potential as seen in the solochain runtime configuration.
The blockchain must ensure that the maximum block weight and length are respected. The maximum weight is defined using BlockWeights, which contains the maximal ref_time and proof_size.
By default, the maximal ref_time is set to 2 seconds of computations, with an average block time of 6 seconds. The BlockWeights structure also defines several limits: the base block weight as the minimal weight consumed by a block execution, the maximal block weight as described above, and the weight limits for an extrinsic by dispatch classes.
The maximum block length is defined using BlockLength. It defaults to 5 * 1024 * 1024 bytes and is specified by dispatch classes.
Substrate incorporates three dispatch classes for transactions: normal (taking up 75% of a block by default), operational (reserved for critical transactions, 25% of a block by default), and mandatory, which are always included regardless of their weights.
The frame_system pallet manages weight and length accounting in Substrate. The current block weight is stored in BlockWeight, while the current block length is stored in AllExtrinsicsLen. The CheckWeight trait, more precisely the do_pre_dispatch function, ensures that extrinsics conform to the block's weight and length limits.
The pre-dispatch function performs several crucial checks when an extrinsic is submitted:
- Length Check: It compares the length of the extrinsic against the current block's total length and limits to see if the extrinsic fits in the block (by dispatch class).
- Weight Check: It compares the extrinsic's weight, which includes the ref_time and proof_size, against the current block's total weight and limits to see if the extrinsic can fit the block (by dispatch class).
- Proof of Validity Check: This involves adding the extrinsic's proof_size to its length and comparing the result against the maximum proof_size allowed for the total block.
- Extrinsic Class Weight Check: It verifies that the extrinsic does not exceed the maximum weight authorized for its dispatch class.
After all these checks, the weight and length can be accounted for. The new weight (ref_time and proof_size) is placed in the BlockWeight storage, and the new length is in the AllExtrinsicsLen storage. For the weight, the two components are added to the current block weight, respecting the dispatch class. For the length, the extrinsic length is added to the current length.
Transaction fees in Substrate are computed by the transaction_payment pallet which converts weights and lengths into fees using the formula:
fee = base_fee + weight2fee * fee_multiplier + length2fee +
tips
Implementing Feelessness in Substrate
Creating a zero-fee chain in the Polkadot ecosystem is challenging due to the risks of spam and potential deadlocks; the system is not designed for this purpose. In the Duniter project, I successfully implemented an almost feeless chain by leveraging the conversation from weight to fee.
I have found a solution to use weight2fee, length2fee, and the fee_multiplier to achieve a zero-fee chain. The complete implementation can be found here.
First, I removed the fee by mapping the weight and length to 0 tokens. It does not remove any other security guarantees; users need to have the existential deposit to submit transactions, etc. At this stage, the chain is vulnerable to spamming attacks, where legitimate users submit numerous transactions until the block is whole. To mitigate this, I defined a block weight and length target (25%), beyond which the fee system becomes active so attackers cannot slow the chain down without incurring some fees. However, the chain remains vulnerable to an attack where entities deplete the "free transactions" quota of 25%, causing subsequent users to be charged. To address this, I used the fee_multiplier to track the status of previous blocks over time. If the earlier blocks exceed the target, we increase the multiplier, so everyone will pay fees in the next block. If the attack continues, the multiplier increases again, raising the fees. If the attack stops, the multiplier decreases, gradually returning the fees to zero.
With this solution, the Duniter blockchain can offer a zero-fee chain while maintaining robust security against spamming attacks. This solochain can handle approximately 500–800 free transactions before reaching the 25% fullness target, sufficient for the expected user base. In case of attacks or congestion, the fee system kicks in before the chain can be slowed down.
A Truly Feeless Chain
The next step is implementing a complete feeless chain with the same security guarantee. In this new design, I removed transaction fees entirely, even during periods of network congestion. But how can a blockchain remain secure, scalable, and resistant to spam without relying on fees?
The answer is partly in a paradigm change and, more practically, in a rate-limiting transaction system through an extrinsic extension.
Rather than relying on fees to deter spam or congestion, we introduce a rate-limiting mechanism to control how frequently an account can submit transactions to the network. By imposing a transaction submission rate limit (a length limit for chain accepting data can also be implemented), we ensure that the network can handle the flood of transactions, regardless of whether they are spammy or legitimate.
This rate-limiting feature is implemented as an extrinsic extension on the Substrate chain, see here. Several extensions are already in place, most by the frame system for checking the validity of a transaction (nonce, mortality, version, etc.) and one by the pallet transaction payment to compute and take the fee associated with the transaction. By adding a transaction submission rate limiter as an extrinsic extension, we can introduce this feature without compromising the fundamental operation of the blockchain.
Basically, the extension performs operations before and after the dispatch. Since there will be no fees, the pre-dispatch operation should be lightweight (at most, one storage access) to minimize its impact on the chain and avoid making it vulnerable to spam. This technique is similar to how Substrate checks if the sender's balance is sufficient for a transaction (even when the balance is zero and no fee can be incurred) without compromising the chain security.
Each user account on the blockchain is given a defined transaction submission rate. For example, an account can only submit one transaction every few blocks, depending on the network's configuration. This limit ensures that no account can flood the network with transactions in a short period, and the low footprint of the extension logic prevents a large group of accounts from overloading the chain with invalid transactions.
Since the rate-limiting mechanism handles congestion control, the network no longer needs to introduce fees based on load. This system makes the blockchain truly feeless, even under stress. Users can continue to transact freely, knowing their actions will be limited only by their account's rate and not by a fluctuating fee system.
Users no longer have to worry about transaction fees, making microtransactions and frequent interactions with the blockchain completely costless. Users can interact with the blockchain without holding many native tokens to pay for variable and unpredictable transaction costs. The feeless blockchain is more predictable, and its price is more transparent for the user.
While a feeless blockchain offers numerous advantages, it has its challenges. A rigid rate-limiting system must ensure fairness. Without proper balance, it could inadvertently disadvantage certain users or applications that must quickly submit a large volume of transactions. More importantly, it can also reduce the incentive to participate in the network because, without fees, the validator rewards are diminished.
For regular physical users, this type of chain will seem totally normal. Let's say Alice goes to the market and buys tomatoes from Bob and cheese from Ferdie. These two transactions will be spaced by minutes. Even for very conservative chains, allowing one transaction per account by six blocks, Alice can still pay anyone without any problem.
The paradigm shift will be perceived mainly by automated users like trading bots, DeFi applications, etc. Microtransactions will not be possible (or by batch if the system allows them). The feeless chain is also a "slow chain" where the timescale between transactions is mirrored to real-life actions and does not suit robots well. As the DeFi ecosystem relies heavily on monetary incentives, the feeless chain will be of no interest for all applications relying on investment and return.
Conclusion
In this post, we have seen first the utility of the transaction fees to secure the blockchain and incentivize users to contribute to the network. I have presented several use cases of chains where fees are not required to incentivize users to contribute to the network.
Then, I presented a solution I designed and implemented for the Duniter blockchain. There is no fee for normal network usage and fees if the network is congested or under attack. This solution offers the same security guarantee as a regular chain while being able to process a high volume of transactions freely.
In the last part, I presented a standalone implementation for a totally feeless chain where each user gets a transaction rate restriction. This solution offers the advantage of having a very predictable behavior for the user, the main disadvantage being that the number of transactions is limited.
To conclude, each solution has its respective case of application and limitations that need to be considered. In each case, the implementation using the template or the dedicated pallet is straightforward.