@net-protocol/cli 0.1.29 → 0.1.31

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.
@@ -0,0 +1,9 @@
1
+ import { Command } from 'commander';
2
+
3
+ declare function registerUpvoteTokenCommand(parent: Command, commandName?: string): void;
4
+
5
+ declare function registerGetUpvotesCommand(parent: Command, commandName?: string): void;
6
+
7
+ declare function registerUpvoteCommand(program: Command): void;
8
+
9
+ export { registerGetUpvotesCommand, registerUpvoteCommand, registerUpvoteTokenCommand };
@@ -0,0 +1,323 @@
1
+ import chalk3 from 'chalk';
2
+ import { createPublicClient, http, parseEther, encodeFunctionData, createWalletClient } from 'viem';
3
+ import { privateKeyToAccount } from 'viem/accounts';
4
+ import { getChainRpcUrls, getBaseDataSuffix } 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 } from '@net-protocol/score';
6
+
7
+ // src/commands/upvote/upvote-token.ts
8
+ var DEFAULT_CHAIN_ID = 8453;
9
+ function getChainIdWithDefault(optionValue) {
10
+ if (optionValue) {
11
+ return optionValue;
12
+ }
13
+ const envChainId = process.env.BOTCHAN_CHAIN_ID || process.env.NET_CHAIN_ID;
14
+ if (envChainId) {
15
+ return parseInt(envChainId, 10);
16
+ }
17
+ return DEFAULT_CHAIN_ID;
18
+ }
19
+ function getRpcUrlWithBotchanFallback(optionValue) {
20
+ return optionValue || process.env.BOTCHAN_RPC_URL || process.env.NET_RPC_URL;
21
+ }
22
+ function parseReadOnlyOptionsWithDefault(options) {
23
+ return {
24
+ chainId: getChainIdWithDefault(options.chainId),
25
+ rpcUrl: getRpcUrlWithBotchanFallback(options.rpcUrl)
26
+ };
27
+ }
28
+ function parseCommonOptionsWithDefault(options, supportsEncodeOnly = false) {
29
+ const privateKey = options.privateKey || process.env.BOTCHAN_PRIVATE_KEY || process.env.NET_PRIVATE_KEY || process.env.PRIVATE_KEY;
30
+ if (!privateKey) {
31
+ const encodeOnlyHint = supportsEncodeOnly ? ", or use --encode-only to output transaction data without submitting" : "";
32
+ console.error(
33
+ chalk3.red(
34
+ `Error: Private key is required. Provide via --private-key flag or NET_PRIVATE_KEY/BOTCHAN_PRIVATE_KEY environment variable${encodeOnlyHint}`
35
+ )
36
+ );
37
+ process.exit(1);
38
+ }
39
+ if (!privateKey.startsWith("0x") || privateKey.length !== 66) {
40
+ console.error(
41
+ chalk3.red(
42
+ "Error: Invalid private key format (must be 0x-prefixed, 66 characters)"
43
+ )
44
+ );
45
+ process.exit(1);
46
+ }
47
+ if (options.privateKey) {
48
+ console.warn(
49
+ chalk3.yellow(
50
+ "Warning: Private key provided via command line. Consider using NET_PRIVATE_KEY environment variable instead."
51
+ )
52
+ );
53
+ }
54
+ return {
55
+ privateKey,
56
+ chainId: getChainIdWithDefault(options.chainId),
57
+ rpcUrl: getRpcUrlWithBotchanFallback(options.rpcUrl)
58
+ };
59
+ }
60
+ function createWallet(privateKey, chainId, rpcUrl) {
61
+ const account = privateKeyToAccount(privateKey);
62
+ const rpcUrls = getChainRpcUrls({
63
+ chainId,
64
+ rpcUrl
65
+ });
66
+ return createWalletClient({
67
+ account,
68
+ transport: http(rpcUrls[0]),
69
+ dataSuffix: getBaseDataSuffix(chainId)
70
+ });
71
+ }
72
+ async function executeTransaction(walletClient, txConfig) {
73
+ const hash = await walletClient.writeContract({
74
+ address: txConfig.to,
75
+ abi: txConfig.abi,
76
+ functionName: txConfig.functionName,
77
+ args: txConfig.args,
78
+ value: txConfig.value,
79
+ chain: null
80
+ });
81
+ return hash;
82
+ }
83
+ function encodeTransaction(config, chainId) {
84
+ const calldata = encodeFunctionData({
85
+ abi: config.abi,
86
+ functionName: config.functionName,
87
+ args: config.args
88
+ });
89
+ return {
90
+ to: config.to,
91
+ data: calldata,
92
+ chainId,
93
+ value: config.value?.toString() ?? "0"
94
+ };
95
+ }
96
+ function exitWithError(message) {
97
+ console.error(chalk3.red(`Error: ${message}`));
98
+ process.exit(1);
99
+ }
100
+ async function executeUpvoteToken(options) {
101
+ const count = parseInt(options.count, 10);
102
+ if (isNaN(count) || count <= 0) {
103
+ exitWithError("Count must be a positive integer");
104
+ return;
105
+ }
106
+ const tokenAddress = options.tokenAddress;
107
+ if (!tokenAddress.startsWith("0x") || tokenAddress.length !== 42) {
108
+ exitWithError(
109
+ "Invalid token address format (must be 0x-prefixed, 42 characters)"
110
+ );
111
+ return;
112
+ }
113
+ const readOnlyOptions = parseReadOnlyOptionsWithDefault({
114
+ chainId: options.chainId,
115
+ rpcUrl: options.rpcUrl
116
+ });
117
+ const rpcUrls = getChainRpcUrls({
118
+ chainId: readOnlyOptions.chainId,
119
+ rpcUrl: readOnlyOptions.rpcUrl
120
+ });
121
+ const publicClient = createPublicClient({
122
+ transport: http(rpcUrls[0])
123
+ });
124
+ console.log(chalk3.blue("Discovering Uniswap pool for token..."));
125
+ let poolResult;
126
+ try {
127
+ poolResult = await discoverTokenPool({
128
+ publicClient,
129
+ tokenAddress
130
+ });
131
+ } catch (error) {
132
+ exitWithError(
133
+ `Failed to discover token pool: ${error instanceof Error ? error.message : String(error)}`
134
+ );
135
+ return;
136
+ }
137
+ let strategyAddress;
138
+ let storedContext;
139
+ if (!poolResult || !poolResult.poolKey) {
140
+ strategyAddress = PURE_ALPHA_STRATEGY.address;
141
+ storedContext = "0x";
142
+ console.log(chalk3.yellow("No pool found \u2014 using Pure Alpha strategy"));
143
+ } else if (options.splitType === "50/50") {
144
+ strategyAddress = UNIV234_POOLS_STRATEGY.address;
145
+ storedContext = encodePoolKey(poolResult.poolKey);
146
+ console.log(
147
+ chalk3.green(
148
+ `Pool found (fee: ${poolResult.fee}) \u2014 using 50/50 Pools strategy`
149
+ )
150
+ );
151
+ } else {
152
+ strategyAddress = DYNAMIC_SPLIT_STRATEGY.address;
153
+ storedContext = encodePoolKey(poolResult.poolKey);
154
+ console.log(
155
+ chalk3.green(
156
+ `Pool found (fee: ${poolResult.fee}) \u2014 using Dynamic Split strategy`
157
+ )
158
+ );
159
+ }
160
+ const scoreKey = getTokenScoreKey(tokenAddress);
161
+ const value = parseEther((count * UPVOTE_PRICE_ETH).toString());
162
+ const txConfig = {
163
+ to: UPVOTE_APP.address,
164
+ abi: UPVOTE_APP.abi,
165
+ functionName: "upvote",
166
+ args: [strategyAddress, scoreKey, count, storedContext, "0x"],
167
+ value
168
+ };
169
+ if (options.encodeOnly) {
170
+ const encoded = encodeTransaction(txConfig, readOnlyOptions.chainId);
171
+ console.log(JSON.stringify(encoded, null, 2));
172
+ return;
173
+ }
174
+ const commonOptions = parseCommonOptionsWithDefault(
175
+ {
176
+ privateKey: options.privateKey,
177
+ chainId: options.chainId,
178
+ rpcUrl: options.rpcUrl
179
+ },
180
+ true
181
+ );
182
+ const walletClient = createWallet(
183
+ commonOptions.privateKey,
184
+ commonOptions.chainId,
185
+ commonOptions.rpcUrl
186
+ );
187
+ console.log(
188
+ chalk3.blue(`Submitting ${count} upvote(s) for ${tokenAddress}...`)
189
+ );
190
+ try {
191
+ const hash = await executeTransaction(walletClient, txConfig);
192
+ console.log(chalk3.green(`Upvote submitted successfully!`));
193
+ console.log(chalk3.white(` Transaction: ${hash}`));
194
+ console.log(chalk3.white(` Token: ${tokenAddress}`));
195
+ console.log(chalk3.white(` Count: ${count}`));
196
+ console.log(
197
+ chalk3.white(` Value: ${(count * UPVOTE_PRICE_ETH).toFixed(6)} ETH`)
198
+ );
199
+ } catch (error) {
200
+ exitWithError(
201
+ `Failed to submit upvote: ${error instanceof Error ? error.message : String(error)}`
202
+ );
203
+ }
204
+ }
205
+ function registerUpvoteTokenCommand(parent, commandName = "token") {
206
+ parent.command(commandName).description("Upvote a token on Net Protocol").requiredOption(
207
+ "--token-address <address>",
208
+ "Token contract address to upvote"
209
+ ).requiredOption("--count <n>", "Number of upvotes").option(
210
+ "--split-type <type>",
211
+ 'Strategy split type: "dynamic" (default) or "50/50"'
212
+ ).option(
213
+ "--chain-id <id>",
214
+ "Chain ID (default: 8453 for Base)",
215
+ (value) => parseInt(value, 10)
216
+ ).option("--rpc-url <url>", "Custom RPC URL").option("--private-key <key>", "Private key (0x-prefixed)").option(
217
+ "--encode-only",
218
+ "Output transaction data as JSON instead of executing"
219
+ ).action(async (options) => {
220
+ await executeUpvoteToken(options);
221
+ });
222
+ }
223
+ function getStrategyName(address) {
224
+ const lower = address.toLowerCase();
225
+ if (lower === PURE_ALPHA_STRATEGY.address.toLowerCase()) return "Pure Alpha";
226
+ if (lower === UNIV234_POOLS_STRATEGY.address.toLowerCase())
227
+ return "50/50 Pools";
228
+ if (lower === DYNAMIC_SPLIT_STRATEGY.address.toLowerCase())
229
+ return "Dynamic Split";
230
+ return address;
231
+ }
232
+ async function executeGetUpvotes(options) {
233
+ const tokenAddress = options.tokenAddress;
234
+ if (!tokenAddress.startsWith("0x") || tokenAddress.length !== 42) {
235
+ exitWithError(
236
+ "Invalid token address format (must be 0x-prefixed, 42 characters)"
237
+ );
238
+ return;
239
+ }
240
+ const readOnlyOptions = parseReadOnlyOptionsWithDefault({
241
+ chainId: options.chainId,
242
+ rpcUrl: options.rpcUrl
243
+ });
244
+ const client = new ScoreClient({
245
+ chainId: readOnlyOptions.chainId,
246
+ overrides: readOnlyOptions.rpcUrl ? { rpcUrls: [readOnlyOptions.rpcUrl] } : void 0
247
+ });
248
+ const scoreKey = getTokenScoreKey(tokenAddress);
249
+ try {
250
+ const [totalCounts, ...perStrategyCounts] = await Promise.all([
251
+ client.getUpvotesWithLegacy({
252
+ scoreKeys: [scoreKey],
253
+ strategies: ALL_STRATEGY_ADDRESSES
254
+ }),
255
+ ...ALL_STRATEGY_ADDRESSES.map(
256
+ (strategy) => client.getStrategyKeyScores({
257
+ strategy,
258
+ scoreKeys: [scoreKey]
259
+ })
260
+ )
261
+ ]);
262
+ const total = totalCounts[0] ?? 0;
263
+ const strategyCounts = ALL_STRATEGY_ADDRESSES.map((addr, i) => ({
264
+ strategy: getStrategyName(addr),
265
+ address: addr,
266
+ count: perStrategyCounts[i]?.[0] ?? 0
267
+ }));
268
+ if (options.json) {
269
+ console.log(
270
+ JSON.stringify(
271
+ {
272
+ tokenAddress,
273
+ scoreKey,
274
+ total,
275
+ strategies: strategyCounts.map((s) => ({
276
+ name: s.strategy,
277
+ address: s.address,
278
+ count: s.count
279
+ }))
280
+ },
281
+ null,
282
+ 2
283
+ )
284
+ );
285
+ } else {
286
+ console.log(chalk3.white(`Upvotes for ${tokenAddress}:`));
287
+ console.log(chalk3.cyan(` Total: ${total}`));
288
+ console.log();
289
+ for (const s of strategyCounts) {
290
+ if (s.count > 0) {
291
+ console.log(chalk3.white(` ${s.strategy}: ${s.count}`));
292
+ }
293
+ }
294
+ if (total === 0) {
295
+ console.log(chalk3.yellow(" No upvotes found"));
296
+ }
297
+ }
298
+ } catch (error) {
299
+ exitWithError(
300
+ `Failed to fetch upvotes: ${error instanceof Error ? error.message : String(error)}`
301
+ );
302
+ }
303
+ }
304
+ function registerGetUpvotesCommand(parent, commandName = "info") {
305
+ parent.command(commandName).description("Get upvote counts for a token").requiredOption("--token-address <address>", "Token contract address").option(
306
+ "--chain-id <id>",
307
+ "Chain ID (default: 8453 for Base)",
308
+ (value) => parseInt(value, 10)
309
+ ).option("--rpc-url <url>", "Custom RPC URL").option("--json", "Output in JSON format").action(async (options) => {
310
+ await executeGetUpvotes(options);
311
+ });
312
+ }
313
+
314
+ // src/commands/upvote/index.ts
315
+ function registerUpvoteCommand(program) {
316
+ const upvoteCommand = program.command("upvote").description("Upvote tokens on Net Protocol");
317
+ registerUpvoteTokenCommand(upvoteCommand);
318
+ registerGetUpvotesCommand(upvoteCommand);
319
+ }
320
+
321
+ export { registerGetUpvotesCommand, registerUpvoteCommand, registerUpvoteTokenCommand };
322
+ //# sourceMappingURL=index.mjs.map
323
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +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/commands/upvote/get-upvotes.ts","../../src/commands/upvote/index.ts"],"names":["chalk","getChainRpcUrls","http","PURE_ALPHA_STRATEGY","UNIV234_POOLS_STRATEGY","DYNAMIC_SPLIT_STRATEGY","getTokenScoreKey"],"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 } from \"viem\";\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 return {\n to: config.to,\n data: calldata,\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 });\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","import chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { parseReadOnlyOptionsWithDefault } from \"../../cli/shared\";\nimport { exitWithError } from \"../../shared/output\";\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 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 { Command } from \"commander\";\nimport { registerUpvoteTokenCommand } from \"./upvote-token\";\nimport { registerGetUpvotesCommand } from \"./get-upvotes\";\n\nexport function registerUpvoteCommand(program: Command): void {\n const upvoteCommand = program\n .command(\"upvote\")\n .description(\"Upvote tokens on Net Protocol\");\n\n registerUpvoteTokenCommand(upvoteCommand);\n registerGetUpvotesCommand(upvoteCommand);\n}\n\n// Re-exports for botchan\nexport { registerUpvoteTokenCommand } from \"./upvote-token\";\nexport { registerGetUpvotesCommand } from \"./get-upvotes\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@net-protocol/cli",
3
- "version": "0.1.29",
3
+ "version": "0.1.31",
4
4
  "description": "CLI tool for Net Protocol",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,6 +18,10 @@
18
18
  "./profile": {
19
19
  "types": "./dist/profile/index.d.ts",
20
20
  "import": "./dist/profile/index.mjs"
21
+ },
22
+ "./upvote": {
23
+ "types": "./dist/upvote/index.d.ts",
24
+ "import": "./dist/upvote/index.mjs"
21
25
  }
22
26
  },
23
27
  "files": [
@@ -41,11 +45,12 @@
41
45
  },
42
46
  "dependencies": {
43
47
  "@net-protocol/bazaar": "^0.1.12",
44
- "@net-protocol/core": "^0.1.8",
48
+ "@net-protocol/core": "^0.1.9",
45
49
  "@net-protocol/feeds": "^0.1.14",
46
50
  "@net-protocol/netr": "^0.1.3",
47
51
  "@net-protocol/profiles": "^0.1.5",
48
52
  "@net-protocol/relay": "^0.1.3",
53
+ "@net-protocol/score": "^0.1.2",
49
54
  "@net-protocol/storage": "^0.1.12",
50
55
  "@x402/evm": "^2.1.0",
51
56
  "@x402/fetch": "^2.1.0",
@@ -53,7 +58,7 @@
53
58
  "commander": "^12.1.0",
54
59
  "dotenv": "^17.2.3",
55
60
  "undici": "^7.20.0",
56
- "viem": "^2.31.4"
61
+ "viem": "^2.45.0"
57
62
  },
58
63
  "devDependencies": {
59
64
  "@types/node": "^20.17.6",