@agirails/sdk 2.5.3 → 2.5.4

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 (166) hide show
  1. package/dist/ACTPClient.d.ts +18 -0
  2. package/dist/ACTPClient.d.ts.map +1 -1
  3. package/dist/ACTPClient.js +67 -22
  4. package/dist/ACTPClient.js.map +1 -1
  5. package/dist/adapters/BasicAdapter.d.ts +12 -0
  6. package/dist/adapters/BasicAdapter.d.ts.map +1 -1
  7. package/dist/adapters/BasicAdapter.js +30 -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 +45 -11
  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 +2 -2
  20. package/dist/config/networks.d.ts.map +1 -1
  21. package/dist/config/networks.js +27 -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.map +1 -1
  34. package/dist/wallet/AutoWalletProvider.js +52 -18
  35. package/dist/wallet/AutoWalletProvider.js.map +1 -1
  36. package/dist/wallet/SmartWalletRouter.d.ts +116 -0
  37. package/dist/wallet/SmartWalletRouter.d.ts.map +1 -0
  38. package/dist/wallet/SmartWalletRouter.js +212 -0
  39. package/dist/wallet/SmartWalletRouter.js.map +1 -0
  40. package/dist/wallet/aa/DualNonceManager.d.ts +19 -0
  41. package/dist/wallet/aa/DualNonceManager.d.ts.map +1 -1
  42. package/dist/wallet/aa/DualNonceManager.js +100 -5
  43. package/dist/wallet/aa/DualNonceManager.js.map +1 -1
  44. package/package.json +3 -6
  45. package/src/ACTPClient.ts +0 -1579
  46. package/src/abi/ACTPKernel.json +0 -1356
  47. package/src/abi/AgentRegistry.json +0 -915
  48. package/src/abi/ERC20.json +0 -40
  49. package/src/abi/EscrowVault.json +0 -134
  50. package/src/abi/IdentityRegistry.json +0 -316
  51. package/src/adapters/AdapterRegistry.ts +0 -173
  52. package/src/adapters/AdapterRouter.ts +0 -416
  53. package/src/adapters/BaseAdapter.ts +0 -498
  54. package/src/adapters/BasicAdapter.ts +0 -514
  55. package/src/adapters/IAdapter.ts +0 -292
  56. package/src/adapters/StandardAdapter.ts +0 -555
  57. package/src/adapters/X402Adapter.ts +0 -731
  58. package/src/adapters/index.ts +0 -60
  59. package/src/builders/DeliveryProofBuilder.ts +0 -327
  60. package/src/builders/QuoteBuilder.ts +0 -483
  61. package/src/builders/index.ts +0 -17
  62. package/src/cli/commands/balance.ts +0 -110
  63. package/src/cli/commands/batch.ts +0 -487
  64. package/src/cli/commands/config.ts +0 -231
  65. package/src/cli/commands/deploy-check.ts +0 -364
  66. package/src/cli/commands/deploy-env.ts +0 -120
  67. package/src/cli/commands/diff.ts +0 -141
  68. package/src/cli/commands/init.ts +0 -469
  69. package/src/cli/commands/mint.ts +0 -116
  70. package/src/cli/commands/pay.ts +0 -113
  71. package/src/cli/commands/publish.ts +0 -475
  72. package/src/cli/commands/pull.ts +0 -124
  73. package/src/cli/commands/register.ts +0 -247
  74. package/src/cli/commands/simulate.ts +0 -345
  75. package/src/cli/commands/time.ts +0 -302
  76. package/src/cli/commands/tx.ts +0 -448
  77. package/src/cli/commands/watch.ts +0 -211
  78. package/src/cli/index.ts +0 -134
  79. package/src/cli/utils/client.ts +0 -252
  80. package/src/cli/utils/config.ts +0 -389
  81. package/src/cli/utils/output.ts +0 -465
  82. package/src/cli/utils/wallet.ts +0 -109
  83. package/src/config/agirailsmd.ts +0 -262
  84. package/src/config/networks.ts +0 -275
  85. package/src/config/pendingPublish.ts +0 -237
  86. package/src/config/publishPipeline.ts +0 -359
  87. package/src/config/syncOperations.ts +0 -279
  88. package/src/erc8004/ERC8004Bridge.ts +0 -462
  89. package/src/erc8004/ReputationReporter.ts +0 -468
  90. package/src/erc8004/index.ts +0 -61
  91. package/src/errors/index.ts +0 -427
  92. package/src/index.ts +0 -364
  93. package/src/level0/Provider.ts +0 -117
  94. package/src/level0/ServiceDirectory.ts +0 -131
  95. package/src/level0/index.ts +0 -10
  96. package/src/level0/provide.ts +0 -132
  97. package/src/level0/request.ts +0 -432
  98. package/src/level1/Agent.ts +0 -1426
  99. package/src/level1/index.ts +0 -10
  100. package/src/level1/pricing/PriceCalculator.ts +0 -255
  101. package/src/level1/pricing/PricingStrategy.ts +0 -198
  102. package/src/level1/types/Job.ts +0 -179
  103. package/src/level1/types/Options.ts +0 -291
  104. package/src/level1/types/index.ts +0 -8
  105. package/src/protocol/ACTPKernel.ts +0 -808
  106. package/src/protocol/AgentRegistry.ts +0 -559
  107. package/src/protocol/DIDManager.ts +0 -629
  108. package/src/protocol/DIDResolver.ts +0 -554
  109. package/src/protocol/EASHelper.ts +0 -378
  110. package/src/protocol/EscrowVault.ts +0 -255
  111. package/src/protocol/EventMonitor.ts +0 -204
  112. package/src/protocol/MessageSigner.ts +0 -510
  113. package/src/protocol/ProofGenerator.ts +0 -339
  114. package/src/protocol/QuoteBuilder.ts +0 -15
  115. package/src/registry/AgentRegistryClient.ts +0 -202
  116. package/src/runtime/BlockchainRuntime.ts +0 -1015
  117. package/src/runtime/IACTPRuntime.ts +0 -306
  118. package/src/runtime/MockRuntime.ts +0 -1298
  119. package/src/runtime/MockStateManager.ts +0 -577
  120. package/src/runtime/index.ts +0 -25
  121. package/src/runtime/types/MockState.ts +0 -237
  122. package/src/storage/ArchiveBundleBuilder.ts +0 -561
  123. package/src/storage/ArweaveClient.ts +0 -946
  124. package/src/storage/FilebaseClient.ts +0 -790
  125. package/src/storage/index.ts +0 -96
  126. package/src/storage/types.ts +0 -348
  127. package/src/types/adapter.ts +0 -310
  128. package/src/types/agent.ts +0 -79
  129. package/src/types/did.ts +0 -223
  130. package/src/types/eip712.ts +0 -175
  131. package/src/types/erc8004.ts +0 -293
  132. package/src/types/escrow.ts +0 -27
  133. package/src/types/index.ts +0 -17
  134. package/src/types/message.ts +0 -145
  135. package/src/types/state.ts +0 -87
  136. package/src/types/transaction.ts +0 -69
  137. package/src/types/x402.ts +0 -251
  138. package/src/utils/ErrorRecoveryGuide.ts +0 -676
  139. package/src/utils/Helpers.ts +0 -688
  140. package/src/utils/IPFSClient.ts +0 -368
  141. package/src/utils/Logger.ts +0 -484
  142. package/src/utils/NonceManager.ts +0 -591
  143. package/src/utils/RateLimiter.ts +0 -534
  144. package/src/utils/ReceivedNonceTracker.ts +0 -567
  145. package/src/utils/SDKLifecycle.ts +0 -416
  146. package/src/utils/SecureNonce.ts +0 -78
  147. package/src/utils/Semaphore.ts +0 -276
  148. package/src/utils/UsedAttestationTracker.ts +0 -385
  149. package/src/utils/canonicalJson.ts +0 -38
  150. package/src/utils/circuitBreaker.ts +0 -324
  151. package/src/utils/computeTypeHash.ts +0 -48
  152. package/src/utils/fsSafe.ts +0 -80
  153. package/src/utils/index.ts +0 -80
  154. package/src/utils/retry.ts +0 -364
  155. package/src/utils/security.ts +0 -418
  156. package/src/utils/validation.ts +0 -540
  157. package/src/wallet/AutoWalletProvider.ts +0 -299
  158. package/src/wallet/EOAWalletProvider.ts +0 -69
  159. package/src/wallet/IWalletProvider.ts +0 -135
  160. package/src/wallet/aa/BundlerClient.ts +0 -274
  161. package/src/wallet/aa/DualNonceManager.ts +0 -173
  162. package/src/wallet/aa/PaymasterClient.ts +0 -174
  163. package/src/wallet/aa/TransactionBatcher.ts +0 -353
  164. package/src/wallet/aa/UserOpBuilder.ts +0 -246
  165. package/src/wallet/aa/constants.ts +0 -60
  166. 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
- }