@net-protocol/cli 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -363,6 +363,7 @@ Profile operations for managing your Net Protocol profile.
363
363
  - `profile get` - Get profile data for an address
364
364
  - `profile set-picture` - Set your profile picture URL
365
365
  - `profile set-x-username` - Set your X (Twitter) username
366
+ - `profile set-bio` - Set your profile bio
366
367
 
367
368
  ##### Profile Get
368
369
 
@@ -476,6 +477,42 @@ netp profile set-x-username \
476
477
  --encode-only
477
478
  ```
478
479
 
480
+ ##### Profile Set Bio
481
+
482
+ Set your profile bio (max 280 characters).
483
+
484
+ ```bash
485
+ netp profile set-bio \
486
+ --bio <bio-text> \
487
+ [--private-key <0x...>] \
488
+ [--chain-id <8453|1|...>] \
489
+ [--rpc-url <custom-rpc>] \
490
+ [--encode-only]
491
+ ```
492
+
493
+ **Profile Set Bio Arguments:**
494
+
495
+ - `--bio` (required): Your profile bio (max 280 characters, no control characters)
496
+ - `--private-key` (optional): Private key. Can also be set via `NET_PRIVATE_KEY` environment variable
497
+ - `--chain-id` (optional): Chain ID. Can also be set via `NET_CHAIN_ID` environment variable
498
+ - `--rpc-url` (optional): Custom RPC URL. Can also be set via `NET_RPC_URL` environment variable
499
+ - `--encode-only` (optional): Output transaction data as JSON instead of executing
500
+
501
+ **Example:**
502
+
503
+ ```bash
504
+ # Set bio
505
+ netp profile set-bio \
506
+ --bio "Building cool stuff on Net Protocol" \
507
+ --chain-id 8453
508
+
509
+ # Encode-only
510
+ netp profile set-bio \
511
+ --bio "Building cool stuff on Net Protocol" \
512
+ --chain-id 8453 \
513
+ --encode-only
514
+ ```
515
+
479
516
  #### Info Command
480
517
 
481
518
  Show contract info and stats.
@@ -11,7 +11,7 @@ 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';
14
+ import { PROFILE_PICTURE_STORAGE_KEY, PROFILE_METADATA_STORAGE_KEY, parseProfileMetadata, isValidUrl, getProfilePictureStorageArgs, STORAGE_CONTRACT, isValidXUsername, getXUsernameStorageArgs, isValidBio, getProfileMetadataStorageArgs } from '@net-protocol/profiles';
15
15
  import { base } from 'viem/chains';
16
16
 
17
17
  function getRequiredChainId(optionValue) {
@@ -29,12 +29,13 @@ function getRequiredChainId(optionValue) {
29
29
  function getRpcUrl(optionValue) {
30
30
  return optionValue || process.env.NET_RPC_URL;
31
31
  }
32
- function parseCommonOptions(options) {
32
+ function parseCommonOptions(options, supportsEncodeOnly = false) {
33
33
  const privateKey = options.privateKey || process.env.NET_PRIVATE_KEY || process.env.PRIVATE_KEY;
34
34
  if (!privateKey) {
35
+ const encodeOnlyHint = supportsEncodeOnly ? ", or use --encode-only to output transaction data without submitting" : "";
35
36
  console.error(
36
37
  chalk4.red(
37
- "Error: Private key is required. Provide via --private-key flag or NET_PRIVATE_KEY/PRIVATE_KEY environment variable"
38
+ `Error: Private key is required. Provide via --private-key flag or NET_PRIVATE_KEY/PRIVATE_KEY environment variable${encodeOnlyHint}`
38
39
  )
39
40
  );
40
41
  process.exit(1);
@@ -1271,11 +1272,15 @@ function registerStorageCommand(program2) {
1271
1272
  }
1272
1273
  return;
1273
1274
  }
1274
- const commonOptions = parseCommonOptions({
1275
- privateKey: options.privateKey,
1276
- chainId: options.chainId,
1277
- rpcUrl: options.rpcUrl
1278
- });
1275
+ const commonOptions = parseCommonOptions(
1276
+ {
1277
+ privateKey: options.privateKey,
1278
+ chainId: options.chainId,
1279
+ rpcUrl: options.rpcUrl
1280
+ },
1281
+ true
1282
+ // supports --encode-only
1283
+ );
1279
1284
  const uploadOptions = {
1280
1285
  filePath: options.file,
1281
1286
  storageKey: options.key,
@@ -1470,8 +1475,8 @@ function registerStorageCommand(program2) {
1470
1475
  console.log(chalk4.blue(`\u{1F4C1} Reading file: ${options.file}`));
1471
1476
  console.log(chalk4.blue(`\u{1F517} Using relay API: ${options.apiUrl}`));
1472
1477
  const result = await uploadFileWithRelay(uploadRelayOptions);
1473
- const { privateKeyToAccount: privateKeyToAccount8 } = await import('viem/accounts');
1474
- const userAccount = privateKeyToAccount8(commonOptions.privateKey);
1478
+ const { privateKeyToAccount: privateKeyToAccount9 } = await import('viem/accounts');
1479
+ const userAccount = privateKeyToAccount9(commonOptions.privateKey);
1475
1480
  const storageUrl = generateStorageUrl(
1476
1481
  userAccount.address,
1477
1482
  commonOptions.chainId,
@@ -1561,11 +1566,15 @@ async function executeSend(options) {
1561
1566
  executeEncodeOnly(options);
1562
1567
  return;
1563
1568
  }
1564
- const commonOptions = parseCommonOptions({
1565
- privateKey: options.privateKey,
1566
- chainId: options.chainId,
1567
- rpcUrl: options.rpcUrl
1568
- });
1569
+ const commonOptions = parseCommonOptions(
1570
+ {
1571
+ privateKey: options.privateKey,
1572
+ chainId: options.chainId,
1573
+ rpcUrl: options.rpcUrl
1574
+ },
1575
+ true
1576
+ // supports --encode-only
1577
+ );
1569
1578
  const client = createNetClient(commonOptions);
1570
1579
  const txConfig = prepareMessageConfig(client, options);
1571
1580
  const account = privateKeyToAccount(commonOptions.privateKey);
@@ -1975,11 +1984,15 @@ async function executeTokenDeploy(options) {
1975
1984
  await executeEncodeOnly2(options);
1976
1985
  return;
1977
1986
  }
1978
- const commonOptions = parseCommonOptions({
1979
- privateKey: options.privateKey,
1980
- chainId: options.chainId,
1981
- rpcUrl: options.rpcUrl
1982
- });
1987
+ const commonOptions = parseCommonOptions(
1988
+ {
1989
+ privateKey: options.privateKey,
1990
+ chainId: options.chainId,
1991
+ rpcUrl: options.rpcUrl
1992
+ },
1993
+ true
1994
+ // supports --encode-only
1995
+ );
1983
1996
  if (!isNetrSupportedChain(commonOptions.chainId)) {
1984
1997
  exitWithError(
1985
1998
  `Chain ${commonOptions.chainId} is not supported for token deployment. Supported: Base (8453), Plasma (9745), Monad (143), HyperEVM (999)`
@@ -2244,6 +2257,7 @@ async function executeProfileGet(options) {
2244
2257
  }
2245
2258
  }
2246
2259
  let xUsername;
2260
+ let bio;
2247
2261
  try {
2248
2262
  const metadataResult = await client.readStorageData({
2249
2263
  key: PROFILE_METADATA_STORAGE_KEY,
@@ -2252,6 +2266,7 @@ async function executeProfileGet(options) {
2252
2266
  if (metadataResult.data) {
2253
2267
  const metadata = parseProfileMetadata(metadataResult.data);
2254
2268
  xUsername = metadata?.x_username;
2269
+ bio = metadata?.bio;
2255
2270
  }
2256
2271
  } catch (error) {
2257
2272
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -2259,13 +2274,14 @@ async function executeProfileGet(options) {
2259
2274
  throw error;
2260
2275
  }
2261
2276
  }
2262
- const hasProfile = profilePicture || xUsername;
2277
+ const hasProfile = profilePicture || xUsername || bio;
2263
2278
  if (options.json) {
2264
2279
  const output = {
2265
2280
  address: options.address,
2266
2281
  chainId: readOnlyOptions.chainId,
2267
2282
  profilePicture: profilePicture || null,
2268
2283
  xUsername: xUsername || null,
2284
+ bio: bio || null,
2269
2285
  hasProfile
2270
2286
  };
2271
2287
  console.log(JSON.stringify(output, null, 2));
@@ -2280,6 +2296,9 @@ async function executeProfileGet(options) {
2280
2296
  console.log(
2281
2297
  ` ${chalk4.cyan("X Username:")} ${xUsername ? `@${xUsername}` : chalk4.gray("(not set)")}`
2282
2298
  );
2299
+ console.log(
2300
+ ` ${chalk4.cyan("Bio:")} ${bio || chalk4.gray("(not set)")}`
2301
+ );
2283
2302
  if (!hasProfile) {
2284
2303
  console.log(chalk4.yellow("\n No profile data found for this address."));
2285
2304
  }
@@ -2314,11 +2333,15 @@ async function executeProfileSetPicture(options) {
2314
2333
  console.log(JSON.stringify(encoded, null, 2));
2315
2334
  return;
2316
2335
  }
2317
- const commonOptions = parseCommonOptions({
2318
- privateKey: options.privateKey,
2319
- chainId: options.chainId,
2320
- rpcUrl: options.rpcUrl
2321
- });
2336
+ const commonOptions = parseCommonOptions(
2337
+ {
2338
+ privateKey: options.privateKey,
2339
+ chainId: options.chainId,
2340
+ rpcUrl: options.rpcUrl
2341
+ },
2342
+ true
2343
+ // supports --encode-only
2344
+ );
2322
2345
  try {
2323
2346
  const account = privateKeyToAccount(commonOptions.privateKey);
2324
2347
  const rpcUrls = getChainRpcUrls({
@@ -2385,11 +2408,15 @@ async function executeProfileSetUsername(options) {
2385
2408
  console.log(JSON.stringify(encoded, null, 2));
2386
2409
  return;
2387
2410
  }
2388
- const commonOptions = parseCommonOptions({
2389
- privateKey: options.privateKey,
2390
- chainId: options.chainId,
2391
- rpcUrl: options.rpcUrl
2392
- });
2411
+ const commonOptions = parseCommonOptions(
2412
+ {
2413
+ privateKey: options.privateKey,
2414
+ chainId: options.chainId,
2415
+ rpcUrl: options.rpcUrl
2416
+ },
2417
+ true
2418
+ // supports --encode-only
2419
+ );
2393
2420
  try {
2394
2421
  const account = privateKeyToAccount(commonOptions.privateKey);
2395
2422
  const rpcUrls = getChainRpcUrls({
@@ -2431,6 +2458,80 @@ async function executeProfileSetUsername(options) {
2431
2458
  );
2432
2459
  }
2433
2460
  }
2461
+ async function executeProfileSetBio(options) {
2462
+ if (!isValidBio(options.bio)) {
2463
+ exitWithError(
2464
+ `Invalid bio: "${options.bio}". Bio must be 1-280 characters and cannot contain control characters.`
2465
+ );
2466
+ }
2467
+ const storageArgs = getProfileMetadataStorageArgs({ bio: options.bio });
2468
+ if (options.encodeOnly) {
2469
+ const readOnlyOptions = parseReadOnlyOptions({
2470
+ chainId: options.chainId,
2471
+ rpcUrl: options.rpcUrl
2472
+ });
2473
+ const encoded = encodeTransaction(
2474
+ {
2475
+ to: STORAGE_CONTRACT.address,
2476
+ abi: STORAGE_CONTRACT.abi,
2477
+ functionName: "put",
2478
+ args: [storageArgs.bytesKey, storageArgs.topic, storageArgs.bytesValue]
2479
+ },
2480
+ readOnlyOptions.chainId
2481
+ );
2482
+ console.log(JSON.stringify(encoded, null, 2));
2483
+ return;
2484
+ }
2485
+ const commonOptions = parseCommonOptions(
2486
+ {
2487
+ privateKey: options.privateKey,
2488
+ chainId: options.chainId,
2489
+ rpcUrl: options.rpcUrl
2490
+ },
2491
+ true
2492
+ // supports --encode-only
2493
+ );
2494
+ try {
2495
+ const account = privateKeyToAccount(commonOptions.privateKey);
2496
+ const rpcUrls = getChainRpcUrls({
2497
+ chainId: commonOptions.chainId,
2498
+ rpcUrl: commonOptions.rpcUrl
2499
+ });
2500
+ const client = createWalletClient({
2501
+ account,
2502
+ chain: base,
2503
+ // TODO: Support other chains
2504
+ transport: http(rpcUrls[0])
2505
+ }).extend(publicActions);
2506
+ console.log(chalk4.blue(`Setting profile bio...`));
2507
+ console.log(chalk4.gray(` Bio: ${options.bio}`));
2508
+ console.log(chalk4.gray(` Address: ${account.address}`));
2509
+ const hash = await client.writeContract({
2510
+ address: STORAGE_CONTRACT.address,
2511
+ abi: STORAGE_CONTRACT.abi,
2512
+ functionName: "put",
2513
+ args: [storageArgs.bytesKey, storageArgs.topic, storageArgs.bytesValue]
2514
+ });
2515
+ console.log(chalk4.blue(`Waiting for confirmation...`));
2516
+ const receipt = await client.waitForTransactionReceipt({ hash });
2517
+ if (receipt.status === "success") {
2518
+ console.log(
2519
+ chalk4.green(
2520
+ `
2521
+ Bio updated successfully!
2522
+ Transaction: ${hash}
2523
+ Bio: ${options.bio}`
2524
+ )
2525
+ );
2526
+ } else {
2527
+ exitWithError(`Transaction failed: ${hash}`);
2528
+ }
2529
+ } catch (error) {
2530
+ exitWithError(
2531
+ `Failed to set bio: ${error instanceof Error ? error.message : String(error)}`
2532
+ );
2533
+ }
2534
+ }
2434
2535
 
2435
2536
  // src/commands/profile/index.ts
2436
2537
  function registerProfileCommand(program2) {
@@ -2497,9 +2598,32 @@ function registerProfileCommand(program2) {
2497
2598
  encodeOnly: options.encodeOnly
2498
2599
  });
2499
2600
  });
2601
+ const setBioCommand = new Command("set-bio").description("Set your profile bio").requiredOption("--bio <bio>", "Your profile bio (max 280 characters)").option(
2602
+ "--private-key <key>",
2603
+ "Private key (0x-prefixed hex, 66 characters). Can also be set via NET_PRIVATE_KEY env var"
2604
+ ).option(
2605
+ "--chain-id <id>",
2606
+ "Chain ID. Can also be set via NET_CHAIN_ID env var",
2607
+ (value) => parseInt(value, 10)
2608
+ ).option(
2609
+ "--rpc-url <url>",
2610
+ "Custom RPC URL. Can also be set via NET_RPC_URL env var"
2611
+ ).option(
2612
+ "--encode-only",
2613
+ "Output transaction data as JSON instead of executing"
2614
+ ).action(async (options) => {
2615
+ await executeProfileSetBio({
2616
+ bio: options.bio,
2617
+ privateKey: options.privateKey,
2618
+ chainId: options.chainId,
2619
+ rpcUrl: options.rpcUrl,
2620
+ encodeOnly: options.encodeOnly
2621
+ });
2622
+ });
2500
2623
  profileCommand.addCommand(getCommand);
2501
2624
  profileCommand.addCommand(setPictureCommand);
2502
2625
  profileCommand.addCommand(setUsernameCommand);
2626
+ profileCommand.addCommand(setBioCommand);
2503
2627
  }
2504
2628
 
2505
2629
  // src/cli/index.ts