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