@pioneer-platform/pioneer-sdk 8.15.41 → 8.17.0

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.
@@ -32,42 +32,93 @@ export interface KkapiHealthStatus {
32
32
  device_connected: boolean;
33
33
  cached_pubkeys: number;
34
34
  vault_version?: string;
35
+ apiVersion?: number;
36
+ supportedChains?: string[];
35
37
  }
36
38
 
37
39
  /**
38
40
  * Check if kkapi:// vault is available and ready
39
41
  */
40
42
  export async function checkKkapiHealth(baseUrl: string = 'kkapi://'): Promise<KkapiHealthStatus> {
43
+ // Default V1 supported chains (legacy chains without Solana, TRON, TON)
44
+ const V1_SUPPORTED_CHAINS = [
45
+ 'bip122:000000000019d6689c085ae165831e93', // BTC
46
+ 'bip122:000000000000000000651ef99cb9fcbe', // BCH
47
+ 'bip122:000007d91d1254d60e2dd1ae58038307', // DASH
48
+ 'bip122:00000000001a91e3dace36e2be3bf030', // DOGE
49
+ 'bip122:12a765e31ffd4059bada1e25190f6e98', // LTC
50
+ 'bip122:4da631f2ac1bed857bd968c67c913978', // DGB
51
+ 'bip122:00040fe8ec8471911baa1db1266ea15d', // ZEC
52
+ 'eip155:1', // ETH
53
+ 'eip155:137', // MATIC
54
+ 'eip155:8453', // BASE
55
+ 'eip155:56', // BSC
56
+ 'cosmos:cosmoshub-4', // ATOM
57
+ 'cosmos:osmosis-1', // OSMO
58
+ 'cosmos:mayachain-mainnet-v1', // MAYA
59
+ 'cosmos:thorchain-mainnet-v1', // RUNE
60
+ 'ripple:4109c6f2045fc7eff4cde8f9905d19c2', // XRP
61
+ ];
62
+
41
63
  try {
42
64
  const healthResponse = await fetch(`${baseUrl}/api/health`);
65
+
66
+ // If health endpoint doesn't exist (404), assume v1 server (Desktop app)
67
+ if (healthResponse.status === 404) {
68
+ console.log('📡 [VERSION] No /api/health endpoint - assuming v1 server (Desktop app)');
69
+ return {
70
+ available: true,
71
+ device_connected: true,
72
+ cached_pubkeys: 0,
73
+ apiVersion: 1,
74
+ supportedChains: V1_SUPPORTED_CHAINS
75
+ };
76
+ }
77
+
43
78
  if (!healthResponse.ok) {
44
- return { available: false, device_connected: false, cached_pubkeys: 0 };
79
+ return {
80
+ available: false,
81
+ device_connected: false,
82
+ cached_pubkeys: 0,
83
+ apiVersion: 1,
84
+ supportedChains: V1_SUPPORTED_CHAINS
85
+ };
45
86
  }
46
87
 
47
88
  const healthData = await healthResponse.json();
48
- if (healthData.cached_pubkeys !== undefined) {
89
+
90
+ // V2 server returns apiVersion and supportedChains
91
+ if (healthData.apiVersion === 2 && healthData.supportedChains) {
49
92
  return {
50
93
  available: true,
51
94
  device_connected: healthData.device_connected || false,
52
95
  cached_pubkeys: healthData.cached_pubkeys || 0,
53
- vault_version: healthData.version
96
+ vault_version: healthData.version,
97
+ apiVersion: healthData.apiVersion,
98
+ supportedChains: healthData.supportedChains
54
99
  };
55
100
  }
56
101
 
57
- const cacheResponse = await fetch(`${baseUrl}/api/cache/status`);
58
- if (!cacheResponse.ok) {
59
- return { available: true, device_connected: true, cached_pubkeys: 0 };
60
- }
61
-
62
- const cacheData = await cacheResponse.json();
102
+ // V1 server or old format - use default v1 chains
63
103
  return {
64
104
  available: true,
65
- device_connected: true,
66
- cached_pubkeys: cacheData.cached_pubkeys || 0,
67
- vault_version: cacheData.vault_version
105
+ device_connected: healthData.device_connected || false,
106
+ cached_pubkeys: healthData.cached_pubkeys || 0,
107
+ vault_version: healthData.version,
108
+ apiVersion: 1,
109
+ supportedChains: V1_SUPPORTED_CHAINS
68
110
  };
111
+
69
112
  } catch (error: any) {
70
- return { available: false, device_connected: false, cached_pubkeys: 0 };
113
+ // Network error or server unreachable - assume v1 for backward compatibility
114
+ console.log('📡 [VERSION] Health check failed - assuming v1 server');
115
+ return {
116
+ available: false,
117
+ device_connected: false,
118
+ cached_pubkeys: 0,
119
+ apiVersion: 1,
120
+ supportedChains: V1_SUPPORTED_CHAINS
121
+ };
71
122
  }
72
123
  }
73
124
 
@@ -138,6 +189,28 @@ export async function optimizedGetPubkeys(
138
189
 
139
190
  const vaultHealth = await checkKkapiHealth(baseUrl);
140
191
 
192
+ // Filter blockchains based on server's supportedChains
193
+ let supportedBlockchains = blockchains;
194
+ if (vaultHealth.supportedChains && vaultHealth.supportedChains.length > 0) {
195
+ console.log(`📡 [VERSION] Server API v${vaultHealth.apiVersion} supports ${vaultHealth.supportedChains.length} chains`);
196
+
197
+ // Filter blockchains to only include ones the server supports
198
+ const unsupportedBlockchains: string[] = [];
199
+ supportedBlockchains = blockchains.filter(blockchain => {
200
+ // Check if this blockchain is in the server's supported list
201
+ const isSupported = vaultHealth.supportedChains!.includes(blockchain);
202
+ if (!isSupported) {
203
+ unsupportedBlockchains.push(blockchain);
204
+ }
205
+ return isSupported;
206
+ });
207
+
208
+ if (unsupportedBlockchains.length > 0) {
209
+ console.log(`⏭️ [VERSION] Skipping ${unsupportedBlockchains.length} unsupported chains:`, unsupportedBlockchains.join(', '));
210
+ }
211
+ console.log(`✅ [VERSION] Using ${supportedBlockchains.length} supported chains`);
212
+ }
213
+
141
214
  let pubkeys: any[] = [];
142
215
  let remainingPaths: any[] = [];
143
216
  let remainingBlockchains: string[] = [];
@@ -146,15 +219,15 @@ export async function optimizedGetPubkeys(
146
219
  const batchResponse = await batchGetPubkeys(paths, context, baseUrl);
147
220
  pubkeys = batchResponse.pubkeys;
148
221
  const cachedPaths = new Set(batchResponse.pubkeys.map(p => p.path));
149
-
150
- for (let i = 0; i < blockchains.length; i++) {
151
- const blockchain = blockchains[i];
222
+
223
+ for (let i = 0; i < supportedBlockchains.length; i++) {
224
+ const blockchain = supportedBlockchains[i];
152
225
  const pathsForChain = paths.filter(path => path.networks && Array.isArray(path.networks) && path.networks.includes(blockchain));
153
-
226
+
154
227
  for (const path of pathsForChain) {
155
228
  const { addressNListToBIP32 } = await import('@pioneer-platform/pioneer-coins');
156
229
  const pathBip32 = addressNListToBIP32(path.addressNList);
157
-
230
+
158
231
  if (!cachedPaths.has(pathBip32)) {
159
232
  remainingPaths.push(path);
160
233
  if (!remainingBlockchains.includes(blockchain)) {
@@ -164,8 +237,12 @@ export async function optimizedGetPubkeys(
164
237
  }
165
238
  }
166
239
  } else {
167
- remainingPaths = paths;
168
- remainingBlockchains = blockchains;
240
+ // Filter paths to only include supported blockchains
241
+ remainingPaths = paths.filter(path =>
242
+ path.networks && Array.isArray(path.networks) &&
243
+ path.networks.some((network: string) => supportedBlockchains.includes(network))
244
+ );
245
+ remainingBlockchains = supportedBlockchains;
169
246
  }
170
247
 
171
248
  if (remainingPaths.length > 0) {
@@ -30,7 +30,12 @@ export const CAIP_TO_COIN_MAP: { [key: string]: string } = {
30
30
  'bip122:00040fe8ec8471911baa1db1266ea15d/slip44:133': 'Zcash',
31
31
  };
32
32
 
33
- export const OTHER_SUPPORT = ['ripple:4109c6f2045fc7eff4cde8f9905d19c2/slip44:144'];
33
+ export const OTHER_SUPPORT = [
34
+ 'ripple:4109c6f2045fc7eff4cde8f9905d19c2/slip44:144', // XRP
35
+ 'solana:5eykt4usfv8p8njdtrepy1vzqkqzkvdp/solana:so11111111111111111111111111111111111111112', // SOL
36
+ 'tron:0x2b6653dc/slip44:195', // TRX
37
+ 'ton:-239/slip44:607', // TON
38
+ ];
34
39
 
35
40
  export const SUPPORTED_CAIPS = {
36
41
  UTXO: UTXO_SUPPORT,
@@ -0,0 +1,132 @@
1
+ /*
2
+ Create Unsigned Solana Transaction
3
+ */
4
+ // @ts-ignore
5
+ import { caipToNetworkId } from '@pioneer-platform/pioneer-caip';
6
+ // @ts-ignore
7
+ import {
8
+ Connection,
9
+ PublicKey,
10
+ Transaction,
11
+ SystemProgram,
12
+ LAMPORTS_PER_SOL,
13
+ } from '@solana/web3.js';
14
+
15
+ const TAG = ' | createUnsignedSolanaTx | ';
16
+
17
+ // Default Solana RPC (could be made configurable)
18
+ const DEFAULT_RPC_URL = 'https://api.mainnet-beta.solana.com';
19
+
20
+ export async function createUnsignedSolanaTx(
21
+ caip: string,
22
+ to: string,
23
+ amount: any,
24
+ memo: string,
25
+ pubkeys: any,
26
+ pioneer: any,
27
+ pubkeyContext: any,
28
+ isMax: boolean,
29
+ ): Promise<any> {
30
+ let tag = TAG + ' | createUnsignedSolanaTx | ';
31
+
32
+ try {
33
+ if (!pioneer) throw new Error('Failed to init! pioneer');
34
+
35
+ // Determine networkId from caip
36
+ const networkId = caipToNetworkId(caip);
37
+
38
+ // Use the passed pubkeyContext directly - it's already been set by Pioneer SDK
39
+ if (!pubkeyContext) {
40
+ throw new Error(`No pubkey context provided for networkId: ${networkId}`);
41
+ }
42
+
43
+ if (!pubkeyContext.networks?.includes(networkId)) {
44
+ throw new Error(
45
+ `Pubkey context is for wrong network. Expected ${networkId}, got ${pubkeyContext.networks}`,
46
+ );
47
+ }
48
+
49
+ console.log(tag, `✅ Using pubkeyContext for network ${networkId}:`, {
50
+ address: pubkeyContext.address,
51
+ });
52
+
53
+ const fromAddress = pubkeyContext.address || pubkeyContext.pubkey;
54
+
55
+ // Get account balance to calculate max amount if needed
56
+ let accountInfo = await pioneer.GetAccountInfo({
57
+ address: fromAddress,
58
+ network: 'solana',
59
+ });
60
+ accountInfo = accountInfo.data;
61
+
62
+ // Calculate amount
63
+ let amountInSol: number;
64
+ if (isMax) {
65
+ // Reserve fee (approximately 0.000005 SOL = 5000 lamports)
66
+ const feeInSol = 0.000005;
67
+ amountInSol = parseFloat(accountInfo.balance) - feeInSol;
68
+ if (amountInSol <= 0) {
69
+ throw new Error('Insufficient balance to cover fee');
70
+ }
71
+ } else {
72
+ amountInSol = parseFloat(amount);
73
+ }
74
+
75
+ // Build transaction using @solana/web3.js
76
+ const connection = new Connection(DEFAULT_RPC_URL, 'confirmed');
77
+
78
+ // Convert to PublicKey objects
79
+ const fromPubkey = new PublicKey(fromAddress);
80
+ const toPubkey = new PublicKey(to);
81
+
82
+ // Convert SOL to lamports
83
+ const lamports = Math.floor(amountInSol * LAMPORTS_PER_SOL);
84
+
85
+ // Get recent blockhash
86
+ const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');
87
+
88
+ // Create transfer instruction
89
+ const transferInstruction = SystemProgram.transfer({
90
+ fromPubkey,
91
+ toPubkey,
92
+ lamports,
93
+ });
94
+
95
+ // Create transaction
96
+ const transaction = new Transaction({
97
+ feePayer: fromPubkey,
98
+ blockhash,
99
+ lastValidBlockHeight,
100
+ }).add(transferInstruction);
101
+
102
+ // Add memo instruction if provided
103
+ if (memo && memo.trim() !== '') {
104
+ // TODO: Add memo instruction
105
+ console.log(tag, 'Memo support not yet implemented');
106
+ }
107
+
108
+ // Serialize transaction message for signing
109
+ const serialized = transaction.serializeMessage();
110
+
111
+ const unsignedTx = {
112
+ transaction,
113
+ serialized: serialized.toString('base64'),
114
+ blockhash,
115
+ lastValidBlockHeight,
116
+ addressNList: pubkeyContext.addressNList || pubkeyContext.addressNListMaster,
117
+ fromAddress,
118
+ toAddress: to,
119
+ amount: amountInSol,
120
+ lamports,
121
+ caip,
122
+ networkId,
123
+ };
124
+
125
+ console.log(tag, '✅ Solana transaction built successfully');
126
+
127
+ return unsignedTx;
128
+ } catch (error) {
129
+ console.error(tag, 'Error:', error);
130
+ throw error;
131
+ }
132
+ }
@@ -0,0 +1,97 @@
1
+ /*
2
+ Create Unsigned TRON Transaction
3
+ */
4
+ // @ts-ignore
5
+ import { caipToNetworkId } from '@pioneer-platform/pioneer-caip';
6
+
7
+ const TAG = ' | createUnsignedTronTx | ';
8
+
9
+ export async function createUnsignedTronTx(
10
+ caip: string,
11
+ to: string,
12
+ amount: any,
13
+ memo: string,
14
+ pubkeys: any,
15
+ pioneer: any,
16
+ pubkeyContext: any,
17
+ isMax: boolean,
18
+ ): Promise<any> {
19
+ let tag = TAG + ' | createUnsignedTronTx | ';
20
+
21
+ try {
22
+ if (!pioneer) throw new Error('Failed to init! pioneer');
23
+
24
+ // Determine networkId from caip
25
+ const networkId = caipToNetworkId(caip);
26
+
27
+ // Use the passed pubkeyContext directly - it's already been set by Pioneer SDK
28
+ if (!pubkeyContext) {
29
+ throw new Error(`No pubkey context provided for networkId: ${networkId}`);
30
+ }
31
+
32
+ if (!pubkeyContext.networks?.includes(networkId)) {
33
+ throw new Error(
34
+ `Pubkey context is for wrong network. Expected ${networkId}, got ${pubkeyContext.networks}`,
35
+ );
36
+ }
37
+
38
+ console.log(tag, `✅ Using pubkeyContext for network ${networkId}:`, {
39
+ address: pubkeyContext.address,
40
+ });
41
+
42
+ const fromAddress = pubkeyContext.address || pubkeyContext.pubkey;
43
+
44
+ // Calculate amount in TRX (6 decimals for TRON)
45
+ let amountInTrx: number;
46
+ if (isMax) {
47
+ // For max transfers, get account balance
48
+ try {
49
+ let accountInfo = await pioneer.GetAccountInfo({
50
+ address: fromAddress,
51
+ network: 'tron',
52
+ });
53
+ accountInfo = accountInfo.data;
54
+
55
+ // Reserve fee (approximately 0.1 TRX)
56
+ const feeInTrx = 0.1;
57
+ amountInTrx = parseFloat(accountInfo.balance) - feeInTrx;
58
+ if (amountInTrx <= 0) {
59
+ throw new Error('Insufficient balance to cover fee');
60
+ }
61
+ } catch (e: any) {
62
+ console.warn(tag, 'GetAccountInfo not available for TRON, max transfer not supported:', e.message);
63
+ throw new Error('Max transfer not supported for TRON - Pioneer backend needs TRON account info support');
64
+ }
65
+ } else {
66
+ // For non-max transfers, use the provided amount directly
67
+ amountInTrx = parseFloat(amount);
68
+ }
69
+
70
+ // Convert TRX to sun (1 TRX = 1,000,000 sun)
71
+ const amountInSun = Math.floor(amountInTrx * 1000000);
72
+
73
+ // Build unsigned transaction object for TRON
74
+ const unsignedTx = {
75
+ from: fromAddress,
76
+ to: to,
77
+ amount: amountInTrx,
78
+ amountInSun: amountInSun,
79
+ memo: memo || '',
80
+ addressNList: pubkeyContext.addressNList || pubkeyContext.addressNListMaster,
81
+ caip,
82
+ networkId,
83
+ };
84
+
85
+ console.log(tag, '✅ TRON transaction built successfully');
86
+ console.log(tag, 'Transaction details:', {
87
+ from: fromAddress,
88
+ to: to,
89
+ amount: `${amountInTrx} TRX (${amountInSun} sun)`,
90
+ });
91
+
92
+ return unsignedTx;
93
+ } catch (error) {
94
+ console.error(tag, 'Error:', error);
95
+ throw error;
96
+ }
97
+ }
@@ -57,8 +57,8 @@ const EXPLORER_BASE_URLS: Record<string, { address: string; tx: string }> = {
57
57
  },
58
58
  // Dash
59
59
  'bip122:000007d91d1254d60e2dd1ae58038307': {
60
- address: 'https://chainz.cryptoid.info/dash/address.dws?',
61
- tx: 'https://chainz.cryptoid.info/dash/tx.dws?'
60
+ address: 'https://explorer.dash.org/insight/address/',
61
+ tx: 'https://explorer.dash.org/insight/tx/'
62
62
  },
63
63
  // DigiByte
64
64
  'bip122:4da631f2ac1bed857bd968c67c913978': {