@pioneer-platform/blockbook 8.12.0 → 8.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @pioneer-platform/blockbook
2
2
 
3
+ ## 8.12.2
4
+
5
+ ### Patch Changes
6
+
7
+ - chore: 🔒 CRITICAL FIX: XRP destination tag validation
8
+
9
+ ## 8.12.1
10
+
11
+ ### Patch Changes
12
+
13
+ - 🔒 CRITICAL FIX: XRP destination tag validation
14
+
3
15
  ## 8.12.0
4
16
 
5
17
  ### Minor Changes
@@ -0,0 +1,142 @@
1
+ # ✅ Critical Fix: Unconfirmed Balance Support
2
+
3
+ ## Problem
4
+
5
+ **CRITICAL UX BUG**: When a user had pending/unconfirmed Bitcoin transactions, the system would show **0 balance** instead of showing the unconfirmed balance. This made users think all their Bitcoin had disappeared!
6
+
7
+ ### Root Cause
8
+
9
+ The `get_balance_by_xpub()` function in `utxo-network` only summed confirmed UTXOs from the `/api/v2/utxo/{xpub}` endpoint. When UTXOs were spent in an unconfirmed transaction:
10
+
11
+ - ✅ Confirmed UTXOs: spent (returns empty array)
12
+ - ❌ Unconfirmed balance: **NOT CHECKED** (showed as $0.00)
13
+
14
+ Result: **User sees 0 balance even though they have pending transactions!**
15
+
16
+ ## Solution
17
+
18
+ ### 1. Backend Fix (`utxo-network/src/index.ts`)
19
+
20
+ Added unconfirmed balance check to `get_balance_by_xpub`:
21
+
22
+ ```typescript
23
+ // CRITICAL FIX: Also get unconfirmed balance from xpub info
24
+ // When UTXOs are spent but not confirmed, balance would show 0 without this
25
+ let unconfirmedBalance = 0;
26
+ try {
27
+ const xpubInfo = await blockbook.getPubkeyInfo(coin, xpub);
28
+ unconfirmedBalance = parseInt(xpubInfo.unconfirmedBalance || '0');
29
+
30
+ if (isBitcoin) log.info(tag, '🔍 [BITCOIN] Xpub info:', {
31
+ confirmedBalance: xpubInfo.balance,
32
+ unconfirmedBalance: xpubInfo.unconfirmedBalance,
33
+ totalReceived: xpubInfo.totalReceived,
34
+ totalSent: xpubInfo.totalSent,
35
+ txCount: xpubInfo.txs
36
+ });
37
+ } catch (e) {
38
+ log.warn(tag, 'Failed to fetch xpub info for unconfirmed balance:', e);
39
+ // Continue with just confirmed balance
40
+ }
41
+
42
+ const totalBalance = balance + unconfirmedBalance;
43
+
44
+ // Return balance, unconfirmed balance, and node URL
45
+ return {
46
+ balance: totalBalance,
47
+ unconfirmedBalance,
48
+ confirmedBalance: balance,
49
+ nodeUrl
50
+ }
51
+ ```
52
+
53
+ ### 2. API Controller Fix (`pioneer-server/src/controllers/utxo.controller.ts`)
54
+
55
+ Updated `GetBalanceByXpub` to return balance breakdown:
56
+
57
+ ```typescript
58
+ public async GetBalanceByXpub(
59
+ @Path() network: string,
60
+ @Path() xpub: string
61
+ ): Promise<{
62
+ balance: string;
63
+ unconfirmedBalance?: number;
64
+ confirmedBalance?: number;
65
+ nodeUrl?: string
66
+ } | BasicResponse> {
67
+ const balanceInfo = await utxoNetwork.getBalanceByXpub(network, xpub);
68
+
69
+ return {
70
+ balance: balanceInfo.balance.toString(),
71
+ unconfirmedBalance: balanceInfo.unconfirmedBalance || 0,
72
+ confirmedBalance: balanceInfo.confirmedBalance || balanceInfo.balance,
73
+ nodeUrl: balanceInfo.nodeUrl
74
+ };
75
+ }
76
+ ```
77
+
78
+ ## API Response Format
79
+
80
+ ### Before (Broken)
81
+ ```json
82
+ {
83
+ "balance": "0" ← Shows 0 even with unconfirmed transactions!
84
+ }
85
+ ```
86
+
87
+ ### After (Fixed)
88
+ ```json
89
+ {
90
+ "balance": "74953", ← Total balance (confirmed + unconfirmed)
91
+ "confirmedBalance": 0, ← Confirmed balance
92
+ "unconfirmedBalance": 74953, ← Unconfirmed balance (pending TX)
93
+ "nodeUrl": "https://btcbook.nownodes.io/..."
94
+ }
95
+ ```
96
+
97
+ ## Testing
98
+
99
+ ### Test Case: Pending Transaction
100
+
101
+ ```bash
102
+ # Wallet with pending transaction
103
+ curl "http://localhost:9001/api/v1/utxo/balance/BTC/{zpub}"
104
+
105
+ # Expected: Shows unconfirmed balance
106
+ {
107
+ "balance": "74953",
108
+ "confirmedBalance": 0,
109
+ "unconfirmedBalance": 74953
110
+ }
111
+ ```
112
+
113
+ ### Test Case: Fully Confirmed Balance
114
+
115
+ ```bash
116
+ # Wallet with confirmed balance
117
+ curl "http://localhost:9001/api/v1/utxo/balance/BTC/{zpub}"
118
+
119
+ # Expected: Shows confirmed balance only
120
+ {
121
+ "balance": "74953",
122
+ "confirmedBalance": 74953,
123
+ "unconfirmedBalance": 0
124
+ }
125
+ ```
126
+
127
+ ## Files Changed
128
+
129
+ 1. `modules/coins/utxo/utxo-network/src/index.ts` - Added unconfirmed balance query
130
+ 2. `services/pioneer-server/src/controllers/utxo.controller.ts` - Updated response format
131
+
132
+ ## Impact
133
+
134
+ ✅ **Users will no longer panic when they see $0.00 balance during pending transactions**
135
+ ✅ **UI can now show "Pending: X BTC" separately from confirmed balance**
136
+ ✅ **Proper debugging with detailed balance breakdown in logs**
137
+
138
+ ## Related
139
+
140
+ - Blockbook API: `/api/v2/xpub/{xpub}` returns `unconfirmedBalance` field
141
+ - Blockbook API: `/api/v2/utxo/{xpub}?confirmed=false` returns unconfirmed UTXOs (but is empty when spent)
142
+
package/lib/index.d.ts CHANGED
@@ -50,7 +50,13 @@ declare let broadcast_transaction: (coin: string, hex: string) => Promise<{
50
50
  txid?: undefined;
51
51
  }>;
52
52
  declare let get_transaction: (coin: string, txid: string) => Promise<any>;
53
- declare let get_utxos_by_xpub: (coin: string, xpub: string) => Promise<any>;
53
+ declare let get_utxos_by_xpub: (coin: string, xpub: string) => Promise<{
54
+ data: any;
55
+ nodeUrl: string;
56
+ } | undefined>;
54
57
  declare let update_node_performance: (coin: string, url: string, success: boolean, responseTime: number) => void;
55
- declare let get_balance_by_xpub: (coin: string, xpub: any) => Promise<number>;
58
+ declare let get_balance_by_xpub: (coin: string, xpub: any) => Promise<{
59
+ balance: number;
60
+ nodeUrl: string;
61
+ }>;
56
62
  declare let get_node_info: () => Promise<boolean>;
package/lib/index.js CHANGED
@@ -757,7 +757,8 @@ var get_utxos_by_xpub = function (coin, xpub) {
757
757
  if (isBitcoin) {
758
758
  log.info(tag, '🔍 [BITCOIN BLOCKBOOK] Returning UTXOs:', resp.data);
759
759
  }
760
- return [2 /*return*/, resp.data];
760
+ // Return both data and the node URL that was used
761
+ return [2 /*return*/, { data: resp.data, nodeUrl: node.url }];
761
762
  case 5:
762
763
  error_2 = _b.sent();
763
764
  responseTime = Date.now() - startTime;
@@ -843,7 +844,7 @@ var update_node_performance = function (coin, url, success, responseTime) {
843
844
  };
844
845
  var get_balance_by_xpub = function (coin, xpub) {
845
846
  return __awaiter(this, void 0, void 0, function () {
846
- var tag, output, balance, i, uxto, e_10;
847
+ var tag, result, output, nodeUrl, balance, i, uxto, e_10;
847
848
  return __generator(this, function (_a) {
848
849
  switch (_a.label) {
849
850
  case 0:
@@ -855,7 +856,12 @@ var get_balance_by_xpub = function (coin, xpub) {
855
856
  log.debug(tag, "xpub: ", xpub);
856
857
  return [4 /*yield*/, get_utxos_by_xpub(coin, xpub)];
857
858
  case 2:
858
- output = _a.sent();
859
+ result = _a.sent();
860
+ if (!result) {
861
+ throw new Error('No result from get_utxos_by_xpub');
862
+ }
863
+ output = result.data;
864
+ nodeUrl = result.nodeUrl;
859
865
  log.debug(tag, "output: ", output);
860
866
  balance = 0;
861
867
  //tally
@@ -863,7 +869,8 @@ var get_balance_by_xpub = function (coin, xpub) {
863
869
  uxto = output[i];
864
870
  balance = balance + parseInt(uxto.value);
865
871
  }
866
- return [2 /*return*/, balance / 100000000];
872
+ // Return both balance and node URL
873
+ return [2 /*return*/, { balance: balance / 100000000, nodeUrl: nodeUrl }];
867
874
  case 3:
868
875
  e_10 = _a.sent();
869
876
  console.error(tag, e_10);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pioneer-platform/blockbook",
3
- "version": "8.12.0",
3
+ "version": "8.12.2",
4
4
  "main": "./lib/index.js",
5
5
  "types": "./lib/index.d.ts",
6
6
  "scripts": {