ARK Core introduced dynamic transaction fees with AIP16 but they ended up having some flaws that validators abuse to provide close to 0 fees. That low fees make it very easy to attack the network by flooding it with a lot of cheap transactions that will congest transaction pools on all nodes on the network.
Odysseia introduces a few changes to how fees are calculated and how they change based on the state of the average transaction pools on the network. In this post we'll go into what issues AIP16 has and Odysseia attempts to address those to provide real dynamic fees based on network activity rather than being fully controlled by validators.
AIP16 has a myriad number of variables that need to be taken into account to calculate fees for accepting and broadcasting transactions - those are separate calculations. Each transaction type comes with its own values and the implementation of the AIP16 calculations can be found in the base handler.
The way the minimum fee calculation works in Odysseia is a bit simpler and has an additional multiplier based on the capacity of transaction pools on the network. The basic formula is as simple as
transactionSizeInBytes * satoshiPerByte and depending on the capacity of transaction pools on the network this formula is expanded by
This formula isn't too different to what ARK Core does but we dropped a value and repurposed another. The satoshiPerByte value in ARK Core corresponds to the minFeePool value rather than a transaction-specific value. In Odysseia satoshiPerByte corresponds to a transaction-specific multiplier, like 100 satoshi per transfer byte or 500 satoshi per delegate registration byte. As you might've noticed by now, if you've looked at the implementation of ARK Core - we repurposed what ARK Core calls addonBytes as satoshiPerByte.
Repurposing this value means that calculations are now purely based on the transactionSizeInBytes and satoshiPerByte without any additional values to artificially increase the cost of transactions - you pay for what you sent. That being said, lets look at how the capacity of the average transaction pool on the network can influence this calculation.
Each node on the network keeps track of the capacity of every transaction pool on the network. This is done in order to know how full any given transaction pool is when new transactions are received. A transaction pool can hold up to 5000 transactions and every 1000 transactions that are pending add a small multiplier to the total cost of a transaction. Lets create an example scenario to illustrate this.
If you are trying to send a transfer transaction while the network has no backlog of transactions you'll pay a low fee with a capacity multiplier of 1. The formula
transactionSizeInBytes * satoshiPerByte * capacityMultiplier would be used in such case. A real example would be
200 * 1000 * 1 where 200 is the byte-size of the transaction, 1000 is the cost per byte in satoshi and 1 is the multiplier that has been determined based on the average transaction pool capacity.
Given this example we can already derive what the formula would look like if the network is congested because the average transaction pools are full. The formula would be
200 * 1000 * 5 - 5 times higher than non-congested transaction fees. This makes it less attractive to send more transactions until the network is no longer congested because it means that you would lose more money in fees. This will help the network to recover because non-malicious actors won't add more transactions to the already large backlog.