Skip to content

Commit cf49eb8

Browse files
feat: add mempool.space fee market (#1927)
1 parent 7cd8c0c commit cf49eb8

File tree

21 files changed

+312
-94
lines changed

21 files changed

+312
-94
lines changed

apps/browser-extension-wallet/.env.defaults

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,6 @@ E2E_FORCE_TREZOR_PICKED=false
148148

149149
# ADA handle cache lifetime
150150
HANDLE_RESOLUTION_CACHE_LIFETIME=600000
151+
152+
# mempool.space api
153+
MEMPOOLSPACE_URL=https://mempool.lw.iog.io

apps/browser-extension-wallet/.env.developerpreview

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,6 @@ LACE_EXTENSION_UNINSTALL_REDIRECT_URL=
9999

100100
# Midnight
101101
MIDNIGHT_EVENT_BANNER_REMINDER_TIME=129600000
102+
103+
# mempool.space api
104+
MEMPOOLSPACE_URL=https://mempool.lw.iog.io

apps/browser-extension-wallet/.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,6 @@ MIN_NUMBER_OF_COSIGNERS=2
117117

118118
# POLLING PAUSE AFTER INACTIVITY
119119
SESSION_TIMEOUT=300000
120+
121+
# mempool.space api
122+
MEMPOOLSPACE_URL=https://mempool.lw.iog.io

apps/browser-extension-wallet/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"permissions": ["webRequest", "storage", "tabs", "unlimitedStorage"],
1919
"host_permissions": ["<all_urls>"],
2020
"content_security_policy": {
21-
"extension_pages": "default-src 'self' $LOCALHOST_DEFAULT_SRC; frame-src https://connect.trezor.io/ https://www.youtube-nocookie.com; script-src 'self' 'wasm-unsafe-eval'; font-src 'self' data: https://use.typekit.net; object-src 'self'; connect-src $BLOCKFROST_URLS $MAESTRO_URLS $CARDANO_SERVICES_URLS $CARDANO_WS_SERVER_URLS $SENTRY_URL $DAPP_RADAR_APPI_URL https://coingecko.live-mainnet.eks.lw.iog.io https://coingecko.live-mainnet.eks.lw.iog.io https://muesliswap.live-mainnet.eks.lw.iog.io $LOCALHOST_CONNECT_SRC $POSTHOG_HOST https://use.typekit.net https://api.handle.me/ https://*.api.handle.me/ data:; style-src * 'unsafe-inline'; img-src * data: blob:;"
21+
"extension_pages": "default-src 'self' $LOCALHOST_DEFAULT_SRC; frame-src https://connect.trezor.io/ https://www.youtube-nocookie.com; script-src 'self' 'wasm-unsafe-eval'; font-src 'self' data: https://use.typekit.net; object-src 'self'; connect-src $MEMPOOLSPACE_URL $BLOCKFROST_URLS $MAESTRO_URLS $CARDANO_SERVICES_URLS $CARDANO_WS_SERVER_URLS $SENTRY_URL $DAPP_RADAR_APPI_URL https://coingecko.live-mainnet.eks.lw.iog.io https://coingecko.live-mainnet.eks.lw.iog.io https://muesliswap.live-mainnet.eks.lw.iog.io $LOCALHOST_CONNECT_SRC $POSTHOG_HOST https://use.typekit.net https://api.handle.me/ https://*.api.handle.me/ data:; style-src * 'unsafe-inline'; img-src * data: blob:;"
2222
},
2323
"content_scripts": [
2424
{

apps/browser-extension-wallet/src/lib/scripts/background/wallet.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,15 @@ const bitcoinWalletFactory: BitcoinWalletFactory<Wallet.WalletMetadata, Wallet.A
254254
Bitcoin.Network.Mainnet
255255
);
256256

257+
const featureFlags = await getFeatureFlags(
258+
network === Bitcoin.Network.Testnet ? Cardano.NetworkMagics.Preprod : Cardano.NetworkMagics.Mainnet
259+
);
260+
const useMempoolSpaceFeeMarket = isExperimentEnabled(featureFlags, ExperimentName.MEMPOOLSPACE_FEE_MARKET);
261+
262+
const feeMarketProvider = useMempoolSpaceFeeMarket
263+
? new Bitcoin.MempoolSpaceMarketProvider(process.env.MEMPOOLSPACE_URL, logger, network)
264+
: new Bitcoin.MaestroFeeMarketProvider(provider, logger, network);
265+
257266
if (!walletAccount.metadata.bitcoin) {
258267
throw new Error('Bitcoin metadata not found');
259268
}
@@ -278,6 +287,7 @@ const bitcoinWalletFactory: BitcoinWalletFactory<Wallet.WalletMetadata, Wallet.A
278287
currentBitcoinWalletProvider$.next(provider);
279288
return new Bitcoin.BitcoinWallet(
280289
provider,
290+
feeMarketProvider,
281291
localPollingIntervalConfig,
282292
20,
283293
walletInfo,

apps/browser-extension-wallet/src/lib/scripts/types/feature-flags.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ export enum ExperimentName {
1414
SEND_CONSOLE_ERRORS_TO_SENTRY = 'send-console-errors-to-sentry',
1515
BITCOIN_WALLETS = 'bitcoin-wallets',
1616
NFTPRINTLAB = 'nftprintlab',
17-
GLACIER_DROP = 'glacier-drop'
17+
GLACIER_DROP = 'glacier-drop',
18+
MEMPOOLSPACE_FEE_MARKET = 'bitcoin-mempool-space-fee-market'
1819
}
1920

2021
export type FeatureFlag = `${ExperimentName}`;

apps/browser-extension-wallet/src/providers/PostHogClientProvider/client/config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ const defaultFeatureFlags: FeatureFlags = {
3838
[ExperimentName.SEND_CONSOLE_ERRORS_TO_SENTRY]: false,
3939
[ExperimentName.BITCOIN_WALLETS]: false,
4040
[ExperimentName.NFTPRINTLAB]: false,
41-
[ExperimentName.GLACIER_DROP]: false
41+
[ExperimentName.GLACIER_DROP]: false,
42+
[ExperimentName.MEMPOOLSPACE_FEE_MARKET]: false
4243
};
4344

4445
export const featureFlagsByNetworkInitialValue: FeatureFlagsByNetwork = {

apps/browser-extension-wallet/webpack-utils.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ const transformManifest = (content, mode, jsAssets = []) => {
5454
: 'http://localhost:* http://127.0.0.1:*'
5555
)
5656
.replace('$POSTHOG_HOST', process.env.POSTHOG_HOST)
57+
.replace('$MEMPOOLSPACE_URL', process.env.MEMPOOLSPACE_URL)
5758
.replace('$SENTRY_URL', constructSentryConnectSrc(process.env.SENTRY_DSN))
5859
.replace('$DAPP_RADAR_APPI_URL', process.env.DAPP_RADAR_API_URL);
5960

packages/bitcoin/README.md

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,46 +10,44 @@ It exposes RxJS streams (balance$, utxos$, transactionHistory$) so front-ends ca
1010

1111
The package is organized in 5 modules:
1212

13-
1413
<p align="center">
1514
<img align="middle" src="./assets/modules_chart.png"/>
1615
</p>
1716

1817
### **Modules Description**
1918

20-
| Module | Purpose |
21-
|--------------------------------------------|-----------------------------------------------------------------------------------------------------------|
22-
| **common/** | Pure, stateless helpers that can be reused by every other module. No network or side-effects. |
23-
| `common/address.ts` | Convert compressed pubkeys to P2PKH, P2SH-P2WPKH, P2WPKH and P2TR addresses; validate addresses. |
24-
| `common/constants.ts` | Shared byte-sizes (input/output/overhead), dust threshold, etc. |
25-
| `common/info.ts` | Type that groups the five account-level XPUBs produced for each address type. |
26-
| `common/keyDerivation.ts` | BIP-39/BIP-32/Electrum seed derivation, account & child key derivation, Taproot tweaks. |
27-
| `common/network.ts` | Enum for Mainnet / Testnet and related helpers. |
28-
| `common/taproot.ts` | Low-level functions to compute BIP-341 tagged hashes and tweak x-only pub/priv keys. |
29-
| `common/transaction.ts` | UnsignedTransaction / SignedTransaction type definitions. |
30-
| **input-selection/** | Pluggable coin-selection strategies. |
31-
| `input-selection/InputSelector.ts` | Interface describing the contract for any selector implementation. |
32-
| `input-selection/GreedyInputSelector.ts` | Deterministic greedy algorithm that selects largest UTXOs until target + fee is met. |
33-
| **providers/** | Abstract access to the Bitcoin network and on-chain data. |
34-
| `providers/BlockchainDataProvider.ts` | Interface for reading chain state, mempool, fee rates, and broadcasting TXs. |
35-
| `providers/MaestroBitcoinDataProvider.ts` | Concrete HTTP client against Maestro’s REST API. |
36-
| `providers/StubBitcoinDataProvider.ts` | In-memory mock that returns deterministic data for unit tests. |
37-
| `providers/BlockchainInputResolver.ts` | Helper that maps a (txid, vout) pair to a full UTxO by calling the provider. |
38-
| **tx-builder/** | Everything needed to construct a PSBT ready for signing. |
39-
| `tx-builder/TransactionBuilder.ts` | Accepts outputs, change address, selector, builds PSBT, estimates vBytes & fee. |
40-
| `tx-builder/utils.ts` | Constants & helpers specific to size/fee calculation inside the builder. |
41-
| **wallet/** | Stateful, observable wallet core and signer. |
42-
| `wallet/BitcoinWallet.ts` | Polls provider, maintains `balance$`, `utxos$`, history & pending TXs via RxJS. |
43-
| `wallet/BitcoinSigner.ts` | Thin wrapper around `@bitcoinerlab/secp256k1` for PSBT input signing + key zeroisation. |
44-
| `index.ts` | Package entry point. |
19+
| Module | Purpose |
20+
| ----------------------------------------- | ------------------------------------------------------------------------------------------------ |
21+
| **common/** | Pure, stateless helpers that can be reused by every other module. No network or side-effects. |
22+
| `common/address.ts` | Convert compressed pubkeys to P2PKH, P2SH-P2WPKH, P2WPKH and P2TR addresses; validate addresses. |
23+
| `common/constants.ts` | Shared byte-sizes (input/output/overhead), dust threshold, etc. |
24+
| `common/info.ts` | Type that groups the five account-level XPUBs produced for each address type. |
25+
| `common/keyDerivation.ts` | BIP-39/BIP-32/Electrum seed derivation, account & child key derivation, Taproot tweaks. |
26+
| `common/network.ts` | Enum for Mainnet / Testnet and related helpers. |
27+
| `common/taproot.ts` | Low-level functions to compute BIP-341 tagged hashes and tweak x-only pub/priv keys. |
28+
| `common/transaction.ts` | UnsignedTransaction / SignedTransaction type definitions. |
29+
| **input-selection/** | Pluggable coin-selection strategies. |
30+
| `input-selection/InputSelector.ts` | Interface describing the contract for any selector implementation. |
31+
| `input-selection/GreedyInputSelector.ts` | Deterministic greedy algorithm that selects largest UTXOs until target + fee is met. |
32+
| **providers/** | Abstract access to the Bitcoin network and on-chain data. |
33+
| `providers/BlockchainDataProvider.ts` | Interface for reading chain state, mempool, fee rates, and broadcasting TXs. |
34+
| `providers/MaestroBitcoinDataProvider.ts` | Concrete HTTP client against Maestro’s REST API. |
35+
| `providers/StubBitcoinDataProvider.ts` | In-memory mock that returns deterministic data for unit tests. |
36+
| `providers/BlockchainInputResolver.ts` | Helper that maps a (txid, vout) pair to a full UTxO by calling the provider. |
37+
| **tx-builder/** | Everything needed to construct a PSBT ready for signing. |
38+
| `tx-builder/TransactionBuilder.ts` | Accepts outputs, change address, selector, builds PSBT, estimates vBytes & fee. |
39+
| `tx-builder/utils.ts` | Constants & helpers specific to size/fee calculation inside the builder. |
40+
| **wallet/** | Stateful, observable wallet core and signer. |
41+
| `wallet/BitcoinWallet.ts` | Polls provider, maintains `balance$`, `utxos$`, history & pending TXs via RxJS. |
42+
| `wallet/BitcoinSigner.ts` | Thin wrapper around `@bitcoinerlab/secp256k1` for PSBT input signing + key zeroisation. |
43+
| `index.ts` | Package entry point. |
4544

4645
## **High Level Overview**
4746

4847
### Common
4948

5049
Handles all deterministic key derivation (BIP-32, Electrum), address transformation, and Taproot tweaking. It is strictly pure code with no network or disk I/O. It contains some common types shared across all other modules.
5150

52-
5351
### Provider
5452

5553
A thin abstraction (BlockchainDataProvider) encodes everything the wallet needs from a chain service: block tip, UTXOs, mempool, fee estimates, and broadcast. The provider isolates all network I/O and normalises raw REST/RPC responses into the package’s own types (BlockInfo, UTxO, TransactionHistoryEntry, …).
@@ -75,5 +73,3 @@ This module provides:
7573
- **Input** selection Extension point for input selection, comes with a GreedyInputSelector that implements a simple, deterministic algorithm.
7674
- **Builder** TransactionBuilder composes PSBTs, computes vBytes, and enforces dust rules.
7775
- **Signer** BitcoinSigner wraps @bitcoinerlab/secp256k1, signs each PSBT input, and scrubs the private key from memory.
78-
79-
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export const INPUT_SIZE = 68;
2-
export const OUTPUT_SIZE = 34;
2+
export const OUTPUT_SIZE = 31;
33
export const TRANSACTION_OVERHEAD = 10;
44
export const DUST_THRESHOLD = 546;

0 commit comments

Comments
 (0)