@pioneer-platform/pioneer-sdk 4.20.0 → 4.20.1

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/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 { Pioneer } from '@pioneer-platform/pioneer-client';
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 { EventEmitter } from 'events';
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 pubkeyManager: PubkeyManager = new PubkeyManager();
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.pubkeyManager.setPubkeys(config.pubkeys);
193
- this.pubkeys = this.pubkeyManager.getPubkeys();
264
+ this.setPubkeys(config.pubkeys);
194
265
  } else {
195
266
  this.pubkeys = [];
196
- this.pubkeyManager.clear();
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 = 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 = 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
- const result = this.pubkeyManager.addPubkey(pubkey);
244
- if (result) {
245
- this.pubkeys = this.pubkeyManager.getPubkeys();
327
+ // Validate pubkey has required fields
328
+ if (!pubkey.pubkey || !pubkey.pathMaster) {
329
+ return false;
246
330
  }
247
- return result;
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
- this.pubkeyManager.setPubkeys(newPubkeys);
253
- this.pubkeys = this.pubkeyManager.getPubkeys();
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
- // TODO: Implement convertVaultPubkeysToPioneerFormat method
305
- const convertedPubkeys = portfolioData.pubkeys;
410
+ const convertedPubkeys = this.convertVaultPubkeysToPioneerFormat(portfolioData.pubkeys);
306
411
  // Use setPubkeys to ensure deduplication
307
- this.pubkeyManager.setPubkeys(convertedPubkeys);
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(this.balances, this.blockchains, this.assetsMap);
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
- return buildDashboardFromBalances(this.balances, this.blockchains, this.assetsMap);
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.keepKeySdk,
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.keepKeySdk,
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.keepKeySdk,
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.keepKeySdk,
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
- // Use the first matching pubkey from assetPubkeys we already filtered
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
- this.pubkeyContext = assetPubkeys[0];
2044
- // Also set it on keepKeySdk so tx builders can access it
2045
- if (this.keepKeySdk) {
2046
- this.keepKeySdk.pubkeyContext = assetPubkeys[0];
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;