Skip to content

fix: replace broken PSBT-to-vbytes estimation with input/output parsing - #17

Open
Alicepoltora wants to merge 1 commit into
UTEXO-Protocol:mainfrom
Alicepoltora:fix/estimate-fee-vbyte-estimation
Open

Alicepoltora wants to merge 1 commit into
UTEXO-Protocol:mainfrom
Alicepoltora:fix/estimate-fee-vbyte-estimation

Conversation

@Alicepoltora

Copy link
Copy Markdown

Problem

estimateFee() uses signedPsbt.length * 3/4 * 0.4 to estimate transaction vbytes. This is wrong.

PSBT is a container format with metadata (derivation paths, witness UTXO descriptors, sig hashes, proprietary fields) that is not present in the final Bitcoin transaction. The raw PSBT is typically 2-3x larger than the transaction it produces.

The coefficient 0.4 is arbitrary and produces estimates ~60% too low. Example:

PSBT base64 length Old formula vbytes Actual tx vbytes Error
2000 chars ~200 ~153 -24%
4000 chars ~400 ~211 -47%

Impact

This affects two critical code paths:

  1. BareRgbLibBinding.estimateFee() — fee displayed to users before confirming transactions
  2. WalletAccountRgb.transfer() — the transferMaxFee guard that should prevent excessive fees

What can go wrong:

  • Stuck transactions: underestimated fee → transaction doesn't get mined → user funds frozen until timeout
  • Guard bypass: transferMaxFee guard uses the same formula → transactions exceeding the limit pass through undetected
  • Misleading UI: users see a low fee estimate, confirm the transaction, then pay (or wait for) a different real fee

Fix

Replace the naive size-based formula with PSBT binary parsing:

  1. Decode the base64 PSBT
  2. Parse the embedded unsigned transaction to count inputs and outputs
  3. Estimate vbytes using P2TR key-path constants (58 vbytes/input, 43 vbytes/output)

This is accurate (±5 vbytes) for the common case of P2TR key-path spending that RGB wallets use.

New src/fee-utils.js module provides estimateVbytesFromPsbt() used by both call sites.

The previous fee estimation used signedPsbt.length * 3/4 * 0.4 which
severely underestimates vbytes. PSBT is a container with metadata
(derivation paths, witness UTXO, sig hashes) that inflates its size
well beyond the final transaction. The 0.4 coefficient was arbitrary
and produced estimates ~60% too low.

New approach: parse the raw PSBT binary to count inputs and outputs
from the embedded unsigned transaction, then estimate vbytes using
P2TR key-path constants (58 vbytes/input, 43 vbytes/output).

Fixes both:
- BareRgbLibBinding.estimateFee() used by fee display
- WalletAccountRgb.transfer() used by transferMaxFee guard

Without this fix, transactions could get stuck in the mempool due to
underestimated fees, or the transferMaxFee guard could fail to catch
transactions exceeding the configured fee limit.
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.

1 participant