@pioneer-platform/pioneer-sdk 8.12.8 → 8.13.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.
package/dist/index.cjs CHANGED
@@ -1098,7 +1098,15 @@ async function createUnsignedEvmTx(caip, to, amount, memo, pubkeys, pioneer, pub
1098
1098
  amountWei = balance - gasFee - buffer;
1099
1099
  console.log(tag, "isMax calculation - balance:", balance.toString(), "gasFee:", gasFee.toString(), "buffer:", buffer.toString(), "amountWei:", amountWei.toString());
1100
1100
  } else {
1101
- amountWei = BigInt(Math.round(amount * 1000000000000000000));
1101
+ const amountNum = typeof amount === "string" ? parseFloat(amount) : amount;
1102
+ if (isNaN(amountNum) || !isFinite(amountNum) || amountNum <= 0) {
1103
+ throw new Error(`Invalid amount for native gas transfer: "${amount}" (type: ${typeof amount}). ` + `Amount must be a valid positive number.`);
1104
+ }
1105
+ const amountCalculation = amountNum * 1000000000000000000;
1106
+ if (isNaN(amountCalculation) || !isFinite(amountCalculation)) {
1107
+ throw new Error(`Invalid amount calculation for native gas transfer. ` + `Input: ${amountNum}`);
1108
+ }
1109
+ amountWei = BigInt(Math.round(amountCalculation));
1102
1110
  const totalNeeded = amountWei + gasFee;
1103
1111
  if (totalNeeded > balance) {
1104
1112
  const availableForSwap = balance > gasFee ? balance - gasFee : 0n;
@@ -1257,11 +1265,22 @@ async function createUnsignedEvmTx(caip, to, amount, memo, pubkeys, pioneer, pub
1257
1265
  const tokenBalance = BigInt(Math.round(tokenBalanceData.data * Number(tokenMultiplier)));
1258
1266
  amountWei = tokenBalance;
1259
1267
  } else {
1260
- amountWei = BigInt(Math.round(amount * Number(tokenMultiplier)));
1268
+ const amountNum = typeof amount === "string" ? parseFloat(amount) : amount;
1269
+ if (isNaN(amountNum) || !isFinite(amountNum) || amountNum <= 0) {
1270
+ throw new Error(`Invalid amount for ERC20 transfer: "${amount}" (type: ${typeof amount}). ` + `Amount must be a valid positive number.`);
1271
+ }
1272
+ const amountCalculation = amountNum * Number(tokenMultiplier);
1273
+ if (isNaN(amountCalculation) || !isFinite(amountCalculation)) {
1274
+ throw new Error(`Invalid amount calculation for ERC20 transfer. ` + `Input: ${amountNum}, Decimals: ${tokenDecimals}, Multiplier: ${tokenMultiplier}`);
1275
+ }
1276
+ amountWei = BigInt(Math.round(amountCalculation));
1261
1277
  console.log(tag, "Token amount calculation:", {
1262
1278
  inputAmount: amount,
1279
+ inputType: typeof amount,
1280
+ parsedAmount: amountNum,
1263
1281
  decimals: tokenDecimals,
1264
- multiplier: tokenMultiplier,
1282
+ multiplier: tokenMultiplier.toString(),
1283
+ calculation: amountCalculation,
1265
1284
  resultWei: amountWei.toString()
1266
1285
  });
1267
1286
  }
@@ -3423,6 +3442,9 @@ class SDK {
3423
3442
  isPioneer;
3424
3443
  keepkeyEndpoint;
3425
3444
  forceLocalhost;
3445
+ viewOnlyMode;
3446
+ skipDevicePairing;
3447
+ skipKeeperEndpoint;
3426
3448
  getPubkeys;
3427
3449
  getBalances;
3428
3450
  blockchains;
@@ -3479,6 +3501,9 @@ class SDK {
3479
3501
  this.keepkeyEndpoint = null;
3480
3502
  this.forceLocalhost = config.forceLocalhost || false;
3481
3503
  this.paths = config.paths || [];
3504
+ this.viewOnlyMode = config.viewOnlyMode || false;
3505
+ this.skipDevicePairing = config.skipDevicePairing || config.viewOnlyMode || false;
3506
+ this.skipKeeperEndpoint = config.skipKeeperEndpoint || config.viewOnlyMode || false;
3482
3507
  this.blockchains = config.blockchains ? [...new Set(config.blockchains)] : [];
3483
3508
  if (config.blockchains && config.blockchains.length !== this.blockchains.length) {
3484
3509
  }
@@ -3519,7 +3544,20 @@ class SDK {
3519
3544
  fallbackToRemote: true
3520
3545
  }) : null;
3521
3546
  this.pairWallet = async (options) => {
3522
- return Promise.resolve({});
3547
+ const tag = TAG9 + " | pairWallet | ";
3548
+ if (this.viewOnlyMode || this.skipDevicePairing) {
3549
+ console.log(tag, "\uD83D\uDC41️ [VIEW-ONLY] Skipping device pairing");
3550
+ return {
3551
+ success: false,
3552
+ reason: "view-only-mode",
3553
+ message: "Device pairing skipped in view-only mode"
3554
+ };
3555
+ }
3556
+ return Promise.resolve({
3557
+ success: false,
3558
+ reason: "not-implemented",
3559
+ message: "Device pairing not yet implemented"
3560
+ });
3523
3561
  };
3524
3562
  this.getPubkeyKey = (pubkey) => {
3525
3563
  return `${pubkey.pubkey}_${pubkey.pathMaster}`;
@@ -3565,6 +3603,15 @@ class SDK {
3565
3603
  this.pubkeys = [];
3566
3604
  this.pubkeySet.clear();
3567
3605
  }
3606
+ this.isViewOnlyMode = () => {
3607
+ return this.viewOnlyMode;
3608
+ };
3609
+ this.canSignTransactions = () => {
3610
+ return !this.viewOnlyMode && !!this.keepKeySdk;
3611
+ };
3612
+ this.isVaultAvailable = () => {
3613
+ return !!this.keepkeyEndpoint && this.keepkeyEndpoint.isAvailable;
3614
+ };
3568
3615
  this.getUnifiedPortfolio = async function() {
3569
3616
  const tag = `${TAG9} | getUnifiedPortfolio | `;
3570
3617
  try {
@@ -3715,26 +3762,35 @@ class SDK {
3715
3762
  throw Error("Failed to init pioneer server!");
3716
3763
  this.paths.concat(import_pioneer_coins4.getPaths(this.blockchains));
3717
3764
  await this.getGasAssets();
3718
- this.keepkeyEndpoint = await detectKkApiAvailability(this.forceLocalhost);
3719
- const keepkeyEndpoint = this.keepkeyEndpoint;
3720
- try {
3721
- const configKeepKey = {
3722
- apiKey: this.keepkeyApiKey || "keepkey-api-key",
3723
- pairingInfo: {
3724
- name: "KeepKey SDK Demo App",
3725
- imageUrl: "https://pioneers.dev/coins/keepkey.png",
3726
- basePath: keepkeyEndpoint.basePath,
3727
- url: keepkeyEndpoint.baseUrl
3765
+ if (!this.skipKeeperEndpoint) {
3766
+ this.keepkeyEndpoint = await detectKkApiAvailability(this.forceLocalhost);
3767
+ const keepkeyEndpoint = this.keepkeyEndpoint;
3768
+ if (!this.skipDevicePairing) {
3769
+ try {
3770
+ const configKeepKey = {
3771
+ apiKey: this.keepkeyApiKey || "keepkey-api-key",
3772
+ pairingInfo: {
3773
+ name: "KeepKey SDK Demo App",
3774
+ imageUrl: "https://pioneers.dev/coins/keepkey.png",
3775
+ basePath: keepkeyEndpoint.basePath,
3776
+ url: keepkeyEndpoint.baseUrl
3777
+ }
3778
+ };
3779
+ console.log("\uD83D\uDD11 [INIT] Initializing KeepKey SDK...");
3780
+ const keepKeySdk = await import_keepkey_sdk.KeepKeySdk.create(configKeepKey);
3781
+ const features = await keepKeySdk.system.info.getFeatures();
3782
+ this.keepkeyApiKey = configKeepKey.apiKey;
3783
+ this.keepKeySdk = keepKeySdk;
3784
+ this.context = "keepkey:" + features.label + ".json";
3785
+ } catch (e) {
3786
+ console.error("⚠️ [INIT] KeepKey SDK initialization failed:", e);
3728
3787
  }
3729
- };
3730
- console.log("\uD83D\uDD11 [INIT] Initializing KeepKey SDK...");
3731
- const keepKeySdk = await import_keepkey_sdk.KeepKeySdk.create(configKeepKey);
3732
- const features = await keepKeySdk.system.info.getFeatures();
3733
- this.keepkeyApiKey = configKeepKey.apiKey;
3734
- this.keepKeySdk = keepKeySdk;
3735
- this.context = "keepkey:" + features.label + ".json";
3736
- } catch (e) {
3737
- console.error("⚠️ [INIT] KeepKey SDK initialization failed:", e);
3788
+ } else {
3789
+ console.log("\uD83D\uDC41️ [VIEW-ONLY] Skipping KeepKey SDK initialization");
3790
+ }
3791
+ } else {
3792
+ console.log("\uD83D\uDC41️ [VIEW-ONLY] Skipping vault endpoint detection");
3793
+ this.keepkeyEndpoint = null;
3738
3794
  }
3739
3795
  let configWss = {
3740
3796
  username: this.username,
@@ -4764,11 +4820,16 @@ class SDK {
4764
4820
  }
4765
4821
  }
4766
4822
  console.log(tag, `Calling batch GetCharts with ${pubkeysForBatch.length} pubkeys`);
4767
- const chartsResponse = await this.pioneer.GetCharts({
4768
- pubkeys: pubkeysForBatch
4769
- });
4770
- const newBalances = chartsResponse?.data?.balances || [];
4771
- console.log(tag, `Received ${newBalances.length} balances from batch endpoint`);
4823
+ let newBalances = [];
4824
+ try {
4825
+ const chartsResponse = await this.pioneer.GetCharts({
4826
+ pubkeys: pubkeysForBatch
4827
+ });
4828
+ newBalances = chartsResponse?.data?.balances || [];
4829
+ console.log(tag, `Received ${newBalances.length} balances from batch endpoint`);
4830
+ } catch (chartsError) {
4831
+ console.warn(tag, `GetCharts API not available (${chartsError.message}), continuing without charts data`);
4832
+ }
4772
4833
  const uniqueBalances = new Map([...this.balances, ...newBalances].map((balance) => [
4773
4834
  balance.identifier || `${balance.caip}:${balance.pubkey}`,
4774
4835
  {
package/dist/index.es.js CHANGED
@@ -1274,7 +1274,15 @@ async function createUnsignedEvmTx(caip, to, amount, memo, pubkeys, pioneer, pub
1274
1274
  amountWei = balance - gasFee - buffer;
1275
1275
  console.log(tag, "isMax calculation - balance:", balance.toString(), "gasFee:", gasFee.toString(), "buffer:", buffer.toString(), "amountWei:", amountWei.toString());
1276
1276
  } else {
1277
- amountWei = BigInt(Math.round(amount * 1000000000000000000));
1277
+ const amountNum = typeof amount === "string" ? parseFloat(amount) : amount;
1278
+ if (isNaN(amountNum) || !isFinite(amountNum) || amountNum <= 0) {
1279
+ throw new Error(`Invalid amount for native gas transfer: "${amount}" (type: ${typeof amount}). ` + `Amount must be a valid positive number.`);
1280
+ }
1281
+ const amountCalculation = amountNum * 1000000000000000000;
1282
+ if (isNaN(amountCalculation) || !isFinite(amountCalculation)) {
1283
+ throw new Error(`Invalid amount calculation for native gas transfer. ` + `Input: ${amountNum}`);
1284
+ }
1285
+ amountWei = BigInt(Math.round(amountCalculation));
1278
1286
  const totalNeeded = amountWei + gasFee;
1279
1287
  if (totalNeeded > balance) {
1280
1288
  const availableForSwap = balance > gasFee ? balance - gasFee : 0n;
@@ -1433,11 +1441,22 @@ async function createUnsignedEvmTx(caip, to, amount, memo, pubkeys, pioneer, pub
1433
1441
  const tokenBalance = BigInt(Math.round(tokenBalanceData.data * Number(tokenMultiplier)));
1434
1442
  amountWei = tokenBalance;
1435
1443
  } else {
1436
- amountWei = BigInt(Math.round(amount * Number(tokenMultiplier)));
1444
+ const amountNum = typeof amount === "string" ? parseFloat(amount) : amount;
1445
+ if (isNaN(amountNum) || !isFinite(amountNum) || amountNum <= 0) {
1446
+ throw new Error(`Invalid amount for ERC20 transfer: "${amount}" (type: ${typeof amount}). ` + `Amount must be a valid positive number.`);
1447
+ }
1448
+ const amountCalculation = amountNum * Number(tokenMultiplier);
1449
+ if (isNaN(amountCalculation) || !isFinite(amountCalculation)) {
1450
+ throw new Error(`Invalid amount calculation for ERC20 transfer. ` + `Input: ${amountNum}, Decimals: ${tokenDecimals}, Multiplier: ${tokenMultiplier}`);
1451
+ }
1452
+ amountWei = BigInt(Math.round(amountCalculation));
1437
1453
  console.log(tag, "Token amount calculation:", {
1438
1454
  inputAmount: amount,
1455
+ inputType: typeof amount,
1456
+ parsedAmount: amountNum,
1439
1457
  decimals: tokenDecimals,
1440
- multiplier: tokenMultiplier,
1458
+ multiplier: tokenMultiplier.toString(),
1459
+ calculation: amountCalculation,
1441
1460
  resultWei: amountWei.toString()
1442
1461
  });
1443
1462
  }
@@ -3599,6 +3618,9 @@ class SDK {
3599
3618
  isPioneer;
3600
3619
  keepkeyEndpoint;
3601
3620
  forceLocalhost;
3621
+ viewOnlyMode;
3622
+ skipDevicePairing;
3623
+ skipKeeperEndpoint;
3602
3624
  getPubkeys;
3603
3625
  getBalances;
3604
3626
  blockchains;
@@ -3655,6 +3677,9 @@ class SDK {
3655
3677
  this.keepkeyEndpoint = null;
3656
3678
  this.forceLocalhost = config.forceLocalhost || false;
3657
3679
  this.paths = config.paths || [];
3680
+ this.viewOnlyMode = config.viewOnlyMode || false;
3681
+ this.skipDevicePairing = config.skipDevicePairing || config.viewOnlyMode || false;
3682
+ this.skipKeeperEndpoint = config.skipKeeperEndpoint || config.viewOnlyMode || false;
3658
3683
  this.blockchains = config.blockchains ? [...new Set(config.blockchains)] : [];
3659
3684
  if (config.blockchains && config.blockchains.length !== this.blockchains.length) {
3660
3685
  }
@@ -3695,7 +3720,20 @@ class SDK {
3695
3720
  fallbackToRemote: true
3696
3721
  }) : null;
3697
3722
  this.pairWallet = async (options) => {
3698
- return Promise.resolve({});
3723
+ const tag = TAG9 + " | pairWallet | ";
3724
+ if (this.viewOnlyMode || this.skipDevicePairing) {
3725
+ console.log(tag, "\uD83D\uDC41️ [VIEW-ONLY] Skipping device pairing");
3726
+ return {
3727
+ success: false,
3728
+ reason: "view-only-mode",
3729
+ message: "Device pairing skipped in view-only mode"
3730
+ };
3731
+ }
3732
+ return Promise.resolve({
3733
+ success: false,
3734
+ reason: "not-implemented",
3735
+ message: "Device pairing not yet implemented"
3736
+ });
3699
3737
  };
3700
3738
  this.getPubkeyKey = (pubkey) => {
3701
3739
  return `${pubkey.pubkey}_${pubkey.pathMaster}`;
@@ -3741,6 +3779,15 @@ class SDK {
3741
3779
  this.pubkeys = [];
3742
3780
  this.pubkeySet.clear();
3743
3781
  }
3782
+ this.isViewOnlyMode = () => {
3783
+ return this.viewOnlyMode;
3784
+ };
3785
+ this.canSignTransactions = () => {
3786
+ return !this.viewOnlyMode && !!this.keepKeySdk;
3787
+ };
3788
+ this.isVaultAvailable = () => {
3789
+ return !!this.keepkeyEndpoint && this.keepkeyEndpoint.isAvailable;
3790
+ };
3744
3791
  this.getUnifiedPortfolio = async function() {
3745
3792
  const tag = `${TAG9} | getUnifiedPortfolio | `;
3746
3793
  try {
@@ -3891,26 +3938,35 @@ class SDK {
3891
3938
  throw Error("Failed to init pioneer server!");
3892
3939
  this.paths.concat(getPaths(this.blockchains));
3893
3940
  await this.getGasAssets();
3894
- this.keepkeyEndpoint = await detectKkApiAvailability(this.forceLocalhost);
3895
- const keepkeyEndpoint = this.keepkeyEndpoint;
3896
- try {
3897
- const configKeepKey = {
3898
- apiKey: this.keepkeyApiKey || "keepkey-api-key",
3899
- pairingInfo: {
3900
- name: "KeepKey SDK Demo App",
3901
- imageUrl: "https://pioneers.dev/coins/keepkey.png",
3902
- basePath: keepkeyEndpoint.basePath,
3903
- url: keepkeyEndpoint.baseUrl
3941
+ if (!this.skipKeeperEndpoint) {
3942
+ this.keepkeyEndpoint = await detectKkApiAvailability(this.forceLocalhost);
3943
+ const keepkeyEndpoint = this.keepkeyEndpoint;
3944
+ if (!this.skipDevicePairing) {
3945
+ try {
3946
+ const configKeepKey = {
3947
+ apiKey: this.keepkeyApiKey || "keepkey-api-key",
3948
+ pairingInfo: {
3949
+ name: "KeepKey SDK Demo App",
3950
+ imageUrl: "https://pioneers.dev/coins/keepkey.png",
3951
+ basePath: keepkeyEndpoint.basePath,
3952
+ url: keepkeyEndpoint.baseUrl
3953
+ }
3954
+ };
3955
+ console.log("\uD83D\uDD11 [INIT] Initializing KeepKey SDK...");
3956
+ const keepKeySdk = await KeepKeySdk.create(configKeepKey);
3957
+ const features = await keepKeySdk.system.info.getFeatures();
3958
+ this.keepkeyApiKey = configKeepKey.apiKey;
3959
+ this.keepKeySdk = keepKeySdk;
3960
+ this.context = "keepkey:" + features.label + ".json";
3961
+ } catch (e) {
3962
+ console.error("⚠️ [INIT] KeepKey SDK initialization failed:", e);
3904
3963
  }
3905
- };
3906
- console.log("\uD83D\uDD11 [INIT] Initializing KeepKey SDK...");
3907
- const keepKeySdk = await KeepKeySdk.create(configKeepKey);
3908
- const features = await keepKeySdk.system.info.getFeatures();
3909
- this.keepkeyApiKey = configKeepKey.apiKey;
3910
- this.keepKeySdk = keepKeySdk;
3911
- this.context = "keepkey:" + features.label + ".json";
3912
- } catch (e) {
3913
- console.error("⚠️ [INIT] KeepKey SDK initialization failed:", e);
3964
+ } else {
3965
+ console.log("\uD83D\uDC41️ [VIEW-ONLY] Skipping KeepKey SDK initialization");
3966
+ }
3967
+ } else {
3968
+ console.log("\uD83D\uDC41️ [VIEW-ONLY] Skipping vault endpoint detection");
3969
+ this.keepkeyEndpoint = null;
3914
3970
  }
3915
3971
  let configWss = {
3916
3972
  username: this.username,
@@ -4940,11 +4996,16 @@ class SDK {
4940
4996
  }
4941
4997
  }
4942
4998
  console.log(tag, `Calling batch GetCharts with ${pubkeysForBatch.length} pubkeys`);
4943
- const chartsResponse = await this.pioneer.GetCharts({
4944
- pubkeys: pubkeysForBatch
4945
- });
4946
- const newBalances = chartsResponse?.data?.balances || [];
4947
- console.log(tag, `Received ${newBalances.length} balances from batch endpoint`);
4999
+ let newBalances = [];
5000
+ try {
5001
+ const chartsResponse = await this.pioneer.GetCharts({
5002
+ pubkeys: pubkeysForBatch
5003
+ });
5004
+ newBalances = chartsResponse?.data?.balances || [];
5005
+ console.log(tag, `Received ${newBalances.length} balances from batch endpoint`);
5006
+ } catch (chartsError) {
5007
+ console.warn(tag, `GetCharts API not available (${chartsError.message}), continuing without charts data`);
5008
+ }
4948
5009
  const uniqueBalances = new Map([...this.balances, ...newBalances].map((balance) => [
4949
5010
  balance.identifier || `${balance.caip}:${balance.pubkey}`,
4950
5011
  {
package/dist/index.js CHANGED
@@ -1274,7 +1274,15 @@ async function createUnsignedEvmTx(caip, to, amount, memo, pubkeys, pioneer, pub
1274
1274
  amountWei = balance - gasFee - buffer;
1275
1275
  console.log(tag, "isMax calculation - balance:", balance.toString(), "gasFee:", gasFee.toString(), "buffer:", buffer.toString(), "amountWei:", amountWei.toString());
1276
1276
  } else {
1277
- amountWei = BigInt(Math.round(amount * 1000000000000000000));
1277
+ const amountNum = typeof amount === "string" ? parseFloat(amount) : amount;
1278
+ if (isNaN(amountNum) || !isFinite(amountNum) || amountNum <= 0) {
1279
+ throw new Error(`Invalid amount for native gas transfer: "${amount}" (type: ${typeof amount}). ` + `Amount must be a valid positive number.`);
1280
+ }
1281
+ const amountCalculation = amountNum * 1000000000000000000;
1282
+ if (isNaN(amountCalculation) || !isFinite(amountCalculation)) {
1283
+ throw new Error(`Invalid amount calculation for native gas transfer. ` + `Input: ${amountNum}`);
1284
+ }
1285
+ amountWei = BigInt(Math.round(amountCalculation));
1278
1286
  const totalNeeded = amountWei + gasFee;
1279
1287
  if (totalNeeded > balance) {
1280
1288
  const availableForSwap = balance > gasFee ? balance - gasFee : 0n;
@@ -1433,11 +1441,22 @@ async function createUnsignedEvmTx(caip, to, amount, memo, pubkeys, pioneer, pub
1433
1441
  const tokenBalance = BigInt(Math.round(tokenBalanceData.data * Number(tokenMultiplier)));
1434
1442
  amountWei = tokenBalance;
1435
1443
  } else {
1436
- amountWei = BigInt(Math.round(amount * Number(tokenMultiplier)));
1444
+ const amountNum = typeof amount === "string" ? parseFloat(amount) : amount;
1445
+ if (isNaN(amountNum) || !isFinite(amountNum) || amountNum <= 0) {
1446
+ throw new Error(`Invalid amount for ERC20 transfer: "${amount}" (type: ${typeof amount}). ` + `Amount must be a valid positive number.`);
1447
+ }
1448
+ const amountCalculation = amountNum * Number(tokenMultiplier);
1449
+ if (isNaN(amountCalculation) || !isFinite(amountCalculation)) {
1450
+ throw new Error(`Invalid amount calculation for ERC20 transfer. ` + `Input: ${amountNum}, Decimals: ${tokenDecimals}, Multiplier: ${tokenMultiplier}`);
1451
+ }
1452
+ amountWei = BigInt(Math.round(amountCalculation));
1437
1453
  console.log(tag, "Token amount calculation:", {
1438
1454
  inputAmount: amount,
1455
+ inputType: typeof amount,
1456
+ parsedAmount: amountNum,
1439
1457
  decimals: tokenDecimals,
1440
- multiplier: tokenMultiplier,
1458
+ multiplier: tokenMultiplier.toString(),
1459
+ calculation: amountCalculation,
1441
1460
  resultWei: amountWei.toString()
1442
1461
  });
1443
1462
  }
@@ -3599,6 +3618,9 @@ class SDK {
3599
3618
  isPioneer;
3600
3619
  keepkeyEndpoint;
3601
3620
  forceLocalhost;
3621
+ viewOnlyMode;
3622
+ skipDevicePairing;
3623
+ skipKeeperEndpoint;
3602
3624
  getPubkeys;
3603
3625
  getBalances;
3604
3626
  blockchains;
@@ -3655,6 +3677,9 @@ class SDK {
3655
3677
  this.keepkeyEndpoint = null;
3656
3678
  this.forceLocalhost = config.forceLocalhost || false;
3657
3679
  this.paths = config.paths || [];
3680
+ this.viewOnlyMode = config.viewOnlyMode || false;
3681
+ this.skipDevicePairing = config.skipDevicePairing || config.viewOnlyMode || false;
3682
+ this.skipKeeperEndpoint = config.skipKeeperEndpoint || config.viewOnlyMode || false;
3658
3683
  this.blockchains = config.blockchains ? [...new Set(config.blockchains)] : [];
3659
3684
  if (config.blockchains && config.blockchains.length !== this.blockchains.length) {
3660
3685
  }
@@ -3695,7 +3720,20 @@ class SDK {
3695
3720
  fallbackToRemote: true
3696
3721
  }) : null;
3697
3722
  this.pairWallet = async (options) => {
3698
- return Promise.resolve({});
3723
+ const tag = TAG9 + " | pairWallet | ";
3724
+ if (this.viewOnlyMode || this.skipDevicePairing) {
3725
+ console.log(tag, "\uD83D\uDC41️ [VIEW-ONLY] Skipping device pairing");
3726
+ return {
3727
+ success: false,
3728
+ reason: "view-only-mode",
3729
+ message: "Device pairing skipped in view-only mode"
3730
+ };
3731
+ }
3732
+ return Promise.resolve({
3733
+ success: false,
3734
+ reason: "not-implemented",
3735
+ message: "Device pairing not yet implemented"
3736
+ });
3699
3737
  };
3700
3738
  this.getPubkeyKey = (pubkey) => {
3701
3739
  return `${pubkey.pubkey}_${pubkey.pathMaster}`;
@@ -3741,6 +3779,15 @@ class SDK {
3741
3779
  this.pubkeys = [];
3742
3780
  this.pubkeySet.clear();
3743
3781
  }
3782
+ this.isViewOnlyMode = () => {
3783
+ return this.viewOnlyMode;
3784
+ };
3785
+ this.canSignTransactions = () => {
3786
+ return !this.viewOnlyMode && !!this.keepKeySdk;
3787
+ };
3788
+ this.isVaultAvailable = () => {
3789
+ return !!this.keepkeyEndpoint && this.keepkeyEndpoint.isAvailable;
3790
+ };
3744
3791
  this.getUnifiedPortfolio = async function() {
3745
3792
  const tag = `${TAG9} | getUnifiedPortfolio | `;
3746
3793
  try {
@@ -3891,26 +3938,35 @@ class SDK {
3891
3938
  throw Error("Failed to init pioneer server!");
3892
3939
  this.paths.concat(getPaths(this.blockchains));
3893
3940
  await this.getGasAssets();
3894
- this.keepkeyEndpoint = await detectKkApiAvailability(this.forceLocalhost);
3895
- const keepkeyEndpoint = this.keepkeyEndpoint;
3896
- try {
3897
- const configKeepKey = {
3898
- apiKey: this.keepkeyApiKey || "keepkey-api-key",
3899
- pairingInfo: {
3900
- name: "KeepKey SDK Demo App",
3901
- imageUrl: "https://pioneers.dev/coins/keepkey.png",
3902
- basePath: keepkeyEndpoint.basePath,
3903
- url: keepkeyEndpoint.baseUrl
3941
+ if (!this.skipKeeperEndpoint) {
3942
+ this.keepkeyEndpoint = await detectKkApiAvailability(this.forceLocalhost);
3943
+ const keepkeyEndpoint = this.keepkeyEndpoint;
3944
+ if (!this.skipDevicePairing) {
3945
+ try {
3946
+ const configKeepKey = {
3947
+ apiKey: this.keepkeyApiKey || "keepkey-api-key",
3948
+ pairingInfo: {
3949
+ name: "KeepKey SDK Demo App",
3950
+ imageUrl: "https://pioneers.dev/coins/keepkey.png",
3951
+ basePath: keepkeyEndpoint.basePath,
3952
+ url: keepkeyEndpoint.baseUrl
3953
+ }
3954
+ };
3955
+ console.log("\uD83D\uDD11 [INIT] Initializing KeepKey SDK...");
3956
+ const keepKeySdk = await KeepKeySdk.create(configKeepKey);
3957
+ const features = await keepKeySdk.system.info.getFeatures();
3958
+ this.keepkeyApiKey = configKeepKey.apiKey;
3959
+ this.keepKeySdk = keepKeySdk;
3960
+ this.context = "keepkey:" + features.label + ".json";
3961
+ } catch (e) {
3962
+ console.error("⚠️ [INIT] KeepKey SDK initialization failed:", e);
3904
3963
  }
3905
- };
3906
- console.log("\uD83D\uDD11 [INIT] Initializing KeepKey SDK...");
3907
- const keepKeySdk = await KeepKeySdk.create(configKeepKey);
3908
- const features = await keepKeySdk.system.info.getFeatures();
3909
- this.keepkeyApiKey = configKeepKey.apiKey;
3910
- this.keepKeySdk = keepKeySdk;
3911
- this.context = "keepkey:" + features.label + ".json";
3912
- } catch (e) {
3913
- console.error("⚠️ [INIT] KeepKey SDK initialization failed:", e);
3964
+ } else {
3965
+ console.log("\uD83D\uDC41️ [VIEW-ONLY] Skipping KeepKey SDK initialization");
3966
+ }
3967
+ } else {
3968
+ console.log("\uD83D\uDC41️ [VIEW-ONLY] Skipping vault endpoint detection");
3969
+ this.keepkeyEndpoint = null;
3914
3970
  }
3915
3971
  let configWss = {
3916
3972
  username: this.username,
@@ -4940,11 +4996,16 @@ class SDK {
4940
4996
  }
4941
4997
  }
4942
4998
  console.log(tag, `Calling batch GetCharts with ${pubkeysForBatch.length} pubkeys`);
4943
- const chartsResponse = await this.pioneer.GetCharts({
4944
- pubkeys: pubkeysForBatch
4945
- });
4946
- const newBalances = chartsResponse?.data?.balances || [];
4947
- console.log(tag, `Received ${newBalances.length} balances from batch endpoint`);
4999
+ let newBalances = [];
5000
+ try {
5001
+ const chartsResponse = await this.pioneer.GetCharts({
5002
+ pubkeys: pubkeysForBatch
5003
+ });
5004
+ newBalances = chartsResponse?.data?.balances || [];
5005
+ console.log(tag, `Received ${newBalances.length} balances from batch endpoint`);
5006
+ } catch (chartsError) {
5007
+ console.warn(tag, `GetCharts API not available (${chartsError.message}), continuing without charts data`);
5008
+ }
4948
5009
  const uniqueBalances = new Map([...this.balances, ...newBalances].map((balance) => [
4949
5010
  balance.identifier || `${balance.caip}:${balance.pubkey}`,
4950
5011
  {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "author": "highlander",
3
3
  "name": "@pioneer-platform/pioneer-sdk",
4
- "version": "8.12.8",
4
+ "version": "8.13.0",
5
5
  "dependencies": {
6
6
  "@keepkey/keepkey-sdk": "^0.2.62",
7
7
  "@pioneer-platform/loggerdog": "^8.11.0",
package/src/index.ts CHANGED
@@ -76,6 +76,10 @@ export interface PioneerSDKConfig {
76
76
  offlineFirst?: boolean;
77
77
  vaultUrl?: string;
78
78
  forceLocalhost?: boolean;
79
+ // View-only mode configuration
80
+ viewOnlyMode?: boolean; // Enable view-only mode (no device/vault required)
81
+ skipDevicePairing?: boolean; // Skip KeepKey device pairing
82
+ skipKeeperEndpoint?: boolean; // Skip vault endpoint detection
79
83
  }
80
84
 
81
85
  export interface SyncState {
@@ -140,6 +144,10 @@ export class SDK {
140
144
  public isPioneer: string | null;
141
145
  public keepkeyEndpoint: { isAvailable: boolean; baseUrl: string; basePath: string } | null;
142
146
  public forceLocalhost: boolean;
147
+ // View-only mode properties
148
+ public viewOnlyMode: boolean;
149
+ public skipDevicePairing: boolean;
150
+ public skipKeeperEndpoint: boolean;
143
151
  // public loadPubkeyCache: (pubkeys: any) => Promise<void>;
144
152
  public getPubkeys: (wallets?: string[]) => Promise<any[]>;
145
153
  public getBalances: (filter?: any) => Promise<any[]>;
@@ -229,6 +237,11 @@ export class SDK {
229
237
  this.forceLocalhost = config.forceLocalhost || false;
230
238
  this.paths = config.paths || [];
231
239
 
240
+ // View-only mode initialization
241
+ this.viewOnlyMode = config.viewOnlyMode || false;
242
+ this.skipDevicePairing = config.skipDevicePairing || config.viewOnlyMode || false;
243
+ this.skipKeeperEndpoint = config.skipKeeperEndpoint || config.viewOnlyMode || false;
244
+
232
245
  // Deduplicate blockchains to prevent duplicate dashboard calculations
233
246
  this.blockchains = config.blockchains ? [...new Set(config.blockchains)] : [];
234
247
  if (config.blockchains && config.blockchains.length !== this.blockchains.length) {
@@ -278,8 +291,25 @@ export class SDK {
278
291
  : null;
279
292
 
280
293
  this.pairWallet = async (options: any) => {
281
- // Implementation will be added later
282
- return Promise.resolve({});
294
+ const tag = TAG + ' | pairWallet | ';
295
+
296
+ // Skip device pairing in view-only mode
297
+ if (this.viewOnlyMode || this.skipDevicePairing) {
298
+ console.log(tag, '👁️ [VIEW-ONLY] Skipping device pairing');
299
+ return {
300
+ success: false,
301
+ reason: 'view-only-mode',
302
+ message: 'Device pairing skipped in view-only mode'
303
+ };
304
+ }
305
+
306
+ // Normal device pairing logic would go here
307
+ // For now, return placeholder
308
+ return Promise.resolve({
309
+ success: false,
310
+ reason: 'not-implemented',
311
+ message: 'Device pairing not yet implemented'
312
+ });
283
313
  };
284
314
 
285
315
  // Helper method to generate unique key for a pubkey
@@ -347,6 +377,19 @@ export class SDK {
347
377
  this.pubkeySet.clear();
348
378
  }
349
379
 
380
+ // View-only mode helper methods
381
+ this.isViewOnlyMode = (): boolean => {
382
+ return this.viewOnlyMode;
383
+ };
384
+
385
+ this.canSignTransactions = (): boolean => {
386
+ return !this.viewOnlyMode && !!this.keepKeySdk;
387
+ };
388
+
389
+ this.isVaultAvailable = (): boolean => {
390
+ return !!this.keepkeyEndpoint && this.keepkeyEndpoint.isAvailable;
391
+ };
392
+
350
393
  // Fast portfolio loading from kkapi:// cache
351
394
  this.getUnifiedPortfolio = async function () {
352
395
  const tag = `${TAG} | getUnifiedPortfolio | `;
@@ -558,31 +601,40 @@ export class SDK {
558
601
  // Get gas assets (needed for asset map)
559
602
  await this.getGasAssets();
560
603
 
561
- // Detect KeepKey endpoint
562
- this.keepkeyEndpoint = await detectKkApiAvailability(this.forceLocalhost);
563
- const keepkeyEndpoint = this.keepkeyEndpoint;
604
+ // Detect KeepKey endpoint (skip if view-only mode)
605
+ if (!this.skipKeeperEndpoint) {
606
+ this.keepkeyEndpoint = await detectKkApiAvailability(this.forceLocalhost);
607
+ const keepkeyEndpoint = this.keepkeyEndpoint;
564
608
 
565
- // Initialize KeepKey SDK if available
566
- try {
567
- const configKeepKey = {
568
- apiKey: this.keepkeyApiKey || 'keepkey-api-key',
569
- pairingInfo: {
570
- name: 'KeepKey SDK Demo App',
571
- imageUrl: 'https://pioneers.dev/coins/keepkey.png',
572
- basePath: keepkeyEndpoint.basePath,
573
- url: keepkeyEndpoint.baseUrl,
574
- },
575
- };
576
-
577
- console.log('🔑 [INIT] Initializing KeepKey SDK...');
578
- const keepKeySdk = await KeepKeySdk.create(configKeepKey);
579
- const features = await keepKeySdk.system.info.getFeatures();
580
-
581
- this.keepkeyApiKey = configKeepKey.apiKey;
582
- this.keepKeySdk = keepKeySdk;
583
- this.context = 'keepkey:' + features.label + '.json';
584
- } catch (e) {
585
- console.error('⚠️ [INIT] KeepKey SDK initialization failed:', e);
609
+ // Initialize KeepKey SDK if available and not skipping device pairing
610
+ if (!this.skipDevicePairing) {
611
+ try {
612
+ const configKeepKey = {
613
+ apiKey: this.keepkeyApiKey || 'keepkey-api-key',
614
+ pairingInfo: {
615
+ name: 'KeepKey SDK Demo App',
616
+ imageUrl: 'https://pioneers.dev/coins/keepkey.png',
617
+ basePath: keepkeyEndpoint.basePath,
618
+ url: keepkeyEndpoint.baseUrl,
619
+ },
620
+ };
621
+
622
+ console.log('🔑 [INIT] Initializing KeepKey SDK...');
623
+ const keepKeySdk = await KeepKeySdk.create(configKeepKey);
624
+ const features = await keepKeySdk.system.info.getFeatures();
625
+
626
+ this.keepkeyApiKey = configKeepKey.apiKey;
627
+ this.keepKeySdk = keepKeySdk;
628
+ this.context = 'keepkey:' + features.label + '.json';
629
+ } catch (e) {
630
+ console.error('⚠️ [INIT] KeepKey SDK initialization failed:', e);
631
+ }
632
+ } else {
633
+ console.log('👁️ [VIEW-ONLY] Skipping KeepKey SDK initialization');
634
+ }
635
+ } else {
636
+ console.log('👁️ [VIEW-ONLY] Skipping vault endpoint detection');
637
+ this.keepkeyEndpoint = null;
586
638
  }
587
639
 
588
640
  // Initialize WebSocket events
@@ -1934,7 +1986,7 @@ export class SDK {
1934
1986
  console.time('GetPortfolioBalances Response Time');
1935
1987
 
1936
1988
  try {
1937
- // FIX: Wrap assetQuery in object with pubkeys field to match server API
1989
+ // Wrap assetQuery in object with pubkeys field (API expects { pubkeys: [...] })
1938
1990
  let marketInfo = await this.pioneer.GetPortfolioBalances({ pubkeys: assetQuery });
1939
1991
  const apiCallTime = performance.now() - apiCallStart;
1940
1992
  console.timeEnd('GetPortfolioBalances Response Time');
@@ -2088,12 +2140,17 @@ export class SDK {
2088
2140
  console.log(tag, `Calling batch GetCharts with ${pubkeysForBatch.length} pubkeys`);
2089
2141
 
2090
2142
  // Single batch call to get ALL charts data
2091
- const chartsResponse = await this.pioneer.GetCharts({
2092
- pubkeys: pubkeysForBatch
2093
- });
2094
-
2095
- const newBalances = chartsResponse?.data?.balances || [];
2096
- console.log(tag, `Received ${newBalances.length} balances from batch endpoint`);
2143
+ let newBalances = [];
2144
+ try {
2145
+ const chartsResponse = await this.pioneer.GetCharts({
2146
+ pubkeys: pubkeysForBatch
2147
+ });
2148
+ newBalances = chartsResponse?.data?.balances || [];
2149
+ console.log(tag, `Received ${newBalances.length} balances from batch endpoint`);
2150
+ } catch (chartsError: any) {
2151
+ // GetCharts API may not be available - gracefully continue without charts data
2152
+ console.warn(tag, `GetCharts API not available (${chartsError.message}), continuing without charts data`);
2153
+ }
2097
2154
 
2098
2155
  // Deduplicate balances using a Map with `identifier` as the key
2099
2156
  const uniqueBalances = new Map(
@@ -330,7 +330,29 @@ export async function createUnsignedEvmTx(
330
330
  amountWei = balance - gasFee - buffer;
331
331
  console.log(tag, 'isMax calculation - balance:', balance.toString(), 'gasFee:', gasFee.toString(), 'buffer:', buffer.toString(), 'amountWei:', amountWei.toString());
332
332
  } else {
333
- amountWei = BigInt(Math.round(amount * 1e18));
333
+ // Validate and convert amount to number
334
+ // Handle both string and number inputs
335
+ const amountNum = typeof amount === 'string' ? parseFloat(amount) : amount;
336
+
337
+ // Validate amount is a valid number
338
+ if (isNaN(amountNum) || !isFinite(amountNum) || amountNum <= 0) {
339
+ throw new Error(
340
+ `Invalid amount for native gas transfer: "${amount}" (type: ${typeof amount}). ` +
341
+ `Amount must be a valid positive number.`
342
+ );
343
+ }
344
+
345
+ const amountCalculation = amountNum * 1e18;
346
+
347
+ // Validate calculation result before BigInt conversion
348
+ if (isNaN(amountCalculation) || !isFinite(amountCalculation)) {
349
+ throw new Error(
350
+ `Invalid amount calculation for native gas transfer. ` +
351
+ `Input: ${amountNum}`
352
+ );
353
+ }
354
+
355
+ amountWei = BigInt(Math.round(amountCalculation));
334
356
  const totalNeeded = amountWei + gasFee;
335
357
 
336
358
  if (totalNeeded > balance) {
@@ -578,12 +600,37 @@ export async function createUnsignedEvmTx(
578
600
  const tokenBalance = BigInt(Math.round(tokenBalanceData.data * Number(tokenMultiplier)));
579
601
  amountWei = tokenBalance;
580
602
  } else {
603
+ // Validate and convert amount to number
604
+ // Handle both string and number inputs
605
+ const amountNum = typeof amount === 'string' ? parseFloat(amount) : amount;
606
+
607
+ // Validate amount is a valid number
608
+ if (isNaN(amountNum) || !isFinite(amountNum) || amountNum <= 0) {
609
+ throw new Error(
610
+ `Invalid amount for ERC20 transfer: "${amount}" (type: ${typeof amount}). ` +
611
+ `Amount must be a valid positive number.`
612
+ );
613
+ }
614
+
581
615
  // Use BigInt math to avoid precision loss
582
- amountWei = BigInt(Math.round(amount * Number(tokenMultiplier)));
616
+ const amountCalculation = amountNum * Number(tokenMultiplier);
617
+
618
+ // Validate calculation result before BigInt conversion
619
+ if (isNaN(amountCalculation) || !isFinite(amountCalculation)) {
620
+ throw new Error(
621
+ `Invalid amount calculation for ERC20 transfer. ` +
622
+ `Input: ${amountNum}, Decimals: ${tokenDecimals}, Multiplier: ${tokenMultiplier}`
623
+ );
624
+ }
625
+
626
+ amountWei = BigInt(Math.round(amountCalculation));
583
627
  console.log(tag, 'Token amount calculation:', {
584
628
  inputAmount: amount,
629
+ inputType: typeof amount,
630
+ parsedAmount: amountNum,
585
631
  decimals: tokenDecimals,
586
- multiplier: tokenMultiplier,
632
+ multiplier: tokenMultiplier.toString(),
633
+ calculation: amountCalculation,
587
634
  resultWei: amountWei.toString(),
588
635
  });
589
636
  }