@pioneer-platform/pioneer-sdk 4.20.0 → 4.20.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/dist/index.cjs +594 -838
- package/dist/index.es.js +595 -838
- package/dist/index.js +595 -838
- package/package.json +2 -2
- package/src/TransactionManager.ts +21 -4
- package/src/fees/index.ts +60 -57
- package/src/fees/index.ts.backup +510 -0
- package/src/getPubkey.ts +57 -26
- package/src/index.ts +391 -61
- package/src/txbuilder/createUnsignedEvmTx.ts +118 -63
- package/src/txbuilder/createUnsignedRippleTx.ts +12 -11
- package/src/txbuilder/createUnsignedStakingTx.ts +13 -11
- package/src/txbuilder/createUnsignedTendermintTx.ts +41 -17
- package/src/txbuilder/createUnsignedUxtoTx.ts +122 -57
- package/src/txbuilder/createUnsignedUxtoTx.ts.backup +384 -0
- package/src/txbuilder/templates/mayachain.ts +2 -0
package/src/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { KeepKeySdk } from '@keepkey/keepkey-sdk';
|
|
2
2
|
import { caipToNetworkId, networkIdToCaip } from '@pioneer-platform/pioneer-caip';
|
|
3
|
-
import
|
|
3
|
+
import Pioneer from '@pioneer-platform/pioneer-client';
|
|
4
4
|
import { addressNListToBIP32, getPaths } from '@pioneer-platform/pioneer-coins';
|
|
5
5
|
import { assetData } from '@pioneer-platform/pioneer-discovery';
|
|
6
6
|
import { Events } from '@pioneer-platform/pioneer-events';
|
|
7
|
-
import
|
|
7
|
+
import EventEmitter from 'events';
|
|
8
8
|
|
|
9
9
|
import { getCharts } from './charts/index.js';
|
|
10
10
|
//internal
|
|
@@ -15,13 +15,73 @@ import { TransactionManager } from './TransactionManager.js';
|
|
|
15
15
|
import { createUnsignedTendermintTx } from './txbuilder/createUnsignedTendermintTx.js';
|
|
16
16
|
import { createUnsignedStakingTx, type StakingTxParams } from './txbuilder/createUnsignedStakingTx.js';
|
|
17
17
|
import { getFees, estimateTransactionFee, type NormalizedFeeRates, type FeeEstimate } from './fees/index.js';
|
|
18
|
-
import { detectKkApiAvailability } from './utils/kkapi-detection.js';
|
|
19
|
-
import { formatTime } from './utils/format-time.js';
|
|
20
|
-
import { buildDashboardFromBalances } from './utils/build-dashboard.js';
|
|
21
|
-
import { PubkeyManager, getPubkeyKey, deduplicatePubkeys, validatePubkey } from './utils/pubkey-helpers.js';
|
|
22
18
|
|
|
23
19
|
const TAG = ' | Pioneer-sdk | ';
|
|
24
20
|
|
|
21
|
+
// Utility function to detect if kkapi is available with smart environment detection
|
|
22
|
+
async function detectKkApiAvailability(forceLocalhost?: boolean): Promise<{
|
|
23
|
+
isAvailable: boolean;
|
|
24
|
+
baseUrl: string;
|
|
25
|
+
basePath: string;
|
|
26
|
+
}> {
|
|
27
|
+
const tag = `${TAG} | detectKkApiAvailability | `;
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
// Smart detection: Check environment (Tauri, browser, or Node.js)
|
|
31
|
+
const isTauri = typeof window !== 'undefined' && '__TAURI__' in window;
|
|
32
|
+
const isBrowser = typeof window !== 'undefined';
|
|
33
|
+
const isNodeJS = typeof process !== 'undefined' && process.versions && process.versions.node;
|
|
34
|
+
const isLocalhost = isBrowser && window.location.hostname === 'localhost';
|
|
35
|
+
|
|
36
|
+
// If in Tauri, use kkapi:// (will be proxied by Tauri)
|
|
37
|
+
if (isTauri) {
|
|
38
|
+
return {
|
|
39
|
+
isAvailable: true,
|
|
40
|
+
baseUrl: 'kkapi://',
|
|
41
|
+
basePath: 'kkapi://spec/swagger.json',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// In Node.js test environment or localhost browser, test if localhost:1646 is available
|
|
46
|
+
// Force localhost if flag is set
|
|
47
|
+
const shouldTestLocalhost = forceLocalhost || isLocalhost || isNodeJS;
|
|
48
|
+
|
|
49
|
+
if (shouldTestLocalhost) {
|
|
50
|
+
const testEnv = isNodeJS ? 'Node.js test environment' : 'development browser';
|
|
51
|
+
try {
|
|
52
|
+
const httpResponse = await fetch('http://localhost:1646/api/v1/health', {
|
|
53
|
+
method: 'GET',
|
|
54
|
+
signal: AbortSignal.timeout(1000), // 1 second timeout for localhost
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (httpResponse.ok) {
|
|
58
|
+
return {
|
|
59
|
+
isAvailable: true,
|
|
60
|
+
baseUrl: 'http://localhost:1646',
|
|
61
|
+
basePath: 'http://localhost:1646/spec/swagger.json',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
} catch (httpError: any) {
|
|
65
|
+
console.error('❌ [KKAPI DETECTION] HTTP localhost:1646 not available:', httpError.message);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Fallback for production non-Tauri or when vault server is not running
|
|
70
|
+
console.warn('⚠️ [KKAPI DETECTION] Using fallback config (vault may not be available)');
|
|
71
|
+
return {
|
|
72
|
+
isAvailable: false,
|
|
73
|
+
baseUrl: 'http://localhost:1646',
|
|
74
|
+
basePath: 'http://localhost:1646/spec/swagger.json',
|
|
75
|
+
};
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error('❌ [KKAPI DETECTION] Error during detection:', error);
|
|
78
|
+
return {
|
|
79
|
+
isAvailable: false,
|
|
80
|
+
baseUrl: 'http://localhost:1646',
|
|
81
|
+
basePath: 'http://localhost:1646/spec/swagger.json',
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
25
85
|
|
|
26
86
|
export interface PioneerSDKConfig {
|
|
27
87
|
appName: string;
|
|
@@ -45,6 +105,18 @@ export interface PioneerSDKConfig {
|
|
|
45
105
|
forceLocalhost?: boolean;
|
|
46
106
|
}
|
|
47
107
|
|
|
108
|
+
// Helper function to format time differences
|
|
109
|
+
function formatTime(durationMs) {
|
|
110
|
+
let seconds = Math.floor((durationMs / 1000) % 60);
|
|
111
|
+
let minutes = Math.floor((durationMs / (1000 * 60)) % 60);
|
|
112
|
+
let hours = Math.floor((durationMs / (1000 * 60 * 60)) % 24);
|
|
113
|
+
|
|
114
|
+
let formatted = '';
|
|
115
|
+
if (hours > 0) formatted += `${hours}h `;
|
|
116
|
+
if (minutes > 0 || hours > 0) formatted += `${minutes}m `;
|
|
117
|
+
formatted += `${seconds}s`;
|
|
118
|
+
return formatted.trim();
|
|
119
|
+
}
|
|
48
120
|
|
|
49
121
|
export class SDK {
|
|
50
122
|
public status: string;
|
|
@@ -76,7 +148,7 @@ export class SDK {
|
|
|
76
148
|
address?: string;
|
|
77
149
|
master?: string;
|
|
78
150
|
}[] = [];
|
|
79
|
-
private
|
|
151
|
+
private pubkeySet: Set<string> = new Set(); // Track unique pubkey identifiers
|
|
80
152
|
public wallets: any[];
|
|
81
153
|
public balances: any[];
|
|
82
154
|
public nodes: any[];
|
|
@@ -189,11 +261,10 @@ export class SDK {
|
|
|
189
261
|
|
|
190
262
|
// Initialize pubkeys with deduplication if provided in config
|
|
191
263
|
if (config.pubkeys && config.pubkeys.length > 0) {
|
|
192
|
-
this.
|
|
193
|
-
this.pubkeys = this.pubkeyManager.getPubkeys();
|
|
264
|
+
this.setPubkeys(config.pubkeys);
|
|
194
265
|
} else {
|
|
195
266
|
this.pubkeys = [];
|
|
196
|
-
this.
|
|
267
|
+
this.pubkeySet.clear();
|
|
197
268
|
}
|
|
198
269
|
|
|
199
270
|
this.balances = config.balances || [];
|
|
@@ -233,24 +304,59 @@ export class SDK {
|
|
|
233
304
|
};
|
|
234
305
|
|
|
235
306
|
// Helper method to generate unique key for a pubkey
|
|
236
|
-
this.getPubkeyKey =
|
|
307
|
+
this.getPubkeyKey = (pubkey: any): string => {
|
|
308
|
+
return `${pubkey.pubkey}_${pubkey.pathMaster}`;
|
|
309
|
+
};
|
|
237
310
|
|
|
238
311
|
// Helper method to deduplicate pubkeys array
|
|
239
|
-
this.deduplicatePubkeys =
|
|
312
|
+
this.deduplicatePubkeys = (pubkeys: any[]): any[] => {
|
|
313
|
+
const seen = new Set<string>();
|
|
314
|
+
const deduped = pubkeys.filter((pubkey) => {
|
|
315
|
+
const key = this.getPubkeyKey(pubkey);
|
|
316
|
+
if (seen.has(key)) {
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
seen.add(key);
|
|
320
|
+
return true;
|
|
321
|
+
});
|
|
322
|
+
return deduped;
|
|
323
|
+
};
|
|
240
324
|
|
|
241
325
|
// Helper method to validate and add a single pubkey
|
|
242
326
|
this.addPubkey = (pubkey: any): boolean => {
|
|
243
|
-
|
|
244
|
-
if (
|
|
245
|
-
|
|
327
|
+
// Validate pubkey has required fields
|
|
328
|
+
if (!pubkey.pubkey || !pubkey.pathMaster) {
|
|
329
|
+
return false;
|
|
246
330
|
}
|
|
247
|
-
|
|
331
|
+
|
|
332
|
+
const key = this.getPubkeyKey(pubkey);
|
|
333
|
+
|
|
334
|
+
// Check if already exists
|
|
335
|
+
if (this.pubkeySet.has(key)) {
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Add to both array and set
|
|
340
|
+
this.pubkeys.push(pubkey);
|
|
341
|
+
this.pubkeySet.add(key);
|
|
342
|
+
return true;
|
|
248
343
|
};
|
|
249
344
|
|
|
250
345
|
// Helper method to set pubkeys array with deduplication
|
|
251
346
|
this.setPubkeys = (newPubkeys: any[]): void => {
|
|
252
|
-
|
|
253
|
-
|
|
347
|
+
const tag = `${TAG} | setPubkeys | `;
|
|
348
|
+
|
|
349
|
+
// Clear existing
|
|
350
|
+
this.pubkeys = [];
|
|
351
|
+
this.pubkeySet.clear();
|
|
352
|
+
|
|
353
|
+
// Add each pubkey with validation
|
|
354
|
+
let added = 0;
|
|
355
|
+
for (const pubkey of newPubkeys) {
|
|
356
|
+
if (this.addPubkey(pubkey)) {
|
|
357
|
+
added++;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
254
360
|
};
|
|
255
361
|
|
|
256
362
|
// Fast portfolio loading from kkapi:// cache
|
|
@@ -301,11 +407,9 @@ export class SDK {
|
|
|
301
407
|
// Update pubkeys from cache
|
|
302
408
|
if (portfolioData.pubkeys && portfolioData.pubkeys.length > 0) {
|
|
303
409
|
// Convert vault pubkey format to pioneer-sdk format
|
|
304
|
-
|
|
305
|
-
const convertedPubkeys = portfolioData.pubkeys;
|
|
410
|
+
const convertedPubkeys = this.convertVaultPubkeysToPioneerFormat(portfolioData.pubkeys);
|
|
306
411
|
// Use setPubkeys to ensure deduplication
|
|
307
|
-
this.
|
|
308
|
-
this.pubkeys = this.pubkeyManager.getPubkeys();
|
|
412
|
+
this.setPubkeys(convertedPubkeys);
|
|
309
413
|
this.events.emit('SET_PUBKEYS', this.pubkeys);
|
|
310
414
|
}
|
|
311
415
|
|
|
@@ -380,7 +484,7 @@ export class SDK {
|
|
|
380
484
|
'[CACHE VALIDATION] ❌ Cache data corrupted, building dashboard from cached balances',
|
|
381
485
|
);
|
|
382
486
|
// Build dashboard from cached balances without hitting Pioneer APIs
|
|
383
|
-
const dashboardData = buildDashboardFromBalances(
|
|
487
|
+
const dashboardData = this.buildDashboardFromBalances();
|
|
384
488
|
this.dashboard = dashboardData;
|
|
385
489
|
this.events.emit('SET_DASHBOARD', this.dashboard);
|
|
386
490
|
}
|
|
@@ -537,8 +641,182 @@ export class SDK {
|
|
|
537
641
|
}
|
|
538
642
|
};
|
|
539
643
|
// Build dashboard from cached balances (no Pioneer API calls)
|
|
540
|
-
this.buildDashboardFromBalances = ()
|
|
541
|
-
|
|
644
|
+
this.buildDashboardFromBalances = function () {
|
|
645
|
+
const tag = `${TAG} | buildDashboardFromBalances | `;
|
|
646
|
+
console.log(tag, '[DASHBOARD] Building dashboard from cached balances...');
|
|
647
|
+
|
|
648
|
+
const dashboardData: {
|
|
649
|
+
networks: {
|
|
650
|
+
networkId: string;
|
|
651
|
+
totalValueUsd: number;
|
|
652
|
+
gasAssetCaip: string | null;
|
|
653
|
+
gasAssetSymbol: string | null;
|
|
654
|
+
icon: string | null;
|
|
655
|
+
color: string | null;
|
|
656
|
+
totalNativeBalance: string;
|
|
657
|
+
}[];
|
|
658
|
+
totalValueUsd: number;
|
|
659
|
+
networkPercentages: { networkId: string; percentage: number }[];
|
|
660
|
+
} = {
|
|
661
|
+
networks: [],
|
|
662
|
+
totalValueUsd: 0,
|
|
663
|
+
networkPercentages: [],
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
let totalPortfolioValue = 0;
|
|
667
|
+
const networksTemp: {
|
|
668
|
+
networkId: string;
|
|
669
|
+
totalValueUsd: number;
|
|
670
|
+
gasAssetCaip: string | null;
|
|
671
|
+
gasAssetSymbol: string | null;
|
|
672
|
+
icon: string | null;
|
|
673
|
+
color: string | null;
|
|
674
|
+
totalNativeBalance: string;
|
|
675
|
+
}[] = [];
|
|
676
|
+
|
|
677
|
+
console.log(tag, 'this.balances: ', this.balances);
|
|
678
|
+
|
|
679
|
+
// Calculate totals for each blockchain
|
|
680
|
+
for (const blockchain of this.blockchains) {
|
|
681
|
+
const filteredBalances = this.balances.filter((b) => {
|
|
682
|
+
const networkId = caipToNetworkId(b.caip);
|
|
683
|
+
return (
|
|
684
|
+
networkId === blockchain ||
|
|
685
|
+
(blockchain === 'eip155:*' && networkId.startsWith('eip155:'))
|
|
686
|
+
);
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
// Deduplicate balances based on caip + pubkey combination
|
|
690
|
+
const balanceMap = new Map();
|
|
691
|
+
|
|
692
|
+
// Special handling for Bitcoin to work around API bug
|
|
693
|
+
const isBitcoin = blockchain.includes('bip122:000000000019d6689c085ae165831e93');
|
|
694
|
+
if (isBitcoin) {
|
|
695
|
+
console.log(tag, 'Bitcoin network detected - checking for duplicate balances');
|
|
696
|
+
// Group Bitcoin balances by value to detect duplicates
|
|
697
|
+
const bitcoinByValue = new Map();
|
|
698
|
+
filteredBalances.forEach((balance) => {
|
|
699
|
+
const valueKey = `${balance.balance}_${balance.valueUsd}`;
|
|
700
|
+
if (!bitcoinByValue.has(valueKey)) {
|
|
701
|
+
bitcoinByValue.set(valueKey, []);
|
|
702
|
+
}
|
|
703
|
+
bitcoinByValue.get(valueKey).push(balance);
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
// Check if all three address types have the same non-zero balance (API bug)
|
|
707
|
+
for (const [valueKey, balances] of bitcoinByValue.entries()) {
|
|
708
|
+
if (balances.length === 3 && parseFloat(balances[0].valueUsd || '0') > 0) {
|
|
709
|
+
console.log(
|
|
710
|
+
tag,
|
|
711
|
+
'BITCOIN API BUG DETECTED: All 3 address types have same balance, keeping only xpub',
|
|
712
|
+
);
|
|
713
|
+
// Keep only the xpub (or first one if no xpub)
|
|
714
|
+
const xpubBalance = balances.find((b) => b.pubkey?.startsWith('xpub')) || balances[0];
|
|
715
|
+
const key = `${xpubBalance.caip}_${xpubBalance.pubkey || 'default'}`;
|
|
716
|
+
balanceMap.set(key, xpubBalance);
|
|
717
|
+
} else {
|
|
718
|
+
// Add all balances normally
|
|
719
|
+
balances.forEach((balance) => {
|
|
720
|
+
const key = `${balance.caip}_${balance.pubkey || 'default'}`;
|
|
721
|
+
balanceMap.set(key, balance);
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
} else {
|
|
726
|
+
// Standard deduplication for non-Bitcoin networks
|
|
727
|
+
filteredBalances.forEach((balance) => {
|
|
728
|
+
const key = `${balance.caip}_${balance.pubkey || 'default'}`;
|
|
729
|
+
// Only keep the first occurrence or the one with higher value
|
|
730
|
+
if (
|
|
731
|
+
!balanceMap.has(key) ||
|
|
732
|
+
parseFloat(balance.valueUsd || '0') > parseFloat(balanceMap.get(key).valueUsd || '0')
|
|
733
|
+
) {
|
|
734
|
+
balanceMap.set(key, balance);
|
|
735
|
+
}
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
const networkBalances = Array.from(balanceMap.values());
|
|
740
|
+
|
|
741
|
+
// Ensure we're working with numbers for calculations
|
|
742
|
+
const networkTotal = networkBalances.reduce((sum, balance, idx) => {
|
|
743
|
+
const valueUsd =
|
|
744
|
+
typeof balance.valueUsd === 'string'
|
|
745
|
+
? parseFloat(balance.valueUsd)
|
|
746
|
+
: balance.valueUsd || 0;
|
|
747
|
+
|
|
748
|
+
if (blockchain.includes('bip122:000000000019d6689c085ae165831e93')) {
|
|
749
|
+
console.log(
|
|
750
|
+
tag,
|
|
751
|
+
`[BITCOIN DEBUG ${idx}] pubkey:`,
|
|
752
|
+
balance.pubkey?.substring(0, 10) + '...',
|
|
753
|
+
'| balance:',
|
|
754
|
+
balance.balance,
|
|
755
|
+
'| valueUsd:',
|
|
756
|
+
balance.valueUsd,
|
|
757
|
+
'→ parsed:',
|
|
758
|
+
valueUsd,
|
|
759
|
+
'| running sum:',
|
|
760
|
+
sum + valueUsd,
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
return sum + valueUsd;
|
|
765
|
+
}, 0);
|
|
766
|
+
|
|
767
|
+
// Get native asset for this blockchain
|
|
768
|
+
const nativeAssetCaip = networkIdToCaip(blockchain);
|
|
769
|
+
const gasAsset = networkBalances.find((b) => b.caip === nativeAssetCaip);
|
|
770
|
+
|
|
771
|
+
// Calculate total native balance (sum of all balances for the native asset)
|
|
772
|
+
const totalNativeBalance = networkBalances
|
|
773
|
+
.filter((b) => b.caip === nativeAssetCaip)
|
|
774
|
+
.reduce((sum, balance) => {
|
|
775
|
+
const balanceNum =
|
|
776
|
+
typeof balance.balance === 'string'
|
|
777
|
+
? parseFloat(balance.balance)
|
|
778
|
+
: balance.balance || 0;
|
|
779
|
+
return sum + balanceNum;
|
|
780
|
+
}, 0)
|
|
781
|
+
.toString();
|
|
782
|
+
|
|
783
|
+
// Get colors from assetMap since balances don't have them
|
|
784
|
+
const assetInfo = nativeAssetCaip ? this.assetsMap.get(nativeAssetCaip) : null;
|
|
785
|
+
|
|
786
|
+
networksTemp.push({
|
|
787
|
+
networkId: blockchain,
|
|
788
|
+
totalValueUsd: networkTotal,
|
|
789
|
+
gasAssetCaip: nativeAssetCaip || null,
|
|
790
|
+
gasAssetSymbol: gasAsset?.ticker || gasAsset?.symbol || assetInfo?.symbol || null,
|
|
791
|
+
icon: gasAsset?.icon || assetInfo?.icon || null,
|
|
792
|
+
color: gasAsset?.color || assetInfo?.color || null,
|
|
793
|
+
totalNativeBalance,
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
totalPortfolioValue += networkTotal;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// Sort networks by USD value and assign to dashboard
|
|
800
|
+
dashboardData.networks = networksTemp.sort((a, b) => b.totalValueUsd - a.totalValueUsd);
|
|
801
|
+
dashboardData.totalValueUsd = totalPortfolioValue;
|
|
802
|
+
|
|
803
|
+
// Calculate network percentages for pie chart
|
|
804
|
+
dashboardData.networkPercentages = dashboardData.networks
|
|
805
|
+
.map((network) => ({
|
|
806
|
+
networkId: network.networkId,
|
|
807
|
+
percentage:
|
|
808
|
+
totalPortfolioValue > 0
|
|
809
|
+
? Number(((network.totalValueUsd / totalPortfolioValue) * 100).toFixed(2))
|
|
810
|
+
: 0,
|
|
811
|
+
}))
|
|
812
|
+
.filter((entry) => entry.percentage > 0); // Remove zero percentages
|
|
813
|
+
|
|
814
|
+
console.log(
|
|
815
|
+
`[FAST DASHBOARD] ✅ Built dashboard: ${
|
|
816
|
+
dashboardData.networks.length
|
|
817
|
+
} networks, $${totalPortfolioValue.toFixed(2)} total`,
|
|
818
|
+
);
|
|
819
|
+
return dashboardData;
|
|
542
820
|
};
|
|
543
821
|
|
|
544
822
|
this.syncMarket = async function () {
|
|
@@ -900,6 +1178,7 @@ export class SDK {
|
|
|
900
1178
|
balances: this.balances,
|
|
901
1179
|
pioneer: this.pioneer,
|
|
902
1180
|
pubkeys: this.pubkeys,
|
|
1181
|
+
pubkeyContext: this.pubkeyContext,
|
|
903
1182
|
nodes: this.nodes,
|
|
904
1183
|
keepKeySdk: this.keepKeySdk,
|
|
905
1184
|
};
|
|
@@ -924,7 +1203,7 @@ export class SDK {
|
|
|
924
1203
|
delegateParams,
|
|
925
1204
|
this.pubkeys,
|
|
926
1205
|
this.pioneer,
|
|
927
|
-
this.
|
|
1206
|
+
this.pubkeyContext,
|
|
928
1207
|
);
|
|
929
1208
|
console.log(tag, 'unsignedTx: ', unsignedTx);
|
|
930
1209
|
return unsignedTx;
|
|
@@ -945,7 +1224,7 @@ export class SDK {
|
|
|
945
1224
|
undelegateParams,
|
|
946
1225
|
this.pubkeys,
|
|
947
1226
|
this.pioneer,
|
|
948
|
-
this.
|
|
1227
|
+
this.pubkeyContext,
|
|
949
1228
|
);
|
|
950
1229
|
console.log(tag, 'unsignedTx: ', unsignedTx);
|
|
951
1230
|
return unsignedTx;
|
|
@@ -966,7 +1245,7 @@ export class SDK {
|
|
|
966
1245
|
claimParams,
|
|
967
1246
|
this.pubkeys,
|
|
968
1247
|
this.pioneer,
|
|
969
|
-
this.
|
|
1248
|
+
this.pubkeyContext,
|
|
970
1249
|
);
|
|
971
1250
|
console.log(tag, 'unsignedTx: ', unsignedTx);
|
|
972
1251
|
return unsignedTx;
|
|
@@ -987,7 +1266,7 @@ export class SDK {
|
|
|
987
1266
|
claimAllParams,
|
|
988
1267
|
this.pubkeys,
|
|
989
1268
|
this.pioneer,
|
|
990
|
-
this.
|
|
1269
|
+
this.pubkeyContext,
|
|
991
1270
|
);
|
|
992
1271
|
//console.log(tag, 'unsignedTx: ', unsignedTx);
|
|
993
1272
|
return unsignedTx;
|
|
@@ -1005,6 +1284,7 @@ export class SDK {
|
|
|
1005
1284
|
balances: this.balances,
|
|
1006
1285
|
pioneer: this.pioneer,
|
|
1007
1286
|
pubkeys: this.pubkeys,
|
|
1287
|
+
pubkeyContext: this.pubkeyContext, // CRITICAL: Must include pubkeyContext for signing!
|
|
1008
1288
|
nodes: this.nodes,
|
|
1009
1289
|
keepKeySdk: this.keepKeySdk,
|
|
1010
1290
|
};
|
|
@@ -1441,6 +1721,65 @@ export class SDK {
|
|
|
1441
1721
|
throw e;
|
|
1442
1722
|
}
|
|
1443
1723
|
};
|
|
1724
|
+
this.addPath = async function (path: any) {
|
|
1725
|
+
const tag = `${TAG} | addPath | `;
|
|
1726
|
+
try {
|
|
1727
|
+
this.paths.push(path);
|
|
1728
|
+
const pubkey = await getPubkey(path.networks[0], path, this.keepKeySdk, this.context);
|
|
1729
|
+
this.addPubkey(pubkey);
|
|
1730
|
+
await this.getBalancesForNetworks(path.networks);
|
|
1731
|
+
this.buildDashboardFromBalances();
|
|
1732
|
+
return { success: true, pubkey };
|
|
1733
|
+
} catch (e) {
|
|
1734
|
+
console.error(tag, 'Failed:', e);
|
|
1735
|
+
throw e;
|
|
1736
|
+
}
|
|
1737
|
+
};
|
|
1738
|
+
this.addPaths = async function (paths: any[]) {
|
|
1739
|
+
const tag = `${TAG} | addPaths | `;
|
|
1740
|
+
try {
|
|
1741
|
+
console.log(tag, `Adding ${paths.length} paths in batch mode...`);
|
|
1742
|
+
|
|
1743
|
+
// Add all paths to the paths array
|
|
1744
|
+
this.paths.push(...paths);
|
|
1745
|
+
|
|
1746
|
+
// Get pubkeys for all paths
|
|
1747
|
+
const newPubkeys = [];
|
|
1748
|
+
for (const path of paths) {
|
|
1749
|
+
try {
|
|
1750
|
+
const pubkey = await getPubkey(path.networks[0], path, this.keepKeySdk, this.context);
|
|
1751
|
+
this.addPubkey(pubkey);
|
|
1752
|
+
newPubkeys.push(pubkey);
|
|
1753
|
+
} catch (error: any) {
|
|
1754
|
+
console.warn(tag, `Failed to get pubkey for path ${path.note}:`, error.message);
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
console.log(tag, `Successfully added ${newPubkeys.length} pubkeys`);
|
|
1759
|
+
|
|
1760
|
+
// Collect unique networks from all paths
|
|
1761
|
+
const networkSet = new Set<string>();
|
|
1762
|
+
for (const path of paths) {
|
|
1763
|
+
if (path.networks && Array.isArray(path.networks)) {
|
|
1764
|
+
path.networks.forEach((net: string) => networkSet.add(net));
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
const uniqueNetworks = [...networkSet];
|
|
1769
|
+
console.log(tag, `Fetching balances for ${uniqueNetworks.length} unique networks in single API call...`);
|
|
1770
|
+
|
|
1771
|
+
// Single API call for all networks
|
|
1772
|
+
await this.getBalancesForNetworks(uniqueNetworks);
|
|
1773
|
+
this.buildDashboardFromBalances();
|
|
1774
|
+
|
|
1775
|
+
console.log(tag, `Batch add complete: ${paths.length} paths, ${newPubkeys.length} pubkeys, ${this.balances?.length || 0} balances`);
|
|
1776
|
+
|
|
1777
|
+
return { success: true, pubkeys: newPubkeys };
|
|
1778
|
+
} catch (e) {
|
|
1779
|
+
console.error(tag, 'Failed:', e);
|
|
1780
|
+
throw e;
|
|
1781
|
+
}
|
|
1782
|
+
};
|
|
1444
1783
|
this.getAssets = async function () {
|
|
1445
1784
|
/*
|
|
1446
1785
|
Get Asset Rules
|
|
@@ -1583,24 +1922,6 @@ export class SDK {
|
|
|
1583
1922
|
let marketInfo = await this.pioneer.GetPortfolioBalances(assetQuery);
|
|
1584
1923
|
console.timeEnd('GetPortfolioBalances Response Time');
|
|
1585
1924
|
|
|
1586
|
-
// DEFENSIVE: Validate response structure to prevent "balances.filter is not a function"
|
|
1587
|
-
if (!marketInfo || !marketInfo.data) {
|
|
1588
|
-
console.error(tag, 'Invalid response structure:', marketInfo);
|
|
1589
|
-
throw new Error('GetPortfolioBalances returned invalid response: missing data field');
|
|
1590
|
-
}
|
|
1591
|
-
|
|
1592
|
-
if (!Array.isArray(marketInfo.data)) {
|
|
1593
|
-
console.error(
|
|
1594
|
-
tag,
|
|
1595
|
-
'GetPortfolioBalances returned non-array data:',
|
|
1596
|
-
typeof marketInfo.data,
|
|
1597
|
-
marketInfo.data
|
|
1598
|
-
);
|
|
1599
|
-
throw new Error(
|
|
1600
|
-
`GetPortfolioBalances returned invalid data type: expected array, got ${typeof marketInfo.data}`
|
|
1601
|
-
);
|
|
1602
|
-
}
|
|
1603
|
-
|
|
1604
1925
|
let balances = marketInfo.data;
|
|
1605
1926
|
|
|
1606
1927
|
const bitcoinBalances = balances.filter(
|
|
@@ -2038,18 +2359,29 @@ export class SDK {
|
|
|
2038
2359
|
}
|
|
2039
2360
|
|
|
2040
2361
|
// Auto-set pubkey context for this asset's network
|
|
2041
|
-
//
|
|
2362
|
+
// IMPORTANT: Only auto-set if no pubkey context exists OR if current context is for wrong network
|
|
2363
|
+
// This preserves custom pubkey contexts set via setPubkeyContext()
|
|
2042
2364
|
if (assetPubkeys && assetPubkeys.length > 0) {
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2365
|
+
const networkId = caipToNetworkId(asset.caip || asset.networkId);
|
|
2366
|
+
const currentContextValid = this.pubkeyContext?.networks?.includes(networkId);
|
|
2367
|
+
|
|
2368
|
+
if (!this.pubkeyContext || !currentContextValid) {
|
|
2369
|
+
// No context or wrong network - auto-set to first matching pubkey
|
|
2370
|
+
this.pubkeyContext = assetPubkeys[0];
|
|
2371
|
+
console.log(
|
|
2372
|
+
tag,
|
|
2373
|
+
'Auto-set pubkey context for network:',
|
|
2374
|
+
this.pubkeyContext.address || this.pubkeyContext.pubkey,
|
|
2375
|
+
);
|
|
2376
|
+
} else {
|
|
2377
|
+
// Valid context already exists for this network - preserve it
|
|
2378
|
+
console.log(
|
|
2379
|
+
tag,
|
|
2380
|
+
'Preserving existing pubkey context for network:',
|
|
2381
|
+
this.pubkeyContext.address || this.pubkeyContext.pubkey,
|
|
2382
|
+
`(addressNList: [${(this.pubkeyContext.addressNList || this.pubkeyContext.addressNListMaster).join(', ')}])`,
|
|
2383
|
+
);
|
|
2047
2384
|
}
|
|
2048
|
-
console.log(
|
|
2049
|
-
tag,
|
|
2050
|
-
'Auto-set pubkey context for network:',
|
|
2051
|
-
this.pubkeyContext.address || this.pubkeyContext.pubkey,
|
|
2052
|
-
);
|
|
2053
2385
|
}
|
|
2054
2386
|
|
|
2055
2387
|
this.events.emit('SET_ASSET_CONTEXT', this.assetContext);
|
|
@@ -2084,16 +2416,14 @@ export class SDK {
|
|
|
2084
2416
|
ethereum account 0/1/2
|
|
2085
2417
|
*/
|
|
2086
2418
|
this.pubkeyContext = pubkey;
|
|
2087
|
-
// Also set it on keepKeySdk so tx builders can access it
|
|
2088
|
-
if (this.keepKeySdk) {
|
|
2089
|
-
this.keepKeySdk.pubkeyContext = pubkey;
|
|
2090
|
-
}
|
|
2091
2419
|
console.log(
|
|
2092
2420
|
tag,
|
|
2093
2421
|
'Pubkey context set to:',
|
|
2094
2422
|
pubkey.address || pubkey.pubkey,
|
|
2095
2423
|
'note:',
|
|
2096
2424
|
pubkey.note,
|
|
2425
|
+
'addressNList:',
|
|
2426
|
+
pubkey.addressNList || pubkey.addressNListMaster,
|
|
2097
2427
|
);
|
|
2098
2428
|
|
|
2099
2429
|
return true;
|