@net-protocol/cli 0.1.21 → 0.1.23

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.
@@ -1,7 +1,7 @@
1
1
  import { Command } from 'commander';
2
2
  import chalk3 from 'chalk';
3
3
  import { StorageClient, chunkDataForStorage, CHUNKED_STORAGE_CONTRACT } from '@net-protocol/storage';
4
- import { PROFILE_PICTURE_STORAGE_KEY, PROFILE_METADATA_STORAGE_KEY, parseProfileMetadata, PROFILE_CANVAS_STORAGE_KEY, isValidUrl, getProfilePictureStorageArgs, STORAGE_CONTRACT, isValidXUsername, getProfileMetadataStorageArgs, isValidBio, isValidTokenAddress } from '@net-protocol/profiles';
4
+ import { PROFILE_PICTURE_STORAGE_KEY, PROFILE_METADATA_STORAGE_KEY, parseProfileMetadata, PROFILE_CANVAS_STORAGE_KEY, isValidUrl, getProfilePictureStorageArgs, STORAGE_CONTRACT, isValidXUsername, getProfileMetadataStorageArgs, isValidBio, isValidDisplayName, isValidTokenAddress } from '@net-protocol/profiles';
5
5
  import { createWalletClient, http, publicActions, encodeFunctionData } from 'viem';
6
6
  import { privateKeyToAccount } from 'viem/accounts';
7
7
  import { base } from 'viem/chains';
@@ -301,8 +301,19 @@ async function executeProfileSetUsername(options) {
301
301
  chainId: options.chainId,
302
302
  rpcUrl: options.rpcUrl
303
303
  });
304
+ let existing = {};
305
+ if (options.address) {
306
+ const storageClient = new StorageClient({
307
+ chainId: readOnlyOptions.chainId,
308
+ overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : void 0
309
+ });
310
+ existing = await readExistingMetadata(options.address, storageClient);
311
+ }
304
312
  const storageArgs = getProfileMetadataStorageArgs({
305
- x_username: usernameForStorage
313
+ x_username: usernameForStorage,
314
+ bio: existing.bio,
315
+ display_name: existing.display_name,
316
+ token_address: existing.token_address
306
317
  });
307
318
  const encoded = encodeTransaction(
308
319
  {
@@ -391,7 +402,20 @@ async function executeProfileSetBio(options) {
391
402
  chainId: options.chainId,
392
403
  rpcUrl: options.rpcUrl
393
404
  });
394
- const storageArgs = getProfileMetadataStorageArgs({ bio: options.bio });
405
+ let existing = {};
406
+ if (options.address) {
407
+ const storageClient = new StorageClient({
408
+ chainId: readOnlyOptions.chainId,
409
+ overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : void 0
410
+ });
411
+ existing = await readExistingMetadata(options.address, storageClient);
412
+ }
413
+ const storageArgs = getProfileMetadataStorageArgs({
414
+ bio: options.bio,
415
+ x_username: existing.x_username,
416
+ display_name: existing.display_name,
417
+ token_address: existing.token_address
418
+ });
395
419
  const encoded = encodeTransaction(
396
420
  {
397
421
  to: STORAGE_CONTRACT.address,
@@ -468,6 +492,107 @@ Bio updated successfully!
468
492
  );
469
493
  }
470
494
  }
495
+ async function executeProfileSetDisplayName(options) {
496
+ if (!isValidDisplayName(options.name)) {
497
+ exitWithError(
498
+ `Invalid display name: "${options.name}". Display name must be 1-25 characters and cannot contain control characters.`
499
+ );
500
+ }
501
+ if (options.encodeOnly) {
502
+ const readOnlyOptions = parseReadOnlyOptions({
503
+ chainId: options.chainId,
504
+ rpcUrl: options.rpcUrl
505
+ });
506
+ let existing = {};
507
+ if (options.address) {
508
+ const storageClient = new StorageClient({
509
+ chainId: readOnlyOptions.chainId,
510
+ overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : void 0
511
+ });
512
+ existing = await readExistingMetadata(options.address, storageClient);
513
+ }
514
+ const storageArgs = getProfileMetadataStorageArgs({
515
+ display_name: options.name,
516
+ bio: existing.bio,
517
+ x_username: existing.x_username,
518
+ token_address: existing.token_address
519
+ });
520
+ const encoded = encodeTransaction(
521
+ {
522
+ to: STORAGE_CONTRACT.address,
523
+ abi: STORAGE_CONTRACT.abi,
524
+ functionName: "put",
525
+ args: [storageArgs.bytesKey, storageArgs.topic, storageArgs.bytesValue]
526
+ },
527
+ readOnlyOptions.chainId
528
+ );
529
+ console.log(JSON.stringify(encoded, null, 2));
530
+ return;
531
+ }
532
+ const commonOptions = parseCommonOptions(
533
+ {
534
+ privateKey: options.privateKey,
535
+ chainId: options.chainId,
536
+ rpcUrl: options.rpcUrl
537
+ },
538
+ true
539
+ // supports --encode-only
540
+ );
541
+ try {
542
+ const account = privateKeyToAccount(commonOptions.privateKey);
543
+ const rpcUrls = getChainRpcUrls({
544
+ chainId: commonOptions.chainId,
545
+ rpcUrl: commonOptions.rpcUrl
546
+ });
547
+ const client = createWalletClient({
548
+ account,
549
+ chain: base,
550
+ // TODO: Support other chains
551
+ transport: http(rpcUrls[0])
552
+ }).extend(publicActions);
553
+ console.log(chalk3.blue(`Setting display name...`));
554
+ console.log(chalk3.gray(` Name: ${options.name}`));
555
+ console.log(chalk3.gray(` Address: ${account.address}`));
556
+ const storageClient = new StorageClient({
557
+ chainId: commonOptions.chainId,
558
+ overrides: commonOptions.rpcUrl ? { rpcUrls: [commonOptions.rpcUrl] } : void 0
559
+ });
560
+ const existing = await readExistingMetadata(
561
+ account.address,
562
+ storageClient
563
+ );
564
+ const storageArgs = getProfileMetadataStorageArgs({
565
+ display_name: options.name,
566
+ bio: existing.bio,
567
+ x_username: existing.x_username,
568
+ token_address: existing.token_address
569
+ });
570
+ const hash = await client.writeContract({
571
+ address: STORAGE_CONTRACT.address,
572
+ abi: STORAGE_CONTRACT.abi,
573
+ functionName: "put",
574
+ args: [storageArgs.bytesKey, storageArgs.topic, storageArgs.bytesValue]
575
+ });
576
+ console.log(chalk3.blue(`Waiting for confirmation...`));
577
+ const receipt = await client.waitForTransactionReceipt({ hash });
578
+ if (receipt.status === "success") {
579
+ console.log(
580
+ chalk3.green(
581
+ `
582
+ Display name updated successfully!
583
+ Transaction: ${hash}
584
+ Name: ${options.name}`
585
+ )
586
+ );
587
+ } else {
588
+ exitWithError(`Transaction failed: ${hash}`);
589
+ }
590
+ } catch (error) {
591
+ exitWithError(
592
+ `Failed to set display name: ${error instanceof Error ? error.message : String(error)}`
593
+ );
594
+ }
595
+ }
471
596
  async function executeProfileSetTokenAddress(options) {
472
597
  if (!isValidTokenAddress(options.tokenAddress)) {
473
598
  exitWithError(
@@ -480,8 +605,19 @@ async function executeProfileSetTokenAddress(options) {
480
605
  chainId: options.chainId,
481
606
  rpcUrl: options.rpcUrl
482
607
  });
608
+ let existing = {};
609
+ if (options.address) {
610
+ const storageClient = new StorageClient({
611
+ chainId: readOnlyOptions.chainId,
612
+ overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : void 0
613
+ });
614
+ existing = await readExistingMetadata(options.address, storageClient);
615
+ }
483
616
  const storageArgs = getProfileMetadataStorageArgs({
484
- token_address: normalizedAddress
617
+ token_address: normalizedAddress,
618
+ x_username: existing.x_username,
619
+ bio: existing.bio,
620
+ display_name: existing.display_name
485
621
  });
486
622
  const encoded = encodeTransaction(
487
623
  {
@@ -861,13 +997,17 @@ function registerProfileCommand(program) {
861
997
  ).option(
862
998
  "--encode-only",
863
999
  "Output transaction data as JSON instead of executing"
1000
+ ).option(
1001
+ "--address <address>",
1002
+ "Wallet address to preserve existing metadata for (used with --encode-only)"
864
1003
  ).action(async (options) => {
865
1004
  await executeProfileSetUsername({
866
1005
  username: options.username,
867
1006
  privateKey: options.privateKey,
868
1007
  chainId: options.chainId,
869
1008
  rpcUrl: options.rpcUrl,
870
- encodeOnly: options.encodeOnly
1009
+ encodeOnly: options.encodeOnly,
1010
+ address: options.address
871
1011
  });
872
1012
  });
873
1013
  const setBioCommand = new Command("set-bio").description("Set your profile bio").requiredOption("--bio <bio>", "Your profile bio (max 280 characters)").option(
@@ -883,13 +1023,43 @@ function registerProfileCommand(program) {
883
1023
  ).option(
884
1024
  "--encode-only",
885
1025
  "Output transaction data as JSON instead of executing"
1026
+ ).option(
1027
+ "--address <address>",
1028
+ "Wallet address to preserve existing metadata for (used with --encode-only)"
886
1029
  ).action(async (options) => {
887
1030
  await executeProfileSetBio({
888
1031
  bio: options.bio,
889
1032
  privateKey: options.privateKey,
890
1033
  chainId: options.chainId,
891
1034
  rpcUrl: options.rpcUrl,
892
- encodeOnly: options.encodeOnly
1035
+ encodeOnly: options.encodeOnly,
1036
+ address: options.address
1037
+ });
1038
+ });
1039
+ const setDisplayNameCommand = new Command("set-display-name").description("Set your profile display name").requiredOption("--name <name>", "Your display name (max 25 characters)").option(
1040
+ "--private-key <key>",
1041
+ "Private key (0x-prefixed hex, 66 characters). Can also be set via NET_PRIVATE_KEY env var"
1042
+ ).option(
1043
+ "--chain-id <id>",
1044
+ "Chain ID. Can also be set via NET_CHAIN_ID env var",
1045
+ (value) => parseInt(value, 10)
1046
+ ).option(
1047
+ "--rpc-url <url>",
1048
+ "Custom RPC URL. Can also be set via NET_RPC_URL env var"
1049
+ ).option(
1050
+ "--encode-only",
1051
+ "Output transaction data as JSON instead of executing"
1052
+ ).option(
1053
+ "--address <address>",
1054
+ "Wallet address to preserve existing metadata for (used with --encode-only)"
1055
+ ).action(async (options) => {
1056
+ await executeProfileSetDisplayName({
1057
+ name: options.name,
1058
+ privateKey: options.privateKey,
1059
+ chainId: options.chainId,
1060
+ rpcUrl: options.rpcUrl,
1061
+ encodeOnly: options.encodeOnly,
1062
+ address: options.address
893
1063
  });
894
1064
  });
895
1065
  const setTokenAddressCommand = new Command("set-token-address").description("Set your profile token address (ERC-20 token that represents you)").requiredOption(
@@ -908,13 +1078,17 @@ function registerProfileCommand(program) {
908
1078
  ).option(
909
1079
  "--encode-only",
910
1080
  "Output transaction data as JSON instead of executing"
1081
+ ).option(
1082
+ "--address <address>",
1083
+ "Wallet address to preserve existing metadata for (used with --encode-only)"
911
1084
  ).action(async (options) => {
912
1085
  await executeProfileSetTokenAddress({
913
1086
  tokenAddress: options.tokenAddress,
914
1087
  privateKey: options.privateKey,
915
1088
  chainId: options.chainId,
916
1089
  rpcUrl: options.rpcUrl,
917
- encodeOnly: options.encodeOnly
1090
+ encodeOnly: options.encodeOnly,
1091
+ address: options.address
918
1092
  });
919
1093
  });
920
1094
  const setCanvasCommand = new Command("set-canvas").description("Set your profile canvas (HTML content)").option("--file <path>", "Path to file containing canvas content").option("--content <html>", "HTML content for canvas (inline)").option(
@@ -960,6 +1134,7 @@ function registerProfileCommand(program) {
960
1134
  profileCommand.addCommand(setPictureCommand);
961
1135
  profileCommand.addCommand(setUsernameCommand);
962
1136
  profileCommand.addCommand(setBioCommand);
1137
+ profileCommand.addCommand(setDisplayNameCommand);
963
1138
  profileCommand.addCommand(setTokenAddressCommand);
964
1139
  profileCommand.addCommand(setCanvasCommand);
965
1140
  profileCommand.addCommand(getCanvasCommand);