@pioneer-platform/pioneer-sdk 8.15.32 → 8.15.34
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 +178 -12
- package/dist/index.es.js +178 -12
- package/dist/index.js +178 -12
- package/package.json +6 -6
- package/src/charts/evm.ts +46 -3
- package/src/charts/maya.ts +24 -2
- package/src/index.ts +66 -8
- package/src/utils/portfolio-helpers.ts +124 -1
package/dist/index.cjs
CHANGED
|
@@ -436,6 +436,17 @@ function processPortfolioBalance(balance, primaryAddress, context, blockchains)
|
|
|
436
436
|
console.log(tag2, `Calculated price from value/balance: ${calculatedPrice} for ${balance.caip}`);
|
|
437
437
|
}
|
|
438
438
|
}
|
|
439
|
+
const decimals = assetInfo?.decimals ?? assetInfo?.decimal ?? balance.decimals ?? balance.decimal;
|
|
440
|
+
if (decimals === undefined || decimals === null) {
|
|
441
|
+
throw new Error(`CRITICAL: Asset ${balance.caip} (${assetInfo?.symbol || balance.symbol || "UNKNOWN"}) has NO decimals/precision! ` + `This would cause incorrect balance calculations. ` + `Asset data: ${JSON.stringify({
|
|
442
|
+
caip: balance.caip,
|
|
443
|
+
symbol: assetInfo?.symbol || balance.symbol,
|
|
444
|
+
hasAssetInfoDecimals: !!assetInfo?.decimals,
|
|
445
|
+
hasAssetInfoDecimal: !!assetInfo?.decimal,
|
|
446
|
+
hasBalanceDecimals: !!balance.decimals,
|
|
447
|
+
hasBalanceDecimal: !!balance.decimal
|
|
448
|
+
})}`);
|
|
449
|
+
}
|
|
439
450
|
const chartBalance = {
|
|
440
451
|
context,
|
|
441
452
|
chart: "pioneer",
|
|
@@ -452,7 +463,9 @@ function processPortfolioBalance(balance, primaryAddress, context, blockchains)
|
|
|
452
463
|
symbol: assetInfo?.symbol || balance.symbol || "UNK",
|
|
453
464
|
type: balanceType,
|
|
454
465
|
token: isToken,
|
|
455
|
-
decimal:
|
|
466
|
+
decimal: decimals,
|
|
467
|
+
decimals,
|
|
468
|
+
precision: decimals,
|
|
456
469
|
balance: balance.balance.toString(),
|
|
457
470
|
price: calculatedPrice,
|
|
458
471
|
priceUsd: calculatedPrice,
|
|
@@ -483,6 +496,17 @@ function processPortfolioToken(token, primaryAddress, context, blockchains) {
|
|
|
483
496
|
}
|
|
484
497
|
const tokenAssetInfo = hydrateAssetData(token.assetCaip);
|
|
485
498
|
const tokenPubkey = token.pubkey || primaryAddress;
|
|
499
|
+
const tokenDecimals = tokenAssetInfo?.decimals ?? tokenAssetInfo?.decimal ?? token.token?.decimals ?? token.token?.decimal;
|
|
500
|
+
if (tokenDecimals === undefined || tokenDecimals === null) {
|
|
501
|
+
throw new Error(`CRITICAL: Token ${token.assetCaip} (${tokenAssetInfo?.symbol || token.token?.symbol || "UNKNOWN"}) has NO decimals/precision! ` + `This would cause incorrect balance calculations. ` + `Token data: ${JSON.stringify({
|
|
502
|
+
caip: token.assetCaip,
|
|
503
|
+
symbol: tokenAssetInfo?.symbol || token.token?.symbol,
|
|
504
|
+
hasAssetInfoDecimals: !!tokenAssetInfo?.decimals,
|
|
505
|
+
hasAssetInfoDecimal: !!tokenAssetInfo?.decimal,
|
|
506
|
+
hasTokenDecimals: !!token.token?.decimals,
|
|
507
|
+
hasTokenDecimal: !!token.token?.decimal
|
|
508
|
+
})}`);
|
|
509
|
+
}
|
|
486
510
|
const chartBalance = {
|
|
487
511
|
context,
|
|
488
512
|
chart: "pioneer",
|
|
@@ -498,7 +522,9 @@ function processPortfolioToken(token, primaryAddress, context, blockchains) {
|
|
|
498
522
|
symbol: tokenAssetInfo?.symbol || token.token?.symbol || "UNK",
|
|
499
523
|
type: tokenAssetInfo?.type || "token",
|
|
500
524
|
token: true,
|
|
501
|
-
decimal:
|
|
525
|
+
decimal: tokenDecimals,
|
|
526
|
+
decimals: tokenDecimals,
|
|
527
|
+
precision: tokenDecimals,
|
|
502
528
|
balance: token.token?.balance?.toString() || "0",
|
|
503
529
|
priceUsd: token.token?.price || 0,
|
|
504
530
|
valueUsd: token.token?.balanceUSD || 0,
|
|
@@ -605,6 +631,17 @@ async function getMayaCharts(params, existingBalances) {
|
|
|
605
631
|
}
|
|
606
632
|
const mayaAssetInfo = hydrateAssetData(mayaBalance.caip);
|
|
607
633
|
const isToken = mayaBalance.caip.includes("/denom:") && !mayaBalance.caip.endsWith("/denom:cacao");
|
|
634
|
+
const decimals = mayaAssetInfo?.decimals ?? mayaAssetInfo?.decimal ?? mayaBalance.decimals ?? mayaBalance.decimal;
|
|
635
|
+
if (decimals === undefined || decimals === null) {
|
|
636
|
+
throw new Error(`CRITICAL: Asset ${mayaBalance.caip} (${mayaAssetInfo?.symbol || "MAYA"}) has NO decimals/precision! ` + `This would cause incorrect balance calculations. ` + `Asset data: ${JSON.stringify({
|
|
637
|
+
caip: mayaBalance.caip,
|
|
638
|
+
symbol: mayaAssetInfo?.symbol,
|
|
639
|
+
hasAssetInfoDecimals: !!mayaAssetInfo?.decimals,
|
|
640
|
+
hasAssetInfoDecimal: !!mayaAssetInfo?.decimal,
|
|
641
|
+
hasBalanceDecimals: !!mayaBalance.decimals,
|
|
642
|
+
hasBalanceDecimal: !!mayaBalance.decimal
|
|
643
|
+
})}`);
|
|
644
|
+
}
|
|
608
645
|
const mayaTokenBalance = {
|
|
609
646
|
context,
|
|
610
647
|
chart: "pioneer",
|
|
@@ -620,7 +657,9 @@ async function getMayaCharts(params, existingBalances) {
|
|
|
620
657
|
symbol: mayaAssetInfo?.symbol || "MAYA",
|
|
621
658
|
type: mayaAssetInfo?.type || "token",
|
|
622
659
|
token: isToken,
|
|
623
|
-
decimal:
|
|
660
|
+
decimal: decimals,
|
|
661
|
+
decimals,
|
|
662
|
+
precision: decimals,
|
|
624
663
|
balance: mayaBalance.balance?.toString() || "0",
|
|
625
664
|
priceUsd: parseFloat(mayaBalance.priceUsd) || 0,
|
|
626
665
|
valueUsd: parseFloat(mayaBalance.valueUsd) || 0,
|
|
@@ -4175,6 +4214,80 @@ function filterPubkeysForAsset(pubkeys, caip, caipToNetworkId7) {
|
|
|
4175
4214
|
|
|
4176
4215
|
// src/utils/portfolio-helpers.ts
|
|
4177
4216
|
var TAG9 = " | portfolio-helpers | ";
|
|
4217
|
+
var EXPLORER_BASE_URLS = {
|
|
4218
|
+
"eip155:1": {
|
|
4219
|
+
address: "https://etherscan.io/address/",
|
|
4220
|
+
tx: "https://etherscan.io/tx/"
|
|
4221
|
+
},
|
|
4222
|
+
"eip155:137": {
|
|
4223
|
+
address: "https://polygonscan.com/address/",
|
|
4224
|
+
tx: "https://polygonscan.com/tx/"
|
|
4225
|
+
},
|
|
4226
|
+
"eip155:8453": {
|
|
4227
|
+
address: "https://basescan.org/address/",
|
|
4228
|
+
tx: "https://basescan.org/tx/"
|
|
4229
|
+
},
|
|
4230
|
+
"eip155:56": {
|
|
4231
|
+
address: "https://bscscan.com/address/",
|
|
4232
|
+
tx: "https://bscscan.com/tx/"
|
|
4233
|
+
},
|
|
4234
|
+
"eip155:41454": {
|
|
4235
|
+
address: "https://explorer.monad.xyz/address/",
|
|
4236
|
+
tx: "https://explorer.monad.xyz/tx/"
|
|
4237
|
+
},
|
|
4238
|
+
"eip155:2868": {
|
|
4239
|
+
address: "https://app.hyperliquid.xyz/explorer/address/",
|
|
4240
|
+
tx: "https://app.hyperliquid.xyz/explorer/tx/"
|
|
4241
|
+
},
|
|
4242
|
+
"bip122:000000000019d6689c085ae165831e93": {
|
|
4243
|
+
address: "https://blockstream.info/address/",
|
|
4244
|
+
tx: "https://blockstream.info/tx/"
|
|
4245
|
+
},
|
|
4246
|
+
"bip122:12a765e31ffd4059bada1e25190f6e98": {
|
|
4247
|
+
address: "https://blockchair.com/litecoin/address/",
|
|
4248
|
+
tx: "https://blockchair.com/litecoin/transaction/"
|
|
4249
|
+
},
|
|
4250
|
+
"bip122:00000000001a91e3dace36e2be3bf030": {
|
|
4251
|
+
address: "https://dogechain.info/address/",
|
|
4252
|
+
tx: "https://dogechain.info/tx/"
|
|
4253
|
+
},
|
|
4254
|
+
"bip122:000000000000000000651ef99cb9fcbe": {
|
|
4255
|
+
address: "https://blockchair.com/bitcoin-cash/address/",
|
|
4256
|
+
tx: "https://blockchair.com/bitcoin-cash/transaction/"
|
|
4257
|
+
},
|
|
4258
|
+
"bip122:000007d91d1254d60e2dd1ae58038307": {
|
|
4259
|
+
address: "https://chainz.cryptoid.info/dash/address.dws?",
|
|
4260
|
+
tx: "https://chainz.cryptoid.info/dash/tx.dws?"
|
|
4261
|
+
},
|
|
4262
|
+
"bip122:4da631f2ac1bed857bd968c67c913978": {
|
|
4263
|
+
address: "https://digiexplorer.info/address/",
|
|
4264
|
+
tx: "https://digiexplorer.info/tx/"
|
|
4265
|
+
},
|
|
4266
|
+
"bip122:00040fe8ec8471911baa1db1266ea15d": {
|
|
4267
|
+
address: "https://explorer.zcha.in/accounts/",
|
|
4268
|
+
tx: "https://explorer.zcha.in/transactions/"
|
|
4269
|
+
},
|
|
4270
|
+
"cosmos:cosmoshub-4": {
|
|
4271
|
+
address: "https://www.mintscan.io/cosmos/address/",
|
|
4272
|
+
tx: "https://www.mintscan.io/cosmos/tx/"
|
|
4273
|
+
},
|
|
4274
|
+
"cosmos:osmosis-1": {
|
|
4275
|
+
address: "https://www.mintscan.io/osmosis/address/",
|
|
4276
|
+
tx: "https://www.mintscan.io/osmosis/tx/"
|
|
4277
|
+
},
|
|
4278
|
+
"cosmos:thorchain-mainnet-v1": {
|
|
4279
|
+
address: "https://thorchain.net/address/",
|
|
4280
|
+
tx: "https://thorchain.net/tx/"
|
|
4281
|
+
},
|
|
4282
|
+
"cosmos:mayachain-mainnet-v1": {
|
|
4283
|
+
address: "https://www.mayascan.org/address/",
|
|
4284
|
+
tx: "https://www.mayascan.org/tx/"
|
|
4285
|
+
},
|
|
4286
|
+
"ripple:4109c6f2045fc7eff4cde8f9905d19c2": {
|
|
4287
|
+
address: "https://xrpscan.com/account/",
|
|
4288
|
+
tx: "https://xrpscan.com/tx/"
|
|
4289
|
+
}
|
|
4290
|
+
};
|
|
4178
4291
|
function isCacheDataValid(portfolioData) {
|
|
4179
4292
|
if (!portfolioData.networks || !Array.isArray(portfolioData.networks)) {
|
|
4180
4293
|
console.warn("[CACHE VALIDATION] Networks is not an array");
|
|
@@ -4326,14 +4439,27 @@ function enrichBalancesWithAssetInfo(balances, assetsMap, caipToNetworkId7) {
|
|
|
4326
4439
|
} else if (typeof balance.priceUsd !== "number") {
|
|
4327
4440
|
balance.priceUsd = 0;
|
|
4328
4441
|
}
|
|
4442
|
+
const networkId = caipToNetworkId7(balance.caip);
|
|
4443
|
+
const explorerUrls = EXPLORER_BASE_URLS[networkId];
|
|
4444
|
+
const explorerAddressLink = explorerUrls && balance.pubkey ? explorerUrls.address + balance.pubkey : undefined;
|
|
4445
|
+
const explorerTxLink = explorerUrls?.tx;
|
|
4446
|
+
const decimals = assetInfo.decimals ?? balance.decimals;
|
|
4447
|
+
if (decimals === undefined || decimals === null) {
|
|
4448
|
+
throw new Error(`CRITICAL: Asset ${balance.caip} (${assetInfo.symbol || "UNKNOWN"}) has NO decimals/precision! ` + `AssetInfo is incomplete. This would cause incorrect balance calculations. ` + `Asset data: ${JSON.stringify({ caip: balance.caip, symbol: assetInfo.symbol, hasDecimals: !!assetInfo.decimals })}`);
|
|
4449
|
+
}
|
|
4329
4450
|
Object.assign(balance, assetInfo, {
|
|
4330
4451
|
type: balance.type || assetInfo.type,
|
|
4331
4452
|
isNative: balance.isNative ?? assetInfo.isNative,
|
|
4332
|
-
networkId
|
|
4453
|
+
networkId,
|
|
4333
4454
|
icon: assetInfo.icon || "https://pioneers.dev/coins/etherum.png",
|
|
4334
4455
|
identifier: `${balance.caip}:${balance.pubkey}`,
|
|
4335
4456
|
updated: Date.now(),
|
|
4336
4457
|
color: assetInfo.color,
|
|
4458
|
+
decimals,
|
|
4459
|
+
precision: decimals,
|
|
4460
|
+
explorerAddressLink,
|
|
4461
|
+
explorerTxLink,
|
|
4462
|
+
explorer: explorerUrls?.address,
|
|
4337
4463
|
valueUsd: balance.valueUsd,
|
|
4338
4464
|
priceUsd: balance.priceUsd
|
|
4339
4465
|
});
|
|
@@ -5761,7 +5887,7 @@ class SDK {
|
|
|
5761
5887
|
const pubkeysForNetwork = findPubkeysForNetwork(this.pubkeys, asset.networkId);
|
|
5762
5888
|
console.log(tag6, `✅ Validated: Found ${pubkeysForNetwork.length} addresses for ${asset.networkId}`);
|
|
5763
5889
|
const freshPriceUsd = await fetchMarketPrice(this.pioneer, asset.caip);
|
|
5764
|
-
let assetInfo = resolveAssetInfo(this.assetsMap,
|
|
5890
|
+
let assetInfo = resolveAssetInfo(this.assetsMap, this.assets, asset);
|
|
5765
5891
|
const matchingBalances = this.balances.filter((b) => b.caip === asset.caip);
|
|
5766
5892
|
if (matchingBalances.length > 0) {
|
|
5767
5893
|
const priceValue = extractPriceFromBalances(matchingBalances);
|
|
@@ -5777,14 +5903,47 @@ class SDK {
|
|
|
5777
5903
|
assetInfo.balance = totalBalance.toString();
|
|
5778
5904
|
assetInfo.valueUsd = totalValueUsd.toFixed(2);
|
|
5779
5905
|
}
|
|
5906
|
+
const EXPLORER_BASE_URLS2 = {
|
|
5907
|
+
"eip155:1": { address: "https://etherscan.io/address/", tx: "https://etherscan.io/tx/" },
|
|
5908
|
+
"eip155:137": { address: "https://polygonscan.com/address/", tx: "https://polygonscan.com/tx/" },
|
|
5909
|
+
"eip155:8453": { address: "https://basescan.org/address/", tx: "https://basescan.org/tx/" },
|
|
5910
|
+
"eip155:56": { address: "https://bscscan.com/address/", tx: "https://bscscan.com/tx/" },
|
|
5911
|
+
"eip155:41454": { address: "https://explorer.monad.xyz/address/", tx: "https://explorer.monad.xyz/tx/" },
|
|
5912
|
+
"eip155:2868": { address: "https://app.hyperliquid.xyz/explorer/address/", tx: "https://app.hyperliquid.xyz/explorer/tx/" },
|
|
5913
|
+
"bip122:000000000019d6689c085ae165831e93": { address: "https://blockstream.info/address/", tx: "https://blockstream.info/tx/" },
|
|
5914
|
+
"bip122:12a765e31ffd4059bada1e25190f6e98": { address: "https://blockchair.com/litecoin/address/", tx: "https://blockchair.com/litecoin/transaction/" },
|
|
5915
|
+
"bip122:00000000001a91e3dace36e2be3bf030": { address: "https://dogechain.info/address/", tx: "https://dogechain.info/tx/" },
|
|
5916
|
+
"bip122:000000000000000000651ef99cb9fcbe": { address: "https://blockchair.com/bitcoin-cash/address/", tx: "https://blockchair.com/bitcoin-cash/transaction/" },
|
|
5917
|
+
"bip122:000007d91d1254d60e2dd1ae58038307": { address: "https://chainz.cryptoid.info/dash/address.dws?", tx: "https://chainz.cryptoid.info/dash/tx.dws?" },
|
|
5918
|
+
"bip122:4da631f2ac1bed857bd968c67c913978": { address: "https://digiexplorer.info/address/", tx: "https://digiexplorer.info/tx/" },
|
|
5919
|
+
"bip122:00040fe8ec8471911baa1db1266ea15d": { address: "https://explorer.zcha.in/accounts/", tx: "https://explorer.zcha.in/transactions/" },
|
|
5920
|
+
"cosmos:cosmoshub-4": { address: "https://www.mintscan.io/cosmos/address/", tx: "https://www.mintscan.io/cosmos/tx/" },
|
|
5921
|
+
"cosmos:osmosis-1": { address: "https://www.mintscan.io/osmosis/address/", tx: "https://www.mintscan.io/osmosis/tx/" },
|
|
5922
|
+
"cosmos:thorchain-mainnet-v1": { address: "https://thorchain.net/address/", tx: "https://thorchain.net/tx/" },
|
|
5923
|
+
"cosmos:mayachain-mainnet-v1": { address: "https://www.mayascan.org/address/", tx: "https://www.mayascan.org/tx/" },
|
|
5924
|
+
"ripple:4109c6f2045fc7eff4cde8f9905d19c2": { address: "https://xrpscan.com/account/", tx: "https://xrpscan.com/tx/" }
|
|
5925
|
+
};
|
|
5926
|
+
const networkId = import_pioneer_caip8.caipToNetworkId(asset.caip);
|
|
5927
|
+
const explorerUrls = EXPLORER_BASE_URLS2[networkId];
|
|
5928
|
+
const firstPubkey = pubkeysForNetwork && pubkeysForNetwork.length > 0 ? pubkeysForNetwork[0].address || pubkeysForNetwork[0].pubkey : null;
|
|
5929
|
+
if (explorerUrls && firstPubkey) {
|
|
5930
|
+
assetInfo.explorerAddressLink = explorerUrls.address + firstPubkey;
|
|
5931
|
+
assetInfo.explorerTxLink = explorerUrls.tx;
|
|
5932
|
+
assetInfo.explorer = explorerUrls.address;
|
|
5933
|
+
}
|
|
5780
5934
|
const assetBalances = this.balances.filter((b) => b.caip === asset.caip);
|
|
5781
5935
|
const assetPubkeys = filterPubkeysForAsset(this.pubkeys, asset.caip, import_pioneer_caip8.caipToNetworkId);
|
|
5936
|
+
const criticalFields = ["decimals", "precision", "explorerAddressLink", "explorerTxLink", "explorer"];
|
|
5782
5937
|
const finalAssetContext = {
|
|
5783
5938
|
...assetInfo,
|
|
5784
|
-
...asset,
|
|
5939
|
+
...Object.fromEntries(Object.entries(asset).filter(([k, v]) => v !== undefined && !criticalFields.includes(k))),
|
|
5785
5940
|
pubkeys: assetPubkeys,
|
|
5786
5941
|
balances: assetBalances
|
|
5787
5942
|
};
|
|
5943
|
+
if (assetInfo.decimals !== undefined) {
|
|
5944
|
+
finalAssetContext.decimals = assetInfo.decimals;
|
|
5945
|
+
finalAssetContext.precision = assetInfo.decimals;
|
|
5946
|
+
}
|
|
5788
5947
|
if ((!asset.priceUsd || asset.priceUsd === 0) && assetInfo.priceUsd && assetInfo.priceUsd > 0) {
|
|
5789
5948
|
finalAssetContext.priceUsd = assetInfo.priceUsd;
|
|
5790
5949
|
}
|
|
@@ -5793,15 +5952,22 @@ class SDK {
|
|
|
5793
5952
|
}
|
|
5794
5953
|
this.assetContext = finalAssetContext;
|
|
5795
5954
|
if (asset.isToken || asset.type === "token" || assetInfo.isToken || assetInfo.type === "token") {
|
|
5796
|
-
const
|
|
5797
|
-
|
|
5798
|
-
|
|
5955
|
+
const networkId2 = asset.networkId || assetInfo.networkId;
|
|
5956
|
+
try {
|
|
5957
|
+
const nativeCaip = import_pioneer_caip8.networkIdToCaip(networkId2);
|
|
5958
|
+
const nativeAssetInfo = import_pioneer_discovery2.assetData[nativeCaip];
|
|
5959
|
+
const nativeSymbol = nativeAssetInfo?.symbol || "GAS";
|
|
5960
|
+
this.assetContext.nativeSymbol = nativeSymbol;
|
|
5799
5961
|
const nativeBalance = this.balances.find((b) => b.caip === nativeCaip);
|
|
5800
5962
|
if (nativeBalance) {
|
|
5801
5963
|
this.assetContext.nativeBalance = nativeBalance.balance || "0";
|
|
5802
5964
|
} else {
|
|
5803
5965
|
this.assetContext.nativeBalance = "0";
|
|
5804
5966
|
}
|
|
5967
|
+
} catch (error) {
|
|
5968
|
+
console.error(`[Pioneer SDK] Unable to get native asset for networkId ${networkId2}:`, error);
|
|
5969
|
+
this.assetContext.nativeSymbol = "GAS";
|
|
5970
|
+
this.assetContext.nativeBalance = "0";
|
|
5805
5971
|
}
|
|
5806
5972
|
}
|
|
5807
5973
|
if (asset.caip) {
|
|
@@ -5810,8 +5976,8 @@ class SDK {
|
|
|
5810
5976
|
this.blockchainContext = asset.networkId;
|
|
5811
5977
|
}
|
|
5812
5978
|
if (assetPubkeys && assetPubkeys.length > 0) {
|
|
5813
|
-
const
|
|
5814
|
-
const currentContextValid = this.pubkeyContext?.networks?.includes(
|
|
5979
|
+
const networkId2 = import_pioneer_caip8.caipToNetworkId(asset.caip || asset.networkId);
|
|
5980
|
+
const currentContextValid = this.pubkeyContext?.networks?.includes(networkId2);
|
|
5815
5981
|
if (!this.pubkeyContext || !currentContextValid) {
|
|
5816
5982
|
this.pubkeyContext = assetPubkeys[0];
|
|
5817
5983
|
console.log(tag6, "Auto-set pubkey context for network:", this.pubkeyContext.address || this.pubkeyContext.pubkey);
|
|
@@ -5862,7 +6028,7 @@ class SDK {
|
|
|
5862
6028
|
if (!pubkey)
|
|
5863
6029
|
throw Error("Invalid network! missing pubkey for network! " + asset.networkId);
|
|
5864
6030
|
const freshPriceUsd = await fetchMarketPrice(this.pioneer, asset.caip);
|
|
5865
|
-
let assetInfo = resolveAssetInfo(this.assetsMap,
|
|
6031
|
+
let assetInfo = resolveAssetInfo(this.assetsMap, this.assets, asset);
|
|
5866
6032
|
const matchingBalances = this.balances.filter((b) => b.caip === asset.caip);
|
|
5867
6033
|
if (matchingBalances.length > 0) {
|
|
5868
6034
|
const priceValue = extractPriceFromBalances(matchingBalances);
|
package/dist/index.es.js
CHANGED
|
@@ -427,6 +427,17 @@ function processPortfolioBalance(balance, primaryAddress, context, blockchains)
|
|
|
427
427
|
console.log(tag2, `Calculated price from value/balance: ${calculatedPrice} for ${balance.caip}`);
|
|
428
428
|
}
|
|
429
429
|
}
|
|
430
|
+
const decimals = assetInfo?.decimals ?? assetInfo?.decimal ?? balance.decimals ?? balance.decimal;
|
|
431
|
+
if (decimals === undefined || decimals === null) {
|
|
432
|
+
throw new Error(`CRITICAL: Asset ${balance.caip} (${assetInfo?.symbol || balance.symbol || "UNKNOWN"}) has NO decimals/precision! ` + `This would cause incorrect balance calculations. ` + `Asset data: ${JSON.stringify({
|
|
433
|
+
caip: balance.caip,
|
|
434
|
+
symbol: assetInfo?.symbol || balance.symbol,
|
|
435
|
+
hasAssetInfoDecimals: !!assetInfo?.decimals,
|
|
436
|
+
hasAssetInfoDecimal: !!assetInfo?.decimal,
|
|
437
|
+
hasBalanceDecimals: !!balance.decimals,
|
|
438
|
+
hasBalanceDecimal: !!balance.decimal
|
|
439
|
+
})}`);
|
|
440
|
+
}
|
|
430
441
|
const chartBalance = {
|
|
431
442
|
context,
|
|
432
443
|
chart: "pioneer",
|
|
@@ -443,7 +454,9 @@ function processPortfolioBalance(balance, primaryAddress, context, blockchains)
|
|
|
443
454
|
symbol: assetInfo?.symbol || balance.symbol || "UNK",
|
|
444
455
|
type: balanceType,
|
|
445
456
|
token: isToken,
|
|
446
|
-
decimal:
|
|
457
|
+
decimal: decimals,
|
|
458
|
+
decimals,
|
|
459
|
+
precision: decimals,
|
|
447
460
|
balance: balance.balance.toString(),
|
|
448
461
|
price: calculatedPrice,
|
|
449
462
|
priceUsd: calculatedPrice,
|
|
@@ -474,6 +487,17 @@ function processPortfolioToken(token, primaryAddress, context, blockchains) {
|
|
|
474
487
|
}
|
|
475
488
|
const tokenAssetInfo = hydrateAssetData(token.assetCaip);
|
|
476
489
|
const tokenPubkey = token.pubkey || primaryAddress;
|
|
490
|
+
const tokenDecimals = tokenAssetInfo?.decimals ?? tokenAssetInfo?.decimal ?? token.token?.decimals ?? token.token?.decimal;
|
|
491
|
+
if (tokenDecimals === undefined || tokenDecimals === null) {
|
|
492
|
+
throw new Error(`CRITICAL: Token ${token.assetCaip} (${tokenAssetInfo?.symbol || token.token?.symbol || "UNKNOWN"}) has NO decimals/precision! ` + `This would cause incorrect balance calculations. ` + `Token data: ${JSON.stringify({
|
|
493
|
+
caip: token.assetCaip,
|
|
494
|
+
symbol: tokenAssetInfo?.symbol || token.token?.symbol,
|
|
495
|
+
hasAssetInfoDecimals: !!tokenAssetInfo?.decimals,
|
|
496
|
+
hasAssetInfoDecimal: !!tokenAssetInfo?.decimal,
|
|
497
|
+
hasTokenDecimals: !!token.token?.decimals,
|
|
498
|
+
hasTokenDecimal: !!token.token?.decimal
|
|
499
|
+
})}`);
|
|
500
|
+
}
|
|
477
501
|
const chartBalance = {
|
|
478
502
|
context,
|
|
479
503
|
chart: "pioneer",
|
|
@@ -489,7 +513,9 @@ function processPortfolioToken(token, primaryAddress, context, blockchains) {
|
|
|
489
513
|
symbol: tokenAssetInfo?.symbol || token.token?.symbol || "UNK",
|
|
490
514
|
type: tokenAssetInfo?.type || "token",
|
|
491
515
|
token: true,
|
|
492
|
-
decimal:
|
|
516
|
+
decimal: tokenDecimals,
|
|
517
|
+
decimals: tokenDecimals,
|
|
518
|
+
precision: tokenDecimals,
|
|
493
519
|
balance: token.token?.balance?.toString() || "0",
|
|
494
520
|
priceUsd: token.token?.price || 0,
|
|
495
521
|
valueUsd: token.token?.balanceUSD || 0,
|
|
@@ -596,6 +622,17 @@ async function getMayaCharts(params, existingBalances) {
|
|
|
596
622
|
}
|
|
597
623
|
const mayaAssetInfo = hydrateAssetData(mayaBalance.caip);
|
|
598
624
|
const isToken = mayaBalance.caip.includes("/denom:") && !mayaBalance.caip.endsWith("/denom:cacao");
|
|
625
|
+
const decimals = mayaAssetInfo?.decimals ?? mayaAssetInfo?.decimal ?? mayaBalance.decimals ?? mayaBalance.decimal;
|
|
626
|
+
if (decimals === undefined || decimals === null) {
|
|
627
|
+
throw new Error(`CRITICAL: Asset ${mayaBalance.caip} (${mayaAssetInfo?.symbol || "MAYA"}) has NO decimals/precision! ` + `This would cause incorrect balance calculations. ` + `Asset data: ${JSON.stringify({
|
|
628
|
+
caip: mayaBalance.caip,
|
|
629
|
+
symbol: mayaAssetInfo?.symbol,
|
|
630
|
+
hasAssetInfoDecimals: !!mayaAssetInfo?.decimals,
|
|
631
|
+
hasAssetInfoDecimal: !!mayaAssetInfo?.decimal,
|
|
632
|
+
hasBalanceDecimals: !!mayaBalance.decimals,
|
|
633
|
+
hasBalanceDecimal: !!mayaBalance.decimal
|
|
634
|
+
})}`);
|
|
635
|
+
}
|
|
599
636
|
const mayaTokenBalance = {
|
|
600
637
|
context,
|
|
601
638
|
chart: "pioneer",
|
|
@@ -611,7 +648,9 @@ async function getMayaCharts(params, existingBalances) {
|
|
|
611
648
|
symbol: mayaAssetInfo?.symbol || "MAYA",
|
|
612
649
|
type: mayaAssetInfo?.type || "token",
|
|
613
650
|
token: isToken,
|
|
614
|
-
decimal:
|
|
651
|
+
decimal: decimals,
|
|
652
|
+
decimals,
|
|
653
|
+
precision: decimals,
|
|
615
654
|
balance: mayaBalance.balance?.toString() || "0",
|
|
616
655
|
priceUsd: parseFloat(mayaBalance.priceUsd) || 0,
|
|
617
656
|
valueUsd: parseFloat(mayaBalance.valueUsd) || 0,
|
|
@@ -4359,6 +4398,80 @@ function filterPubkeysForAsset(pubkeys, caip, caipToNetworkId7) {
|
|
|
4359
4398
|
|
|
4360
4399
|
// src/utils/portfolio-helpers.ts
|
|
4361
4400
|
var TAG9 = " | portfolio-helpers | ";
|
|
4401
|
+
var EXPLORER_BASE_URLS = {
|
|
4402
|
+
"eip155:1": {
|
|
4403
|
+
address: "https://etherscan.io/address/",
|
|
4404
|
+
tx: "https://etherscan.io/tx/"
|
|
4405
|
+
},
|
|
4406
|
+
"eip155:137": {
|
|
4407
|
+
address: "https://polygonscan.com/address/",
|
|
4408
|
+
tx: "https://polygonscan.com/tx/"
|
|
4409
|
+
},
|
|
4410
|
+
"eip155:8453": {
|
|
4411
|
+
address: "https://basescan.org/address/",
|
|
4412
|
+
tx: "https://basescan.org/tx/"
|
|
4413
|
+
},
|
|
4414
|
+
"eip155:56": {
|
|
4415
|
+
address: "https://bscscan.com/address/",
|
|
4416
|
+
tx: "https://bscscan.com/tx/"
|
|
4417
|
+
},
|
|
4418
|
+
"eip155:41454": {
|
|
4419
|
+
address: "https://explorer.monad.xyz/address/",
|
|
4420
|
+
tx: "https://explorer.monad.xyz/tx/"
|
|
4421
|
+
},
|
|
4422
|
+
"eip155:2868": {
|
|
4423
|
+
address: "https://app.hyperliquid.xyz/explorer/address/",
|
|
4424
|
+
tx: "https://app.hyperliquid.xyz/explorer/tx/"
|
|
4425
|
+
},
|
|
4426
|
+
"bip122:000000000019d6689c085ae165831e93": {
|
|
4427
|
+
address: "https://blockstream.info/address/",
|
|
4428
|
+
tx: "https://blockstream.info/tx/"
|
|
4429
|
+
},
|
|
4430
|
+
"bip122:12a765e31ffd4059bada1e25190f6e98": {
|
|
4431
|
+
address: "https://blockchair.com/litecoin/address/",
|
|
4432
|
+
tx: "https://blockchair.com/litecoin/transaction/"
|
|
4433
|
+
},
|
|
4434
|
+
"bip122:00000000001a91e3dace36e2be3bf030": {
|
|
4435
|
+
address: "https://dogechain.info/address/",
|
|
4436
|
+
tx: "https://dogechain.info/tx/"
|
|
4437
|
+
},
|
|
4438
|
+
"bip122:000000000000000000651ef99cb9fcbe": {
|
|
4439
|
+
address: "https://blockchair.com/bitcoin-cash/address/",
|
|
4440
|
+
tx: "https://blockchair.com/bitcoin-cash/transaction/"
|
|
4441
|
+
},
|
|
4442
|
+
"bip122:000007d91d1254d60e2dd1ae58038307": {
|
|
4443
|
+
address: "https://chainz.cryptoid.info/dash/address.dws?",
|
|
4444
|
+
tx: "https://chainz.cryptoid.info/dash/tx.dws?"
|
|
4445
|
+
},
|
|
4446
|
+
"bip122:4da631f2ac1bed857bd968c67c913978": {
|
|
4447
|
+
address: "https://digiexplorer.info/address/",
|
|
4448
|
+
tx: "https://digiexplorer.info/tx/"
|
|
4449
|
+
},
|
|
4450
|
+
"bip122:00040fe8ec8471911baa1db1266ea15d": {
|
|
4451
|
+
address: "https://explorer.zcha.in/accounts/",
|
|
4452
|
+
tx: "https://explorer.zcha.in/transactions/"
|
|
4453
|
+
},
|
|
4454
|
+
"cosmos:cosmoshub-4": {
|
|
4455
|
+
address: "https://www.mintscan.io/cosmos/address/",
|
|
4456
|
+
tx: "https://www.mintscan.io/cosmos/tx/"
|
|
4457
|
+
},
|
|
4458
|
+
"cosmos:osmosis-1": {
|
|
4459
|
+
address: "https://www.mintscan.io/osmosis/address/",
|
|
4460
|
+
tx: "https://www.mintscan.io/osmosis/tx/"
|
|
4461
|
+
},
|
|
4462
|
+
"cosmos:thorchain-mainnet-v1": {
|
|
4463
|
+
address: "https://thorchain.net/address/",
|
|
4464
|
+
tx: "https://thorchain.net/tx/"
|
|
4465
|
+
},
|
|
4466
|
+
"cosmos:mayachain-mainnet-v1": {
|
|
4467
|
+
address: "https://www.mayascan.org/address/",
|
|
4468
|
+
tx: "https://www.mayascan.org/tx/"
|
|
4469
|
+
},
|
|
4470
|
+
"ripple:4109c6f2045fc7eff4cde8f9905d19c2": {
|
|
4471
|
+
address: "https://xrpscan.com/account/",
|
|
4472
|
+
tx: "https://xrpscan.com/tx/"
|
|
4473
|
+
}
|
|
4474
|
+
};
|
|
4362
4475
|
function isCacheDataValid(portfolioData) {
|
|
4363
4476
|
if (!portfolioData.networks || !Array.isArray(portfolioData.networks)) {
|
|
4364
4477
|
console.warn("[CACHE VALIDATION] Networks is not an array");
|
|
@@ -4510,14 +4623,27 @@ function enrichBalancesWithAssetInfo(balances, assetsMap, caipToNetworkId7) {
|
|
|
4510
4623
|
} else if (typeof balance.priceUsd !== "number") {
|
|
4511
4624
|
balance.priceUsd = 0;
|
|
4512
4625
|
}
|
|
4626
|
+
const networkId = caipToNetworkId7(balance.caip);
|
|
4627
|
+
const explorerUrls = EXPLORER_BASE_URLS[networkId];
|
|
4628
|
+
const explorerAddressLink = explorerUrls && balance.pubkey ? explorerUrls.address + balance.pubkey : undefined;
|
|
4629
|
+
const explorerTxLink = explorerUrls?.tx;
|
|
4630
|
+
const decimals = assetInfo.decimals ?? balance.decimals;
|
|
4631
|
+
if (decimals === undefined || decimals === null) {
|
|
4632
|
+
throw new Error(`CRITICAL: Asset ${balance.caip} (${assetInfo.symbol || "UNKNOWN"}) has NO decimals/precision! ` + `AssetInfo is incomplete. This would cause incorrect balance calculations. ` + `Asset data: ${JSON.stringify({ caip: balance.caip, symbol: assetInfo.symbol, hasDecimals: !!assetInfo.decimals })}`);
|
|
4633
|
+
}
|
|
4513
4634
|
Object.assign(balance, assetInfo, {
|
|
4514
4635
|
type: balance.type || assetInfo.type,
|
|
4515
4636
|
isNative: balance.isNative ?? assetInfo.isNative,
|
|
4516
|
-
networkId
|
|
4637
|
+
networkId,
|
|
4517
4638
|
icon: assetInfo.icon || "https://pioneers.dev/coins/etherum.png",
|
|
4518
4639
|
identifier: `${balance.caip}:${balance.pubkey}`,
|
|
4519
4640
|
updated: Date.now(),
|
|
4520
4641
|
color: assetInfo.color,
|
|
4642
|
+
decimals,
|
|
4643
|
+
precision: decimals,
|
|
4644
|
+
explorerAddressLink,
|
|
4645
|
+
explorerTxLink,
|
|
4646
|
+
explorer: explorerUrls?.address,
|
|
4521
4647
|
valueUsd: balance.valueUsd,
|
|
4522
4648
|
priceUsd: balance.priceUsd
|
|
4523
4649
|
});
|
|
@@ -5945,7 +6071,7 @@ class SDK {
|
|
|
5945
6071
|
const pubkeysForNetwork = findPubkeysForNetwork(this.pubkeys, asset.networkId);
|
|
5946
6072
|
console.log(tag6, `✅ Validated: Found ${pubkeysForNetwork.length} addresses for ${asset.networkId}`);
|
|
5947
6073
|
const freshPriceUsd = await fetchMarketPrice(this.pioneer, asset.caip);
|
|
5948
|
-
let assetInfo = resolveAssetInfo(this.assetsMap,
|
|
6074
|
+
let assetInfo = resolveAssetInfo(this.assetsMap, this.assets, asset);
|
|
5949
6075
|
const matchingBalances = this.balances.filter((b2) => b2.caip === asset.caip);
|
|
5950
6076
|
if (matchingBalances.length > 0) {
|
|
5951
6077
|
const priceValue = extractPriceFromBalances(matchingBalances);
|
|
@@ -5961,14 +6087,47 @@ class SDK {
|
|
|
5961
6087
|
assetInfo.balance = totalBalance.toString();
|
|
5962
6088
|
assetInfo.valueUsd = totalValueUsd.toFixed(2);
|
|
5963
6089
|
}
|
|
6090
|
+
const EXPLORER_BASE_URLS2 = {
|
|
6091
|
+
"eip155:1": { address: "https://etherscan.io/address/", tx: "https://etherscan.io/tx/" },
|
|
6092
|
+
"eip155:137": { address: "https://polygonscan.com/address/", tx: "https://polygonscan.com/tx/" },
|
|
6093
|
+
"eip155:8453": { address: "https://basescan.org/address/", tx: "https://basescan.org/tx/" },
|
|
6094
|
+
"eip155:56": { address: "https://bscscan.com/address/", tx: "https://bscscan.com/tx/" },
|
|
6095
|
+
"eip155:41454": { address: "https://explorer.monad.xyz/address/", tx: "https://explorer.monad.xyz/tx/" },
|
|
6096
|
+
"eip155:2868": { address: "https://app.hyperliquid.xyz/explorer/address/", tx: "https://app.hyperliquid.xyz/explorer/tx/" },
|
|
6097
|
+
"bip122:000000000019d6689c085ae165831e93": { address: "https://blockstream.info/address/", tx: "https://blockstream.info/tx/" },
|
|
6098
|
+
"bip122:12a765e31ffd4059bada1e25190f6e98": { address: "https://blockchair.com/litecoin/address/", tx: "https://blockchair.com/litecoin/transaction/" },
|
|
6099
|
+
"bip122:00000000001a91e3dace36e2be3bf030": { address: "https://dogechain.info/address/", tx: "https://dogechain.info/tx/" },
|
|
6100
|
+
"bip122:000000000000000000651ef99cb9fcbe": { address: "https://blockchair.com/bitcoin-cash/address/", tx: "https://blockchair.com/bitcoin-cash/transaction/" },
|
|
6101
|
+
"bip122:000007d91d1254d60e2dd1ae58038307": { address: "https://chainz.cryptoid.info/dash/address.dws?", tx: "https://chainz.cryptoid.info/dash/tx.dws?" },
|
|
6102
|
+
"bip122:4da631f2ac1bed857bd968c67c913978": { address: "https://digiexplorer.info/address/", tx: "https://digiexplorer.info/tx/" },
|
|
6103
|
+
"bip122:00040fe8ec8471911baa1db1266ea15d": { address: "https://explorer.zcha.in/accounts/", tx: "https://explorer.zcha.in/transactions/" },
|
|
6104
|
+
"cosmos:cosmoshub-4": { address: "https://www.mintscan.io/cosmos/address/", tx: "https://www.mintscan.io/cosmos/tx/" },
|
|
6105
|
+
"cosmos:osmosis-1": { address: "https://www.mintscan.io/osmosis/address/", tx: "https://www.mintscan.io/osmosis/tx/" },
|
|
6106
|
+
"cosmos:thorchain-mainnet-v1": { address: "https://thorchain.net/address/", tx: "https://thorchain.net/tx/" },
|
|
6107
|
+
"cosmos:mayachain-mainnet-v1": { address: "https://www.mayascan.org/address/", tx: "https://www.mayascan.org/tx/" },
|
|
6108
|
+
"ripple:4109c6f2045fc7eff4cde8f9905d19c2": { address: "https://xrpscan.com/account/", tx: "https://xrpscan.com/tx/" }
|
|
6109
|
+
};
|
|
6110
|
+
const networkId = caipToNetworkId7(asset.caip);
|
|
6111
|
+
const explorerUrls = EXPLORER_BASE_URLS2[networkId];
|
|
6112
|
+
const firstPubkey = pubkeysForNetwork && pubkeysForNetwork.length > 0 ? pubkeysForNetwork[0].address || pubkeysForNetwork[0].pubkey : null;
|
|
6113
|
+
if (explorerUrls && firstPubkey) {
|
|
6114
|
+
assetInfo.explorerAddressLink = explorerUrls.address + firstPubkey;
|
|
6115
|
+
assetInfo.explorerTxLink = explorerUrls.tx;
|
|
6116
|
+
assetInfo.explorer = explorerUrls.address;
|
|
6117
|
+
}
|
|
5964
6118
|
const assetBalances = this.balances.filter((b2) => b2.caip === asset.caip);
|
|
5965
6119
|
const assetPubkeys = filterPubkeysForAsset(this.pubkeys, asset.caip, caipToNetworkId7);
|
|
6120
|
+
const criticalFields = ["decimals", "precision", "explorerAddressLink", "explorerTxLink", "explorer"];
|
|
5966
6121
|
const finalAssetContext = {
|
|
5967
6122
|
...assetInfo,
|
|
5968
|
-
...asset,
|
|
6123
|
+
...Object.fromEntries(Object.entries(asset).filter(([k, v2]) => v2 !== undefined && !criticalFields.includes(k))),
|
|
5969
6124
|
pubkeys: assetPubkeys,
|
|
5970
6125
|
balances: assetBalances
|
|
5971
6126
|
};
|
|
6127
|
+
if (assetInfo.decimals !== undefined) {
|
|
6128
|
+
finalAssetContext.decimals = assetInfo.decimals;
|
|
6129
|
+
finalAssetContext.precision = assetInfo.decimals;
|
|
6130
|
+
}
|
|
5972
6131
|
if ((!asset.priceUsd || asset.priceUsd === 0) && assetInfo.priceUsd && assetInfo.priceUsd > 0) {
|
|
5973
6132
|
finalAssetContext.priceUsd = assetInfo.priceUsd;
|
|
5974
6133
|
}
|
|
@@ -5977,15 +6136,22 @@ class SDK {
|
|
|
5977
6136
|
}
|
|
5978
6137
|
this.assetContext = finalAssetContext;
|
|
5979
6138
|
if (asset.isToken || asset.type === "token" || assetInfo.isToken || assetInfo.type === "token") {
|
|
5980
|
-
const
|
|
5981
|
-
|
|
5982
|
-
|
|
6139
|
+
const networkId2 = asset.networkId || assetInfo.networkId;
|
|
6140
|
+
try {
|
|
6141
|
+
const nativeCaip = networkIdToCaip2(networkId2);
|
|
6142
|
+
const nativeAssetInfo = assetData2[nativeCaip];
|
|
6143
|
+
const nativeSymbol = nativeAssetInfo?.symbol || "GAS";
|
|
6144
|
+
this.assetContext.nativeSymbol = nativeSymbol;
|
|
5983
6145
|
const nativeBalance = this.balances.find((b2) => b2.caip === nativeCaip);
|
|
5984
6146
|
if (nativeBalance) {
|
|
5985
6147
|
this.assetContext.nativeBalance = nativeBalance.balance || "0";
|
|
5986
6148
|
} else {
|
|
5987
6149
|
this.assetContext.nativeBalance = "0";
|
|
5988
6150
|
}
|
|
6151
|
+
} catch (error) {
|
|
6152
|
+
console.error(`[Pioneer SDK] Unable to get native asset for networkId ${networkId2}:`, error);
|
|
6153
|
+
this.assetContext.nativeSymbol = "GAS";
|
|
6154
|
+
this.assetContext.nativeBalance = "0";
|
|
5989
6155
|
}
|
|
5990
6156
|
}
|
|
5991
6157
|
if (asset.caip) {
|
|
@@ -5994,8 +6160,8 @@ class SDK {
|
|
|
5994
6160
|
this.blockchainContext = asset.networkId;
|
|
5995
6161
|
}
|
|
5996
6162
|
if (assetPubkeys && assetPubkeys.length > 0) {
|
|
5997
|
-
const
|
|
5998
|
-
const currentContextValid = this.pubkeyContext?.networks?.includes(
|
|
6163
|
+
const networkId2 = caipToNetworkId7(asset.caip || asset.networkId);
|
|
6164
|
+
const currentContextValid = this.pubkeyContext?.networks?.includes(networkId2);
|
|
5999
6165
|
if (!this.pubkeyContext || !currentContextValid) {
|
|
6000
6166
|
this.pubkeyContext = assetPubkeys[0];
|
|
6001
6167
|
console.log(tag6, "Auto-set pubkey context for network:", this.pubkeyContext.address || this.pubkeyContext.pubkey);
|
|
@@ -6046,7 +6212,7 @@ class SDK {
|
|
|
6046
6212
|
if (!pubkey)
|
|
6047
6213
|
throw Error("Invalid network! missing pubkey for network! " + asset.networkId);
|
|
6048
6214
|
const freshPriceUsd = await fetchMarketPrice(this.pioneer, asset.caip);
|
|
6049
|
-
let assetInfo = resolveAssetInfo(this.assetsMap,
|
|
6215
|
+
let assetInfo = resolveAssetInfo(this.assetsMap, this.assets, asset);
|
|
6050
6216
|
const matchingBalances = this.balances.filter((b2) => b2.caip === asset.caip);
|
|
6051
6217
|
if (matchingBalances.length > 0) {
|
|
6052
6218
|
const priceValue = extractPriceFromBalances(matchingBalances);
|
package/dist/index.js
CHANGED
|
@@ -427,6 +427,17 @@ function processPortfolioBalance(balance, primaryAddress, context, blockchains)
|
|
|
427
427
|
console.log(tag2, `Calculated price from value/balance: ${calculatedPrice} for ${balance.caip}`);
|
|
428
428
|
}
|
|
429
429
|
}
|
|
430
|
+
const decimals = assetInfo?.decimals ?? assetInfo?.decimal ?? balance.decimals ?? balance.decimal;
|
|
431
|
+
if (decimals === undefined || decimals === null) {
|
|
432
|
+
throw new Error(`CRITICAL: Asset ${balance.caip} (${assetInfo?.symbol || balance.symbol || "UNKNOWN"}) has NO decimals/precision! ` + `This would cause incorrect balance calculations. ` + `Asset data: ${JSON.stringify({
|
|
433
|
+
caip: balance.caip,
|
|
434
|
+
symbol: assetInfo?.symbol || balance.symbol,
|
|
435
|
+
hasAssetInfoDecimals: !!assetInfo?.decimals,
|
|
436
|
+
hasAssetInfoDecimal: !!assetInfo?.decimal,
|
|
437
|
+
hasBalanceDecimals: !!balance.decimals,
|
|
438
|
+
hasBalanceDecimal: !!balance.decimal
|
|
439
|
+
})}`);
|
|
440
|
+
}
|
|
430
441
|
const chartBalance = {
|
|
431
442
|
context,
|
|
432
443
|
chart: "pioneer",
|
|
@@ -443,7 +454,9 @@ function processPortfolioBalance(balance, primaryAddress, context, blockchains)
|
|
|
443
454
|
symbol: assetInfo?.symbol || balance.symbol || "UNK",
|
|
444
455
|
type: balanceType,
|
|
445
456
|
token: isToken,
|
|
446
|
-
decimal:
|
|
457
|
+
decimal: decimals,
|
|
458
|
+
decimals,
|
|
459
|
+
precision: decimals,
|
|
447
460
|
balance: balance.balance.toString(),
|
|
448
461
|
price: calculatedPrice,
|
|
449
462
|
priceUsd: calculatedPrice,
|
|
@@ -474,6 +487,17 @@ function processPortfolioToken(token, primaryAddress, context, blockchains) {
|
|
|
474
487
|
}
|
|
475
488
|
const tokenAssetInfo = hydrateAssetData(token.assetCaip);
|
|
476
489
|
const tokenPubkey = token.pubkey || primaryAddress;
|
|
490
|
+
const tokenDecimals = tokenAssetInfo?.decimals ?? tokenAssetInfo?.decimal ?? token.token?.decimals ?? token.token?.decimal;
|
|
491
|
+
if (tokenDecimals === undefined || tokenDecimals === null) {
|
|
492
|
+
throw new Error(`CRITICAL: Token ${token.assetCaip} (${tokenAssetInfo?.symbol || token.token?.symbol || "UNKNOWN"}) has NO decimals/precision! ` + `This would cause incorrect balance calculations. ` + `Token data: ${JSON.stringify({
|
|
493
|
+
caip: token.assetCaip,
|
|
494
|
+
symbol: tokenAssetInfo?.symbol || token.token?.symbol,
|
|
495
|
+
hasAssetInfoDecimals: !!tokenAssetInfo?.decimals,
|
|
496
|
+
hasAssetInfoDecimal: !!tokenAssetInfo?.decimal,
|
|
497
|
+
hasTokenDecimals: !!token.token?.decimals,
|
|
498
|
+
hasTokenDecimal: !!token.token?.decimal
|
|
499
|
+
})}`);
|
|
500
|
+
}
|
|
477
501
|
const chartBalance = {
|
|
478
502
|
context,
|
|
479
503
|
chart: "pioneer",
|
|
@@ -489,7 +513,9 @@ function processPortfolioToken(token, primaryAddress, context, blockchains) {
|
|
|
489
513
|
symbol: tokenAssetInfo?.symbol || token.token?.symbol || "UNK",
|
|
490
514
|
type: tokenAssetInfo?.type || "token",
|
|
491
515
|
token: true,
|
|
492
|
-
decimal:
|
|
516
|
+
decimal: tokenDecimals,
|
|
517
|
+
decimals: tokenDecimals,
|
|
518
|
+
precision: tokenDecimals,
|
|
493
519
|
balance: token.token?.balance?.toString() || "0",
|
|
494
520
|
priceUsd: token.token?.price || 0,
|
|
495
521
|
valueUsd: token.token?.balanceUSD || 0,
|
|
@@ -596,6 +622,17 @@ async function getMayaCharts(params, existingBalances) {
|
|
|
596
622
|
}
|
|
597
623
|
const mayaAssetInfo = hydrateAssetData(mayaBalance.caip);
|
|
598
624
|
const isToken = mayaBalance.caip.includes("/denom:") && !mayaBalance.caip.endsWith("/denom:cacao");
|
|
625
|
+
const decimals = mayaAssetInfo?.decimals ?? mayaAssetInfo?.decimal ?? mayaBalance.decimals ?? mayaBalance.decimal;
|
|
626
|
+
if (decimals === undefined || decimals === null) {
|
|
627
|
+
throw new Error(`CRITICAL: Asset ${mayaBalance.caip} (${mayaAssetInfo?.symbol || "MAYA"}) has NO decimals/precision! ` + `This would cause incorrect balance calculations. ` + `Asset data: ${JSON.stringify({
|
|
628
|
+
caip: mayaBalance.caip,
|
|
629
|
+
symbol: mayaAssetInfo?.symbol,
|
|
630
|
+
hasAssetInfoDecimals: !!mayaAssetInfo?.decimals,
|
|
631
|
+
hasAssetInfoDecimal: !!mayaAssetInfo?.decimal,
|
|
632
|
+
hasBalanceDecimals: !!mayaBalance.decimals,
|
|
633
|
+
hasBalanceDecimal: !!mayaBalance.decimal
|
|
634
|
+
})}`);
|
|
635
|
+
}
|
|
599
636
|
const mayaTokenBalance = {
|
|
600
637
|
context,
|
|
601
638
|
chart: "pioneer",
|
|
@@ -611,7 +648,9 @@ async function getMayaCharts(params, existingBalances) {
|
|
|
611
648
|
symbol: mayaAssetInfo?.symbol || "MAYA",
|
|
612
649
|
type: mayaAssetInfo?.type || "token",
|
|
613
650
|
token: isToken,
|
|
614
|
-
decimal:
|
|
651
|
+
decimal: decimals,
|
|
652
|
+
decimals,
|
|
653
|
+
precision: decimals,
|
|
615
654
|
balance: mayaBalance.balance?.toString() || "0",
|
|
616
655
|
priceUsd: parseFloat(mayaBalance.priceUsd) || 0,
|
|
617
656
|
valueUsd: parseFloat(mayaBalance.valueUsd) || 0,
|
|
@@ -4359,6 +4398,80 @@ function filterPubkeysForAsset(pubkeys, caip, caipToNetworkId7) {
|
|
|
4359
4398
|
|
|
4360
4399
|
// src/utils/portfolio-helpers.ts
|
|
4361
4400
|
var TAG9 = " | portfolio-helpers | ";
|
|
4401
|
+
var EXPLORER_BASE_URLS = {
|
|
4402
|
+
"eip155:1": {
|
|
4403
|
+
address: "https://etherscan.io/address/",
|
|
4404
|
+
tx: "https://etherscan.io/tx/"
|
|
4405
|
+
},
|
|
4406
|
+
"eip155:137": {
|
|
4407
|
+
address: "https://polygonscan.com/address/",
|
|
4408
|
+
tx: "https://polygonscan.com/tx/"
|
|
4409
|
+
},
|
|
4410
|
+
"eip155:8453": {
|
|
4411
|
+
address: "https://basescan.org/address/",
|
|
4412
|
+
tx: "https://basescan.org/tx/"
|
|
4413
|
+
},
|
|
4414
|
+
"eip155:56": {
|
|
4415
|
+
address: "https://bscscan.com/address/",
|
|
4416
|
+
tx: "https://bscscan.com/tx/"
|
|
4417
|
+
},
|
|
4418
|
+
"eip155:41454": {
|
|
4419
|
+
address: "https://explorer.monad.xyz/address/",
|
|
4420
|
+
tx: "https://explorer.monad.xyz/tx/"
|
|
4421
|
+
},
|
|
4422
|
+
"eip155:2868": {
|
|
4423
|
+
address: "https://app.hyperliquid.xyz/explorer/address/",
|
|
4424
|
+
tx: "https://app.hyperliquid.xyz/explorer/tx/"
|
|
4425
|
+
},
|
|
4426
|
+
"bip122:000000000019d6689c085ae165831e93": {
|
|
4427
|
+
address: "https://blockstream.info/address/",
|
|
4428
|
+
tx: "https://blockstream.info/tx/"
|
|
4429
|
+
},
|
|
4430
|
+
"bip122:12a765e31ffd4059bada1e25190f6e98": {
|
|
4431
|
+
address: "https://blockchair.com/litecoin/address/",
|
|
4432
|
+
tx: "https://blockchair.com/litecoin/transaction/"
|
|
4433
|
+
},
|
|
4434
|
+
"bip122:00000000001a91e3dace36e2be3bf030": {
|
|
4435
|
+
address: "https://dogechain.info/address/",
|
|
4436
|
+
tx: "https://dogechain.info/tx/"
|
|
4437
|
+
},
|
|
4438
|
+
"bip122:000000000000000000651ef99cb9fcbe": {
|
|
4439
|
+
address: "https://blockchair.com/bitcoin-cash/address/",
|
|
4440
|
+
tx: "https://blockchair.com/bitcoin-cash/transaction/"
|
|
4441
|
+
},
|
|
4442
|
+
"bip122:000007d91d1254d60e2dd1ae58038307": {
|
|
4443
|
+
address: "https://chainz.cryptoid.info/dash/address.dws?",
|
|
4444
|
+
tx: "https://chainz.cryptoid.info/dash/tx.dws?"
|
|
4445
|
+
},
|
|
4446
|
+
"bip122:4da631f2ac1bed857bd968c67c913978": {
|
|
4447
|
+
address: "https://digiexplorer.info/address/",
|
|
4448
|
+
tx: "https://digiexplorer.info/tx/"
|
|
4449
|
+
},
|
|
4450
|
+
"bip122:00040fe8ec8471911baa1db1266ea15d": {
|
|
4451
|
+
address: "https://explorer.zcha.in/accounts/",
|
|
4452
|
+
tx: "https://explorer.zcha.in/transactions/"
|
|
4453
|
+
},
|
|
4454
|
+
"cosmos:cosmoshub-4": {
|
|
4455
|
+
address: "https://www.mintscan.io/cosmos/address/",
|
|
4456
|
+
tx: "https://www.mintscan.io/cosmos/tx/"
|
|
4457
|
+
},
|
|
4458
|
+
"cosmos:osmosis-1": {
|
|
4459
|
+
address: "https://www.mintscan.io/osmosis/address/",
|
|
4460
|
+
tx: "https://www.mintscan.io/osmosis/tx/"
|
|
4461
|
+
},
|
|
4462
|
+
"cosmos:thorchain-mainnet-v1": {
|
|
4463
|
+
address: "https://thorchain.net/address/",
|
|
4464
|
+
tx: "https://thorchain.net/tx/"
|
|
4465
|
+
},
|
|
4466
|
+
"cosmos:mayachain-mainnet-v1": {
|
|
4467
|
+
address: "https://www.mayascan.org/address/",
|
|
4468
|
+
tx: "https://www.mayascan.org/tx/"
|
|
4469
|
+
},
|
|
4470
|
+
"ripple:4109c6f2045fc7eff4cde8f9905d19c2": {
|
|
4471
|
+
address: "https://xrpscan.com/account/",
|
|
4472
|
+
tx: "https://xrpscan.com/tx/"
|
|
4473
|
+
}
|
|
4474
|
+
};
|
|
4362
4475
|
function isCacheDataValid(portfolioData) {
|
|
4363
4476
|
if (!portfolioData.networks || !Array.isArray(portfolioData.networks)) {
|
|
4364
4477
|
console.warn("[CACHE VALIDATION] Networks is not an array");
|
|
@@ -4510,14 +4623,27 @@ function enrichBalancesWithAssetInfo(balances, assetsMap, caipToNetworkId7) {
|
|
|
4510
4623
|
} else if (typeof balance.priceUsd !== "number") {
|
|
4511
4624
|
balance.priceUsd = 0;
|
|
4512
4625
|
}
|
|
4626
|
+
const networkId = caipToNetworkId7(balance.caip);
|
|
4627
|
+
const explorerUrls = EXPLORER_BASE_URLS[networkId];
|
|
4628
|
+
const explorerAddressLink = explorerUrls && balance.pubkey ? explorerUrls.address + balance.pubkey : undefined;
|
|
4629
|
+
const explorerTxLink = explorerUrls?.tx;
|
|
4630
|
+
const decimals = assetInfo.decimals ?? balance.decimals;
|
|
4631
|
+
if (decimals === undefined || decimals === null) {
|
|
4632
|
+
throw new Error(`CRITICAL: Asset ${balance.caip} (${assetInfo.symbol || "UNKNOWN"}) has NO decimals/precision! ` + `AssetInfo is incomplete. This would cause incorrect balance calculations. ` + `Asset data: ${JSON.stringify({ caip: balance.caip, symbol: assetInfo.symbol, hasDecimals: !!assetInfo.decimals })}`);
|
|
4633
|
+
}
|
|
4513
4634
|
Object.assign(balance, assetInfo, {
|
|
4514
4635
|
type: balance.type || assetInfo.type,
|
|
4515
4636
|
isNative: balance.isNative ?? assetInfo.isNative,
|
|
4516
|
-
networkId
|
|
4637
|
+
networkId,
|
|
4517
4638
|
icon: assetInfo.icon || "https://pioneers.dev/coins/etherum.png",
|
|
4518
4639
|
identifier: `${balance.caip}:${balance.pubkey}`,
|
|
4519
4640
|
updated: Date.now(),
|
|
4520
4641
|
color: assetInfo.color,
|
|
4642
|
+
decimals,
|
|
4643
|
+
precision: decimals,
|
|
4644
|
+
explorerAddressLink,
|
|
4645
|
+
explorerTxLink,
|
|
4646
|
+
explorer: explorerUrls?.address,
|
|
4521
4647
|
valueUsd: balance.valueUsd,
|
|
4522
4648
|
priceUsd: balance.priceUsd
|
|
4523
4649
|
});
|
|
@@ -5945,7 +6071,7 @@ class SDK {
|
|
|
5945
6071
|
const pubkeysForNetwork = findPubkeysForNetwork(this.pubkeys, asset.networkId);
|
|
5946
6072
|
console.log(tag6, `✅ Validated: Found ${pubkeysForNetwork.length} addresses for ${asset.networkId}`);
|
|
5947
6073
|
const freshPriceUsd = await fetchMarketPrice(this.pioneer, asset.caip);
|
|
5948
|
-
let assetInfo = resolveAssetInfo(this.assetsMap,
|
|
6074
|
+
let assetInfo = resolveAssetInfo(this.assetsMap, this.assets, asset);
|
|
5949
6075
|
const matchingBalances = this.balances.filter((b2) => b2.caip === asset.caip);
|
|
5950
6076
|
if (matchingBalances.length > 0) {
|
|
5951
6077
|
const priceValue = extractPriceFromBalances(matchingBalances);
|
|
@@ -5961,14 +6087,47 @@ class SDK {
|
|
|
5961
6087
|
assetInfo.balance = totalBalance.toString();
|
|
5962
6088
|
assetInfo.valueUsd = totalValueUsd.toFixed(2);
|
|
5963
6089
|
}
|
|
6090
|
+
const EXPLORER_BASE_URLS2 = {
|
|
6091
|
+
"eip155:1": { address: "https://etherscan.io/address/", tx: "https://etherscan.io/tx/" },
|
|
6092
|
+
"eip155:137": { address: "https://polygonscan.com/address/", tx: "https://polygonscan.com/tx/" },
|
|
6093
|
+
"eip155:8453": { address: "https://basescan.org/address/", tx: "https://basescan.org/tx/" },
|
|
6094
|
+
"eip155:56": { address: "https://bscscan.com/address/", tx: "https://bscscan.com/tx/" },
|
|
6095
|
+
"eip155:41454": { address: "https://explorer.monad.xyz/address/", tx: "https://explorer.monad.xyz/tx/" },
|
|
6096
|
+
"eip155:2868": { address: "https://app.hyperliquid.xyz/explorer/address/", tx: "https://app.hyperliquid.xyz/explorer/tx/" },
|
|
6097
|
+
"bip122:000000000019d6689c085ae165831e93": { address: "https://blockstream.info/address/", tx: "https://blockstream.info/tx/" },
|
|
6098
|
+
"bip122:12a765e31ffd4059bada1e25190f6e98": { address: "https://blockchair.com/litecoin/address/", tx: "https://blockchair.com/litecoin/transaction/" },
|
|
6099
|
+
"bip122:00000000001a91e3dace36e2be3bf030": { address: "https://dogechain.info/address/", tx: "https://dogechain.info/tx/" },
|
|
6100
|
+
"bip122:000000000000000000651ef99cb9fcbe": { address: "https://blockchair.com/bitcoin-cash/address/", tx: "https://blockchair.com/bitcoin-cash/transaction/" },
|
|
6101
|
+
"bip122:000007d91d1254d60e2dd1ae58038307": { address: "https://chainz.cryptoid.info/dash/address.dws?", tx: "https://chainz.cryptoid.info/dash/tx.dws?" },
|
|
6102
|
+
"bip122:4da631f2ac1bed857bd968c67c913978": { address: "https://digiexplorer.info/address/", tx: "https://digiexplorer.info/tx/" },
|
|
6103
|
+
"bip122:00040fe8ec8471911baa1db1266ea15d": { address: "https://explorer.zcha.in/accounts/", tx: "https://explorer.zcha.in/transactions/" },
|
|
6104
|
+
"cosmos:cosmoshub-4": { address: "https://www.mintscan.io/cosmos/address/", tx: "https://www.mintscan.io/cosmos/tx/" },
|
|
6105
|
+
"cosmos:osmosis-1": { address: "https://www.mintscan.io/osmosis/address/", tx: "https://www.mintscan.io/osmosis/tx/" },
|
|
6106
|
+
"cosmos:thorchain-mainnet-v1": { address: "https://thorchain.net/address/", tx: "https://thorchain.net/tx/" },
|
|
6107
|
+
"cosmos:mayachain-mainnet-v1": { address: "https://www.mayascan.org/address/", tx: "https://www.mayascan.org/tx/" },
|
|
6108
|
+
"ripple:4109c6f2045fc7eff4cde8f9905d19c2": { address: "https://xrpscan.com/account/", tx: "https://xrpscan.com/tx/" }
|
|
6109
|
+
};
|
|
6110
|
+
const networkId = caipToNetworkId7(asset.caip);
|
|
6111
|
+
const explorerUrls = EXPLORER_BASE_URLS2[networkId];
|
|
6112
|
+
const firstPubkey = pubkeysForNetwork && pubkeysForNetwork.length > 0 ? pubkeysForNetwork[0].address || pubkeysForNetwork[0].pubkey : null;
|
|
6113
|
+
if (explorerUrls && firstPubkey) {
|
|
6114
|
+
assetInfo.explorerAddressLink = explorerUrls.address + firstPubkey;
|
|
6115
|
+
assetInfo.explorerTxLink = explorerUrls.tx;
|
|
6116
|
+
assetInfo.explorer = explorerUrls.address;
|
|
6117
|
+
}
|
|
5964
6118
|
const assetBalances = this.balances.filter((b2) => b2.caip === asset.caip);
|
|
5965
6119
|
const assetPubkeys = filterPubkeysForAsset(this.pubkeys, asset.caip, caipToNetworkId7);
|
|
6120
|
+
const criticalFields = ["decimals", "precision", "explorerAddressLink", "explorerTxLink", "explorer"];
|
|
5966
6121
|
const finalAssetContext = {
|
|
5967
6122
|
...assetInfo,
|
|
5968
|
-
...asset,
|
|
6123
|
+
...Object.fromEntries(Object.entries(asset).filter(([k, v2]) => v2 !== undefined && !criticalFields.includes(k))),
|
|
5969
6124
|
pubkeys: assetPubkeys,
|
|
5970
6125
|
balances: assetBalances
|
|
5971
6126
|
};
|
|
6127
|
+
if (assetInfo.decimals !== undefined) {
|
|
6128
|
+
finalAssetContext.decimals = assetInfo.decimals;
|
|
6129
|
+
finalAssetContext.precision = assetInfo.decimals;
|
|
6130
|
+
}
|
|
5972
6131
|
if ((!asset.priceUsd || asset.priceUsd === 0) && assetInfo.priceUsd && assetInfo.priceUsd > 0) {
|
|
5973
6132
|
finalAssetContext.priceUsd = assetInfo.priceUsd;
|
|
5974
6133
|
}
|
|
@@ -5977,15 +6136,22 @@ class SDK {
|
|
|
5977
6136
|
}
|
|
5978
6137
|
this.assetContext = finalAssetContext;
|
|
5979
6138
|
if (asset.isToken || asset.type === "token" || assetInfo.isToken || assetInfo.type === "token") {
|
|
5980
|
-
const
|
|
5981
|
-
|
|
5982
|
-
|
|
6139
|
+
const networkId2 = asset.networkId || assetInfo.networkId;
|
|
6140
|
+
try {
|
|
6141
|
+
const nativeCaip = networkIdToCaip2(networkId2);
|
|
6142
|
+
const nativeAssetInfo = assetData2[nativeCaip];
|
|
6143
|
+
const nativeSymbol = nativeAssetInfo?.symbol || "GAS";
|
|
6144
|
+
this.assetContext.nativeSymbol = nativeSymbol;
|
|
5983
6145
|
const nativeBalance = this.balances.find((b2) => b2.caip === nativeCaip);
|
|
5984
6146
|
if (nativeBalance) {
|
|
5985
6147
|
this.assetContext.nativeBalance = nativeBalance.balance || "0";
|
|
5986
6148
|
} else {
|
|
5987
6149
|
this.assetContext.nativeBalance = "0";
|
|
5988
6150
|
}
|
|
6151
|
+
} catch (error) {
|
|
6152
|
+
console.error(`[Pioneer SDK] Unable to get native asset for networkId ${networkId2}:`, error);
|
|
6153
|
+
this.assetContext.nativeSymbol = "GAS";
|
|
6154
|
+
this.assetContext.nativeBalance = "0";
|
|
5989
6155
|
}
|
|
5990
6156
|
}
|
|
5991
6157
|
if (asset.caip) {
|
|
@@ -5994,8 +6160,8 @@ class SDK {
|
|
|
5994
6160
|
this.blockchainContext = asset.networkId;
|
|
5995
6161
|
}
|
|
5996
6162
|
if (assetPubkeys && assetPubkeys.length > 0) {
|
|
5997
|
-
const
|
|
5998
|
-
const currentContextValid = this.pubkeyContext?.networks?.includes(
|
|
6163
|
+
const networkId2 = caipToNetworkId7(asset.caip || asset.networkId);
|
|
6164
|
+
const currentContextValid = this.pubkeyContext?.networks?.includes(networkId2);
|
|
5999
6165
|
if (!this.pubkeyContext || !currentContextValid) {
|
|
6000
6166
|
this.pubkeyContext = assetPubkeys[0];
|
|
6001
6167
|
console.log(tag6, "Auto-set pubkey context for network:", this.pubkeyContext.address || this.pubkeyContext.pubkey);
|
|
@@ -6046,7 +6212,7 @@ class SDK {
|
|
|
6046
6212
|
if (!pubkey)
|
|
6047
6213
|
throw Error("Invalid network! missing pubkey for network! " + asset.networkId);
|
|
6048
6214
|
const freshPriceUsd = await fetchMarketPrice(this.pioneer, asset.caip);
|
|
6049
|
-
let assetInfo = resolveAssetInfo(this.assetsMap,
|
|
6215
|
+
let assetInfo = resolveAssetInfo(this.assetsMap, this.assets, asset);
|
|
6050
6216
|
const matchingBalances = this.balances.filter((b2) => b2.caip === asset.caip);
|
|
6051
6217
|
if (matchingBalances.length > 0) {
|
|
6052
6218
|
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.
|
|
4
|
+
"version": "8.15.34",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@keepkey/keepkey-sdk": "^0.2.62",
|
|
7
|
-
"@pioneer-platform/pioneer-caip": "^9.10.
|
|
8
|
-
"@pioneer-platform/pioneer-client": "^9.10.
|
|
9
|
-
"@pioneer-platform/pioneer-coins": "^9.11.
|
|
10
|
-
"@pioneer-platform/pioneer-discovery": "^8.15.
|
|
11
|
-
"@pioneer-platform/pioneer-events": "^8.12.
|
|
7
|
+
"@pioneer-platform/pioneer-caip": "^9.10.11",
|
|
8
|
+
"@pioneer-platform/pioneer-client": "^9.10.17",
|
|
9
|
+
"@pioneer-platform/pioneer-coins": "^9.11.11",
|
|
10
|
+
"@pioneer-platform/pioneer-discovery": "^8.15.34",
|
|
11
|
+
"@pioneer-platform/pioneer-events": "^8.12.6",
|
|
12
12
|
"coinselect": "^3.1.13",
|
|
13
13
|
"eventemitter3": "^5.0.1",
|
|
14
14
|
"neotraverse": "^0.6.8",
|
package/src/charts/evm.ts
CHANGED
|
@@ -178,6 +178,25 @@ function processPortfolioBalance(
|
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
+
// CRITICAL: FAIL FAST if decimals/precision is missing
|
|
182
|
+
// Extract decimals from multiple possible sources (assetInfo, balance API response)
|
|
183
|
+
const decimals = assetInfo?.decimals ?? assetInfo?.decimal ?? balance.decimals ?? balance.decimal;
|
|
184
|
+
|
|
185
|
+
if (decimals === undefined || decimals === null) {
|
|
186
|
+
throw new Error(
|
|
187
|
+
`CRITICAL: Asset ${balance.caip} (${assetInfo?.symbol || balance.symbol || 'UNKNOWN'}) has NO decimals/precision! ` +
|
|
188
|
+
`This would cause incorrect balance calculations. ` +
|
|
189
|
+
`Asset data: ${JSON.stringify({
|
|
190
|
+
caip: balance.caip,
|
|
191
|
+
symbol: assetInfo?.symbol || balance.symbol,
|
|
192
|
+
hasAssetInfoDecimals: !!assetInfo?.decimals,
|
|
193
|
+
hasAssetInfoDecimal: !!assetInfo?.decimal,
|
|
194
|
+
hasBalanceDecimals: !!balance.decimals,
|
|
195
|
+
hasBalanceDecimal: !!balance.decimal
|
|
196
|
+
})}`
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
181
200
|
const chartBalance: ChartBalance = {
|
|
182
201
|
context,
|
|
183
202
|
chart: 'pioneer',
|
|
@@ -194,7 +213,10 @@ function processPortfolioBalance(
|
|
|
194
213
|
symbol: assetInfo?.symbol || balance.symbol || 'UNK',
|
|
195
214
|
type: balanceType,
|
|
196
215
|
token: isToken,
|
|
197
|
-
|
|
216
|
+
// CRITICAL: Set BOTH decimal (legacy) and decimals/precision (new standard)
|
|
217
|
+
decimal: decimals,
|
|
218
|
+
decimals: decimals,
|
|
219
|
+
precision: decimals,
|
|
198
220
|
balance: balance.balance.toString(),
|
|
199
221
|
price: calculatedPrice,
|
|
200
222
|
priceUsd: calculatedPrice,
|
|
@@ -244,10 +266,28 @@ function processPortfolioToken(
|
|
|
244
266
|
|
|
245
267
|
// Hydrate token with assetData
|
|
246
268
|
const tokenAssetInfo = hydrateAssetData(token.assetCaip);
|
|
247
|
-
|
|
269
|
+
|
|
248
270
|
// CRITICAL FIX: Use consistent pubkey for tokens too
|
|
249
271
|
const tokenPubkey = token.pubkey || primaryAddress;
|
|
250
272
|
|
|
273
|
+
// CRITICAL: FAIL FAST if decimals/precision is missing for tokens
|
|
274
|
+
const tokenDecimals = tokenAssetInfo?.decimals ?? tokenAssetInfo?.decimal ?? token.token?.decimals ?? token.token?.decimal;
|
|
275
|
+
|
|
276
|
+
if (tokenDecimals === undefined || tokenDecimals === null) {
|
|
277
|
+
throw new Error(
|
|
278
|
+
`CRITICAL: Token ${token.assetCaip} (${tokenAssetInfo?.symbol || token.token?.symbol || 'UNKNOWN'}) has NO decimals/precision! ` +
|
|
279
|
+
`This would cause incorrect balance calculations. ` +
|
|
280
|
+
`Token data: ${JSON.stringify({
|
|
281
|
+
caip: token.assetCaip,
|
|
282
|
+
symbol: tokenAssetInfo?.symbol || token.token?.symbol,
|
|
283
|
+
hasAssetInfoDecimals: !!tokenAssetInfo?.decimals,
|
|
284
|
+
hasAssetInfoDecimal: !!tokenAssetInfo?.decimal,
|
|
285
|
+
hasTokenDecimals: !!token.token?.decimals,
|
|
286
|
+
hasTokenDecimal: !!token.token?.decimal
|
|
287
|
+
})}`
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
251
291
|
const chartBalance: ChartBalance = {
|
|
252
292
|
context,
|
|
253
293
|
chart: 'pioneer',
|
|
@@ -263,7 +303,10 @@ function processPortfolioToken(
|
|
|
263
303
|
symbol: tokenAssetInfo?.symbol || token.token?.symbol || 'UNK',
|
|
264
304
|
type: tokenAssetInfo?.type || 'token',
|
|
265
305
|
token: true, // Tokens from portfolio.tokens are always tokens
|
|
266
|
-
|
|
306
|
+
// CRITICAL: Set ALL decimal fields for compatibility
|
|
307
|
+
decimal: tokenDecimals,
|
|
308
|
+
decimals: tokenDecimals,
|
|
309
|
+
precision: tokenDecimals,
|
|
267
310
|
balance: token.token?.balance?.toString() || '0',
|
|
268
311
|
priceUsd: token.token?.price || 0,
|
|
269
312
|
valueUsd: token.token?.balanceUSD || 0,
|
package/src/charts/maya.ts
CHANGED
|
@@ -79,7 +79,26 @@ export async function getMayaCharts(
|
|
|
79
79
|
|
|
80
80
|
// CACAO is the native asset, MAYA is a token on the Maya chain
|
|
81
81
|
const isToken = mayaBalance.caip.includes('/denom:') && !mayaBalance.caip.endsWith('/denom:cacao');
|
|
82
|
-
|
|
82
|
+
|
|
83
|
+
// CRITICAL: FAIL FAST if decimals/precision is missing
|
|
84
|
+
// Extract decimals from multiple possible sources (assetInfo, balance API response)
|
|
85
|
+
const decimals = mayaAssetInfo?.decimals ?? mayaAssetInfo?.decimal ?? mayaBalance.decimals ?? mayaBalance.decimal;
|
|
86
|
+
|
|
87
|
+
if (decimals === undefined || decimals === null) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
`CRITICAL: Asset ${mayaBalance.caip} (${mayaAssetInfo?.symbol || 'MAYA'}) has NO decimals/precision! ` +
|
|
90
|
+
`This would cause incorrect balance calculations. ` +
|
|
91
|
+
`Asset data: ${JSON.stringify({
|
|
92
|
+
caip: mayaBalance.caip,
|
|
93
|
+
symbol: mayaAssetInfo?.symbol,
|
|
94
|
+
hasAssetInfoDecimals: !!mayaAssetInfo?.decimals,
|
|
95
|
+
hasAssetInfoDecimal: !!mayaAssetInfo?.decimal,
|
|
96
|
+
hasBalanceDecimals: !!mayaBalance.decimals,
|
|
97
|
+
hasBalanceDecimal: !!mayaBalance.decimal
|
|
98
|
+
})}`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
83
102
|
const mayaTokenBalance: ChartBalance = {
|
|
84
103
|
context,
|
|
85
104
|
chart: 'pioneer',
|
|
@@ -95,7 +114,10 @@ export async function getMayaCharts(
|
|
|
95
114
|
symbol: mayaAssetInfo?.symbol || 'MAYA',
|
|
96
115
|
type: mayaAssetInfo?.type || 'token',
|
|
97
116
|
token: isToken, // MAYA is a token, CACAO is the native asset
|
|
98
|
-
|
|
117
|
+
// CRITICAL: Set BOTH decimal (legacy) and decimals/precision (new standard)
|
|
118
|
+
decimal: decimals,
|
|
119
|
+
decimals: decimals,
|
|
120
|
+
precision: decimals,
|
|
99
121
|
balance: mayaBalance.balance?.toString() || '0',
|
|
100
122
|
priceUsd: parseFloat(mayaBalance.priceUsd) || 0,
|
|
101
123
|
valueUsd: parseFloat(mayaBalance.valueUsd) || 0,
|
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,
|
|
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
|
-
|
|
1896
|
-
|
|
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,
|
|
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,43 @@ 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
|
+
|
|
348
|
+
// CRITICAL: NEVER default decimals to 8 - this causes incorrect balance calculations
|
|
349
|
+
// If decimals is missing, the asset data is incomplete and we should FAIL FAST
|
|
350
|
+
const decimals = assetInfo.decimals ?? balance.decimals;
|
|
351
|
+
if (decimals === undefined || decimals === null) {
|
|
352
|
+
throw new Error(
|
|
353
|
+
`CRITICAL: Asset ${balance.caip} (${assetInfo.symbol || 'UNKNOWN'}) has NO decimals/precision! ` +
|
|
354
|
+
`AssetInfo is incomplete. This would cause incorrect balance calculations. ` +
|
|
355
|
+
`Asset data: ${JSON.stringify({ caip: balance.caip, symbol: assetInfo.symbol, hasDecimals: !!assetInfo.decimals })}`
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
243
359
|
Object.assign(balance, assetInfo, {
|
|
244
360
|
type: balance.type || assetInfo.type,
|
|
245
361
|
isNative: balance.isNative ?? assetInfo.isNative,
|
|
246
|
-
networkId
|
|
362
|
+
networkId,
|
|
247
363
|
icon: assetInfo.icon || 'https://pioneers.dev/coins/etherum.png',
|
|
248
364
|
identifier: `${balance.caip}:${balance.pubkey}`,
|
|
249
365
|
updated: Date.now(),
|
|
250
366
|
color: assetInfo.color,
|
|
367
|
+
// CRITICAL: Use validated decimals
|
|
368
|
+
decimals,
|
|
369
|
+
precision: decimals, // Also set precision for compatibility
|
|
370
|
+
// Add explorer links
|
|
371
|
+
explorerAddressLink,
|
|
372
|
+
explorerTxLink,
|
|
373
|
+
explorer: explorerUrls?.address, // Base explorer URL
|
|
251
374
|
// Ensure these are numbers (redundant but explicit)
|
|
252
375
|
valueUsd: balance.valueUsd,
|
|
253
376
|
priceUsd: balance.priceUsd,
|