@pioneer-platform/pioneer-sdk 8.15.32 → 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.cjs CHANGED
@@ -4175,6 +4175,80 @@ function filterPubkeysForAsset(pubkeys, caip, caipToNetworkId7) {
4175
4175
 
4176
4176
  // src/utils/portfolio-helpers.ts
4177
4177
  var TAG9 = " | portfolio-helpers | ";
4178
+ var EXPLORER_BASE_URLS = {
4179
+ "eip155:1": {
4180
+ address: "https://etherscan.io/address/",
4181
+ tx: "https://etherscan.io/tx/"
4182
+ },
4183
+ "eip155:137": {
4184
+ address: "https://polygonscan.com/address/",
4185
+ tx: "https://polygonscan.com/tx/"
4186
+ },
4187
+ "eip155:8453": {
4188
+ address: "https://basescan.org/address/",
4189
+ tx: "https://basescan.org/tx/"
4190
+ },
4191
+ "eip155:56": {
4192
+ address: "https://bscscan.com/address/",
4193
+ tx: "https://bscscan.com/tx/"
4194
+ },
4195
+ "eip155:41454": {
4196
+ address: "https://explorer.monad.xyz/address/",
4197
+ tx: "https://explorer.monad.xyz/tx/"
4198
+ },
4199
+ "eip155:2868": {
4200
+ address: "https://app.hyperliquid.xyz/explorer/address/",
4201
+ tx: "https://app.hyperliquid.xyz/explorer/tx/"
4202
+ },
4203
+ "bip122:000000000019d6689c085ae165831e93": {
4204
+ address: "https://blockstream.info/address/",
4205
+ tx: "https://blockstream.info/tx/"
4206
+ },
4207
+ "bip122:12a765e31ffd4059bada1e25190f6e98": {
4208
+ address: "https://blockchair.com/litecoin/address/",
4209
+ tx: "https://blockchair.com/litecoin/transaction/"
4210
+ },
4211
+ "bip122:00000000001a91e3dace36e2be3bf030": {
4212
+ address: "https://dogechain.info/address/",
4213
+ tx: "https://dogechain.info/tx/"
4214
+ },
4215
+ "bip122:000000000000000000651ef99cb9fcbe": {
4216
+ address: "https://blockchair.com/bitcoin-cash/address/",
4217
+ tx: "https://blockchair.com/bitcoin-cash/transaction/"
4218
+ },
4219
+ "bip122:000007d91d1254d60e2dd1ae58038307": {
4220
+ address: "https://chainz.cryptoid.info/dash/address.dws?",
4221
+ tx: "https://chainz.cryptoid.info/dash/tx.dws?"
4222
+ },
4223
+ "bip122:4da631f2ac1bed857bd968c67c913978": {
4224
+ address: "https://digiexplorer.info/address/",
4225
+ tx: "https://digiexplorer.info/tx/"
4226
+ },
4227
+ "bip122:00040fe8ec8471911baa1db1266ea15d": {
4228
+ address: "https://explorer.zcha.in/accounts/",
4229
+ tx: "https://explorer.zcha.in/transactions/"
4230
+ },
4231
+ "cosmos:cosmoshub-4": {
4232
+ address: "https://www.mintscan.io/cosmos/address/",
4233
+ tx: "https://www.mintscan.io/cosmos/tx/"
4234
+ },
4235
+ "cosmos:osmosis-1": {
4236
+ address: "https://www.mintscan.io/osmosis/address/",
4237
+ tx: "https://www.mintscan.io/osmosis/tx/"
4238
+ },
4239
+ "cosmos:thorchain-mainnet-v1": {
4240
+ address: "https://thorchain.net/address/",
4241
+ tx: "https://thorchain.net/tx/"
4242
+ },
4243
+ "cosmos:mayachain-mainnet-v1": {
4244
+ address: "https://www.mayascan.org/address/",
4245
+ tx: "https://www.mayascan.org/tx/"
4246
+ },
4247
+ "ripple:4109c6f2045fc7eff4cde8f9905d19c2": {
4248
+ address: "https://xrpscan.com/account/",
4249
+ tx: "https://xrpscan.com/tx/"
4250
+ }
4251
+ };
4178
4252
  function isCacheDataValid(portfolioData) {
4179
4253
  if (!portfolioData.networks || !Array.isArray(portfolioData.networks)) {
4180
4254
  console.warn("[CACHE VALIDATION] Networks is not an array");
@@ -4326,14 +4400,22 @@ function enrichBalancesWithAssetInfo(balances, assetsMap, caipToNetworkId7) {
4326
4400
  } else if (typeof balance.priceUsd !== "number") {
4327
4401
  balance.priceUsd = 0;
4328
4402
  }
4403
+ const networkId = caipToNetworkId7(balance.caip);
4404
+ const explorerUrls = EXPLORER_BASE_URLS[networkId];
4405
+ const explorerAddressLink = explorerUrls && balance.pubkey ? explorerUrls.address + balance.pubkey : undefined;
4406
+ const explorerTxLink = explorerUrls?.tx;
4329
4407
  Object.assign(balance, assetInfo, {
4330
4408
  type: balance.type || assetInfo.type,
4331
4409
  isNative: balance.isNative ?? assetInfo.isNative,
4332
- networkId: caipToNetworkId7(balance.caip),
4410
+ networkId,
4333
4411
  icon: assetInfo.icon || "https://pioneers.dev/coins/etherum.png",
4334
4412
  identifier: `${balance.caip}:${balance.pubkey}`,
4335
4413
  updated: Date.now(),
4336
4414
  color: assetInfo.color,
4415
+ decimals: assetInfo.decimals || balance.decimals || 8,
4416
+ explorerAddressLink,
4417
+ explorerTxLink,
4418
+ explorer: explorerUrls?.address,
4337
4419
  valueUsd: balance.valueUsd,
4338
4420
  priceUsd: balance.priceUsd
4339
4421
  });
@@ -5761,7 +5843,7 @@ class SDK {
5761
5843
  const pubkeysForNetwork = findPubkeysForNetwork(this.pubkeys, asset.networkId);
5762
5844
  console.log(tag6, `✅ Validated: Found ${pubkeysForNetwork.length} addresses for ${asset.networkId}`);
5763
5845
  const freshPriceUsd = await fetchMarketPrice(this.pioneer, asset.caip);
5764
- let assetInfo = resolveAssetInfo(this.assetsMap, import_pioneer_discovery2.assetData, asset);
5846
+ let assetInfo = resolveAssetInfo(this.assetsMap, this.assets, asset);
5765
5847
  const matchingBalances = this.balances.filter((b) => b.caip === asset.caip);
5766
5848
  if (matchingBalances.length > 0) {
5767
5849
  const priceValue = extractPriceFromBalances(matchingBalances);
@@ -5777,14 +5859,47 @@ class SDK {
5777
5859
  assetInfo.balance = totalBalance.toString();
5778
5860
  assetInfo.valueUsd = totalValueUsd.toFixed(2);
5779
5861
  }
5862
+ const EXPLORER_BASE_URLS2 = {
5863
+ "eip155:1": { address: "https://etherscan.io/address/", tx: "https://etherscan.io/tx/" },
5864
+ "eip155:137": { address: "https://polygonscan.com/address/", tx: "https://polygonscan.com/tx/" },
5865
+ "eip155:8453": { address: "https://basescan.org/address/", tx: "https://basescan.org/tx/" },
5866
+ "eip155:56": { address: "https://bscscan.com/address/", tx: "https://bscscan.com/tx/" },
5867
+ "eip155:41454": { address: "https://explorer.monad.xyz/address/", tx: "https://explorer.monad.xyz/tx/" },
5868
+ "eip155:2868": { address: "https://app.hyperliquid.xyz/explorer/address/", tx: "https://app.hyperliquid.xyz/explorer/tx/" },
5869
+ "bip122:000000000019d6689c085ae165831e93": { address: "https://blockstream.info/address/", tx: "https://blockstream.info/tx/" },
5870
+ "bip122:12a765e31ffd4059bada1e25190f6e98": { address: "https://blockchair.com/litecoin/address/", tx: "https://blockchair.com/litecoin/transaction/" },
5871
+ "bip122:00000000001a91e3dace36e2be3bf030": { address: "https://dogechain.info/address/", tx: "https://dogechain.info/tx/" },
5872
+ "bip122:000000000000000000651ef99cb9fcbe": { address: "https://blockchair.com/bitcoin-cash/address/", tx: "https://blockchair.com/bitcoin-cash/transaction/" },
5873
+ "bip122:000007d91d1254d60e2dd1ae58038307": { address: "https://chainz.cryptoid.info/dash/address.dws?", tx: "https://chainz.cryptoid.info/dash/tx.dws?" },
5874
+ "bip122:4da631f2ac1bed857bd968c67c913978": { address: "https://digiexplorer.info/address/", tx: "https://digiexplorer.info/tx/" },
5875
+ "bip122:00040fe8ec8471911baa1db1266ea15d": { address: "https://explorer.zcha.in/accounts/", tx: "https://explorer.zcha.in/transactions/" },
5876
+ "cosmos:cosmoshub-4": { address: "https://www.mintscan.io/cosmos/address/", tx: "https://www.mintscan.io/cosmos/tx/" },
5877
+ "cosmos:osmosis-1": { address: "https://www.mintscan.io/osmosis/address/", tx: "https://www.mintscan.io/osmosis/tx/" },
5878
+ "cosmos:thorchain-mainnet-v1": { address: "https://thorchain.net/address/", tx: "https://thorchain.net/tx/" },
5879
+ "cosmos:mayachain-mainnet-v1": { address: "https://www.mayascan.org/address/", tx: "https://www.mayascan.org/tx/" },
5880
+ "ripple:4109c6f2045fc7eff4cde8f9905d19c2": { address: "https://xrpscan.com/account/", tx: "https://xrpscan.com/tx/" }
5881
+ };
5882
+ const networkId = import_pioneer_caip8.caipToNetworkId(asset.caip);
5883
+ const explorerUrls = EXPLORER_BASE_URLS2[networkId];
5884
+ const firstPubkey = pubkeysForNetwork && pubkeysForNetwork.length > 0 ? pubkeysForNetwork[0].address || pubkeysForNetwork[0].pubkey : null;
5885
+ if (explorerUrls && firstPubkey) {
5886
+ assetInfo.explorerAddressLink = explorerUrls.address + firstPubkey;
5887
+ assetInfo.explorerTxLink = explorerUrls.tx;
5888
+ assetInfo.explorer = explorerUrls.address;
5889
+ }
5780
5890
  const assetBalances = this.balances.filter((b) => b.caip === asset.caip);
5781
5891
  const assetPubkeys = filterPubkeysForAsset(this.pubkeys, asset.caip, import_pioneer_caip8.caipToNetworkId);
5892
+ const criticalFields = ["decimals", "precision", "explorerAddressLink", "explorerTxLink", "explorer"];
5782
5893
  const finalAssetContext = {
5783
5894
  ...assetInfo,
5784
- ...asset,
5895
+ ...Object.fromEntries(Object.entries(asset).filter(([k, v]) => v !== undefined && !criticalFields.includes(k))),
5785
5896
  pubkeys: assetPubkeys,
5786
5897
  balances: assetBalances
5787
5898
  };
5899
+ if (assetInfo.decimals !== undefined) {
5900
+ finalAssetContext.decimals = assetInfo.decimals;
5901
+ finalAssetContext.precision = assetInfo.decimals;
5902
+ }
5788
5903
  if ((!asset.priceUsd || asset.priceUsd === 0) && assetInfo.priceUsd && assetInfo.priceUsd > 0) {
5789
5904
  finalAssetContext.priceUsd = assetInfo.priceUsd;
5790
5905
  }
@@ -5793,15 +5908,22 @@ class SDK {
5793
5908
  }
5794
5909
  this.assetContext = finalAssetContext;
5795
5910
  if (asset.isToken || asset.type === "token" || assetInfo.isToken || assetInfo.type === "token") {
5796
- const networkId = asset.networkId || assetInfo.networkId;
5797
- this.assetContext.nativeSymbol = nativeSymbol;
5798
- if (nativeCaip) {
5911
+ const networkId2 = asset.networkId || assetInfo.networkId;
5912
+ try {
5913
+ const nativeCaip = import_pioneer_caip8.networkIdToCaip(networkId2);
5914
+ const nativeAssetInfo = import_pioneer_discovery2.assetData[nativeCaip];
5915
+ const nativeSymbol = nativeAssetInfo?.symbol || "GAS";
5916
+ this.assetContext.nativeSymbol = nativeSymbol;
5799
5917
  const nativeBalance = this.balances.find((b) => b.caip === nativeCaip);
5800
5918
  if (nativeBalance) {
5801
5919
  this.assetContext.nativeBalance = nativeBalance.balance || "0";
5802
5920
  } else {
5803
5921
  this.assetContext.nativeBalance = "0";
5804
5922
  }
5923
+ } catch (error) {
5924
+ console.error(`[Pioneer SDK] Unable to get native asset for networkId ${networkId2}:`, error);
5925
+ this.assetContext.nativeSymbol = "GAS";
5926
+ this.assetContext.nativeBalance = "0";
5805
5927
  }
5806
5928
  }
5807
5929
  if (asset.caip) {
@@ -5810,8 +5932,8 @@ class SDK {
5810
5932
  this.blockchainContext = asset.networkId;
5811
5933
  }
5812
5934
  if (assetPubkeys && assetPubkeys.length > 0) {
5813
- const networkId = import_pioneer_caip8.caipToNetworkId(asset.caip || asset.networkId);
5814
- const currentContextValid = this.pubkeyContext?.networks?.includes(networkId);
5935
+ const networkId2 = import_pioneer_caip8.caipToNetworkId(asset.caip || asset.networkId);
5936
+ const currentContextValid = this.pubkeyContext?.networks?.includes(networkId2);
5815
5937
  if (!this.pubkeyContext || !currentContextValid) {
5816
5938
  this.pubkeyContext = assetPubkeys[0];
5817
5939
  console.log(tag6, "Auto-set pubkey context for network:", this.pubkeyContext.address || this.pubkeyContext.pubkey);
@@ -5862,7 +5984,7 @@ class SDK {
5862
5984
  if (!pubkey)
5863
5985
  throw Error("Invalid network! missing pubkey for network! " + asset.networkId);
5864
5986
  const freshPriceUsd = await fetchMarketPrice(this.pioneer, asset.caip);
5865
- let assetInfo = resolveAssetInfo(this.assetsMap, import_pioneer_discovery2.assetData, asset);
5987
+ let assetInfo = resolveAssetInfo(this.assetsMap, this.assets, asset);
5866
5988
  const matchingBalances = this.balances.filter((b) => b.caip === asset.caip);
5867
5989
  if (matchingBalances.length > 0) {
5868
5990
  const priceValue = extractPriceFromBalances(matchingBalances);
package/dist/index.es.js CHANGED
@@ -4359,6 +4359,80 @@ function filterPubkeysForAsset(pubkeys, caip, caipToNetworkId7) {
4359
4359
 
4360
4360
  // src/utils/portfolio-helpers.ts
4361
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
+ };
4362
4436
  function isCacheDataValid(portfolioData) {
4363
4437
  if (!portfolioData.networks || !Array.isArray(portfolioData.networks)) {
4364
4438
  console.warn("[CACHE VALIDATION] Networks is not an array");
@@ -4510,14 +4584,22 @@ function enrichBalancesWithAssetInfo(balances, assetsMap, caipToNetworkId7) {
4510
4584
  } else if (typeof balance.priceUsd !== "number") {
4511
4585
  balance.priceUsd = 0;
4512
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;
4513
4591
  Object.assign(balance, assetInfo, {
4514
4592
  type: balance.type || assetInfo.type,
4515
4593
  isNative: balance.isNative ?? assetInfo.isNative,
4516
- networkId: caipToNetworkId7(balance.caip),
4594
+ networkId,
4517
4595
  icon: assetInfo.icon || "https://pioneers.dev/coins/etherum.png",
4518
4596
  identifier: `${balance.caip}:${balance.pubkey}`,
4519
4597
  updated: Date.now(),
4520
4598
  color: assetInfo.color,
4599
+ decimals: assetInfo.decimals || balance.decimals || 8,
4600
+ explorerAddressLink,
4601
+ explorerTxLink,
4602
+ explorer: explorerUrls?.address,
4521
4603
  valueUsd: balance.valueUsd,
4522
4604
  priceUsd: balance.priceUsd
4523
4605
  });
@@ -5945,7 +6027,7 @@ class SDK {
5945
6027
  const pubkeysForNetwork = findPubkeysForNetwork(this.pubkeys, asset.networkId);
5946
6028
  console.log(tag6, `✅ Validated: Found ${pubkeysForNetwork.length} addresses for ${asset.networkId}`);
5947
6029
  const freshPriceUsd = await fetchMarketPrice(this.pioneer, asset.caip);
5948
- let assetInfo = resolveAssetInfo(this.assetsMap, assetData2, asset);
6030
+ let assetInfo = resolveAssetInfo(this.assetsMap, this.assets, asset);
5949
6031
  const matchingBalances = this.balances.filter((b2) => b2.caip === asset.caip);
5950
6032
  if (matchingBalances.length > 0) {
5951
6033
  const priceValue = extractPriceFromBalances(matchingBalances);
@@ -5961,14 +6043,47 @@ class SDK {
5961
6043
  assetInfo.balance = totalBalance.toString();
5962
6044
  assetInfo.valueUsd = totalValueUsd.toFixed(2);
5963
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
+ }
5964
6074
  const assetBalances = this.balances.filter((b2) => b2.caip === asset.caip);
5965
6075
  const assetPubkeys = filterPubkeysForAsset(this.pubkeys, asset.caip, caipToNetworkId7);
6076
+ const criticalFields = ["decimals", "precision", "explorerAddressLink", "explorerTxLink", "explorer"];
5966
6077
  const finalAssetContext = {
5967
6078
  ...assetInfo,
5968
- ...asset,
6079
+ ...Object.fromEntries(Object.entries(asset).filter(([k, v2]) => v2 !== undefined && !criticalFields.includes(k))),
5969
6080
  pubkeys: assetPubkeys,
5970
6081
  balances: assetBalances
5971
6082
  };
6083
+ if (assetInfo.decimals !== undefined) {
6084
+ finalAssetContext.decimals = assetInfo.decimals;
6085
+ finalAssetContext.precision = assetInfo.decimals;
6086
+ }
5972
6087
  if ((!asset.priceUsd || asset.priceUsd === 0) && assetInfo.priceUsd && assetInfo.priceUsd > 0) {
5973
6088
  finalAssetContext.priceUsd = assetInfo.priceUsd;
5974
6089
  }
@@ -5977,15 +6092,22 @@ class SDK {
5977
6092
  }
5978
6093
  this.assetContext = finalAssetContext;
5979
6094
  if (asset.isToken || asset.type === "token" || assetInfo.isToken || assetInfo.type === "token") {
5980
- const networkId = asset.networkId || assetInfo.networkId;
5981
- this.assetContext.nativeSymbol = nativeSymbol;
5982
- 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;
5983
6101
  const nativeBalance = this.balances.find((b2) => b2.caip === nativeCaip);
5984
6102
  if (nativeBalance) {
5985
6103
  this.assetContext.nativeBalance = nativeBalance.balance || "0";
5986
6104
  } else {
5987
6105
  this.assetContext.nativeBalance = "0";
5988
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";
5989
6111
  }
5990
6112
  }
5991
6113
  if (asset.caip) {
@@ -5994,8 +6116,8 @@ class SDK {
5994
6116
  this.blockchainContext = asset.networkId;
5995
6117
  }
5996
6118
  if (assetPubkeys && assetPubkeys.length > 0) {
5997
- const networkId = caipToNetworkId7(asset.caip || asset.networkId);
5998
- const currentContextValid = this.pubkeyContext?.networks?.includes(networkId);
6119
+ const networkId2 = caipToNetworkId7(asset.caip || asset.networkId);
6120
+ const currentContextValid = this.pubkeyContext?.networks?.includes(networkId2);
5999
6121
  if (!this.pubkeyContext || !currentContextValid) {
6000
6122
  this.pubkeyContext = assetPubkeys[0];
6001
6123
  console.log(tag6, "Auto-set pubkey context for network:", this.pubkeyContext.address || this.pubkeyContext.pubkey);
@@ -6046,7 +6168,7 @@ class SDK {
6046
6168
  if (!pubkey)
6047
6169
  throw Error("Invalid network! missing pubkey for network! " + asset.networkId);
6048
6170
  const freshPriceUsd = await fetchMarketPrice(this.pioneer, asset.caip);
6049
- let assetInfo = resolveAssetInfo(this.assetsMap, assetData2, asset);
6171
+ let assetInfo = resolveAssetInfo(this.assetsMap, this.assets, asset);
6050
6172
  const matchingBalances = this.balances.filter((b2) => b2.caip === asset.caip);
6051
6173
  if (matchingBalances.length > 0) {
6052
6174
  const priceValue = extractPriceFromBalances(matchingBalances);
package/dist/index.js CHANGED
@@ -4359,6 +4359,80 @@ function filterPubkeysForAsset(pubkeys, caip, caipToNetworkId7) {
4359
4359
 
4360
4360
  // src/utils/portfolio-helpers.ts
4361
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
+ };
4362
4436
  function isCacheDataValid(portfolioData) {
4363
4437
  if (!portfolioData.networks || !Array.isArray(portfolioData.networks)) {
4364
4438
  console.warn("[CACHE VALIDATION] Networks is not an array");
@@ -4510,14 +4584,22 @@ function enrichBalancesWithAssetInfo(balances, assetsMap, caipToNetworkId7) {
4510
4584
  } else if (typeof balance.priceUsd !== "number") {
4511
4585
  balance.priceUsd = 0;
4512
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;
4513
4591
  Object.assign(balance, assetInfo, {
4514
4592
  type: balance.type || assetInfo.type,
4515
4593
  isNative: balance.isNative ?? assetInfo.isNative,
4516
- networkId: caipToNetworkId7(balance.caip),
4594
+ networkId,
4517
4595
  icon: assetInfo.icon || "https://pioneers.dev/coins/etherum.png",
4518
4596
  identifier: `${balance.caip}:${balance.pubkey}`,
4519
4597
  updated: Date.now(),
4520
4598
  color: assetInfo.color,
4599
+ decimals: assetInfo.decimals || balance.decimals || 8,
4600
+ explorerAddressLink,
4601
+ explorerTxLink,
4602
+ explorer: explorerUrls?.address,
4521
4603
  valueUsd: balance.valueUsd,
4522
4604
  priceUsd: balance.priceUsd
4523
4605
  });
@@ -5945,7 +6027,7 @@ class SDK {
5945
6027
  const pubkeysForNetwork = findPubkeysForNetwork(this.pubkeys, asset.networkId);
5946
6028
  console.log(tag6, `✅ Validated: Found ${pubkeysForNetwork.length} addresses for ${asset.networkId}`);
5947
6029
  const freshPriceUsd = await fetchMarketPrice(this.pioneer, asset.caip);
5948
- let assetInfo = resolveAssetInfo(this.assetsMap, assetData2, asset);
6030
+ let assetInfo = resolveAssetInfo(this.assetsMap, this.assets, asset);
5949
6031
  const matchingBalances = this.balances.filter((b2) => b2.caip === asset.caip);
5950
6032
  if (matchingBalances.length > 0) {
5951
6033
  const priceValue = extractPriceFromBalances(matchingBalances);
@@ -5961,14 +6043,47 @@ class SDK {
5961
6043
  assetInfo.balance = totalBalance.toString();
5962
6044
  assetInfo.valueUsd = totalValueUsd.toFixed(2);
5963
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
+ }
5964
6074
  const assetBalances = this.balances.filter((b2) => b2.caip === asset.caip);
5965
6075
  const assetPubkeys = filterPubkeysForAsset(this.pubkeys, asset.caip, caipToNetworkId7);
6076
+ const criticalFields = ["decimals", "precision", "explorerAddressLink", "explorerTxLink", "explorer"];
5966
6077
  const finalAssetContext = {
5967
6078
  ...assetInfo,
5968
- ...asset,
6079
+ ...Object.fromEntries(Object.entries(asset).filter(([k, v2]) => v2 !== undefined && !criticalFields.includes(k))),
5969
6080
  pubkeys: assetPubkeys,
5970
6081
  balances: assetBalances
5971
6082
  };
6083
+ if (assetInfo.decimals !== undefined) {
6084
+ finalAssetContext.decimals = assetInfo.decimals;
6085
+ finalAssetContext.precision = assetInfo.decimals;
6086
+ }
5972
6087
  if ((!asset.priceUsd || asset.priceUsd === 0) && assetInfo.priceUsd && assetInfo.priceUsd > 0) {
5973
6088
  finalAssetContext.priceUsd = assetInfo.priceUsd;
5974
6089
  }
@@ -5977,15 +6092,22 @@ class SDK {
5977
6092
  }
5978
6093
  this.assetContext = finalAssetContext;
5979
6094
  if (asset.isToken || asset.type === "token" || assetInfo.isToken || assetInfo.type === "token") {
5980
- const networkId = asset.networkId || assetInfo.networkId;
5981
- this.assetContext.nativeSymbol = nativeSymbol;
5982
- 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;
5983
6101
  const nativeBalance = this.balances.find((b2) => b2.caip === nativeCaip);
5984
6102
  if (nativeBalance) {
5985
6103
  this.assetContext.nativeBalance = nativeBalance.balance || "0";
5986
6104
  } else {
5987
6105
  this.assetContext.nativeBalance = "0";
5988
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";
5989
6111
  }
5990
6112
  }
5991
6113
  if (asset.caip) {
@@ -5994,8 +6116,8 @@ class SDK {
5994
6116
  this.blockchainContext = asset.networkId;
5995
6117
  }
5996
6118
  if (assetPubkeys && assetPubkeys.length > 0) {
5997
- const networkId = caipToNetworkId7(asset.caip || asset.networkId);
5998
- const currentContextValid = this.pubkeyContext?.networks?.includes(networkId);
6119
+ const networkId2 = caipToNetworkId7(asset.caip || asset.networkId);
6120
+ const currentContextValid = this.pubkeyContext?.networks?.includes(networkId2);
5999
6121
  if (!this.pubkeyContext || !currentContextValid) {
6000
6122
  this.pubkeyContext = assetPubkeys[0];
6001
6123
  console.log(tag6, "Auto-set pubkey context for network:", this.pubkeyContext.address || this.pubkeyContext.pubkey);
@@ -6046,7 +6168,7 @@ class SDK {
6046
6168
  if (!pubkey)
6047
6169
  throw Error("Invalid network! missing pubkey for network! " + asset.networkId);
6048
6170
  const freshPriceUsd = await fetchMarketPrice(this.pioneer, asset.caip);
6049
- let assetInfo = resolveAssetInfo(this.assetsMap, assetData2, asset);
6171
+ let assetInfo = resolveAssetInfo(this.assetsMap, this.assets, asset);
6050
6172
  const matchingBalances = this.balances.filter((b2) => b2.caip === asset.caip);
6051
6173
  if (matchingBalances.length > 0) {
6052
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.32",
4
+ "version": "8.15.33",
5
5
  "dependencies": {
6
6
  "@keepkey/keepkey-sdk": "^0.2.62",
7
- "@pioneer-platform/pioneer-caip": "^9.10.9",
8
- "@pioneer-platform/pioneer-client": "^9.10.15",
9
- "@pioneer-platform/pioneer-coins": "^9.11.9",
10
- "@pioneer-platform/pioneer-discovery": "^8.15.32",
11
- "@pioneer-platform/pioneer-events": "^8.12.4",
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/index.ts CHANGED
@@ -1832,7 +1832,7 @@ export class SDK {
1832
1832
  const freshPriceUsd = await fetchMarketPrice(this.pioneer, asset.caip);
1833
1833
 
1834
1834
  // Resolve asset info from multiple sources
1835
- let assetInfo = resolveAssetInfo(this.assetsMap, assetData, asset);
1835
+ let assetInfo = resolveAssetInfo(this.assetsMap, this.assets, asset);
1836
1836
 
1837
1837
  // Get matching balances
1838
1838
  const matchingBalances = this.balances.filter((b) => b.caip === asset.caip);
@@ -1856,18 +1856,63 @@ export class SDK {
1856
1856
  assetInfo.valueUsd = totalValueUsd.toFixed(2);
1857
1857
  }
1858
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
+
1859
1896
  // Filter balances and pubkeys for this asset
1860
1897
  const assetBalances = this.balances.filter((b) => b.caip === asset.caip);
1861
1898
  const assetPubkeys = filterPubkeysForAsset(this.pubkeys, asset.caip, caipToNetworkId);
1862
1899
 
1863
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'];
1864
1903
  const finalAssetContext = {
1865
1904
  ...assetInfo,
1866
- ...asset,
1905
+ ...Object.fromEntries(Object.entries(asset).filter(([k, v]) => v !== undefined && !criticalFields.includes(k))),
1867
1906
  pubkeys: assetPubkeys,
1868
1907
  balances: assetBalances,
1869
1908
  };
1870
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
+
1871
1916
  // If input has priceUsd of 0 but we found a valid price, use the found price
1872
1917
  if ((!asset.priceUsd || asset.priceUsd === 0) && assetInfo.priceUsd && assetInfo.priceUsd > 0) {
1873
1918
  finalAssetContext.priceUsd = assetInfo.priceUsd;
@@ -1887,19 +1932,32 @@ export class SDK {
1887
1932
  assetInfo.isToken ||
1888
1933
  assetInfo.type === 'token'
1889
1934
  ) {
1890
- // Get the native asset for this network
1935
+ // Get the native asset for this network using the proper CAIP utility
1891
1936
  const networkId = asset.networkId || assetInfo.networkId;
1892
- // Set the native symbol
1893
- this.assetContext.nativeSymbol = nativeSymbol;
1894
1937
 
1895
- // Try to find the native balance
1896
- 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
1897
1950
  const nativeBalance = this.balances.find((b) => b.caip === nativeCaip);
1898
1951
  if (nativeBalance) {
1899
1952
  this.assetContext.nativeBalance = nativeBalance.balance || '0';
1900
1953
  } else {
1901
1954
  this.assetContext.nativeBalance = '0';
1902
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';
1903
1961
  }
1904
1962
  }
1905
1963
 
@@ -2007,7 +2065,7 @@ export class SDK {
2007
2065
  const freshPriceUsd = await fetchMarketPrice(this.pioneer, asset.caip);
2008
2066
 
2009
2067
  // Resolve asset info from multiple sources
2010
- let assetInfo = resolveAssetInfo(this.assetsMap, assetData, asset);
2068
+ let assetInfo = resolveAssetInfo(this.assetsMap, this.assets, asset);
2011
2069
 
2012
2070
  // Get matching balances
2013
2071
  const matchingBalances = this.balances.filter((b) => b.caip === asset.caip);
@@ -3,6 +3,100 @@
3
3
  import { logger as log } from './logger.js';
4
4
  const TAG = ' | portfolio-helpers | ';
5
5
 
6
+ // CAIP-based explorer URL mapping
7
+ const EXPLORER_BASE_URLS: Record<string, { address: string; tx: string }> = {
8
+ // Ethereum mainnet
9
+ 'eip155:1': {
10
+ address: 'https://etherscan.io/address/',
11
+ tx: 'https://etherscan.io/tx/'
12
+ },
13
+ // Polygon
14
+ 'eip155:137': {
15
+ address: 'https://polygonscan.com/address/',
16
+ tx: 'https://polygonscan.com/tx/'
17
+ },
18
+ // Base
19
+ 'eip155:8453': {
20
+ address: 'https://basescan.org/address/',
21
+ tx: 'https://basescan.org/tx/'
22
+ },
23
+ // BSC
24
+ 'eip155:56': {
25
+ address: 'https://bscscan.com/address/',
26
+ tx: 'https://bscscan.com/tx/'
27
+ },
28
+ // Monad
29
+ 'eip155:41454': {
30
+ address: 'https://explorer.monad.xyz/address/',
31
+ tx: 'https://explorer.monad.xyz/tx/'
32
+ },
33
+ // Hyperliquid
34
+ 'eip155:2868': {
35
+ address: 'https://app.hyperliquid.xyz/explorer/address/',
36
+ tx: 'https://app.hyperliquid.xyz/explorer/tx/'
37
+ },
38
+ // Bitcoin
39
+ 'bip122:000000000019d6689c085ae165831e93': {
40
+ address: 'https://blockstream.info/address/',
41
+ tx: 'https://blockstream.info/tx/'
42
+ },
43
+ // Litecoin
44
+ 'bip122:12a765e31ffd4059bada1e25190f6e98': {
45
+ address: 'https://blockchair.com/litecoin/address/',
46
+ tx: 'https://blockchair.com/litecoin/transaction/'
47
+ },
48
+ // Dogecoin
49
+ 'bip122:00000000001a91e3dace36e2be3bf030': {
50
+ address: 'https://dogechain.info/address/',
51
+ tx: 'https://dogechain.info/tx/'
52
+ },
53
+ // Bitcoin Cash
54
+ 'bip122:000000000000000000651ef99cb9fcbe': {
55
+ address: 'https://blockchair.com/bitcoin-cash/address/',
56
+ tx: 'https://blockchair.com/bitcoin-cash/transaction/'
57
+ },
58
+ // Dash
59
+ 'bip122:000007d91d1254d60e2dd1ae58038307': {
60
+ address: 'https://chainz.cryptoid.info/dash/address.dws?',
61
+ tx: 'https://chainz.cryptoid.info/dash/tx.dws?'
62
+ },
63
+ // DigiByte
64
+ 'bip122:4da631f2ac1bed857bd968c67c913978': {
65
+ address: 'https://digiexplorer.info/address/',
66
+ tx: 'https://digiexplorer.info/tx/'
67
+ },
68
+ // Zcash
69
+ 'bip122:00040fe8ec8471911baa1db1266ea15d': {
70
+ address: 'https://explorer.zcha.in/accounts/',
71
+ tx: 'https://explorer.zcha.in/transactions/'
72
+ },
73
+ // Cosmos Hub
74
+ 'cosmos:cosmoshub-4': {
75
+ address: 'https://www.mintscan.io/cosmos/address/',
76
+ tx: 'https://www.mintscan.io/cosmos/tx/'
77
+ },
78
+ // Osmosis
79
+ 'cosmos:osmosis-1': {
80
+ address: 'https://www.mintscan.io/osmosis/address/',
81
+ tx: 'https://www.mintscan.io/osmosis/tx/'
82
+ },
83
+ // THORChain
84
+ 'cosmos:thorchain-mainnet-v1': {
85
+ address: 'https://thorchain.net/address/',
86
+ tx: 'https://thorchain.net/tx/'
87
+ },
88
+ // Maya Protocol
89
+ 'cosmos:mayachain-mainnet-v1': {
90
+ address: 'https://www.mayascan.org/address/',
91
+ tx: 'https://www.mayascan.org/tx/'
92
+ },
93
+ // Ripple
94
+ 'ripple:4109c6f2045fc7eff4cde8f9905d19c2': {
95
+ address: 'https://xrpscan.com/account/',
96
+ tx: 'https://xrpscan.com/tx/'
97
+ }
98
+ };
99
+
6
100
  export function isCacheDataValid(portfolioData: any): boolean {
7
101
  // Check if networks data is reasonable (should be < 50 networks, not thousands)
8
102
  if (!portfolioData.networks || !Array.isArray(portfolioData.networks)) {
@@ -240,14 +334,31 @@ export function enrichBalancesWithAssetInfo(
240
334
  balance.priceUsd = 0;
241
335
  }
242
336
 
337
+ // Get networkId for explorer lookup
338
+ const networkId = caipToNetworkId(balance.caip);
339
+ const explorerUrls = EXPLORER_BASE_URLS[networkId];
340
+
341
+ // Generate explorer links if we have a pubkey/address and explorer URLs for this network
342
+ const explorerAddressLink = (explorerUrls && balance.pubkey)
343
+ ? explorerUrls.address + balance.pubkey
344
+ : undefined;
345
+
346
+ const explorerTxLink = explorerUrls?.tx; // Base URL for txs, actual txid added later
347
+
243
348
  Object.assign(balance, assetInfo, {
244
349
  type: balance.type || assetInfo.type,
245
350
  isNative: balance.isNative ?? assetInfo.isNative,
246
- networkId: caipToNetworkId(balance.caip),
351
+ networkId,
247
352
  icon: assetInfo.icon || 'https://pioneers.dev/coins/etherum.png',
248
353
  identifier: `${balance.caip}:${balance.pubkey}`,
249
354
  updated: Date.now(),
250
355
  color: assetInfo.color,
356
+ // CRITICAL: Always use decimals from assetInfo if available
357
+ decimals: assetInfo.decimals || balance.decimals || 8,
358
+ // Add explorer links
359
+ explorerAddressLink,
360
+ explorerTxLink,
361
+ explorer: explorerUrls?.address, // Base explorer URL
251
362
  // Ensure these are numbers (redundant but explicit)
252
363
  valueUsd: balance.valueUsd,
253
364
  priceUsd: balance.priceUsd,