@pioneer-platform/pioneer-sdk 8.15.31 → 8.15.33

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.js CHANGED
@@ -2823,7 +2823,21 @@ async function createUnsignedUxtoTx(caip, to, amount, memo, pubkeys, pioneer, pu
2823
2823
  }
2824
2824
  } catch (error) {
2825
2825
  console.error(`${tag}: Failed to get fee rates from Pioneer API:`, error.message || error);
2826
- throw new Error(`Unable to get fee rate for network ${networkId}: ${error.message || "API unavailable"}`);
2826
+ const defaultFees = {
2827
+ "bip122:000000000019d6689c085ae165831e93": { slow: 3, average: 5, fast: 10, fastest: 15 },
2828
+ "bip122:12a765e31ffd4059bada1e25190f6e98": { slow: 2, average: 3, fast: 5, fastest: 10 },
2829
+ "bip122:00000000001a91e3dace36e2be3bf030": { slow: 1, average: 1, fast: 2, fastest: 3 },
2830
+ "bip122:000000000000000000651ef99cb9fcbe": { slow: 1, average: 1, fast: 2, fastest: 3 },
2831
+ "bip122:000007d91d1254d60e2dd1ae58038307": { slow: 1, average: 1, fast: 2, fastest: 3 },
2832
+ "bip122:4da631f2ac1bed857bd968c67c913978": { slow: 75, average: 80, fast: 100, fastest: 120 },
2833
+ "bip122:00040fe8ec8471911baa1db1266ea15d": { slow: 1, average: 1, fast: 2, fastest: 3 }
2834
+ };
2835
+ if (defaultFees[networkId]) {
2836
+ console.warn(`${tag}: Using default fee rates for ${networkId}`);
2837
+ feeRateFromNode = defaultFees[networkId];
2838
+ } else {
2839
+ throw new Error(`Unable to get fee rate for network ${networkId}: ${error.message || "API unavailable"}`);
2840
+ }
2827
2841
  }
2828
2842
  }
2829
2843
  let effectiveFeeRate;
@@ -3539,6 +3553,7 @@ function getNetworkName(networkId) {
3539
3553
  "bip122:00000000001a91e3dace36e2be3bf030": "Dogecoin",
3540
3554
  "bip122:000000000000000000651ef99cb9fcbe": "Bitcoin Cash",
3541
3555
  "bip122:000007d91d1254d60e2dd1ae58038307": "Dash",
3556
+ "bip122:00040fe8ec8471911baa1db1266ea15d": "Zcash",
3542
3557
  "bip122:4da631f2ac1bed857bd968c67c913978": "DigiByte",
3543
3558
  "eip155:1": "Ethereum",
3544
3559
  "eip155:56": "BNB Smart Chain",
@@ -4110,7 +4125,7 @@ function buildDashboardFromBalances(balances, blockchains, assetsMap) {
4110
4125
  totalValueUsd: 0,
4111
4126
  networkPercentages: []
4112
4127
  };
4113
- let totalPortfolioValue = 0;
4128
+ let totalPortfolioValueCents = 0;
4114
4129
  const networksTemp = [];
4115
4130
  for (const blockchain of blockchains) {
4116
4131
  const filteredBalances = balances.filter((b2) => {
@@ -4118,50 +4133,46 @@ function buildDashboardFromBalances(balances, blockchains, assetsMap) {
4118
4133
  return networkId === blockchain || blockchain === "eip155:*" && networkId.startsWith("eip155:");
4119
4134
  });
4120
4135
  const balanceMap = new Map;
4121
- const isBitcoin = blockchain.includes("bip122:000000000019d6689c085ae165831e93");
4122
- if (isBitcoin) {
4123
- const bitcoinByValue = new Map;
4124
- filteredBalances.forEach((balance) => {
4125
- const valueKey = `${balance.balance}_${balance.valueUsd}`;
4126
- if (!bitcoinByValue.has(valueKey)) {
4127
- bitcoinByValue.set(valueKey, []);
4128
- }
4129
- bitcoinByValue.get(valueKey).push(balance);
4130
- });
4131
- for (const [valueKey, balances2] of bitcoinByValue.entries()) {
4132
- if (balances2.length === 3 && parseFloat(balances2[0].valueUsd || "0") > 0) {
4133
- const xpubBalance = balances2.find((b2) => b2.pubkey?.startsWith("xpub")) || balances2[0];
4134
- const key = `${xpubBalance.caip}_${xpubBalance.pubkey || "default"}`;
4135
- balanceMap.set(key, xpubBalance);
4136
- } else {
4137
- balances2.forEach((balance) => {
4138
- const key = `${balance.caip}_${balance.pubkey || "default"}`;
4139
- balanceMap.set(key, balance);
4140
- });
4141
- }
4136
+ filteredBalances.forEach((balance) => {
4137
+ const key = `${balance.caip}_${balance.pubkey || "default"}`;
4138
+ if (!balanceMap.has(key) || parseFloat(balance.valueUsd || "0") > parseFloat(balanceMap.get(key).valueUsd || "0")) {
4139
+ balanceMap.set(key, balance);
4140
+ } else {
4141
+ const existing = balanceMap.get(key);
4142
+ const existingValue = parseFloat(existing.valueUsd || "0");
4143
+ const newValue = parseFloat(balance.valueUsd || "0");
4144
+ console.log(`⚠️ [BUILD-DASHBOARD] ${blockchain} dedup skipped (same caip+pubkey):`, {
4145
+ caip: balance.caip,
4146
+ pubkey: (balance.pubkey || "default").substring(0, 20),
4147
+ existing: existingValue,
4148
+ new: newValue,
4149
+ diff: Math.abs(existingValue - newValue)
4150
+ });
4142
4151
  }
4143
- } else {
4144
- filteredBalances.forEach((balance) => {
4145
- const key = `${balance.caip}_${balance.pubkey || "default"}`;
4146
- if (!balanceMap.has(key) || parseFloat(balance.valueUsd || "0") > parseFloat(balanceMap.get(key).valueUsd || "0")) {
4147
- balanceMap.set(key, balance);
4148
- }
4149
- });
4150
- }
4152
+ });
4151
4153
  const networkBalances = Array.from(balanceMap.values());
4152
- const networkTotal = networkBalances.reduce((sum, balance, idx) => {
4153
- const valueUsd = typeof balance.valueUsd === "string" ? parseFloat(balance.valueUsd) : balance.valueUsd || 0;
4154
+ const networkTotalCents = networkBalances.reduce((sumCents, balance, idx) => {
4155
+ let valueUsd = typeof balance.valueUsd === "string" ? parseFloat(balance.valueUsd) : balance.valueUsd || 0;
4156
+ if (isNaN(valueUsd)) {
4157
+ console.warn(`⚠️ [BUILD-DASHBOARD] NaN value detected for ${balance.caip}:`, balance.valueUsd);
4158
+ return sumCents;
4159
+ }
4160
+ balance.valueUsd = valueUsd;
4161
+ const valueCents = Math.round(valueUsd * 100 * 1000) / 1000;
4154
4162
  if (idx < 2) {
4155
4163
  console.log(`\uD83D\uDCB0 [BUILD-DASHBOARD] ${blockchain} balance #${idx}:`, {
4156
4164
  caip: balance.caip,
4157
4165
  balance: balance.balance,
4158
4166
  valueUsd: balance.valueUsd,
4167
+ valueUsdType: typeof balance.valueUsd,
4159
4168
  parsedValueUsd: valueUsd,
4160
- runningSum: sum + valueUsd
4169
+ valueCents,
4170
+ runningSumCents: sumCents + valueCents
4161
4171
  });
4162
4172
  }
4163
- return sum + valueUsd;
4173
+ return sumCents + valueCents;
4164
4174
  }, 0);
4175
+ const networkTotal = networkTotalCents / 100;
4165
4176
  console.log(`\uD83D\uDCB0 [BUILD-DASHBOARD] ${blockchain} totals:`, {
4166
4177
  balancesCount: networkBalances.length,
4167
4178
  networkTotal,
@@ -4184,8 +4195,9 @@ function buildDashboardFromBalances(balances, blockchains, assetsMap) {
4184
4195
  color: gasAsset?.color || assetInfo?.color || null,
4185
4196
  totalNativeBalance
4186
4197
  });
4187
- totalPortfolioValue += networkTotal;
4198
+ totalPortfolioValueCents += networkTotalCents;
4188
4199
  }
4200
+ const totalPortfolioValue = totalPortfolioValueCents / 100;
4189
4201
  dashboardData.networks = networksTemp.sort((a2, b2) => b2.totalValueUsd - a2.totalValueUsd);
4190
4202
  dashboardData.totalValueUsd = totalPortfolioValue;
4191
4203
  dashboardData.networkPercentages = dashboardData.networks.map((network) => ({
@@ -4193,6 +4205,26 @@ function buildDashboardFromBalances(balances, blockchains, assetsMap) {
4193
4205
  percentage: totalPortfolioValue > 0 ? Number((network.totalValueUsd / totalPortfolioValue * 100).toFixed(2)) : 0
4194
4206
  })).filter((entry) => entry.percentage > 0);
4195
4207
  console.log(`[FAST DASHBOARD] ✅ Built dashboard: ${dashboardData.networks.length} networks, $${totalPortfolioValue.toFixed(2)} total`);
4208
+ const inputBalancesTotal = balances.reduce((sum, b2) => {
4209
+ const value = typeof b2.valueUsd === "string" ? parseFloat(b2.valueUsd) : b2.valueUsd || 0;
4210
+ return sum + (isNaN(value) ? 0 : value);
4211
+ }, 0);
4212
+ const difference = Math.abs(inputBalancesTotal - totalPortfolioValue);
4213
+ if (difference > 0.01) {
4214
+ console.error(`\uD83D\uDEA8 [BUILD-DASHBOARD] BALANCE MISMATCH DETECTED!`);
4215
+ console.error(` Input balances total: $${inputBalancesTotal.toFixed(2)} USD`);
4216
+ console.error(` Dashboard total: $${totalPortfolioValue.toFixed(2)} USD`);
4217
+ console.error(` Difference: $${difference.toFixed(2)} USD`);
4218
+ console.error(` This indicates lost balances during dashboard aggregation!`);
4219
+ console.error(` Network breakdown:`);
4220
+ dashboardData.networks.forEach((network) => {
4221
+ console.error(` ${network.networkId}: $${network.totalValueUsd.toFixed(2)}`);
4222
+ });
4223
+ } else if (difference > 0.001) {
4224
+ console.warn(`⚠️ [BUILD-DASHBOARD] Minor balance rounding difference: $${difference.toFixed(4)} USD`);
4225
+ } else {
4226
+ console.log(`✅ [BUILD-DASHBOARD] Balance reconciliation passed (diff: $${difference.toFixed(4)})`);
4227
+ }
4196
4228
  return dashboardData;
4197
4229
  }
4198
4230
 
@@ -4327,6 +4359,80 @@ function filterPubkeysForAsset(pubkeys, caip, caipToNetworkId7) {
4327
4359
 
4328
4360
  // src/utils/portfolio-helpers.ts
4329
4361
  var TAG9 = " | portfolio-helpers | ";
4362
+ var EXPLORER_BASE_URLS = {
4363
+ "eip155:1": {
4364
+ address: "https://etherscan.io/address/",
4365
+ tx: "https://etherscan.io/tx/"
4366
+ },
4367
+ "eip155:137": {
4368
+ address: "https://polygonscan.com/address/",
4369
+ tx: "https://polygonscan.com/tx/"
4370
+ },
4371
+ "eip155:8453": {
4372
+ address: "https://basescan.org/address/",
4373
+ tx: "https://basescan.org/tx/"
4374
+ },
4375
+ "eip155:56": {
4376
+ address: "https://bscscan.com/address/",
4377
+ tx: "https://bscscan.com/tx/"
4378
+ },
4379
+ "eip155:41454": {
4380
+ address: "https://explorer.monad.xyz/address/",
4381
+ tx: "https://explorer.monad.xyz/tx/"
4382
+ },
4383
+ "eip155:2868": {
4384
+ address: "https://app.hyperliquid.xyz/explorer/address/",
4385
+ tx: "https://app.hyperliquid.xyz/explorer/tx/"
4386
+ },
4387
+ "bip122:000000000019d6689c085ae165831e93": {
4388
+ address: "https://blockstream.info/address/",
4389
+ tx: "https://blockstream.info/tx/"
4390
+ },
4391
+ "bip122:12a765e31ffd4059bada1e25190f6e98": {
4392
+ address: "https://blockchair.com/litecoin/address/",
4393
+ tx: "https://blockchair.com/litecoin/transaction/"
4394
+ },
4395
+ "bip122:00000000001a91e3dace36e2be3bf030": {
4396
+ address: "https://dogechain.info/address/",
4397
+ tx: "https://dogechain.info/tx/"
4398
+ },
4399
+ "bip122:000000000000000000651ef99cb9fcbe": {
4400
+ address: "https://blockchair.com/bitcoin-cash/address/",
4401
+ tx: "https://blockchair.com/bitcoin-cash/transaction/"
4402
+ },
4403
+ "bip122:000007d91d1254d60e2dd1ae58038307": {
4404
+ address: "https://chainz.cryptoid.info/dash/address.dws?",
4405
+ tx: "https://chainz.cryptoid.info/dash/tx.dws?"
4406
+ },
4407
+ "bip122:4da631f2ac1bed857bd968c67c913978": {
4408
+ address: "https://digiexplorer.info/address/",
4409
+ tx: "https://digiexplorer.info/tx/"
4410
+ },
4411
+ "bip122:00040fe8ec8471911baa1db1266ea15d": {
4412
+ address: "https://explorer.zcha.in/accounts/",
4413
+ tx: "https://explorer.zcha.in/transactions/"
4414
+ },
4415
+ "cosmos:cosmoshub-4": {
4416
+ address: "https://www.mintscan.io/cosmos/address/",
4417
+ tx: "https://www.mintscan.io/cosmos/tx/"
4418
+ },
4419
+ "cosmos:osmosis-1": {
4420
+ address: "https://www.mintscan.io/osmosis/address/",
4421
+ tx: "https://www.mintscan.io/osmosis/tx/"
4422
+ },
4423
+ "cosmos:thorchain-mainnet-v1": {
4424
+ address: "https://thorchain.net/address/",
4425
+ tx: "https://thorchain.net/tx/"
4426
+ },
4427
+ "cosmos:mayachain-mainnet-v1": {
4428
+ address: "https://www.mayascan.org/address/",
4429
+ tx: "https://www.mayascan.org/tx/"
4430
+ },
4431
+ "ripple:4109c6f2045fc7eff4cde8f9905d19c2": {
4432
+ address: "https://xrpscan.com/account/",
4433
+ tx: "https://xrpscan.com/tx/"
4434
+ }
4435
+ };
4330
4436
  function isCacheDataValid(portfolioData) {
4331
4437
  if (!portfolioData.networks || !Array.isArray(portfolioData.networks)) {
4332
4438
  console.warn("[CACHE VALIDATION] Networks is not an array");
@@ -4465,14 +4571,37 @@ function enrichBalancesWithAssetInfo(balances, assetsMap, caipToNetworkId7) {
4465
4571
  console.warn(tag, `Missing AssetInfo for CAIP: "${balance.caip}" - skipping this balance`);
4466
4572
  continue;
4467
4573
  }
4574
+ if (typeof balance.valueUsd === "string") {
4575
+ balance.valueUsd = parseFloat(balance.valueUsd) || 0;
4576
+ } else if (typeof balance.valueUsd !== "number") {
4577
+ balance.valueUsd = 0;
4578
+ }
4579
+ if (typeof balance.balance === "string") {
4580
+ balance.balance = balance.balance;
4581
+ }
4582
+ if (typeof balance.priceUsd === "string") {
4583
+ balance.priceUsd = parseFloat(balance.priceUsd) || 0;
4584
+ } else if (typeof balance.priceUsd !== "number") {
4585
+ balance.priceUsd = 0;
4586
+ }
4587
+ const networkId = caipToNetworkId7(balance.caip);
4588
+ const explorerUrls = EXPLORER_BASE_URLS[networkId];
4589
+ const explorerAddressLink = explorerUrls && balance.pubkey ? explorerUrls.address + balance.pubkey : undefined;
4590
+ const explorerTxLink = explorerUrls?.tx;
4468
4591
  Object.assign(balance, assetInfo, {
4469
4592
  type: balance.type || assetInfo.type,
4470
4593
  isNative: balance.isNative ?? assetInfo.isNative,
4471
- networkId: caipToNetworkId7(balance.caip),
4594
+ networkId,
4472
4595
  icon: assetInfo.icon || "https://pioneers.dev/coins/etherum.png",
4473
4596
  identifier: `${balance.caip}:${balance.pubkey}`,
4474
4597
  updated: Date.now(),
4475
- color: assetInfo.color
4598
+ color: assetInfo.color,
4599
+ decimals: assetInfo.decimals || balance.decimals || 8,
4600
+ explorerAddressLink,
4601
+ explorerTxLink,
4602
+ explorer: explorerUrls?.address,
4603
+ valueUsd: balance.valueUsd,
4604
+ priceUsd: balance.priceUsd
4476
4605
  });
4477
4606
  enrichedBalances.push(balance);
4478
4607
  }
@@ -5007,6 +5136,20 @@ class SDK {
5007
5136
  this.balances[index] = balance;
5008
5137
  }
5009
5138
  this.events.emit("BALANCE_UPDATE", balance);
5139
+ console.log(tag7, "\uD83D\uDD04 Rebuilding dashboard after balance update...");
5140
+ const previousTotal = this.dashboard?.totalValueUsd || 0;
5141
+ this.dashboard = this.buildDashboardFromBalances();
5142
+ const newTotal = this.dashboard?.totalValueUsd || 0;
5143
+ if (Math.abs(newTotal - previousTotal) > 0.01) {
5144
+ console.log(tag7, `\uD83D\uDCB0 Portfolio value changed: $${previousTotal.toFixed(2)} → $${newTotal.toFixed(2)}`);
5145
+ }
5146
+ this.events.emit("DASHBOARD_UPDATE", {
5147
+ dashboard: this.dashboard,
5148
+ trigger: "balance_update",
5149
+ affectedAsset: balance.caip,
5150
+ previousTotal,
5151
+ newTotal
5152
+ });
5010
5153
  } catch (e) {
5011
5154
  console.error(tag7, "Error processing balance update:", e);
5012
5155
  }
@@ -5884,7 +6027,7 @@ class SDK {
5884
6027
  const pubkeysForNetwork = findPubkeysForNetwork(this.pubkeys, asset.networkId);
5885
6028
  console.log(tag6, `✅ Validated: Found ${pubkeysForNetwork.length} addresses for ${asset.networkId}`);
5886
6029
  const freshPriceUsd = await fetchMarketPrice(this.pioneer, asset.caip);
5887
- let assetInfo = resolveAssetInfo(this.assetsMap, assetData2, asset);
6030
+ let assetInfo = resolveAssetInfo(this.assetsMap, this.assets, asset);
5888
6031
  const matchingBalances = this.balances.filter((b2) => b2.caip === asset.caip);
5889
6032
  if (matchingBalances.length > 0) {
5890
6033
  const priceValue = extractPriceFromBalances(matchingBalances);
@@ -5900,14 +6043,47 @@ class SDK {
5900
6043
  assetInfo.balance = totalBalance.toString();
5901
6044
  assetInfo.valueUsd = totalValueUsd.toFixed(2);
5902
6045
  }
6046
+ const EXPLORER_BASE_URLS2 = {
6047
+ "eip155:1": { address: "https://etherscan.io/address/", tx: "https://etherscan.io/tx/" },
6048
+ "eip155:137": { address: "https://polygonscan.com/address/", tx: "https://polygonscan.com/tx/" },
6049
+ "eip155:8453": { address: "https://basescan.org/address/", tx: "https://basescan.org/tx/" },
6050
+ "eip155:56": { address: "https://bscscan.com/address/", tx: "https://bscscan.com/tx/" },
6051
+ "eip155:41454": { address: "https://explorer.monad.xyz/address/", tx: "https://explorer.monad.xyz/tx/" },
6052
+ "eip155:2868": { address: "https://app.hyperliquid.xyz/explorer/address/", tx: "https://app.hyperliquid.xyz/explorer/tx/" },
6053
+ "bip122:000000000019d6689c085ae165831e93": { address: "https://blockstream.info/address/", tx: "https://blockstream.info/tx/" },
6054
+ "bip122:12a765e31ffd4059bada1e25190f6e98": { address: "https://blockchair.com/litecoin/address/", tx: "https://blockchair.com/litecoin/transaction/" },
6055
+ "bip122:00000000001a91e3dace36e2be3bf030": { address: "https://dogechain.info/address/", tx: "https://dogechain.info/tx/" },
6056
+ "bip122:000000000000000000651ef99cb9fcbe": { address: "https://blockchair.com/bitcoin-cash/address/", tx: "https://blockchair.com/bitcoin-cash/transaction/" },
6057
+ "bip122:000007d91d1254d60e2dd1ae58038307": { address: "https://chainz.cryptoid.info/dash/address.dws?", tx: "https://chainz.cryptoid.info/dash/tx.dws?" },
6058
+ "bip122:4da631f2ac1bed857bd968c67c913978": { address: "https://digiexplorer.info/address/", tx: "https://digiexplorer.info/tx/" },
6059
+ "bip122:00040fe8ec8471911baa1db1266ea15d": { address: "https://explorer.zcha.in/accounts/", tx: "https://explorer.zcha.in/transactions/" },
6060
+ "cosmos:cosmoshub-4": { address: "https://www.mintscan.io/cosmos/address/", tx: "https://www.mintscan.io/cosmos/tx/" },
6061
+ "cosmos:osmosis-1": { address: "https://www.mintscan.io/osmosis/address/", tx: "https://www.mintscan.io/osmosis/tx/" },
6062
+ "cosmos:thorchain-mainnet-v1": { address: "https://thorchain.net/address/", tx: "https://thorchain.net/tx/" },
6063
+ "cosmos:mayachain-mainnet-v1": { address: "https://www.mayascan.org/address/", tx: "https://www.mayascan.org/tx/" },
6064
+ "ripple:4109c6f2045fc7eff4cde8f9905d19c2": { address: "https://xrpscan.com/account/", tx: "https://xrpscan.com/tx/" }
6065
+ };
6066
+ const networkId = caipToNetworkId7(asset.caip);
6067
+ const explorerUrls = EXPLORER_BASE_URLS2[networkId];
6068
+ const firstPubkey = pubkeysForNetwork && pubkeysForNetwork.length > 0 ? pubkeysForNetwork[0].address || pubkeysForNetwork[0].pubkey : null;
6069
+ if (explorerUrls && firstPubkey) {
6070
+ assetInfo.explorerAddressLink = explorerUrls.address + firstPubkey;
6071
+ assetInfo.explorerTxLink = explorerUrls.tx;
6072
+ assetInfo.explorer = explorerUrls.address;
6073
+ }
5903
6074
  const assetBalances = this.balances.filter((b2) => b2.caip === asset.caip);
5904
6075
  const assetPubkeys = filterPubkeysForAsset(this.pubkeys, asset.caip, caipToNetworkId7);
6076
+ const criticalFields = ["decimals", "precision", "explorerAddressLink", "explorerTxLink", "explorer"];
5905
6077
  const finalAssetContext = {
5906
6078
  ...assetInfo,
5907
- ...asset,
6079
+ ...Object.fromEntries(Object.entries(asset).filter(([k, v2]) => v2 !== undefined && !criticalFields.includes(k))),
5908
6080
  pubkeys: assetPubkeys,
5909
6081
  balances: assetBalances
5910
6082
  };
6083
+ if (assetInfo.decimals !== undefined) {
6084
+ finalAssetContext.decimals = assetInfo.decimals;
6085
+ finalAssetContext.precision = assetInfo.decimals;
6086
+ }
5911
6087
  if ((!asset.priceUsd || asset.priceUsd === 0) && assetInfo.priceUsd && assetInfo.priceUsd > 0) {
5912
6088
  finalAssetContext.priceUsd = assetInfo.priceUsd;
5913
6089
  }
@@ -5916,15 +6092,22 @@ class SDK {
5916
6092
  }
5917
6093
  this.assetContext = finalAssetContext;
5918
6094
  if (asset.isToken || asset.type === "token" || assetInfo.isToken || assetInfo.type === "token") {
5919
- const networkId = asset.networkId || assetInfo.networkId;
5920
- this.assetContext.nativeSymbol = nativeSymbol;
5921
- if (nativeCaip) {
6095
+ const networkId2 = asset.networkId || assetInfo.networkId;
6096
+ try {
6097
+ const nativeCaip = networkIdToCaip2(networkId2);
6098
+ const nativeAssetInfo = assetData2[nativeCaip];
6099
+ const nativeSymbol = nativeAssetInfo?.symbol || "GAS";
6100
+ this.assetContext.nativeSymbol = nativeSymbol;
5922
6101
  const nativeBalance = this.balances.find((b2) => b2.caip === nativeCaip);
5923
6102
  if (nativeBalance) {
5924
6103
  this.assetContext.nativeBalance = nativeBalance.balance || "0";
5925
6104
  } else {
5926
6105
  this.assetContext.nativeBalance = "0";
5927
6106
  }
6107
+ } catch (error) {
6108
+ console.error(`[Pioneer SDK] Unable to get native asset for networkId ${networkId2}:`, error);
6109
+ this.assetContext.nativeSymbol = "GAS";
6110
+ this.assetContext.nativeBalance = "0";
5928
6111
  }
5929
6112
  }
5930
6113
  if (asset.caip) {
@@ -5933,8 +6116,8 @@ class SDK {
5933
6116
  this.blockchainContext = asset.networkId;
5934
6117
  }
5935
6118
  if (assetPubkeys && assetPubkeys.length > 0) {
5936
- const networkId = caipToNetworkId7(asset.caip || asset.networkId);
5937
- const currentContextValid = this.pubkeyContext?.networks?.includes(networkId);
6119
+ const networkId2 = caipToNetworkId7(asset.caip || asset.networkId);
6120
+ const currentContextValid = this.pubkeyContext?.networks?.includes(networkId2);
5938
6121
  if (!this.pubkeyContext || !currentContextValid) {
5939
6122
  this.pubkeyContext = assetPubkeys[0];
5940
6123
  console.log(tag6, "Auto-set pubkey context for network:", this.pubkeyContext.address || this.pubkeyContext.pubkey);
@@ -5985,7 +6168,7 @@ class SDK {
5985
6168
  if (!pubkey)
5986
6169
  throw Error("Invalid network! missing pubkey for network! " + asset.networkId);
5987
6170
  const freshPriceUsd = await fetchMarketPrice(this.pioneer, asset.caip);
5988
- let assetInfo = resolveAssetInfo(this.assetsMap, assetData2, asset);
6171
+ let assetInfo = resolveAssetInfo(this.assetsMap, this.assets, asset);
5989
6172
  const matchingBalances = this.balances.filter((b2) => b2.caip === asset.caip);
5990
6173
  if (matchingBalances.length > 0) {
5991
6174
  const priceValue = extractPriceFromBalances(matchingBalances);
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "author": "highlander",
3
3
  "name": "@pioneer-platform/pioneer-sdk",
4
- "version": "8.15.31",
4
+ "version": "8.15.33",
5
5
  "dependencies": {
6
6
  "@keepkey/keepkey-sdk": "^0.2.62",
7
- "@pioneer-platform/pioneer-caip": "^9.10.8",
8
- "@pioneer-platform/pioneer-client": "^9.10.14",
9
- "@pioneer-platform/pioneer-coins": "^9.11.8",
10
- "@pioneer-platform/pioneer-discovery": "^8.15.31",
11
- "@pioneer-platform/pioneer-events": "^8.12.3",
7
+ "@pioneer-platform/pioneer-caip": "^9.10.10",
8
+ "@pioneer-platform/pioneer-client": "^9.10.16",
9
+ "@pioneer-platform/pioneer-coins": "^9.11.10",
10
+ "@pioneer-platform/pioneer-discovery": "^8.15.33",
11
+ "@pioneer-platform/pioneer-events": "^8.12.5",
12
12
  "coinselect": "^3.1.13",
13
13
  "eventemitter3": "^5.0.1",
14
14
  "neotraverse": "^0.6.8",
package/src/fees/index.ts CHANGED
@@ -48,6 +48,7 @@ function getNetworkName(networkId: string): string {
48
48
  'bip122:00000000001a91e3dace36e2be3bf030': 'Dogecoin',
49
49
  'bip122:000000000000000000651ef99cb9fcbe': 'Bitcoin Cash',
50
50
  'bip122:000007d91d1254d60e2dd1ae58038307': 'Dash',
51
+ 'bip122:00040fe8ec8471911baa1db1266ea15d': 'Zcash',
51
52
  'bip122:4da631f2ac1bed857bd968c67c913978': 'DigiByte',
52
53
  'eip155:1': 'Ethereum',
53
54
  'eip155:56': 'BNB Smart Chain',
package/src/index.ts CHANGED
@@ -552,6 +552,25 @@ export class SDK {
552
552
 
553
553
  // Emit SDK event for UI updates
554
554
  this.events.emit('BALANCE_UPDATE', balance);
555
+
556
+ // CRITICAL FIX: Rebuild dashboard when balance changes
557
+ console.log(tag, '🔄 Rebuilding dashboard after balance update...');
558
+ const previousTotal = this.dashboard?.totalValueUsd || 0;
559
+ this.dashboard = this.buildDashboardFromBalances();
560
+ const newTotal = this.dashboard?.totalValueUsd || 0;
561
+
562
+ if (Math.abs(newTotal - previousTotal) > 0.01) {
563
+ console.log(tag, `💰 Portfolio value changed: $${previousTotal.toFixed(2)} → $${newTotal.toFixed(2)}`);
564
+ }
565
+
566
+ // Emit dashboard update event for real-time UI updates
567
+ this.events.emit('DASHBOARD_UPDATE', {
568
+ dashboard: this.dashboard,
569
+ trigger: 'balance_update',
570
+ affectedAsset: balance.caip,
571
+ previousTotal,
572
+ newTotal
573
+ });
555
574
  } catch (e) {
556
575
  console.error(tag, 'Error processing balance update:', e);
557
576
  }
@@ -1813,7 +1832,7 @@ export class SDK {
1813
1832
  const freshPriceUsd = await fetchMarketPrice(this.pioneer, asset.caip);
1814
1833
 
1815
1834
  // Resolve asset info from multiple sources
1816
- let assetInfo = resolveAssetInfo(this.assetsMap, assetData, asset);
1835
+ let assetInfo = resolveAssetInfo(this.assetsMap, this.assets, asset);
1817
1836
 
1818
1837
  // Get matching balances
1819
1838
  const matchingBalances = this.balances.filter((b) => b.caip === asset.caip);
@@ -1837,18 +1856,63 @@ export class SDK {
1837
1856
  assetInfo.valueUsd = totalValueUsd.toFixed(2);
1838
1857
  }
1839
1858
 
1859
+ // CAIP-based explorer URL mapping
1860
+ const EXPLORER_BASE_URLS: Record<string, { address: string; tx: string }> = {
1861
+ 'eip155:1': { address: 'https://etherscan.io/address/', tx: 'https://etherscan.io/tx/' },
1862
+ 'eip155:137': { address: 'https://polygonscan.com/address/', tx: 'https://polygonscan.com/tx/' },
1863
+ 'eip155:8453': { address: 'https://basescan.org/address/', tx: 'https://basescan.org/tx/' },
1864
+ 'eip155:56': { address: 'https://bscscan.com/address/', tx: 'https://bscscan.com/tx/' },
1865
+ 'eip155:41454': { address: 'https://explorer.monad.xyz/address/', tx: 'https://explorer.monad.xyz/tx/' },
1866
+ 'eip155:2868': { address: 'https://app.hyperliquid.xyz/explorer/address/', tx: 'https://app.hyperliquid.xyz/explorer/tx/' },
1867
+ 'bip122:000000000019d6689c085ae165831e93': { address: 'https://blockstream.info/address/', tx: 'https://blockstream.info/tx/' },
1868
+ 'bip122:12a765e31ffd4059bada1e25190f6e98': { address: 'https://blockchair.com/litecoin/address/', tx: 'https://blockchair.com/litecoin/transaction/' },
1869
+ 'bip122:00000000001a91e3dace36e2be3bf030': { address: 'https://dogechain.info/address/', tx: 'https://dogechain.info/tx/' },
1870
+ 'bip122:000000000000000000651ef99cb9fcbe': { address: 'https://blockchair.com/bitcoin-cash/address/', tx: 'https://blockchair.com/bitcoin-cash/transaction/' },
1871
+ 'bip122:000007d91d1254d60e2dd1ae58038307': { address: 'https://chainz.cryptoid.info/dash/address.dws?', tx: 'https://chainz.cryptoid.info/dash/tx.dws?' },
1872
+ 'bip122:4da631f2ac1bed857bd968c67c913978': { address: 'https://digiexplorer.info/address/', tx: 'https://digiexplorer.info/tx/' },
1873
+ 'bip122:00040fe8ec8471911baa1db1266ea15d': { address: 'https://explorer.zcha.in/accounts/', tx: 'https://explorer.zcha.in/transactions/' },
1874
+ 'cosmos:cosmoshub-4': { address: 'https://www.mintscan.io/cosmos/address/', tx: 'https://www.mintscan.io/cosmos/tx/' },
1875
+ 'cosmos:osmosis-1': { address: 'https://www.mintscan.io/osmosis/address/', tx: 'https://www.mintscan.io/osmosis/tx/' },
1876
+ 'cosmos:thorchain-mainnet-v1': { address: 'https://thorchain.net/address/', tx: 'https://thorchain.net/tx/' },
1877
+ 'cosmos:mayachain-mainnet-v1': { address: 'https://www.mayascan.org/address/', tx: 'https://www.mayascan.org/tx/' },
1878
+ 'ripple:4109c6f2045fc7eff4cde8f9905d19c2': { address: 'https://xrpscan.com/account/', tx: 'https://xrpscan.com/tx/' },
1879
+ };
1880
+
1881
+ // Generate explorer links for this asset
1882
+ const networkId = caipToNetworkId(asset.caip);
1883
+ const explorerUrls = EXPLORER_BASE_URLS[networkId];
1884
+
1885
+ // Get the first pubkey for this asset to generate explorer link
1886
+ const firstPubkey = pubkeysForNetwork && pubkeysForNetwork.length > 0
1887
+ ? (pubkeysForNetwork[0].address || pubkeysForNetwork[0].pubkey)
1888
+ : null;
1889
+
1890
+ if (explorerUrls && firstPubkey) {
1891
+ assetInfo.explorerAddressLink = explorerUrls.address + firstPubkey;
1892
+ assetInfo.explorerTxLink = explorerUrls.tx;
1893
+ assetInfo.explorer = explorerUrls.address;
1894
+ }
1895
+
1840
1896
  // Filter balances and pubkeys for this asset
1841
1897
  const assetBalances = this.balances.filter((b) => b.caip === asset.caip);
1842
1898
  const assetPubkeys = filterPubkeysForAsset(this.pubkeys, asset.caip, caipToNetworkId);
1843
1899
 
1844
1900
  // Combine the user-provided asset with any additional info we have
1901
+ // CRITICAL: Preserve decimals, precision, and explorer links from assetInfo
1902
+ const criticalFields = ['decimals', 'precision', 'explorerAddressLink', 'explorerTxLink', 'explorer'];
1845
1903
  const finalAssetContext = {
1846
1904
  ...assetInfo,
1847
- ...asset,
1905
+ ...Object.fromEntries(Object.entries(asset).filter(([k, v]) => v !== undefined && !criticalFields.includes(k))),
1848
1906
  pubkeys: assetPubkeys,
1849
1907
  balances: assetBalances,
1850
1908
  };
1851
1909
 
1910
+ // Ensure precision matches decimals from assetInfo (for backward compatibility)
1911
+ if (assetInfo.decimals !== undefined) {
1912
+ finalAssetContext.decimals = assetInfo.decimals;
1913
+ finalAssetContext.precision = assetInfo.decimals;
1914
+ }
1915
+
1852
1916
  // If input has priceUsd of 0 but we found a valid price, use the found price
1853
1917
  if ((!asset.priceUsd || asset.priceUsd === 0) && assetInfo.priceUsd && assetInfo.priceUsd > 0) {
1854
1918
  finalAssetContext.priceUsd = assetInfo.priceUsd;
@@ -1868,19 +1932,32 @@ export class SDK {
1868
1932
  assetInfo.isToken ||
1869
1933
  assetInfo.type === 'token'
1870
1934
  ) {
1871
- // Get the native asset for this network
1935
+ // Get the native asset for this network using the proper CAIP utility
1872
1936
  const networkId = asset.networkId || assetInfo.networkId;
1873
- // Set the native symbol
1874
- this.assetContext.nativeSymbol = nativeSymbol;
1875
1937
 
1876
- // Try to find the native balance
1877
- if (nativeCaip) {
1938
+ try {
1939
+ // Use networkIdToCaip to get the native asset CAIP
1940
+ const nativeCaip = networkIdToCaip(networkId);
1941
+
1942
+ // Look up the native asset info from assetData
1943
+ const nativeAssetInfo = assetData[nativeCaip];
1944
+ const nativeSymbol = nativeAssetInfo?.symbol || 'GAS';
1945
+
1946
+ // Set the native symbol
1947
+ this.assetContext.nativeSymbol = nativeSymbol;
1948
+
1949
+ // Try to find the native balance
1878
1950
  const nativeBalance = this.balances.find((b) => b.caip === nativeCaip);
1879
1951
  if (nativeBalance) {
1880
1952
  this.assetContext.nativeBalance = nativeBalance.balance || '0';
1881
1953
  } else {
1882
1954
  this.assetContext.nativeBalance = '0';
1883
1955
  }
1956
+ } catch (error) {
1957
+ console.error(`[Pioneer SDK] Unable to get native asset for networkId ${networkId}:`, error);
1958
+ // Fallback to 'GAS' if we can't determine the native asset
1959
+ this.assetContext.nativeSymbol = 'GAS';
1960
+ this.assetContext.nativeBalance = '0';
1884
1961
  }
1885
1962
  }
1886
1963
 
@@ -1988,7 +2065,7 @@ export class SDK {
1988
2065
  const freshPriceUsd = await fetchMarketPrice(this.pioneer, asset.caip);
1989
2066
 
1990
2067
  // Resolve asset info from multiple sources
1991
- let assetInfo = resolveAssetInfo(this.assetsMap, assetData, asset);
2068
+ let assetInfo = resolveAssetInfo(this.assetsMap, this.assets, asset);
1992
2069
 
1993
2070
  // Get matching balances
1994
2071
  const matchingBalances = this.balances.filter((b) => b.caip === asset.caip);
@@ -287,9 +287,26 @@ export async function createUnsignedUxtoTx(
287
287
  }
288
288
  } catch (error: any) {
289
289
  console.error(`${tag}: Failed to get fee rates from Pioneer API:`, error.message || error);
290
- throw new Error(
291
- `Unable to get fee rate for network ${networkId}: ${error.message || 'API unavailable'}`,
292
- );
290
+
291
+ // Fallback to hardcoded default fees for known networks
292
+ const defaultFees: Record<string, { slow: number; average: number; fast: number; fastest: number }> = {
293
+ 'bip122:000000000019d6689c085ae165831e93': { slow: 3, average: 5, fast: 10, fastest: 15 }, // BTC
294
+ 'bip122:12a765e31ffd4059bada1e25190f6e98': { slow: 2, average: 3, fast: 5, fastest: 10 }, // LTC
295
+ 'bip122:00000000001a91e3dace36e2be3bf030': { slow: 1, average: 1, fast: 2, fastest: 3 }, // DOGE
296
+ 'bip122:000000000000000000651ef99cb9fcbe': { slow: 1, average: 1, fast: 2, fastest: 3 }, // BCH
297
+ 'bip122:000007d91d1254d60e2dd1ae58038307': { slow: 1, average: 1, fast: 2, fastest: 3 }, // DASH
298
+ 'bip122:4da631f2ac1bed857bd968c67c913978': { slow: 75, average: 80, fast: 100, fastest: 120 }, // DGB
299
+ 'bip122:00040fe8ec8471911baa1db1266ea15d': { slow: 1, average: 1, fast: 2, fastest: 3 }, // ZEC
300
+ };
301
+
302
+ if (defaultFees[networkId]) {
303
+ console.warn(`${tag}: Using default fee rates for ${networkId}`);
304
+ feeRateFromNode = defaultFees[networkId];
305
+ } else {
306
+ throw new Error(
307
+ `Unable to get fee rate for network ${networkId}: ${error.message || 'API unavailable'}`,
308
+ );
309
+ }
293
310
  }
294
311
  }
295
312