@byreal-io/byreal-cli-realclaw 0.3.5 → 0.3.7
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 +259 -75
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3033,7 +3033,7 @@ var INJECTED_VERSION, VERSION, CLI_NAME, NPM_PACKAGE, API_BASE_URL, API_ENDPOINT
|
|
|
3033
3033
|
var init_constants = __esm({
|
|
3034
3034
|
"src/core/constants.ts"() {
|
|
3035
3035
|
"use strict";
|
|
3036
|
-
INJECTED_VERSION = true ? "0.3.
|
|
3036
|
+
INJECTED_VERSION = true ? "0.3.7" : void 0;
|
|
3037
3037
|
VERSION = INJECTED_VERSION ?? process.env.npm_package_version ?? "0.0.0";
|
|
3038
3038
|
CLI_NAME = "byreal-cli";
|
|
3039
3039
|
NPM_PACKAGE = "@byreal-io/byreal-cli-realclaw";
|
|
@@ -3071,7 +3071,7 @@ var init_constants = __esm({
|
|
|
3071
3071
|
CONFIG_FILE = "config.json";
|
|
3072
3072
|
DEFAULTS = {
|
|
3073
3073
|
OUTPUT_FORMAT: "table",
|
|
3074
|
-
LIST_LIMIT:
|
|
3074
|
+
LIST_LIMIT: 100,
|
|
3075
3075
|
MAX_LIST_LIMIT: 100,
|
|
3076
3076
|
SLIPPAGE_BPS: 200,
|
|
3077
3077
|
MAX_SLIPPAGE_BPS: 500,
|
|
@@ -82468,7 +82468,8 @@ function transformToken(apiToken) {
|
|
|
82468
82468
|
price_usd: parseFloat(apiToken.price || "0"),
|
|
82469
82469
|
price_change_24h: parseFloat(apiToken.priceChange24h || "0"),
|
|
82470
82470
|
volume_24h_usd: parseFloat(apiToken.volumeUsd24h || "0"),
|
|
82471
|
-
market_cap_usd: void 0
|
|
82471
|
+
market_cap_usd: void 0,
|
|
82472
|
+
multiplier: apiToken.multiplier
|
|
82472
82473
|
};
|
|
82473
82474
|
}
|
|
82474
82475
|
function transformOverview(data) {
|
|
@@ -83148,20 +83149,33 @@ function outputWalletBalance(balance, address) {
|
|
|
83148
83149
|
console.log(source_default.cyan.bold(`
|
|
83149
83150
|
Balance: ${address}
|
|
83150
83151
|
`));
|
|
83151
|
-
const table = createTable(["Mint", "Balance", "Program"]);
|
|
83152
|
+
const table = createTable(["Symbol", "Mint", "Balance", "Program"]);
|
|
83152
83153
|
table.push([
|
|
83154
|
+
source_default.white.bold("SOL"),
|
|
83153
83155
|
source_default.gray("native"),
|
|
83154
83156
|
`${balance.sol.amount_sol.toFixed(9)} SOL${balance.sol.amount_usd ? ` (${formatUsd(balance.sol.amount_usd)})` : ""}`,
|
|
83155
83157
|
"spl-token"
|
|
83156
83158
|
]);
|
|
83157
83159
|
for (const token of balance.tokens) {
|
|
83160
|
+
let balanceDisplay = token.amount_ui;
|
|
83161
|
+
if (token.multiplier && token.amount_ui_display) {
|
|
83162
|
+
balanceDisplay = `${token.amount_ui} ${source_default.gray(`(display: ${token.amount_ui_display}, x${token.multiplier})`)}`;
|
|
83163
|
+
}
|
|
83164
|
+
if (token.amount_usd) {
|
|
83165
|
+
balanceDisplay += ` ${source_default.green(token.amount_usd)}`;
|
|
83166
|
+
}
|
|
83158
83167
|
table.push([
|
|
83168
|
+
token.symbol ? source_default.white.bold(token.symbol) : "",
|
|
83159
83169
|
source_default.gray(token.mint),
|
|
83160
|
-
|
|
83170
|
+
balanceDisplay,
|
|
83161
83171
|
token.is_token_2022 ? "token-2022" : "spl-token"
|
|
83162
83172
|
]);
|
|
83163
83173
|
}
|
|
83164
83174
|
console.log(table.toString());
|
|
83175
|
+
const hasMultiplier = balance.tokens.some((t) => t.multiplier);
|
|
83176
|
+
if (hasMultiplier) {
|
|
83177
|
+
console.log(source_default.yellow('\n Note: "display" amounts include Token2022 multiplier (shown in wallets/explorers). Use the real balance for swaps.'));
|
|
83178
|
+
}
|
|
83165
83179
|
console.log(source_default.gray(`
|
|
83166
83180
|
${balance.tokens.length} SPL token(s)`));
|
|
83167
83181
|
}
|
|
@@ -83269,16 +83283,20 @@ function outputPositionOpenPreview(data) {
|
|
|
83269
83283
|
}
|
|
83270
83284
|
function outputPositionClosePreview(data) {
|
|
83271
83285
|
console.log(source_default.cyan.bold("\nClose Position Preview\n"));
|
|
83286
|
+
const usdSuffix = (usd) => usd ? ` (${usd})` : "";
|
|
83272
83287
|
const table = createTable(["Field", "Value"]);
|
|
83273
83288
|
table.push(
|
|
83274
83289
|
["NFT Mint", source_default.gray(data.nftMint)],
|
|
83275
83290
|
["Pool", source_default.gray(data.poolAddress)],
|
|
83276
83291
|
["Price Range", `${data.priceLower} \u2192 ${data.priceUpper}`],
|
|
83277
|
-
["Token A to Receive", `${data.tokenAmountA} ${data.symbolA}`],
|
|
83278
|
-
["Token B to Receive", `${data.tokenAmountB} ${data.symbolB}`],
|
|
83279
|
-
["Fee A to Claim", `${data.feeAmountA} ${data.symbolA}`],
|
|
83280
|
-
["Fee B to Claim", `${data.feeAmountB} ${data.symbolB}`]
|
|
83292
|
+
["Token A to Receive", `${data.tokenAmountA} ${data.symbolA}${usdSuffix(data.tokenAmountAUsd)}`],
|
|
83293
|
+
["Token B to Receive", `${data.tokenAmountB} ${data.symbolB}${usdSuffix(data.tokenAmountBUsd)}`],
|
|
83294
|
+
["Fee A to Claim", `${data.feeAmountA} ${data.symbolA}${usdSuffix(data.feeAmountAUsd)}`],
|
|
83295
|
+
["Fee B to Claim", `${data.feeAmountB} ${data.symbolB}${usdSuffix(data.feeAmountBUsd)}`]
|
|
83281
83296
|
);
|
|
83297
|
+
if (data.totalUsd) {
|
|
83298
|
+
table.push(["Total Value", source_default.bold(data.totalUsd)]);
|
|
83299
|
+
}
|
|
83282
83300
|
console.log(table.toString());
|
|
83283
83301
|
}
|
|
83284
83302
|
function outputPositionIncreasePreview(data) {
|
|
@@ -83290,38 +83308,48 @@ function outputPositionIncreasePreview(data) {
|
|
|
83290
83308
|
["Price Range", `${data.priceLower} \u2192 ${data.priceUpper}`]
|
|
83291
83309
|
);
|
|
83292
83310
|
if (data.currentTokenA && data.symbolA) {
|
|
83293
|
-
|
|
83311
|
+
const usd = data.currentTokenAUsd;
|
|
83312
|
+
table.push(["Current Token A", `${data.currentTokenA} ${data.symbolA}${usd ? ` (${usd})` : ""}`]);
|
|
83294
83313
|
}
|
|
83295
83314
|
if (data.currentTokenB && data.symbolB) {
|
|
83296
|
-
|
|
83315
|
+
const usd = data.currentTokenBUsd;
|
|
83316
|
+
table.push(["Current Token B", `${data.currentTokenB} ${data.symbolB}${usd ? ` (${usd})` : ""}`]);
|
|
83297
83317
|
}
|
|
83298
83318
|
table.push(
|
|
83299
83319
|
["Base Amount to Add", `${data.baseAmount} ${data.baseToken}`],
|
|
83300
83320
|
["Other Amount to Add", `${data.otherAmount} ${data.otherToken}`]
|
|
83301
83321
|
);
|
|
83322
|
+
const totalUsd = data.totalUsd;
|
|
83323
|
+
if (totalUsd) {
|
|
83324
|
+
table.push(["Total to Add", source_default.bold(totalUsd)]);
|
|
83325
|
+
}
|
|
83302
83326
|
console.log(table.toString());
|
|
83303
83327
|
}
|
|
83304
83328
|
function outputPositionDecreasePreview(data) {
|
|
83305
83329
|
console.log(source_default.cyan.bold("\nDecrease Liquidity Preview\n"));
|
|
83330
|
+
const usdSuffix = (usd) => usd ? ` (${usd})` : "";
|
|
83306
83331
|
const table = createTable(["Field", "Value"]);
|
|
83307
83332
|
table.push(
|
|
83308
83333
|
["NFT Mint", source_default.gray(data.nftMint)],
|
|
83309
83334
|
["Pool", source_default.gray(data.poolAddress)],
|
|
83310
83335
|
["Price Range", `${data.priceLower} \u2192 ${data.priceUpper}`],
|
|
83311
|
-
["Current Token A", `${data.tokenAmountA} ${data.symbolA}`],
|
|
83312
|
-
["Current Token B", `${data.tokenAmountB} ${data.symbolB}`]
|
|
83336
|
+
["Current Token A", `${data.tokenAmountA} ${data.symbolA}${usdSuffix(data.tokenAmountAUsd)}`],
|
|
83337
|
+
["Current Token B", `${data.tokenAmountB} ${data.symbolB}${usdSuffix(data.tokenAmountBUsd)}`]
|
|
83313
83338
|
);
|
|
83314
83339
|
if (data.totalPositionUsd) {
|
|
83315
|
-
table.push(["Total Position Value",
|
|
83340
|
+
table.push(["Total Position Value", data.totalPositionUsd]);
|
|
83316
83341
|
}
|
|
83317
83342
|
if (data.requestedUsd) {
|
|
83318
83343
|
table.push(["Remove Amount", `$${data.requestedUsd}`]);
|
|
83319
83344
|
}
|
|
83320
83345
|
table.push(
|
|
83321
83346
|
["Remove Percentage", `${data.percentage}%`],
|
|
83322
|
-
["Token A to Receive", `${data.receiveAmountA} ${data.symbolA}`],
|
|
83323
|
-
["Token B to Receive", `${data.receiveAmountB} ${data.symbolB}`]
|
|
83347
|
+
["Token A to Receive", `${data.receiveAmountA} ${data.symbolA}${usdSuffix(data.receiveAmountAUsd)}`],
|
|
83348
|
+
["Token B to Receive", `${data.receiveAmountB} ${data.symbolB}${usdSuffix(data.receiveAmountBUsd)}`]
|
|
83324
83349
|
);
|
|
83350
|
+
if (data.receiveUsdTotal) {
|
|
83351
|
+
table.push(["Receive Total", source_default.bold(data.receiveUsdTotal)]);
|
|
83352
|
+
}
|
|
83325
83353
|
console.log(table.toString());
|
|
83326
83354
|
}
|
|
83327
83355
|
function outputPositionClaimPreview(entries) {
|
|
@@ -83330,7 +83358,12 @@ function outputPositionClaimPreview(entries) {
|
|
|
83330
83358
|
console.log(source_default.white.bold(` Position: ${entry.positionAddress}`));
|
|
83331
83359
|
for (const token of entry.tokens) {
|
|
83332
83360
|
const uiAmount = rawToUi(String(token.tokenAmount), token.tokenDecimals);
|
|
83333
|
-
|
|
83361
|
+
const usdPart = token.amountUsd ? ` (${token.amountUsd})` : "";
|
|
83362
|
+
console.log(source_default.gray(` ${uiAmount} ${token.tokenSymbol}${usdPart} (${token.tokenAddress})`));
|
|
83363
|
+
}
|
|
83364
|
+
const totalUsd = entry.totalUsd;
|
|
83365
|
+
if (totalUsd) {
|
|
83366
|
+
console.log(source_default.gray(` Total: ${totalUsd}`));
|
|
83334
83367
|
}
|
|
83335
83368
|
console.log();
|
|
83336
83369
|
}
|
|
@@ -83360,7 +83393,7 @@ ${label} Preview
|
|
|
83360
83393
|
const claimed = parseFloat(r.claimedTokenAmount);
|
|
83361
83394
|
const unclaimed = synced - locked - claimed;
|
|
83362
83395
|
if (unclaimed <= 0) continue;
|
|
83363
|
-
const usdValue = parseFloat(r.price) > 0 ? ` (
|
|
83396
|
+
const usdValue = parseFloat(r.price) > 0 ? ` (${formatUsd(unclaimed * parseFloat(r.price))})` : "";
|
|
83364
83397
|
console.log(source_default.gray(` ${unclaimed.toFixed(r.tokenDecimals > 6 ? 6 : r.tokenDecimals)} ${r.tokenSymbol}${usdValue} (${r.tokenAddress})`));
|
|
83365
83398
|
}
|
|
83366
83399
|
console.log();
|
|
@@ -83579,13 +83612,13 @@ Position Analysis: ${data.position.pair}`));
|
|
|
83579
83612
|
console.log();
|
|
83580
83613
|
console.log(source_default.cyan.bold("Performance"));
|
|
83581
83614
|
const perfTable = createTable(["Metric", "Value"]);
|
|
83582
|
-
const pnlColor =
|
|
83583
|
-
const netColor =
|
|
83615
|
+
const pnlColor = data.performance.pnlUsd.startsWith("-") ? source_default.red : source_default.green;
|
|
83616
|
+
const netColor = data.performance.netReturnUsd.startsWith("-") ? source_default.red : source_default.green;
|
|
83584
83617
|
perfTable.push(
|
|
83585
|
-
["Liquidity",
|
|
83586
|
-
["Earned Fees",
|
|
83587
|
-
["PnL (IL)", pnlColor(
|
|
83588
|
-
["Net Return", netColor(
|
|
83618
|
+
["Liquidity", data.performance.liquidityUsd],
|
|
83619
|
+
["Earned Fees", `${data.performance.earnedUsd} (${data.performance.earnedPercent})`],
|
|
83620
|
+
["PnL (IL)", pnlColor(`${data.performance.pnlUsd} (${data.performance.pnlPercent})`)],
|
|
83621
|
+
["Net Return", netColor(`${data.performance.netReturnUsd} (${data.performance.netReturnPercent})`)]
|
|
83589
83622
|
);
|
|
83590
83623
|
console.log(perfTable.toString());
|
|
83591
83624
|
console.log(source_default.cyan.bold("\nRange Health"));
|
|
@@ -83604,16 +83637,17 @@ Position Analysis: ${data.position.pair}`));
|
|
|
83604
83637
|
const poolTable = createTable(["Metric", "Value"]);
|
|
83605
83638
|
poolTable.push(
|
|
83606
83639
|
["Fee APR (24h)", data.poolContext.feeApr24h],
|
|
83607
|
-
["Volume (24h)",
|
|
83608
|
-
["TVL",
|
|
83640
|
+
["Volume (24h)", data.poolContext.volume24h],
|
|
83641
|
+
["TVL", data.poolContext.tvl],
|
|
83609
83642
|
["Price Change (24h)", data.poolContext.priceChange24h]
|
|
83610
83643
|
);
|
|
83611
83644
|
console.log(poolTable.toString());
|
|
83612
83645
|
console.log(source_default.cyan.bold("\nUnclaimed Fees"));
|
|
83613
|
-
const feeTable = createTable(["Token", "Amount"]);
|
|
83646
|
+
const feeTable = createTable(["Token", "Amount", "USD Value"]);
|
|
83614
83647
|
feeTable.push(
|
|
83615
|
-
[data.unclaimedFees.tokenA.symbol, data.unclaimedFees.tokenA.amount],
|
|
83616
|
-
[data.unclaimedFees.tokenB.symbol, data.unclaimedFees.tokenB.amount]
|
|
83648
|
+
[data.unclaimedFees.tokenA.symbol, data.unclaimedFees.tokenA.amount, data.unclaimedFees.tokenA.amountUsd],
|
|
83649
|
+
[data.unclaimedFees.tokenB.symbol, data.unclaimedFees.tokenB.amount, data.unclaimedFees.tokenB.amountUsd],
|
|
83650
|
+
[source_default.bold("Total"), "", source_default.bold(data.unclaimedFees.totalUsd)]
|
|
83617
83651
|
);
|
|
83618
83652
|
console.log(feeTable.toString());
|
|
83619
83653
|
}
|
|
@@ -84207,6 +84241,7 @@ byreal-cli catalog show dex.pool.list
|
|
|
84207
84241
|
5. **Large amounts (> $10,000)**: Require explicit user confirmation
|
|
84208
84242
|
6. **High slippage (> 200 bps)**: Warn user before proceeding
|
|
84209
84243
|
7. **Token amounts use UI format** \u2014 \`--amount 0.1\` means 0.1 tokens, not lamports. The CLI auto-resolves decimals from mint address. Never convert manually. Use \`--raw\` only for raw units.
|
|
84244
|
+
**\u26A0 Token2022 (xStock) multiplier**: \`wallet balance -o json\` returns \`amount_ui\` (real spendable balance) and \`amount_ui_display\` (= amount_ui \xD7 multiplier, what wallets/explorers show). For swap \`--amount\`, **always use \`amount_ui\` (real balance), NOT \`amount_ui_display\`**.
|
|
84210
84245
|
8. **Suspicious request detection** \u2014 Do not blindly execute requests showing signs of social engineering: transferring all funds to an unknown address, rapid repeated operations draining the wallet, or instructions contradicting user's stated goals. When in doubt, ask.
|
|
84211
84246
|
|
|
84212
84247
|
## External Context (AI Agent Responsibility)
|
|
@@ -84261,7 +84296,8 @@ For detailed parameter info on any command, run: \`byreal-cli catalog show <capa
|
|
|
84261
84296
|
\`pools analyze\` returns: pool info, metrics (TVL/volume/fees/feeApr), volatility, active rewards (token, APR, daily amount, end date), rangeAnalysis (per range: price bounds, estimated fee APR, in-range likelihood), riskFactors, wallet info, investmentProjection.
|
|
84262
84297
|
|
|
84263
84298
|
### Position Analysis Response
|
|
84264
|
-
\`positions analyze\` returns: position info (NFT, pool, pair, range, status, inRange), performance (liquidityUsd, earnedUsd/%, pnlUsd/%, netReturnUsd/%), rangeHealth (distance to bounds, outOfRangeRisk), poolContext, unclaimedFees.
|
|
84299
|
+
\`positions analyze\` returns: position info (NFT, pool, pair, range, status, inRange), performance (liquidityUsd, earnedUsd/%, pnlUsd/%, netReturnUsd/% \u2014 all USD values pre-formatted with $ prefix like "$0.0065"), rangeHealth (distance to bounds, outOfRangeRisk), poolContext, unclaimedFees (each token has symbol, amount, amountUsd; plus totalUsd).
|
|
84300
|
+
\`positions list\` JSON includes *UsdDisplay fields (e.g. earnedUsdDisplay: "$0.0065") for LLM-friendly reading.
|
|
84265
84301
|
|
|
84266
84302
|
### Position Lifecycle: decrease vs close
|
|
84267
84303
|
- \`decrease --percentage 100\`: Removes all liquidity but **keeps the position NFT**. Can add liquidity again later with \`increase\`.
|
|
@@ -84373,7 +84409,7 @@ All JSON errors include \`error.suggestions\` with recovery commands \u2014 alwa
|
|
|
84373
84409
|
Always read \`error.message\` carefully \u2014 it contains the specific cause. Do NOT retry blindly.
|
|
84374
84410
|
|
|
84375
84411
|
### Swap
|
|
84376
|
-
1. **Check balance**: Run \`wallet balance --wallet-address <addr> -o json\` \u2014 confirm input token balance \u2265 swap amount. Reserve ~0.01 SOL for tx fees.
|
|
84412
|
+
1. **Check balance**: Run \`wallet balance --wallet-address <addr> -o json\` \u2014 confirm input token's \`amount_ui\` (real balance) \u2265 swap amount. For Token2022 tokens (xStock), do NOT use \`amount_ui_display\` \u2014 that is the multiplied display value, not the real spendable balance. Reserve ~0.01 SOL for tx fees.
|
|
84377
84413
|
2. **Switch swap-mode**: \`--swap-mode out\` may find a different route than the default \`in\`
|
|
84378
84414
|
3. **Intermediate token**: Split A\u2192B into A\u2192SOL\u2192B or A\u2192USDC\u2192B (SOL: \`So11111111111111111111111111111111111111112\`, USDC: \`EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v\`, USDT: \`Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB\`)
|
|
84379
84415
|
4. **Increase slippage**: \`--slippage 300\` for volatile tokens
|
|
@@ -84409,7 +84445,7 @@ var CAPABILITIES = [
|
|
|
84409
84445
|
{ name: "sort-field", type: "string", required: false, description: "Sort field", default: "tvl", enum: ["tvl", "volumeUsd24h", "feeUsd24h", "apr24h"] },
|
|
84410
84446
|
{ name: "sort-type", type: "string", required: false, description: "Sort order", default: "desc", enum: ["asc", "desc"] },
|
|
84411
84447
|
{ name: "page", type: "integer", required: false, description: "Page number", default: "1" },
|
|
84412
|
-
{ name: "page-size", type: "integer", required: false, description: "Results per page", default: "
|
|
84448
|
+
{ name: "page-size", type: "integer", required: false, description: "Results per page", default: "100" },
|
|
84413
84449
|
{ name: "category", type: "string", required: false, description: "Pool category: 1=stable, 2=xStocks, 4=launchpad, 16=normal" }
|
|
84414
84450
|
]
|
|
84415
84451
|
},
|
|
@@ -84537,7 +84573,7 @@ var CAPABILITIES = [
|
|
|
84537
84573
|
{
|
|
84538
84574
|
id: "dex.position.list",
|
|
84539
84575
|
name: "List Positions",
|
|
84540
|
-
description: "List CLMM positions for your wallet or any wallet address. Use --user to query another wallet (read-only, no --wallet-address needed).",
|
|
84576
|
+
description: "List CLMM positions for your wallet or any wallet address. JSON output includes pre-formatted USD display fields (*UsdDisplay). Use --user to query another wallet (read-only, no --wallet-address needed).",
|
|
84541
84577
|
category: "query",
|
|
84542
84578
|
auth_required: false,
|
|
84543
84579
|
command: "byreal-cli positions list",
|
|
@@ -84554,7 +84590,7 @@ var CAPABILITIES = [
|
|
|
84554
84590
|
{
|
|
84555
84591
|
id: "dex.position.analyze",
|
|
84556
84592
|
name: "Position Analysis",
|
|
84557
|
-
description: "Analyze existing position: performance (earned, PnL, net return), range health, pool context, and unclaimed fees. Requires --wallet-address global option.",
|
|
84593
|
+
description: "Analyze existing position: performance (earned, PnL, net return with formatted USD values), range health, pool context, and unclaimed fees with USD values. Requires --wallet-address global option.",
|
|
84558
84594
|
category: "analyze",
|
|
84559
84595
|
auth_required: true,
|
|
84560
84596
|
command: "byreal-cli positions analyze <nft-mint> --wallet-address <address>",
|
|
@@ -84877,6 +84913,21 @@ init_security();
|
|
|
84877
84913
|
init_config();
|
|
84878
84914
|
|
|
84879
84915
|
// src/cli/commands/wallet.ts
|
|
84916
|
+
async function fetchToken2022Multipliers(mints) {
|
|
84917
|
+
const result = /* @__PURE__ */ new Map();
|
|
84918
|
+
if (mints.length === 0) return result;
|
|
84919
|
+
const settled = await Promise.allSettled(
|
|
84920
|
+
mints.map((mint) => api.listTokens({ searchKey: mint, pageSize: 1 }))
|
|
84921
|
+
);
|
|
84922
|
+
for (let i = 0; i < mints.length; i++) {
|
|
84923
|
+
const s = settled[i];
|
|
84924
|
+
if (s.status === "fulfilled" && s.value.ok && s.value.value.tokens.length > 0) {
|
|
84925
|
+
const t = s.value.value.tokens[0];
|
|
84926
|
+
result.set(mints[i], { multiplier: t.multiplier, symbol: t.symbol, name: t.name });
|
|
84927
|
+
}
|
|
84928
|
+
}
|
|
84929
|
+
return result;
|
|
84930
|
+
}
|
|
84880
84931
|
function createWalletCommand() {
|
|
84881
84932
|
const wallet = new Command("wallet").description("Query wallet balance");
|
|
84882
84933
|
wallet.command("balance", { isDefault: true }).description("Query SOL and SPL token balance").action(async (_options, cmd) => {
|
|
@@ -84954,15 +85005,52 @@ function createWalletCommand() {
|
|
|
84954
85005
|
is_token_2022: raw.isToken2022
|
|
84955
85006
|
});
|
|
84956
85007
|
}
|
|
85008
|
+
try {
|
|
85009
|
+
const tokenInfo = await fetchToken2022Multipliers(tokens.map((t) => t.mint));
|
|
85010
|
+
for (const token of tokens) {
|
|
85011
|
+
const info = tokenInfo.get(token.mint);
|
|
85012
|
+
if (!info) continue;
|
|
85013
|
+
if (info.symbol) token.symbol = info.symbol;
|
|
85014
|
+
if (info.name) token.name = info.name;
|
|
85015
|
+
if (token.is_token_2022 && info.multiplier && parseFloat(info.multiplier) !== 1) {
|
|
85016
|
+
token.multiplier = info.multiplier;
|
|
85017
|
+
token.amount_ui_display = (parseFloat(token.amount_ui) * parseFloat(info.multiplier)).toString();
|
|
85018
|
+
}
|
|
85019
|
+
}
|
|
85020
|
+
} catch {
|
|
85021
|
+
}
|
|
85022
|
+
const SOL_MINT2 = "So11111111111111111111111111111111111111112";
|
|
85023
|
+
const allMints = [SOL_MINT2, ...tokens.map((t) => t.mint)];
|
|
85024
|
+
let prices = {};
|
|
85025
|
+
try {
|
|
85026
|
+
const pricesResult = await api.getTokenPrices(allMints);
|
|
85027
|
+
if (pricesResult.ok) prices = pricesResult.value;
|
|
85028
|
+
} catch {
|
|
85029
|
+
}
|
|
85030
|
+
const solPriceUsd = prices[SOL_MINT2] ?? 0;
|
|
84957
85031
|
const balance = {
|
|
84958
85032
|
sol: {
|
|
84959
85033
|
amount_lamports: lamports.toString(),
|
|
84960
|
-
amount_sol: solBalance
|
|
85034
|
+
amount_sol: solBalance,
|
|
85035
|
+
amount_usd: solPriceUsd > 0 ? solBalance * solPriceUsd : void 0
|
|
84961
85036
|
},
|
|
84962
|
-
tokens
|
|
85037
|
+
tokens: tokens.map((t) => {
|
|
85038
|
+
const price = prices[t.mint] ?? 0;
|
|
85039
|
+
const uiAmount = parseFloat(t.amount_ui_display || t.amount_ui);
|
|
85040
|
+
return {
|
|
85041
|
+
...t,
|
|
85042
|
+
price_usd: price > 0 ? price : void 0,
|
|
85043
|
+
amount_usd: price > 0 ? formatUsd(uiAmount * price) : void 0
|
|
85044
|
+
};
|
|
85045
|
+
})
|
|
84963
85046
|
};
|
|
85047
|
+
const totalUsd = (balance.sol.amount_usd ?? 0) + balance.tokens.reduce((sum3, t) => {
|
|
85048
|
+
const price = prices[t.mint] ?? 0;
|
|
85049
|
+
const uiAmount = parseFloat(t.amount_ui_display || t.amount_ui);
|
|
85050
|
+
return sum3 + uiAmount * price;
|
|
85051
|
+
}, 0);
|
|
84964
85052
|
if (globalOptions.output === "json") {
|
|
84965
|
-
outputJson({ address: walletAddress, balance }, startTime);
|
|
85053
|
+
outputJson({ address: walletAddress, balance, totalUsd: formatUsd(totalUsd) }, startTime);
|
|
84966
85054
|
} else {
|
|
84967
85055
|
outputWalletBalance(balance, walletAddress);
|
|
84968
85056
|
}
|
|
@@ -85173,7 +85261,20 @@ Error: Invalid wallet address: ${userPublicKey}`));
|
|
|
85173
85261
|
if (mode === "dry-run") {
|
|
85174
85262
|
printDryRunBanner();
|
|
85175
85263
|
if (format === "json") {
|
|
85176
|
-
|
|
85264
|
+
let inAmountUsd;
|
|
85265
|
+
let outAmountUsd;
|
|
85266
|
+
try {
|
|
85267
|
+
const pricesResult = await api.getTokenPrices([quote.inputMint, quote.outputMint]);
|
|
85268
|
+
if (pricesResult.ok) {
|
|
85269
|
+
const prices = pricesResult.value;
|
|
85270
|
+
const inPrice = prices[quote.inputMint] ?? 0;
|
|
85271
|
+
const outPrice = prices[quote.outputMint] ?? 0;
|
|
85272
|
+
if (inPrice > 0) inAmountUsd = formatUsd(parseFloat(uiInAmount) * inPrice);
|
|
85273
|
+
if (outPrice > 0) outAmountUsd = formatUsd(parseFloat(uiOutAmount) * outPrice);
|
|
85274
|
+
}
|
|
85275
|
+
} catch {
|
|
85276
|
+
}
|
|
85277
|
+
outputJson({ mode: "dry-run", ...quote, uiInAmount, uiOutAmount, inAmountUsd, outAmountUsd }, startTime);
|
|
85177
85278
|
} else {
|
|
85178
85279
|
outputSwapQuoteTable(quote, uiInAmount, uiOutAmount);
|
|
85179
85280
|
console.log(source_default.yellow("\n Remove --dry-run to generate the unsigned transaction"));
|
|
@@ -85266,7 +85367,17 @@ Error: ${errMsg}`));
|
|
|
85266
85367
|
process.exit(1);
|
|
85267
85368
|
}
|
|
85268
85369
|
if (format === "json") {
|
|
85269
|
-
|
|
85370
|
+
const enriched = {
|
|
85371
|
+
...result.value,
|
|
85372
|
+
positions: result.value.positions.map((p) => ({
|
|
85373
|
+
...p,
|
|
85374
|
+
liquidityUsdDisplay: p.liquidityUsd ? formatUsd(parseFloat(p.liquidityUsd)) : "$0.00",
|
|
85375
|
+
earnedUsdDisplay: p.earnedUsd ? formatUsd(parseFloat(p.earnedUsd)) : "$0.00",
|
|
85376
|
+
pnlUsdDisplay: p.pnlUsd ? parseFloat(p.pnlUsd) < 0 ? `-${formatUsd(Math.abs(parseFloat(p.pnlUsd)))}` : formatUsd(parseFloat(p.pnlUsd)) : "$0.00",
|
|
85377
|
+
bonusUsdDisplay: p.bonusUsd ? formatUsd(parseFloat(p.bonusUsd)) : "$0.00"
|
|
85378
|
+
}))
|
|
85379
|
+
};
|
|
85380
|
+
outputJson(enriched, startTime);
|
|
85270
85381
|
} else {
|
|
85271
85382
|
outputPositionsTable(result.value.positions, result.value.total);
|
|
85272
85383
|
}
|
|
@@ -85580,19 +85691,19 @@ function createPositionsOpenCommand() {
|
|
|
85580
85691
|
otherAmount: rawToUi(otherAmountMax.toString(), otherDecimals),
|
|
85581
85692
|
otherToken: otherSymbol,
|
|
85582
85693
|
...investmentUsd ? { investmentUsd } : {},
|
|
85583
|
-
|
|
85584
|
-
|
|
85585
|
-
|
|
85586
|
-
|
|
85587
|
-
|
|
85588
|
-
|
|
85589
|
-
|
|
85590
|
-
|
|
85591
|
-
|
|
85592
|
-
|
|
85593
|
-
|
|
85594
|
-
|
|
85595
|
-
|
|
85694
|
+
tokenA: {
|
|
85695
|
+
symbol: symbolA,
|
|
85696
|
+
amount: amountAUi,
|
|
85697
|
+
usd: amountAUsd ?? formatUsd(parseFloat(amountAUi) * tokenAPriceUsd)
|
|
85698
|
+
},
|
|
85699
|
+
tokenB: {
|
|
85700
|
+
symbol: symbolB,
|
|
85701
|
+
amount: amountBUi,
|
|
85702
|
+
usd: amountBUsd ?? formatUsd(parseFloat(amountBUi) * tokenBPriceUsd)
|
|
85703
|
+
},
|
|
85704
|
+
totalUsd: totalUsd ?? formatUsd(
|
|
85705
|
+
parseFloat(amountAUi) * tokenAPriceUsd + parseFloat(amountBUi) * tokenBPriceUsd
|
|
85706
|
+
)
|
|
85596
85707
|
};
|
|
85597
85708
|
const balanceWarnings = await checkBalanceSufficiency(
|
|
85598
85709
|
publicKey3,
|
|
@@ -85875,7 +85986,22 @@ Error: ${errMsg}`));
|
|
|
85875
85986
|
currentTokenB: positionInfo.tokenB.uiAmount,
|
|
85876
85987
|
symbolA,
|
|
85877
85988
|
symbolB,
|
|
85878
|
-
...investmentUsd ? { investmentUsd } : {}
|
|
85989
|
+
...investmentUsd ? { investmentUsd } : {},
|
|
85990
|
+
tokenA: {
|
|
85991
|
+
symbol: symbolA,
|
|
85992
|
+
amount: rawToUi((base === "MintA" ? baseAmount : otherAmountMax).toString(), poolInfo.mintDecimalsA),
|
|
85993
|
+
usd: formatUsd(parseFloat(rawToUi((base === "MintA" ? baseAmount : otherAmountMax).toString(), poolInfo.mintDecimalsA)) * tokenAPriceUsd)
|
|
85994
|
+
},
|
|
85995
|
+
tokenB: {
|
|
85996
|
+
symbol: symbolB,
|
|
85997
|
+
amount: rawToUi((base === "MintA" ? otherAmountMax : baseAmount).toString(), poolInfo.mintDecimalsB),
|
|
85998
|
+
usd: formatUsd(parseFloat(rawToUi((base === "MintA" ? otherAmountMax : baseAmount).toString(), poolInfo.mintDecimalsB)) * tokenBPriceUsd)
|
|
85999
|
+
},
|
|
86000
|
+
currentTokenAUsd: formatUsd(parseFloat(positionInfo.tokenA.uiAmount) * tokenAPriceUsd),
|
|
86001
|
+
currentTokenBUsd: formatUsd(parseFloat(positionInfo.tokenB.uiAmount) * tokenBPriceUsd),
|
|
86002
|
+
totalUsd: formatUsd(
|
|
86003
|
+
parseFloat(rawToUi((base === "MintA" ? baseAmount : otherAmountMax).toString(), poolInfo.mintDecimalsA)) * tokenAPriceUsd + parseFloat(rawToUi((base === "MintA" ? otherAmountMax : baseAmount).toString(), poolInfo.mintDecimalsB)) * tokenBPriceUsd
|
|
86004
|
+
)
|
|
85879
86005
|
};
|
|
85880
86006
|
const balanceWarnings = await checkBalanceSufficiency(
|
|
85881
86007
|
publicKey3,
|
|
@@ -86138,6 +86264,8 @@ Error: ${errMsg}`));
|
|
|
86138
86264
|
const slippage = options.slippage ? parseInt(options.slippage, 10) / 1e4 : getSlippageBps() / 1e4;
|
|
86139
86265
|
if (mode === "dry-run") {
|
|
86140
86266
|
printDryRunBanner();
|
|
86267
|
+
const receiveAUsd = parseFloat(receiveAmountAUi) * tokenAPriceUsd;
|
|
86268
|
+
const receiveBUsd = parseFloat(receiveAmountBUi) * tokenBPriceUsd;
|
|
86141
86269
|
const previewData = {
|
|
86142
86270
|
nftMint: options.nftMint,
|
|
86143
86271
|
poolAddress,
|
|
@@ -86145,12 +86273,17 @@ Error: ${errMsg}`));
|
|
|
86145
86273
|
priceUpper: positionInfo.uiPriceUpper,
|
|
86146
86274
|
percentage: Math.round(percentage * 100) / 100,
|
|
86147
86275
|
tokenAmountA: positionInfo.tokenA.uiAmount,
|
|
86276
|
+
tokenAmountAUsd: formatUsd(tokenAUiAmount * tokenAPriceUsd),
|
|
86148
86277
|
tokenAmountB: positionInfo.tokenB.uiAmount,
|
|
86278
|
+
tokenAmountBUsd: formatUsd(tokenBUiAmount * tokenBPriceUsd),
|
|
86149
86279
|
receiveAmountA: receiveAmountAUi,
|
|
86280
|
+
receiveAmountAUsd: formatUsd(receiveAUsd),
|
|
86150
86281
|
receiveAmountB: receiveAmountBUi,
|
|
86282
|
+
receiveAmountBUsd: formatUsd(receiveBUsd),
|
|
86283
|
+
receiveUsdTotal: formatUsd(receiveAUsd + receiveBUsd),
|
|
86151
86284
|
symbolA,
|
|
86152
86285
|
symbolB,
|
|
86153
|
-
...totalPositionUsd > 0 ? { totalPositionUsd: totalPositionUsd
|
|
86286
|
+
...totalPositionUsd > 0 ? { totalPositionUsd: formatUsd(totalPositionUsd) } : {},
|
|
86154
86287
|
...requestedUsd ? { requestedUsd } : {}
|
|
86155
86288
|
};
|
|
86156
86289
|
if (format === "json") {
|
|
@@ -86241,24 +86374,39 @@ Error: ${errMsg}`));
|
|
|
86241
86374
|
const poolAddress = positionInfo.rawPoolInfo.poolId.toBase58();
|
|
86242
86375
|
let symbolA = positionInfo.tokenA.address.toBase58();
|
|
86243
86376
|
let symbolB = positionInfo.tokenB.address.toBase58();
|
|
86377
|
+
let tokenAPriceUsd = 0;
|
|
86378
|
+
let tokenBPriceUsd = 0;
|
|
86244
86379
|
const poolResult = await api.getPoolInfo(poolAddress);
|
|
86245
86380
|
if (poolResult.ok) {
|
|
86246
86381
|
symbolA = poolResult.value.token_a.symbol || symbolA;
|
|
86247
86382
|
symbolB = poolResult.value.token_b.symbol || symbolB;
|
|
86383
|
+
tokenAPriceUsd = poolResult.value.token_a.price_usd ?? 0;
|
|
86384
|
+
tokenBPriceUsd = poolResult.value.token_b.price_usd ?? 0;
|
|
86248
86385
|
}
|
|
86249
86386
|
if (mode === "dry-run") {
|
|
86250
86387
|
printDryRunBanner();
|
|
86388
|
+
const tokenAmountAVal = parseFloat(positionInfo.tokenA.uiAmount || "0");
|
|
86389
|
+
const tokenAmountBVal = parseFloat(positionInfo.tokenB.uiAmount || "0");
|
|
86390
|
+
const feeAmountAVal = parseFloat(positionInfo.tokenA.uiFeeAmount || "0");
|
|
86391
|
+
const feeAmountBVal = parseFloat(positionInfo.tokenB.uiFeeAmount || "0");
|
|
86251
86392
|
const previewData = {
|
|
86252
86393
|
nftMint: options.nftMint,
|
|
86253
86394
|
poolAddress,
|
|
86254
86395
|
priceLower: positionInfo.uiPriceLower,
|
|
86255
86396
|
priceUpper: positionInfo.uiPriceUpper,
|
|
86256
86397
|
tokenAmountA: positionInfo.tokenA.uiAmount,
|
|
86398
|
+
tokenAmountAUsd: formatUsd(tokenAmountAVal * tokenAPriceUsd),
|
|
86257
86399
|
tokenAmountB: positionInfo.tokenB.uiAmount,
|
|
86400
|
+
tokenAmountBUsd: formatUsd(tokenAmountBVal * tokenBPriceUsd),
|
|
86258
86401
|
feeAmountA: positionInfo.tokenA.uiFeeAmount,
|
|
86402
|
+
feeAmountAUsd: formatUsd(feeAmountAVal * tokenAPriceUsd),
|
|
86259
86403
|
feeAmountB: positionInfo.tokenB.uiFeeAmount,
|
|
86404
|
+
feeAmountBUsd: formatUsd(feeAmountBVal * tokenBPriceUsd),
|
|
86260
86405
|
symbolA,
|
|
86261
|
-
symbolB
|
|
86406
|
+
symbolB,
|
|
86407
|
+
totalUsd: formatUsd(
|
|
86408
|
+
(tokenAmountAVal + feeAmountAVal) * tokenAPriceUsd + (tokenAmountBVal + feeAmountBVal) * tokenBPriceUsd
|
|
86409
|
+
)
|
|
86262
86410
|
};
|
|
86263
86411
|
if (format === "json") {
|
|
86264
86412
|
outputJson({ mode: "dry-run", ...previewData }, startTime);
|
|
@@ -86391,10 +86539,26 @@ Error: ${errMsg}`));
|
|
|
86391
86539
|
}
|
|
86392
86540
|
if (mode === "dry-run") {
|
|
86393
86541
|
printDryRunBanner();
|
|
86542
|
+
const allMints = [...new Set(entries.flatMap((e) => e.tokens.map((t) => t.tokenAddress)))];
|
|
86543
|
+
const pricesResult = await api.getTokenPrices(allMints);
|
|
86544
|
+
const prices = pricesResult.ok ? pricesResult.value : {};
|
|
86545
|
+
const enrichedEntries = entries.map((entry) => {
|
|
86546
|
+
const enrichedTokens = entry.tokens.map((t) => {
|
|
86547
|
+
const uiAmount = parseFloat(t.tokenAmount) / Math.pow(10, t.tokenDecimals);
|
|
86548
|
+
const price = prices[t.tokenAddress] ?? 0;
|
|
86549
|
+
return { ...t, amountUsd: formatUsd(uiAmount * price) };
|
|
86550
|
+
});
|
|
86551
|
+
const totalUsd = enrichedTokens.reduce((sum3, t) => {
|
|
86552
|
+
const uiAmount = parseFloat(t.tokenAmount) / Math.pow(10, t.tokenDecimals);
|
|
86553
|
+
const price = prices[t.tokenAddress] ?? 0;
|
|
86554
|
+
return sum3 + uiAmount * price;
|
|
86555
|
+
}, 0);
|
|
86556
|
+
return { ...entry, tokens: enrichedTokens, totalUsd: formatUsd(totalUsd) };
|
|
86557
|
+
});
|
|
86394
86558
|
if (format === "json") {
|
|
86395
|
-
outputJson({ mode: "dry-run", entries }, startTime);
|
|
86559
|
+
outputJson({ mode: "dry-run", entries: enrichedEntries }, startTime);
|
|
86396
86560
|
} else {
|
|
86397
|
-
outputPositionClaimPreview(
|
|
86561
|
+
outputPositionClaimPreview(enrichedEntries);
|
|
86398
86562
|
console.log(source_default.yellow("\n Remove --dry-run to generate the unsigned transaction(s)"));
|
|
86399
86563
|
}
|
|
86400
86564
|
return;
|
|
@@ -86437,13 +86601,24 @@ function createPositionsClaimRewardsCommand() {
|
|
|
86437
86601
|
const openRewards = filterUnclaimed(unclaimedOpenIncentives);
|
|
86438
86602
|
const closedRewards = filterUnclaimed(unclaimedClosedIncentives);
|
|
86439
86603
|
const allRewards = [...openRewards, ...closedRewards];
|
|
86604
|
+
const enrichRewardItem = (item) => {
|
|
86605
|
+
const unclaimed = parseFloat(item.syncedTokenAmount) - parseFloat(item.lockedTokenAmount) - parseFloat(item.claimedTokenAmount);
|
|
86606
|
+
const price = parseFloat(item.price || "0");
|
|
86607
|
+
return {
|
|
86608
|
+
...item,
|
|
86609
|
+
unclaimedAmount: unclaimed.toString(),
|
|
86610
|
+
unclaimedAmountUsd: formatUsd(unclaimed * price)
|
|
86611
|
+
};
|
|
86612
|
+
};
|
|
86440
86613
|
if (mode === "dry-run") {
|
|
86441
86614
|
printDryRunBanner();
|
|
86442
86615
|
if (format === "json") {
|
|
86616
|
+
const enrichedOpen = openRewards.map(enrichRewardItem);
|
|
86617
|
+
const enrichedClosed = closedRewards.map(enrichRewardItem);
|
|
86443
86618
|
outputJson({
|
|
86444
86619
|
mode: "dry-run",
|
|
86445
|
-
openPositionRewards:
|
|
86446
|
-
closedPositionRewards:
|
|
86620
|
+
openPositionRewards: enrichedOpen,
|
|
86621
|
+
closedPositionRewards: enrichedClosed,
|
|
86447
86622
|
totalPositions: new Set(allRewards.map((r) => r.positionAddress)).size
|
|
86448
86623
|
}, startTime);
|
|
86449
86624
|
} else {
|
|
@@ -86770,12 +86945,12 @@ Error: ${errMsg}`));
|
|
|
86770
86945
|
inRange
|
|
86771
86946
|
},
|
|
86772
86947
|
performance: {
|
|
86773
|
-
liquidityUsd: liquidityUsd
|
|
86774
|
-
earnedUsd: earnedUsd
|
|
86948
|
+
liquidityUsd: formatUsd(liquidityUsd),
|
|
86949
|
+
earnedUsd: formatUsd(earnedUsd),
|
|
86775
86950
|
earnedPercent: `${parseFloat(String(earnedPercent)).toFixed(2)}%`,
|
|
86776
|
-
pnlUsd: pnlUsd.
|
|
86951
|
+
pnlUsd: pnlUsd < 0 ? `-${formatUsd(Math.abs(pnlUsd))}` : formatUsd(pnlUsd),
|
|
86777
86952
|
pnlPercent: `${parseFloat(String(pnlPercent)).toFixed(2)}%`,
|
|
86778
|
-
netReturnUsd: netReturnUsd.
|
|
86953
|
+
netReturnUsd: netReturnUsd < 0 ? `-${formatUsd(Math.abs(netReturnUsd))}` : formatUsd(netReturnUsd),
|
|
86779
86954
|
netReturnPercent: `${parseFloat(netReturnPercent).toFixed(2)}%`
|
|
86780
86955
|
},
|
|
86781
86956
|
rangeHealth: {
|
|
@@ -86787,20 +86962,29 @@ Error: ${errMsg}`));
|
|
|
86787
86962
|
},
|
|
86788
86963
|
poolContext: {
|
|
86789
86964
|
feeApr24h: `${pool.apr.toFixed(2)}%`,
|
|
86790
|
-
volume24h: pool.volume_24h_usd
|
|
86791
|
-
tvl: pool.tvl_usd
|
|
86965
|
+
volume24h: formatUsd(pool.volume_24h_usd),
|
|
86966
|
+
tvl: formatUsd(pool.tvl_usd),
|
|
86792
86967
|
priceChange24h: `${(pool.price_change_24h || 0).toFixed(2)}%`
|
|
86793
86968
|
},
|
|
86794
|
-
unclaimedFees: {
|
|
86795
|
-
tokenA
|
|
86796
|
-
|
|
86797
|
-
|
|
86798
|
-
|
|
86799
|
-
|
|
86800
|
-
|
|
86801
|
-
|
|
86802
|
-
|
|
86803
|
-
|
|
86969
|
+
unclaimedFees: (() => {
|
|
86970
|
+
const feeAmountA = parseFloat(positionInfo.tokenA.uiFeeAmount || "0");
|
|
86971
|
+
const feeAmountB = parseFloat(positionInfo.tokenB.uiFeeAmount || "0");
|
|
86972
|
+
const feeAUsd = feeAmountA * (pool.token_a.price_usd ?? 0);
|
|
86973
|
+
const feeBUsd = feeAmountB * (pool.token_b.price_usd ?? 0);
|
|
86974
|
+
return {
|
|
86975
|
+
tokenA: {
|
|
86976
|
+
symbol: symbolA,
|
|
86977
|
+
amount: positionInfo.tokenA.uiFeeAmount,
|
|
86978
|
+
amountUsd: formatUsd(feeAUsd)
|
|
86979
|
+
},
|
|
86980
|
+
tokenB: {
|
|
86981
|
+
symbol: symbolB,
|
|
86982
|
+
amount: positionInfo.tokenB.uiFeeAmount,
|
|
86983
|
+
amountUsd: formatUsd(feeBUsd)
|
|
86984
|
+
},
|
|
86985
|
+
totalUsd: formatUsd(feeAUsd + feeBUsd)
|
|
86986
|
+
};
|
|
86987
|
+
})()
|
|
86804
86988
|
};
|
|
86805
86989
|
if (format === "json") {
|
|
86806
86990
|
outputJson(analysisData, startTime);
|