Skip to content

fix(gas-manager): keep legacy gas price history per chain - #222

Merged
fichiokaku merged 1 commit into
developfrom
fix/per-chain-gas-price-history
Jul 30, 2026
Merged

fix(gas-manager): keep legacy gas price history per chain#222
fichiokaku merged 1 commit into
developfrom
fix/per-chain-gas-price-history

Conversation

@fichiokaku

Copy link
Copy Markdown
Collaborator

Problem

GasManagerService.getGasPrice maintained a single rolling history shared by every chain the node serves:

private legacyGasPriceHistory: bigint[] = [];   // one array for all chains

Gas prices across our chains differ by orders of magnitude, so the median of that mixture is meaningless. The outlier checks then throw away a chain's real price in favour of the mixture's median:

} else if (currentToMedianRatio > 500n) {
  estimatedPrice = medianGasPrice;   // "outlier"
} else if (currentToMedianRatio < 50n) {
  estimatedPrice = medianGasPrice;   // "stale/broken"
}

The result is that a chain gets priced using an unrelated chain's gas price.

Impact observed on live nodes

Staging — Chiliz Spicy dominates traffic (2,925 log lines vs 18 for Fuji in one window), so it sets the median:

Chain Real Node used
Avalanche Fuji 160 wei 2,751,100,000,000 wei

That value is exactly the Chiliz median plus the 10% standard buffer (2501 gwei x 1.10). A client hit this as repeated execution failures on Fuji.

Production — the cheap chains dominate instead, so expensive chains are badly underpriced:

Chain Real Node used Off by
Chiliz 2,501 gwei 0.0387 gwei 64,625x
Polygon 281 gwei 0.0698 gwei 4,028x
Monad 102 gwei 0.0387 gwei 2,636x
Apechain 102 gwei 0.0550 gwei 1,849x
Sonic / Sei 55 gwei 0.0387 gwei ~1,420x

Ethereum and Base look correct only because they sit near the mixture's median.

Fix

Key the history by chainId so each chain's median is computed from its own samples. No change to the buffering or outlier logic itself.

Tests

New gas-manager.service.test.ts covers both directions:

  • an expensive chain polled rarely alongside a frequently polled cheap chain keeps its own price
  • a cheap chain polled rarely alongside a frequently polled expensive chain keeps its own price
  • repeated samples for a single chain still smooth as before

Verified the tests fail on the previous behaviour, reproducing the exact production value:

AssertionError: expected 2751100000000n to be 6600000n

Follow-up worth considering (not in this PR)

The currentToMedianRatio < 50n branch treats a genuinely low price as "stale/broken". Now that history is per chain that is much safer, but a real sustained drop (as Avalanche had post-ACP-176) would still be discarded for a while. Worth revisiting separately.

getGasPrice pushed every chain's gas price into one shared rolling history and took
the median across it. Chains served by the same node differ by orders of magnitude,
so that median is meaningless, and the outlier checks then discard a chain's real
price in favour of it:

  ratio < 50%  -> "stale/broken", use the median
  ratio > 500% -> "outlier", use the median

Observed on the staging node: Avalanche Fuji reports 160 wei, but was priced at
2,751,100,000,000 wei, which is the Chiliz median plus the 10% standard buffer.
Production shows the inverse, with the cheap chains dominating the median:

  Chiliz    real 2501 gwei   priced 0.0387 gwei
  Polygon   real  281 gwei   priced 0.0698 gwei
  Monad     real  102 gwei   priced 0.0387 gwei
  Apechain  real  102 gwei   priced 0.0550 gwei
  Sonic/Sei real   55 gwei   priced 0.0387 gwei

Key the history by chainId so each chain's median is computed from its own samples.
Adds a regression test covering both directions of the mixture.
@fichiokaku
fichiokaku merged commit 63e07c9 into develop Jul 30, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants