@net-protocol/cli 0.1.48 → 0.2.1

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.
@@ -8,6 +8,8 @@ declare function registerUpvoteUserCommand(parent: Command, commandName?: string
8
8
 
9
9
  declare function registerGetUserUpvotesCommand(parent: Command, commandName?: string): void;
10
10
 
11
+ declare function registerRankingsCommand(parent: Command, commandName?: string): void;
12
+
11
13
  declare function registerUpvoteCommand(program: Command): void;
12
14
 
13
- export { registerGetUpvotesCommand, registerGetUserUpvotesCommand, registerUpvoteCommand, registerUpvoteTokenCommand, registerUpvoteUserCommand };
15
+ export { registerGetUpvotesCommand, registerGetUserUpvotesCommand, registerRankingsCommand, registerUpvoteCommand, registerUpvoteTokenCommand, registerUpvoteUserCommand };
@@ -2,7 +2,7 @@ import chalk3 from 'chalk';
2
2
  import { createPublicClient, http, parseEther, formatEther, encodeFunctionData, concat, createWalletClient } from 'viem';
3
3
  import { privateKeyToAccount } from 'viem/accounts';
4
4
  import { getChainRpcUrls, getBaseDataSuffix, getChainSlug } from '@net-protocol/core';
5
- import { discoverTokenPool, PURE_ALPHA_STRATEGY, UNIV234_POOLS_STRATEGY, encodePoolKey, DYNAMIC_SPLIT_STRATEGY, getTokenScoreKey, UPVOTE_PRICE_ETH, UPVOTE_APP, ScoreClient, ALL_STRATEGY_ADDRESSES, NULL_ADDRESS, UserUpvoteClient, calculateUpvoteCost, USER_UPVOTE_CONTRACT } from '@net-protocol/score';
5
+ import { discoverTokenPool, PURE_ALPHA_STRATEGY, UNIV234_POOLS_STRATEGY, encodePoolKey, DYNAMIC_SPLIT_STRATEGY, getTokenScoreKey, UPVOTE_PRICE_ETH, UPVOTE_APP, ScoreClient, ALL_STRATEGY_ADDRESSES, NULL_ADDRESS, UserUpvoteClient, calculateUpvoteCost, USER_UPVOTE_CONTRACT, getTokenRankings } from '@net-protocol/score';
6
6
  import '@net-protocol/storage';
7
7
 
8
8
  // src/commands/upvote/upvote-token.ts
@@ -496,6 +496,155 @@ function registerGetUserUpvotesCommand(parent, commandName = "user-info") {
496
496
  await executeGetUserUpvotes(options);
497
497
  });
498
498
  }
499
+ var VALID_SORTS = ["trending", "recent", "top"];
500
+ function formatNumber(n, digits = 2) {
501
+ if (n == null || !Number.isFinite(n)) return "-";
502
+ if (n >= 1e9) return `${(n / 1e9).toFixed(digits)}B`;
503
+ if (n >= 1e6) return `${(n / 1e6).toFixed(digits)}M`;
504
+ if (n >= 1e3) return `${(n / 1e3).toFixed(digits)}K`;
505
+ return n.toFixed(digits);
506
+ }
507
+ function formatPrice(price) {
508
+ if (price == null || !Number.isFinite(price)) return "-";
509
+ if (price < 1e-6) return price.toExponential(2);
510
+ if (price < 1) return `$${price.toFixed(6)}`;
511
+ return `$${price.toFixed(4)}`;
512
+ }
513
+ async function executeRankings(options) {
514
+ const sort = (options.sort ?? "trending").toLowerCase();
515
+ if (!VALID_SORTS.includes(sort)) {
516
+ exitWithError(
517
+ `Invalid --sort "${options.sort}". Must be one of: ${VALID_SORTS.join(", ")}`
518
+ );
519
+ return;
520
+ }
521
+ const limit = options.limit ?? 10;
522
+ if (!Number.isFinite(limit) || limit < 1 || limit > 100) {
523
+ exitWithError("Invalid --limit. Must be an integer between 1 and 100.");
524
+ return;
525
+ }
526
+ const optionalPositiveInt = (value, name, { allowZero = false } = {}) => {
527
+ if (value === void 0) return void 0;
528
+ if (!Number.isFinite(value) || (allowZero ? value < 0 : value < 1)) {
529
+ exitWithError(
530
+ `Invalid ${name}. Must be a ${allowZero ? "non-negative" : "positive"} integer.`
531
+ );
532
+ }
533
+ return value;
534
+ };
535
+ const scanWindow = optionalPositiveInt(options.scanWindow, "--scan-window");
536
+ const minUpvotes = optionalPositiveInt(options.minUpvotes, "--min-upvotes", {
537
+ allowZero: true
538
+ });
539
+ const minMarketCap = optionalPositiveInt(
540
+ options.minMarketCap,
541
+ "--min-market-cap",
542
+ { allowZero: true }
543
+ );
544
+ const recencyHours = optionalPositiveInt(
545
+ options.recencyHours,
546
+ "--recency-hours",
547
+ { allowZero: true }
548
+ );
549
+ if (options.chainId !== void 0 && !Number.isFinite(options.chainId)) {
550
+ exitWithError("Invalid --chain-id. Must be an integer.");
551
+ return;
552
+ }
553
+ const readOnlyOptions = parseReadOnlyOptionsWithDefault({
554
+ chainId: options.chainId,
555
+ rpcUrl: options.rpcUrl
556
+ });
557
+ try {
558
+ const tokens = await getTokenRankings({
559
+ chainId: readOnlyOptions.chainId,
560
+ sort,
561
+ maxTokens: limit,
562
+ messageScanWindow: scanWindow,
563
+ thresholds: minUpvotes != null || minMarketCap != null || recencyHours != null ? {
564
+ minUpvotes,
565
+ minMarketCap,
566
+ recencyHours
567
+ } : void 0,
568
+ rpcUrl: readOnlyOptions.rpcUrl
569
+ });
570
+ if (options.json) {
571
+ console.log(
572
+ JSON.stringify(
573
+ {
574
+ chainId: readOnlyOptions.chainId,
575
+ sort,
576
+ count: tokens.length,
577
+ tokens: tokens.map((t) => ({
578
+ ...t,
579
+ url: tokenUrl(readOnlyOptions.chainId, t.address)
580
+ }))
581
+ },
582
+ null,
583
+ 2
584
+ )
585
+ );
586
+ return;
587
+ }
588
+ if (tokens.length === 0) {
589
+ console.log(chalk3.yellow("No tokens found."));
590
+ return;
591
+ }
592
+ console.log(
593
+ chalk3.white(
594
+ `Top ${tokens.length} tokens by ${sort} on chain ${readOnlyOptions.chainId}:`
595
+ )
596
+ );
597
+ console.log();
598
+ tokens.forEach((t, i) => {
599
+ const rank = chalk3.dim(`#${(i + 1).toString().padStart(2, " ")}`);
600
+ const symbol = chalk3.cyan((t.symbol || "?").padEnd(10, " "));
601
+ const upvotes = chalk3.white(`${t.upvotes} upvotes`.padEnd(18, " "));
602
+ const fdv = chalk3.dim(`FDV ${formatNumber(t.fdv)}`.padEnd(14, " "));
603
+ const price = chalk3.dim(`${formatPrice(t.priceInUsdc)}`.padEnd(14, " "));
604
+ console.log(`${rank} ${symbol} ${upvotes} ${fdv} ${price} ${t.address}`);
605
+ });
606
+ } catch (error) {
607
+ exitWithError(
608
+ `Failed to fetch token rankings: ${error instanceof Error ? error.message : String(error)}`
609
+ );
610
+ }
611
+ }
612
+ function registerRankingsCommand(parent, commandName = "rankings") {
613
+ parent.command(commandName).description(
614
+ "List tokens ranked by upvote activity (trending / recent / top)"
615
+ ).option(
616
+ "--sort <sort>",
617
+ `Ranking strategy: ${VALID_SORTS.join(" | ")} (default: trending)`,
618
+ "trending"
619
+ ).option(
620
+ "--limit <n>",
621
+ "Number of tokens to return (1-100, default: 50)",
622
+ (v) => parseInt(v, 10),
623
+ 50
624
+ ).option(
625
+ "--scan-window <n>",
626
+ "Messages to scan per contract (default: 150)",
627
+ (v) => parseInt(v, 10)
628
+ ).option(
629
+ "--min-upvotes <n>",
630
+ "Floor for two-tier filter (default: 500)",
631
+ (v) => parseInt(v, 10)
632
+ ).option(
633
+ "--min-market-cap <n>",
634
+ "FDV floor in USDC (default: 40000)",
635
+ (v) => parseInt(v, 10)
636
+ ).option(
637
+ "--recency-hours <n>",
638
+ "Drop below-floor tokens with no upvote within N hours (default: 48)",
639
+ (v) => parseInt(v, 10)
640
+ ).option(
641
+ "--chain-id <id>",
642
+ "Chain ID (default: 8453 for Base)",
643
+ (v) => parseInt(v, 10)
644
+ ).option("--rpc-url <url>", "Custom RPC URL").option("--json", "Output in JSON format").action(async (options) => {
645
+ await executeRankings(options);
646
+ });
647
+ }
499
648
 
500
649
  // src/commands/upvote/index.ts
501
650
  function registerUpvoteCommand(program) {
@@ -504,8 +653,9 @@ function registerUpvoteCommand(program) {
504
653
  registerGetUpvotesCommand(upvoteCommand);
505
654
  registerUpvoteUserCommand(upvoteCommand);
506
655
  registerGetUserUpvotesCommand(upvoteCommand);
656
+ registerRankingsCommand(upvoteCommand);
507
657
  }
508
658
 
509
- export { registerGetUpvotesCommand, registerGetUserUpvotesCommand, registerUpvoteCommand, registerUpvoteTokenCommand, registerUpvoteUserCommand };
659
+ export { registerGetUpvotesCommand, registerGetUserUpvotesCommand, registerRankingsCommand, registerUpvoteCommand, registerUpvoteTokenCommand, registerUpvoteUserCommand };
510
660
  //# sourceMappingURL=index.mjs.map
511
661
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/shared.ts","../../src/shared/wallet.ts","../../src/shared/encode.ts","../../src/shared/output.ts","../../src/commands/upvote/upvote-token.ts","../../src/shared/urls.ts","../../src/commands/upvote/get-upvotes.ts","../../src/commands/upvote/upvote-user.ts","../../src/commands/upvote/get-user-upvotes.ts","../../src/commands/upvote/index.ts"],"names":["chalk","getBaseDataSuffix","getChainRpcUrls","http","PURE_ALPHA_STRATEGY","UNIV234_POOLS_STRATEGY","DYNAMIC_SPLIT_STRATEGY","getTokenScoreKey","UserUpvoteClient","formatEther"],"mappings":"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","file":"index.mjs","sourcesContent":["import chalk from \"chalk\";\nimport type { CommonOptions, ReadOnlyOptions } from \"../shared/types\";\n\n/**\n * Default chain ID (Base mainnet) - used by feed commands\n */\nexport const DEFAULT_CHAIN_ID = 8453;\n\n/**\n * Get chain ID from option or environment variable, exit if not found\n */\nfunction getRequiredChainId(optionValue?: number): number {\n const chainId =\n optionValue ||\n (process.env.NET_CHAIN_ID\n ? parseInt(process.env.NET_CHAIN_ID, 10)\n : undefined);\n\n if (!chainId) {\n console.error(\n chalk.red(\n \"Error: Chain ID is required. Provide via --chain-id flag or NET_CHAIN_ID environment variable\"\n )\n );\n process.exit(1);\n }\n\n return chainId;\n}\n\n/**\n * Get chain ID from option or environment variable, defaulting to Base (8453)\n * Also checks BOTCHAN_* env vars for backward compat\n */\nfunction getChainIdWithDefault(optionValue?: number): number {\n if (optionValue) {\n return optionValue;\n }\n\n const envChainId =\n process.env.BOTCHAN_CHAIN_ID || process.env.NET_CHAIN_ID;\n\n if (envChainId) {\n return parseInt(envChainId, 10);\n }\n\n return DEFAULT_CHAIN_ID;\n}\n\n/**\n * Get RPC URL from option or environment variable\n */\nfunction getRpcUrl(optionValue?: string): string | undefined {\n return optionValue || process.env.NET_RPC_URL;\n}\n\n/**\n * Get RPC URL from option or environment variable, also checking BOTCHAN_* env vars.\n * Used only by feed commands for backward compat.\n */\nfunction getRpcUrlWithBotchanFallback(optionValue?: string): string | undefined {\n return optionValue || process.env.BOTCHAN_RPC_URL || process.env.NET_RPC_URL;\n}\n\n/**\n * Parse and validate common options shared across all commands.\n * Extracts private key, chain ID, and RPC URL from command options or environment variables.\n * @param options - Command options\n * @param supportsEncodeOnly - If true, mention --encode-only in error messages as an alternative\n */\nexport function parseCommonOptions(\n options: {\n privateKey?: string;\n chainId?: number;\n rpcUrl?: string;\n },\n supportsEncodeOnly = false\n): CommonOptions {\n const privateKey =\n options.privateKey ||\n process.env.NET_PRIVATE_KEY ||\n process.env.PRIVATE_KEY;\n\n if (!privateKey) {\n const encodeOnlyHint = supportsEncodeOnly\n ? \", or use --encode-only to output transaction data without submitting\"\n : \"\";\n console.error(\n chalk.red(\n `Error: Private key is required. Provide via --private-key flag or NET_PRIVATE_KEY/PRIVATE_KEY environment variable${encodeOnlyHint}`\n )\n );\n process.exit(1);\n }\n\n if (!privateKey.startsWith(\"0x\") || privateKey.length !== 66) {\n console.error(\n chalk.red(\n \"Error: Invalid private key format (must be 0x-prefixed, 66 characters)\"\n )\n );\n process.exit(1);\n }\n\n if (options.privateKey) {\n console.warn(\n chalk.yellow(\n \"Warning: Private key provided via command line. Consider using NET_PRIVATE_KEY environment variable instead.\"\n )\n );\n }\n\n return {\n privateKey: privateKey as `0x${string}`,\n chainId: getRequiredChainId(options.chainId),\n rpcUrl: getRpcUrl(options.rpcUrl),\n };\n}\n\n/**\n * Parse and validate read-only options for commands that don't need a private key.\n * Extracts chain ID and RPC URL from command options or environment variables.\n */\nexport function parseReadOnlyOptions(options: {\n chainId?: number;\n rpcUrl?: string;\n}): ReadOnlyOptions {\n return {\n chainId: getRequiredChainId(options.chainId),\n rpcUrl: getRpcUrl(options.rpcUrl),\n };\n}\n\n/**\n * Parse read-only options with a default chain ID (8453/Base).\n * Used by feed commands where chain ID is optional.\n * Also checks BOTCHAN_* env vars for backward compat.\n */\nexport function parseReadOnlyOptionsWithDefault(options: {\n chainId?: number;\n rpcUrl?: string;\n}): ReadOnlyOptions {\n return {\n chainId: getChainIdWithDefault(options.chainId),\n rpcUrl: getRpcUrlWithBotchanFallback(options.rpcUrl),\n };\n}\n\n/**\n * Parse common options with a default chain ID (8453/Base).\n * Used by feed write commands where chain ID is optional.\n * Also checks BOTCHAN_* env vars for backward compat.\n */\nexport function parseCommonOptionsWithDefault(\n options: {\n privateKey?: string;\n chainId?: number;\n rpcUrl?: string;\n },\n supportsEncodeOnly = false\n): CommonOptions {\n const privateKey =\n options.privateKey ||\n process.env.BOTCHAN_PRIVATE_KEY ||\n process.env.NET_PRIVATE_KEY ||\n process.env.PRIVATE_KEY;\n\n if (!privateKey) {\n const encodeOnlyHint = supportsEncodeOnly\n ? \", or use --encode-only to output transaction data without submitting\"\n : \"\";\n console.error(\n chalk.red(\n `Error: Private key is required. Provide via --private-key flag or NET_PRIVATE_KEY/BOTCHAN_PRIVATE_KEY environment variable${encodeOnlyHint}`\n )\n );\n process.exit(1);\n }\n\n if (!privateKey.startsWith(\"0x\") || privateKey.length !== 66) {\n console.error(\n chalk.red(\n \"Error: Invalid private key format (must be 0x-prefixed, 66 characters)\"\n )\n );\n process.exit(1);\n }\n\n if (options.privateKey) {\n console.warn(\n chalk.yellow(\n \"Warning: Private key provided via command line. Consider using NET_PRIVATE_KEY environment variable instead.\"\n )\n );\n }\n\n return {\n privateKey: privateKey as `0x${string}`,\n chainId: getChainIdWithDefault(options.chainId),\n rpcUrl: getRpcUrlWithBotchanFallback(options.rpcUrl),\n };\n}\n","import { createWalletClient, http } from \"viem\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { getChainRpcUrls, getBaseDataSuffix } from \"@net-protocol/core\";\nimport type { WriteTransactionConfig } from \"@net-protocol/core\";\n\n/**\n * Create a wallet client from a private key\n */\nexport function createWallet(\n privateKey: `0x${string}`,\n chainId: number,\n rpcUrl?: string\n) {\n const account = privateKeyToAccount(privateKey);\n const rpcUrls = getChainRpcUrls({\n chainId,\n rpcUrl: rpcUrl,\n });\n\n return createWalletClient({\n account,\n transport: http(rpcUrls[0]),\n dataSuffix: getBaseDataSuffix(chainId),\n });\n}\n\n/**\n * Execute a transaction using a wallet client\n */\nexport async function executeTransaction(\n walletClient: ReturnType<typeof createWallet>,\n txConfig: WriteTransactionConfig\n): Promise<`0x${string}`> {\n const hash = await walletClient.writeContract({\n address: txConfig.to,\n abi: txConfig.abi,\n functionName: txConfig.functionName,\n args: txConfig.args,\n value: txConfig.value,\n chain: null,\n } as Parameters<typeof walletClient.writeContract>[0]);\n\n return hash;\n}\n","import { encodeFunctionData, concat } from \"viem\";\nimport { getBaseDataSuffix } from \"@net-protocol/core\";\nimport type { WriteTransactionConfig } from \"@net-protocol/core\";\nimport type { EncodedTransaction } from \"./types\";\n\nexport type { EncodedTransaction };\n\n/**\n * Encode a write transaction config into transaction data\n * Used for --encode-only mode where we output transaction data instead of executing\n */\nexport function encodeTransaction(\n config: WriteTransactionConfig,\n chainId: number\n): EncodedTransaction {\n const calldata = encodeFunctionData({\n abi: config.abi,\n functionName: config.functionName,\n args: config.args,\n });\n\n const suffix = getBaseDataSuffix(chainId);\n const data = suffix ? concat([calldata, suffix]) : calldata;\n\n return {\n to: config.to,\n data,\n chainId,\n value: config.value?.toString() ?? \"0\",\n };\n}\n","import chalk from \"chalk\";\nimport type { NetMessage } from \"@net-protocol/core\";\n\n/**\n * Format a message for human-readable output\n */\nexport function formatMessage(\n message: NetMessage,\n index: number\n): string {\n const timestamp = new Date(Number(message.timestamp) * 1000).toISOString();\n const lines = [\n chalk.cyan(`[${index}]`) + ` ${chalk.gray(timestamp)}`,\n ` ${chalk.white(\"Sender:\")} ${message.sender}`,\n ` ${chalk.white(\"App:\")} ${message.app}`,\n ];\n\n if (message.topic) {\n lines.push(` ${chalk.white(\"Topic:\")} ${message.topic}`);\n }\n\n lines.push(` ${chalk.white(\"Text:\")} ${message.text}`);\n\n if (message.data && message.data !== \"0x\") {\n lines.push(` ${chalk.white(\"Data:\")} ${message.data}`);\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format a message for JSON output\n */\nexport function messageToJson(\n message: NetMessage,\n index: number\n): Record<string, unknown> {\n return {\n index,\n sender: message.sender,\n app: message.app,\n timestamp: Number(message.timestamp),\n text: message.text,\n topic: message.topic,\n data: message.data,\n };\n}\n\n/**\n * Print messages in human-readable or JSON format\n */\nexport function printMessages(\n messages: NetMessage[],\n startIndex: number,\n json: boolean\n): void {\n if (json) {\n const output = messages.map((msg, i) => messageToJson(msg, startIndex + i));\n console.log(JSON.stringify(output, null, 2));\n } else {\n if (messages.length === 0) {\n console.log(chalk.yellow(\"No messages found\"));\n return;\n }\n\n messages.forEach((msg, i) => {\n console.log(formatMessage(msg, startIndex + i));\n if (i < messages.length - 1) {\n console.log(); // Empty line between messages\n }\n });\n }\n}\n\n/**\n * Print a count result\n */\nexport function printCount(\n count: number,\n label: string,\n json: boolean\n): void {\n if (json) {\n console.log(JSON.stringify({ count }, null, 2));\n } else {\n console.log(`${chalk.white(label)} ${chalk.cyan(count)}`);\n }\n}\n\n/**\n * Print an error message and exit\n */\nexport function exitWithError(message: string): never {\n console.error(chalk.red(`Error: ${message}`));\n process.exit(1);\n}\n","import chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createPublicClient, http, parseEther } from \"viem\";\nimport {\n parseReadOnlyOptionsWithDefault,\n parseCommonOptionsWithDefault,\n} from \"../../cli/shared\";\nimport { createWallet, executeTransaction } from \"../../shared/wallet\";\nimport { encodeTransaction } from \"../../shared/encode\";\nimport { exitWithError } from \"../../shared/output\";\nimport { getChainRpcUrls } from \"@net-protocol/core\";\nimport {\n discoverTokenPool,\n getTokenScoreKey,\n encodePoolKey,\n UPVOTE_APP,\n PURE_ALPHA_STRATEGY,\n DYNAMIC_SPLIT_STRATEGY,\n UNIV234_POOLS_STRATEGY,\n UPVOTE_PRICE_ETH,\n} from \"@net-protocol/score\";\nimport type { UpvoteTokenOptions } from \"./types\";\n\nasync function executeUpvoteToken(options: UpvoteTokenOptions): Promise<void> {\n const count = parseInt(options.count, 10);\n if (isNaN(count) || count <= 0) {\n exitWithError(\"Count must be a positive integer\");\n return;\n }\n\n const tokenAddress = options.tokenAddress;\n if (!tokenAddress.startsWith(\"0x\") || tokenAddress.length !== 42) {\n exitWithError(\n \"Invalid token address format (must be 0x-prefixed, 42 characters)\"\n );\n return;\n }\n\n // Read-only options for pool discovery (always needed)\n const readOnlyOptions = parseReadOnlyOptionsWithDefault({\n chainId: options.chainId,\n rpcUrl: options.rpcUrl,\n });\n\n // Create public client for pool discovery\n const rpcUrls = getChainRpcUrls({\n chainId: readOnlyOptions.chainId,\n rpcUrl: readOnlyOptions.rpcUrl,\n });\n const publicClient = createPublicClient({\n transport: http(rpcUrls[0]),\n });\n\n console.log(chalk.blue(\"Discovering Uniswap pool for token...\"));\n\n let poolResult;\n try {\n poolResult = await discoverTokenPool({\n publicClient,\n tokenAddress,\n chainId: readOnlyOptions.chainId,\n });\n } catch (error) {\n exitWithError(\n `Failed to discover token pool: ${error instanceof Error ? error.message : String(error)}`\n );\n return;\n }\n\n // Determine strategy\n let strategyAddress: `0x${string}`;\n let storedContext: `0x${string}`;\n\n if (!poolResult || !poolResult.poolKey) {\n // No pool found → pure alpha\n strategyAddress = PURE_ALPHA_STRATEGY.address;\n storedContext = \"0x\";\n console.log(chalk.yellow(\"No pool found — using Pure Alpha strategy\"));\n } else if (options.splitType === \"50/50\") {\n // Pool found + 50/50 override → UNIV234_POOLS_STRATEGY\n strategyAddress = UNIV234_POOLS_STRATEGY.address;\n storedContext = encodePoolKey(poolResult.poolKey);\n console.log(\n chalk.green(\n `Pool found (fee: ${poolResult.fee}) — using 50/50 Pools strategy`\n )\n );\n } else {\n // Pool found + default/dynamic → DYNAMIC_SPLIT_STRATEGY\n strategyAddress = DYNAMIC_SPLIT_STRATEGY.address;\n storedContext = encodePoolKey(poolResult.poolKey);\n console.log(\n chalk.green(\n `Pool found (fee: ${poolResult.fee}) — using Dynamic Split strategy`\n )\n );\n }\n\n // Build transaction config\n const scoreKey = getTokenScoreKey(tokenAddress);\n const value = parseEther((count * UPVOTE_PRICE_ETH).toString());\n\n const txConfig = {\n to: UPVOTE_APP.address,\n abi: UPVOTE_APP.abi,\n functionName: \"upvote\" as const,\n args: [strategyAddress, scoreKey, count, storedContext, \"0x\"],\n value,\n };\n\n if (options.encodeOnly) {\n const encoded = encodeTransaction(txConfig, readOnlyOptions.chainId);\n console.log(JSON.stringify(encoded, null, 2));\n return;\n }\n\n // Execute transaction\n const commonOptions = parseCommonOptionsWithDefault(\n {\n privateKey: options.privateKey,\n chainId: options.chainId,\n rpcUrl: options.rpcUrl,\n },\n true\n );\n\n const walletClient = createWallet(\n commonOptions.privateKey,\n commonOptions.chainId,\n commonOptions.rpcUrl\n );\n\n console.log(\n chalk.blue(`Submitting ${count} upvote(s) for ${tokenAddress}...`)\n );\n\n try {\n const hash = await executeTransaction(walletClient, txConfig);\n\n console.log(chalk.green(`Upvote submitted successfully!`));\n console.log(chalk.white(` Transaction: ${hash}`));\n console.log(chalk.white(` Token: ${tokenAddress}`));\n console.log(chalk.white(` Count: ${count}`));\n console.log(\n chalk.white(` Value: ${(count * UPVOTE_PRICE_ETH).toFixed(6)} ETH`)\n );\n } catch (error) {\n exitWithError(\n `Failed to submit upvote: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\nexport function registerUpvoteTokenCommand(\n parent: Command,\n commandName = \"token\"\n): void {\n parent\n .command(commandName)\n .description(\"Upvote a token on Net Protocol\")\n .requiredOption(\n \"--token-address <address>\",\n \"Token contract address to upvote\"\n )\n .requiredOption(\"--count <n>\", \"Number of upvotes\")\n .option(\n \"--split-type <type>\",\n 'Strategy split type: \"dynamic\" (default) or \"50/50\"'\n )\n .option(\"--chain-id <id>\", \"Chain ID (default: 8453 for Base)\", (value) =>\n parseInt(value, 10)\n )\n .option(\"--rpc-url <url>\", \"Custom RPC URL\")\n .option(\"--private-key <key>\", \"Private key (0x-prefixed)\")\n .option(\n \"--encode-only\",\n \"Output transaction data as JSON instead of executing\"\n )\n .action(async (options) => {\n await executeUpvoteToken(options);\n });\n}\n","/**\n * URL builders for Net Protocol web pages and external services.\n *\n * These helpers exist so callers (and AI agents reading CLI JSON output) never\n * need to construct URLs by hand. URL grammar quirks (the \"feed-\" topic\n * prefix, hyphen-vs-colon separators in comment IDs, lowercased addresses,\n * etc.) all live here in one place.\n *\n * Chain slugs and block-explorer base URLs come from\n * `@net-protocol/core`'s chain config — this module deliberately holds no\n * per-chain tables of its own.\n */\n\nimport {\n getChainBlockExplorer,\n getChainSlug,\n} from \"@net-protocol/core\";\nimport { encodeStorageKeyForUrl } from \"@net-protocol/storage\";\n\nconst WEBSITE_BASE = \"https://netprotocol.app\";\nconst STORAGE_BASE = \"https://storedon.net\";\n\nexport function chainSlug(chainId: number): string | null {\n return getChainSlug({ chainId }) ?? null;\n}\n\n/**\n * Strip the \"feed-\" prefix from a topic if present (case-insensitive).\n * Used when converting an on-chain topic to the URL path/query form.\n */\nfunction stripFeedPrefix(topic: string): string {\n const lower = topic.toLowerCase();\n return lower.startsWith(\"feed-\") ? lower.slice(5) : lower;\n}\n\n/**\n * URL of a feed page. `feedName` may be passed with or without the \"feed-\"\n * prefix; either way the URL form is stripped+lowercased.\n */\nexport function feedUrl(chainId: number, feedName: string): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n return `${WEBSITE_BASE}/app/feed/${slug}/${stripFeedPrefix(feedName)}`;\n}\n\n/**\n * URL of an address's wall (their personal feed at `feed-{lower(address)}`).\n */\nexport function walletUrl(chainId: number, address: string): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n return `${WEBSITE_BASE}/app/feed/${slug}/${address.toLowerCase()}`;\n}\n\n/**\n * URL of a user/agent profile page.\n */\nexport function profileUrl(chainId: number, address: string): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n return `${WEBSITE_BASE}/app/profile/${slug}/${address.toLowerCase()}`;\n}\n\n/**\n * URL of a group chat page.\n */\nexport function chatUrl(chainId: number, chatName: string): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n return `${WEBSITE_BASE}/app/chat/${slug}/${encodeURIComponent(\n chatName.toLowerCase()\n )}`;\n}\n\n/**\n * URL of a Netr token page.\n */\nexport function tokenUrl(\n chainId: number,\n tokenAddress: string\n): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n return `${WEBSITE_BASE}/app/token/${slug}/${tokenAddress.toLowerCase()}`;\n}\n\n/**\n * URL of a Bazaar NFT collection page.\n */\nexport function bazaarUrl(\n chainId: number,\n nftAddress: string\n): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n return `${WEBSITE_BASE}/app/bazaar/${slug}/${nftAddress.toLowerCase()}`;\n}\n\n/**\n * URL of an onchain agent detail page.\n */\nexport function agentUrl(chainId: number, agentId: string): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n return `${WEBSITE_BASE}/app/agents/${slug}/${encodeURIComponent(agentId)}`;\n}\n\n/**\n * Public storage retrieval URL via storedon.net. Works on any chain with Net\n * Storage deployed (returns a URL even when chainSlug is unknown — storedon\n * uses numeric chain IDs).\n *\n * Uses `encodeStorageKeyForUrl` from `@net-protocol/storage` so the encoder\n * stays in sync with the storage SDK if it ever needs to handle binary keys\n * or other special characters differently from `encodeURIComponent`.\n */\nexport function storageUrl(\n chainId: number,\n operatorAddress: string,\n key: string\n): string {\n return `${STORAGE_BASE}/net/${chainId}/storage/load/${operatorAddress.toLowerCase()}/${encodeStorageKeyForUrl(\n key\n )}`;\n}\n\nexport function explorerTxUrl(\n chainId: number,\n txHash: string\n): string | null {\n const base = getChainBlockExplorer({ chainId })?.url;\n if (!base) return null;\n return `${base}/tx/${txHash}`;\n}\n\nexport function explorerAddressUrl(\n chainId: number,\n address: string\n): string | null {\n const base = getChainBlockExplorer({ chainId })?.url;\n if (!base) return null;\n return `${base}/address/${address}`;\n}\n\n/**\n * Convert a post ID (`{sender}:{timestamp}`) into the comment-permalink form\n * used by the web's `?commentId=` query parameter (`{sender}-{timestamp}`).\n *\n * The two formats are intentionally documented separately because the website\n * scrolls to the DOM id `comment-{sender}-{timestamp}` and matches against the\n * raw query value (see CommentThread.tsx in the Net repo).\n */\nexport function postIdToCommentParam(postId: string): string {\n const colon = postId.indexOf(\":\");\n if (colon === -1) return postId;\n return `${postId.slice(0, colon)}-${postId.slice(colon + 1)}`;\n}\n\nexport interface PostPermalinkOptions {\n /**\n * Global message index from the contract's MessageSent event. Most reliable\n * when available — works regardless of how the post was queried.\n */\n globalIndex?: number;\n /** Topic-filtered absolute index (from a topic-scoped read). */\n topic?: string;\n topicIndex?: number;\n /** Maker-filtered absolute index (from a sender-scoped read). */\n user?: string;\n userIndex?: number;\n /**\n * Optional comment to deep-link. Pass either a post-ID-style string\n * (`{sender}:{timestamp}` — colon will be normalized to hyphen) or the\n * already-converted form.\n */\n commentId?: string;\n}\n\n/**\n * Build a permalink to the dedicated post page. Picks the most reliable URL\n * form available, in priority order: global -> topic -> user. Returns null\n * when no usable index is provided or chain is unknown.\n */\nexport function postPermalink(\n chainId: number,\n opts: PostPermalinkOptions\n): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n\n const params = new URLSearchParams();\n\n if (opts.globalIndex != null) {\n params.set(\"index\", String(opts.globalIndex));\n } else if (opts.topic != null && opts.topicIndex != null) {\n params.set(\"topic\", stripFeedPrefix(opts.topic));\n params.set(\"index\", String(opts.topicIndex));\n } else if (opts.user != null && opts.userIndex != null) {\n params.set(\"user\", opts.user.toLowerCase());\n params.set(\"index\", String(opts.userIndex));\n } else {\n return null;\n }\n\n if (opts.commentId) {\n params.set(\"commentId\", postIdToCommentParam(opts.commentId));\n }\n\n return `${WEBSITE_BASE}/app/feed/${slug}/post?${params.toString()}`;\n}\n\n/**\n * URL of the canonical hosted skill (proxies to net-public/SKILL.md).\n */\nexport const HOSTED_SKILL_URL = `${WEBSITE_BASE}/skill.md`;\n","import chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { parseReadOnlyOptionsWithDefault } from \"../../cli/shared\";\nimport { exitWithError } from \"../../shared/output\";\nimport { tokenUrl as buildTokenUrl } from \"../../shared/urls\";\nimport {\n ScoreClient,\n getTokenScoreKey,\n ALL_STRATEGY_ADDRESSES,\n PURE_ALPHA_STRATEGY,\n UNIV234_POOLS_STRATEGY,\n DYNAMIC_SPLIT_STRATEGY,\n} from \"@net-protocol/score\";\nimport type { GetUpvotesOptions } from \"./types\";\n\nfunction getStrategyName(address: string): string {\n const lower = address.toLowerCase();\n if (lower === PURE_ALPHA_STRATEGY.address.toLowerCase()) return \"Pure Alpha\";\n if (lower === UNIV234_POOLS_STRATEGY.address.toLowerCase())\n return \"50/50 Pools\";\n if (lower === DYNAMIC_SPLIT_STRATEGY.address.toLowerCase())\n return \"Dynamic Split\";\n return address;\n}\n\nasync function executeGetUpvotes(options: GetUpvotesOptions): Promise<void> {\n const tokenAddress = options.tokenAddress;\n if (!tokenAddress.startsWith(\"0x\") || tokenAddress.length !== 42) {\n exitWithError(\n \"Invalid token address format (must be 0x-prefixed, 42 characters)\"\n );\n return;\n }\n\n const readOnlyOptions = parseReadOnlyOptionsWithDefault({\n chainId: options.chainId,\n rpcUrl: options.rpcUrl,\n });\n\n const client = new ScoreClient({\n chainId: readOnlyOptions.chainId,\n overrides: readOnlyOptions.rpcUrl\n ? { rpcUrls: [readOnlyOptions.rpcUrl] }\n : undefined,\n });\n\n const scoreKey = getTokenScoreKey(tokenAddress);\n\n try {\n // getUpvotesWithLegacy returns one aggregated count per scoreKey\n // (summing legacy + all specified strategies). To get per-strategy\n // counts, use getStrategyKeyScores for each strategy individually.\n const [totalCounts, ...perStrategyCounts] = await Promise.all([\n client.getUpvotesWithLegacy({\n scoreKeys: [scoreKey],\n strategies: ALL_STRATEGY_ADDRESSES,\n }),\n ...ALL_STRATEGY_ADDRESSES.map((strategy) =>\n client.getStrategyKeyScores({\n strategy,\n scoreKeys: [scoreKey],\n })\n ),\n ]);\n\n const total = totalCounts[0] ?? 0;\n const strategyCounts = ALL_STRATEGY_ADDRESSES.map((addr, i) => ({\n strategy: getStrategyName(addr),\n address: addr,\n count: perStrategyCounts[i]?.[0] ?? 0,\n }));\n\n if (options.json) {\n console.log(\n JSON.stringify(\n {\n tokenAddress,\n tokenUrl: buildTokenUrl(readOnlyOptions.chainId, tokenAddress),\n scoreKey,\n total,\n strategies: strategyCounts.map((s) => ({\n name: s.strategy,\n address: s.address,\n count: s.count,\n })),\n },\n null,\n 2\n )\n );\n } else {\n console.log(chalk.white(`Upvotes for ${tokenAddress}:`));\n console.log(chalk.cyan(` Total: ${total}`));\n console.log();\n for (const s of strategyCounts) {\n if (s.count > 0) {\n console.log(chalk.white(` ${s.strategy}: ${s.count}`));\n }\n }\n if (total === 0) {\n console.log(chalk.yellow(\" No upvotes found\"));\n }\n }\n } catch (error) {\n exitWithError(\n `Failed to fetch upvotes: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\nexport function registerGetUpvotesCommand(\n parent: Command,\n commandName = \"info\"\n): void {\n parent\n .command(commandName)\n .description(\"Get upvote counts for a token\")\n .requiredOption(\"--token-address <address>\", \"Token contract address\")\n .option(\"--chain-id <id>\", \"Chain ID (default: 8453 for Base)\", (value) =>\n parseInt(value, 10)\n )\n .option(\"--rpc-url <url>\", \"Custom RPC URL\")\n .option(\"--json\", \"Output in JSON format\")\n .action(async (options) => {\n await executeGetUpvotes(options);\n });\n}\n","import chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { formatEther } from \"viem\";\nimport {\n parseReadOnlyOptionsWithDefault,\n parseCommonOptionsWithDefault,\n} from \"../../cli/shared\";\nimport { createWallet } from \"../../shared/wallet\";\nimport { encodeTransaction } from \"../../shared/encode\";\nimport { exitWithError } from \"../../shared/output\";\nimport {\n UserUpvoteClient,\n USER_UPVOTE_CONTRACT,\n NULL_ADDRESS,\n calculateUpvoteCost,\n} from \"@net-protocol/score\";\nimport type { UpvoteUserOptions } from \"./types\";\n\nexport async function executeUpvoteUser(\n options: UpvoteUserOptions\n): Promise<void> {\n const count = parseInt(options.count, 10);\n if (isNaN(count) || count <= 0) {\n exitWithError(\"Count must be a positive integer\");\n return;\n }\n\n const userAddress = options.address;\n if (!userAddress.startsWith(\"0x\") || userAddress.length !== 42) {\n exitWithError(\n \"Invalid address format (must be 0x-prefixed, 42 characters)\"\n );\n return;\n }\n\n const token = (options.token ?? NULL_ADDRESS) as `0x${string}`;\n const feeTier = options.feeTier ? parseInt(options.feeTier, 10) : 0;\n\n const readOnlyOptions = parseReadOnlyOptionsWithDefault({\n chainId: options.chainId,\n rpcUrl: options.rpcUrl,\n });\n\n const client = new UserUpvoteClient({\n chainId: readOnlyOptions.chainId,\n overrides: readOnlyOptions.rpcUrl\n ? { rpcUrls: [readOnlyOptions.rpcUrl] }\n : undefined,\n });\n\n // Fetch current upvote price from contract\n let upvotePrice: bigint;\n try {\n upvotePrice = await client.getUpvotePrice();\n } catch (error) {\n exitWithError(\n `Failed to fetch upvote price: ${error instanceof Error ? error.message : String(error)}`\n );\n return;\n }\n\n const totalCost = calculateUpvoteCost(count, upvotePrice);\n\n if (options.encodeOnly) {\n const txConfig = {\n to: USER_UPVOTE_CONTRACT.address,\n abi: USER_UPVOTE_CONTRACT.abi,\n functionName: \"upvoteUser\" as const,\n args: [userAddress as `0x${string}`, token, BigInt(count), BigInt(feeTier)],\n value: totalCost,\n };\n const encoded = encodeTransaction(txConfig, readOnlyOptions.chainId);\n console.log(JSON.stringify(encoded, null, 2));\n return;\n }\n\n const commonOptions = parseCommonOptionsWithDefault(\n {\n privateKey: options.privateKey,\n chainId: options.chainId,\n rpcUrl: options.rpcUrl,\n },\n true\n );\n\n const walletClient = createWallet(\n commonOptions.privateKey,\n commonOptions.chainId,\n commonOptions.rpcUrl\n );\n\n console.log(\n chalk.blue(`Submitting ${count} profile upvote(s) for ${userAddress}...`)\n );\n\n try {\n const hash = await client.upvoteUser({\n walletClient,\n userToUpvote: userAddress as `0x${string}`,\n token,\n numUpvotes: count,\n feeTier,\n value: totalCost,\n });\n\n console.log(chalk.green(\"Profile upvote submitted successfully!\"));\n console.log(chalk.white(` Transaction: ${hash}`));\n console.log(chalk.white(` User: ${userAddress}`));\n console.log(chalk.white(` Count: ${count}`));\n console.log(chalk.white(` Value: ${formatEther(totalCost)} ETH`));\n if (token !== NULL_ADDRESS) {\n console.log(chalk.white(` Token: ${token}`));\n }\n } catch (error) {\n exitWithError(\n `Failed to submit profile upvote: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\nexport function registerUpvoteUserCommand(\n parent: Command,\n commandName = \"user\"\n): void {\n parent\n .command(commandName)\n .description(\"Upvote a user's profile on Net Protocol\")\n .requiredOption(\"--address <address>\", \"User address to upvote\")\n .requiredOption(\"--count <n>\", \"Number of upvotes\")\n .option(\"--token <address>\", \"Token address (default: null address)\")\n .option(\"--fee-tier <tier>\", \"Fee tier (default: 0)\")\n .option(\"--chain-id <id>\", \"Chain ID (default: 8453 for Base)\", (value) =>\n parseInt(value, 10)\n )\n .option(\"--rpc-url <url>\", \"Custom RPC URL\")\n .option(\"--private-key <key>\", \"Private key (0x-prefixed)\")\n .option(\n \"--encode-only\",\n \"Output transaction data as JSON instead of executing\"\n )\n .action(async (options) => {\n await executeUpvoteUser(options);\n });\n}\n","import chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { formatEther } from \"viem\";\nimport { parseReadOnlyOptionsWithDefault } from \"../../cli/shared\";\nimport { exitWithError } from \"../../shared/output\";\nimport { UserUpvoteClient } from \"@net-protocol/score\";\nimport {\n profileUrl as buildProfileUrl,\n walletUrl as buildWalletUrl,\n} from \"../../shared/urls\";\nimport type { GetUserUpvotesOptions } from \"./types\";\n\nexport async function executeGetUserUpvotes(\n options: GetUserUpvotesOptions\n): Promise<void> {\n const userAddress = options.address;\n if (!userAddress.startsWith(\"0x\") || userAddress.length !== 42) {\n exitWithError(\n \"Invalid address format (must be 0x-prefixed, 42 characters)\"\n );\n return;\n }\n\n const readOnlyOptions = parseReadOnlyOptionsWithDefault({\n chainId: options.chainId,\n rpcUrl: options.rpcUrl,\n });\n\n const client = new UserUpvoteClient({\n chainId: readOnlyOptions.chainId,\n overrides: readOnlyOptions.rpcUrl\n ? { rpcUrls: [readOnlyOptions.rpcUrl] }\n : undefined,\n });\n\n try {\n const [given, received, upvotePrice] = await Promise.all([\n client.getUserUpvotesGiven({\n user: userAddress as `0x${string}`,\n }),\n client.getUserUpvotesReceived({\n user: userAddress as `0x${string}`,\n }),\n client.getUpvotePrice(),\n ]);\n\n if (options.json) {\n console.log(\n JSON.stringify(\n {\n address: userAddress,\n chainId: readOnlyOptions.chainId,\n profileUrl: buildProfileUrl(readOnlyOptions.chainId, userAddress),\n walletUrl: buildWalletUrl(readOnlyOptions.chainId, userAddress),\n upvotesGiven: Number(given),\n upvotesReceived: Number(received),\n upvotePriceWei: upvotePrice.toString(),\n upvotePriceEth: formatEther(upvotePrice),\n },\n null,\n 2\n )\n );\n } else {\n console.log(chalk.white(`Profile upvotes for ${userAddress}:`));\n console.log(chalk.cyan(` Upvotes Given: ${given}`));\n console.log(chalk.cyan(` Upvotes Received: ${received}`));\n console.log(\n chalk.white(` Upvote Price: ${formatEther(upvotePrice)} ETH`)\n );\n }\n } catch (error) {\n exitWithError(\n `Failed to fetch user upvotes: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\nexport function registerGetUserUpvotesCommand(\n parent: Command,\n commandName = \"user-info\"\n): void {\n parent\n .command(commandName)\n .description(\"Get profile upvote stats for a user\")\n .requiredOption(\"--address <address>\", \"User address to look up\")\n .option(\"--chain-id <id>\", \"Chain ID (default: 8453 for Base)\", (value) =>\n parseInt(value, 10)\n )\n .option(\"--rpc-url <url>\", \"Custom RPC URL\")\n .option(\"--json\", \"Output in JSON format\")\n .action(async (options) => {\n await executeGetUserUpvotes(options);\n });\n}\n","import { Command } from \"commander\";\nimport { registerUpvoteTokenCommand } from \"./upvote-token\";\nimport { registerGetUpvotesCommand } from \"./get-upvotes\";\nimport { registerUpvoteUserCommand } from \"./upvote-user\";\nimport { registerGetUserUpvotesCommand } from \"./get-user-upvotes\";\n\nexport function registerUpvoteCommand(program: Command): void {\n const upvoteCommand = program\n .command(\"upvote\")\n .description(\"Upvote tokens and users on Net Protocol\");\n\n registerUpvoteTokenCommand(upvoteCommand);\n registerGetUpvotesCommand(upvoteCommand);\n registerUpvoteUserCommand(upvoteCommand);\n registerGetUserUpvotesCommand(upvoteCommand);\n}\n\n// Re-exports for botchan\nexport { registerUpvoteTokenCommand } from \"./upvote-token\";\nexport { registerGetUpvotesCommand } from \"./get-upvotes\";\nexport { registerUpvoteUserCommand } from \"./upvote-user\";\nexport { registerGetUserUpvotesCommand } from \"./get-user-upvotes\";\n"]}
1
+ {"version":3,"sources":["../../src/cli/shared.ts","../../src/shared/wallet.ts","../../src/shared/encode.ts","../../src/shared/output.ts","../../src/commands/upvote/upvote-token.ts","../../src/shared/urls.ts","../../src/commands/upvote/get-upvotes.ts","../../src/commands/upvote/upvote-user.ts","../../src/commands/upvote/get-user-upvotes.ts","../../src/commands/upvote/rankings.ts","../../src/commands/upvote/index.ts"],"names":["chalk","getBaseDataSuffix","getChainRpcUrls","http","PURE_ALPHA_STRATEGY","UNIV234_POOLS_STRATEGY","DYNAMIC_SPLIT_STRATEGY","getTokenScoreKey","UserUpvoteClient","formatEther"],"mappings":";;;;;;;;AAMO,IAAM,gBAAA,GAAmB,IAAA;AA4BhC,SAAS,sBAAsB,WAAA,EAA8B;AAC3D,EAAA,IAAI,WAAA,EAAa;AACf,IAAA,OAAO,WAAA;AAAA,EACT;AAEA,EAAA,MAAM,UAAA,GACJ,OAAA,CAAQ,GAAA,CAAI,gBAAA,IAAoB,QAAQ,GAAA,CAAI,YAAA;AAE9C,EAAA,IAAI,UAAA,EAAY;AACd,IAAA,OAAO,QAAA,CAAS,YAAY,EAAE,CAAA;AAAA,EAChC;AAEA,EAAA,OAAO,gBAAA;AACT;AAaA,SAAS,6BAA6B,WAAA,EAA0C;AAC9E,EAAA,OAAO,WAAA,IAAe,OAAA,CAAQ,GAAA,CAAI,eAAA,IAAmB,QAAQ,GAAA,CAAI,WAAA;AACnE;AA4EO,SAAS,gCAAgC,OAAA,EAG5B;AAClB,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,qBAAA,CAAsB,OAAA,CAAQ,OAAO,CAAA;AAAA,IAC9C,MAAA,EAAQ,4BAAA,CAA6B,OAAA,CAAQ,MAAM;AAAA,GACrD;AACF;AAOO,SAAS,6BAAA,CACd,OAAA,EAKA,kBAAA,GAAqB,KAAA,EACN;AACf,EAAA,MAAM,UAAA,GACJ,OAAA,CAAQ,UAAA,IACR,OAAA,CAAQ,GAAA,CAAI,uBACZ,OAAA,CAAQ,GAAA,CAAI,eAAA,IACZ,OAAA,CAAQ,GAAA,CAAI,WAAA;AAEd,EAAA,IAAI,CAAC,UAAA,EAAY;AACf,IAAA,MAAM,cAAA,GAAiB,qBACnB,sEAAA,GACA,EAAA;AACJ,IAAA,OAAA,CAAQ,KAAA;AAAA,MACNA,MAAA,CAAM,GAAA;AAAA,QACJ,6HAA6H,cAAc,CAAA;AAAA;AAC7I,KACF;AACA,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAChB;AAEA,EAAA,IAAI,CAAC,UAAA,CAAW,UAAA,CAAW,IAAI,CAAA,IAAK,UAAA,CAAW,WAAW,EAAA,EAAI;AAC5D,IAAA,OAAA,CAAQ,KAAA;AAAA,MACNA,MAAA,CAAM,GAAA;AAAA,QACJ;AAAA;AACF,KACF;AACA,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAChB;AAEA,EAAA,IAAI,QAAQ,UAAA,EAAY;AACtB,IAAA,OAAA,CAAQ,IAAA;AAAA,MACNA,MAAA,CAAM,MAAA;AAAA,QACJ;AAAA;AACF,KACF;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,UAAA;AAAA,IACA,OAAA,EAAS,qBAAA,CAAsB,OAAA,CAAQ,OAAO,CAAA;AAAA,IAC9C,MAAA,EAAQ,4BAAA,CAA6B,OAAA,CAAQ,MAAM;AAAA,GACrD;AACF;ACjMO,SAAS,YAAA,CACd,UAAA,EACA,OAAA,EACA,MAAA,EACA;AACA,EAAA,MAAM,OAAA,GAAU,oBAAoB,UAAU,CAAA;AAC9C,EAAA,MAAM,UAAU,eAAA,CAAgB;AAAA,IAC9B,OAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,OAAO,kBAAA,CAAmB;AAAA,IACxB,OAAA;AAAA,IACA,SAAA,EAAW,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAC,CAAA;AAAA,IAC1B,UAAA,EAAY,kBAAkB,OAAO;AAAA,GACtC,CAAA;AACH;AAKA,eAAsB,kBAAA,CACpB,cACA,QAAA,EACwB;AACxB,EAAA,MAAM,IAAA,GAAO,MAAM,YAAA,CAAa,aAAA,CAAc;AAAA,IAC5C,SAAS,QAAA,CAAS,EAAA;AAAA,IAClB,KAAK,QAAA,CAAS,GAAA;AAAA,IACd,cAAc,QAAA,CAAS,YAAA;AAAA,IACvB,MAAM,QAAA,CAAS,IAAA;AAAA,IACf,OAAO,QAAA,CAAS,KAAA;AAAA,IAChB,KAAA,EAAO;AAAA,GAC4C,CAAA;AAErD,EAAA,OAAO,IAAA;AACT;AChCO,SAAS,iBAAA,CACd,QACA,OAAA,EACoB;AACpB,EAAA,MAAM,WAAW,kBAAA,CAAmB;AAAA,IAClC,KAAK,MAAA,CAAO,GAAA;AAAA,IACZ,cAAc,MAAA,CAAO,YAAA;AAAA,IACrB,MAAM,MAAA,CAAO;AAAA,GACd,CAAA;AAED,EAAA,MAAM,MAAA,GAASC,kBAAkB,OAAO,CAAA;AACxC,EAAA,MAAM,OAAO,MAAA,GAAS,MAAA,CAAO,CAAC,QAAA,EAAU,MAAM,CAAC,CAAA,GAAI,QAAA;AAEnD,EAAA,OAAO;AAAA,IACL,IAAI,MAAA,CAAO,EAAA;AAAA,IACX,IAAA;AAAA,IACA,OAAA;AAAA,IACA,KAAA,EAAO,MAAA,CAAO,KAAA,EAAO,QAAA,EAAS,IAAK;AAAA,GACrC;AACF;AC8DO,SAAS,cAAc,OAAA,EAAwB;AACpD,EAAA,OAAA,CAAQ,MAAMD,MAAAA,CAAM,GAAA,CAAI,CAAA,OAAA,EAAU,OAAO,EAAE,CAAC,CAAA;AAC5C,EAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAChB;ACxEA,eAAe,mBAAmB,OAAA,EAA4C;AAC5E,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AACxC,EAAA,IAAI,KAAA,CAAM,KAAK,CAAA,IAAK,KAAA,IAAS,CAAA,EAAG;AAC9B,IAAA,aAAA,CAAc,kCAAkC,CAAA;AAChD,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,eAAe,OAAA,CAAQ,YAAA;AAC7B,EAAA,IAAI,CAAC,YAAA,CAAa,UAAA,CAAW,IAAI,CAAA,IAAK,YAAA,CAAa,WAAW,EAAA,EAAI;AAChE,IAAA,aAAA;AAAA,MACE;AAAA,KACF;AACA,IAAA;AAAA,EACF;AAGA,EAAA,MAAM,kBAAkB,+BAAA,CAAgC;AAAA,IACtD,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,QAAQ,OAAA,CAAQ;AAAA,GACjB,CAAA;AAGD,EAAA,MAAM,UAAUE,eAAAA,CAAgB;AAAA,IAC9B,SAAS,eAAA,CAAgB,OAAA;AAAA,IACzB,QAAQ,eAAA,CAAgB;AAAA,GACzB,CAAA;AACD,EAAA,MAAM,eAAe,kBAAA,CAAmB;AAAA,IACtC,SAAA,EAAWC,IAAAA,CAAK,OAAA,CAAQ,CAAC,CAAC;AAAA,GAC3B,CAAA;AAED,EAAA,OAAA,CAAQ,GAAA,CAAIH,MAAAA,CAAM,IAAA,CAAK,uCAAuC,CAAC,CAAA;AAE/D,EAAA,IAAI,UAAA;AACJ,EAAA,IAAI;AACF,IAAA,UAAA,GAAa,MAAM,iBAAA,CAAkB;AAAA,MACnC,YAAA;AAAA,MACA,YAAA;AAAA,MACA,SAAS,eAAA,CAAgB;AAAA,KAC1B,CAAA;AAAA,EACH,SAAS,KAAA,EAAO;AACd,IAAA,aAAA;AAAA,MACE,kCAAkC,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,KAC1F;AACA,IAAA;AAAA,EACF;AAGA,EAAA,IAAI,eAAA;AACJ,EAAA,IAAI,aAAA;AAEJ,EAAA,IAAI,CAAC,UAAA,IAAc,CAAC,UAAA,CAAW,OAAA,EAAS;AAEtC,IAAA,eAAA,GAAkB,mBAAA,CAAoB,OAAA;AACtC,IAAA,aAAA,GAAgB,IAAA;AAChB,IAAA,OAAA,CAAQ,GAAA,CAAIA,MAAAA,CAAM,MAAA,CAAO,gDAA2C,CAAC,CAAA;AAAA,EACvE,CAAA,MAAA,IAAW,OAAA,CAAQ,SAAA,KAAc,OAAA,EAAS;AAExC,IAAA,eAAA,GAAkB,sBAAA,CAAuB,OAAA;AACzC,IAAA,aAAA,GAAgB,aAAA,CAAc,WAAW,OAAO,CAAA;AAChD,IAAA,OAAA,CAAQ,GAAA;AAAA,MACNA,MAAAA,CAAM,KAAA;AAAA,QACJ,CAAA,iBAAA,EAAoB,WAAW,GAAG,CAAA,mCAAA;AAAA;AACpC,KACF;AAAA,EACF,CAAA,MAAO;AAEL,IAAA,eAAA,GAAkB,sBAAA,CAAuB,OAAA;AACzC,IAAA,aAAA,GAAgB,aAAA,CAAc,WAAW,OAAO,CAAA;AAChD,IAAA,OAAA,CAAQ,GAAA;AAAA,MACNA,MAAAA,CAAM,KAAA;AAAA,QACJ,CAAA,iBAAA,EAAoB,WAAW,GAAG,CAAA,qCAAA;AAAA;AACpC,KACF;AAAA,EACF;AAGA,EAAA,MAAM,QAAA,GAAW,iBAAiB,YAAY,CAAA;AAC9C,EAAA,MAAM,KAAA,GAAQ,UAAA,CAAA,CAAY,KAAA,GAAQ,gBAAA,EAAkB,UAAU,CAAA;AAE9D,EAAA,MAAM,QAAA,GAAW;AAAA,IACf,IAAI,UAAA,CAAW,OAAA;AAAA,IACf,KAAK,UAAA,CAAW,GAAA;AAAA,IAChB,YAAA,EAAc,QAAA;AAAA,IACd,MAAM,CAAC,eAAA,EAAiB,QAAA,EAAU,KAAA,EAAO,eAAe,IAAI,CAAA;AAAA,IAC5D;AAAA,GACF;AAEA,EAAA,IAAI,QAAQ,UAAA,EAAY;AACtB,IAAA,MAAM,OAAA,GAAU,iBAAA,CAAkB,QAAA,EAAU,eAAA,CAAgB,OAAO,CAAA;AACnE,IAAA,OAAA,CAAQ,IAAI,IAAA,CAAK,SAAA,CAAU,OAAA,EAAS,IAAA,EAAM,CAAC,CAAC,CAAA;AAC5C,IAAA;AAAA,EACF;AAGA,EAAA,MAAM,aAAA,GAAgB,6BAAA;AAAA,IACpB;AAAA,MACE,YAAY,OAAA,CAAQ,UAAA;AAAA,MACpB,SAAS,OAAA,CAAQ,OAAA;AAAA,MACjB,QAAQ,OAAA,CAAQ;AAAA,KAClB;AAAA,IACA;AAAA,GACF;AAEA,EAAA,MAAM,YAAA,GAAe,YAAA;AAAA,IACnB,aAAA,CAAc,UAAA;AAAA,IACd,aAAA,CAAc,OAAA;AAAA,IACd,aAAA,CAAc;AAAA,GAChB;AAEA,EAAA,OAAA,CAAQ,GAAA;AAAA,IACNA,OAAM,IAAA,CAAK,CAAA,WAAA,EAAc,KAAK,CAAA,eAAA,EAAkB,YAAY,CAAA,GAAA,CAAK;AAAA,GACnE;AAEA,EAAA,IAAI;AACF,IAAA,MAAM,IAAA,GAAO,MAAM,kBAAA,CAAmB,YAAA,EAAc,QAAQ,CAAA;AAE5D,IAAA,OAAA,CAAQ,GAAA,CAAIA,MAAAA,CAAM,KAAA,CAAM,CAAA,8BAAA,CAAgC,CAAC,CAAA;AACzD,IAAA,OAAA,CAAQ,IAAIA,MAAAA,CAAM,KAAA,CAAM,CAAA,eAAA,EAAkB,IAAI,EAAE,CAAC,CAAA;AACjD,IAAA,OAAA,CAAQ,IAAIA,MAAAA,CAAM,KAAA,CAAM,CAAA,SAAA,EAAY,YAAY,EAAE,CAAC,CAAA;AACnD,IAAA,OAAA,CAAQ,IAAIA,MAAAA,CAAM,KAAA,CAAM,CAAA,SAAA,EAAY,KAAK,EAAE,CAAC,CAAA;AAC5C,IAAA,OAAA,CAAQ,GAAA;AAAA,MACNA,MAAAA,CAAM,MAAM,CAAA,SAAA,EAAA,CAAa,KAAA,GAAQ,kBAAkB,OAAA,CAAQ,CAAC,CAAC,CAAA,IAAA,CAAM;AAAA,KACrE;AAAA,EACF,SAAS,KAAA,EAAO;AACd,IAAA,aAAA;AAAA,MACE,4BAA4B,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,KACpF;AAAA,EACF;AACF;AAEO,SAAS,0BAAA,CACd,MAAA,EACA,WAAA,GAAc,OAAA,EACR;AACN,EAAA,MAAA,CACG,OAAA,CAAQ,WAAW,CAAA,CACnB,WAAA,CAAY,gCAAgC,CAAA,CAC5C,cAAA;AAAA,IACC,2BAAA;AAAA,IACA;AAAA,GACF,CACC,cAAA,CAAe,aAAA,EAAe,mBAAmB,CAAA,CACjD,MAAA;AAAA,IACC,qBAAA;AAAA,IACA;AAAA,GACF,CACC,MAAA;AAAA,IAAO,iBAAA;AAAA,IAAmB,mCAAA;AAAA,IAAqC,CAAC,KAAA,KAC/D,QAAA,CAAS,KAAA,EAAO,EAAE;AAAA,GACpB,CACC,OAAO,iBAAA,EAAmB,gBAAgB,EAC1C,MAAA,CAAO,qBAAA,EAAuB,2BAA2B,CAAA,CACzD,MAAA;AAAA,IACC,eAAA;AAAA,IACA;AAAA,GACF,CACC,MAAA,CAAO,OAAO,OAAA,KAAY;AACzB,IAAA,MAAM,mBAAmB,OAAO,CAAA;AAAA,EAClC,CAAC,CAAA;AACL;AClKA,IAAM,YAAA,GAAe,yBAAA;AAGd,SAAS,UAAU,OAAA,EAAgC;AACxD,EAAA,OAAO,YAAA,CAAa,EAAE,OAAA,EAAS,CAAA,IAAK,IAAA;AACtC;AAwBO,SAAS,SAAA,CAAU,SAAiB,OAAA,EAAgC;AACzE,EAAA,MAAM,IAAA,GAAO,UAAU,OAAO,CAAA;AAC9B,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,EAAA,OAAO,GAAG,YAAY,CAAA,UAAA,EAAa,IAAI,CAAA,CAAA,EAAI,OAAA,CAAQ,aAAa,CAAA,CAAA;AAClE;AAKO,SAAS,UAAA,CAAW,SAAiB,OAAA,EAAgC;AAC1E,EAAA,MAAM,IAAA,GAAO,UAAU,OAAO,CAAA;AAC9B,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,EAAA,OAAO,GAAG,YAAY,CAAA,aAAA,EAAgB,IAAI,CAAA,CAAA,EAAI,OAAA,CAAQ,aAAa,CAAA,CAAA;AACrE;AAgBO,SAAS,QAAA,CACd,SACA,YAAA,EACe;AACf,EAAA,MAAM,IAAA,GAAO,UAAU,OAAO,CAAA;AAC9B,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,EAAA,OAAO,GAAG,YAAY,CAAA,WAAA,EAAc,IAAI,CAAA,CAAA,EAAI,YAAA,CAAa,aAAa,CAAA,CAAA;AACxE;ACrEA,SAAS,gBAAgB,OAAA,EAAyB;AAChD,EAAA,MAAM,KAAA,GAAQ,QAAQ,WAAA,EAAY;AAClC,EAAA,IAAI,KAAA,KAAUI,mBAAAA,CAAoB,OAAA,CAAQ,WAAA,IAAe,OAAO,YAAA;AAChE,EAAA,IAAI,KAAA,KAAUC,sBAAAA,CAAuB,OAAA,CAAQ,WAAA,EAAY;AACvD,IAAA,OAAO,aAAA;AACT,EAAA,IAAI,KAAA,KAAUC,sBAAAA,CAAuB,OAAA,CAAQ,WAAA,EAAY;AACvD,IAAA,OAAO,eAAA;AACT,EAAA,OAAO,OAAA;AACT;AAEA,eAAe,kBAAkB,OAAA,EAA2C;AAC1E,EAAA,MAAM,eAAe,OAAA,CAAQ,YAAA;AAC7B,EAAA,IAAI,CAAC,YAAA,CAAa,UAAA,CAAW,IAAI,CAAA,IAAK,YAAA,CAAa,WAAW,EAAA,EAAI;AAChE,IAAA,aAAA;AAAA,MACE;AAAA,KACF;AACA,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,kBAAkB,+BAAA,CAAgC;AAAA,IACtD,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,QAAQ,OAAA,CAAQ;AAAA,GACjB,CAAA;AAED,EAAA,MAAM,MAAA,GAAS,IAAI,WAAA,CAAY;AAAA,IAC7B,SAAS,eAAA,CAAgB,OAAA;AAAA,IACzB,SAAA,EAAW,gBAAgB,MAAA,GACvB,EAAE,SAAS,CAAC,eAAA,CAAgB,MAAM,CAAA,EAAE,GACpC;AAAA,GACL,CAAA;AAED,EAAA,MAAM,QAAA,GAAWC,iBAAiB,YAAY,CAAA;AAE9C,EAAA,IAAI;AAIF,IAAA,MAAM,CAAC,WAAA,EAAa,GAAG,iBAAiB,CAAA,GAAI,MAAM,QAAQ,GAAA,CAAI;AAAA,MAC5D,OAAO,oBAAA,CAAqB;AAAA,QAC1B,SAAA,EAAW,CAAC,QAAQ,CAAA;AAAA,QACpB,UAAA,EAAY;AAAA,OACb,CAAA;AAAA,MACD,GAAG,sBAAA,CAAuB,GAAA;AAAA,QAAI,CAAC,QAAA,KAC7B,MAAA,CAAO,oBAAA,CAAqB;AAAA,UAC1B,QAAA;AAAA,UACA,SAAA,EAAW,CAAC,QAAQ;AAAA,SACrB;AAAA;AACH,KACD,CAAA;AAED,IAAA,MAAM,KAAA,GAAQ,WAAA,CAAY,CAAC,CAAA,IAAK,CAAA;AAChC,IAAA,MAAM,cAAA,GAAiB,sBAAA,CAAuB,GAAA,CAAI,CAAC,MAAM,CAAA,MAAO;AAAA,MAC9D,QAAA,EAAU,gBAAgB,IAAI,CAAA;AAAA,MAC9B,OAAA,EAAS,IAAA;AAAA,MACT,KAAA,EAAO,iBAAA,CAAkB,CAAC,CAAA,GAAI,CAAC,CAAA,IAAK;AAAA,KACtC,CAAE,CAAA;AAEF,IAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,OAAA,CAAQ,GAAA;AAAA,QACN,IAAA,CAAK,SAAA;AAAA,UACH;AAAA,YACE,YAAA;AAAA,YACA,QAAA,EAAU,QAAA,CAAc,eAAA,CAAgB,OAAA,EAAS,YAAY,CAAA;AAAA,YAC7D,QAAA;AAAA,YACA,KAAA;AAAA,YACA,UAAA,EAAY,cAAA,CAAe,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,cACrC,MAAM,CAAA,CAAE,QAAA;AAAA,cACR,SAAS,CAAA,CAAE,OAAA;AAAA,cACX,OAAO,CAAA,CAAE;AAAA,aACX,CAAE;AAAA,WACJ;AAAA,UACA,IAAA;AAAA,UACA;AAAA;AACF,OACF;AAAA,IACF,CAAA,MAAO;AACL,MAAA,OAAA,CAAQ,IAAIP,MAAAA,CAAM,KAAA,CAAM,CAAA,YAAA,EAAe,YAAY,GAAG,CAAC,CAAA;AACvD,MAAA,OAAA,CAAQ,IAAIA,MAAAA,CAAM,IAAA,CAAK,CAAA,SAAA,EAAY,KAAK,EAAE,CAAC,CAAA;AAC3C,MAAA,OAAA,CAAQ,GAAA,EAAI;AACZ,MAAA,KAAA,MAAW,KAAK,cAAA,EAAgB;AAC9B,QAAA,IAAI,CAAA,CAAE,QAAQ,CAAA,EAAG;AACf,UAAA,OAAA,CAAQ,GAAA,CAAIA,MAAAA,CAAM,KAAA,CAAM,CAAA,EAAA,EAAK,CAAA,CAAE,QAAQ,CAAA,EAAA,EAAK,CAAA,CAAE,KAAK,CAAA,CAAE,CAAC,CAAA;AAAA,QACxD;AAAA,MACF;AACA,MAAA,IAAI,UAAU,CAAA,EAAG;AACf,QAAA,OAAA,CAAQ,GAAA,CAAIA,MAAAA,CAAM,MAAA,CAAO,oBAAoB,CAAC,CAAA;AAAA,MAChD;AAAA,IACF;AAAA,EACF,SAAS,KAAA,EAAO;AACd,IAAA,aAAA;AAAA,MACE,4BAA4B,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,KACpF;AAAA,EACF;AACF;AAEO,SAAS,yBAAA,CACd,MAAA,EACA,WAAA,GAAc,MAAA,EACR;AACN,EAAA,MAAA,CACG,OAAA,CAAQ,WAAW,CAAA,CACnB,WAAA,CAAY,+BAA+B,CAAA,CAC3C,cAAA,CAAe,2BAAA,EAA6B,wBAAwB,CAAA,CACpE,MAAA;AAAA,IAAO,iBAAA;AAAA,IAAmB,mCAAA;AAAA,IAAqC,CAAC,KAAA,KAC/D,QAAA,CAAS,KAAA,EAAO,EAAE;AAAA,GACpB,CACC,MAAA,CAAO,iBAAA,EAAmB,gBAAgB,CAAA,CAC1C,MAAA,CAAO,QAAA,EAAU,uBAAuB,CAAA,CACxC,MAAA,CAAO,OAAO,OAAA,KAAY;AACzB,IAAA,MAAM,kBAAkB,OAAO,CAAA;AAAA,EACjC,CAAC,CAAA;AACL;AC5GA,eAAsB,kBACpB,OAAA,EACe;AACf,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AACxC,EAAA,IAAI,KAAA,CAAM,KAAK,CAAA,IAAK,KAAA,IAAS,CAAA,EAAG;AAC9B,IAAA,aAAA,CAAc,kCAAkC,CAAA;AAChD,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,cAAc,OAAA,CAAQ,OAAA;AAC5B,EAAA,IAAI,CAAC,WAAA,CAAY,UAAA,CAAW,IAAI,CAAA,IAAK,WAAA,CAAY,WAAW,EAAA,EAAI;AAC9D,IAAA,aAAA;AAAA,MACE;AAAA,KACF;AACA,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,KAAA,GAAS,QAAQ,KAAA,IAAS,YAAA;AAChC,EAAA,MAAM,UAAU,OAAA,CAAQ,OAAA,GAAU,SAAS,OAAA,CAAQ,OAAA,EAAS,EAAE,CAAA,GAAI,CAAA;AAElE,EAAA,MAAM,kBAAkB,+BAAA,CAAgC;AAAA,IACtD,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,QAAQ,OAAA,CAAQ;AAAA,GACjB,CAAA;AAED,EAAA,MAAM,MAAA,GAAS,IAAI,gBAAA,CAAiB;AAAA,IAClC,SAAS,eAAA,CAAgB,OAAA;AAAA,IACzB,SAAA,EAAW,gBAAgB,MAAA,GACvB,EAAE,SAAS,CAAC,eAAA,CAAgB,MAAM,CAAA,EAAE,GACpC;AAAA,GACL,CAAA;AAGD,EAAA,IAAI,WAAA;AACJ,EAAA,IAAI;AACF,IAAA,WAAA,GAAc,MAAM,OAAO,cAAA,EAAe;AAAA,EAC5C,SAAS,KAAA,EAAO;AACd,IAAA,aAAA;AAAA,MACE,iCAAiC,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,KACzF;AACA,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,SAAA,GAAY,mBAAA,CAAoB,KAAA,EAAO,WAAW,CAAA;AAExD,EAAA,IAAI,QAAQ,UAAA,EAAY;AACtB,IAAA,MAAM,QAAA,GAAW;AAAA,MACf,IAAI,oBAAA,CAAqB,OAAA;AAAA,MACzB,KAAK,oBAAA,CAAqB,GAAA;AAAA,MAC1B,YAAA,EAAc,YAAA;AAAA,MACd,IAAA,EAAM,CAAC,WAAA,EAA8B,KAAA,EAAO,OAAO,KAAK,CAAA,EAAG,MAAA,CAAO,OAAO,CAAC,CAAA;AAAA,MAC1E,KAAA,EAAO;AAAA,KACT;AACA,IAAA,MAAM,OAAA,GAAU,iBAAA,CAAkB,QAAA,EAAU,eAAA,CAAgB,OAAO,CAAA;AACnE,IAAA,OAAA,CAAQ,IAAI,IAAA,CAAK,SAAA,CAAU,OAAA,EAAS,IAAA,EAAM,CAAC,CAAC,CAAA;AAC5C,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,aAAA,GAAgB,6BAAA;AAAA,IACpB;AAAA,MACE,YAAY,OAAA,CAAQ,UAAA;AAAA,MACpB,SAAS,OAAA,CAAQ,OAAA;AAAA,MACjB,QAAQ,OAAA,CAAQ;AAAA,KAClB;AAAA,IACA;AAAA,GACF;AAEA,EAAA,MAAM,YAAA,GAAe,YAAA;AAAA,IACnB,aAAA,CAAc,UAAA;AAAA,IACd,aAAA,CAAc,OAAA;AAAA,IACd,aAAA,CAAc;AAAA,GAChB;AAEA,EAAA,OAAA,CAAQ,GAAA;AAAA,IACNA,OAAM,IAAA,CAAK,CAAA,WAAA,EAAc,KAAK,CAAA,uBAAA,EAA0B,WAAW,CAAA,GAAA,CAAK;AAAA,GAC1E;AAEA,EAAA,IAAI;AACF,IAAA,MAAM,IAAA,GAAO,MAAM,MAAA,CAAO,UAAA,CAAW;AAAA,MACnC,YAAA;AAAA,MACA,YAAA,EAAc,WAAA;AAAA,MACd,KAAA;AAAA,MACA,UAAA,EAAY,KAAA;AAAA,MACZ,OAAA;AAAA,MACA,KAAA,EAAO;AAAA,KACR,CAAA;AAED,IAAA,OAAA,CAAQ,GAAA,CAAIA,MAAAA,CAAM,KAAA,CAAM,wCAAwC,CAAC,CAAA;AACjE,IAAA,OAAA,CAAQ,IAAIA,MAAAA,CAAM,KAAA,CAAM,CAAA,eAAA,EAAkB,IAAI,EAAE,CAAC,CAAA;AACjD,IAAA,OAAA,CAAQ,IAAIA,MAAAA,CAAM,KAAA,CAAM,CAAA,QAAA,EAAW,WAAW,EAAE,CAAC,CAAA;AACjD,IAAA,OAAA,CAAQ,IAAIA,MAAAA,CAAM,KAAA,CAAM,CAAA,SAAA,EAAY,KAAK,EAAE,CAAC,CAAA;AAC5C,IAAA,OAAA,CAAQ,GAAA,CAAIA,OAAM,KAAA,CAAM,CAAA,SAAA,EAAY,YAAY,SAAS,CAAC,MAAM,CAAC,CAAA;AACjE,IAAA,IAAI,UAAU,YAAA,EAAc;AAC1B,MAAA,OAAA,CAAQ,IAAIA,MAAAA,CAAM,KAAA,CAAM,CAAA,SAAA,EAAY,KAAK,EAAE,CAAC,CAAA;AAAA,IAC9C;AAAA,EACF,SAAS,KAAA,EAAO;AACd,IAAA,aAAA;AAAA,MACE,oCAAoC,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,KAC5F;AAAA,EACF;AACF;AAEO,SAAS,yBAAA,CACd,MAAA,EACA,WAAA,GAAc,MAAA,EACR;AACN,EAAA,MAAA,CACG,OAAA,CAAQ,WAAW,CAAA,CACnB,WAAA,CAAY,yCAAyC,CAAA,CACrD,cAAA,CAAe,uBAAuB,wBAAwB,CAAA,CAC9D,eAAe,aAAA,EAAe,mBAAmB,EACjD,MAAA,CAAO,mBAAA,EAAqB,uCAAuC,CAAA,CACnE,MAAA,CAAO,mBAAA,EAAqB,uBAAuB,CAAA,CACnD,MAAA;AAAA,IAAO,iBAAA;AAAA,IAAmB,mCAAA;AAAA,IAAqC,CAAC,KAAA,KAC/D,QAAA,CAAS,KAAA,EAAO,EAAE;AAAA,GACpB,CACC,OAAO,iBAAA,EAAmB,gBAAgB,EAC1C,MAAA,CAAO,qBAAA,EAAuB,2BAA2B,CAAA,CACzD,MAAA;AAAA,IACC,eAAA;AAAA,IACA;AAAA,GACF,CACC,MAAA,CAAO,OAAO,OAAA,KAAY;AACzB,IAAA,MAAM,kBAAkB,OAAO,CAAA;AAAA,EACjC,CAAC,CAAA;AACL;ACnIA,eAAsB,sBACpB,OAAA,EACe;AACf,EAAA,MAAM,cAAc,OAAA,CAAQ,OAAA;AAC5B,EAAA,IAAI,CAAC,WAAA,CAAY,UAAA,CAAW,IAAI,CAAA,IAAK,WAAA,CAAY,WAAW,EAAA,EAAI;AAC9D,IAAA,aAAA;AAAA,MACE;AAAA,KACF;AACA,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,kBAAkB,+BAAA,CAAgC;AAAA,IACtD,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,QAAQ,OAAA,CAAQ;AAAA,GACjB,CAAA;AAED,EAAA,MAAM,MAAA,GAAS,IAAIQ,gBAAAA,CAAiB;AAAA,IAClC,SAAS,eAAA,CAAgB,OAAA;AAAA,IACzB,SAAA,EAAW,gBAAgB,MAAA,GACvB,EAAE,SAAS,CAAC,eAAA,CAAgB,MAAM,CAAA,EAAE,GACpC;AAAA,GACL,CAAA;AAED,EAAA,IAAI;AACF,IAAA,MAAM,CAAC,KAAA,EAAO,QAAA,EAAU,WAAW,CAAA,GAAI,MAAM,QAAQ,GAAA,CAAI;AAAA,MACvD,OAAO,mBAAA,CAAoB;AAAA,QACzB,IAAA,EAAM;AAAA,OACP,CAAA;AAAA,MACD,OAAO,sBAAA,CAAuB;AAAA,QAC5B,IAAA,EAAM;AAAA,OACP,CAAA;AAAA,MACD,OAAO,cAAA;AAAe,KACvB,CAAA;AAED,IAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,OAAA,CAAQ,GAAA;AAAA,QACN,IAAA,CAAK,SAAA;AAAA,UACH;AAAA,YACE,OAAA,EAAS,WAAA;AAAA,YACT,SAAS,eAAA,CAAgB,OAAA;AAAA,YACzB,UAAA,EAAY,UAAA,CAAgB,eAAA,CAAgB,OAAA,EAAS,WAAW,CAAA;AAAA,YAChE,SAAA,EAAW,SAAA,CAAe,eAAA,CAAgB,OAAA,EAAS,WAAW,CAAA;AAAA,YAC9D,YAAA,EAAc,OAAO,KAAK,CAAA;AAAA,YAC1B,eAAA,EAAiB,OAAO,QAAQ,CAAA;AAAA,YAChC,cAAA,EAAgB,YAAY,QAAA,EAAS;AAAA,YACrC,cAAA,EAAgBC,YAAY,WAAW;AAAA,WACzC;AAAA,UACA,IAAA;AAAA,UACA;AAAA;AACF,OACF;AAAA,IACF,CAAA,MAAO;AACL,MAAA,OAAA,CAAQ,IAAIT,MAAAA,CAAM,KAAA,CAAM,CAAA,oBAAA,EAAuB,WAAW,GAAG,CAAC,CAAA;AAC9D,MAAA,OAAA,CAAQ,IAAIA,MAAAA,CAAM,IAAA,CAAK,CAAA,oBAAA,EAAuB,KAAK,EAAE,CAAC,CAAA;AACtD,MAAA,OAAA,CAAQ,IAAIA,MAAAA,CAAM,IAAA,CAAK,CAAA,oBAAA,EAAuB,QAAQ,EAAE,CAAC,CAAA;AACzD,MAAA,OAAA,CAAQ,GAAA;AAAA,QACNA,OAAM,KAAA,CAAM,CAAA,oBAAA,EAAuBS,WAAAA,CAAY,WAAW,CAAC,CAAA,IAAA,CAAM;AAAA,OACnE;AAAA,IACF;AAAA,EACF,SAAS,KAAA,EAAO;AACd,IAAA,aAAA;AAAA,MACE,iCAAiC,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,KACzF;AAAA,EACF;AACF;AAEO,SAAS,6BAAA,CACd,MAAA,EACA,WAAA,GAAc,WAAA,EACR;AACN,EAAA,MAAA,CACG,OAAA,CAAQ,WAAW,CAAA,CACnB,WAAA,CAAY,qCAAqC,CAAA,CACjD,cAAA,CAAe,qBAAA,EAAuB,yBAAyB,CAAA,CAC/D,MAAA;AAAA,IAAO,iBAAA;AAAA,IAAmB,mCAAA;AAAA,IAAqC,CAAC,KAAA,KAC/D,QAAA,CAAS,KAAA,EAAO,EAAE;AAAA,GACpB,CACC,MAAA,CAAO,iBAAA,EAAmB,gBAAgB,CAAA,CAC1C,MAAA,CAAO,QAAA,EAAU,uBAAuB,CAAA,CACxC,MAAA,CAAO,OAAO,OAAA,KAAY;AACzB,IAAA,MAAM,sBAAsB,OAAO,CAAA;AAAA,EACrC,CAAC,CAAA;AACL;ACtFA,IAAM,WAAA,GAA6B,CAAC,UAAA,EAAY,QAAA,EAAU,KAAK,CAAA;AAE/D,SAAS,YAAA,CAAa,CAAA,EAAuB,MAAA,GAAS,CAAA,EAAW;AAC/D,EAAA,IAAI,KAAK,IAAA,IAAQ,CAAC,OAAO,QAAA,CAAS,CAAC,GAAG,OAAO,GAAA;AAC7C,EAAA,IAAI,CAAA,IAAK,KAAe,OAAO,CAAA,EAAA,CAAI,IAAI,GAAA,EAAe,OAAA,CAAQ,MAAM,CAAC,CAAA,CAAA,CAAA;AACrE,EAAA,IAAI,CAAA,IAAK,KAAW,OAAO,CAAA,EAAA,CAAI,IAAI,GAAA,EAAW,OAAA,CAAQ,MAAM,CAAC,CAAA,CAAA,CAAA;AAC7D,EAAA,IAAI,CAAA,IAAK,KAAO,OAAO,CAAA,EAAA,CAAI,IAAI,GAAA,EAAO,OAAA,CAAQ,MAAM,CAAC,CAAA,CAAA,CAAA;AACrD,EAAA,OAAO,CAAA,CAAE,QAAQ,MAAM,CAAA;AACzB;AAEA,SAAS,YAAY,KAAA,EAAmC;AACtD,EAAA,IAAI,SAAS,IAAA,IAAQ,CAAC,OAAO,QAAA,CAAS,KAAK,GAAG,OAAO,GAAA;AACrD,EAAA,IAAI,KAAA,GAAQ,IAAA,EAAU,OAAO,KAAA,CAAM,cAAc,CAAC,CAAA;AAClD,EAAA,IAAI,QAAQ,CAAA,EAAG,OAAO,IAAI,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA;AAC1C,EAAA,OAAO,CAAA,CAAA,EAAI,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA;AAC7B;AAEA,eAAe,gBAAgB,OAAA,EAAyC;AACtE,EAAA,MAAM,IAAA,GAAA,CAAQ,OAAA,CAAQ,IAAA,IAAQ,UAAA,EAAY,WAAA,EAAY;AACtD,EAAA,IAAI,CAAC,WAAA,CAAY,QAAA,CAAS,IAAI,CAAA,EAAG;AAC/B,IAAA,aAAA;AAAA,MACE,mBAAmB,OAAA,CAAQ,IAAI,sBAAsB,WAAA,CAAY,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,KAC7E;AACA,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,KAAA,GAAQ,QAAQ,KAAA,IAAS,EAAA;AAC/B,EAAA,IAAI,CAAC,OAAO,QAAA,CAAS,KAAK,KAAK,KAAA,GAAQ,CAAA,IAAK,QAAQ,GAAA,EAAK;AACvD,IAAA,aAAA,CAAc,wDAAwD,CAAA;AACtE,IAAA;AAAA,EACF;AAKA,EAAA,MAAM,mBAAA,GAAsB,CAC1B,KAAA,EACA,IAAA,EACA,EAAE,SAAA,GAAY,KAAA,EAAM,GAA6B,EAAC,KAC3B;AACvB,IAAA,IAAI,KAAA,KAAU,QAAW,OAAO,MAAA;AAChC,IAAA,IAAI,CAAC,OAAO,QAAA,CAAS,KAAK,MAAM,SAAA,GAAY,KAAA,GAAQ,CAAA,GAAI,KAAA,GAAQ,CAAA,CAAA,EAAI;AAClE,MAAA,aAAA;AAAA,QACE,CAAA,QAAA,EAAW,IAAI,CAAA,YAAA,EAAe,SAAA,GAAY,iBAAiB,UAAU,CAAA,SAAA;AAAA,OACvE;AAAA,IACF;AACA,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AACA,EAAA,MAAM,UAAA,GAAa,mBAAA,CAAoB,OAAA,CAAQ,UAAA,EAAY,eAAe,CAAA;AAC1E,EAAA,MAAM,UAAA,GAAa,mBAAA,CAAoB,OAAA,CAAQ,UAAA,EAAY,eAAA,EAAiB;AAAA,IAC1E,SAAA,EAAW;AAAA,GACZ,CAAA;AACD,EAAA,MAAM,YAAA,GAAe,mBAAA;AAAA,IACnB,OAAA,CAAQ,YAAA;AAAA,IACR,kBAAA;AAAA,IACA,EAAE,WAAW,IAAA;AAAK,GACpB;AACA,EAAA,MAAM,YAAA,GAAe,mBAAA;AAAA,IACnB,OAAA,CAAQ,YAAA;AAAA,IACR,iBAAA;AAAA,IACA,EAAE,WAAW,IAAA;AAAK,GACpB;AACA,EAAA,IAAI,OAAA,CAAQ,YAAY,MAAA,IAAa,CAAC,OAAO,QAAA,CAAS,OAAA,CAAQ,OAAO,CAAA,EAAG;AACtE,IAAA,aAAA,CAAc,yCAAyC,CAAA;AACvD,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,kBAAkB,+BAAA,CAAgC;AAAA,IACtD,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,QAAQ,OAAA,CAAQ;AAAA,GACjB,CAAA;AAED,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,MAAM,gBAAA,CAAiB;AAAA,MACpC,SAAS,eAAA,CAAgB,OAAA;AAAA,MACzB,IAAA;AAAA,MACA,SAAA,EAAW,KAAA;AAAA,MACX,iBAAA,EAAmB,UAAA;AAAA,MACnB,YACE,UAAA,IAAc,IAAA,IAAQ,YAAA,IAAgB,IAAA,IAAQ,gBAAgB,IAAA,GAC1D;AAAA,QACE,UAAA;AAAA,QACA,YAAA;AAAA,QACA;AAAA,OACF,GACA,KAAA,CAAA;AAAA,MACN,QAAQ,eAAA,CAAgB;AAAA,KACzB,CAAA;AAED,IAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,OAAA,CAAQ,GAAA;AAAA,QACN,IAAA,CAAK,SAAA;AAAA,UACH;AAAA,YACE,SAAS,eAAA,CAAgB,OAAA;AAAA,YACzB,IAAA;AAAA,YACA,OAAO,MAAA,CAAO,MAAA;AAAA,YACd,MAAA,EAAQ,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,cACzB,GAAG,CAAA;AAAA,cACH,GAAA,EAAK,QAAA,CAAc,eAAA,CAAgB,OAAA,EAAS,EAAE,OAAO;AAAA,aACvD,CAAE;AAAA,WACJ;AAAA,UACA,IAAA;AAAA,UACA;AAAA;AACF,OACF;AACA,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AACvB,MAAA,OAAA,CAAQ,GAAA,CAAIT,MAAAA,CAAM,MAAA,CAAO,kBAAkB,CAAC,CAAA;AAC5C,MAAA;AAAA,IACF;AAEA,IAAA,OAAA,CAAQ,GAAA;AAAA,MACNA,MAAAA,CAAM,KAAA;AAAA,QACJ,OAAO,MAAA,CAAO,MAAM,cAAc,IAAI,CAAA,UAAA,EAAa,gBAAgB,OAAO,CAAA,CAAA;AAAA;AAC5E,KACF;AACA,IAAA,OAAA,CAAQ,GAAA,EAAI;AACZ,IAAA,MAAA,CAAO,OAAA,CAAQ,CAAC,CAAA,EAAG,CAAA,KAAM;AACvB,MAAA,MAAM,IAAA,GAAOA,MAAAA,CAAM,GAAA,CAAI,CAAA,CAAA,EAAA,CAAK,CAAA,GAAI,CAAA,EAAG,QAAA,EAAS,CAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAE,CAAA;AAChE,MAAA,MAAM,MAAA,GAASA,OAAM,IAAA,CAAA,CAAM,CAAA,CAAE,UAAU,GAAA,EAAK,MAAA,CAAO,EAAA,EAAI,GAAG,CAAC,CAAA;AAC3D,MAAA,MAAM,OAAA,GAAUA,MAAAA,CAAM,KAAA,CAAM,CAAA,EAAG,CAAA,CAAE,OAAO,CAAA,QAAA,CAAA,CAAW,MAAA,CAAO,EAAA,EAAI,GAAG,CAAC,CAAA;AAClE,MAAA,MAAM,GAAA,GAAMA,MAAAA,CAAM,GAAA,CAAI,CAAA,IAAA,EAAO,YAAA,CAAa,CAAA,CAAE,GAAG,CAAC,CAAA,CAAA,CAAG,MAAA,CAAO,EAAA,EAAI,GAAG,CAAC,CAAA;AAClE,MAAA,MAAM,KAAA,GAAQA,MAAAA,CAAM,GAAA,CAAI,CAAA,EAAG,WAAA,CAAY,CAAA,CAAE,WAAW,CAAC,CAAA,CAAA,CAAG,MAAA,CAAO,EAAA,EAAI,GAAG,CAAC,CAAA;AACvE,MAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,EAAG,IAAI,CAAA,EAAA,EAAK,MAAM,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA,EAAI,CAAA,CAAE,OAAO,CAAA,CAAE,CAAA;AAAA,IAC1E,CAAC,CAAA;AAAA,EACH,SAAS,KAAA,EAAO;AACd,IAAA,aAAA;AAAA,MACE,mCAAmC,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,KAC3F;AAAA,EACF;AACF;AAEO,SAAS,uBAAA,CACd,MAAA,EACA,WAAA,GAAc,UAAA,EACR;AACN,EAAA,MAAA,CACG,OAAA,CAAQ,WAAW,CAAA,CACnB,WAAA;AAAA,IACC;AAAA,GACF,CACC,MAAA;AAAA,IACC,eAAA;AAAA,IACA,CAAA,kBAAA,EAAqB,WAAA,CAAY,IAAA,CAAK,KAAK,CAAC,CAAA,oBAAA,CAAA;AAAA,IAC5C;AAAA,GACF,CACC,MAAA;AAAA,IACC,aAAA;AAAA,IACA,iDAAA;AAAA,IACA,CAAC,CAAA,KAAM,QAAA,CAAS,CAAA,EAAG,EAAE,CAAA;AAAA,IACrB;AAAA,GACF,CACC,MAAA;AAAA,IACC,mBAAA;AAAA,IACA,8CAAA;AAAA,IACA,CAAC,CAAA,KAAM,QAAA,CAAS,CAAA,EAAG,EAAE;AAAA,GACvB,CACC,MAAA;AAAA,IACC,mBAAA;AAAA,IACA,0CAAA;AAAA,IACA,CAAC,CAAA,KAAM,QAAA,CAAS,CAAA,EAAG,EAAE;AAAA,GACvB,CACC,MAAA;AAAA,IACC,sBAAA;AAAA,IACA,oCAAA;AAAA,IACA,CAAC,CAAA,KAAM,QAAA,CAAS,CAAA,EAAG,EAAE;AAAA,GACvB,CACC,MAAA;AAAA,IACC,qBAAA;AAAA,IACA,qEAAA;AAAA,IACA,CAAC,CAAA,KAAM,QAAA,CAAS,CAAA,EAAG,EAAE;AAAA,GACvB,CACC,MAAA;AAAA,IAAO,iBAAA;AAAA,IAAmB,mCAAA;AAAA,IAAqC,CAAC,CAAA,KAC/D,QAAA,CAAS,CAAA,EAAG,EAAE;AAAA,GAChB,CACC,MAAA,CAAO,iBAAA,EAAmB,gBAAgB,CAAA,CAC1C,MAAA,CAAO,QAAA,EAAU,uBAAuB,CAAA,CACxC,MAAA,CAAO,OAAO,OAAA,KAAY;AACzB,IAAA,MAAM,gBAAgB,OAAO,CAAA;AAAA,EAC/B,CAAC,CAAA;AACL;;;ACvLO,SAAS,sBAAsB,OAAA,EAAwB;AAC5D,EAAA,MAAM,gBAAgB,OAAA,CACnB,OAAA,CAAQ,QAAQ,CAAA,CAChB,YAAY,yCAAyC,CAAA;AAExD,EAAA,0BAAA,CAA2B,aAAa,CAAA;AACxC,EAAA,yBAAA,CAA0B,aAAa,CAAA;AACvC,EAAA,yBAAA,CAA0B,aAAa,CAAA;AACvC,EAAA,6BAAA,CAA8B,aAAa,CAAA;AAC3C,EAAA,uBAAA,CAAwB,aAAa,CAAA;AACvC","file":"index.mjs","sourcesContent":["import chalk from \"chalk\";\nimport type { CommonOptions, ReadOnlyOptions } from \"../shared/types\";\n\n/**\n * Default chain ID (Base mainnet) - used by feed commands\n */\nexport const DEFAULT_CHAIN_ID = 8453;\n\n/**\n * Get chain ID from option or environment variable, exit if not found\n */\nfunction getRequiredChainId(optionValue?: number): number {\n const chainId =\n optionValue ||\n (process.env.NET_CHAIN_ID\n ? parseInt(process.env.NET_CHAIN_ID, 10)\n : undefined);\n\n if (!chainId) {\n console.error(\n chalk.red(\n \"Error: Chain ID is required. Provide via --chain-id flag or NET_CHAIN_ID environment variable\"\n )\n );\n process.exit(1);\n }\n\n return chainId;\n}\n\n/**\n * Get chain ID from option or environment variable, defaulting to Base (8453)\n * Also checks BOTCHAN_* env vars for backward compat\n */\nfunction getChainIdWithDefault(optionValue?: number): number {\n if (optionValue) {\n return optionValue;\n }\n\n const envChainId =\n process.env.BOTCHAN_CHAIN_ID || process.env.NET_CHAIN_ID;\n\n if (envChainId) {\n return parseInt(envChainId, 10);\n }\n\n return DEFAULT_CHAIN_ID;\n}\n\n/**\n * Get RPC URL from option or environment variable\n */\nfunction getRpcUrl(optionValue?: string): string | undefined {\n return optionValue || process.env.NET_RPC_URL;\n}\n\n/**\n * Get RPC URL from option or environment variable, also checking BOTCHAN_* env vars.\n * Used only by feed commands for backward compat.\n */\nfunction getRpcUrlWithBotchanFallback(optionValue?: string): string | undefined {\n return optionValue || process.env.BOTCHAN_RPC_URL || process.env.NET_RPC_URL;\n}\n\n/**\n * Parse and validate common options shared across all commands.\n * Extracts private key, chain ID, and RPC URL from command options or environment variables.\n * @param options - Command options\n * @param supportsEncodeOnly - If true, mention --encode-only in error messages as an alternative\n */\nexport function parseCommonOptions(\n options: {\n privateKey?: string;\n chainId?: number;\n rpcUrl?: string;\n },\n supportsEncodeOnly = false\n): CommonOptions {\n const privateKey =\n options.privateKey ||\n process.env.NET_PRIVATE_KEY ||\n process.env.PRIVATE_KEY;\n\n if (!privateKey) {\n const encodeOnlyHint = supportsEncodeOnly\n ? \", or use --encode-only to output transaction data without submitting\"\n : \"\";\n console.error(\n chalk.red(\n `Error: Private key is required. Provide via --private-key flag or NET_PRIVATE_KEY/PRIVATE_KEY environment variable${encodeOnlyHint}`\n )\n );\n process.exit(1);\n }\n\n if (!privateKey.startsWith(\"0x\") || privateKey.length !== 66) {\n console.error(\n chalk.red(\n \"Error: Invalid private key format (must be 0x-prefixed, 66 characters)\"\n )\n );\n process.exit(1);\n }\n\n if (options.privateKey) {\n console.warn(\n chalk.yellow(\n \"Warning: Private key provided via command line. Consider using NET_PRIVATE_KEY environment variable instead.\"\n )\n );\n }\n\n return {\n privateKey: privateKey as `0x${string}`,\n chainId: getRequiredChainId(options.chainId),\n rpcUrl: getRpcUrl(options.rpcUrl),\n };\n}\n\n/**\n * Parse and validate read-only options for commands that don't need a private key.\n * Extracts chain ID and RPC URL from command options or environment variables.\n */\nexport function parseReadOnlyOptions(options: {\n chainId?: number;\n rpcUrl?: string;\n}): ReadOnlyOptions {\n return {\n chainId: getRequiredChainId(options.chainId),\n rpcUrl: getRpcUrl(options.rpcUrl),\n };\n}\n\n/**\n * Parse read-only options with a default chain ID (8453/Base).\n * Used by feed commands where chain ID is optional.\n * Also checks BOTCHAN_* env vars for backward compat.\n */\nexport function parseReadOnlyOptionsWithDefault(options: {\n chainId?: number;\n rpcUrl?: string;\n}): ReadOnlyOptions {\n return {\n chainId: getChainIdWithDefault(options.chainId),\n rpcUrl: getRpcUrlWithBotchanFallback(options.rpcUrl),\n };\n}\n\n/**\n * Parse common options with a default chain ID (8453/Base).\n * Used by feed write commands where chain ID is optional.\n * Also checks BOTCHAN_* env vars for backward compat.\n */\nexport function parseCommonOptionsWithDefault(\n options: {\n privateKey?: string;\n chainId?: number;\n rpcUrl?: string;\n },\n supportsEncodeOnly = false\n): CommonOptions {\n const privateKey =\n options.privateKey ||\n process.env.BOTCHAN_PRIVATE_KEY ||\n process.env.NET_PRIVATE_KEY ||\n process.env.PRIVATE_KEY;\n\n if (!privateKey) {\n const encodeOnlyHint = supportsEncodeOnly\n ? \", or use --encode-only to output transaction data without submitting\"\n : \"\";\n console.error(\n chalk.red(\n `Error: Private key is required. Provide via --private-key flag or NET_PRIVATE_KEY/BOTCHAN_PRIVATE_KEY environment variable${encodeOnlyHint}`\n )\n );\n process.exit(1);\n }\n\n if (!privateKey.startsWith(\"0x\") || privateKey.length !== 66) {\n console.error(\n chalk.red(\n \"Error: Invalid private key format (must be 0x-prefixed, 66 characters)\"\n )\n );\n process.exit(1);\n }\n\n if (options.privateKey) {\n console.warn(\n chalk.yellow(\n \"Warning: Private key provided via command line. Consider using NET_PRIVATE_KEY environment variable instead.\"\n )\n );\n }\n\n return {\n privateKey: privateKey as `0x${string}`,\n chainId: getChainIdWithDefault(options.chainId),\n rpcUrl: getRpcUrlWithBotchanFallback(options.rpcUrl),\n };\n}\n","import { createWalletClient, http } from \"viem\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { getChainRpcUrls, getBaseDataSuffix } from \"@net-protocol/core\";\nimport type { WriteTransactionConfig } from \"@net-protocol/core\";\n\n/**\n * Create a wallet client from a private key\n */\nexport function createWallet(\n privateKey: `0x${string}`,\n chainId: number,\n rpcUrl?: string\n) {\n const account = privateKeyToAccount(privateKey);\n const rpcUrls = getChainRpcUrls({\n chainId,\n rpcUrl: rpcUrl,\n });\n\n return createWalletClient({\n account,\n transport: http(rpcUrls[0]),\n dataSuffix: getBaseDataSuffix(chainId),\n });\n}\n\n/**\n * Execute a transaction using a wallet client\n */\nexport async function executeTransaction(\n walletClient: ReturnType<typeof createWallet>,\n txConfig: WriteTransactionConfig\n): Promise<`0x${string}`> {\n const hash = await walletClient.writeContract({\n address: txConfig.to,\n abi: txConfig.abi,\n functionName: txConfig.functionName,\n args: txConfig.args,\n value: txConfig.value,\n chain: null,\n } as Parameters<typeof walletClient.writeContract>[0]);\n\n return hash;\n}\n","import { encodeFunctionData, concat } from \"viem\";\nimport { getBaseDataSuffix } from \"@net-protocol/core\";\nimport type { WriteTransactionConfig } from \"@net-protocol/core\";\nimport type { EncodedTransaction } from \"./types\";\n\nexport type { EncodedTransaction };\n\n/**\n * Encode a write transaction config into transaction data\n * Used for --encode-only mode where we output transaction data instead of executing\n */\nexport function encodeTransaction(\n config: WriteTransactionConfig,\n chainId: number\n): EncodedTransaction {\n const calldata = encodeFunctionData({\n abi: config.abi,\n functionName: config.functionName,\n args: config.args,\n });\n\n const suffix = getBaseDataSuffix(chainId);\n const data = suffix ? concat([calldata, suffix]) : calldata;\n\n return {\n to: config.to,\n data,\n chainId,\n value: config.value?.toString() ?? \"0\",\n };\n}\n","import chalk from \"chalk\";\nimport type { NetMessage } from \"@net-protocol/core\";\n\n/**\n * Format a message for human-readable output\n */\nexport function formatMessage(\n message: NetMessage,\n index: number\n): string {\n const timestamp = new Date(Number(message.timestamp) * 1000).toISOString();\n const lines = [\n chalk.cyan(`[${index}]`) + ` ${chalk.gray(timestamp)}`,\n ` ${chalk.white(\"Sender:\")} ${message.sender}`,\n ` ${chalk.white(\"App:\")} ${message.app}`,\n ];\n\n if (message.topic) {\n lines.push(` ${chalk.white(\"Topic:\")} ${message.topic}`);\n }\n\n lines.push(` ${chalk.white(\"Text:\")} ${message.text}`);\n\n if (message.data && message.data !== \"0x\") {\n lines.push(` ${chalk.white(\"Data:\")} ${message.data}`);\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format a message for JSON output\n */\nexport function messageToJson(\n message: NetMessage,\n index: number\n): Record<string, unknown> {\n return {\n index,\n sender: message.sender,\n app: message.app,\n timestamp: Number(message.timestamp),\n text: message.text,\n topic: message.topic,\n data: message.data,\n };\n}\n\n/**\n * Print messages in human-readable or JSON format\n */\nexport function printMessages(\n messages: NetMessage[],\n startIndex: number,\n json: boolean\n): void {\n if (json) {\n const output = messages.map((msg, i) => messageToJson(msg, startIndex + i));\n console.log(JSON.stringify(output, null, 2));\n } else {\n if (messages.length === 0) {\n console.log(chalk.yellow(\"No messages found\"));\n return;\n }\n\n messages.forEach((msg, i) => {\n console.log(formatMessage(msg, startIndex + i));\n if (i < messages.length - 1) {\n console.log(); // Empty line between messages\n }\n });\n }\n}\n\n/**\n * Print a count result\n */\nexport function printCount(\n count: number,\n label: string,\n json: boolean\n): void {\n if (json) {\n console.log(JSON.stringify({ count }, null, 2));\n } else {\n console.log(`${chalk.white(label)} ${chalk.cyan(count)}`);\n }\n}\n\n/**\n * Print an error message and exit\n */\nexport function exitWithError(message: string): never {\n console.error(chalk.red(`Error: ${message}`));\n process.exit(1);\n}\n","import chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createPublicClient, http, parseEther } from \"viem\";\nimport {\n parseReadOnlyOptionsWithDefault,\n parseCommonOptionsWithDefault,\n} from \"../../cli/shared\";\nimport { createWallet, executeTransaction } from \"../../shared/wallet\";\nimport { encodeTransaction } from \"../../shared/encode\";\nimport { exitWithError } from \"../../shared/output\";\nimport { getChainRpcUrls } from \"@net-protocol/core\";\nimport {\n discoverTokenPool,\n getTokenScoreKey,\n encodePoolKey,\n UPVOTE_APP,\n PURE_ALPHA_STRATEGY,\n DYNAMIC_SPLIT_STRATEGY,\n UNIV234_POOLS_STRATEGY,\n UPVOTE_PRICE_ETH,\n} from \"@net-protocol/score\";\nimport type { UpvoteTokenOptions } from \"./types\";\n\nasync function executeUpvoteToken(options: UpvoteTokenOptions): Promise<void> {\n const count = parseInt(options.count, 10);\n if (isNaN(count) || count <= 0) {\n exitWithError(\"Count must be a positive integer\");\n return;\n }\n\n const tokenAddress = options.tokenAddress;\n if (!tokenAddress.startsWith(\"0x\") || tokenAddress.length !== 42) {\n exitWithError(\n \"Invalid token address format (must be 0x-prefixed, 42 characters)\"\n );\n return;\n }\n\n // Read-only options for pool discovery (always needed)\n const readOnlyOptions = parseReadOnlyOptionsWithDefault({\n chainId: options.chainId,\n rpcUrl: options.rpcUrl,\n });\n\n // Create public client for pool discovery\n const rpcUrls = getChainRpcUrls({\n chainId: readOnlyOptions.chainId,\n rpcUrl: readOnlyOptions.rpcUrl,\n });\n const publicClient = createPublicClient({\n transport: http(rpcUrls[0]),\n });\n\n console.log(chalk.blue(\"Discovering Uniswap pool for token...\"));\n\n let poolResult;\n try {\n poolResult = await discoverTokenPool({\n publicClient,\n tokenAddress,\n chainId: readOnlyOptions.chainId,\n });\n } catch (error) {\n exitWithError(\n `Failed to discover token pool: ${error instanceof Error ? error.message : String(error)}`\n );\n return;\n }\n\n // Determine strategy\n let strategyAddress: `0x${string}`;\n let storedContext: `0x${string}`;\n\n if (!poolResult || !poolResult.poolKey) {\n // No pool found → pure alpha\n strategyAddress = PURE_ALPHA_STRATEGY.address;\n storedContext = \"0x\";\n console.log(chalk.yellow(\"No pool found — using Pure Alpha strategy\"));\n } else if (options.splitType === \"50/50\") {\n // Pool found + 50/50 override → UNIV234_POOLS_STRATEGY\n strategyAddress = UNIV234_POOLS_STRATEGY.address;\n storedContext = encodePoolKey(poolResult.poolKey);\n console.log(\n chalk.green(\n `Pool found (fee: ${poolResult.fee}) — using 50/50 Pools strategy`\n )\n );\n } else {\n // Pool found + default/dynamic → DYNAMIC_SPLIT_STRATEGY\n strategyAddress = DYNAMIC_SPLIT_STRATEGY.address;\n storedContext = encodePoolKey(poolResult.poolKey);\n console.log(\n chalk.green(\n `Pool found (fee: ${poolResult.fee}) — using Dynamic Split strategy`\n )\n );\n }\n\n // Build transaction config\n const scoreKey = getTokenScoreKey(tokenAddress);\n const value = parseEther((count * UPVOTE_PRICE_ETH).toString());\n\n const txConfig = {\n to: UPVOTE_APP.address,\n abi: UPVOTE_APP.abi,\n functionName: \"upvote\" as const,\n args: [strategyAddress, scoreKey, count, storedContext, \"0x\"],\n value,\n };\n\n if (options.encodeOnly) {\n const encoded = encodeTransaction(txConfig, readOnlyOptions.chainId);\n console.log(JSON.stringify(encoded, null, 2));\n return;\n }\n\n // Execute transaction\n const commonOptions = parseCommonOptionsWithDefault(\n {\n privateKey: options.privateKey,\n chainId: options.chainId,\n rpcUrl: options.rpcUrl,\n },\n true\n );\n\n const walletClient = createWallet(\n commonOptions.privateKey,\n commonOptions.chainId,\n commonOptions.rpcUrl\n );\n\n console.log(\n chalk.blue(`Submitting ${count} upvote(s) for ${tokenAddress}...`)\n );\n\n try {\n const hash = await executeTransaction(walletClient, txConfig);\n\n console.log(chalk.green(`Upvote submitted successfully!`));\n console.log(chalk.white(` Transaction: ${hash}`));\n console.log(chalk.white(` Token: ${tokenAddress}`));\n console.log(chalk.white(` Count: ${count}`));\n console.log(\n chalk.white(` Value: ${(count * UPVOTE_PRICE_ETH).toFixed(6)} ETH`)\n );\n } catch (error) {\n exitWithError(\n `Failed to submit upvote: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\nexport function registerUpvoteTokenCommand(\n parent: Command,\n commandName = \"token\"\n): void {\n parent\n .command(commandName)\n .description(\"Upvote a token on Net Protocol\")\n .requiredOption(\n \"--token-address <address>\",\n \"Token contract address to upvote\"\n )\n .requiredOption(\"--count <n>\", \"Number of upvotes\")\n .option(\n \"--split-type <type>\",\n 'Strategy split type: \"dynamic\" (default) or \"50/50\"'\n )\n .option(\"--chain-id <id>\", \"Chain ID (default: 8453 for Base)\", (value) =>\n parseInt(value, 10)\n )\n .option(\"--rpc-url <url>\", \"Custom RPC URL\")\n .option(\"--private-key <key>\", \"Private key (0x-prefixed)\")\n .option(\n \"--encode-only\",\n \"Output transaction data as JSON instead of executing\"\n )\n .action(async (options) => {\n await executeUpvoteToken(options);\n });\n}\n","/**\n * URL builders for Net Protocol web pages and external services.\n *\n * These helpers exist so callers (and AI agents reading CLI JSON output) never\n * need to construct URLs by hand. URL grammar quirks (the \"feed-\" topic\n * prefix, hyphen-vs-colon separators in comment IDs, lowercased addresses,\n * etc.) all live here in one place.\n *\n * Chain slugs and block-explorer base URLs come from\n * `@net-protocol/core`'s chain config — this module deliberately holds no\n * per-chain tables of its own.\n */\n\nimport {\n getChainBlockExplorer,\n getChainSlug,\n} from \"@net-protocol/core\";\nimport { encodeStorageKeyForUrl } from \"@net-protocol/storage\";\n\nconst WEBSITE_BASE = \"https://netprotocol.app\";\nconst STORAGE_BASE = \"https://storedon.net\";\n\nexport function chainSlug(chainId: number): string | null {\n return getChainSlug({ chainId }) ?? null;\n}\n\n/**\n * Strip the \"feed-\" prefix from a topic if present (case-insensitive).\n * Used when converting an on-chain topic to the URL path/query form.\n */\nfunction stripFeedPrefix(topic: string): string {\n const lower = topic.toLowerCase();\n return lower.startsWith(\"feed-\") ? lower.slice(5) : lower;\n}\n\n/**\n * URL of a feed page. `feedName` may be passed with or without the \"feed-\"\n * prefix; either way the URL form is stripped+lowercased.\n */\nexport function feedUrl(chainId: number, feedName: string): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n return `${WEBSITE_BASE}/app/feed/${slug}/${stripFeedPrefix(feedName)}`;\n}\n\n/**\n * URL of an address's wall (their personal feed at `feed-{lower(address)}`).\n */\nexport function walletUrl(chainId: number, address: string): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n return `${WEBSITE_BASE}/app/feed/${slug}/${address.toLowerCase()}`;\n}\n\n/**\n * URL of a user/agent profile page.\n */\nexport function profileUrl(chainId: number, address: string): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n return `${WEBSITE_BASE}/app/profile/${slug}/${address.toLowerCase()}`;\n}\n\n/**\n * URL of a group chat page.\n */\nexport function chatUrl(chainId: number, chatName: string): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n return `${WEBSITE_BASE}/app/chat/${slug}/${encodeURIComponent(\n chatName.toLowerCase()\n )}`;\n}\n\n/**\n * URL of a Netr token page.\n */\nexport function tokenUrl(\n chainId: number,\n tokenAddress: string\n): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n return `${WEBSITE_BASE}/app/token/${slug}/${tokenAddress.toLowerCase()}`;\n}\n\n/**\n * URL of a Bazaar NFT collection page.\n */\nexport function bazaarUrl(\n chainId: number,\n nftAddress: string\n): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n return `${WEBSITE_BASE}/app/bazaar/${slug}/${nftAddress.toLowerCase()}`;\n}\n\n/**\n * URL of an onchain agent detail page.\n */\nexport function agentUrl(chainId: number, agentId: string): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n return `${WEBSITE_BASE}/app/agents/${slug}/${encodeURIComponent(agentId)}`;\n}\n\n/**\n * Public storage retrieval URL via storedon.net. Works on any chain with Net\n * Storage deployed (returns a URL even when chainSlug is unknown — storedon\n * uses numeric chain IDs).\n *\n * Uses `encodeStorageKeyForUrl` from `@net-protocol/storage` so the encoder\n * stays in sync with the storage SDK if it ever needs to handle binary keys\n * or other special characters differently from `encodeURIComponent`.\n */\nexport function storageUrl(\n chainId: number,\n operatorAddress: string,\n key: string\n): string {\n return `${STORAGE_BASE}/net/${chainId}/storage/load/${operatorAddress.toLowerCase()}/${encodeStorageKeyForUrl(\n key\n )}`;\n}\n\nexport function explorerTxUrl(\n chainId: number,\n txHash: string\n): string | null {\n const base = getChainBlockExplorer({ chainId })?.url;\n if (!base) return null;\n return `${base}/tx/${txHash}`;\n}\n\nexport function explorerAddressUrl(\n chainId: number,\n address: string\n): string | null {\n const base = getChainBlockExplorer({ chainId })?.url;\n if (!base) return null;\n return `${base}/address/${address}`;\n}\n\n/**\n * Convert a post ID (`{sender}:{timestamp}`) into the comment-permalink form\n * used by the web's `?commentId=` query parameter (`{sender}-{timestamp}`).\n *\n * The two formats are intentionally documented separately because the website\n * scrolls to the DOM id `comment-{sender}-{timestamp}` and matches against the\n * raw query value (see CommentThread.tsx in the Net repo).\n */\nexport function postIdToCommentParam(postId: string): string {\n const colon = postId.indexOf(\":\");\n if (colon === -1) return postId;\n return `${postId.slice(0, colon)}-${postId.slice(colon + 1)}`;\n}\n\nexport interface PostPermalinkOptions {\n /**\n * Global message index from the contract's MessageSent event. Most reliable\n * when available — works regardless of how the post was queried.\n */\n globalIndex?: number;\n /** Topic-filtered absolute index (from a topic-scoped read). */\n topic?: string;\n topicIndex?: number;\n /** Maker-filtered absolute index (from a sender-scoped read). */\n user?: string;\n userIndex?: number;\n /**\n * Optional comment to deep-link. Pass either a post-ID-style string\n * (`{sender}:{timestamp}` — colon will be normalized to hyphen) or the\n * already-converted form.\n */\n commentId?: string;\n}\n\n/**\n * Build a permalink to the dedicated post page. Picks the most reliable URL\n * form available, in priority order: global -> topic -> user. Returns null\n * when no usable index is provided or chain is unknown.\n */\nexport function postPermalink(\n chainId: number,\n opts: PostPermalinkOptions\n): string | null {\n const slug = chainSlug(chainId);\n if (!slug) return null;\n\n const params = new URLSearchParams();\n\n if (opts.globalIndex != null) {\n params.set(\"index\", String(opts.globalIndex));\n } else if (opts.topic != null && opts.topicIndex != null) {\n params.set(\"topic\", stripFeedPrefix(opts.topic));\n params.set(\"index\", String(opts.topicIndex));\n } else if (opts.user != null && opts.userIndex != null) {\n params.set(\"user\", opts.user.toLowerCase());\n params.set(\"index\", String(opts.userIndex));\n } else {\n return null;\n }\n\n if (opts.commentId) {\n params.set(\"commentId\", postIdToCommentParam(opts.commentId));\n }\n\n return `${WEBSITE_BASE}/app/feed/${slug}/post?${params.toString()}`;\n}\n\n/**\n * URL of the canonical hosted skill (proxies to net-public/SKILL.md).\n */\nexport const HOSTED_SKILL_URL = `${WEBSITE_BASE}/skill.md`;\n","import chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { parseReadOnlyOptionsWithDefault } from \"../../cli/shared\";\nimport { exitWithError } from \"../../shared/output\";\nimport { tokenUrl as buildTokenUrl } from \"../../shared/urls\";\nimport {\n ScoreClient,\n getTokenScoreKey,\n ALL_STRATEGY_ADDRESSES,\n PURE_ALPHA_STRATEGY,\n UNIV234_POOLS_STRATEGY,\n DYNAMIC_SPLIT_STRATEGY,\n} from \"@net-protocol/score\";\nimport type { GetUpvotesOptions } from \"./types\";\n\nfunction getStrategyName(address: string): string {\n const lower = address.toLowerCase();\n if (lower === PURE_ALPHA_STRATEGY.address.toLowerCase()) return \"Pure Alpha\";\n if (lower === UNIV234_POOLS_STRATEGY.address.toLowerCase())\n return \"50/50 Pools\";\n if (lower === DYNAMIC_SPLIT_STRATEGY.address.toLowerCase())\n return \"Dynamic Split\";\n return address;\n}\n\nasync function executeGetUpvotes(options: GetUpvotesOptions): Promise<void> {\n const tokenAddress = options.tokenAddress;\n if (!tokenAddress.startsWith(\"0x\") || tokenAddress.length !== 42) {\n exitWithError(\n \"Invalid token address format (must be 0x-prefixed, 42 characters)\"\n );\n return;\n }\n\n const readOnlyOptions = parseReadOnlyOptionsWithDefault({\n chainId: options.chainId,\n rpcUrl: options.rpcUrl,\n });\n\n const client = new ScoreClient({\n chainId: readOnlyOptions.chainId,\n overrides: readOnlyOptions.rpcUrl\n ? { rpcUrls: [readOnlyOptions.rpcUrl] }\n : undefined,\n });\n\n const scoreKey = getTokenScoreKey(tokenAddress);\n\n try {\n // getUpvotesWithLegacy returns one aggregated count per scoreKey\n // (summing legacy + all specified strategies). To get per-strategy\n // counts, use getStrategyKeyScores for each strategy individually.\n const [totalCounts, ...perStrategyCounts] = await Promise.all([\n client.getUpvotesWithLegacy({\n scoreKeys: [scoreKey],\n strategies: ALL_STRATEGY_ADDRESSES,\n }),\n ...ALL_STRATEGY_ADDRESSES.map((strategy) =>\n client.getStrategyKeyScores({\n strategy,\n scoreKeys: [scoreKey],\n })\n ),\n ]);\n\n const total = totalCounts[0] ?? 0;\n const strategyCounts = ALL_STRATEGY_ADDRESSES.map((addr, i) => ({\n strategy: getStrategyName(addr),\n address: addr,\n count: perStrategyCounts[i]?.[0] ?? 0,\n }));\n\n if (options.json) {\n console.log(\n JSON.stringify(\n {\n tokenAddress,\n tokenUrl: buildTokenUrl(readOnlyOptions.chainId, tokenAddress),\n scoreKey,\n total,\n strategies: strategyCounts.map((s) => ({\n name: s.strategy,\n address: s.address,\n count: s.count,\n })),\n },\n null,\n 2\n )\n );\n } else {\n console.log(chalk.white(`Upvotes for ${tokenAddress}:`));\n console.log(chalk.cyan(` Total: ${total}`));\n console.log();\n for (const s of strategyCounts) {\n if (s.count > 0) {\n console.log(chalk.white(` ${s.strategy}: ${s.count}`));\n }\n }\n if (total === 0) {\n console.log(chalk.yellow(\" No upvotes found\"));\n }\n }\n } catch (error) {\n exitWithError(\n `Failed to fetch upvotes: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\nexport function registerGetUpvotesCommand(\n parent: Command,\n commandName = \"info\"\n): void {\n parent\n .command(commandName)\n .description(\"Get upvote counts for a token\")\n .requiredOption(\"--token-address <address>\", \"Token contract address\")\n .option(\"--chain-id <id>\", \"Chain ID (default: 8453 for Base)\", (value) =>\n parseInt(value, 10)\n )\n .option(\"--rpc-url <url>\", \"Custom RPC URL\")\n .option(\"--json\", \"Output in JSON format\")\n .action(async (options) => {\n await executeGetUpvotes(options);\n });\n}\n","import chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { formatEther } from \"viem\";\nimport {\n parseReadOnlyOptionsWithDefault,\n parseCommonOptionsWithDefault,\n} from \"../../cli/shared\";\nimport { createWallet } from \"../../shared/wallet\";\nimport { encodeTransaction } from \"../../shared/encode\";\nimport { exitWithError } from \"../../shared/output\";\nimport {\n UserUpvoteClient,\n USER_UPVOTE_CONTRACT,\n NULL_ADDRESS,\n calculateUpvoteCost,\n} from \"@net-protocol/score\";\nimport type { UpvoteUserOptions } from \"./types\";\n\nexport async function executeUpvoteUser(\n options: UpvoteUserOptions\n): Promise<void> {\n const count = parseInt(options.count, 10);\n if (isNaN(count) || count <= 0) {\n exitWithError(\"Count must be a positive integer\");\n return;\n }\n\n const userAddress = options.address;\n if (!userAddress.startsWith(\"0x\") || userAddress.length !== 42) {\n exitWithError(\n \"Invalid address format (must be 0x-prefixed, 42 characters)\"\n );\n return;\n }\n\n const token = (options.token ?? NULL_ADDRESS) as `0x${string}`;\n const feeTier = options.feeTier ? parseInt(options.feeTier, 10) : 0;\n\n const readOnlyOptions = parseReadOnlyOptionsWithDefault({\n chainId: options.chainId,\n rpcUrl: options.rpcUrl,\n });\n\n const client = new UserUpvoteClient({\n chainId: readOnlyOptions.chainId,\n overrides: readOnlyOptions.rpcUrl\n ? { rpcUrls: [readOnlyOptions.rpcUrl] }\n : undefined,\n });\n\n // Fetch current upvote price from contract\n let upvotePrice: bigint;\n try {\n upvotePrice = await client.getUpvotePrice();\n } catch (error) {\n exitWithError(\n `Failed to fetch upvote price: ${error instanceof Error ? error.message : String(error)}`\n );\n return;\n }\n\n const totalCost = calculateUpvoteCost(count, upvotePrice);\n\n if (options.encodeOnly) {\n const txConfig = {\n to: USER_UPVOTE_CONTRACT.address,\n abi: USER_UPVOTE_CONTRACT.abi,\n functionName: \"upvoteUser\" as const,\n args: [userAddress as `0x${string}`, token, BigInt(count), BigInt(feeTier)],\n value: totalCost,\n };\n const encoded = encodeTransaction(txConfig, readOnlyOptions.chainId);\n console.log(JSON.stringify(encoded, null, 2));\n return;\n }\n\n const commonOptions = parseCommonOptionsWithDefault(\n {\n privateKey: options.privateKey,\n chainId: options.chainId,\n rpcUrl: options.rpcUrl,\n },\n true\n );\n\n const walletClient = createWallet(\n commonOptions.privateKey,\n commonOptions.chainId,\n commonOptions.rpcUrl\n );\n\n console.log(\n chalk.blue(`Submitting ${count} profile upvote(s) for ${userAddress}...`)\n );\n\n try {\n const hash = await client.upvoteUser({\n walletClient,\n userToUpvote: userAddress as `0x${string}`,\n token,\n numUpvotes: count,\n feeTier,\n value: totalCost,\n });\n\n console.log(chalk.green(\"Profile upvote submitted successfully!\"));\n console.log(chalk.white(` Transaction: ${hash}`));\n console.log(chalk.white(` User: ${userAddress}`));\n console.log(chalk.white(` Count: ${count}`));\n console.log(chalk.white(` Value: ${formatEther(totalCost)} ETH`));\n if (token !== NULL_ADDRESS) {\n console.log(chalk.white(` Token: ${token}`));\n }\n } catch (error) {\n exitWithError(\n `Failed to submit profile upvote: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\nexport function registerUpvoteUserCommand(\n parent: Command,\n commandName = \"user\"\n): void {\n parent\n .command(commandName)\n .description(\"Upvote a user's profile on Net Protocol\")\n .requiredOption(\"--address <address>\", \"User address to upvote\")\n .requiredOption(\"--count <n>\", \"Number of upvotes\")\n .option(\"--token <address>\", \"Token address (default: null address)\")\n .option(\"--fee-tier <tier>\", \"Fee tier (default: 0)\")\n .option(\"--chain-id <id>\", \"Chain ID (default: 8453 for Base)\", (value) =>\n parseInt(value, 10)\n )\n .option(\"--rpc-url <url>\", \"Custom RPC URL\")\n .option(\"--private-key <key>\", \"Private key (0x-prefixed)\")\n .option(\n \"--encode-only\",\n \"Output transaction data as JSON instead of executing\"\n )\n .action(async (options) => {\n await executeUpvoteUser(options);\n });\n}\n","import chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { formatEther } from \"viem\";\nimport { parseReadOnlyOptionsWithDefault } from \"../../cli/shared\";\nimport { exitWithError } from \"../../shared/output\";\nimport { UserUpvoteClient } from \"@net-protocol/score\";\nimport {\n profileUrl as buildProfileUrl,\n walletUrl as buildWalletUrl,\n} from \"../../shared/urls\";\nimport type { GetUserUpvotesOptions } from \"./types\";\n\nexport async function executeGetUserUpvotes(\n options: GetUserUpvotesOptions\n): Promise<void> {\n const userAddress = options.address;\n if (!userAddress.startsWith(\"0x\") || userAddress.length !== 42) {\n exitWithError(\n \"Invalid address format (must be 0x-prefixed, 42 characters)\"\n );\n return;\n }\n\n const readOnlyOptions = parseReadOnlyOptionsWithDefault({\n chainId: options.chainId,\n rpcUrl: options.rpcUrl,\n });\n\n const client = new UserUpvoteClient({\n chainId: readOnlyOptions.chainId,\n overrides: readOnlyOptions.rpcUrl\n ? { rpcUrls: [readOnlyOptions.rpcUrl] }\n : undefined,\n });\n\n try {\n const [given, received, upvotePrice] = await Promise.all([\n client.getUserUpvotesGiven({\n user: userAddress as `0x${string}`,\n }),\n client.getUserUpvotesReceived({\n user: userAddress as `0x${string}`,\n }),\n client.getUpvotePrice(),\n ]);\n\n if (options.json) {\n console.log(\n JSON.stringify(\n {\n address: userAddress,\n chainId: readOnlyOptions.chainId,\n profileUrl: buildProfileUrl(readOnlyOptions.chainId, userAddress),\n walletUrl: buildWalletUrl(readOnlyOptions.chainId, userAddress),\n upvotesGiven: Number(given),\n upvotesReceived: Number(received),\n upvotePriceWei: upvotePrice.toString(),\n upvotePriceEth: formatEther(upvotePrice),\n },\n null,\n 2\n )\n );\n } else {\n console.log(chalk.white(`Profile upvotes for ${userAddress}:`));\n console.log(chalk.cyan(` Upvotes Given: ${given}`));\n console.log(chalk.cyan(` Upvotes Received: ${received}`));\n console.log(\n chalk.white(` Upvote Price: ${formatEther(upvotePrice)} ETH`)\n );\n }\n } catch (error) {\n exitWithError(\n `Failed to fetch user upvotes: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\nexport function registerGetUserUpvotesCommand(\n parent: Command,\n commandName = \"user-info\"\n): void {\n parent\n .command(commandName)\n .description(\"Get profile upvote stats for a user\")\n .requiredOption(\"--address <address>\", \"User address to look up\")\n .option(\"--chain-id <id>\", \"Chain ID (default: 8453 for Base)\", (value) =>\n parseInt(value, 10)\n )\n .option(\"--rpc-url <url>\", \"Custom RPC URL\")\n .option(\"--json\", \"Output in JSON format\")\n .action(async (options) => {\n await executeGetUserUpvotes(options);\n });\n}\n","import chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { parseReadOnlyOptionsWithDefault } from \"../../cli/shared\";\nimport { exitWithError } from \"../../shared/output\";\nimport { tokenUrl as buildTokenUrl } from \"../../shared/urls\";\nimport { getTokenRankings, type RankingSort } from \"@net-protocol/score\";\nimport type { RankingsOptions } from \"./types\";\n\nconst VALID_SORTS: RankingSort[] = [\"trending\", \"recent\", \"top\"];\n\nfunction formatNumber(n: number | undefined, digits = 2): string {\n if (n == null || !Number.isFinite(n)) return \"-\";\n if (n >= 1_000_000_000) return `${(n / 1_000_000_000).toFixed(digits)}B`;\n if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(digits)}M`;\n if (n >= 1_000) return `${(n / 1_000).toFixed(digits)}K`;\n return n.toFixed(digits);\n}\n\nfunction formatPrice(price: number | undefined): string {\n if (price == null || !Number.isFinite(price)) return \"-\";\n if (price < 0.000001) return price.toExponential(2);\n if (price < 1) return `$${price.toFixed(6)}`;\n return `$${price.toFixed(4)}`;\n}\n\nasync function executeRankings(options: RankingsOptions): Promise<void> {\n const sort = (options.sort ?? \"trending\").toLowerCase() as RankingSort;\n if (!VALID_SORTS.includes(sort)) {\n exitWithError(\n `Invalid --sort \"${options.sort}\". Must be one of: ${VALID_SORTS.join(\", \")}`\n );\n return;\n }\n\n const limit = options.limit ?? 10;\n if (!Number.isFinite(limit) || limit < 1 || limit > 100) {\n exitWithError(\"Invalid --limit. Must be an integer between 1 and 100.\");\n return;\n }\n\n // Reject NaN that survives parseInt for any numeric flag. Without this an\n // input like `--scan-window foo` propagates NaN into the ranking algorithm\n // and silently produces empty/junk output.\n const optionalPositiveInt = (\n value: number | undefined,\n name: string,\n { allowZero = false }: { allowZero?: boolean } = {}\n ): number | undefined => {\n if (value === undefined) return undefined;\n if (!Number.isFinite(value) || (allowZero ? value < 0 : value < 1)) {\n exitWithError(\n `Invalid ${name}. Must be a ${allowZero ? \"non-negative\" : \"positive\"} integer.`\n );\n }\n return value;\n };\n const scanWindow = optionalPositiveInt(options.scanWindow, \"--scan-window\");\n const minUpvotes = optionalPositiveInt(options.minUpvotes, \"--min-upvotes\", {\n allowZero: true,\n });\n const minMarketCap = optionalPositiveInt(\n options.minMarketCap,\n \"--min-market-cap\",\n { allowZero: true }\n );\n const recencyHours = optionalPositiveInt(\n options.recencyHours,\n \"--recency-hours\",\n { allowZero: true }\n );\n if (options.chainId !== undefined && !Number.isFinite(options.chainId)) {\n exitWithError(\"Invalid --chain-id. Must be an integer.\");\n return;\n }\n\n const readOnlyOptions = parseReadOnlyOptionsWithDefault({\n chainId: options.chainId,\n rpcUrl: options.rpcUrl,\n });\n\n try {\n const tokens = await getTokenRankings({\n chainId: readOnlyOptions.chainId,\n sort,\n maxTokens: limit,\n messageScanWindow: scanWindow,\n thresholds:\n minUpvotes != null || minMarketCap != null || recencyHours != null\n ? {\n minUpvotes,\n minMarketCap,\n recencyHours,\n }\n : undefined,\n rpcUrl: readOnlyOptions.rpcUrl,\n });\n\n if (options.json) {\n console.log(\n JSON.stringify(\n {\n chainId: readOnlyOptions.chainId,\n sort,\n count: tokens.length,\n tokens: tokens.map((t) => ({\n ...t,\n url: buildTokenUrl(readOnlyOptions.chainId, t.address),\n })),\n },\n null,\n 2\n )\n );\n return;\n }\n\n if (tokens.length === 0) {\n console.log(chalk.yellow(\"No tokens found.\"));\n return;\n }\n\n console.log(\n chalk.white(\n `Top ${tokens.length} tokens by ${sort} on chain ${readOnlyOptions.chainId}:`\n )\n );\n console.log();\n tokens.forEach((t, i) => {\n const rank = chalk.dim(`#${(i + 1).toString().padStart(2, \" \")}`);\n const symbol = chalk.cyan((t.symbol || \"?\").padEnd(10, \" \"));\n const upvotes = chalk.white(`${t.upvotes} upvotes`.padEnd(18, \" \"));\n const fdv = chalk.dim(`FDV ${formatNumber(t.fdv)}`.padEnd(14, \" \"));\n const price = chalk.dim(`${formatPrice(t.priceInUsdc)}`.padEnd(14, \" \"));\n console.log(`${rank} ${symbol} ${upvotes} ${fdv} ${price} ${t.address}`);\n });\n } catch (error) {\n exitWithError(\n `Failed to fetch token rankings: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\nexport function registerRankingsCommand(\n parent: Command,\n commandName = \"rankings\"\n): void {\n parent\n .command(commandName)\n .description(\n \"List tokens ranked by upvote activity (trending / recent / top)\"\n )\n .option(\n \"--sort <sort>\",\n `Ranking strategy: ${VALID_SORTS.join(\" | \")} (default: trending)`,\n \"trending\"\n )\n .option(\n \"--limit <n>\",\n \"Number of tokens to return (1-100, default: 50)\",\n (v) => parseInt(v, 10),\n 50\n )\n .option(\n \"--scan-window <n>\",\n \"Messages to scan per contract (default: 150)\",\n (v) => parseInt(v, 10)\n )\n .option(\n \"--min-upvotes <n>\",\n \"Floor for two-tier filter (default: 500)\",\n (v) => parseInt(v, 10)\n )\n .option(\n \"--min-market-cap <n>\",\n \"FDV floor in USDC (default: 40000)\",\n (v) => parseInt(v, 10)\n )\n .option(\n \"--recency-hours <n>\",\n \"Drop below-floor tokens with no upvote within N hours (default: 48)\",\n (v) => parseInt(v, 10)\n )\n .option(\"--chain-id <id>\", \"Chain ID (default: 8453 for Base)\", (v) =>\n parseInt(v, 10)\n )\n .option(\"--rpc-url <url>\", \"Custom RPC URL\")\n .option(\"--json\", \"Output in JSON format\")\n .action(async (options) => {\n await executeRankings(options);\n });\n}\n","import { Command } from \"commander\";\nimport { registerUpvoteTokenCommand } from \"./upvote-token\";\nimport { registerGetUpvotesCommand } from \"./get-upvotes\";\nimport { registerUpvoteUserCommand } from \"./upvote-user\";\nimport { registerGetUserUpvotesCommand } from \"./get-user-upvotes\";\nimport { registerRankingsCommand } from \"./rankings\";\n\nexport function registerUpvoteCommand(program: Command): void {\n const upvoteCommand = program\n .command(\"upvote\")\n .description(\"Upvote tokens and users on Net Protocol\");\n\n registerUpvoteTokenCommand(upvoteCommand);\n registerGetUpvotesCommand(upvoteCommand);\n registerUpvoteUserCommand(upvoteCommand);\n registerGetUserUpvotesCommand(upvoteCommand);\n registerRankingsCommand(upvoteCommand);\n}\n\n// Re-exports for botchan\nexport { registerUpvoteTokenCommand } from \"./upvote-token\";\nexport { registerGetUpvotesCommand } from \"./get-upvotes\";\nexport { registerUpvoteUserCommand } from \"./upvote-user\";\nexport { registerGetUserUpvotesCommand } from \"./get-user-upvotes\";\nexport { registerRankingsCommand } from \"./rankings\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@net-protocol/cli",
3
- "version": "0.1.48",
3
+ "version": "0.2.1",
4
4
  "description": "CLI tool for Net Protocol",
5
5
  "type": "module",
6
6
  "bin": {
@@ -49,7 +49,7 @@
49
49
  },
50
50
  "dependencies": {
51
51
  "@net-protocol/agents": "^0.1.0",
52
- "@net-protocol/bazaar": "^0.1.17",
52
+ "@net-protocol/bazaar": "^0.2.0",
53
53
  "@net-protocol/chats": "^0.1.0",
54
54
  "@net-protocol/core": "^0.1.9",
55
55
  "@net-protocol/feeds": "^0.1.14",