Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gas price oracle improvement #1541

Draft
wants to merge 4 commits into
base: nightly
Choose a base branch
from

Conversation

yaziciahmet
Copy link
Contributor

@yaziciahmet yaziciahmet commented Dec 1, 2024

Description

It seems that the tips suggested for transactions are unnecessarily high. They can even be set to 0 with the current network conditions since no block is full, but using that is not a viable algorithm as tip is meant to be what user's are willing to pay and which of these made it into the block.

Ethereum's algorithm is simply taking the 3 lowest tip transactions from the last 20 blocks, and returning the tip at 60th percentile, which makes sense as Ethereum's blocks always has some transactions. They handle empty blocks by putting the last cached tip as placeholder into the collected tip list. It is pretty obvious that they almost never have to go into this part of their algorithm.

We also copied the same algorithm, but unlike Ethereum we do a lot of adding placeholder for empty blocks.

It seems that the tips we suggest are unnecessarily high even though almost all blocks are near empty, and this is an attempt to lower the tip estimations.

Using the exact same algorithm as Ethereum might actually cause some problems in our estimations. Imagine the scenario:

  1. 100th block has a single tx with a high tip.
  2. we generate 900 empty blocks.
  3. even though the last 900 blocks were empty, we use the same fee as 100th block as placeholder for that 900 empty blocks, and highly likely gonna return that value as tip estimation, which doesn't make sense at all

This example is obviously exaggerated, but it is by no means impossible have this problem in smaller scale and with smaller number of blocks, but the problem aggregates over time.

This PR attempts to solve this problem by decaying the effect of last cached tip by the distance of the cached block number. So, the farther the cache tip is estimated, the smaller placeholder for empty blocks is going to be used.

Considering block_distance = current_block_number - last_cached_block_number, exact algorithm is:

last_cached_price / block_distance

I choose division because it made more sense.

If we were to use exponential algo smh like:

last_cached_price / (2 ^ (block_distance))

it would be decaying way too fast.

If we were to use a quadratic algorithm:

last_cached_price * (1 - (block_distance ^ 2) / 10 ^ 2)

This would go down to 0 at exactly 10 (which could be ok), but the main problem is that initial decay would be super slow. In contrast we want initial decay to be faster than later because if there is even 1 empty block that is a strong indication that tip could be much lower.

TODO
I will try to test this by running a testnet node by cherry-picking commits on top of v0.5.4, and for 1 day, I will send eth_maxPriorityFeePerGas requests to both actual testnet node, and my node with certain interval, and record the responses to see if it actually had any difference.

IMPORTANT
Do not merge this into nightly. Opened this PR to nightly just to see the difference. Will later open this PR into the release branch.

Linked Issues

Testing

Describe how these changes were tested. If you've added new features, have you added unit tests?

Docs

Describe where this code is documented. If it changes a documented interface, have the docs been updated?

@yaziciahmet yaziciahmet added HOLD-MERGE PR is not draft but should not be merged yet DECISION-NEEDED the issue should be carefully evaluated for inclusion into the repo labels Dec 1, 2024
Comment on lines +280 to 286
let filler_price = if last_price.block_number == 0 {
// if this is the first call to suggest_tip_cap, use default 0 price
last_price.price
} else {
self.oracle_config.max_block_history * 2
let block_distance = header_number - last_price.block_number;
last_price.price / block_distance as u128
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit but wouldn't it be self explanatory to cache an Option and remove Default on GasPriceOracleResult then we could use map_or(0, |..| ..) here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
DECISION-NEEDED the issue should be carefully evaluated for inclusion into the repo HOLD-MERGE PR is not draft but should not be merged yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Revisit priority fee RPC
2 participants