@beclab/olaresid 0.1.1 → 0.1.2

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.
Files changed (87) hide show
  1. package/CLI.md +1300 -0
  2. package/README.md +37 -31
  3. package/TAG.md +589 -0
  4. package/dist/abi/RootResolver2ABI.d.ts +54 -0
  5. package/dist/abi/RootResolver2ABI.d.ts.map +1 -0
  6. package/dist/abi/RootResolver2ABI.js +240 -0
  7. package/dist/abi/RootResolver2ABI.js.map +1 -0
  8. package/dist/business/index.d.ts +302 -0
  9. package/dist/business/index.d.ts.map +1 -0
  10. package/dist/business/index.js +1209 -0
  11. package/dist/business/index.js.map +1 -0
  12. package/dist/business/tag-context.d.ts +219 -0
  13. package/dist/business/tag-context.d.ts.map +1 -0
  14. package/dist/business/tag-context.js +537 -0
  15. package/dist/business/tag-context.js.map +1 -0
  16. package/dist/cli.js +2085 -39
  17. package/dist/cli.js.map +1 -1
  18. package/dist/debug.d.ts.map +1 -1
  19. package/dist/debug.js +14 -2
  20. package/dist/debug.js.map +1 -1
  21. package/dist/index.d.ts +50 -2
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +229 -9
  24. package/dist/index.js.map +1 -1
  25. package/dist/utils/crypto-utils.d.ts +130 -0
  26. package/dist/utils/crypto-utils.d.ts.map +1 -0
  27. package/dist/utils/crypto-utils.js +402 -0
  28. package/dist/utils/crypto-utils.js.map +1 -0
  29. package/dist/utils/error-parser.d.ts +35 -0
  30. package/dist/utils/error-parser.d.ts.map +1 -0
  31. package/dist/utils/error-parser.js +202 -0
  32. package/dist/utils/error-parser.js.map +1 -0
  33. package/dist/utils/tag-abi-codec.d.ts +69 -0
  34. package/dist/utils/tag-abi-codec.d.ts.map +1 -0
  35. package/dist/utils/tag-abi-codec.js +144 -0
  36. package/dist/utils/tag-abi-codec.js.map +1 -0
  37. package/dist/utils/tag-type-builder.d.ts +158 -0
  38. package/dist/utils/tag-type-builder.d.ts.map +1 -0
  39. package/dist/utils/tag-type-builder.js +410 -0
  40. package/dist/utils/tag-type-builder.js.map +1 -0
  41. package/examples/crypto-utilities.ts +140 -0
  42. package/examples/domain-context.ts +80 -0
  43. package/examples/generate-mnemonic.ts +149 -0
  44. package/examples/index.ts +1 -1
  45. package/examples/ip.ts +171 -0
  46. package/examples/legacy.ts +10 -10
  47. package/examples/list-wallets.ts +81 -0
  48. package/examples/quasar-demo/.eslintrc.js +23 -0
  49. package/examples/quasar-demo/.quasar/app.js +43 -0
  50. package/examples/quasar-demo/.quasar/client-entry.js +38 -0
  51. package/examples/quasar-demo/.quasar/client-prefetch.js +130 -0
  52. package/examples/quasar-demo/.quasar/quasar-user-options.js +16 -0
  53. package/examples/quasar-demo/README.md +49 -0
  54. package/examples/quasar-demo/index.html +11 -0
  55. package/examples/quasar-demo/package-lock.json +6407 -0
  56. package/examples/quasar-demo/package.json +36 -0
  57. package/examples/quasar-demo/quasar.config.js +73 -0
  58. package/examples/quasar-demo/src/App.vue +13 -0
  59. package/examples/quasar-demo/src/css/app.scss +1 -0
  60. package/examples/quasar-demo/src/layouts/MainLayout.vue +21 -0
  61. package/examples/quasar-demo/src/pages/IndexPage.vue +905 -0
  62. package/examples/quasar-demo/src/router/index.ts +25 -0
  63. package/examples/quasar-demo/src/router/routes.ts +11 -0
  64. package/examples/quasar-demo/tsconfig.json +28 -0
  65. package/examples/register-subdomain.ts +152 -0
  66. package/examples/rsa-keypair.ts +148 -0
  67. package/examples/tag-builder.ts +235 -0
  68. package/examples/tag-management.ts +534 -0
  69. package/examples/tag-nested-tuple.ts +190 -0
  70. package/examples/tag-simple.ts +149 -0
  71. package/examples/tag-tagger.ts +217 -0
  72. package/examples/test-nested-tuple-conversion.ts +143 -0
  73. package/examples/test-type-bytes-parser.ts +70 -0
  74. package/examples/transfer-domain.ts +197 -0
  75. package/examples/wallet-management.ts +196 -0
  76. package/package.json +24 -15
  77. package/src/abi/RootResolver2ABI.ts +237 -0
  78. package/src/business/index.ts +1490 -0
  79. package/src/business/tag-context.ts +713 -0
  80. package/src/cli.ts +2755 -39
  81. package/src/debug.ts +17 -2
  82. package/src/index.ts +300 -14
  83. package/src/utils/crypto-utils.ts +459 -0
  84. package/src/utils/error-parser.ts +225 -0
  85. package/src/utils/tag-abi-codec.ts +158 -0
  86. package/src/utils/tag-type-builder.ts +469 -0
  87. package/tsconfig.json +1 -1
package/src/cli.ts CHANGED
@@ -1,7 +1,28 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import OlaresID from './index';
3
+ import OlaresID, {
4
+ createRsaKeyPair,
5
+ pemToDer,
6
+ derToPem,
7
+ ipv4ToBytes4,
8
+ bytes4ToIpv4,
9
+ generateMnemonic,
10
+ getEthereumAddressFromMnemonic,
11
+ getEVMPrivateKeyFromMnemonic,
12
+ getDIDFromMnemonic,
13
+ generateDIDKeyData,
14
+ deriveDIDFromMnemonic,
15
+ TagTypeBuilder
16
+ } from './index';
4
17
  import { debug } from './debug';
18
+ import * as fs from 'fs';
19
+ import * as path from 'path';
20
+
21
+ // CLI Version - read from package.json
22
+ const packageJson = JSON.parse(
23
+ fs.readFileSync(path.join(__dirname, '../package.json'), 'utf-8')
24
+ );
25
+ const CLI_VERSION = packageJson.version;
5
26
 
6
27
  // 预设的网络配置
7
28
  const NETWORKS = {
@@ -9,13 +30,17 @@ const NETWORKS = {
9
30
  rpc: 'https://sepolia.optimism.io',
10
31
  contractDid: '0xe2D7c3a9013960E04d4E9F5F9B63fff37eEd97A8',
11
32
  contractRootResolver: '0xeF727cb066Fee98F88Db84555830063b4A24ddfc',
12
- contractAbiType: '0x7386fCBae6Ad4CCE1499d9153D99bc950B589718'
33
+ contractAbiType: '0x7386fCBae6Ad4CCE1499d9153D99bc950B589718',
34
+ contractRootResolver2: '0xcbC02aa08c77a374eC0D5A0403E108b7573d96e8',
35
+ supportSvcUrl: 'https://api-test.olares.com/did/support'
13
36
  },
14
37
  mainnet: {
15
38
  rpc: 'https://optimism-rpc.publicnode.com',
16
39
  contractDid: '0x5DA4Fa8E567d86e52Ef8Da860de1be8f54cae97D',
17
40
  contractRootResolver: '0xE2EABA0979277A90511F8873ae1e8cA26B54E740',
18
- contractAbiType: '0x9ae3F16bD99294Af1784beB1a0A5C84bf2636365'
41
+ contractAbiType: '0x9ae3F16bD99294Af1784beB1a0A5C84bf2636365',
42
+ contractRootResolver2: '0x7e7961aB771cA942CE4DB6e79579e016a33Dc95B',
43
+ supportSvcUrl: 'https://api.olares.com/did/support'
19
44
  }
20
45
  };
21
46
 
@@ -25,16 +50,23 @@ interface CliOptions {
25
50
  contractDid?: string;
26
51
  contractResolver?: string;
27
52
  contractAbi?: string;
53
+ contractRootResolver2?: string;
54
+ supportSvcUrl?: string;
28
55
  json: boolean;
29
56
  verbose: boolean;
30
57
  debug: boolean;
31
58
  debugLevel: string;
32
59
  help: boolean;
60
+ output?: string;
61
+ keyLength?: number;
62
+ words?: number;
33
63
  }
34
64
 
35
65
  function parseArgs(): {
36
66
  command: string;
67
+ subCommand?: string;
37
68
  domain?: string;
69
+ value?: string;
38
70
  options: CliOptions;
39
71
  } {
40
72
  const args = process.argv.slice(2);
@@ -48,7 +80,19 @@ function parseArgs(): {
48
80
  };
49
81
 
50
82
  let command = '';
83
+ let subCommand: string | undefined;
51
84
  let domain: string | undefined;
85
+ let value: string | undefined;
86
+
87
+ // Commands that don't have subcommands - next arg is domain directly
88
+ const commandsWithoutSubcommand = [
89
+ 'info',
90
+ 'owner',
91
+ 'is-owner',
92
+ 'transfer',
93
+ 'fetch',
94
+ 'fetch-domain'
95
+ ];
52
96
 
53
97
  for (let i = 0; i < args.length; i++) {
54
98
  const arg = args[i];
@@ -56,7 +100,7 @@ function parseArgs(): {
56
100
  if (arg === '--help' || arg === '-h') {
57
101
  options.help = true;
58
102
  } else if (arg === '--version' || arg === '-V') {
59
- console.log('0.1.0');
103
+ console.log(CLI_VERSION);
60
104
  process.exit(0);
61
105
  } else if (arg === '--network' || arg === '-n') {
62
106
  options.network = args[++i] || 'sepolia';
@@ -68,6 +112,16 @@ function parseArgs(): {
68
112
  options.contractResolver = args[++i];
69
113
  } else if (arg === '--contract-abi') {
70
114
  options.contractAbi = args[++i];
115
+ } else if (arg === '--contract-resolver2') {
116
+ options.contractRootResolver2 = args[++i];
117
+ } else if (arg === '--support-svc-url') {
118
+ options.supportSvcUrl = args[++i];
119
+ } else if (arg === '--output' || arg === '-o') {
120
+ options.output = args[++i];
121
+ } else if (arg === '--key-length') {
122
+ options.keyLength = parseInt(args[++i], 10);
123
+ } else if (arg === '--words' || arg === '-w') {
124
+ options.words = parseInt(args[++i], 10);
71
125
  } else if (arg === '--json' || arg === '-j') {
72
126
  options.json = true;
73
127
  } else if (arg === '--verbose' || arg === '-v') {
@@ -79,31 +133,108 @@ function parseArgs(): {
79
133
  } else if (!command) {
80
134
  command = arg;
81
135
  } else if (
82
- !domain &&
83
- (command === 'fetch' || command === 'fetch-domain')
136
+ !subCommand &&
137
+ !commandsWithoutSubcommand.includes(command)
84
138
  ) {
139
+ // Only set subCommand if the command requires one
140
+ subCommand = arg;
141
+ } else if (!domain) {
85
142
  domain = arg;
143
+ } else if (!value) {
144
+ value = arg;
86
145
  }
87
146
  }
88
147
 
89
- return { command, domain, options };
148
+ return { command, subCommand, domain, value, options };
90
149
  }
91
150
 
92
151
  function showHelp(): void {
93
152
  console.log(`
94
- DID CLI Tool v0.1.0
153
+ DID CLI Tool v${CLI_VERSION}
95
154
 
96
155
  USAGE:
97
- did-cli <command> [arguments] [options]
156
+ did-cli <command> [subcommand] [arguments] [options]
98
157
 
99
158
  COMMANDS:
100
- fetch <domain> Fetch domain information (alias: fetch-domain)
101
- fetch-all Fetch all domains from contract
102
- config Show network configurations
103
- help Show this help message
104
-
105
- ARGUMENTS:
106
- <domain> Domain name to fetch (e.g., example.olares.com)
159
+ Query Commands:
160
+ info <domain> Get domain metadata
161
+ owner <domain> Get domain owner address
162
+ is-owner <domain> Check if you are the domain owner (requires PRIVATE_KEY_OR_MNEMONIC)
163
+
164
+ RSA Key Commands:
165
+ rsa generate Generate a new RSA key pair
166
+ rsa get <domain> Get RSA public key for domain
167
+ rsa set <domain> <file> Set RSA public key from PEM file (requires PRIVATE_KEY_OR_MNEMONIC)
168
+ rsa remove <domain> Remove RSA public key (requires PRIVATE_KEY_OR_MNEMONIC)
169
+
170
+ IP Commands:
171
+ ip get <domain> Get DNS A record (IPv4)
172
+ ip set <domain> <ip> Set DNS A record (requires PRIVATE_KEY_OR_MNEMONIC)
173
+ ip remove <domain> Remove DNS A record (requires PRIVATE_KEY_OR_MNEMONIC)
174
+
175
+ Subdomain Commands:
176
+ subdomain register <parent> <label>
177
+ Register a new subdomain (requires PRIVATE_KEY_OR_MNEMONIC)
178
+ Use MNEMONIC env var for subdomain owner or auto-generates one
179
+
180
+ Transfer Commands:
181
+ transfer <domain> Transfer domain ownership to a new owner (requires PRIVATE_KEY_OR_MNEMONIC)
182
+ Use MNEMONIC env var for new owner or auto-generates one
183
+
184
+ Wallet Management Commands:
185
+ wallet evm add <domain> Add EVM wallet to domain (requires PRIVATE_KEY_OR_MNEMONIC & EVM_PRIVATE_KEY)
186
+ wallet evm remove <domain> Remove EVM wallet from domain (requires PRIVATE_KEY_OR_MNEMONIC & EVM_PRIVATE_KEY)
187
+ wallet evm list <domain> List all EVM wallets for domain
188
+ wallet solana add <domain> Add Solana wallet to domain (requires PRIVATE_KEY_OR_MNEMONIC & SOLANA_PRIVATE_KEY)
189
+ wallet solana remove <domain>
190
+ Remove Solana wallet from domain (requires PRIVATE_KEY_OR_MNEMONIC & SOLANA_PRIVATE_KEY)
191
+ wallet solana list <domain>
192
+ List all Solana wallets for domain
193
+
194
+ Tag Management Commands:
195
+ tag define <domain> <tag-name> <type>
196
+ Define a new tag type (requires PRIVATE_KEY_OR_MNEMONIC)
197
+ Supported types: string, uint8, uint256, int8, int256, bool, address, bytes, bytes32
198
+ Array types: string[], uint8[], etc.
199
+ tag set <domain> <tag-name> <value>
200
+ Set tag value (requires PRIVATE_KEY_OR_MNEMONIC)
201
+ Value can be a string or JSON for arrays/objects
202
+ tag get <domain> <tag-name>
203
+ Get tag value (read-only)
204
+ tag remove <domain> <tag-name>
205
+ Remove tag value (requires PRIVATE_KEY_OR_MNEMONIC)
206
+ tag list <domain> List all tags with values for domain (read-only)
207
+ tag list-defined <domain> List all defined tag types for domain (read-only)
208
+ tag set-tagger <domain> <tag-name> <address>
209
+ Set tagger address for a tag (requires PRIVATE_KEY_OR_MNEMONIC)
210
+ tag get-tagger <domain> <tag-name>
211
+ Get tagger address for a tag (read-only)
212
+
213
+ Operator Commands:
214
+ operator get Get current operator address
215
+ operator set <address> Set operator address (requires PRIVATE_KEY_OR_MNEMONIC)
216
+
217
+ Crypto Utility Commands:
218
+ crypto generate Generate a new mnemonic phrase (--words option)
219
+ Saves to file, use --output to specify file path
220
+ crypto address Get Ethereum address from mnemonic (requires MNEMONIC env var)
221
+ crypto did Get DID from mnemonic (requires MNEMONIC env var)
222
+ crypto privatekey Get EVM private key from mnemonic (requires MNEMONIC env var)
223
+ Saves to file, use --output to specify file path
224
+ crypto derive Derive all keys from mnemonic (requires MNEMONIC env var)
225
+ Saves to file, use --output to specify file path
226
+
227
+ Utility Commands:
228
+ convert pem-to-der <file> Convert PEM file to DER hex
229
+ convert der-to-pem <hex> Convert DER hex to PEM
230
+ convert ip-to-bytes <ip> Convert IPv4 to bytes4
231
+ convert bytes-to-ip <hex> Convert bytes4 to IPv4
232
+
233
+ Legacy Commands:
234
+ fetch <domain> Fetch domain information (alias: fetch-domain)
235
+ fetch-all Fetch all domains from contract
236
+ config Show network configurations
237
+ help Show this help message
107
238
 
108
239
  OPTIONS:
109
240
  -n, --network <network> Network to use (sepolia|mainnet) [default: sepolia]
@@ -111,6 +242,12 @@ OPTIONS:
111
242
  --contract-did <address> Custom DID contract address
112
243
  --contract-resolver <address> Custom RootResolver contract address
113
244
  --contract-abi <address> Custom ABIType contract address
245
+ --contract-resolver2 <address> Custom RootResolver2 contract address
246
+ --support-svc-url <url> Custom support service URL
247
+ -o, --output <file> Output file path (for generate commands)
248
+ --key-length <bits> RSA key length in bits [default: 2048]
249
+ -w, --words <count> Mnemonic word count: 12|15|18|21|24 [default: 12]
250
+ -j, --json Output in JSON format
114
251
  -v, --verbose Enable verbose debug output
115
252
  --debug Enable debug output
116
253
  --debug-level <level> Set debug level (debug|info|warn|error) [default: info]
@@ -118,12 +255,122 @@ OPTIONS:
118
255
  -V, --version Show version number
119
256
 
120
257
  EXAMPLES:
121
- did-cli fetch example.olares.com
122
- did-cli fetch example.olares.com --network mainnet
123
- did-cli fetch example.olares.com --json
124
- did-cli fetch example.olares.com --debug
125
- did-cli fetch-all --network sepolia
126
- did-cli config
258
+ # Query domain info
259
+ did-cli info example.olares.com --network mainnet
260
+ did-cli owner example.olares.com
261
+ export PRIVATE_KEY_OR_MNEMONIC=0xYOUR_PRIVATE_KEY
262
+ did-cli is-owner example.olares.com
263
+
264
+ # RSA key management
265
+ did-cli rsa generate --output ./my-key.pem --key-length 4096
266
+ did-cli rsa get example.olares.com
267
+ did-cli rsa set example.olares.com ./public-key.pem
268
+ did-cli rsa remove example.olares.com
269
+
270
+ # IP management
271
+ did-cli ip get example.olares.com
272
+ did-cli ip set example.olares.com 192.168.1.100
273
+ did-cli ip remove example.olares.com
274
+
275
+ # Subdomain management (auto-generate mnemonic for subdomain owner)
276
+ export PRIVATE_KEY_OR_MNEMONIC=0xYOUR_PRIVATE_KEY
277
+ did-cli subdomain register parent.com child
278
+ did-cli subdomain register parent.com child --words 24
279
+
280
+ # Subdomain management (use existing mnemonic for subdomain owner)
281
+ export MNEMONIC="your twelve word mnemonic here"
282
+ did-cli subdomain register parent.com child
283
+
284
+ # Subdomain management (JSON output)
285
+ did-cli subdomain register parent.com child --json
286
+
287
+ # Transfer domain ownership (auto-generate mnemonic for new owner)
288
+ export PRIVATE_KEY_OR_MNEMONIC=0xYOUR_PRIVATE_KEY
289
+ did-cli transfer example.com
290
+ did-cli transfer example.com --words 24
291
+
292
+ # Transfer domain ownership (use existing mnemonic for new owner)
293
+ export MNEMONIC="your twelve word mnemonic here"
294
+ did-cli transfer example.com
295
+
296
+ # Crypto utilities
297
+ did-cli crypto generate --words 12
298
+ did-cli crypto generate --words 24 --output ./custom-path.txt
299
+ export MNEMONIC="your twelve word mnemonic here"
300
+ did-cli crypto address
301
+ did-cli crypto did
302
+ did-cli crypto privatekey --output ./my-private-key.txt
303
+ did-cli crypto derive --output ./my-keys.txt
304
+
305
+ # Wallet management
306
+ export PRIVATE_KEY_OR_MNEMONIC=0xYOUR_PRIVATE_KEY
307
+
308
+ # Add EVM wallet
309
+ export EVM_PRIVATE_KEY=0xWALLET_PRIVATE_KEY
310
+ did-cli wallet evm add example.com
311
+
312
+ # Remove EVM wallet
313
+ did-cli wallet evm remove example.com
314
+
315
+ # List EVM wallets
316
+ did-cli wallet evm list example.com
317
+
318
+ # Add Solana wallet (base58 format)
319
+ export SOLANA_PRIVATE_KEY="5J7W..."
320
+ did-cli wallet solana add example.com
321
+
322
+ # Add Solana wallet (JSON array format from Phantom)
323
+ export SOLANA_PRIVATE_KEY='[1,2,3,...]'
324
+ did-cli wallet solana add example.com
325
+
326
+ # List Solana wallets
327
+ did-cli wallet solana list example.com
328
+
329
+ # Tag management
330
+ export PRIVATE_KEY_OR_MNEMONIC=0xYOUR_PRIVATE_KEY
331
+
332
+ # Define a simple tag
333
+ did-cli tag define example.com email string
334
+
335
+ # Set tagger (usually yourself)
336
+ did-cli tag set-tagger example.com email 0xYourAddress
337
+
338
+ # Set tag value
339
+ did-cli tag set example.com email "user@example.com"
340
+
341
+ # Get tag value
342
+ did-cli tag get example.com email
343
+
344
+ # List all tags
345
+ did-cli tag list example.com
346
+
347
+ # Array type (quote to prevent shell interpretation)
348
+ did-cli tag define example.com links "string[]"
349
+ did-cli tag set-tagger example.com links 0xYourAddress
350
+ did-cli tag set example.com links '["https://x.com","https://github.com"]'
351
+
352
+ # Remove tag
353
+ did-cli tag remove example.com email
354
+
355
+ # Operator management
356
+ did-cli operator get
357
+ did-cli operator set 0x1234...
358
+
359
+ # Utilities
360
+ did-cli convert pem-to-der ./public-key.pem
361
+ did-cli convert ip-to-bytes 192.168.1.1
362
+ did-cli convert bytes-to-ip 0xc0a80101
363
+
364
+ ENVIRONMENT VARIABLES:
365
+ PRIVATE_KEY_OR_MNEMONIC Private key (0x...) or mnemonic phrase for signing transactions
366
+ (required for write operations)
367
+ MNEMONIC Mnemonic phrase for generating owner identity
368
+ (used in subdomain registration, domain transfer, and crypto utilities)
369
+ EVM_PRIVATE_KEY EVM wallet private key for wallet management operations
370
+ (required for wallet evm add/remove commands)
371
+ SOLANA_PRIVATE_KEY Solana wallet private key for wallet management operations
372
+ Format: base58 string or JSON array (e.g., "[1,2,3,...]")
373
+ (required for wallet solana add/remove commands)
127
374
  `);
128
375
  }
129
376
 
@@ -194,29 +441,2498 @@ async function fetchDomain(domain: string, options: CliOptions): Promise<void> {
194
441
  }
195
442
  }
196
443
 
197
- async function main(): Promise<void> {
198
- const { command, domain, options } = parseArgs();
444
+ // ============================================================================
445
+ // Helper Functions
446
+ // ============================================================================
199
447
 
200
- if (options.help || !command || command === 'help') {
201
- showHelp();
202
- return;
448
+ function getConsole(options: CliOptions) {
449
+ let config;
450
+ if (
451
+ options.rpc &&
452
+ options.contractDid &&
453
+ options.contractResolver &&
454
+ options.contractAbi &&
455
+ options.contractRootResolver2 &&
456
+ options.supportSvcUrl
457
+ ) {
458
+ config = {
459
+ rpc: options.rpc,
460
+ contractDid: options.contractDid,
461
+ contractRootResolver: options.contractResolver,
462
+ contractAbiType: options.contractAbi,
463
+ contractRootResolver2: options.contractRootResolver2,
464
+ supportSvcUrl: options.supportSvcUrl
465
+ };
466
+ } else if (NETWORKS[options.network as keyof typeof NETWORKS]) {
467
+ config = NETWORKS[options.network as keyof typeof NETWORKS];
468
+ } else {
469
+ throw new Error(`Unknown network: ${options.network}`);
203
470
  }
204
471
 
205
- switch (command) {
206
- case 'fetch':
207
- case 'fetch-domain':
208
- if (!domain) {
209
- console.error('❌ Error: Domain argument is required');
210
- console.error('Usage: did-cli fetch <domain> [options]');
211
- process.exit(1);
472
+ return OlaresID.createConsole(
473
+ config.rpc,
474
+ config.contractDid,
475
+ config.contractRootResolver,
476
+ config.contractAbiType,
477
+ config.contractRootResolver2,
478
+ config.supportSvcUrl
479
+ );
480
+ }
481
+
482
+ function getPrivateKeyOrMnemonic(): string {
483
+ const key = process.env.PRIVATE_KEY_OR_MNEMONIC;
484
+ if (!key) {
485
+ console.error(
486
+ '❌ Error: Private key or mnemonic is required for this operation'
487
+ );
488
+ console.error(
489
+ 'Please set PRIVATE_KEY_OR_MNEMONIC environment variable'
490
+ );
491
+ console.error(
492
+ 'Example: export PRIVATE_KEY_OR_MNEMONIC=0xYOUR_PRIVATE_KEY'
493
+ );
494
+ console.error(
495
+ 'Or: export PRIVATE_KEY_OR_MNEMONIC="your twelve word mnemonic phrase here"'
496
+ );
497
+ process.exit(1);
498
+ }
499
+ return key;
500
+ }
501
+
502
+ // ============================================================================
503
+ // Domain Info Commands
504
+ // ============================================================================
505
+
506
+ async function getDomainInfo(
507
+ domain: string,
508
+ options: CliOptions
509
+ ): Promise<void> {
510
+ try {
511
+ const didConsole = getConsole(options);
512
+ const domainContext = didConsole.domain(domain);
513
+ const metaInfo = await domainContext.getMetaInfo();
514
+
515
+ if (options.json) {
516
+ console.log(JSON.stringify(metaInfo, null, 2));
517
+ } else {
518
+ console.log('📋 Domain Metadata:');
519
+ console.log(` Name: ${metaInfo.name}`);
520
+ console.log(` DID: ${metaInfo.did}`);
521
+ console.log(` Token ID: ${metaInfo.id}`);
522
+ console.log(` Note: ${metaInfo.note}`);
523
+ console.log(` Allow Subdomain: ${metaInfo.allowSubdomain}`);
524
+ }
525
+ } catch (error) {
526
+ console.error(
527
+ '❌ Error:',
528
+ error instanceof Error ? error.message : String(error)
529
+ );
530
+ process.exit(1);
531
+ }
532
+ }
533
+
534
+ async function getDomainOwner(
535
+ domain: string,
536
+ options: CliOptions
537
+ ): Promise<void> {
538
+ try {
539
+ const didConsole = getConsole(options);
540
+ const domainContext = didConsole.domain(domain);
541
+ const owner = await domainContext.getOwner();
542
+
543
+ if (options.json) {
544
+ console.log(JSON.stringify({ owner }, null, 2));
545
+ } else {
546
+ console.log(`👤 Owner: ${owner}`);
547
+ }
548
+ } catch (error) {
549
+ console.error(
550
+ '❌ Error:',
551
+ error instanceof Error ? error.message : String(error)
552
+ );
553
+ process.exit(1);
554
+ }
555
+ }
556
+
557
+ async function checkIsOwner(
558
+ domain: string,
559
+ options: CliOptions
560
+ ): Promise<void> {
561
+ try {
562
+ const privateKeyOrMnemonic = getPrivateKeyOrMnemonic();
563
+ const didConsole = getConsole(options);
564
+ await didConsole.setSigner(privateKeyOrMnemonic);
565
+
566
+ const domainContext = didConsole.domain(domain);
567
+ const isOwner = await domainContext.isOwner();
568
+
569
+ if (options.json) {
570
+ console.log(JSON.stringify({ isOwner }, null, 2));
571
+ } else {
572
+ console.log(
573
+ isOwner ? '✅ You are the owner' : '❌ You are not the owner'
574
+ );
575
+ }
576
+ } catch (error) {
577
+ console.error(
578
+ '❌ Error:',
579
+ error instanceof Error ? error.message : String(error)
580
+ );
581
+ process.exit(1);
582
+ }
583
+ }
584
+
585
+ // ============================================================================
586
+ // RSA Key Commands
587
+ // ============================================================================
588
+
589
+ async function generateRsaKey(options: CliOptions): Promise<void> {
590
+ try {
591
+ const keyLength = options.keyLength || 2048;
592
+ const keyPair = await createRsaKeyPair(keyLength);
593
+
594
+ if (options.output) {
595
+ // Save to files
596
+ const publicKeyFile = options.output;
597
+ const privateKeyFile = options.output.replace(
598
+ /\.pem$/,
599
+ '-private.pem'
600
+ );
601
+
602
+ fs.writeFileSync(publicKeyFile, keyPair.rsaPublicKey);
603
+ fs.writeFileSync(privateKeyFile, keyPair.rsaPrivateKey);
604
+
605
+ console.log(`✅ RSA key pair generated (${keyLength} bits)`);
606
+ console.log(`📄 Public key: ${publicKeyFile}`);
607
+ console.log(`🔐 Private key: ${privateKeyFile}`);
608
+ } else {
609
+ // When no output file is specified, still save to default files for security
610
+ const defaultPublicFile = './rsa-public.pem';
611
+ const defaultPrivateFile = './rsa-private.pem';
612
+ fs.writeFileSync(defaultPublicFile, keyPair.rsaPublicKey);
613
+ fs.writeFileSync(defaultPrivateFile, keyPair.rsaPrivateKey);
614
+
615
+ if (options.json) {
616
+ console.log(
617
+ JSON.stringify(
618
+ {
619
+ keyLength: keyLength,
620
+ publicKeyFile: defaultPublicFile,
621
+ privateKeyFile: defaultPrivateFile
622
+ },
623
+ null,
624
+ 2
625
+ )
626
+ );
627
+ } else {
628
+ console.log(`✅ RSA key pair generated (${keyLength} bits)`);
629
+ console.log(`📄 Public key: ${defaultPublicFile}`);
630
+ console.log(`🔐 Private key: ${defaultPrivateFile}`);
631
+ console.log('\n⚠️ SECURITY WARNING:');
632
+ console.log(' • Keep the private key secure! Never share it!');
633
+ console.log(
634
+ ' • Delete the file after copying to a secure location'
635
+ );
636
+ console.log(` • Command: rm ${defaultPrivateFile}`);
637
+ }
638
+ }
639
+ } catch (error) {
640
+ console.error(
641
+ '❌ Error:',
642
+ error instanceof Error ? error.message : String(error)
643
+ );
644
+ process.exit(1);
645
+ }
646
+ }
647
+
648
+ async function getRsaKey(domain: string, options: CliOptions): Promise<void> {
649
+ try {
650
+ const didConsole = getConsole(options);
651
+ const domainContext = didConsole.domain(domain);
652
+ const rsaKey = await domainContext.getRSAPublicKey();
653
+
654
+ if (!rsaKey) {
655
+ console.log('❌ No RSA public key set for this domain');
656
+ process.exit(1);
657
+ }
658
+
659
+ if (options.json) {
660
+ console.log(JSON.stringify({ rsaPublicKey: rsaKey }, null, 2));
661
+ } else {
662
+ console.log('📄 RSA Public Key:');
663
+ console.log(rsaKey);
664
+ }
665
+ } catch (error) {
666
+ console.error(
667
+ '❌ Error:',
668
+ error instanceof Error ? error.message : String(error)
669
+ );
670
+ process.exit(1);
671
+ }
672
+ }
673
+
674
+ async function setRsaKey(
675
+ domain: string,
676
+ pemFile: string,
677
+ options: CliOptions
678
+ ): Promise<void> {
679
+ try {
680
+ const privateKeyOrMnemonic = getPrivateKeyOrMnemonic();
681
+ const didConsole = getConsole(options);
682
+ await didConsole.setSigner(privateKeyOrMnemonic);
683
+
684
+ // Read PEM file
685
+ if (!fs.existsSync(pemFile)) {
686
+ console.error(`❌ Error: File not found: ${pemFile}`);
687
+ process.exit(1);
688
+ }
689
+
690
+ const rsaPublicKey = fs.readFileSync(pemFile, 'utf-8');
691
+ const domainContext = didConsole.domain(domain);
692
+ const result = await domainContext.setRSAPublicKey(rsaPublicKey);
693
+
694
+ if (result.success) {
695
+ console.log('✅ RSA public key set successfully');
696
+ console.log(`📝 Transaction: ${result.transactionHash}`);
697
+ if (result.gasUsed) {
698
+ console.log(`⛽ Gas used: ${result.gasUsed.toString()}`);
699
+ }
700
+ } else {
701
+ console.error(`❌ Failed: ${result.error}`);
702
+ process.exit(1);
703
+ }
704
+ } catch (error) {
705
+ console.error(
706
+ '❌ Error:',
707
+ error instanceof Error ? error.message : String(error)
708
+ );
709
+ process.exit(1);
710
+ }
711
+ }
712
+
713
+ async function removeRsaKey(
714
+ domain: string,
715
+ options: CliOptions
716
+ ): Promise<void> {
717
+ try {
718
+ const privateKeyOrMnemonic = getPrivateKeyOrMnemonic();
719
+ const didConsole = getConsole(options);
720
+ await didConsole.setSigner(privateKeyOrMnemonic);
721
+
722
+ const domainContext = didConsole.domain(domain);
723
+ const result = await domainContext.removeRSAPublicKey();
724
+
725
+ if (result.success) {
726
+ console.log('✅ RSA public key removed successfully');
727
+ console.log(`📝 Transaction: ${result.transactionHash}`);
728
+ if (result.gasUsed) {
729
+ console.log(`⛽ Gas used: ${result.gasUsed.toString()}`);
730
+ }
731
+ } else {
732
+ console.error(`❌ Failed: ${result.error}`);
733
+ process.exit(1);
734
+ }
735
+ } catch (error) {
736
+ console.error(
737
+ '❌ Error:',
738
+ error instanceof Error ? error.message : String(error)
739
+ );
740
+ process.exit(1);
741
+ }
742
+ }
743
+
744
+ // ============================================================================
745
+ // IP Commands
746
+ // ============================================================================
747
+
748
+ async function getIP(domain: string, options: CliOptions): Promise<void> {
749
+ try {
750
+ const didConsole = getConsole(options);
751
+ const domainContext = didConsole.domain(domain);
752
+ const ip = await domainContext.getIP();
753
+
754
+ if (!ip) {
755
+ console.log('❌ No IP address set for this domain');
756
+ process.exit(1);
757
+ }
758
+
759
+ if (options.json) {
760
+ console.log(JSON.stringify({ ip }, null, 2));
761
+ } else {
762
+ console.log(`🌐 IP Address: ${ip}`);
763
+ }
764
+ } catch (error) {
765
+ console.error(
766
+ '❌ Error:',
767
+ error instanceof Error ? error.message : String(error)
768
+ );
769
+ process.exit(1);
770
+ }
771
+ }
772
+
773
+ async function setIP(
774
+ domain: string,
775
+ ip: string,
776
+ options: CliOptions
777
+ ): Promise<void> {
778
+ try {
779
+ const privateKeyOrMnemonic = getPrivateKeyOrMnemonic();
780
+ const didConsole = getConsole(options);
781
+ await didConsole.setSigner(privateKeyOrMnemonic);
782
+
783
+ const domainContext = didConsole.domain(domain);
784
+ const result = await domainContext.setIP(ip);
785
+
786
+ if (result.success) {
787
+ console.log(`✅ IP address set to ${ip}`);
788
+ console.log(`📝 Transaction: ${result.transactionHash}`);
789
+ if (result.gasUsed) {
790
+ console.log(`⛽ Gas used: ${result.gasUsed.toString()}`);
791
+ }
792
+ } else {
793
+ console.error(`❌ Failed: ${result.error}`);
794
+ process.exit(1);
795
+ }
796
+ } catch (error) {
797
+ console.error(
798
+ '❌ Error:',
799
+ error instanceof Error ? error.message : String(error)
800
+ );
801
+ process.exit(1);
802
+ }
803
+ }
804
+
805
+ async function removeIP(domain: string, options: CliOptions): Promise<void> {
806
+ try {
807
+ const privateKeyOrMnemonic = getPrivateKeyOrMnemonic();
808
+ const didConsole = getConsole(options);
809
+ await didConsole.setSigner(privateKeyOrMnemonic);
810
+
811
+ const domainContext = didConsole.domain(domain);
812
+ const result = await domainContext.removeIP();
813
+
814
+ if (result.success) {
815
+ console.log('✅ IP address removed successfully');
816
+ console.log(`📝 Transaction: ${result.transactionHash}`);
817
+ if (result.gasUsed) {
818
+ console.log(`⛽ Gas used: ${result.gasUsed.toString()}`);
819
+ }
820
+ } else {
821
+ console.error(`❌ Failed: ${result.error}`);
822
+ process.exit(1);
823
+ }
824
+ } catch (error) {
825
+ console.error(
826
+ '❌ Error:',
827
+ error instanceof Error ? error.message : String(error)
828
+ );
829
+ process.exit(1);
830
+ }
831
+ }
832
+
833
+ // ============================================================================
834
+ // Operator Commands
835
+ // ============================================================================
836
+
837
+ async function getOperator(options: CliOptions): Promise<void> {
838
+ try {
839
+ const didConsole = getConsole(options);
840
+ const operator = await didConsole.getOperator();
841
+
842
+ if (options.json) {
843
+ console.log(JSON.stringify({ operator }, null, 2));
844
+ } else {
845
+ console.log(`👔 Operator: ${operator}`);
846
+ }
847
+ } catch (error) {
848
+ console.error(
849
+ '❌ Error:',
850
+ error instanceof Error ? error.message : String(error)
851
+ );
852
+ process.exit(1);
853
+ }
854
+ }
855
+
856
+ async function setOperator(
857
+ address: string,
858
+ options: CliOptions
859
+ ): Promise<void> {
860
+ try {
861
+ const privateKeyOrMnemonic = getPrivateKeyOrMnemonic();
862
+ const didConsole = getConsole(options);
863
+ await didConsole.setSigner(privateKeyOrMnemonic);
864
+
865
+ const result = await didConsole.setOperator(address);
866
+
867
+ if (result.success) {
868
+ console.log(`✅ Operator set to ${address}`);
869
+ console.log(`📝 Transaction: ${result.transactionHash}`);
870
+ if (result.gasUsed) {
871
+ console.log(`⛽ Gas used: ${result.gasUsed.toString()}`);
212
872
  }
213
- await fetchDomain(domain, options);
214
- break;
873
+ } else {
874
+ console.error(`❌ Failed: ${result.error}`);
875
+ process.exit(1);
876
+ }
877
+ } catch (error) {
878
+ console.error(
879
+ '❌ Error:',
880
+ error instanceof Error ? error.message : String(error)
881
+ );
882
+ process.exit(1);
883
+ }
884
+ }
885
+
886
+ // ============================================================================
887
+ // Conversion Utilities
888
+ // ============================================================================
215
889
 
216
- default:
217
- console.error(`❌ Unknown command: ${command}`);
218
- console.error('Run "did-cli help" for usage information');
890
+ function convertPemToDer(pemFile: string, options: CliOptions): void {
891
+ try {
892
+ if (!fs.existsSync(pemFile)) {
893
+ console.error(`❌ Error: File not found: ${pemFile}`);
219
894
  process.exit(1);
895
+ }
896
+
897
+ const pem = fs.readFileSync(pemFile, 'utf-8');
898
+ const der = pemToDer(pem);
899
+
900
+ if (options.json) {
901
+ console.log(JSON.stringify({ der }, null, 2));
902
+ } else {
903
+ console.log('🔄 DER Hex:');
904
+ console.log(der);
905
+ }
906
+ } catch (error) {
907
+ console.error(
908
+ '❌ Error:',
909
+ error instanceof Error ? error.message : String(error)
910
+ );
911
+ process.exit(1);
912
+ }
913
+ }
914
+
915
+ function convertDerToPem(derHex: string, options: CliOptions): void {
916
+ try {
917
+ const pem = derToPem(derHex);
918
+
919
+ if (options.json) {
920
+ console.log(JSON.stringify({ pem }, null, 2));
921
+ } else {
922
+ console.log('🔄 PEM:');
923
+ console.log(pem);
924
+ }
925
+ } catch (error) {
926
+ console.error(
927
+ '❌ Error:',
928
+ error instanceof Error ? error.message : String(error)
929
+ );
930
+ process.exit(1);
931
+ }
932
+ }
933
+
934
+ function convertIpToBytes(ip: string, options: CliOptions): void {
935
+ try {
936
+ const bytes = ipv4ToBytes4(ip);
937
+
938
+ if (options.json) {
939
+ console.log(JSON.stringify({ ip, bytes }, null, 2));
940
+ } else {
941
+ console.log(`🔄 ${ip} → ${bytes}`);
942
+ }
943
+ } catch (error) {
944
+ console.error(
945
+ '❌ Error:',
946
+ error instanceof Error ? error.message : String(error)
947
+ );
948
+ process.exit(1);
949
+ }
950
+ }
951
+
952
+ function convertBytesToIp(bytes: string, options: CliOptions): void {
953
+ try {
954
+ const ip = bytes4ToIpv4(bytes);
955
+
956
+ if (options.json) {
957
+ console.log(JSON.stringify({ bytes, ip }, null, 2));
958
+ } else {
959
+ console.log(`🔄 ${bytes} → ${ip}`);
960
+ }
961
+ } catch (error) {
962
+ console.error(
963
+ '❌ Error:',
964
+ error instanceof Error ? error.message : String(error)
965
+ );
966
+ process.exit(1);
967
+ }
968
+ }
969
+
970
+ // ============================================================================
971
+ // Subdomain Commands
972
+ // ============================================================================
973
+
974
+ async function registerSubdomain(
975
+ parentDomain: string,
976
+ subdomain: string,
977
+ options: CliOptions
978
+ ): Promise<void> {
979
+ try {
980
+ const privateKeyOrMnemonic = getPrivateKeyOrMnemonic();
981
+ const didConsole = getConsole(options);
982
+
983
+ // Initialize with signer
984
+ await didConsole.setSigner(privateKeyOrMnemonic);
985
+
986
+ const domain = didConsole.domain(parentDomain);
987
+
988
+ // Get or generate mnemonic for subdomain owner
989
+ let mnemonic: string;
990
+ const subdomainMnemonic = process.env.MNEMONIC;
991
+
992
+ if (subdomainMnemonic) {
993
+ mnemonic = subdomainMnemonic;
994
+ console.log('📝 Using mnemonic from MNEMONIC environment variable');
995
+ } else {
996
+ const wordCount = options.words || 12;
997
+ if (![12, 15, 18, 21, 24].includes(wordCount)) {
998
+ console.error(
999
+ '❌ Error: Word count must be one of: 12, 15, 18, 21, 24'
1000
+ );
1001
+ process.exit(1);
1002
+ }
1003
+ mnemonic = generateMnemonic(wordCount);
1004
+
1005
+ // Save mnemonic to file
1006
+ const mnemonicFile = './subdomain-mnemonic.txt';
1007
+ fs.writeFileSync(mnemonicFile, mnemonic, 'utf-8');
1008
+
1009
+ console.log(
1010
+ `🔑 Generated ${wordCount}-word mnemonic for subdomain owner`
1011
+ );
1012
+ console.log(`📄 Mnemonic saved to: ${mnemonicFile}`);
1013
+ console.log('\n⚠️ SECURITY WARNING:');
1014
+ console.log(
1015
+ ' • Keep this mnemonic secure! It controls the subdomain!'
1016
+ );
1017
+ console.log(
1018
+ ' • Store it in a safe place (paper backup, hardware wallet, etc.)'
1019
+ );
1020
+ console.log(
1021
+ ' • Delete the file after copying to a secure location'
1022
+ );
1023
+ console.log(` • Command: rm ${mnemonicFile}`);
1024
+ console.log('\n💡 To use this mnemonic for future operations:');
1025
+ console.log(` export MNEMONIC="$(cat ${mnemonicFile})"\n`);
1026
+ }
1027
+
1028
+ // Register subdomain
1029
+ console.log(`📋 Registering subdomain: ${subdomain}.${parentDomain}`);
1030
+ console.log('⏳ Submitting transaction...\n');
1031
+
1032
+ const result = await domain.registerSubdomain(subdomain, mnemonic);
1033
+
1034
+ if (result.success) {
1035
+ if (options.json) {
1036
+ console.log(
1037
+ JSON.stringify(
1038
+ {
1039
+ success: true,
1040
+ subdomain: result.data.subdomain,
1041
+ fullDomainName: result.data.fullDomainName,
1042
+ owner: result.data.owner,
1043
+ did: result.data.did,
1044
+ transactionHash: result.transactionHash,
1045
+ gasUsed: result.gasUsed?.toString(),
1046
+ blockNumber: result.blockNumber
1047
+ },
1048
+ null,
1049
+ 2
1050
+ )
1051
+ );
1052
+ } else {
1053
+ console.log('✅ Subdomain registered successfully!\n');
1054
+ console.log('═'.repeat(60));
1055
+ console.log('Registration Details:');
1056
+ console.log('═'.repeat(60));
1057
+ console.log(`Subdomain label: ${result.data.subdomain}`);
1058
+ console.log(
1059
+ `Full domain name: ${result.data.fullDomainName}`
1060
+ );
1061
+ console.log(`Owner address: ${result.data.owner}`);
1062
+ console.log(`DID: ${result.data.did}`);
1063
+ console.log(`Transaction hash: ${result.transactionHash}`);
1064
+ console.log(
1065
+ `Gas used: ${result.gasUsed?.toString() || 'N/A'}`
1066
+ );
1067
+ console.log(
1068
+ `Block number: ${result.blockNumber || 'N/A'}`
1069
+ );
1070
+ console.log('═'.repeat(60));
1071
+
1072
+ const explorerUrl =
1073
+ options.network === 'mainnet'
1074
+ ? `https://optimistic.etherscan.io/tx/${result.transactionHash}`
1075
+ : `https://sepolia-optimism.etherscan.io/tx/${result.transactionHash}`;
1076
+ console.log(`\n🔗 View on block explorer:`);
1077
+ console.log(` ${explorerUrl}\n`);
1078
+ }
1079
+ } else {
1080
+ console.error('❌ Subdomain registration failed!');
1081
+ console.error(` Error: ${result.error}`);
1082
+ process.exit(1);
1083
+ }
1084
+ } catch (error) {
1085
+ console.error(
1086
+ '❌ Error:',
1087
+ error instanceof Error ? error.message : String(error)
1088
+ );
1089
+ if (options.debug && error instanceof Error && error.stack) {
1090
+ console.error('\nStack trace:');
1091
+ console.error(error.stack);
1092
+ }
1093
+ process.exit(1);
1094
+ }
1095
+ }
1096
+
1097
+ /**
1098
+ * Transfer domain ownership to a new owner
1099
+ */
1100
+ async function transferDomain(
1101
+ domain: string,
1102
+ options: CliOptions
1103
+ ): Promise<void> {
1104
+ try {
1105
+ const privateKeyOrMnemonic = getPrivateKeyOrMnemonic();
1106
+ const didConsole = getConsole(options);
1107
+
1108
+ // Initialize with current owner's signer
1109
+ await didConsole.setSigner(privateKeyOrMnemonic);
1110
+
1111
+ const domainContext = didConsole.domain(domain);
1112
+
1113
+ // Get or generate mnemonic for new owner
1114
+ let mnemonic: string;
1115
+ const newOwnerMnemonic = process.env.MNEMONIC;
1116
+
1117
+ if (newOwnerMnemonic) {
1118
+ mnemonic = newOwnerMnemonic;
1119
+ console.log(
1120
+ '📝 Using mnemonic from MNEMONIC environment variable for new owner'
1121
+ );
1122
+ } else {
1123
+ const wordCount = options.words || 12;
1124
+ if (![12, 15, 18, 21, 24].includes(wordCount)) {
1125
+ console.error(
1126
+ '❌ Error: Word count must be one of: 12, 15, 18, 21, 24'
1127
+ );
1128
+ process.exit(1);
1129
+ }
1130
+ mnemonic = generateMnemonic(wordCount);
1131
+
1132
+ // Save mnemonic to file
1133
+ const mnemonicFile = './transfer-new-owner-mnemonic.txt';
1134
+ fs.writeFileSync(mnemonicFile, mnemonic, 'utf-8');
1135
+
1136
+ console.log(
1137
+ `🔑 Generated ${wordCount}-word mnemonic for new owner`
1138
+ );
1139
+ console.log(`📄 Mnemonic saved to: ${mnemonicFile}`);
1140
+ console.log('\n⚠️ SECURITY WARNING:');
1141
+ console.log(
1142
+ ' • Keep this mnemonic secure! It controls the domain!'
1143
+ );
1144
+ console.log(
1145
+ ' • Store it in a safe place (paper backup, hardware wallet, etc.)'
1146
+ );
1147
+ console.log(
1148
+ ' • Delete the file after copying to a secure location'
1149
+ );
1150
+ console.log(` • Command: rm ${mnemonicFile}`);
1151
+ console.log('\n💡 To use this mnemonic for future operations:');
1152
+ console.log(` export MNEMONIC="$(cat ${mnemonicFile})"\n`);
1153
+ }
1154
+
1155
+ // Transfer domain
1156
+ console.log(`📋 Transferring domain: ${domain}`);
1157
+ console.log('⏳ Submitting transactions...\n');
1158
+
1159
+ const result = await domainContext.transfer(mnemonic);
1160
+
1161
+ if (result.success) {
1162
+ if (options.json) {
1163
+ console.log(
1164
+ JSON.stringify(
1165
+ {
1166
+ success: true,
1167
+ domain: domain,
1168
+ newOwner: result.data.newOwner,
1169
+ newDid: result.data.newDid,
1170
+ transferTxHash: result.data.transferTxHash,
1171
+ setDidTxHash: result.data.setDidTxHash
1172
+ },
1173
+ null,
1174
+ 2
1175
+ )
1176
+ );
1177
+ } else {
1178
+ console.log('✅ Domain transfer successful!\n');
1179
+ console.log('Transaction Details:');
1180
+ console.log(' Domain:', domain);
1181
+ console.log(' New Owner:', result.data.newOwner);
1182
+ console.log(' New DID:', result.data.newDid);
1183
+ console.log(' Transfer Tx:', result.data.transferTxHash);
1184
+ console.log(' Set DID Tx:', result.data.setDidTxHash);
1185
+ if (!newOwnerMnemonic) {
1186
+ console.log(
1187
+ '\n📝 New owner mnemonic saved to: ./transfer-new-owner-mnemonic.txt'
1188
+ );
1189
+ }
1190
+ }
1191
+ } else {
1192
+ console.error('❌ Transfer failed:', result.error);
1193
+ process.exit(1);
1194
+ }
1195
+ } catch (error) {
1196
+ console.error('❌ Failed:', error);
1197
+ if (error instanceof Error) {
1198
+ console.error(' Message:', error.message);
1199
+ }
1200
+ process.exit(1);
1201
+ }
1202
+ }
1203
+
1204
+ /**
1205
+ * Crypto utility: Generate mnemonic
1206
+ */
1207
+ async function cryptoGenerate(options: CliOptions): Promise<void> {
1208
+ try {
1209
+ const wordCount = options.words || 12;
1210
+ if (![12, 15, 18, 21, 24].includes(wordCount)) {
1211
+ console.error(
1212
+ '❌ Error: Word count must be one of: 12, 15, 18, 21, 24'
1213
+ );
1214
+ process.exit(1);
1215
+ }
1216
+
1217
+ const mnemonic = generateMnemonic(wordCount);
1218
+
1219
+ // Save mnemonic to file
1220
+ const outputFile = options.output || './mnemonic.txt';
1221
+ fs.writeFileSync(outputFile, mnemonic, 'utf-8');
1222
+
1223
+ if (options.json) {
1224
+ console.log(
1225
+ JSON.stringify(
1226
+ {
1227
+ words: wordCount,
1228
+ savedTo: outputFile
1229
+ },
1230
+ null,
1231
+ 2
1232
+ )
1233
+ );
1234
+ } else {
1235
+ console.log(`🔑 Generated ${wordCount}-word mnemonic`);
1236
+ console.log(`📄 Mnemonic saved to: ${outputFile}`);
1237
+ console.log('\n⚠️ SECURITY WARNING:');
1238
+ console.log(
1239
+ ' • Keep this mnemonic secure! It controls all derived keys!'
1240
+ );
1241
+ console.log(
1242
+ ' • Store it in a safe place (paper backup, hardware wallet, etc.)'
1243
+ );
1244
+ console.log(
1245
+ ' • Delete the file after copying to a secure location'
1246
+ );
1247
+ console.log(` • Command: rm ${outputFile}`);
1248
+ console.log('\n💡 To use the mnemonic:');
1249
+ console.log(` export MNEMONIC="$(cat ${outputFile})"`);
1250
+ }
1251
+ } catch (error) {
1252
+ console.error('❌ Failed:', error);
1253
+ if (error instanceof Error) {
1254
+ console.error(' Message:', error.message);
1255
+ }
1256
+ process.exit(1);
1257
+ }
1258
+ }
1259
+
1260
+ /**
1261
+ * Get mnemonic from environment variable
1262
+ */
1263
+ function getMnemonic(): string {
1264
+ const mnemonic = process.env.MNEMONIC;
1265
+ if (!mnemonic) {
1266
+ console.error('❌ Error: MNEMONIC environment variable is required');
1267
+ console.error('Please set MNEMONIC environment variable');
1268
+ console.error(
1269
+ 'Example: export MNEMONIC="your twelve word mnemonic phrase here"'
1270
+ );
1271
+ console.error(
1272
+ '\n⚠️ Important: Always use quotes for mnemonic phrases!'
1273
+ );
1274
+ process.exit(1);
1275
+ }
1276
+ return mnemonic;
1277
+ }
1278
+
1279
+ /**
1280
+ * Crypto utility: Get Ethereum address from mnemonic
1281
+ */
1282
+ async function cryptoGetAddress(options: CliOptions): Promise<void> {
1283
+ try {
1284
+ const mnemonic = getMnemonic();
1285
+ const address = await getEthereumAddressFromMnemonic(mnemonic);
1286
+
1287
+ if (options.json) {
1288
+ console.log(
1289
+ JSON.stringify(
1290
+ {
1291
+ address: address
1292
+ },
1293
+ null,
1294
+ 2
1295
+ )
1296
+ );
1297
+ } else {
1298
+ console.log('🏠 Ethereum Address:');
1299
+ console.log(` ${address}`);
1300
+ }
1301
+ } catch (error) {
1302
+ console.error('❌ Failed:', error);
1303
+ if (error instanceof Error) {
1304
+ console.error(' Message:', error.message);
1305
+ }
1306
+ process.exit(1);
1307
+ }
1308
+ }
1309
+
1310
+ /**
1311
+ * Crypto utility: Get DID from mnemonic
1312
+ */
1313
+ async function cryptoGetDID(options: CliOptions): Promise<void> {
1314
+ try {
1315
+ const mnemonic = getMnemonic();
1316
+ const did = await getDIDFromMnemonic(mnemonic);
1317
+
1318
+ if (options.json) {
1319
+ console.log(
1320
+ JSON.stringify(
1321
+ {
1322
+ did: did
1323
+ },
1324
+ null,
1325
+ 2
1326
+ )
1327
+ );
1328
+ } else {
1329
+ console.log('🆔 DID:');
1330
+ console.log(` ${did}`);
1331
+ }
1332
+ } catch (error) {
1333
+ console.error('❌ Failed:', error);
1334
+ if (error instanceof Error) {
1335
+ console.error(' Message:', error.message);
1336
+ }
1337
+ process.exit(1);
1338
+ }
1339
+ }
1340
+
1341
+ /**
1342
+ * Crypto utility: Get EVM private key from mnemonic
1343
+ */
1344
+ async function cryptoGetPrivateKey(options: CliOptions): Promise<void> {
1345
+ try {
1346
+ const mnemonic = getMnemonic();
1347
+ const privateKey = await getEVMPrivateKeyFromMnemonic(mnemonic);
1348
+
1349
+ // Save private key to file
1350
+ const outputFile = options.output || './private-key.txt';
1351
+ fs.writeFileSync(outputFile, privateKey, 'utf-8');
1352
+
1353
+ if (options.json) {
1354
+ console.log(
1355
+ JSON.stringify(
1356
+ {
1357
+ savedTo: outputFile
1358
+ },
1359
+ null,
1360
+ 2
1361
+ )
1362
+ );
1363
+ } else {
1364
+ console.log('🔑 EVM Private Key generated');
1365
+ console.log(`📄 Private key saved to: ${outputFile}`);
1366
+ console.log('\n⚠️ SECURITY WARNING:');
1367
+ console.log(' • Keep this private key secure! Never share it!');
1368
+ console.log(
1369
+ ' • Delete the file after copying to a secure location'
1370
+ );
1371
+ console.log(` • Command: rm ${outputFile}`);
1372
+ }
1373
+ } catch (error) {
1374
+ console.error('❌ Failed:', error);
1375
+ if (error instanceof Error) {
1376
+ console.error(' Message:', error.message);
1377
+ }
1378
+ process.exit(1);
1379
+ }
1380
+ }
1381
+
1382
+ /**
1383
+ * Crypto utility: Derive all keys from mnemonic
1384
+ */
1385
+ async function cryptoDerive(options: CliOptions): Promise<void> {
1386
+ try {
1387
+ const mnemonic = getMnemonic();
1388
+ const { owner, did } = await deriveDIDFromMnemonic(mnemonic);
1389
+ const privateKey = await getEVMPrivateKeyFromMnemonic(mnemonic);
1390
+
1391
+ // Save all keys to file
1392
+ const outputFile = options.output || './derived-keys.txt';
1393
+ const fileContent = `Ethereum Address: ${owner}\nDID: ${did}\nPrivate Key: ${privateKey}`;
1394
+ fs.writeFileSync(outputFile, fileContent, 'utf-8');
1395
+
1396
+ if (options.json) {
1397
+ console.log(
1398
+ JSON.stringify(
1399
+ {
1400
+ address: owner,
1401
+ did: did,
1402
+ savedTo: outputFile
1403
+ },
1404
+ null,
1405
+ 2
1406
+ )
1407
+ );
1408
+ } else {
1409
+ console.log('🔐 Derived Keys:');
1410
+ console.log(' Address:', owner);
1411
+ console.log(' DID:', did);
1412
+ console.log(`\n📄 All keys saved to: ${outputFile}`);
1413
+ console.log('\n⚠️ SECURITY WARNING:');
1414
+ console.log(' • Keep these credentials secure! Never share them!');
1415
+ console.log(
1416
+ ' • Private key included in file - handle with extreme care!'
1417
+ );
1418
+ console.log(
1419
+ ' • Delete the file after copying to a secure location'
1420
+ );
1421
+ console.log(` • Command: rm ${outputFile}`);
1422
+ }
1423
+ } catch (error) {
1424
+ console.error('❌ Failed:', error);
1425
+ if (error instanceof Error) {
1426
+ console.error(' Message:', error.message);
1427
+ }
1428
+ process.exit(1);
1429
+ }
1430
+ }
1431
+
1432
+ // ============================================================================
1433
+ // Wallet Management Commands
1434
+ // ============================================================================
1435
+
1436
+ async function walletEvm(
1437
+ action: string,
1438
+ domain: string,
1439
+ options: CliOptions
1440
+ ): Promise<void> {
1441
+ try {
1442
+ const didConsole = getConsole(options);
1443
+ const domainContext = didConsole.domain(domain);
1444
+
1445
+ switch (action) {
1446
+ case 'add': {
1447
+ const evmPrivateKey = process.env.EVM_PRIVATE_KEY;
1448
+ if (!evmPrivateKey) {
1449
+ console.error(
1450
+ '❌ Error: EVM_PRIVATE_KEY environment variable is required'
1451
+ );
1452
+ console.error(
1453
+ ' Set it with: export EVM_PRIVATE_KEY="0x..."'
1454
+ );
1455
+ process.exit(1);
1456
+ }
1457
+
1458
+ const privateKeyOrMnemonic = getPrivateKeyOrMnemonic();
1459
+ await didConsole.setSigner(privateKeyOrMnemonic);
1460
+
1461
+ console.log(`\n💼 Adding EVM wallet to domain: ${domain}`);
1462
+ const result = await domainContext.addEVMWallet(evmPrivateKey);
1463
+
1464
+ if (result.success) {
1465
+ if (options.json) {
1466
+ console.log(
1467
+ JSON.stringify(
1468
+ {
1469
+ success: true,
1470
+ domain,
1471
+ address: result.data?.address,
1472
+ transactionHash: result.transactionHash,
1473
+ gasUsed: result.gasUsed?.toString(),
1474
+ blockNumber: result.blockNumber
1475
+ },
1476
+ null,
1477
+ 2
1478
+ )
1479
+ );
1480
+ } else {
1481
+ console.log(`\n✅ EVM wallet added successfully!`);
1482
+ console.log(` Address: ${result.data?.address}`);
1483
+ console.log(
1484
+ ` Transaction: ${result.transactionHash}`
1485
+ );
1486
+ console.log(
1487
+ ` Gas used: ${result.gasUsed?.toString()}`
1488
+ );
1489
+ }
1490
+ } else {
1491
+ console.error(`\n❌ Failed: ${result.error}`);
1492
+ process.exit(1);
1493
+ }
1494
+ break;
1495
+ }
1496
+
1497
+ case 'remove': {
1498
+ const evmPrivateKey = process.env.EVM_PRIVATE_KEY;
1499
+ if (!evmPrivateKey) {
1500
+ console.error(
1501
+ '❌ Error: EVM_PRIVATE_KEY environment variable is required'
1502
+ );
1503
+ console.error(
1504
+ ' Set it with: export EVM_PRIVATE_KEY="0x..."'
1505
+ );
1506
+ process.exit(1);
1507
+ }
1508
+
1509
+ const privateKeyOrMnemonic = getPrivateKeyOrMnemonic();
1510
+ await didConsole.setSigner(privateKeyOrMnemonic);
1511
+
1512
+ console.log(`\n🗑️ Removing EVM wallet from domain: ${domain}`);
1513
+ const result = await domainContext.removeEVMWallet(
1514
+ evmPrivateKey
1515
+ );
1516
+
1517
+ if (result.success) {
1518
+ if (options.json) {
1519
+ console.log(
1520
+ JSON.stringify(
1521
+ {
1522
+ success: true,
1523
+ domain,
1524
+ address: result.data?.address,
1525
+ transactionHash: result.transactionHash,
1526
+ gasUsed: result.gasUsed?.toString(),
1527
+ blockNumber: result.blockNumber
1528
+ },
1529
+ null,
1530
+ 2
1531
+ )
1532
+ );
1533
+ } else {
1534
+ console.log(`\n✅ EVM wallet removed successfully!`);
1535
+ console.log(` Address: ${result.data?.address}`);
1536
+ console.log(
1537
+ ` Transaction: ${result.transactionHash}`
1538
+ );
1539
+ }
1540
+ } else {
1541
+ console.error(`\n❌ Failed: ${result.error}`);
1542
+ process.exit(1);
1543
+ }
1544
+ break;
1545
+ }
1546
+
1547
+ case 'list': {
1548
+ console.log(`\n📋 Listing EVM wallets for domain: ${domain}`);
1549
+ const wallets = await domainContext.getEVMWallets();
1550
+
1551
+ if (options.json) {
1552
+ console.log(
1553
+ JSON.stringify(
1554
+ {
1555
+ domain,
1556
+ wallets,
1557
+ count: wallets.length
1558
+ },
1559
+ null,
1560
+ 2
1561
+ )
1562
+ );
1563
+ } else {
1564
+ if (wallets.length === 0) {
1565
+ console.log(` No EVM wallets found`);
1566
+ } else {
1567
+ console.log(` Found ${wallets.length} wallet(s):`);
1568
+ wallets.forEach((addr, i) => {
1569
+ console.log(` ${i + 1}. ${addr}`);
1570
+ });
1571
+ }
1572
+ }
1573
+ break;
1574
+ }
1575
+
1576
+ default:
1577
+ console.error(`❌ Unknown action: ${action}`);
1578
+ console.error(' Valid actions: add, remove, list');
1579
+ process.exit(1);
1580
+ }
1581
+ } catch (error) {
1582
+ console.error(
1583
+ '❌ Error:',
1584
+ error instanceof Error ? error.message : String(error)
1585
+ );
1586
+ process.exit(1);
1587
+ }
1588
+ }
1589
+
1590
+ async function walletSolana(
1591
+ action: string,
1592
+ domain: string,
1593
+ options: CliOptions
1594
+ ): Promise<void> {
1595
+ try {
1596
+ const didConsole = getConsole(options);
1597
+ const domainContext = didConsole.domain(domain);
1598
+
1599
+ switch (action) {
1600
+ case 'add': {
1601
+ const solanaPrivateKey = process.env.SOLANA_PRIVATE_KEY;
1602
+ if (!solanaPrivateKey) {
1603
+ console.error(
1604
+ '❌ Error: SOLANA_PRIVATE_KEY environment variable is required'
1605
+ );
1606
+ console.error(
1607
+ ' Set it with: export SOLANA_PRIVATE_KEY="[1,2,3,...]"'
1608
+ );
1609
+ console.error(
1610
+ ' Or: export SOLANA_PRIVATE_KEY="base58string"'
1611
+ );
1612
+ process.exit(1);
1613
+ }
1614
+
1615
+ const privateKeyOrMnemonic = getPrivateKeyOrMnemonic();
1616
+ await didConsole.setSigner(privateKeyOrMnemonic);
1617
+
1618
+ console.log(`\n💼 Adding Solana wallet to domain: ${domain}`);
1619
+ const result = await domainContext.addSolanaWallet(
1620
+ solanaPrivateKey
1621
+ );
1622
+
1623
+ if (result.success) {
1624
+ if (options.json) {
1625
+ console.log(
1626
+ JSON.stringify(
1627
+ {
1628
+ success: true,
1629
+ domain,
1630
+ address: result.data?.address,
1631
+ addressHex: result.data?.addressHex,
1632
+ transactionHash: result.transactionHash,
1633
+ gasUsed: result.gasUsed?.toString(),
1634
+ blockNumber: result.blockNumber
1635
+ },
1636
+ null,
1637
+ 2
1638
+ )
1639
+ );
1640
+ } else {
1641
+ console.log(`\n✅ Solana wallet added successfully!`);
1642
+ console.log(` Address: ${result.data?.address}`);
1643
+ console.log(
1644
+ ` Transaction: ${result.transactionHash}`
1645
+ );
1646
+ console.log(
1647
+ ` Gas used: ${result.gasUsed?.toString()}`
1648
+ );
1649
+ }
1650
+ } else {
1651
+ console.error(`\n❌ Failed: ${result.error}`);
1652
+ process.exit(1);
1653
+ }
1654
+ break;
1655
+ }
1656
+
1657
+ case 'remove': {
1658
+ const solanaPrivateKey = process.env.SOLANA_PRIVATE_KEY;
1659
+ if (!solanaPrivateKey) {
1660
+ console.error(
1661
+ '❌ Error: SOLANA_PRIVATE_KEY environment variable is required'
1662
+ );
1663
+ console.error(
1664
+ ' Set it with: export SOLANA_PRIVATE_KEY="[1,2,3,...]"'
1665
+ );
1666
+ console.error(
1667
+ ' Or: export SOLANA_PRIVATE_KEY="base58string"'
1668
+ );
1669
+ process.exit(1);
1670
+ }
1671
+
1672
+ const privateKeyOrMnemonic = getPrivateKeyOrMnemonic();
1673
+ await didConsole.setSigner(privateKeyOrMnemonic);
1674
+
1675
+ console.log(
1676
+ `\n🗑️ Removing Solana wallet from domain: ${domain}`
1677
+ );
1678
+ const result = await domainContext.removeSolanaWallet(
1679
+ solanaPrivateKey
1680
+ );
1681
+
1682
+ if (result.success) {
1683
+ if (options.json) {
1684
+ console.log(
1685
+ JSON.stringify(
1686
+ {
1687
+ success: true,
1688
+ domain,
1689
+ address: result.data?.address,
1690
+ addressHex: result.data?.addressHex,
1691
+ transactionHash: result.transactionHash,
1692
+ gasUsed: result.gasUsed?.toString(),
1693
+ blockNumber: result.blockNumber
1694
+ },
1695
+ null,
1696
+ 2
1697
+ )
1698
+ );
1699
+ } else {
1700
+ console.log(`\n✅ Solana wallet removed successfully!`);
1701
+ console.log(` Address: ${result.data?.address}`);
1702
+ console.log(
1703
+ ` Transaction: ${result.transactionHash}`
1704
+ );
1705
+ }
1706
+ } else {
1707
+ console.error(`\n❌ Failed: ${result.error}`);
1708
+ process.exit(1);
1709
+ }
1710
+ break;
1711
+ }
1712
+
1713
+ case 'list': {
1714
+ console.log(
1715
+ `\n📋 Listing Solana wallets for domain: ${domain}`
1716
+ );
1717
+ const wallets = await domainContext.getSolanaWallets();
1718
+
1719
+ if (options.json) {
1720
+ console.log(
1721
+ JSON.stringify(
1722
+ {
1723
+ domain,
1724
+ wallets,
1725
+ count: wallets.length
1726
+ },
1727
+ null,
1728
+ 2
1729
+ )
1730
+ );
1731
+ } else {
1732
+ if (wallets.length === 0) {
1733
+ console.log(` No Solana wallets found`);
1734
+ } else {
1735
+ console.log(` Found ${wallets.length} wallet(s):`);
1736
+ wallets.forEach((addr, i) => {
1737
+ console.log(` ${i + 1}. ${addr}`);
1738
+ });
1739
+ }
1740
+ }
1741
+ break;
1742
+ }
1743
+
1744
+ default:
1745
+ console.error(`❌ Unknown action: ${action}`);
1746
+ console.error(' Valid actions: add, remove, list');
1747
+ process.exit(1);
1748
+ }
1749
+ } catch (error) {
1750
+ console.error(
1751
+ '❌ Error:',
1752
+ error instanceof Error ? error.message : String(error)
1753
+ );
1754
+ process.exit(1);
1755
+ }
1756
+ }
1757
+
1758
+ // ========================================
1759
+ // Tag Management Functions
1760
+ // ========================================
1761
+
1762
+ /**
1763
+ * Define a simple tag type
1764
+ */
1765
+ async function tagDefine(
1766
+ domain: string,
1767
+ tagName: string,
1768
+ typeString: string,
1769
+ options: CliOptions
1770
+ ): Promise<void> {
1771
+ try {
1772
+ const didConsole = getConsole(options);
1773
+ const privateKeyOrMnemonic = getPrivateKeyOrMnemonic();
1774
+ await didConsole.setSigner(privateKeyOrMnemonic);
1775
+
1776
+ const domainContext = didConsole.domain(domain);
1777
+ const tagCtx = domainContext.tag();
1778
+
1779
+ console.log(`\n📝 Defining tag "${tagName}" for domain: ${domain}`);
1780
+ console.log(` Type: ${typeString}`);
1781
+
1782
+ // Parse type string and create TagTypeBuilder
1783
+ let tagType: TagTypeBuilder;
1784
+
1785
+ // Support simple types: string, uint8, uint256, int8, int256, bool, address, bytes, bytes32
1786
+ // Match pattern: (type)(size)(array)?
1787
+ // Examples: uint8, string[], bytes32[]
1788
+ const match = typeString.match(
1789
+ /^(uint|int|bytes|string|bool|address)(\d*)(\[\])?$/
1790
+ );
1791
+ if (!match) {
1792
+ throw new Error(
1793
+ `Invalid type string: ${typeString}. Supported: string, uint8, uint256, int8, int256, bool, address, bytes, bytes32, and arrays (e.g., string[], uint8[])`
1794
+ );
1795
+ }
1796
+
1797
+ const [, baseType, size, isArray] = match;
1798
+
1799
+ // Create base type
1800
+ switch (baseType) {
1801
+ case 'string':
1802
+ tagType = TagTypeBuilder.string();
1803
+ break;
1804
+ case 'uint':
1805
+ const uintSize = size ? parseInt(size) : 256;
1806
+ tagType = TagTypeBuilder.uint(uintSize);
1807
+ break;
1808
+ case 'int':
1809
+ const intSize = size ? parseInt(size) : 256;
1810
+ tagType = TagTypeBuilder.int(intSize);
1811
+ break;
1812
+ case 'bool':
1813
+ tagType = TagTypeBuilder.bool();
1814
+ break;
1815
+ case 'address':
1816
+ tagType = TagTypeBuilder.address();
1817
+ break;
1818
+ case 'bytes':
1819
+ if (size) {
1820
+ const bytesSize = parseInt(size);
1821
+ tagType = TagTypeBuilder.fixedBytes(bytesSize);
1822
+ } else {
1823
+ tagType = TagTypeBuilder.bytes();
1824
+ }
1825
+ break;
1826
+ default:
1827
+ throw new Error(`Unsupported type: ${baseType}`);
1828
+ }
1829
+
1830
+ // Wrap in array if needed
1831
+ if (isArray) {
1832
+ tagType = TagTypeBuilder.array(tagType);
1833
+ }
1834
+
1835
+ const result = await tagCtx.defineTag(tagName, tagType);
1836
+
1837
+ if (result.success) {
1838
+ if (options.json) {
1839
+ console.log(
1840
+ JSON.stringify(
1841
+ {
1842
+ success: true,
1843
+ domain,
1844
+ tagName,
1845
+ type: typeString,
1846
+ transactionHash: result.transactionHash,
1847
+ gasUsed: result.gasUsed?.toString(),
1848
+ blockNumber: result.blockNumber
1849
+ },
1850
+ null,
1851
+ 2
1852
+ )
1853
+ );
1854
+ } else {
1855
+ console.log(`\n✅ Tag "${tagName}" defined successfully`);
1856
+ console.log(` Transaction: ${result.transactionHash}`);
1857
+ if (options.verbose) {
1858
+ console.log(` Block: ${result.blockNumber}`);
1859
+ console.log(` Gas used: ${result.gasUsed?.toString()}`);
1860
+ }
1861
+ }
1862
+ } else {
1863
+ console.error(`\n❌ Failed to define tag`);
1864
+ process.exit(1);
1865
+ }
1866
+ } catch (error) {
1867
+ console.error(
1868
+ '❌ Error:',
1869
+ error instanceof Error ? error.message : String(error)
1870
+ );
1871
+ process.exit(1);
1872
+ }
1873
+ }
1874
+
1875
+ /**
1876
+ * Set tag value
1877
+ */
1878
+ async function tagSet(
1879
+ domain: string,
1880
+ tagName: string,
1881
+ valueStr: string,
1882
+ options: CliOptions
1883
+ ): Promise<void> {
1884
+ try {
1885
+ const didConsole = getConsole(options);
1886
+ const privateKeyOrMnemonic = getPrivateKeyOrMnemonic();
1887
+ await didConsole.setSigner(privateKeyOrMnemonic);
1888
+
1889
+ const domainContext = didConsole.domain(domain);
1890
+ const tagCtx = domainContext.tag();
1891
+
1892
+ console.log(`\n📝 Setting tag "${tagName}" for domain: ${domain}`);
1893
+ console.log(` Value: ${valueStr}`);
1894
+
1895
+ // Try to parse value as JSON, otherwise use as string
1896
+ let value: any;
1897
+ try {
1898
+ value = JSON.parse(valueStr);
1899
+ } catch {
1900
+ value = valueStr;
1901
+ }
1902
+
1903
+ const result = await tagCtx.setTag(domain, tagName, value);
1904
+
1905
+ if (result.success) {
1906
+ if (options.json) {
1907
+ console.log(
1908
+ JSON.stringify(
1909
+ {
1910
+ success: true,
1911
+ domain,
1912
+ tagName,
1913
+ value,
1914
+ transactionHash: result.transactionHash,
1915
+ gasUsed: result.gasUsed?.toString(),
1916
+ blockNumber: result.blockNumber
1917
+ },
1918
+ null,
1919
+ 2
1920
+ )
1921
+ );
1922
+ } else {
1923
+ console.log(`\n✅ Tag "${tagName}" set successfully`);
1924
+ console.log(` Transaction: ${result.transactionHash}`);
1925
+ if (options.verbose) {
1926
+ console.log(` Block: ${result.blockNumber}`);
1927
+ console.log(` Gas used: ${result.gasUsed?.toString()}`);
1928
+ }
1929
+ }
1930
+ } else {
1931
+ console.error(`\n❌ Failed to set tag`);
1932
+ process.exit(1);
1933
+ }
1934
+ } catch (error) {
1935
+ console.error(
1936
+ '❌ Error:',
1937
+ error instanceof Error ? error.message : String(error)
1938
+ );
1939
+ process.exit(1);
1940
+ }
1941
+ }
1942
+
1943
+ /**
1944
+ * Get tag value
1945
+ */
1946
+ async function tagGet(
1947
+ domain: string,
1948
+ tagName: string,
1949
+ options: CliOptions
1950
+ ): Promise<void> {
1951
+ try {
1952
+ const didConsole = getConsole(options);
1953
+ const domainContext = didConsole.domain(domain);
1954
+ const tagCtx = domainContext.tag();
1955
+
1956
+ console.log(`\n📖 Getting tag "${tagName}" for domain: ${domain}`);
1957
+
1958
+ const value = await tagCtx.getTag(domain, tagName);
1959
+
1960
+ if (value !== null && value !== undefined) {
1961
+ if (options.json) {
1962
+ console.log(
1963
+ JSON.stringify(
1964
+ {
1965
+ success: true,
1966
+ domain,
1967
+ tagName,
1968
+ value
1969
+ },
1970
+ (_, v) => (typeof v === 'bigint' ? v.toString() : v),
1971
+ 2
1972
+ )
1973
+ );
1974
+ } else {
1975
+ console.log(`\n✅ Tag value:`);
1976
+ if (typeof value === 'object') {
1977
+ console.log(
1978
+ ` ${JSON.stringify(
1979
+ value,
1980
+ (_, v) =>
1981
+ typeof v === 'bigint' ? v.toString() : v,
1982
+ 2
1983
+ )}`
1984
+ );
1985
+ } else {
1986
+ console.log(` ${value}`);
1987
+ }
1988
+ }
1989
+ } else {
1990
+ if (options.json) {
1991
+ console.log(
1992
+ JSON.stringify({
1993
+ success: true,
1994
+ domain,
1995
+ tagName,
1996
+ value: null
1997
+ })
1998
+ );
1999
+ } else {
2000
+ console.log(
2001
+ `\n Tag "${tagName}" has no value (or does not exist)`
2002
+ );
2003
+ }
2004
+ }
2005
+ } catch (error) {
2006
+ console.error(
2007
+ '❌ Error:',
2008
+ error instanceof Error ? error.message : String(error)
2009
+ );
2010
+ process.exit(1);
2011
+ }
2012
+ }
2013
+
2014
+ /**
2015
+ * Remove tag value
2016
+ */
2017
+ async function tagRemove(
2018
+ domain: string,
2019
+ tagName: string,
2020
+ options: CliOptions
2021
+ ): Promise<void> {
2022
+ try {
2023
+ const didConsole = getConsole(options);
2024
+ const privateKeyOrMnemonic = getPrivateKeyOrMnemonic();
2025
+ await didConsole.setSigner(privateKeyOrMnemonic);
2026
+
2027
+ const domainContext = didConsole.domain(domain);
2028
+ const tagCtx = domainContext.tag();
2029
+
2030
+ console.log(`\n🗑️ Removing tag "${tagName}" for domain: ${domain}`);
2031
+
2032
+ const result = await tagCtx.removeTag(domain, tagName);
2033
+
2034
+ if (result.success) {
2035
+ if (options.json) {
2036
+ console.log(
2037
+ JSON.stringify(
2038
+ {
2039
+ success: true,
2040
+ domain,
2041
+ tagName,
2042
+ transactionHash: result.transactionHash,
2043
+ gasUsed: result.gasUsed?.toString(),
2044
+ blockNumber: result.blockNumber
2045
+ },
2046
+ null,
2047
+ 2
2048
+ )
2049
+ );
2050
+ } else {
2051
+ console.log(`\n✅ Tag "${tagName}" value removed successfully`);
2052
+ console.log(` Transaction: ${result.transactionHash}`);
2053
+ if (options.verbose) {
2054
+ console.log(` Block: ${result.blockNumber}`);
2055
+ console.log(` Gas used: ${result.gasUsed?.toString()}`);
2056
+ }
2057
+ }
2058
+ } else {
2059
+ console.error(`\n❌ Failed to remove tag`);
2060
+ process.exit(1);
2061
+ }
2062
+ } catch (error) {
2063
+ console.error(
2064
+ '❌ Error:',
2065
+ error instanceof Error ? error.message : String(error)
2066
+ );
2067
+ process.exit(1);
2068
+ }
2069
+ }
2070
+
2071
+ /**
2072
+ * List all tags for a domain
2073
+ */
2074
+ async function tagList(domain: string, options: CliOptions): Promise<void> {
2075
+ try {
2076
+ const didConsole = getConsole(options);
2077
+ const domainContext = didConsole.domain(domain);
2078
+
2079
+ console.log(`\n📋 Listing all tags for domain: ${domain}`);
2080
+
2081
+ const tags = await domainContext.getAllTags();
2082
+
2083
+ if (options.json) {
2084
+ console.log(
2085
+ JSON.stringify(
2086
+ {
2087
+ success: true,
2088
+ domain,
2089
+ count: tags.length,
2090
+ tags
2091
+ },
2092
+ (_, v) => (typeof v === 'bigint' ? v.toString() : v),
2093
+ 2
2094
+ )
2095
+ );
2096
+ } else {
2097
+ console.log(`\n✅ Found ${tags.length} tags:\n`);
2098
+ for (const tag of tags) {
2099
+ console.log(` Tag: "${tag.name}"`);
2100
+ if (typeof tag.value === 'object' && tag.value !== null) {
2101
+ console.log(
2102
+ ` Value: ${JSON.stringify(tag.value, (_, v) =>
2103
+ typeof v === 'bigint' ? v.toString() : v
2104
+ )}`
2105
+ );
2106
+ } else {
2107
+ console.log(` Value: ${tag.value}`);
2108
+ }
2109
+ }
2110
+ }
2111
+ } catch (error) {
2112
+ console.error(
2113
+ '❌ Error:',
2114
+ error instanceof Error ? error.message : String(error)
2115
+ );
2116
+ process.exit(1);
2117
+ }
2118
+ }
2119
+
2120
+ /**
2121
+ * List all defined tag types for a domain
2122
+ */
2123
+ async function tagListDefined(
2124
+ domain: string,
2125
+ options: CliOptions
2126
+ ): Promise<void> {
2127
+ try {
2128
+ const didConsole = getConsole(options);
2129
+ const domainContext = didConsole.domain(domain);
2130
+
2131
+ console.log(`\n📋 Listing defined tag types for domain: ${domain}`);
2132
+
2133
+ const tags = await domainContext.getDefinedTags();
2134
+
2135
+ if (options.json) {
2136
+ console.log(
2137
+ JSON.stringify(
2138
+ {
2139
+ success: true,
2140
+ domain,
2141
+ count: tags.length,
2142
+ tags
2143
+ },
2144
+ null,
2145
+ 2
2146
+ )
2147
+ );
2148
+ } else {
2149
+ console.log(`\n✅ Found ${tags.length} defined tag types:\n`);
2150
+ for (const tagName of tags) {
2151
+ console.log(` - ${tagName}`);
2152
+ }
2153
+ }
2154
+ } catch (error) {
2155
+ console.error(
2156
+ '❌ Error:',
2157
+ error instanceof Error ? error.message : String(error)
2158
+ );
2159
+ process.exit(1);
2160
+ }
2161
+ }
2162
+
2163
+ /**
2164
+ * Set tagger for a tag
2165
+ */
2166
+ async function tagSetTagger(
2167
+ domain: string,
2168
+ tagName: string,
2169
+ taggerAddress: string,
2170
+ options: CliOptions
2171
+ ): Promise<void> {
2172
+ try {
2173
+ const didConsole = getConsole(options);
2174
+ const privateKeyOrMnemonic = getPrivateKeyOrMnemonic();
2175
+ await didConsole.setSigner(privateKeyOrMnemonic);
2176
+
2177
+ const domainContext = didConsole.domain(domain);
2178
+
2179
+ console.log(
2180
+ `\n🔐 Setting tagger for tag "${tagName}" in domain: ${domain}`
2181
+ );
2182
+ console.log(` Tagger address: ${taggerAddress}`);
2183
+
2184
+ const result = await domainContext.setTagger(tagName, taggerAddress);
2185
+
2186
+ if (result.success) {
2187
+ if (options.json) {
2188
+ console.log(
2189
+ JSON.stringify(
2190
+ {
2191
+ success: true,
2192
+ domain,
2193
+ tagName,
2194
+ taggerAddress,
2195
+ transactionHash: result.transactionHash,
2196
+ gasUsed: result.gasUsed?.toString(),
2197
+ blockNumber: result.blockNumber
2198
+ },
2199
+ null,
2200
+ 2
2201
+ )
2202
+ );
2203
+ } else {
2204
+ console.log(`\n✅ Tagger set successfully`);
2205
+ console.log(` Transaction: ${result.transactionHash}`);
2206
+ if (options.verbose) {
2207
+ console.log(` Block: ${result.blockNumber}`);
2208
+ console.log(` Gas used: ${result.gasUsed?.toString()}`);
2209
+ }
2210
+ }
2211
+ } else {
2212
+ console.error(`\n❌ Failed to set tagger`);
2213
+ process.exit(1);
2214
+ }
2215
+ } catch (error) {
2216
+ console.error(
2217
+ '❌ Error:',
2218
+ error instanceof Error ? error.message : String(error)
2219
+ );
2220
+ process.exit(1);
2221
+ }
2222
+ }
2223
+
2224
+ /**
2225
+ * Get tagger for a tag
2226
+ */
2227
+ async function tagGetTagger(
2228
+ domain: string,
2229
+ tagName: string,
2230
+ options: CliOptions
2231
+ ): Promise<void> {
2232
+ try {
2233
+ const didConsole = getConsole(options);
2234
+ const domainContext = didConsole.domain(domain);
2235
+ const tagCtx = domainContext.tag();
2236
+
2237
+ console.log(
2238
+ `\n🔍 Getting tagger for tag "${tagName}" in domain: ${domain}`
2239
+ );
2240
+
2241
+ const taggerAddress = await tagCtx.getTagger(tagName);
2242
+
2243
+ if (options.json) {
2244
+ console.log(
2245
+ JSON.stringify(
2246
+ {
2247
+ success: true,
2248
+ domain,
2249
+ tagName,
2250
+ taggerAddress
2251
+ },
2252
+ null,
2253
+ 2
2254
+ )
2255
+ );
2256
+ } else {
2257
+ console.log(`\n✅ Tagger address: ${taggerAddress}`);
2258
+ }
2259
+ } catch (error) {
2260
+ console.error(
2261
+ '❌ Error:',
2262
+ error instanceof Error ? error.message : String(error)
2263
+ );
2264
+ process.exit(1);
2265
+ }
2266
+ }
2267
+
2268
+ async function main(): Promise<void> {
2269
+ const { command, subCommand, domain, value, options } = parseArgs();
2270
+
2271
+ if (options.help || !command || command === 'help') {
2272
+ showHelp();
2273
+ return;
2274
+ }
2275
+
2276
+ // Set debug options
2277
+ if (options.verbose || options.debug) {
2278
+ debug.enable();
2279
+ debug.setLevel(options.debugLevel as any);
2280
+ }
2281
+
2282
+ try {
2283
+ switch (command) {
2284
+ // Query commands
2285
+ case 'info':
2286
+ if (!domain) {
2287
+ console.error('❌ Error: Domain argument is required');
2288
+ console.error('Usage: did-cli info <domain>');
2289
+ process.exit(1);
2290
+ }
2291
+ await getDomainInfo(domain, options);
2292
+ break;
2293
+
2294
+ case 'owner':
2295
+ if (!domain) {
2296
+ console.error('❌ Error: Domain argument is required');
2297
+ console.error('Usage: did-cli owner <domain>');
2298
+ process.exit(1);
2299
+ }
2300
+ await getDomainOwner(domain, options);
2301
+ break;
2302
+
2303
+ case 'is-owner':
2304
+ if (!domain) {
2305
+ console.error('❌ Error: Domain argument is required');
2306
+ console.error('Usage: did-cli is-owner <domain>');
2307
+ console.error(
2308
+ 'Note: Set PRIVATE_KEY_OR_MNEMONIC environment variable first'
2309
+ );
2310
+ process.exit(1);
2311
+ }
2312
+ await checkIsOwner(domain, options);
2313
+ break;
2314
+
2315
+ // Transfer command
2316
+ case 'transfer':
2317
+ if (!domain) {
2318
+ console.error('❌ Error: Domain argument is required');
2319
+ console.error('Usage: did-cli transfer <domain>');
2320
+ console.error('Options: --words 12|24 (default: 12)');
2321
+ console.error(
2322
+ 'Note: Set PRIVATE_KEY_OR_MNEMONIC environment variable first'
2323
+ );
2324
+ console.error(
2325
+ 'Optional: Set MNEMONIC environment variable for new owner (auto-generates if not set)'
2326
+ );
2327
+ process.exit(1);
2328
+ }
2329
+ await transferDomain(domain, options);
2330
+ break;
2331
+
2332
+ // RSA commands
2333
+ case 'rsa':
2334
+ if (!subCommand) {
2335
+ console.error('❌ Error: RSA subcommand is required');
2336
+ console.error(
2337
+ 'Usage: did-cli rsa <generate|get|set|remove> [args]'
2338
+ );
2339
+ process.exit(1);
2340
+ }
2341
+ switch (subCommand) {
2342
+ case 'generate':
2343
+ await generateRsaKey(options);
2344
+ break;
2345
+ case 'get':
2346
+ if (!domain) {
2347
+ console.error(
2348
+ '❌ Error: Domain argument is required'
2349
+ );
2350
+ process.exit(1);
2351
+ }
2352
+ await getRsaKey(domain, options);
2353
+ break;
2354
+ case 'set':
2355
+ if (!domain || !value) {
2356
+ console.error(
2357
+ '❌ Error: Domain and PEM file path are required'
2358
+ );
2359
+ console.error(
2360
+ 'Usage: did-cli rsa set <domain> <pem-file>'
2361
+ );
2362
+ console.error(
2363
+ 'Note: Set PRIVATE_KEY_OR_MNEMONIC environment variable first'
2364
+ );
2365
+ process.exit(1);
2366
+ }
2367
+ await setRsaKey(domain, value, options);
2368
+ break;
2369
+ case 'remove':
2370
+ if (!domain) {
2371
+ console.error(
2372
+ '❌ Error: Domain argument is required'
2373
+ );
2374
+ process.exit(1);
2375
+ }
2376
+ await removeRsaKey(domain, options);
2377
+ break;
2378
+ default:
2379
+ console.error(
2380
+ `❌ Unknown RSA subcommand: ${subCommand}`
2381
+ );
2382
+ process.exit(1);
2383
+ }
2384
+ break;
2385
+
2386
+ // IP commands
2387
+ case 'ip':
2388
+ if (!subCommand) {
2389
+ console.error('❌ Error: IP subcommand is required');
2390
+ console.error('Usage: did-cli ip <get|set|remove> [args]');
2391
+ process.exit(1);
2392
+ }
2393
+ switch (subCommand) {
2394
+ case 'get':
2395
+ if (!domain) {
2396
+ console.error(
2397
+ '❌ Error: Domain argument is required'
2398
+ );
2399
+ process.exit(1);
2400
+ }
2401
+ await getIP(domain, options);
2402
+ break;
2403
+ case 'set':
2404
+ if (!domain || !value) {
2405
+ console.error(
2406
+ '❌ Error: Domain and IP address are required'
2407
+ );
2408
+ console.error(
2409
+ 'Usage: did-cli ip set <domain> <ip-address>'
2410
+ );
2411
+ console.error(
2412
+ 'Note: Set PRIVATE_KEY_OR_MNEMONIC environment variable first'
2413
+ );
2414
+ process.exit(1);
2415
+ }
2416
+ await setIP(domain, value, options);
2417
+ break;
2418
+ case 'remove':
2419
+ if (!domain) {
2420
+ console.error(
2421
+ '❌ Error: Domain argument is required'
2422
+ );
2423
+ process.exit(1);
2424
+ }
2425
+ await removeIP(domain, options);
2426
+ break;
2427
+ default:
2428
+ console.error(
2429
+ `❌ Unknown IP subcommand: ${subCommand}`
2430
+ );
2431
+ process.exit(1);
2432
+ }
2433
+ break;
2434
+
2435
+ // Subdomain commands
2436
+ case 'subdomain':
2437
+ if (!subCommand) {
2438
+ console.error('❌ Error: Subdomain subcommand is required');
2439
+ console.error(
2440
+ 'Usage: did-cli subdomain <register> <parent-domain> <subdomain-label>'
2441
+ );
2442
+ process.exit(1);
2443
+ }
2444
+ switch (subCommand) {
2445
+ case 'register':
2446
+ if (!domain || !value) {
2447
+ console.error(
2448
+ '❌ Error: Parent domain and subdomain label are required'
2449
+ );
2450
+ console.error(
2451
+ 'Usage: did-cli subdomain register <parent-domain> <subdomain-label>'
2452
+ );
2453
+ console.error(
2454
+ 'Options: --words 12|24 (default: 12)'
2455
+ );
2456
+ console.error(
2457
+ 'Note: Set PRIVATE_KEY_OR_MNEMONIC environment variable first'
2458
+ );
2459
+ console.error(
2460
+ 'Optional: Set MNEMONIC environment variable for subdomain owner (auto-generates if not set)'
2461
+ );
2462
+ process.exit(1);
2463
+ }
2464
+ await registerSubdomain(domain, value, options);
2465
+ break;
2466
+ default:
2467
+ console.error(
2468
+ `❌ Unknown subdomain subcommand: ${subCommand}`
2469
+ );
2470
+ process.exit(1);
2471
+ }
2472
+ break;
2473
+
2474
+ // Operator commands
2475
+ case 'operator':
2476
+ if (!subCommand) {
2477
+ console.error('❌ Error: Operator subcommand is required');
2478
+ console.error('Usage: did-cli operator <get|set> [args]');
2479
+ process.exit(1);
2480
+ }
2481
+ switch (subCommand) {
2482
+ case 'get':
2483
+ await getOperator(options);
2484
+ break;
2485
+ case 'set':
2486
+ if (!domain) {
2487
+ console.error(
2488
+ '❌ Error: Operator address is required'
2489
+ );
2490
+ console.error(
2491
+ 'Usage: did-cli operator set <address>'
2492
+ );
2493
+ console.error(
2494
+ 'Note: Set PRIVATE_KEY_OR_MNEMONIC environment variable first'
2495
+ );
2496
+ process.exit(1);
2497
+ }
2498
+ await setOperator(domain, options);
2499
+ break;
2500
+ default:
2501
+ console.error(
2502
+ `❌ Unknown operator subcommand: ${subCommand}`
2503
+ );
2504
+ process.exit(1);
2505
+ }
2506
+ break;
2507
+
2508
+ // Crypto utilities
2509
+ case 'crypto':
2510
+ if (!subCommand) {
2511
+ console.error('❌ Error: Crypto subcommand is required');
2512
+ console.error(
2513
+ 'Usage: did-cli crypto <generate|address|did|privatekey|derive> [mnemonic]'
2514
+ );
2515
+ process.exit(1);
2516
+ }
2517
+ switch (subCommand) {
2518
+ case 'generate':
2519
+ await cryptoGenerate(options);
2520
+ break;
2521
+ case 'address':
2522
+ await cryptoGetAddress(options);
2523
+ break;
2524
+ case 'did':
2525
+ await cryptoGetDID(options);
2526
+ break;
2527
+ case 'privatekey':
2528
+ await cryptoGetPrivateKey(options);
2529
+ break;
2530
+ case 'derive':
2531
+ await cryptoDerive(options);
2532
+ break;
2533
+ default:
2534
+ console.error(
2535
+ `❌ Unknown crypto subcommand: ${subCommand}`
2536
+ );
2537
+ console.error(
2538
+ 'Available: generate, address, did, privatekey, derive'
2539
+ );
2540
+ process.exit(1);
2541
+ }
2542
+ break;
2543
+
2544
+ // Conversion utilities
2545
+ case 'convert':
2546
+ if (!subCommand) {
2547
+ console.error('❌ Error: Conversion type is required');
2548
+ console.error(
2549
+ 'Usage: did-cli convert <pem-to-der|der-to-pem|ip-to-bytes|bytes-to-ip> <value>'
2550
+ );
2551
+ process.exit(1);
2552
+ }
2553
+ switch (subCommand) {
2554
+ case 'pem-to-der':
2555
+ if (!domain) {
2556
+ console.error(
2557
+ '❌ Error: PEM file path is required'
2558
+ );
2559
+ process.exit(1);
2560
+ }
2561
+ convertPemToDer(domain, options);
2562
+ break;
2563
+ case 'der-to-pem':
2564
+ if (!domain) {
2565
+ console.error(
2566
+ '❌ Error: DER hex string is required'
2567
+ );
2568
+ process.exit(1);
2569
+ }
2570
+ convertDerToPem(domain, options);
2571
+ break;
2572
+ case 'ip-to-bytes':
2573
+ if (!domain) {
2574
+ console.error('❌ Error: IP address is required');
2575
+ process.exit(1);
2576
+ }
2577
+ convertIpToBytes(domain, options);
2578
+ break;
2579
+ case 'bytes-to-ip':
2580
+ if (!domain) {
2581
+ console.error(
2582
+ '❌ Error: Bytes4 hex string is required'
2583
+ );
2584
+ process.exit(1);
2585
+ }
2586
+ convertBytesToIp(domain, options);
2587
+ break;
2588
+ default:
2589
+ console.error(
2590
+ `❌ Unknown conversion type: ${subCommand}`
2591
+ );
2592
+ process.exit(1);
2593
+ }
2594
+ break;
2595
+
2596
+ case 'wallet':
2597
+ if (!subCommand) {
2598
+ console.error('❌ Error: Wallet subcommand is required');
2599
+ console.error(
2600
+ 'Usage: did-cli wallet <evm|solana> <add|remove|list> <domain>'
2601
+ );
2602
+ process.exit(1);
2603
+ }
2604
+
2605
+ switch (subCommand) {
2606
+ case 'evm':
2607
+ if (!domain || !value) {
2608
+ console.error(
2609
+ '❌ Error: wallet evm requires <action> and <domain>'
2610
+ );
2611
+ console.error(
2612
+ 'Usage: did-cli wallet evm <add|remove|list> <domain>'
2613
+ );
2614
+ process.exit(1);
2615
+ }
2616
+ await walletEvm(domain, value, options);
2617
+ break;
2618
+ case 'solana':
2619
+ if (!domain || !value) {
2620
+ console.error(
2621
+ '❌ Error: wallet solana requires <action> and <domain>'
2622
+ );
2623
+ console.error(
2624
+ 'Usage: did-cli wallet solana <add|remove|list> <domain>'
2625
+ );
2626
+ process.exit(1);
2627
+ }
2628
+ await walletSolana(domain, value, options);
2629
+ break;
2630
+ default:
2631
+ console.error(`❌ Unknown wallet type: ${subCommand}`);
2632
+ console.error(
2633
+ 'Usage: did-cli wallet <evm|solana> <add|remove|list> <domain>'
2634
+ );
2635
+ process.exit(1);
2636
+ }
2637
+ break;
2638
+
2639
+ // Tag commands
2640
+ case 'tag':
2641
+ if (!subCommand) {
2642
+ console.error('❌ Error: Tag subcommand is required');
2643
+ console.error(
2644
+ 'Usage: did-cli tag <define|set|get|remove|list|list-defined|set-tagger|get-tagger> [args]'
2645
+ );
2646
+ process.exit(1);
2647
+ }
2648
+
2649
+ switch (subCommand) {
2650
+ case 'define':
2651
+ if (!domain || !value) {
2652
+ console.error(
2653
+ '❌ Error: tag define requires <domain>, <tag-name> and <type>'
2654
+ );
2655
+ console.error(
2656
+ 'Usage: did-cli tag define <domain> <tag-name> <type>'
2657
+ );
2658
+ console.error(
2659
+ ' Supported types: string, uint8, uint256, int8, int256, bool, address, bytes, bytes32'
2660
+ );
2661
+ console.error(
2662
+ ' Array types: string[], uint8[], etc.'
2663
+ );
2664
+ console.error(
2665
+ '\nNote: Set PRIVATE_KEY_OR_MNEMONIC environment variable first'
2666
+ );
2667
+ process.exit(1);
2668
+ }
2669
+ // For define command: domain=tagName, value=type
2670
+ // We need to get the actual domain from the previous positional arg
2671
+ // This is a bit tricky with current parsing, let's handle it
2672
+ {
2673
+ const args = process.argv.slice(2);
2674
+ const defineIdx = args.indexOf('define');
2675
+ if (defineIdx >= 0 && args.length > defineIdx + 3) {
2676
+ const actualDomain = args[defineIdx + 1];
2677
+ const tagName = args[defineIdx + 2];
2678
+ const typeString = args[defineIdx + 3];
2679
+ await tagDefine(
2680
+ actualDomain,
2681
+ tagName,
2682
+ typeString,
2683
+ options
2684
+ );
2685
+ } else {
2686
+ console.error(
2687
+ '❌ Error: Missing arguments for tag define'
2688
+ );
2689
+ console.error(
2690
+ 'Usage: did-cli tag define <domain> <tag-name> <type>'
2691
+ );
2692
+ process.exit(1);
2693
+ }
2694
+ }
2695
+ break;
2696
+
2697
+ case 'set':
2698
+ if (!domain || !value) {
2699
+ console.error(
2700
+ '❌ Error: tag set requires <domain>, <tag-name> and <value>'
2701
+ );
2702
+ console.error(
2703
+ 'Usage: did-cli tag set <domain> <tag-name> <value>'
2704
+ );
2705
+ console.error(
2706
+ ' Value can be a string or JSON (e.g., \'["item1","item2"]\' for arrays)'
2707
+ );
2708
+ console.error(
2709
+ '\nNote: Set PRIVATE_KEY_OR_MNEMONIC environment variable first'
2710
+ );
2711
+ process.exit(1);
2712
+ }
2713
+ {
2714
+ const args = process.argv.slice(2);
2715
+ const setIdx = args.indexOf('set');
2716
+ if (setIdx >= 0 && args.length > setIdx + 3) {
2717
+ const actualDomain = args[setIdx + 1];
2718
+ const tagName = args[setIdx + 2];
2719
+ const valueStr = args[setIdx + 3];
2720
+ await tagSet(
2721
+ actualDomain,
2722
+ tagName,
2723
+ valueStr,
2724
+ options
2725
+ );
2726
+ } else {
2727
+ console.error(
2728
+ '❌ Error: Missing arguments for tag set'
2729
+ );
2730
+ console.error(
2731
+ 'Usage: did-cli tag set <domain> <tag-name> <value>'
2732
+ );
2733
+ process.exit(1);
2734
+ }
2735
+ }
2736
+ break;
2737
+
2738
+ case 'get':
2739
+ if (!domain) {
2740
+ console.error(
2741
+ '❌ Error: tag get requires <domain> and <tag-name>'
2742
+ );
2743
+ console.error(
2744
+ 'Usage: did-cli tag get <domain> <tag-name>'
2745
+ );
2746
+ process.exit(1);
2747
+ }
2748
+ {
2749
+ const args = process.argv.slice(2);
2750
+ const getIdx = args.indexOf('get');
2751
+ if (getIdx >= 0 && args.length > getIdx + 2) {
2752
+ const actualDomain = args[getIdx + 1];
2753
+ const tagName = args[getIdx + 2];
2754
+ await tagGet(actualDomain, tagName, options);
2755
+ } else {
2756
+ console.error(
2757
+ '❌ Error: Missing arguments for tag get'
2758
+ );
2759
+ console.error(
2760
+ 'Usage: did-cli tag get <domain> <tag-name>'
2761
+ );
2762
+ process.exit(1);
2763
+ }
2764
+ }
2765
+ break;
2766
+
2767
+ case 'remove':
2768
+ if (!domain) {
2769
+ console.error(
2770
+ '❌ Error: tag remove requires <domain> and <tag-name>'
2771
+ );
2772
+ console.error(
2773
+ 'Usage: did-cli tag remove <domain> <tag-name>'
2774
+ );
2775
+ console.error(
2776
+ '\nNote: Set PRIVATE_KEY_OR_MNEMONIC environment variable first'
2777
+ );
2778
+ process.exit(1);
2779
+ }
2780
+ {
2781
+ const args = process.argv.slice(2);
2782
+ const removeIdx = args.indexOf('remove');
2783
+ if (removeIdx >= 0 && args.length > removeIdx + 2) {
2784
+ const actualDomain = args[removeIdx + 1];
2785
+ const tagName = args[removeIdx + 2];
2786
+ await tagRemove(actualDomain, tagName, options);
2787
+ } else {
2788
+ console.error(
2789
+ '❌ Error: Missing arguments for tag remove'
2790
+ );
2791
+ console.error(
2792
+ 'Usage: did-cli tag remove <domain> <tag-name>'
2793
+ );
2794
+ process.exit(1);
2795
+ }
2796
+ }
2797
+ break;
2798
+
2799
+ case 'list':
2800
+ if (!domain) {
2801
+ console.error(
2802
+ '❌ Error: Domain argument is required'
2803
+ );
2804
+ console.error('Usage: did-cli tag list <domain>');
2805
+ process.exit(1);
2806
+ }
2807
+ await tagList(domain, options);
2808
+ break;
2809
+
2810
+ case 'list-defined':
2811
+ if (!domain) {
2812
+ console.error(
2813
+ '❌ Error: Domain argument is required'
2814
+ );
2815
+ console.error(
2816
+ 'Usage: did-cli tag list-defined <domain>'
2817
+ );
2818
+ process.exit(1);
2819
+ }
2820
+ await tagListDefined(domain, options);
2821
+ break;
2822
+
2823
+ case 'set-tagger':
2824
+ if (!domain || !value) {
2825
+ console.error(
2826
+ '❌ Error: tag set-tagger requires <domain>, <tag-name> and <tagger-address>'
2827
+ );
2828
+ console.error(
2829
+ 'Usage: did-cli tag set-tagger <domain> <tag-name> <tagger-address>'
2830
+ );
2831
+ console.error(
2832
+ '\nNote: Set PRIVATE_KEY_OR_MNEMONIC environment variable first'
2833
+ );
2834
+ process.exit(1);
2835
+ }
2836
+ {
2837
+ const args = process.argv.slice(2);
2838
+ const setTaggerIdx = args.indexOf('set-tagger');
2839
+ if (
2840
+ setTaggerIdx >= 0 &&
2841
+ args.length > setTaggerIdx + 3
2842
+ ) {
2843
+ const actualDomain = args[setTaggerIdx + 1];
2844
+ const tagName = args[setTaggerIdx + 2];
2845
+ const taggerAddress = args[setTaggerIdx + 3];
2846
+ await tagSetTagger(
2847
+ actualDomain,
2848
+ tagName,
2849
+ taggerAddress,
2850
+ options
2851
+ );
2852
+ } else {
2853
+ console.error(
2854
+ '❌ Error: Missing arguments for tag set-tagger'
2855
+ );
2856
+ console.error(
2857
+ 'Usage: did-cli tag set-tagger <domain> <tag-name> <tagger-address>'
2858
+ );
2859
+ process.exit(1);
2860
+ }
2861
+ }
2862
+ break;
2863
+
2864
+ case 'get-tagger':
2865
+ if (!domain) {
2866
+ console.error(
2867
+ '❌ Error: tag get-tagger requires <domain> and <tag-name>'
2868
+ );
2869
+ console.error(
2870
+ 'Usage: did-cli tag get-tagger <domain> <tag-name>'
2871
+ );
2872
+ process.exit(1);
2873
+ }
2874
+ {
2875
+ const args = process.argv.slice(2);
2876
+ const getTaggerIdx = args.indexOf('get-tagger');
2877
+ if (
2878
+ getTaggerIdx >= 0 &&
2879
+ args.length > getTaggerIdx + 2
2880
+ ) {
2881
+ const actualDomain = args[getTaggerIdx + 1];
2882
+ const tagName = args[getTaggerIdx + 2];
2883
+ await tagGetTagger(
2884
+ actualDomain,
2885
+ tagName,
2886
+ options
2887
+ );
2888
+ } else {
2889
+ console.error(
2890
+ '❌ Error: Missing arguments for tag get-tagger'
2891
+ );
2892
+ console.error(
2893
+ 'Usage: did-cli tag get-tagger <domain> <tag-name>'
2894
+ );
2895
+ process.exit(1);
2896
+ }
2897
+ }
2898
+ break;
2899
+
2900
+ default:
2901
+ console.error(
2902
+ `❌ Unknown tag subcommand: ${subCommand}`
2903
+ );
2904
+ console.error(
2905
+ 'Usage: did-cli tag <define|set|get|remove|list|list-defined|set-tagger|get-tagger> [args]'
2906
+ );
2907
+ process.exit(1);
2908
+ }
2909
+ break;
2910
+
2911
+ // Legacy commands
2912
+ case 'fetch':
2913
+ case 'fetch-domain':
2914
+ if (!domain) {
2915
+ console.error('❌ Error: Domain argument is required');
2916
+ console.error('Usage: did-cli fetch <domain> [options]');
2917
+ process.exit(1);
2918
+ }
2919
+ await fetchDomain(domain, options);
2920
+ break;
2921
+
2922
+ default:
2923
+ console.error(`❌ Unknown command: ${command}`);
2924
+ console.error('Run "did-cli help" for usage information');
2925
+ process.exit(1);
2926
+ }
2927
+ } catch (error) {
2928
+ console.error(
2929
+ '❌ Error:',
2930
+ error instanceof Error ? error.message : String(error)
2931
+ );
2932
+ if (debug.isEnabled()) {
2933
+ debug.error('Full error details:', error);
2934
+ }
2935
+ process.exit(1);
220
2936
  }
221
2937
  }
222
2938