Skip to content

Commit 39b0487

Browse files
ghgoodreaumicaelae
andauthored
feat: bitcoin fee calc for display and error handling (#6932)
## Explanation <!-- Thanks for your contribution! Take a moment to answer these questions so that reviewers have the information they need to properly understand your changes: * What is the current state of things and why does it need to change? * What is the solution your changes offer and how does it work? * Are there any changes whose purpose might not obvious to those unfamiliar with the domain? * If your primary goal was to update one package but you found you had to update another one along the way, why did you do so? * If you had to upgrade a dependency, why did you do so? --> Fix Bitcoin network fee computation in bridge quotes Problem: Bitcoin bridge quotes were not computing network fees, causing them to always show $0 for gas costs. Root Cause: The appendNonEvmFees function only called the snap's computeFee method when trade was a string. This worked for Solana (which has string trades), but Bitcoin trades are objects with structure { unsignedPsbtBase64: string, inputsToSign: null }. Fix: Remove the typeof trade === 'string' check Extract unsignedPsbtBase64 from Bitcoin trade objects before passing to snap Support both 'base' (Solana) and 'priority' (Bitcoin) fee types in snap response Add error handling to return quotes with nonEvmFeesInNative: undefined if snap fails. > snap fails on bitcoin when the account does not have enough for gas. we added this error handling to allow 'quote shopping' on bitcoin without a runtime error causing the quote to fail. ## References <!-- Are there any issues that this pull request is tied to? Are there other links that reviewers should consult to understand these changes better? Are there client or consumer pull requests to adopt any breaking changes? For example: * Fixes #12345 * Related to #67890 --> Draft client PR: MetaMask/metamask-extension#37137 ## Checklist - [x] I've updated the test suite for new or updated code as appropriate - [x] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [x] I've communicated my changes to consumers by [updating changelogs for packages I've changed](https://github.com/MetaMask/core/tree/main/docs/contributing.md#updating-changelogs), highlighting breaking changes as necessary - [x] I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Fix Bitcoin fee computation in bridge quotes by normalizing trade data for snaps, supporting 'priority' fees, and adding graceful error handling. > > - **Bridge fees (non‑EVM)**: > - Normalize trade data for snap fee computation: use string directly (Solana) or extract `unsignedPsbtBase64` from `BitcoinTradeData` (Bitcoin). > - Support `'priority'` fee type (Bitcoin) in addition to `'base'`; fallback to first returned fee. > - Add guards for missing snap/trade; wrap in try/catch and set `nonEvmFeesInNative` to `undefined` on failures; improved error logging with `quote.requestId`. > - **Types/exports**: > - Export `BitcoinTradeData`; consume it in `quote-fees`. > - **Changelog**: > - Document fix for Bitcoin network fee computation under Unreleased. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 45987f7. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Micaela <[email protected]>
1 parent f144990 commit 39b0487

File tree

3 files changed

+39
-7
lines changed

3 files changed

+39
-7
lines changed

packages/bridge-controller/CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Changed
1111

12-
- Clean up SSE stream reader after use ([#6965](https://github.com/MetaMask/core/pull/6965))p
12+
- Clean up SSE stream reader after use ([#6965](https://github.com/MetaMask/core/pull/6965))
13+
14+
### Fixed
15+
16+
- Fix Bitcoin network fee computation by extracting `unsignedPsbtBase64` from Bitcoin trade objects and supporting `'priority'` fee type from Bitcoin snap ([#XXXX](https://github.com/MetaMask/core/pull/XXXX))
1317

1418
## [56.0.0]
1519

packages/bridge-controller/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export type {
4040
QuoteResponse,
4141
FeeData,
4242
TxData,
43+
BitcoinTradeData,
4344
BridgeControllerState,
4445
BridgeControllerAction,
4546
BridgeControllerActions,

packages/bridge-controller/src/utils/quote-fees.ts

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type {
1212
NonEvmFees,
1313
TxData,
1414
BridgeControllerMessenger,
15+
BitcoinTradeData,
1516
} from '../types';
1617

1718
/**
@@ -109,14 +110,27 @@ const appendNonEvmFees = async (
109110
quotes.map(async (quoteResponse) => {
110111
const { trade, quote } = quoteResponse;
111112

112-
if (selectedAccount?.metadata?.snap?.id && typeof trade === 'string') {
113+
// Skip fee computation if no snap account or trade data
114+
if (!selectedAccount?.metadata?.snap?.id || !trade) {
115+
return quoteResponse;
116+
}
117+
118+
try {
113119
const scope = formatChainIdToCaip(quote.srcChainId);
114120

121+
// Normalize trade data to string format expected by snap
122+
// Solana: trade is already a base64 transaction string
123+
// Bitcoin: extract unsignedPsbtBase64 from trade object
124+
const transaction =
125+
typeof trade === 'string'
126+
? trade
127+
: (trade as BitcoinTradeData).unsignedPsbtBase64;
128+
115129
const response = (await messenger.call(
116130
'SnapController:handleRequest',
117131
computeFeeRequest(
118132
selectedAccount.metadata.snap?.id,
119-
trade,
133+
transaction,
120134
selectedAccount.id,
121135
scope,
122136
),
@@ -130,16 +144,29 @@ const appendNonEvmFees = async (
130144
};
131145
}[];
132146

133-
const baseFee = response?.find((fee) => fee.type === 'base');
134-
// Store fees in native units as returned by the snap (e.g., SOL, BTC)
135-
const feeInNative = baseFee?.asset?.amount || '0';
147+
// Bitcoin snap returns 'priority' fee, Solana returns 'base' fee
148+
const fee =
149+
response?.find((f) => f.type === 'base') ||
150+
response?.find((f) => f.type === 'priority') ||
151+
response?.[0];
152+
const feeInNative = fee?.asset?.amount || '0';
136153

137154
return {
138155
...quoteResponse,
139156
nonEvmFeesInNative: feeInNative,
140157
};
158+
} catch (error) {
159+
// Return quote with undefined fee if snap fails (e.g., insufficient UTXO funds)
160+
// Client can render special UI or skip the quote card row for quotes with missing fee data
161+
console.error(
162+
`Failed to compute non-EVM fees for quote ${quote.requestId}:`,
163+
error,
164+
);
165+
return {
166+
...quoteResponse,
167+
nonEvmFeesInNative: undefined,
168+
};
141169
}
142-
return quoteResponse;
143170
}),
144171
);
145172

0 commit comments

Comments
 (0)