@beclab/olaresid 0.1.1 → 0.1.3

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