fix: linea gas fee flow and user-op gas buffer truncate fractional muln multipliers - #10045
Open
gomesalexandre wants to merge 2 commits into
Open
fix: linea gas fee flow and user-op gas buffer truncate fractional muln multipliers#10045gomesalexandre wants to merge 2 commits into
gomesalexandre wants to merge 2 commits into
Conversation
…ln multipliers BN.muln() requires an integer multiplier but never validates that. It silently truncates non-integer input per 26-bit word instead of throwing, so LineaGasFeeFlow's 1.35/1.7/1.05/1.1 multipliers and the user-operation controller's 1.5 gas buffer multiplier were producing wrong, under-computed values instead of the intended ones. On Linea this reaches what actually gets signed by default. On the user-operation side it only bites once an estimate needs more than one 26-bit BN word (~67.1M+), so it's a real but lower-frequency instance of the same class. Fixed both to use fractionBN (target.mul(numerator).div(denominator)), which does full-precision BN math instead of muln's fixed-width path - the same helper already used correctly for this exact purpose at transaction-controller/src/utils/gas.ts:401. Recomputed the two pinned LineaGasFeeFlow.test.ts hex expectations that were snapshotting the buggy truncated output, and added a test to each affected suite that computes its expected value independently (BigInt exact math / a magnitude chosen to fail under the old muln) rather than asserting another pinned snapshot - verified these fail against the old implementation with the exact wrong values.
gomesalexandre
marked this pull request as ready for review
September 1, 2026 12:04
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
LineaGasFeeFlow(@metamask/transaction-controller) andnormalizeGasEstimate(@metamask/user-operation-controller) both apply a decimal multiplier to aBNviaBN.muln(fractionalNumber).bn.js'simulnrequires an integer multiplier but never validates that — it doesw & 0x3ffffffper 26-bit word, so a fractional multiplier gets silently truncated instead of throwing. The value that comes out the other end is wrong and under-computed, and on the Linea side it reaches what actually gets signed by default (RandomisedEstimationsGasFeeFlowonly supersedes this flow behind a remote feature flag).Repro (pinned
bn.js@5.2.2, exactly what ships in this repo)The transaction-controller side is live right now:
LineaGasFeeFlow.test.ts's existing pinned expectations (0x23a3d70a3,0x25658bf25for priority fees,0x3a7ae1479,0x42428f5c1for max fees) are snapshots of the buggy output, not independently-verified correct values — I confirmed this by computing the oldmulnoutput directly and it matches the pinned hex exactly.The user-operation-controller side (
GAS_ESTIMATE_MULTIPLIER = 1.5) needs the estimate to cross 2^26 (~67.1M) before it's wrong, which is above typical UserOperation gas but not impossible (heavy calldata/batched ops, chains with higher gas costs). Included as the same bug class with the same trivial fix, not as the headline issue.Fix
Both now use
fractionBN(already exported from@metamask/controller-utils, and already used correctly for exactly this purpose atpackages/transaction-controller/src/utils/gas.ts:401) instead ofmuln.fractionBNdoestarget.mul(numerator).div(denominator)— full-precisionBNmultiplication, not the fixed-widthimulnpath — so it has no truncation boundary. The decimal multiplier is converted to an integer numerator/denominator pair viaMath.round(multiplier * PRECISION)/PRECISION(guards againstmultiplier * 100landing on a non-integer float, e.g.1.1 * 100 === 110.00000000000001in JS).Tests
LineaGasFeeFlow.test.tsthat were snapshotting the buggy output (see repro above for how I know the old values were wrong, not just different).BigInt/exact decimal math and uses an input magnitude chosen specifically to fail against the oldmulnimplementation. Verified this directly: stashed just the source fix (kept the new tests) and confirmed 3 tests fail with the exact truncated values shown above; restored the fix and all pass.Risk
Low — this only ever increases the previously-under-computed medium/high estimates toward the mathematically-intended value; it never changes the
lowlevel (multiplier1, unaffected either way) and never produces a value the code wasn't already trying to produce.Scope note
Kept to the two
muln-with-fractional-multiplier call sites found viarg. Did not touch otherBNarithmetic in either package.Note
Medium Risk
Linea default gas fees and user-op gas buffers increase toward previously intended values, affecting what users see and sign; behavior is corrective but touches fee-critical paths.
Overview
Fixes silent under-estimation when applying decimal gas multipliers with
BN.muln, which truncates fractional inputs per 26-bit word instead of multiplying correctly.LineaGasFeeFlownow scales base and priority fees for medium/high viafractionBNthrough a new#applyMultiplierhelper (replacingbase.muln(1.35),1.7,1.05,1.1). Medium and high max fee and priority fee values increase toward the intended math; low (multiplier1) is unchanged. Tests update previously wrong pinned hex expectations and add a case that asserts exact fractional math.normalizeGasEstimatein user-operation-controller applies the same pattern for the 1.5× gas buffer, fixing wrong buffered limits when estimates exceed ~67M gas. A regression test covers a largecallGasLimit.Changelogs document both fixes (#10045).
Reviewed by Cursor Bugbot for commit 4a6d230. Bugbot is set up for automated code reviews on this repo. Configure here.