@net-protocol/cli 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @net-protocol/cli
2
2
 
3
- A command-line tool for interacting with Net Protocol. Supports storage uploads and more.
3
+ Command-line tool for Net Protocol send messages, store data onchain, and deploy tokens across Base and other EVM chains.
4
4
 
5
5
  ## Installation
6
6
 
@@ -5,12 +5,14 @@ import { createRequire } from 'module';
5
5
  import chalk4 from 'chalk';
6
6
  import * as fs from 'fs';
7
7
  import { readFileSync } from 'fs';
8
- import { StorageClient, detectFileTypeFromBase64, base64ToDataUri, shouldSuggestXmlStorage, getStorageKeyBytes, encodeStorageKeyForUrl, STORAGE_CONTRACT, CHUNKED_STORAGE_CONTRACT } from '@net-protocol/storage';
9
- import { stringToHex, createWalletClient, http, hexToString, parseEther, encodeFunctionData, defineChain } from 'viem';
8
+ import { StorageClient, detectFileTypeFromBase64, base64ToDataUri, shouldSuggestXmlStorage, getStorageKeyBytes, encodeStorageKeyForUrl, STORAGE_CONTRACT as STORAGE_CONTRACT$1, CHUNKED_STORAGE_CONTRACT } from '@net-protocol/storage';
9
+ import { stringToHex, createWalletClient, http, hexToString, parseEther, encodeFunctionData, publicActions, defineChain } from 'viem';
10
10
  import { privateKeyToAccount } from 'viem/accounts';
11
11
  import { getNetContract, getChainName, getPublicClient, getChainRpcUrls, NetClient } from '@net-protocol/core';
12
12
  import { createRelayX402Client, createRelaySession, checkBackendWalletBalance, fundBackendWallet, batchTransactions, submitTransactionsViaRelay, waitForConfirmations, retryFailedTransactions as retryFailedTransactions$1 } from '@net-protocol/relay';
13
13
  import { isNetrSupportedChain, NetrClient } from '@net-protocol/netr';
14
+ import { PROFILE_PICTURE_STORAGE_KEY, PROFILE_METADATA_STORAGE_KEY, parseProfileMetadata, isValidUrl, getProfilePictureStorageArgs, STORAGE_CONTRACT, isValidXUsername, getXUsernameStorageArgs } from '@net-protocol/profiles';
15
+ import { base } from 'viem/chains';
14
16
 
15
17
  function getRequiredChainId(optionValue) {
16
18
  const chainId = optionValue || (process.env.NET_CHAIN_ID ? parseInt(process.env.NET_CHAIN_ID, 10) : void 0);
@@ -297,7 +299,7 @@ async function filterExistingTransactions(params) {
297
299
  async function filterXmlStorageTransactions(params) {
298
300
  const { storageClient, transactions, operatorAddress } = params;
299
301
  const metadataTx = transactions.find(
300
- (tx) => tx.to.toLowerCase() === STORAGE_CONTRACT.address.toLowerCase()
302
+ (tx) => tx.to.toLowerCase() === STORAGE_CONTRACT$1.address.toLowerCase()
301
303
  );
302
304
  const chunkTxs = transactions.filter(
303
305
  (tx) => tx.to.toLowerCase() === CHUNKED_STORAGE_CONTRACT.address.toLowerCase()
@@ -1468,8 +1470,8 @@ function registerStorageCommand(program2) {
1468
1470
  console.log(chalk4.blue(`\u{1F4C1} Reading file: ${options.file}`));
1469
1471
  console.log(chalk4.blue(`\u{1F517} Using relay API: ${options.apiUrl}`));
1470
1472
  const result = await uploadFileWithRelay(uploadRelayOptions);
1471
- const { privateKeyToAccount: privateKeyToAccount6 } = await import('viem/accounts');
1472
- const userAccount = privateKeyToAccount6(commonOptions.privateKey);
1473
+ const { privateKeyToAccount: privateKeyToAccount8 } = await import('viem/accounts');
1474
+ const userAccount = privateKeyToAccount8(commonOptions.privateKey);
1473
1475
  const storageUrl = generateStorageUrl(
1474
1476
  userAccount.address,
1475
1477
  commonOptions.chainId,
@@ -2216,6 +2218,289 @@ function registerTokenCommand(program2) {
2216
2218
  tokenCommand.addCommand(deployCommand);
2217
2219
  tokenCommand.addCommand(infoCommand);
2218
2220
  }
2221
+ async function executeProfileGet(options) {
2222
+ const readOnlyOptions = parseReadOnlyOptions({
2223
+ chainId: options.chainId,
2224
+ rpcUrl: options.rpcUrl
2225
+ });
2226
+ const client = new StorageClient({
2227
+ chainId: readOnlyOptions.chainId,
2228
+ overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : void 0
2229
+ });
2230
+ try {
2231
+ let profilePicture;
2232
+ try {
2233
+ const pictureResult = await client.readStorageData({
2234
+ key: PROFILE_PICTURE_STORAGE_KEY,
2235
+ operator: options.address
2236
+ });
2237
+ if (pictureResult.data) {
2238
+ profilePicture = pictureResult.data;
2239
+ }
2240
+ } catch (error) {
2241
+ const errorMessage = error instanceof Error ? error.message : String(error);
2242
+ if (errorMessage !== "StoredDataNotFound") {
2243
+ throw error;
2244
+ }
2245
+ }
2246
+ let xUsername;
2247
+ try {
2248
+ const metadataResult = await client.readStorageData({
2249
+ key: PROFILE_METADATA_STORAGE_KEY,
2250
+ operator: options.address
2251
+ });
2252
+ if (metadataResult.data) {
2253
+ const metadata = parseProfileMetadata(metadataResult.data);
2254
+ xUsername = metadata?.x_username;
2255
+ }
2256
+ } catch (error) {
2257
+ const errorMessage = error instanceof Error ? error.message : String(error);
2258
+ if (errorMessage !== "StoredDataNotFound") {
2259
+ throw error;
2260
+ }
2261
+ }
2262
+ const hasProfile = profilePicture || xUsername;
2263
+ if (options.json) {
2264
+ const output = {
2265
+ address: options.address,
2266
+ chainId: readOnlyOptions.chainId,
2267
+ profilePicture: profilePicture || null,
2268
+ xUsername: xUsername || null,
2269
+ hasProfile
2270
+ };
2271
+ console.log(JSON.stringify(output, null, 2));
2272
+ return;
2273
+ }
2274
+ console.log(chalk4.white.bold("\nProfile:\n"));
2275
+ console.log(` ${chalk4.cyan("Address:")} ${options.address}`);
2276
+ console.log(` ${chalk4.cyan("Chain ID:")} ${readOnlyOptions.chainId}`);
2277
+ console.log(
2278
+ ` ${chalk4.cyan("Profile Picture:")} ${profilePicture || chalk4.gray("(not set)")}`
2279
+ );
2280
+ console.log(
2281
+ ` ${chalk4.cyan("X Username:")} ${xUsername ? `@${xUsername}` : chalk4.gray("(not set)")}`
2282
+ );
2283
+ if (!hasProfile) {
2284
+ console.log(chalk4.yellow("\n No profile data found for this address."));
2285
+ }
2286
+ console.log();
2287
+ } catch (error) {
2288
+ exitWithError(
2289
+ `Failed to read profile: ${error instanceof Error ? error.message : String(error)}`
2290
+ );
2291
+ }
2292
+ }
2293
+ async function executeProfileSetPicture(options) {
2294
+ if (!isValidUrl(options.url)) {
2295
+ exitWithError(
2296
+ `Invalid URL: "${options.url}". Please provide a valid URL (e.g., https://example.com/image.jpg)`
2297
+ );
2298
+ }
2299
+ const storageArgs = getProfilePictureStorageArgs(options.url);
2300
+ if (options.encodeOnly) {
2301
+ const readOnlyOptions = parseReadOnlyOptions({
2302
+ chainId: options.chainId,
2303
+ rpcUrl: options.rpcUrl
2304
+ });
2305
+ const encoded = encodeTransaction(
2306
+ {
2307
+ to: STORAGE_CONTRACT.address,
2308
+ abi: STORAGE_CONTRACT.abi,
2309
+ functionName: "put",
2310
+ args: [storageArgs.bytesKey, storageArgs.topic, storageArgs.bytesValue]
2311
+ },
2312
+ readOnlyOptions.chainId
2313
+ );
2314
+ console.log(JSON.stringify(encoded, null, 2));
2315
+ return;
2316
+ }
2317
+ const commonOptions = parseCommonOptions({
2318
+ privateKey: options.privateKey,
2319
+ chainId: options.chainId,
2320
+ rpcUrl: options.rpcUrl
2321
+ });
2322
+ try {
2323
+ const account = privateKeyToAccount(commonOptions.privateKey);
2324
+ const rpcUrls = getChainRpcUrls({
2325
+ chainId: commonOptions.chainId,
2326
+ rpcUrl: commonOptions.rpcUrl
2327
+ });
2328
+ const client = createWalletClient({
2329
+ account,
2330
+ chain: base,
2331
+ // TODO: Support other chains
2332
+ transport: http(rpcUrls[0])
2333
+ }).extend(publicActions);
2334
+ console.log(chalk4.blue(`\u{1F4F7} Setting profile picture...`));
2335
+ console.log(chalk4.gray(` URL: ${options.url}`));
2336
+ console.log(chalk4.gray(` Address: ${account.address}`));
2337
+ const hash = await client.writeContract({
2338
+ address: STORAGE_CONTRACT.address,
2339
+ abi: STORAGE_CONTRACT.abi,
2340
+ functionName: "put",
2341
+ args: [storageArgs.bytesKey, storageArgs.topic, storageArgs.bytesValue]
2342
+ });
2343
+ console.log(chalk4.blue(`\u23F3 Waiting for confirmation...`));
2344
+ const receipt = await client.waitForTransactionReceipt({ hash });
2345
+ if (receipt.status === "success") {
2346
+ console.log(
2347
+ chalk4.green(
2348
+ `
2349
+ \u2713 Profile picture updated successfully!
2350
+ Transaction: ${hash}
2351
+ URL: ${options.url}`
2352
+ )
2353
+ );
2354
+ } else {
2355
+ exitWithError(`Transaction failed: ${hash}`);
2356
+ }
2357
+ } catch (error) {
2358
+ exitWithError(
2359
+ `Failed to set profile picture: ${error instanceof Error ? error.message : String(error)}`
2360
+ );
2361
+ }
2362
+ }
2363
+ async function executeProfileSetUsername(options) {
2364
+ if (!isValidXUsername(options.username)) {
2365
+ exitWithError(
2366
+ `Invalid X username: "${options.username}". Usernames must be 1-15 characters, alphanumeric and underscores only.`
2367
+ );
2368
+ }
2369
+ const storageArgs = getXUsernameStorageArgs(options.username);
2370
+ const displayUsername = options.username.startsWith("@") ? options.username : `@${options.username}`;
2371
+ if (options.encodeOnly) {
2372
+ const readOnlyOptions = parseReadOnlyOptions({
2373
+ chainId: options.chainId,
2374
+ rpcUrl: options.rpcUrl
2375
+ });
2376
+ const encoded = encodeTransaction(
2377
+ {
2378
+ to: STORAGE_CONTRACT.address,
2379
+ abi: STORAGE_CONTRACT.abi,
2380
+ functionName: "put",
2381
+ args: [storageArgs.bytesKey, storageArgs.topic, storageArgs.bytesValue]
2382
+ },
2383
+ readOnlyOptions.chainId
2384
+ );
2385
+ console.log(JSON.stringify(encoded, null, 2));
2386
+ return;
2387
+ }
2388
+ const commonOptions = parseCommonOptions({
2389
+ privateKey: options.privateKey,
2390
+ chainId: options.chainId,
2391
+ rpcUrl: options.rpcUrl
2392
+ });
2393
+ try {
2394
+ const account = privateKeyToAccount(commonOptions.privateKey);
2395
+ const rpcUrls = getChainRpcUrls({
2396
+ chainId: commonOptions.chainId,
2397
+ rpcUrl: commonOptions.rpcUrl
2398
+ });
2399
+ const client = createWalletClient({
2400
+ account,
2401
+ chain: base,
2402
+ // TODO: Support other chains
2403
+ transport: http(rpcUrls[0])
2404
+ }).extend(publicActions);
2405
+ console.log(chalk4.blue(`\u{1F426} Setting X username...`));
2406
+ console.log(chalk4.gray(` Username: ${displayUsername}`));
2407
+ console.log(chalk4.gray(` Address: ${account.address}`));
2408
+ const hash = await client.writeContract({
2409
+ address: STORAGE_CONTRACT.address,
2410
+ abi: STORAGE_CONTRACT.abi,
2411
+ functionName: "put",
2412
+ args: [storageArgs.bytesKey, storageArgs.topic, storageArgs.bytesValue]
2413
+ });
2414
+ console.log(chalk4.blue(`\u23F3 Waiting for confirmation...`));
2415
+ const receipt = await client.waitForTransactionReceipt({ hash });
2416
+ if (receipt.status === "success") {
2417
+ console.log(
2418
+ chalk4.green(
2419
+ `
2420
+ \u2713 X username updated successfully!
2421
+ Transaction: ${hash}
2422
+ Username: ${displayUsername}`
2423
+ )
2424
+ );
2425
+ } else {
2426
+ exitWithError(`Transaction failed: ${hash}`);
2427
+ }
2428
+ } catch (error) {
2429
+ exitWithError(
2430
+ `Failed to set X username: ${error instanceof Error ? error.message : String(error)}`
2431
+ );
2432
+ }
2433
+ }
2434
+
2435
+ // src/commands/profile/index.ts
2436
+ function registerProfileCommand(program2) {
2437
+ const profileCommand = program2.command("profile").description("User profile operations");
2438
+ const getCommand = new Command("get").description("Get profile data for an address").requiredOption("--address <address>", "Wallet address to get profile for").option(
2439
+ "--chain-id <id>",
2440
+ "Chain ID. Can also be set via NET_CHAIN_ID env var",
2441
+ (value) => parseInt(value, 10)
2442
+ ).option(
2443
+ "--rpc-url <url>",
2444
+ "Custom RPC URL. Can also be set via NET_RPC_URL env var"
2445
+ ).option("--json", "Output in JSON format").action(async (options) => {
2446
+ await executeProfileGet({
2447
+ address: options.address,
2448
+ chainId: options.chainId,
2449
+ rpcUrl: options.rpcUrl,
2450
+ json: options.json
2451
+ });
2452
+ });
2453
+ const setPictureCommand = new Command("set-picture").description("Set your profile picture URL").requiredOption("--url <url>", "Image URL for profile picture").option(
2454
+ "--private-key <key>",
2455
+ "Private key (0x-prefixed hex, 66 characters). Can also be set via NET_PRIVATE_KEY env var"
2456
+ ).option(
2457
+ "--chain-id <id>",
2458
+ "Chain ID. Can also be set via NET_CHAIN_ID env var",
2459
+ (value) => parseInt(value, 10)
2460
+ ).option(
2461
+ "--rpc-url <url>",
2462
+ "Custom RPC URL. Can also be set via NET_RPC_URL env var"
2463
+ ).option(
2464
+ "--encode-only",
2465
+ "Output transaction data as JSON instead of executing"
2466
+ ).action(async (options) => {
2467
+ await executeProfileSetPicture({
2468
+ url: options.url,
2469
+ privateKey: options.privateKey,
2470
+ chainId: options.chainId,
2471
+ rpcUrl: options.rpcUrl,
2472
+ encodeOnly: options.encodeOnly
2473
+ });
2474
+ });
2475
+ const setUsernameCommand = new Command("set-x-username").description("Set your X (Twitter) username for your profile").requiredOption(
2476
+ "--username <username>",
2477
+ "Your X (Twitter) username (with or without @)"
2478
+ ).option(
2479
+ "--private-key <key>",
2480
+ "Private key (0x-prefixed hex, 66 characters). Can also be set via NET_PRIVATE_KEY env var"
2481
+ ).option(
2482
+ "--chain-id <id>",
2483
+ "Chain ID. Can also be set via NET_CHAIN_ID env var",
2484
+ (value) => parseInt(value, 10)
2485
+ ).option(
2486
+ "--rpc-url <url>",
2487
+ "Custom RPC URL. Can also be set via NET_RPC_URL env var"
2488
+ ).option(
2489
+ "--encode-only",
2490
+ "Output transaction data as JSON instead of executing"
2491
+ ).action(async (options) => {
2492
+ await executeProfileSetUsername({
2493
+ username: options.username,
2494
+ privateKey: options.privateKey,
2495
+ chainId: options.chainId,
2496
+ rpcUrl: options.rpcUrl,
2497
+ encodeOnly: options.encodeOnly
2498
+ });
2499
+ });
2500
+ profileCommand.addCommand(getCommand);
2501
+ profileCommand.addCommand(setPictureCommand);
2502
+ profileCommand.addCommand(setUsernameCommand);
2503
+ }
2219
2504
 
2220
2505
  // src/cli/index.ts
2221
2506
  var require2 = createRequire(import.meta.url);
@@ -2227,6 +2512,7 @@ registerMessageCommand(program);
2227
2512
  registerChainsCommand(program);
2228
2513
  registerInfoCommand(program);
2229
2514
  registerTokenCommand(program);
2515
+ registerProfileCommand(program);
2230
2516
  program.parse();
2231
2517
  //# sourceMappingURL=index.mjs.map
2232
2518
  //# sourceMappingURL=index.mjs.map