@agirails/sdk 2.5.3 → 2.5.5

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 (169) hide show
  1. package/dist/ACTPClient.d.ts +18 -0
  2. package/dist/ACTPClient.d.ts.map +1 -1
  3. package/dist/ACTPClient.js +72 -23
  4. package/dist/ACTPClient.js.map +1 -1
  5. package/dist/adapters/BasicAdapter.d.ts +15 -0
  6. package/dist/adapters/BasicAdapter.d.ts.map +1 -1
  7. package/dist/adapters/BasicAdapter.js +33 -4
  8. package/dist/adapters/BasicAdapter.js.map +1 -1
  9. package/dist/adapters/StandardAdapter.d.ts +20 -3
  10. package/dist/adapters/StandardAdapter.d.ts.map +1 -1
  11. package/dist/adapters/StandardAdapter.js +90 -12
  12. package/dist/adapters/StandardAdapter.js.map +1 -1
  13. package/dist/cli/commands/publish.js +16 -4
  14. package/dist/cli/commands/publish.js.map +1 -1
  15. package/dist/cli/commands/register.js +16 -4
  16. package/dist/cli/commands/register.js.map +1 -1
  17. package/dist/cli/commands/tx.js +31 -3
  18. package/dist/cli/commands/tx.js.map +1 -1
  19. package/dist/config/networks.d.ts +10 -2
  20. package/dist/config/networks.d.ts.map +1 -1
  21. package/dist/config/networks.js +31 -22
  22. package/dist/config/networks.js.map +1 -1
  23. package/dist/level0/request.d.ts.map +1 -1
  24. package/dist/level0/request.js +2 -1
  25. package/dist/level0/request.js.map +1 -1
  26. package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
  27. package/dist/runtime/BlockchainRuntime.js +11 -5
  28. package/dist/runtime/BlockchainRuntime.js.map +1 -1
  29. package/dist/utils/IPFSClient.d.ts +3 -1
  30. package/dist/utils/IPFSClient.d.ts.map +1 -1
  31. package/dist/utils/IPFSClient.js +27 -7
  32. package/dist/utils/IPFSClient.js.map +1 -1
  33. package/dist/wallet/AutoWalletProvider.d.ts +11 -1
  34. package/dist/wallet/AutoWalletProvider.d.ts.map +1 -1
  35. package/dist/wallet/AutoWalletProvider.js +84 -19
  36. package/dist/wallet/AutoWalletProvider.js.map +1 -1
  37. package/dist/wallet/IWalletProvider.d.ts +34 -0
  38. package/dist/wallet/IWalletProvider.d.ts.map +1 -1
  39. package/dist/wallet/SmartWalletRouter.d.ts +128 -0
  40. package/dist/wallet/SmartWalletRouter.d.ts.map +1 -0
  41. package/dist/wallet/SmartWalletRouter.js +248 -0
  42. package/dist/wallet/SmartWalletRouter.js.map +1 -0
  43. package/dist/wallet/aa/DualNonceManager.d.ts +26 -1
  44. package/dist/wallet/aa/DualNonceManager.d.ts.map +1 -1
  45. package/dist/wallet/aa/DualNonceManager.js +140 -6
  46. package/dist/wallet/aa/DualNonceManager.js.map +1 -1
  47. package/package.json +3 -6
  48. package/src/ACTPClient.ts +0 -1579
  49. package/src/abi/ACTPKernel.json +0 -1356
  50. package/src/abi/AgentRegistry.json +0 -915
  51. package/src/abi/ERC20.json +0 -40
  52. package/src/abi/EscrowVault.json +0 -134
  53. package/src/abi/IdentityRegistry.json +0 -316
  54. package/src/adapters/AdapterRegistry.ts +0 -173
  55. package/src/adapters/AdapterRouter.ts +0 -416
  56. package/src/adapters/BaseAdapter.ts +0 -498
  57. package/src/adapters/BasicAdapter.ts +0 -514
  58. package/src/adapters/IAdapter.ts +0 -292
  59. package/src/adapters/StandardAdapter.ts +0 -555
  60. package/src/adapters/X402Adapter.ts +0 -731
  61. package/src/adapters/index.ts +0 -60
  62. package/src/builders/DeliveryProofBuilder.ts +0 -327
  63. package/src/builders/QuoteBuilder.ts +0 -483
  64. package/src/builders/index.ts +0 -17
  65. package/src/cli/commands/balance.ts +0 -110
  66. package/src/cli/commands/batch.ts +0 -487
  67. package/src/cli/commands/config.ts +0 -231
  68. package/src/cli/commands/deploy-check.ts +0 -364
  69. package/src/cli/commands/deploy-env.ts +0 -120
  70. package/src/cli/commands/diff.ts +0 -141
  71. package/src/cli/commands/init.ts +0 -469
  72. package/src/cli/commands/mint.ts +0 -116
  73. package/src/cli/commands/pay.ts +0 -113
  74. package/src/cli/commands/publish.ts +0 -475
  75. package/src/cli/commands/pull.ts +0 -124
  76. package/src/cli/commands/register.ts +0 -247
  77. package/src/cli/commands/simulate.ts +0 -345
  78. package/src/cli/commands/time.ts +0 -302
  79. package/src/cli/commands/tx.ts +0 -448
  80. package/src/cli/commands/watch.ts +0 -211
  81. package/src/cli/index.ts +0 -134
  82. package/src/cli/utils/client.ts +0 -252
  83. package/src/cli/utils/config.ts +0 -389
  84. package/src/cli/utils/output.ts +0 -465
  85. package/src/cli/utils/wallet.ts +0 -109
  86. package/src/config/agirailsmd.ts +0 -262
  87. package/src/config/networks.ts +0 -275
  88. package/src/config/pendingPublish.ts +0 -237
  89. package/src/config/publishPipeline.ts +0 -359
  90. package/src/config/syncOperations.ts +0 -279
  91. package/src/erc8004/ERC8004Bridge.ts +0 -462
  92. package/src/erc8004/ReputationReporter.ts +0 -468
  93. package/src/erc8004/index.ts +0 -61
  94. package/src/errors/index.ts +0 -427
  95. package/src/index.ts +0 -364
  96. package/src/level0/Provider.ts +0 -117
  97. package/src/level0/ServiceDirectory.ts +0 -131
  98. package/src/level0/index.ts +0 -10
  99. package/src/level0/provide.ts +0 -132
  100. package/src/level0/request.ts +0 -432
  101. package/src/level1/Agent.ts +0 -1426
  102. package/src/level1/index.ts +0 -10
  103. package/src/level1/pricing/PriceCalculator.ts +0 -255
  104. package/src/level1/pricing/PricingStrategy.ts +0 -198
  105. package/src/level1/types/Job.ts +0 -179
  106. package/src/level1/types/Options.ts +0 -291
  107. package/src/level1/types/index.ts +0 -8
  108. package/src/protocol/ACTPKernel.ts +0 -808
  109. package/src/protocol/AgentRegistry.ts +0 -559
  110. package/src/protocol/DIDManager.ts +0 -629
  111. package/src/protocol/DIDResolver.ts +0 -554
  112. package/src/protocol/EASHelper.ts +0 -378
  113. package/src/protocol/EscrowVault.ts +0 -255
  114. package/src/protocol/EventMonitor.ts +0 -204
  115. package/src/protocol/MessageSigner.ts +0 -510
  116. package/src/protocol/ProofGenerator.ts +0 -339
  117. package/src/protocol/QuoteBuilder.ts +0 -15
  118. package/src/registry/AgentRegistryClient.ts +0 -202
  119. package/src/runtime/BlockchainRuntime.ts +0 -1015
  120. package/src/runtime/IACTPRuntime.ts +0 -306
  121. package/src/runtime/MockRuntime.ts +0 -1298
  122. package/src/runtime/MockStateManager.ts +0 -577
  123. package/src/runtime/index.ts +0 -25
  124. package/src/runtime/types/MockState.ts +0 -237
  125. package/src/storage/ArchiveBundleBuilder.ts +0 -561
  126. package/src/storage/ArweaveClient.ts +0 -946
  127. package/src/storage/FilebaseClient.ts +0 -790
  128. package/src/storage/index.ts +0 -96
  129. package/src/storage/types.ts +0 -348
  130. package/src/types/adapter.ts +0 -310
  131. package/src/types/agent.ts +0 -79
  132. package/src/types/did.ts +0 -223
  133. package/src/types/eip712.ts +0 -175
  134. package/src/types/erc8004.ts +0 -293
  135. package/src/types/escrow.ts +0 -27
  136. package/src/types/index.ts +0 -17
  137. package/src/types/message.ts +0 -145
  138. package/src/types/state.ts +0 -87
  139. package/src/types/transaction.ts +0 -69
  140. package/src/types/x402.ts +0 -251
  141. package/src/utils/ErrorRecoveryGuide.ts +0 -676
  142. package/src/utils/Helpers.ts +0 -688
  143. package/src/utils/IPFSClient.ts +0 -368
  144. package/src/utils/Logger.ts +0 -484
  145. package/src/utils/NonceManager.ts +0 -591
  146. package/src/utils/RateLimiter.ts +0 -534
  147. package/src/utils/ReceivedNonceTracker.ts +0 -567
  148. package/src/utils/SDKLifecycle.ts +0 -416
  149. package/src/utils/SecureNonce.ts +0 -78
  150. package/src/utils/Semaphore.ts +0 -276
  151. package/src/utils/UsedAttestationTracker.ts +0 -385
  152. package/src/utils/canonicalJson.ts +0 -38
  153. package/src/utils/circuitBreaker.ts +0 -324
  154. package/src/utils/computeTypeHash.ts +0 -48
  155. package/src/utils/fsSafe.ts +0 -80
  156. package/src/utils/index.ts +0 -80
  157. package/src/utils/retry.ts +0 -364
  158. package/src/utils/security.ts +0 -418
  159. package/src/utils/validation.ts +0 -540
  160. package/src/wallet/AutoWalletProvider.ts +0 -299
  161. package/src/wallet/EOAWalletProvider.ts +0 -69
  162. package/src/wallet/IWalletProvider.ts +0 -135
  163. package/src/wallet/aa/BundlerClient.ts +0 -274
  164. package/src/wallet/aa/DualNonceManager.ts +0 -173
  165. package/src/wallet/aa/PaymasterClient.ts +0 -174
  166. package/src/wallet/aa/TransactionBatcher.ts +0 -353
  167. package/src/wallet/aa/UserOpBuilder.ts +0 -246
  168. package/src/wallet/aa/constants.ts +0 -60
  169. package/src/wallet/keystore.ts +0 -240
@@ -1,554 +0,0 @@
1
- import { verifyMessage, getAddress } from 'ethers';
2
- // NOTE: These imports require npm packages to be installed:
3
- // - did-resolver
4
- // - ethr-did-resolver
5
- import { Resolver } from 'did-resolver';
6
- import { getResolver } from 'ethr-did-resolver';
7
- import {
8
- DID,
9
- DIDResolutionResult,
10
- DIDResolverConfig,
11
- ParsedDID,
12
- VerifySignatureOptions,
13
- SignatureVerificationResult,
14
- VerificationMethod
15
- } from '../types/did';
16
- import { getNetwork } from '../config/networks';
17
- import { ValidationError } from '../errors';
18
-
19
- /**
20
- * DIDResolver - Resolve DIDs to DID Documents (AIP-7 §2.2)
21
- *
22
- * Uses ethr-did-resolver library for resolution against AGIRAILS Identity Registry.
23
- * Supports did:ethr method with explicit chainId format: did:ethr:<chainId>:<address>
24
- *
25
- * @example
26
- * ```typescript
27
- * const resolver = await DIDResolver.create({ network: 'base-sepolia' });
28
- *
29
- * // Resolve DID to DID Document
30
- * const result = await resolver.resolve('did:ethr:84532:0x742d35cc6634c0532925a3b844bc9e7595f0beb');
31
- * console.log(result.didDocument);
32
- *
33
- * // Verify signature
34
- * const isValid = await resolver.verifySignature(
35
- * 'did:ethr:84532:0x742d35cc...',
36
- * 'Hello AGIRAILS',
37
- * '0x1234...',
38
- * { chainId: 84532 }
39
- * );
40
- * ```
41
- *
42
- * @security
43
- * - Always validates DID format before resolution
44
- * - Checks chainId matches expected network
45
- * - Verifies signatures using ethers.js verifyMessage
46
- * - Prevents cross-chain replay attacks via chainId validation
47
- */
48
- export class DIDResolver {
49
- private resolver!: Resolver;
50
- private config: Required<DIDResolverConfig>;
51
-
52
- /**
53
- * Private constructor - use DIDResolver.create() factory method
54
- */
55
- private constructor(config: Required<DIDResolverConfig>) {
56
- this.config = config;
57
- }
58
-
59
- /**
60
- * Factory method to create DIDResolver instance
61
- *
62
- * @param config - Configuration options
63
- * @returns Configured DIDResolver instance
64
- * @throws ValidationError if configuration is invalid
65
- *
66
- * @example
67
- * ```typescript
68
- * // Using predefined network
69
- * const resolver = await DIDResolver.create({ network: 'base-sepolia' });
70
- *
71
- * // Using custom RPC and registry
72
- * const resolver = await DIDResolver.create({
73
- * chainId: 84532,
74
- * rpcUrl: 'https://sepolia.base.org',
75
- * registryAddress: '0x...'
76
- * });
77
- * ```
78
- */
79
- static async create(config: DIDResolverConfig = {}): Promise<DIDResolver> {
80
- // Resolve configuration with defaults
81
- const resolvedConfig = DIDResolver.resolveConfig(config);
82
-
83
- // Validate configuration
84
- DIDResolver.validateConfig(resolvedConfig);
85
-
86
- // Initialize did-resolver with ethr-did-resolver
87
- const providerConfig = {
88
- networks: [
89
- {
90
- name: resolvedConfig.network || `chain-${resolvedConfig.chainId}`,
91
- chainId: `0x${resolvedConfig.chainId.toString(16)}`,
92
- rpcUrl: resolvedConfig.rpcUrl,
93
- registry: resolvedConfig.registryAddress
94
- }
95
- ]
96
- };
97
-
98
- const ethrResolver = getResolver(providerConfig);
99
- const resolver = new Resolver(ethrResolver);
100
-
101
- const instance = new DIDResolver(resolvedConfig);
102
- instance.resolver = resolver;
103
-
104
- return instance;
105
- }
106
-
107
- /**
108
- * Resolve configuration with defaults from network config
109
- */
110
- private static resolveConfig(config: DIDResolverConfig): Required<DIDResolverConfig> {
111
- // If network specified, use network defaults
112
- if (config.network) {
113
- const networkConfig = getNetwork(config.network);
114
- return {
115
- network: config.network,
116
- chainId: config.chainId || networkConfig.chainId,
117
- rpcUrl: config.rpcUrl || networkConfig.rpcUrl,
118
- registryAddress: config.registryAddress || networkConfig.contracts.identityRegistry || ''
119
- };
120
- }
121
-
122
- // Custom configuration (must provide all required fields)
123
- if (!config.chainId || !config.rpcUrl) {
124
- throw new ValidationError(
125
- 'config',
126
- 'Must provide either network name or (chainId + rpcUrl)'
127
- );
128
- }
129
-
130
- return {
131
- network: config.network || '',
132
- chainId: config.chainId,
133
- rpcUrl: config.rpcUrl,
134
- registryAddress: config.registryAddress || ''
135
- };
136
- }
137
-
138
- /**
139
- * Validate configuration
140
- */
141
- private static validateConfig(config: Required<DIDResolverConfig>): void {
142
- if (!config.chainId || config.chainId <= 0) {
143
- throw new ValidationError('chainId', 'Invalid chainId');
144
- }
145
-
146
- if (!config.rpcUrl || !config.rpcUrl.startsWith('http')) {
147
- throw new ValidationError('rpcUrl', 'Invalid RPC URL');
148
- }
149
-
150
- // Registry address is optional (will use default if not provided)
151
- // But if provided, must be valid address
152
- if (config.registryAddress) {
153
- try {
154
- getAddress(config.registryAddress);
155
- } catch (error) {
156
- throw new ValidationError('registryAddress', 'Invalid registry address');
157
- }
158
- }
159
- }
160
-
161
- /**
162
- * Get resolver configuration
163
- */
164
- getConfig(): Required<DIDResolverConfig> {
165
- return { ...this.config };
166
- }
167
-
168
- /**
169
- * Resolve DID to DID Document
170
- *
171
- * @param did - DID to resolve (format: did:ethr:<chainId>:<address>)
172
- * @returns DID resolution result with document and metadata
173
- * @throws ValidationError if DID format is invalid
174
- *
175
- * @example
176
- * ```typescript
177
- * const result = await resolver.resolve('did:ethr:84532:0x742d35cc6634c0532925a3b844bc9e7595f0beb');
178
- *
179
- * if (result.didDocument) {
180
- * console.log('Identity owner:', result.didDocument.verificationMethod[0].controller);
181
- * console.log('Service endpoints:', result.didDocument.service);
182
- * } else {
183
- * console.error('Resolution failed:', result.didResolutionMetadata.error);
184
- * }
185
- * ```
186
- */
187
- async resolve(did: DID): Promise<DIDResolutionResult> {
188
- // Validate DID format
189
- const parsed = DIDResolver.parseDID(did);
190
-
191
- // Check chainId matches expected network
192
- if (parsed.chainId !== this.config.chainId) {
193
- return {
194
- didDocument: null,
195
- didResolutionMetadata: {
196
- error: 'invalidDid',
197
- message: `DID chainId ${parsed.chainId} does not match resolver chainId ${this.config.chainId}`
198
- },
199
- didDocumentMetadata: {}
200
- };
201
- }
202
-
203
- // Perform resolution using did-resolver
204
- const result = await this.resolver.resolve(did);
205
- return result as DIDResolutionResult;
206
- }
207
-
208
- /**
209
- * Verify a signature was made by DID controller or authorized delegate
210
- *
211
- * @param did - DID of expected signer
212
- * @param message - Original message that was signed
213
- * @param signature - Signature to verify (0x-prefixed hex string)
214
- * @param options - Verification options (chainId validation, domain separation, etc.)
215
- * @returns Verification result with validity and signer info
216
- *
217
- * @security
218
- * - Validates chainId to prevent cross-chain replay attacks
219
- * - Uses domain separation to prevent cross-protocol replay attacks (MEDIUM finding fix)
220
- * - Resolves DID to get current owner and delegates
221
- * - Checks if signer is owner or valid delegate (MEDIUM finding fix)
222
- * - Validates delegate expiration timestamps if provided
223
- *
224
- * @example
225
- * ```typescript
226
- * // With domain separation (recommended - default)
227
- * const result = await resolver.verifySignature(
228
- * 'did:ethr:84532:0x742d35cc...',
229
- * 'Hello AGIRAILS',
230
- * '0x1234...',
231
- * { chainId: 84532, useDomainSeparation: true }
232
- * );
233
- *
234
- * // Without domain separation (backwards compatibility only)
235
- * const legacyResult = await resolver.verifySignature(
236
- * 'did:ethr:84532:0x742d35cc...',
237
- * 'Hello AGIRAILS',
238
- * '0x1234...',
239
- * { chainId: 84532, useDomainSeparation: false }
240
- * );
241
- *
242
- * if (result.valid) {
243
- * console.log('Signature is valid!');
244
- * console.log('Signer:', result.signer);
245
- * console.log('Is delegate:', result.isDelegate);
246
- * if (result.isDelegate) {
247
- * console.log('Delegate type:', result.delegateType);
248
- * }
249
- * } else {
250
- * console.error('Invalid signature:', result.error);
251
- * }
252
- * ```
253
- */
254
- async verifySignature(
255
- did: DID,
256
- message: string,
257
- signature: string,
258
- options: VerifySignatureOptions
259
- ): Promise<SignatureVerificationResult> {
260
- try {
261
- // Validate DID format
262
- const parsed = DIDResolver.parseDID(did);
263
-
264
- // Validate chainId matches
265
- if (parsed.chainId !== options.chainId) {
266
- return {
267
- valid: false,
268
- error: `DID chainId ${parsed.chainId} does not match expected chainId ${options.chainId}`
269
- };
270
- }
271
-
272
- // SECURITY FIX (MEDIUM): Domain separation to prevent cross-protocol replay attacks
273
- // Default to true for security, but allow opt-out for backwards compatibility
274
- const useDomainSeparation = options.useDomainSeparation !== false;
275
- const messageToVerify = useDomainSeparation
276
- ? this.createDomainSeparatedMessage(message, parsed.chainId, did)
277
- : message;
278
-
279
- // Recover signer from signature
280
- const recoveredAddress = verifyMessage(messageToVerify, signature);
281
- const signerLower = recoveredAddress.toLowerCase();
282
- const didAddressLower = parsed.address.toLowerCase();
283
-
284
- // Check if signer is the DID address itself (owner)
285
- if (signerLower === didAddressLower) {
286
- return {
287
- valid: true,
288
- signer: recoveredAddress,
289
- isDelegate: false
290
- };
291
- }
292
-
293
- // SECURITY FIX (MEDIUM): Implement delegate validation
294
- // Check if signer is a valid delegate in the DID Document
295
- const resolution = await this.resolve(did);
296
- if (resolution.didDocument && resolution.didDocument.authentication) {
297
- // Iterate through authentication methods to find valid delegates
298
- for (const auth of resolution.didDocument.authentication) {
299
- // auth can be a string (reference to verificationMethod) or VerificationMethod object
300
- const verificationMethod = typeof auth === 'string'
301
- ? resolution.didDocument.verificationMethod?.find(vm => vm.id === auth)
302
- : auth;
303
-
304
- if (verificationMethod) {
305
- // Extract delegate address from verification method
306
- const delegateAddress = this.extractAddressFromVerificationMethod(verificationMethod);
307
-
308
- if (delegateAddress && delegateAddress.toLowerCase() === signerLower) {
309
- // Check delegate validity if timestamp provided
310
- if (options.timestamp && verificationMethod.validTo) {
311
- const validToTimestamp = typeof verificationMethod.validTo === 'bigint'
312
- ? Number(verificationMethod.validTo)
313
- : verificationMethod.validTo;
314
-
315
- if (options.timestamp > validToTimestamp) {
316
- return {
317
- valid: false,
318
- signer: recoveredAddress,
319
- error: 'Delegate expired'
320
- };
321
- }
322
- }
323
-
324
- return {
325
- valid: true,
326
- signer: recoveredAddress,
327
- isDelegate: true,
328
- delegateType: verificationMethod.type
329
- };
330
- }
331
- }
332
- }
333
- }
334
-
335
- // Signer is neither owner nor valid delegate
336
- return {
337
- valid: false,
338
- signer: recoveredAddress,
339
- error: 'Signer is not the DID controller or a valid delegate'
340
- };
341
-
342
- } catch (error) {
343
- return {
344
- valid: false,
345
- error: error instanceof Error ? error.message : 'Signature verification failed'
346
- };
347
- }
348
- }
349
-
350
- /**
351
- * Build DID from address and chainId
352
- *
353
- * @param address - Ethereum address (with or without 0x prefix)
354
- * @param chainId - Chain ID (e.g., 84532 for Base Sepolia)
355
- * @returns Canonical DID string (lowercase address)
356
- *
357
- * @example
358
- * ```typescript
359
- * const did = DIDResolver.buildDID('0x742d35cc6634c0532925a3b844bc9e7595f0beb', 84532);
360
- * // Returns: 'did:ethr:84532:0x742d35cc6634c0532925a3b844bc9e7595f0beb'
361
- * ```
362
- */
363
- static buildDID(address: string, chainId: number): DID {
364
- // Validate and checksum address
365
- const checksummed = getAddress(address);
366
-
367
- // Convert to lowercase for canonical format (per AIP-7 §2.1)
368
- const canonical = checksummed.toLowerCase();
369
-
370
- return `did:ethr:${chainId}:${canonical}`;
371
- }
372
-
373
- /**
374
- * Parse DID to extract components
375
- *
376
- * @param did - DID to parse
377
- * @returns Parsed components
378
- * @throws ValidationError if DID format is invalid
379
- *
380
- * @example
381
- * ```typescript
382
- * const parsed = DIDResolver.parseDID('did:ethr:84532:0x742d35cc6634c0532925a3b844bc9e7595f0beb');
383
- * console.log(parsed.method); // 'ethr'
384
- * console.log(parsed.chainId); // 84532
385
- * console.log(parsed.address); // '0x742d35cc6634c0532925a3b844bc9e7595f0beb'
386
- * ```
387
- */
388
- static parseDID(did: DID): ParsedDID {
389
- // Validate basic format
390
- if (!did || typeof did !== 'string') {
391
- throw new ValidationError('did', 'DID must be a non-empty string');
392
- }
393
-
394
- // DID format: did:ethr:<chainId>:<address>
395
- const parts = did.split(':');
396
-
397
- if (parts.length !== 4) {
398
- throw new ValidationError(
399
- 'did',
400
- 'Invalid DID format. Expected: did:ethr:<chainId>:<address>'
401
- );
402
- }
403
-
404
- const [scheme, method, chainIdStr, address] = parts;
405
-
406
- // Validate scheme
407
- if (scheme !== 'did') {
408
- throw new ValidationError('did', `Invalid DID scheme: ${scheme}. Expected: did`);
409
- }
410
-
411
- // Validate method
412
- if (method !== 'ethr') {
413
- throw new ValidationError('did', `Invalid DID method: ${method}. Expected: ethr`);
414
- }
415
-
416
- // Validate chainId
417
- const chainId = parseInt(chainIdStr, 10);
418
- if (isNaN(chainId) || chainId <= 0) {
419
- throw new ValidationError('did', `Invalid chainId: ${chainIdStr}`);
420
- }
421
-
422
- // Validate address
423
- try {
424
- const checksummed = getAddress(address);
425
- return {
426
- method,
427
- chainId,
428
- address: checksummed.toLowerCase() // Canonical format per AIP-7
429
- };
430
- } catch (error) {
431
- throw new ValidationError('did', `Invalid Ethereum address: ${address}`);
432
- }
433
- }
434
-
435
- /**
436
- * Validate DID format
437
- *
438
- * @param did - DID to validate
439
- * @returns True if valid, false otherwise
440
- */
441
- static isValidDID(did: DID): boolean {
442
- try {
443
- DIDResolver.parseDID(did);
444
- return true;
445
- } catch {
446
- return false;
447
- }
448
- }
449
-
450
- /**
451
- * Extract address from DID
452
- *
453
- * @param did - DID to extract from
454
- * @returns Ethereum address (lowercase)
455
- * @throws ValidationError if DID format is invalid
456
- */
457
- static extractAddress(did: DID): string {
458
- const parsed = DIDResolver.parseDID(did);
459
- return parsed.address;
460
- }
461
-
462
- /**
463
- * Extract chainId from DID
464
- *
465
- * @param did - DID to extract from
466
- * @returns Chain ID
467
- * @throws ValidationError if DID format is invalid
468
- */
469
- static extractChainId(did: DID): number {
470
- const parsed = DIDResolver.parseDID(did);
471
- return parsed.chainId;
472
- }
473
-
474
- /**
475
- * Create domain-separated message to prevent cross-protocol replay attacks
476
- *
477
- * @param message - Original message
478
- * @param chainId - Chain ID for domain separation
479
- * @param did - DID for additional context binding
480
- * @returns Domain-separated message
481
- *
482
- * @security
483
- * - Binds signature to AGIRAILS protocol
484
- * - Binds signature to specific chainId (prevents cross-chain replay)
485
- * - Binds signature to specific DID (prevents cross-identity replay)
486
- *
487
- * @example
488
- * ```typescript
489
- * const domainMsg = this.createDomainSeparatedMessage('Hello', 84532, 'did:ethr:84532:0x...');
490
- * // Returns: "AGIRAILS:84532:did:ethr:84532:0x...\nHello"
491
- * ```
492
- */
493
- private createDomainSeparatedMessage(message: string, chainId: number, did: string): string {
494
- return `AGIRAILS:${chainId}:${did}\n${message}`;
495
- }
496
-
497
- /**
498
- * Extract Ethereum address from a verification method
499
- *
500
- * @param vm - Verification method from DID Document
501
- * @returns Ethereum address (checksummed) or null if not found
502
- *
503
- * @security
504
- * - Handles multiple address formats (blockchainAccountId, ethereumAddress, controller DID)
505
- * - Validates extracted address format
506
- * - Returns null instead of throwing for graceful error handling
507
- *
508
- * @example
509
- * ```typescript
510
- * const vm = {
511
- * id: 'did:ethr:84532:0x123...#delegate1',
512
- * type: 'EcdsaSecp256k1RecoveryMethod2020',
513
- * controller: 'did:ethr:84532:0x123...',
514
- * blockchainAccountId: '0x456...@eip155:84532'
515
- * };
516
- * const address = this.extractAddressFromVerificationMethod(vm);
517
- * // Returns: '0x456...' (checksummed)
518
- * ```
519
- */
520
- private extractAddressFromVerificationMethod(vm: VerificationMethod): string | null {
521
- // Try blockchainAccountId first (CAIP-10 format: "0x123...@eip155:chainId")
522
- if (vm.blockchainAccountId) {
523
- const match = vm.blockchainAccountId.match(/^(0x[a-fA-F0-9]{40})@/);
524
- if (match) {
525
- try {
526
- return getAddress(match[1]); // Checksum the address
527
- } catch {
528
- return null;
529
- }
530
- }
531
- }
532
-
533
- // Try ethereumAddress field
534
- if (vm.ethereumAddress) {
535
- try {
536
- return getAddress(vm.ethereumAddress);
537
- } catch {
538
- return null;
539
- }
540
- }
541
-
542
- // Try extracting from controller DID
543
- if (vm.controller) {
544
- try {
545
- const { address } = DIDResolver.parseDID(vm.controller);
546
- return getAddress(address); // Already lowercase, checksum it
547
- } catch {
548
- return null;
549
- }
550
- }
551
-
552
- return null;
553
- }
554
- }