@pioneer-platform/pioneer-sdk 8.15.31 → 8.15.32
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 +98 -37
- package/dist/index.es.js +98 -37
- package/dist/index.js +98 -37
- package/package.json +6 -6
- package/src/fees/index.ts +1 -0
- package/src/index.ts +19 -0
- package/src/txbuilder/createUnsignedUxtoTx.ts +20 -3
- package/src/utils/build-dashboard.ts +86 -46
- package/src/utils/portfolio-helpers.ts +23 -0
package/dist/index.cjs
CHANGED
|
@@ -2639,7 +2639,21 @@ async function createUnsignedUxtoTx(caip, to, amount, memo, pubkeys, pioneer, pu
|
|
|
2639
2639
|
}
|
|
2640
2640
|
} catch (error) {
|
|
2641
2641
|
console.error(`${tag}: Failed to get fee rates from Pioneer API:`, error.message || error);
|
|
2642
|
-
|
|
2642
|
+
const defaultFees = {
|
|
2643
|
+
"bip122:000000000019d6689c085ae165831e93": { slow: 3, average: 5, fast: 10, fastest: 15 },
|
|
2644
|
+
"bip122:12a765e31ffd4059bada1e25190f6e98": { slow: 2, average: 3, fast: 5, fastest: 10 },
|
|
2645
|
+
"bip122:00000000001a91e3dace36e2be3bf030": { slow: 1, average: 1, fast: 2, fastest: 3 },
|
|
2646
|
+
"bip122:000000000000000000651ef99cb9fcbe": { slow: 1, average: 1, fast: 2, fastest: 3 },
|
|
2647
|
+
"bip122:000007d91d1254d60e2dd1ae58038307": { slow: 1, average: 1, fast: 2, fastest: 3 },
|
|
2648
|
+
"bip122:4da631f2ac1bed857bd968c67c913978": { slow: 75, average: 80, fast: 100, fastest: 120 },
|
|
2649
|
+
"bip122:00040fe8ec8471911baa1db1266ea15d": { slow: 1, average: 1, fast: 2, fastest: 3 }
|
|
2650
|
+
};
|
|
2651
|
+
if (defaultFees[networkId]) {
|
|
2652
|
+
console.warn(`${tag}: Using default fee rates for ${networkId}`);
|
|
2653
|
+
feeRateFromNode = defaultFees[networkId];
|
|
2654
|
+
} else {
|
|
2655
|
+
throw new Error(`Unable to get fee rate for network ${networkId}: ${error.message || "API unavailable"}`);
|
|
2656
|
+
}
|
|
2643
2657
|
}
|
|
2644
2658
|
}
|
|
2645
2659
|
let effectiveFeeRate;
|
|
@@ -3355,6 +3369,7 @@ function getNetworkName(networkId) {
|
|
|
3355
3369
|
"bip122:00000000001a91e3dace36e2be3bf030": "Dogecoin",
|
|
3356
3370
|
"bip122:000000000000000000651ef99cb9fcbe": "Bitcoin Cash",
|
|
3357
3371
|
"bip122:000007d91d1254d60e2dd1ae58038307": "Dash",
|
|
3372
|
+
"bip122:00040fe8ec8471911baa1db1266ea15d": "Zcash",
|
|
3358
3373
|
"bip122:4da631f2ac1bed857bd968c67c913978": "DigiByte",
|
|
3359
3374
|
"eip155:1": "Ethereum",
|
|
3360
3375
|
"eip155:56": "BNB Smart Chain",
|
|
@@ -3926,7 +3941,7 @@ function buildDashboardFromBalances(balances, blockchains, assetsMap) {
|
|
|
3926
3941
|
totalValueUsd: 0,
|
|
3927
3942
|
networkPercentages: []
|
|
3928
3943
|
};
|
|
3929
|
-
let
|
|
3944
|
+
let totalPortfolioValueCents = 0;
|
|
3930
3945
|
const networksTemp = [];
|
|
3931
3946
|
for (const blockchain of blockchains) {
|
|
3932
3947
|
const filteredBalances = balances.filter((b) => {
|
|
@@ -3934,50 +3949,46 @@ function buildDashboardFromBalances(balances, blockchains, assetsMap) {
|
|
|
3934
3949
|
return networkId === blockchain || blockchain === "eip155:*" && networkId.startsWith("eip155:");
|
|
3935
3950
|
});
|
|
3936
3951
|
const balanceMap = new Map;
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
} else {
|
|
3953
|
-
balances2.forEach((balance) => {
|
|
3954
|
-
const key = `${balance.caip}_${balance.pubkey || "default"}`;
|
|
3955
|
-
balanceMap.set(key, balance);
|
|
3956
|
-
});
|
|
3957
|
-
}
|
|
3952
|
+
filteredBalances.forEach((balance) => {
|
|
3953
|
+
const key = `${balance.caip}_${balance.pubkey || "default"}`;
|
|
3954
|
+
if (!balanceMap.has(key) || parseFloat(balance.valueUsd || "0") > parseFloat(balanceMap.get(key).valueUsd || "0")) {
|
|
3955
|
+
balanceMap.set(key, balance);
|
|
3956
|
+
} else {
|
|
3957
|
+
const existing = balanceMap.get(key);
|
|
3958
|
+
const existingValue = parseFloat(existing.valueUsd || "0");
|
|
3959
|
+
const newValue = parseFloat(balance.valueUsd || "0");
|
|
3960
|
+
console.log(`⚠️ [BUILD-DASHBOARD] ${blockchain} dedup skipped (same caip+pubkey):`, {
|
|
3961
|
+
caip: balance.caip,
|
|
3962
|
+
pubkey: (balance.pubkey || "default").substring(0, 20),
|
|
3963
|
+
existing: existingValue,
|
|
3964
|
+
new: newValue,
|
|
3965
|
+
diff: Math.abs(existingValue - newValue)
|
|
3966
|
+
});
|
|
3958
3967
|
}
|
|
3959
|
-
}
|
|
3960
|
-
filteredBalances.forEach((balance) => {
|
|
3961
|
-
const key = `${balance.caip}_${balance.pubkey || "default"}`;
|
|
3962
|
-
if (!balanceMap.has(key) || parseFloat(balance.valueUsd || "0") > parseFloat(balanceMap.get(key).valueUsd || "0")) {
|
|
3963
|
-
balanceMap.set(key, balance);
|
|
3964
|
-
}
|
|
3965
|
-
});
|
|
3966
|
-
}
|
|
3968
|
+
});
|
|
3967
3969
|
const networkBalances = Array.from(balanceMap.values());
|
|
3968
|
-
const
|
|
3969
|
-
|
|
3970
|
+
const networkTotalCents = networkBalances.reduce((sumCents, balance, idx) => {
|
|
3971
|
+
let valueUsd = typeof balance.valueUsd === "string" ? parseFloat(balance.valueUsd) : balance.valueUsd || 0;
|
|
3972
|
+
if (isNaN(valueUsd)) {
|
|
3973
|
+
console.warn(`⚠️ [BUILD-DASHBOARD] NaN value detected for ${balance.caip}:`, balance.valueUsd);
|
|
3974
|
+
return sumCents;
|
|
3975
|
+
}
|
|
3976
|
+
balance.valueUsd = valueUsd;
|
|
3977
|
+
const valueCents = Math.round(valueUsd * 100 * 1000) / 1000;
|
|
3970
3978
|
if (idx < 2) {
|
|
3971
3979
|
console.log(`\uD83D\uDCB0 [BUILD-DASHBOARD] ${blockchain} balance #${idx}:`, {
|
|
3972
3980
|
caip: balance.caip,
|
|
3973
3981
|
balance: balance.balance,
|
|
3974
3982
|
valueUsd: balance.valueUsd,
|
|
3983
|
+
valueUsdType: typeof balance.valueUsd,
|
|
3975
3984
|
parsedValueUsd: valueUsd,
|
|
3976
|
-
|
|
3985
|
+
valueCents,
|
|
3986
|
+
runningSumCents: sumCents + valueCents
|
|
3977
3987
|
});
|
|
3978
3988
|
}
|
|
3979
|
-
return
|
|
3989
|
+
return sumCents + valueCents;
|
|
3980
3990
|
}, 0);
|
|
3991
|
+
const networkTotal = networkTotalCents / 100;
|
|
3981
3992
|
console.log(`\uD83D\uDCB0 [BUILD-DASHBOARD] ${blockchain} totals:`, {
|
|
3982
3993
|
balancesCount: networkBalances.length,
|
|
3983
3994
|
networkTotal,
|
|
@@ -4000,8 +4011,9 @@ function buildDashboardFromBalances(balances, blockchains, assetsMap) {
|
|
|
4000
4011
|
color: gasAsset?.color || assetInfo?.color || null,
|
|
4001
4012
|
totalNativeBalance
|
|
4002
4013
|
});
|
|
4003
|
-
|
|
4014
|
+
totalPortfolioValueCents += networkTotalCents;
|
|
4004
4015
|
}
|
|
4016
|
+
const totalPortfolioValue = totalPortfolioValueCents / 100;
|
|
4005
4017
|
dashboardData.networks = networksTemp.sort((a, b) => b.totalValueUsd - a.totalValueUsd);
|
|
4006
4018
|
dashboardData.totalValueUsd = totalPortfolioValue;
|
|
4007
4019
|
dashboardData.networkPercentages = dashboardData.networks.map((network) => ({
|
|
@@ -4009,6 +4021,26 @@ function buildDashboardFromBalances(balances, blockchains, assetsMap) {
|
|
|
4009
4021
|
percentage: totalPortfolioValue > 0 ? Number((network.totalValueUsd / totalPortfolioValue * 100).toFixed(2)) : 0
|
|
4010
4022
|
})).filter((entry) => entry.percentage > 0);
|
|
4011
4023
|
console.log(`[FAST DASHBOARD] ✅ Built dashboard: ${dashboardData.networks.length} networks, $${totalPortfolioValue.toFixed(2)} total`);
|
|
4024
|
+
const inputBalancesTotal = balances.reduce((sum, b) => {
|
|
4025
|
+
const value = typeof b.valueUsd === "string" ? parseFloat(b.valueUsd) : b.valueUsd || 0;
|
|
4026
|
+
return sum + (isNaN(value) ? 0 : value);
|
|
4027
|
+
}, 0);
|
|
4028
|
+
const difference = Math.abs(inputBalancesTotal - totalPortfolioValue);
|
|
4029
|
+
if (difference > 0.01) {
|
|
4030
|
+
console.error(`\uD83D\uDEA8 [BUILD-DASHBOARD] BALANCE MISMATCH DETECTED!`);
|
|
4031
|
+
console.error(` Input balances total: $${inputBalancesTotal.toFixed(2)} USD`);
|
|
4032
|
+
console.error(` Dashboard total: $${totalPortfolioValue.toFixed(2)} USD`);
|
|
4033
|
+
console.error(` Difference: $${difference.toFixed(2)} USD`);
|
|
4034
|
+
console.error(` This indicates lost balances during dashboard aggregation!`);
|
|
4035
|
+
console.error(` Network breakdown:`);
|
|
4036
|
+
dashboardData.networks.forEach((network) => {
|
|
4037
|
+
console.error(` ${network.networkId}: $${network.totalValueUsd.toFixed(2)}`);
|
|
4038
|
+
});
|
|
4039
|
+
} else if (difference > 0.001) {
|
|
4040
|
+
console.warn(`⚠️ [BUILD-DASHBOARD] Minor balance rounding difference: $${difference.toFixed(4)} USD`);
|
|
4041
|
+
} else {
|
|
4042
|
+
console.log(`✅ [BUILD-DASHBOARD] Balance reconciliation passed (diff: $${difference.toFixed(4)})`);
|
|
4043
|
+
}
|
|
4012
4044
|
return dashboardData;
|
|
4013
4045
|
}
|
|
4014
4046
|
|
|
@@ -4281,6 +4313,19 @@ function enrichBalancesWithAssetInfo(balances, assetsMap, caipToNetworkId7) {
|
|
|
4281
4313
|
console.warn(tag, `Missing AssetInfo for CAIP: "${balance.caip}" - skipping this balance`);
|
|
4282
4314
|
continue;
|
|
4283
4315
|
}
|
|
4316
|
+
if (typeof balance.valueUsd === "string") {
|
|
4317
|
+
balance.valueUsd = parseFloat(balance.valueUsd) || 0;
|
|
4318
|
+
} else if (typeof balance.valueUsd !== "number") {
|
|
4319
|
+
balance.valueUsd = 0;
|
|
4320
|
+
}
|
|
4321
|
+
if (typeof balance.balance === "string") {
|
|
4322
|
+
balance.balance = balance.balance;
|
|
4323
|
+
}
|
|
4324
|
+
if (typeof balance.priceUsd === "string") {
|
|
4325
|
+
balance.priceUsd = parseFloat(balance.priceUsd) || 0;
|
|
4326
|
+
} else if (typeof balance.priceUsd !== "number") {
|
|
4327
|
+
balance.priceUsd = 0;
|
|
4328
|
+
}
|
|
4284
4329
|
Object.assign(balance, assetInfo, {
|
|
4285
4330
|
type: balance.type || assetInfo.type,
|
|
4286
4331
|
isNative: balance.isNative ?? assetInfo.isNative,
|
|
@@ -4288,7 +4333,9 @@ function enrichBalancesWithAssetInfo(balances, assetsMap, caipToNetworkId7) {
|
|
|
4288
4333
|
icon: assetInfo.icon || "https://pioneers.dev/coins/etherum.png",
|
|
4289
4334
|
identifier: `${balance.caip}:${balance.pubkey}`,
|
|
4290
4335
|
updated: Date.now(),
|
|
4291
|
-
color: assetInfo.color
|
|
4336
|
+
color: assetInfo.color,
|
|
4337
|
+
valueUsd: balance.valueUsd,
|
|
4338
|
+
priceUsd: balance.priceUsd
|
|
4292
4339
|
});
|
|
4293
4340
|
enrichedBalances.push(balance);
|
|
4294
4341
|
}
|
|
@@ -4823,6 +4870,20 @@ class SDK {
|
|
|
4823
4870
|
this.balances[index] = balance;
|
|
4824
4871
|
}
|
|
4825
4872
|
this.events.emit("BALANCE_UPDATE", balance);
|
|
4873
|
+
console.log(tag7, "\uD83D\uDD04 Rebuilding dashboard after balance update...");
|
|
4874
|
+
const previousTotal = this.dashboard?.totalValueUsd || 0;
|
|
4875
|
+
this.dashboard = this.buildDashboardFromBalances();
|
|
4876
|
+
const newTotal = this.dashboard?.totalValueUsd || 0;
|
|
4877
|
+
if (Math.abs(newTotal - previousTotal) > 0.01) {
|
|
4878
|
+
console.log(tag7, `\uD83D\uDCB0 Portfolio value changed: $${previousTotal.toFixed(2)} → $${newTotal.toFixed(2)}`);
|
|
4879
|
+
}
|
|
4880
|
+
this.events.emit("DASHBOARD_UPDATE", {
|
|
4881
|
+
dashboard: this.dashboard,
|
|
4882
|
+
trigger: "balance_update",
|
|
4883
|
+
affectedAsset: balance.caip,
|
|
4884
|
+
previousTotal,
|
|
4885
|
+
newTotal
|
|
4886
|
+
});
|
|
4826
4887
|
} catch (e) {
|
|
4827
4888
|
console.error(tag7, "Error processing balance update:", e);
|
|
4828
4889
|
}
|
package/dist/index.es.js
CHANGED
|
@@ -2823,7 +2823,21 @@ async function createUnsignedUxtoTx(caip, to, amount, memo, pubkeys, pioneer, pu
|
|
|
2823
2823
|
}
|
|
2824
2824
|
} catch (error) {
|
|
2825
2825
|
console.error(`${tag}: Failed to get fee rates from Pioneer API:`, error.message || error);
|
|
2826
|
-
|
|
2826
|
+
const defaultFees = {
|
|
2827
|
+
"bip122:000000000019d6689c085ae165831e93": { slow: 3, average: 5, fast: 10, fastest: 15 },
|
|
2828
|
+
"bip122:12a765e31ffd4059bada1e25190f6e98": { slow: 2, average: 3, fast: 5, fastest: 10 },
|
|
2829
|
+
"bip122:00000000001a91e3dace36e2be3bf030": { slow: 1, average: 1, fast: 2, fastest: 3 },
|
|
2830
|
+
"bip122:000000000000000000651ef99cb9fcbe": { slow: 1, average: 1, fast: 2, fastest: 3 },
|
|
2831
|
+
"bip122:000007d91d1254d60e2dd1ae58038307": { slow: 1, average: 1, fast: 2, fastest: 3 },
|
|
2832
|
+
"bip122:4da631f2ac1bed857bd968c67c913978": { slow: 75, average: 80, fast: 100, fastest: 120 },
|
|
2833
|
+
"bip122:00040fe8ec8471911baa1db1266ea15d": { slow: 1, average: 1, fast: 2, fastest: 3 }
|
|
2834
|
+
};
|
|
2835
|
+
if (defaultFees[networkId]) {
|
|
2836
|
+
console.warn(`${tag}: Using default fee rates for ${networkId}`);
|
|
2837
|
+
feeRateFromNode = defaultFees[networkId];
|
|
2838
|
+
} else {
|
|
2839
|
+
throw new Error(`Unable to get fee rate for network ${networkId}: ${error.message || "API unavailable"}`);
|
|
2840
|
+
}
|
|
2827
2841
|
}
|
|
2828
2842
|
}
|
|
2829
2843
|
let effectiveFeeRate;
|
|
@@ -3539,6 +3553,7 @@ function getNetworkName(networkId) {
|
|
|
3539
3553
|
"bip122:00000000001a91e3dace36e2be3bf030": "Dogecoin",
|
|
3540
3554
|
"bip122:000000000000000000651ef99cb9fcbe": "Bitcoin Cash",
|
|
3541
3555
|
"bip122:000007d91d1254d60e2dd1ae58038307": "Dash",
|
|
3556
|
+
"bip122:00040fe8ec8471911baa1db1266ea15d": "Zcash",
|
|
3542
3557
|
"bip122:4da631f2ac1bed857bd968c67c913978": "DigiByte",
|
|
3543
3558
|
"eip155:1": "Ethereum",
|
|
3544
3559
|
"eip155:56": "BNB Smart Chain",
|
|
@@ -4110,7 +4125,7 @@ function buildDashboardFromBalances(balances, blockchains, assetsMap) {
|
|
|
4110
4125
|
totalValueUsd: 0,
|
|
4111
4126
|
networkPercentages: []
|
|
4112
4127
|
};
|
|
4113
|
-
let
|
|
4128
|
+
let totalPortfolioValueCents = 0;
|
|
4114
4129
|
const networksTemp = [];
|
|
4115
4130
|
for (const blockchain of blockchains) {
|
|
4116
4131
|
const filteredBalances = balances.filter((b2) => {
|
|
@@ -4118,50 +4133,46 @@ function buildDashboardFromBalances(balances, blockchains, assetsMap) {
|
|
|
4118
4133
|
return networkId === blockchain || blockchain === "eip155:*" && networkId.startsWith("eip155:");
|
|
4119
4134
|
});
|
|
4120
4135
|
const balanceMap = new Map;
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
} else {
|
|
4137
|
-
balances2.forEach((balance) => {
|
|
4138
|
-
const key = `${balance.caip}_${balance.pubkey || "default"}`;
|
|
4139
|
-
balanceMap.set(key, balance);
|
|
4140
|
-
});
|
|
4141
|
-
}
|
|
4136
|
+
filteredBalances.forEach((balance) => {
|
|
4137
|
+
const key = `${balance.caip}_${balance.pubkey || "default"}`;
|
|
4138
|
+
if (!balanceMap.has(key) || parseFloat(balance.valueUsd || "0") > parseFloat(balanceMap.get(key).valueUsd || "0")) {
|
|
4139
|
+
balanceMap.set(key, balance);
|
|
4140
|
+
} else {
|
|
4141
|
+
const existing = balanceMap.get(key);
|
|
4142
|
+
const existingValue = parseFloat(existing.valueUsd || "0");
|
|
4143
|
+
const newValue = parseFloat(balance.valueUsd || "0");
|
|
4144
|
+
console.log(`⚠️ [BUILD-DASHBOARD] ${blockchain} dedup skipped (same caip+pubkey):`, {
|
|
4145
|
+
caip: balance.caip,
|
|
4146
|
+
pubkey: (balance.pubkey || "default").substring(0, 20),
|
|
4147
|
+
existing: existingValue,
|
|
4148
|
+
new: newValue,
|
|
4149
|
+
diff: Math.abs(existingValue - newValue)
|
|
4150
|
+
});
|
|
4142
4151
|
}
|
|
4143
|
-
}
|
|
4144
|
-
filteredBalances.forEach((balance) => {
|
|
4145
|
-
const key = `${balance.caip}_${balance.pubkey || "default"}`;
|
|
4146
|
-
if (!balanceMap.has(key) || parseFloat(balance.valueUsd || "0") > parseFloat(balanceMap.get(key).valueUsd || "0")) {
|
|
4147
|
-
balanceMap.set(key, balance);
|
|
4148
|
-
}
|
|
4149
|
-
});
|
|
4150
|
-
}
|
|
4152
|
+
});
|
|
4151
4153
|
const networkBalances = Array.from(balanceMap.values());
|
|
4152
|
-
const
|
|
4153
|
-
|
|
4154
|
+
const networkTotalCents = networkBalances.reduce((sumCents, balance, idx) => {
|
|
4155
|
+
let valueUsd = typeof balance.valueUsd === "string" ? parseFloat(balance.valueUsd) : balance.valueUsd || 0;
|
|
4156
|
+
if (isNaN(valueUsd)) {
|
|
4157
|
+
console.warn(`⚠️ [BUILD-DASHBOARD] NaN value detected for ${balance.caip}:`, balance.valueUsd);
|
|
4158
|
+
return sumCents;
|
|
4159
|
+
}
|
|
4160
|
+
balance.valueUsd = valueUsd;
|
|
4161
|
+
const valueCents = Math.round(valueUsd * 100 * 1000) / 1000;
|
|
4154
4162
|
if (idx < 2) {
|
|
4155
4163
|
console.log(`\uD83D\uDCB0 [BUILD-DASHBOARD] ${blockchain} balance #${idx}:`, {
|
|
4156
4164
|
caip: balance.caip,
|
|
4157
4165
|
balance: balance.balance,
|
|
4158
4166
|
valueUsd: balance.valueUsd,
|
|
4167
|
+
valueUsdType: typeof balance.valueUsd,
|
|
4159
4168
|
parsedValueUsd: valueUsd,
|
|
4160
|
-
|
|
4169
|
+
valueCents,
|
|
4170
|
+
runningSumCents: sumCents + valueCents
|
|
4161
4171
|
});
|
|
4162
4172
|
}
|
|
4163
|
-
return
|
|
4173
|
+
return sumCents + valueCents;
|
|
4164
4174
|
}, 0);
|
|
4175
|
+
const networkTotal = networkTotalCents / 100;
|
|
4165
4176
|
console.log(`\uD83D\uDCB0 [BUILD-DASHBOARD] ${blockchain} totals:`, {
|
|
4166
4177
|
balancesCount: networkBalances.length,
|
|
4167
4178
|
networkTotal,
|
|
@@ -4184,8 +4195,9 @@ function buildDashboardFromBalances(balances, blockchains, assetsMap) {
|
|
|
4184
4195
|
color: gasAsset?.color || assetInfo?.color || null,
|
|
4185
4196
|
totalNativeBalance
|
|
4186
4197
|
});
|
|
4187
|
-
|
|
4198
|
+
totalPortfolioValueCents += networkTotalCents;
|
|
4188
4199
|
}
|
|
4200
|
+
const totalPortfolioValue = totalPortfolioValueCents / 100;
|
|
4189
4201
|
dashboardData.networks = networksTemp.sort((a2, b2) => b2.totalValueUsd - a2.totalValueUsd);
|
|
4190
4202
|
dashboardData.totalValueUsd = totalPortfolioValue;
|
|
4191
4203
|
dashboardData.networkPercentages = dashboardData.networks.map((network) => ({
|
|
@@ -4193,6 +4205,26 @@ function buildDashboardFromBalances(balances, blockchains, assetsMap) {
|
|
|
4193
4205
|
percentage: totalPortfolioValue > 0 ? Number((network.totalValueUsd / totalPortfolioValue * 100).toFixed(2)) : 0
|
|
4194
4206
|
})).filter((entry) => entry.percentage > 0);
|
|
4195
4207
|
console.log(`[FAST DASHBOARD] ✅ Built dashboard: ${dashboardData.networks.length} networks, $${totalPortfolioValue.toFixed(2)} total`);
|
|
4208
|
+
const inputBalancesTotal = balances.reduce((sum, b2) => {
|
|
4209
|
+
const value = typeof b2.valueUsd === "string" ? parseFloat(b2.valueUsd) : b2.valueUsd || 0;
|
|
4210
|
+
return sum + (isNaN(value) ? 0 : value);
|
|
4211
|
+
}, 0);
|
|
4212
|
+
const difference = Math.abs(inputBalancesTotal - totalPortfolioValue);
|
|
4213
|
+
if (difference > 0.01) {
|
|
4214
|
+
console.error(`\uD83D\uDEA8 [BUILD-DASHBOARD] BALANCE MISMATCH DETECTED!`);
|
|
4215
|
+
console.error(` Input balances total: $${inputBalancesTotal.toFixed(2)} USD`);
|
|
4216
|
+
console.error(` Dashboard total: $${totalPortfolioValue.toFixed(2)} USD`);
|
|
4217
|
+
console.error(` Difference: $${difference.toFixed(2)} USD`);
|
|
4218
|
+
console.error(` This indicates lost balances during dashboard aggregation!`);
|
|
4219
|
+
console.error(` Network breakdown:`);
|
|
4220
|
+
dashboardData.networks.forEach((network) => {
|
|
4221
|
+
console.error(` ${network.networkId}: $${network.totalValueUsd.toFixed(2)}`);
|
|
4222
|
+
});
|
|
4223
|
+
} else if (difference > 0.001) {
|
|
4224
|
+
console.warn(`⚠️ [BUILD-DASHBOARD] Minor balance rounding difference: $${difference.toFixed(4)} USD`);
|
|
4225
|
+
} else {
|
|
4226
|
+
console.log(`✅ [BUILD-DASHBOARD] Balance reconciliation passed (diff: $${difference.toFixed(4)})`);
|
|
4227
|
+
}
|
|
4196
4228
|
return dashboardData;
|
|
4197
4229
|
}
|
|
4198
4230
|
|
|
@@ -4465,6 +4497,19 @@ function enrichBalancesWithAssetInfo(balances, assetsMap, caipToNetworkId7) {
|
|
|
4465
4497
|
console.warn(tag, `Missing AssetInfo for CAIP: "${balance.caip}" - skipping this balance`);
|
|
4466
4498
|
continue;
|
|
4467
4499
|
}
|
|
4500
|
+
if (typeof balance.valueUsd === "string") {
|
|
4501
|
+
balance.valueUsd = parseFloat(balance.valueUsd) || 0;
|
|
4502
|
+
} else if (typeof balance.valueUsd !== "number") {
|
|
4503
|
+
balance.valueUsd = 0;
|
|
4504
|
+
}
|
|
4505
|
+
if (typeof balance.balance === "string") {
|
|
4506
|
+
balance.balance = balance.balance;
|
|
4507
|
+
}
|
|
4508
|
+
if (typeof balance.priceUsd === "string") {
|
|
4509
|
+
balance.priceUsd = parseFloat(balance.priceUsd) || 0;
|
|
4510
|
+
} else if (typeof balance.priceUsd !== "number") {
|
|
4511
|
+
balance.priceUsd = 0;
|
|
4512
|
+
}
|
|
4468
4513
|
Object.assign(balance, assetInfo, {
|
|
4469
4514
|
type: balance.type || assetInfo.type,
|
|
4470
4515
|
isNative: balance.isNative ?? assetInfo.isNative,
|
|
@@ -4472,7 +4517,9 @@ function enrichBalancesWithAssetInfo(balances, assetsMap, caipToNetworkId7) {
|
|
|
4472
4517
|
icon: assetInfo.icon || "https://pioneers.dev/coins/etherum.png",
|
|
4473
4518
|
identifier: `${balance.caip}:${balance.pubkey}`,
|
|
4474
4519
|
updated: Date.now(),
|
|
4475
|
-
color: assetInfo.color
|
|
4520
|
+
color: assetInfo.color,
|
|
4521
|
+
valueUsd: balance.valueUsd,
|
|
4522
|
+
priceUsd: balance.priceUsd
|
|
4476
4523
|
});
|
|
4477
4524
|
enrichedBalances.push(balance);
|
|
4478
4525
|
}
|
|
@@ -5007,6 +5054,20 @@ class SDK {
|
|
|
5007
5054
|
this.balances[index] = balance;
|
|
5008
5055
|
}
|
|
5009
5056
|
this.events.emit("BALANCE_UPDATE", balance);
|
|
5057
|
+
console.log(tag7, "\uD83D\uDD04 Rebuilding dashboard after balance update...");
|
|
5058
|
+
const previousTotal = this.dashboard?.totalValueUsd || 0;
|
|
5059
|
+
this.dashboard = this.buildDashboardFromBalances();
|
|
5060
|
+
const newTotal = this.dashboard?.totalValueUsd || 0;
|
|
5061
|
+
if (Math.abs(newTotal - previousTotal) > 0.01) {
|
|
5062
|
+
console.log(tag7, `\uD83D\uDCB0 Portfolio value changed: $${previousTotal.toFixed(2)} → $${newTotal.toFixed(2)}`);
|
|
5063
|
+
}
|
|
5064
|
+
this.events.emit("DASHBOARD_UPDATE", {
|
|
5065
|
+
dashboard: this.dashboard,
|
|
5066
|
+
trigger: "balance_update",
|
|
5067
|
+
affectedAsset: balance.caip,
|
|
5068
|
+
previousTotal,
|
|
5069
|
+
newTotal
|
|
5070
|
+
});
|
|
5010
5071
|
} catch (e) {
|
|
5011
5072
|
console.error(tag7, "Error processing balance update:", e);
|
|
5012
5073
|
}
|
package/dist/index.js
CHANGED
|
@@ -2823,7 +2823,21 @@ async function createUnsignedUxtoTx(caip, to, amount, memo, pubkeys, pioneer, pu
|
|
|
2823
2823
|
}
|
|
2824
2824
|
} catch (error) {
|
|
2825
2825
|
console.error(`${tag}: Failed to get fee rates from Pioneer API:`, error.message || error);
|
|
2826
|
-
|
|
2826
|
+
const defaultFees = {
|
|
2827
|
+
"bip122:000000000019d6689c085ae165831e93": { slow: 3, average: 5, fast: 10, fastest: 15 },
|
|
2828
|
+
"bip122:12a765e31ffd4059bada1e25190f6e98": { slow: 2, average: 3, fast: 5, fastest: 10 },
|
|
2829
|
+
"bip122:00000000001a91e3dace36e2be3bf030": { slow: 1, average: 1, fast: 2, fastest: 3 },
|
|
2830
|
+
"bip122:000000000000000000651ef99cb9fcbe": { slow: 1, average: 1, fast: 2, fastest: 3 },
|
|
2831
|
+
"bip122:000007d91d1254d60e2dd1ae58038307": { slow: 1, average: 1, fast: 2, fastest: 3 },
|
|
2832
|
+
"bip122:4da631f2ac1bed857bd968c67c913978": { slow: 75, average: 80, fast: 100, fastest: 120 },
|
|
2833
|
+
"bip122:00040fe8ec8471911baa1db1266ea15d": { slow: 1, average: 1, fast: 2, fastest: 3 }
|
|
2834
|
+
};
|
|
2835
|
+
if (defaultFees[networkId]) {
|
|
2836
|
+
console.warn(`${tag}: Using default fee rates for ${networkId}`);
|
|
2837
|
+
feeRateFromNode = defaultFees[networkId];
|
|
2838
|
+
} else {
|
|
2839
|
+
throw new Error(`Unable to get fee rate for network ${networkId}: ${error.message || "API unavailable"}`);
|
|
2840
|
+
}
|
|
2827
2841
|
}
|
|
2828
2842
|
}
|
|
2829
2843
|
let effectiveFeeRate;
|
|
@@ -3539,6 +3553,7 @@ function getNetworkName(networkId) {
|
|
|
3539
3553
|
"bip122:00000000001a91e3dace36e2be3bf030": "Dogecoin",
|
|
3540
3554
|
"bip122:000000000000000000651ef99cb9fcbe": "Bitcoin Cash",
|
|
3541
3555
|
"bip122:000007d91d1254d60e2dd1ae58038307": "Dash",
|
|
3556
|
+
"bip122:00040fe8ec8471911baa1db1266ea15d": "Zcash",
|
|
3542
3557
|
"bip122:4da631f2ac1bed857bd968c67c913978": "DigiByte",
|
|
3543
3558
|
"eip155:1": "Ethereum",
|
|
3544
3559
|
"eip155:56": "BNB Smart Chain",
|
|
@@ -4110,7 +4125,7 @@ function buildDashboardFromBalances(balances, blockchains, assetsMap) {
|
|
|
4110
4125
|
totalValueUsd: 0,
|
|
4111
4126
|
networkPercentages: []
|
|
4112
4127
|
};
|
|
4113
|
-
let
|
|
4128
|
+
let totalPortfolioValueCents = 0;
|
|
4114
4129
|
const networksTemp = [];
|
|
4115
4130
|
for (const blockchain of blockchains) {
|
|
4116
4131
|
const filteredBalances = balances.filter((b2) => {
|
|
@@ -4118,50 +4133,46 @@ function buildDashboardFromBalances(balances, blockchains, assetsMap) {
|
|
|
4118
4133
|
return networkId === blockchain || blockchain === "eip155:*" && networkId.startsWith("eip155:");
|
|
4119
4134
|
});
|
|
4120
4135
|
const balanceMap = new Map;
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
} else {
|
|
4137
|
-
balances2.forEach((balance) => {
|
|
4138
|
-
const key = `${balance.caip}_${balance.pubkey || "default"}`;
|
|
4139
|
-
balanceMap.set(key, balance);
|
|
4140
|
-
});
|
|
4141
|
-
}
|
|
4136
|
+
filteredBalances.forEach((balance) => {
|
|
4137
|
+
const key = `${balance.caip}_${balance.pubkey || "default"}`;
|
|
4138
|
+
if (!balanceMap.has(key) || parseFloat(balance.valueUsd || "0") > parseFloat(balanceMap.get(key).valueUsd || "0")) {
|
|
4139
|
+
balanceMap.set(key, balance);
|
|
4140
|
+
} else {
|
|
4141
|
+
const existing = balanceMap.get(key);
|
|
4142
|
+
const existingValue = parseFloat(existing.valueUsd || "0");
|
|
4143
|
+
const newValue = parseFloat(balance.valueUsd || "0");
|
|
4144
|
+
console.log(`⚠️ [BUILD-DASHBOARD] ${blockchain} dedup skipped (same caip+pubkey):`, {
|
|
4145
|
+
caip: balance.caip,
|
|
4146
|
+
pubkey: (balance.pubkey || "default").substring(0, 20),
|
|
4147
|
+
existing: existingValue,
|
|
4148
|
+
new: newValue,
|
|
4149
|
+
diff: Math.abs(existingValue - newValue)
|
|
4150
|
+
});
|
|
4142
4151
|
}
|
|
4143
|
-
}
|
|
4144
|
-
filteredBalances.forEach((balance) => {
|
|
4145
|
-
const key = `${balance.caip}_${balance.pubkey || "default"}`;
|
|
4146
|
-
if (!balanceMap.has(key) || parseFloat(balance.valueUsd || "0") > parseFloat(balanceMap.get(key).valueUsd || "0")) {
|
|
4147
|
-
balanceMap.set(key, balance);
|
|
4148
|
-
}
|
|
4149
|
-
});
|
|
4150
|
-
}
|
|
4152
|
+
});
|
|
4151
4153
|
const networkBalances = Array.from(balanceMap.values());
|
|
4152
|
-
const
|
|
4153
|
-
|
|
4154
|
+
const networkTotalCents = networkBalances.reduce((sumCents, balance, idx) => {
|
|
4155
|
+
let valueUsd = typeof balance.valueUsd === "string" ? parseFloat(balance.valueUsd) : balance.valueUsd || 0;
|
|
4156
|
+
if (isNaN(valueUsd)) {
|
|
4157
|
+
console.warn(`⚠️ [BUILD-DASHBOARD] NaN value detected for ${balance.caip}:`, balance.valueUsd);
|
|
4158
|
+
return sumCents;
|
|
4159
|
+
}
|
|
4160
|
+
balance.valueUsd = valueUsd;
|
|
4161
|
+
const valueCents = Math.round(valueUsd * 100 * 1000) / 1000;
|
|
4154
4162
|
if (idx < 2) {
|
|
4155
4163
|
console.log(`\uD83D\uDCB0 [BUILD-DASHBOARD] ${blockchain} balance #${idx}:`, {
|
|
4156
4164
|
caip: balance.caip,
|
|
4157
4165
|
balance: balance.balance,
|
|
4158
4166
|
valueUsd: balance.valueUsd,
|
|
4167
|
+
valueUsdType: typeof balance.valueUsd,
|
|
4159
4168
|
parsedValueUsd: valueUsd,
|
|
4160
|
-
|
|
4169
|
+
valueCents,
|
|
4170
|
+
runningSumCents: sumCents + valueCents
|
|
4161
4171
|
});
|
|
4162
4172
|
}
|
|
4163
|
-
return
|
|
4173
|
+
return sumCents + valueCents;
|
|
4164
4174
|
}, 0);
|
|
4175
|
+
const networkTotal = networkTotalCents / 100;
|
|
4165
4176
|
console.log(`\uD83D\uDCB0 [BUILD-DASHBOARD] ${blockchain} totals:`, {
|
|
4166
4177
|
balancesCount: networkBalances.length,
|
|
4167
4178
|
networkTotal,
|
|
@@ -4184,8 +4195,9 @@ function buildDashboardFromBalances(balances, blockchains, assetsMap) {
|
|
|
4184
4195
|
color: gasAsset?.color || assetInfo?.color || null,
|
|
4185
4196
|
totalNativeBalance
|
|
4186
4197
|
});
|
|
4187
|
-
|
|
4198
|
+
totalPortfolioValueCents += networkTotalCents;
|
|
4188
4199
|
}
|
|
4200
|
+
const totalPortfolioValue = totalPortfolioValueCents / 100;
|
|
4189
4201
|
dashboardData.networks = networksTemp.sort((a2, b2) => b2.totalValueUsd - a2.totalValueUsd);
|
|
4190
4202
|
dashboardData.totalValueUsd = totalPortfolioValue;
|
|
4191
4203
|
dashboardData.networkPercentages = dashboardData.networks.map((network) => ({
|
|
@@ -4193,6 +4205,26 @@ function buildDashboardFromBalances(balances, blockchains, assetsMap) {
|
|
|
4193
4205
|
percentage: totalPortfolioValue > 0 ? Number((network.totalValueUsd / totalPortfolioValue * 100).toFixed(2)) : 0
|
|
4194
4206
|
})).filter((entry) => entry.percentage > 0);
|
|
4195
4207
|
console.log(`[FAST DASHBOARD] ✅ Built dashboard: ${dashboardData.networks.length} networks, $${totalPortfolioValue.toFixed(2)} total`);
|
|
4208
|
+
const inputBalancesTotal = balances.reduce((sum, b2) => {
|
|
4209
|
+
const value = typeof b2.valueUsd === "string" ? parseFloat(b2.valueUsd) : b2.valueUsd || 0;
|
|
4210
|
+
return sum + (isNaN(value) ? 0 : value);
|
|
4211
|
+
}, 0);
|
|
4212
|
+
const difference = Math.abs(inputBalancesTotal - totalPortfolioValue);
|
|
4213
|
+
if (difference > 0.01) {
|
|
4214
|
+
console.error(`\uD83D\uDEA8 [BUILD-DASHBOARD] BALANCE MISMATCH DETECTED!`);
|
|
4215
|
+
console.error(` Input balances total: $${inputBalancesTotal.toFixed(2)} USD`);
|
|
4216
|
+
console.error(` Dashboard total: $${totalPortfolioValue.toFixed(2)} USD`);
|
|
4217
|
+
console.error(` Difference: $${difference.toFixed(2)} USD`);
|
|
4218
|
+
console.error(` This indicates lost balances during dashboard aggregation!`);
|
|
4219
|
+
console.error(` Network breakdown:`);
|
|
4220
|
+
dashboardData.networks.forEach((network) => {
|
|
4221
|
+
console.error(` ${network.networkId}: $${network.totalValueUsd.toFixed(2)}`);
|
|
4222
|
+
});
|
|
4223
|
+
} else if (difference > 0.001) {
|
|
4224
|
+
console.warn(`⚠️ [BUILD-DASHBOARD] Minor balance rounding difference: $${difference.toFixed(4)} USD`);
|
|
4225
|
+
} else {
|
|
4226
|
+
console.log(`✅ [BUILD-DASHBOARD] Balance reconciliation passed (diff: $${difference.toFixed(4)})`);
|
|
4227
|
+
}
|
|
4196
4228
|
return dashboardData;
|
|
4197
4229
|
}
|
|
4198
4230
|
|
|
@@ -4465,6 +4497,19 @@ function enrichBalancesWithAssetInfo(balances, assetsMap, caipToNetworkId7) {
|
|
|
4465
4497
|
console.warn(tag, `Missing AssetInfo for CAIP: "${balance.caip}" - skipping this balance`);
|
|
4466
4498
|
continue;
|
|
4467
4499
|
}
|
|
4500
|
+
if (typeof balance.valueUsd === "string") {
|
|
4501
|
+
balance.valueUsd = parseFloat(balance.valueUsd) || 0;
|
|
4502
|
+
} else if (typeof balance.valueUsd !== "number") {
|
|
4503
|
+
balance.valueUsd = 0;
|
|
4504
|
+
}
|
|
4505
|
+
if (typeof balance.balance === "string") {
|
|
4506
|
+
balance.balance = balance.balance;
|
|
4507
|
+
}
|
|
4508
|
+
if (typeof balance.priceUsd === "string") {
|
|
4509
|
+
balance.priceUsd = parseFloat(balance.priceUsd) || 0;
|
|
4510
|
+
} else if (typeof balance.priceUsd !== "number") {
|
|
4511
|
+
balance.priceUsd = 0;
|
|
4512
|
+
}
|
|
4468
4513
|
Object.assign(balance, assetInfo, {
|
|
4469
4514
|
type: balance.type || assetInfo.type,
|
|
4470
4515
|
isNative: balance.isNative ?? assetInfo.isNative,
|
|
@@ -4472,7 +4517,9 @@ function enrichBalancesWithAssetInfo(balances, assetsMap, caipToNetworkId7) {
|
|
|
4472
4517
|
icon: assetInfo.icon || "https://pioneers.dev/coins/etherum.png",
|
|
4473
4518
|
identifier: `${balance.caip}:${balance.pubkey}`,
|
|
4474
4519
|
updated: Date.now(),
|
|
4475
|
-
color: assetInfo.color
|
|
4520
|
+
color: assetInfo.color,
|
|
4521
|
+
valueUsd: balance.valueUsd,
|
|
4522
|
+
priceUsd: balance.priceUsd
|
|
4476
4523
|
});
|
|
4477
4524
|
enrichedBalances.push(balance);
|
|
4478
4525
|
}
|
|
@@ -5007,6 +5054,20 @@ class SDK {
|
|
|
5007
5054
|
this.balances[index] = balance;
|
|
5008
5055
|
}
|
|
5009
5056
|
this.events.emit("BALANCE_UPDATE", balance);
|
|
5057
|
+
console.log(tag7, "\uD83D\uDD04 Rebuilding dashboard after balance update...");
|
|
5058
|
+
const previousTotal = this.dashboard?.totalValueUsd || 0;
|
|
5059
|
+
this.dashboard = this.buildDashboardFromBalances();
|
|
5060
|
+
const newTotal = this.dashboard?.totalValueUsd || 0;
|
|
5061
|
+
if (Math.abs(newTotal - previousTotal) > 0.01) {
|
|
5062
|
+
console.log(tag7, `\uD83D\uDCB0 Portfolio value changed: $${previousTotal.toFixed(2)} → $${newTotal.toFixed(2)}`);
|
|
5063
|
+
}
|
|
5064
|
+
this.events.emit("DASHBOARD_UPDATE", {
|
|
5065
|
+
dashboard: this.dashboard,
|
|
5066
|
+
trigger: "balance_update",
|
|
5067
|
+
affectedAsset: balance.caip,
|
|
5068
|
+
previousTotal,
|
|
5069
|
+
newTotal
|
|
5070
|
+
});
|
|
5010
5071
|
} catch (e) {
|
|
5011
5072
|
console.error(tag7, "Error processing balance update:", e);
|
|
5012
5073
|
}
|
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.32",
|
|
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.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",
|
|
12
12
|
"coinselect": "^3.1.13",
|
|
13
13
|
"eventemitter3": "^5.0.1",
|
|
14
14
|
"neotraverse": "^0.6.8",
|
package/src/fees/index.ts
CHANGED
|
@@ -48,6 +48,7 @@ function getNetworkName(networkId: string): string {
|
|
|
48
48
|
'bip122:00000000001a91e3dace36e2be3bf030': 'Dogecoin',
|
|
49
49
|
'bip122:000000000000000000651ef99cb9fcbe': 'Bitcoin Cash',
|
|
50
50
|
'bip122:000007d91d1254d60e2dd1ae58038307': 'Dash',
|
|
51
|
+
'bip122:00040fe8ec8471911baa1db1266ea15d': 'Zcash',
|
|
51
52
|
'bip122:4da631f2ac1bed857bd968c67c913978': 'DigiByte',
|
|
52
53
|
'eip155:1': 'Ethereum',
|
|
53
54
|
'eip155:56': 'BNB Smart Chain',
|
package/src/index.ts
CHANGED
|
@@ -552,6 +552,25 @@ export class SDK {
|
|
|
552
552
|
|
|
553
553
|
// Emit SDK event for UI updates
|
|
554
554
|
this.events.emit('BALANCE_UPDATE', balance);
|
|
555
|
+
|
|
556
|
+
// CRITICAL FIX: Rebuild dashboard when balance changes
|
|
557
|
+
console.log(tag, '🔄 Rebuilding dashboard after balance update...');
|
|
558
|
+
const previousTotal = this.dashboard?.totalValueUsd || 0;
|
|
559
|
+
this.dashboard = this.buildDashboardFromBalances();
|
|
560
|
+
const newTotal = this.dashboard?.totalValueUsd || 0;
|
|
561
|
+
|
|
562
|
+
if (Math.abs(newTotal - previousTotal) > 0.01) {
|
|
563
|
+
console.log(tag, `💰 Portfolio value changed: $${previousTotal.toFixed(2)} → $${newTotal.toFixed(2)}`);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Emit dashboard update event for real-time UI updates
|
|
567
|
+
this.events.emit('DASHBOARD_UPDATE', {
|
|
568
|
+
dashboard: this.dashboard,
|
|
569
|
+
trigger: 'balance_update',
|
|
570
|
+
affectedAsset: balance.caip,
|
|
571
|
+
previousTotal,
|
|
572
|
+
newTotal
|
|
573
|
+
});
|
|
555
574
|
} catch (e) {
|
|
556
575
|
console.error(tag, 'Error processing balance update:', e);
|
|
557
576
|
}
|
|
@@ -287,9 +287,26 @@ export async function createUnsignedUxtoTx(
|
|
|
287
287
|
}
|
|
288
288
|
} catch (error: any) {
|
|
289
289
|
console.error(`${tag}: Failed to get fee rates from Pioneer API:`, error.message || error);
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
290
|
+
|
|
291
|
+
// Fallback to hardcoded default fees for known networks
|
|
292
|
+
const defaultFees: Record<string, { slow: number; average: number; fast: number; fastest: number }> = {
|
|
293
|
+
'bip122:000000000019d6689c085ae165831e93': { slow: 3, average: 5, fast: 10, fastest: 15 }, // BTC
|
|
294
|
+
'bip122:12a765e31ffd4059bada1e25190f6e98': { slow: 2, average: 3, fast: 5, fastest: 10 }, // LTC
|
|
295
|
+
'bip122:00000000001a91e3dace36e2be3bf030': { slow: 1, average: 1, fast: 2, fastest: 3 }, // DOGE
|
|
296
|
+
'bip122:000000000000000000651ef99cb9fcbe': { slow: 1, average: 1, fast: 2, fastest: 3 }, // BCH
|
|
297
|
+
'bip122:000007d91d1254d60e2dd1ae58038307': { slow: 1, average: 1, fast: 2, fastest: 3 }, // DASH
|
|
298
|
+
'bip122:4da631f2ac1bed857bd968c67c913978': { slow: 75, average: 80, fast: 100, fastest: 120 }, // DGB
|
|
299
|
+
'bip122:00040fe8ec8471911baa1db1266ea15d': { slow: 1, average: 1, fast: 2, fastest: 3 }, // ZEC
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
if (defaultFees[networkId]) {
|
|
303
|
+
console.warn(`${tag}: Using default fee rates for ${networkId}`);
|
|
304
|
+
feeRateFromNode = defaultFees[networkId];
|
|
305
|
+
} else {
|
|
306
|
+
throw new Error(
|
|
307
|
+
`Unable to get fee rate for network ${networkId}: ${error.message || 'API unavailable'}`,
|
|
308
|
+
);
|
|
309
|
+
}
|
|
293
310
|
}
|
|
294
311
|
}
|
|
295
312
|
|
|
@@ -23,7 +23,7 @@ export function buildDashboardFromBalances(balances: any[], blockchains: string[
|
|
|
23
23
|
networkPercentages: [],
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
let
|
|
26
|
+
let totalPortfolioValueCents = 0; // Use cents for integer math
|
|
27
27
|
const networksTemp: {
|
|
28
28
|
networkId: string;
|
|
29
29
|
totalValueUsd: number;
|
|
@@ -46,73 +46,81 @@ export function buildDashboardFromBalances(balances: any[], blockchains: string[
|
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
// Deduplicate balances based on caip + pubkey combination
|
|
49
|
+
// NOTE: Each pubkey (xpub/ypub/zpub) represents a DIFFERENT address space with potentially
|
|
50
|
+
// different balances - they are NOT duplicates! Keep all of them.
|
|
49
51
|
const balanceMap = new Map();
|
|
50
52
|
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
});
|
|
77
|
-
}
|
|
53
|
+
// Standard deduplication for all networks (including Bitcoin)
|
|
54
|
+
// Only deduplicate if BOTH caip AND pubkey are identical (true duplicates)
|
|
55
|
+
filteredBalances.forEach((balance) => {
|
|
56
|
+
const key = `${balance.caip}_${balance.pubkey || 'default'}`;
|
|
57
|
+
|
|
58
|
+
// Only keep the first occurrence or the one with higher value for TRUE duplicates
|
|
59
|
+
if (
|
|
60
|
+
!balanceMap.has(key) ||
|
|
61
|
+
parseFloat(balance.valueUsd || '0') > parseFloat(balanceMap.get(key).valueUsd || '0')
|
|
62
|
+
) {
|
|
63
|
+
balanceMap.set(key, balance);
|
|
64
|
+
} else {
|
|
65
|
+
// DEBUG: Log when we skip a balance due to deduplication
|
|
66
|
+
const existing = balanceMap.get(key);
|
|
67
|
+
const existingValue = parseFloat(existing.valueUsd || '0');
|
|
68
|
+
const newValue = parseFloat(balance.valueUsd || '0');
|
|
69
|
+
|
|
70
|
+
// Always log duplicates being skipped (removed the 0.01 threshold)
|
|
71
|
+
console.log(`⚠️ [BUILD-DASHBOARD] ${blockchain} dedup skipped (same caip+pubkey):`, {
|
|
72
|
+
caip: balance.caip,
|
|
73
|
+
pubkey: (balance.pubkey || 'default').substring(0, 20),
|
|
74
|
+
existing: existingValue,
|
|
75
|
+
new: newValue,
|
|
76
|
+
diff: Math.abs(existingValue - newValue)
|
|
77
|
+
});
|
|
78
78
|
}
|
|
79
|
-
}
|
|
80
|
-
// Standard deduplication for non-Bitcoin networks
|
|
81
|
-
filteredBalances.forEach((balance) => {
|
|
82
|
-
const key = `${balance.caip}_${balance.pubkey || 'default'}`;
|
|
83
|
-
// Only keep the first occurrence or the one with higher value
|
|
84
|
-
if (
|
|
85
|
-
!balanceMap.has(key) ||
|
|
86
|
-
parseFloat(balance.valueUsd || '0') > parseFloat(balanceMap.get(key).valueUsd || '0')
|
|
87
|
-
) {
|
|
88
|
-
balanceMap.set(key, balance);
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
}
|
|
79
|
+
});
|
|
92
80
|
|
|
93
81
|
const networkBalances = Array.from(balanceMap.values());
|
|
94
82
|
|
|
95
83
|
// Ensure we're working with numbers for calculations
|
|
96
|
-
|
|
97
|
-
|
|
84
|
+
// Accumulate in cents (integer math) to avoid floating point errors
|
|
85
|
+
const networkTotalCents = networkBalances.reduce((sumCents, balance, idx) => {
|
|
86
|
+
// Normalize valueUsd to number type
|
|
87
|
+
let valueUsd =
|
|
98
88
|
typeof balance.valueUsd === 'string'
|
|
99
89
|
? parseFloat(balance.valueUsd)
|
|
100
90
|
: balance.valueUsd || 0;
|
|
101
91
|
|
|
92
|
+
// Check for NaN values which can cause calculation errors
|
|
93
|
+
if (isNaN(valueUsd)) {
|
|
94
|
+
console.warn(`⚠️ [BUILD-DASHBOARD] NaN value detected for ${balance.caip}:`, balance.valueUsd);
|
|
95
|
+
return sumCents;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Normalize the balance object to always use number type for valueUsd
|
|
99
|
+
balance.valueUsd = valueUsd;
|
|
100
|
+
|
|
101
|
+
// Convert to cents with higher precision to reduce rounding errors
|
|
102
|
+
// Use Math.round with extended precision, then scale back
|
|
103
|
+
const valueCents = Math.round(valueUsd * 100 * 1000) / 1000;
|
|
104
|
+
|
|
102
105
|
// Debug first few balances for each network
|
|
103
106
|
if (idx < 2) {
|
|
104
107
|
console.log(`💰 [BUILD-DASHBOARD] ${blockchain} balance #${idx}:`, {
|
|
105
108
|
caip: balance.caip,
|
|
106
109
|
balance: balance.balance,
|
|
107
110
|
valueUsd: balance.valueUsd,
|
|
111
|
+
valueUsdType: typeof balance.valueUsd,
|
|
108
112
|
parsedValueUsd: valueUsd,
|
|
109
|
-
|
|
113
|
+
valueCents: valueCents,
|
|
114
|
+
runningSumCents: sumCents + valueCents
|
|
110
115
|
});
|
|
111
116
|
}
|
|
112
117
|
|
|
113
|
-
return
|
|
118
|
+
return sumCents + valueCents;
|
|
114
119
|
}, 0);
|
|
115
120
|
|
|
121
|
+
// Convert back to dollars
|
|
122
|
+
const networkTotal = networkTotalCents / 100;
|
|
123
|
+
|
|
116
124
|
console.log(`💰 [BUILD-DASHBOARD] ${blockchain} totals:`, {
|
|
117
125
|
balancesCount: networkBalances.length,
|
|
118
126
|
networkTotal,
|
|
@@ -149,9 +157,13 @@ export function buildDashboardFromBalances(balances: any[], blockchains: string[
|
|
|
149
157
|
totalNativeBalance,
|
|
150
158
|
});
|
|
151
159
|
|
|
152
|
-
|
|
160
|
+
// Add to total using cents (integer math) to avoid floating point errors
|
|
161
|
+
totalPortfolioValueCents += networkTotalCents;
|
|
153
162
|
}
|
|
154
163
|
|
|
164
|
+
// Convert total from cents to dollars
|
|
165
|
+
const totalPortfolioValue = totalPortfolioValueCents / 100;
|
|
166
|
+
|
|
155
167
|
// Sort networks by USD value and assign to dashboard
|
|
156
168
|
dashboardData.networks = networksTemp.sort((a, b) => b.totalValueUsd - a.totalValueUsd);
|
|
157
169
|
dashboardData.totalValueUsd = totalPortfolioValue;
|
|
@@ -172,5 +184,33 @@ export function buildDashboardFromBalances(balances: any[], blockchains: string[
|
|
|
172
184
|
dashboardData.networks.length
|
|
173
185
|
} networks, $${totalPortfolioValue.toFixed(2)} total`,
|
|
174
186
|
);
|
|
187
|
+
|
|
188
|
+
// ===== BALANCE RECONCILIATION CHECK =====
|
|
189
|
+
// Verify that dashboard total matches the sum of all input balances
|
|
190
|
+
const inputBalancesTotal = balances.reduce((sum, b) => {
|
|
191
|
+
const value = typeof b.valueUsd === 'string' ? parseFloat(b.valueUsd) : (b.valueUsd || 0);
|
|
192
|
+
return sum + (isNaN(value) ? 0 : value);
|
|
193
|
+
}, 0);
|
|
194
|
+
|
|
195
|
+
const difference = Math.abs(inputBalancesTotal - totalPortfolioValue);
|
|
196
|
+
|
|
197
|
+
if (difference > 0.01) {
|
|
198
|
+
console.error(`🚨 [BUILD-DASHBOARD] BALANCE MISMATCH DETECTED!`);
|
|
199
|
+
console.error(` Input balances total: $${inputBalancesTotal.toFixed(2)} USD`);
|
|
200
|
+
console.error(` Dashboard total: $${totalPortfolioValue.toFixed(2)} USD`);
|
|
201
|
+
console.error(` Difference: $${difference.toFixed(2)} USD`);
|
|
202
|
+
console.error(` This indicates lost balances during dashboard aggregation!`);
|
|
203
|
+
|
|
204
|
+
// Log per-network breakdown to help debug
|
|
205
|
+
console.error(` Network breakdown:`);
|
|
206
|
+
dashboardData.networks.forEach(network => {
|
|
207
|
+
console.error(` ${network.networkId}: $${network.totalValueUsd.toFixed(2)}`);
|
|
208
|
+
});
|
|
209
|
+
} else if (difference > 0.001) {
|
|
210
|
+
console.warn(`⚠️ [BUILD-DASHBOARD] Minor balance rounding difference: $${difference.toFixed(4)} USD`);
|
|
211
|
+
} else {
|
|
212
|
+
console.log(`✅ [BUILD-DASHBOARD] Balance reconciliation passed (diff: $${difference.toFixed(4)})`);
|
|
213
|
+
}
|
|
214
|
+
|
|
175
215
|
return dashboardData;
|
|
176
216
|
}
|
|
@@ -220,6 +220,26 @@ export function enrichBalancesWithAssetInfo(
|
|
|
220
220
|
continue;
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
+
// ===== CRITICAL: Normalize valueUsd to NUMBER type =====
|
|
224
|
+
// Ensures consistent type throughout the SDK to prevent calculation errors
|
|
225
|
+
if (typeof balance.valueUsd === 'string') {
|
|
226
|
+
balance.valueUsd = parseFloat(balance.valueUsd) || 0;
|
|
227
|
+
} else if (typeof balance.valueUsd !== 'number') {
|
|
228
|
+
balance.valueUsd = 0;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Also normalize balance amount to number
|
|
232
|
+
if (typeof balance.balance === 'string') {
|
|
233
|
+
balance.balance = balance.balance; // Keep as string for precision (e.g., "0.00000001")
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Normalize priceUsd to number as well
|
|
237
|
+
if (typeof balance.priceUsd === 'string') {
|
|
238
|
+
balance.priceUsd = parseFloat(balance.priceUsd) || 0;
|
|
239
|
+
} else if (typeof balance.priceUsd !== 'number') {
|
|
240
|
+
balance.priceUsd = 0;
|
|
241
|
+
}
|
|
242
|
+
|
|
223
243
|
Object.assign(balance, assetInfo, {
|
|
224
244
|
type: balance.type || assetInfo.type,
|
|
225
245
|
isNative: balance.isNative ?? assetInfo.isNative,
|
|
@@ -228,6 +248,9 @@ export function enrichBalancesWithAssetInfo(
|
|
|
228
248
|
identifier: `${balance.caip}:${balance.pubkey}`,
|
|
229
249
|
updated: Date.now(),
|
|
230
250
|
color: assetInfo.color,
|
|
251
|
+
// Ensure these are numbers (redundant but explicit)
|
|
252
|
+
valueUsd: balance.valueUsd,
|
|
253
|
+
priceUsd: balance.priceUsd,
|
|
231
254
|
});
|
|
232
255
|
|
|
233
256
|
enrichedBalances.push(balance);
|