Skip to content

fix: round base fee multiplier instead of ceiling it in fee estimation#4862

Open
Kropiunig wants to merge 2 commits into
wevm:mainfrom
Kropiunig:fix/base-fee-multiplier-rounding
Open

fix: round base fee multiplier instead of ceiling it in fee estimation#4862
Kropiunig wants to merge 2 commits into
wevm:mainfrom
Kropiunig:fix/base-fee-multiplier-rounding

Conversation

@Kropiunig

Copy link
Copy Markdown
Contributor

Summary

estimateFeesPerGas (and fillTransaction) apply the baseFeeMultiplier using fixed-point integer math:

const decimals = baseFeeMultiplier.toString().split('.')[1]?.length ?? 0
const denominator = 10 ** decimals
const multiply = (base: bigint) =>
  (base * BigInt(Math.ceil(baseFeeMultiplier * denominator))) / BigInt(denominator)

baseFeeMultiplier * denominator is mathematically an exact integer — that is the whole purpose of deriving decimals/denominator from the number's decimal places. It only needs a guard because of floating-point error, but Math.ceil guards it in the wrong direction. When the float product lands just above the integer, Math.ceil bumps it to the next integer:

  • 1.09 * 100 === 109.00000000000001Math.ceil110 (applies 1.10, not 1.09)
  • also affects 1.11 (→ 112), 1.12 (→ 113), and others

So a caller that sets baseFeeMultiplier: 1.09 silently gets a 1.10 multiplier. It is inconsistent, too: 1.15 (114.999…) already works because its float error falls below the integer, which shows the ceil is not an intentional round-up.

Fix

Use Math.round instead of Math.ceil. The target is always an exact integer and the float error is far below 0.5, so round recovers the intended value in every case (and matches ceil wherever ceil was already correct, e.g. 2.01). The same one-line fix applies to the duplicated multiplyFee helper in fillTransaction.

Test

Extended the existing args: chain \baseFeeMultiplier` override (value)test inestimateFeesPerGas.test.tswith a1.09case assertingmaxFeePerGas === baseFeePerGas * 109n / 100n + maxPriorityFeePerGas. It fails on main(produces* 110n / 100n`) and passes with the fix.

`estimateFeesPerGas` and `fillTransaction` build an integer numerator for the
base fee multiplier as `baseFeeMultiplier * 10 ** decimals`. That product is
mathematically an exact integer, but floating-point error can push it slightly
above the target (e.g. `1.09 * 100` evaluates to `109.00000000000001`). Using
`Math.ceil` then rounds it up to `110`, silently applying a `1.10` multiplier
instead of the requested `1.09` (also affects `1.11`, `1.12`, and others).
`Math.round` recovers the intended integer in every case.
@vercel

vercel Bot commented Jul 19, 2026

Copy link
Copy Markdown

@Kropiunig is attempting to deploy a commit to the Wevm Team on Vercel.

A member of the Team first needs to authorize it.

@changeset-bot

changeset-bot Bot commented Jul 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3682860

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
viem Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jul 19, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/viem@4862

commit: 3682860

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