@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,559 +0,0 @@
1
- import { Contract, Signer, keccak256, toUtf8Bytes } from 'ethers';
2
- import AgentRegistryABI from '../abi/AgentRegistry.json';
3
- import {
4
- AgentProfile,
5
- ServiceDescriptor,
6
- RegisterAgentParams,
7
- QueryAgentsParams
8
- } from '../types';
9
- import { TransactionRevertedError, ValidationError, QueryCapExceededError } from '../errors';
10
- import { validateAddress, validateEndpointURL } from '../utils/validation';
11
-
12
- /**
13
- * Gas options for transactions
14
- */
15
- interface GasOptions {
16
- maxFeePerGas?: bigint;
17
- maxPriorityFeePerGas?: bigint;
18
- }
19
-
20
- /**
21
- * AgentRegistry - Agent Identity & Reputation SDK Module (AIP-7)
22
- *
23
- * Provides methods for:
24
- * - Registering AI agents on-chain
25
- * - Managing agent profiles and services
26
- * - Querying agents by service type
27
- * - Reading reputation scores
28
- *
29
- * Reference: AIP-7 Agent Identity, Registry & Storage System
30
- */
31
- export class AgentRegistry {
32
- private contract: Contract;
33
- private readonly gasSettings?: GasOptions;
34
-
35
- constructor(
36
- private readonly address: string,
37
- signer: Signer,
38
- gasSettings?: GasOptions
39
- ) {
40
- this.contract = new Contract(address, AgentRegistryABI, signer);
41
- this.gasSettings = gasSettings;
42
- }
43
-
44
- /**
45
- * Get gas buffer multiplier based on operation complexity
46
- */
47
- private getGasBufferMultiplier(operation: string): number {
48
- const buffers: Record<string, number> = {
49
- 'registerAgent': 1.40, // 40% - Complex storage writes
50
- 'addServiceType': 1.25, // 25% - Storage update
51
- 'removeServiceType': 1.25,
52
- 'updateEndpoint': 1.20, // 20% - Simple storage update
53
- 'setActiveStatus': 1.15 // 15% - Boolean update
54
- };
55
-
56
- return buffers[operation] || 1.20;
57
- }
58
-
59
- /**
60
- * Build transaction options with gas settings
61
- */
62
- private buildTxOptions(estimatedGas: bigint, operation: string = 'default'): any {
63
- const bufferMultiplier = this.getGasBufferMultiplier(operation);
64
- let gasLimit = (estimatedGas * BigInt(Math.round(bufferMultiplier * 100))) / 100n;
65
-
66
- // Cap gas limit (Base L2 safety)
67
- const MAX_GAS_LIMIT = 10_000_000n;
68
- if (gasLimit > MAX_GAS_LIMIT) {
69
- throw new ValidationError('gasLimit', `Estimated gas exceeds maximum safe limit (${MAX_GAS_LIMIT})`);
70
- }
71
-
72
- const options: any = { gasLimit };
73
-
74
- if (this.gasSettings?.maxFeePerGas) {
75
- options.maxFeePerGas = this.gasSettings.maxFeePerGas;
76
- }
77
- if (this.gasSettings?.maxPriorityFeePerGas) {
78
- options.maxPriorityFeePerGas = this.gasSettings.maxPriorityFeePerGas;
79
- }
80
-
81
- return options;
82
- }
83
-
84
- /**
85
- * Get registry contract address
86
- */
87
- getAddress(): string {
88
- return this.address;
89
- }
90
-
91
- /**
92
- * Compute service type hash
93
- * Service types must be lowercase with only a-z, 0-9, and hyphens
94
- *
95
- * @param serviceType - Human-readable service type (e.g., "text-generation")
96
- * @returns keccak256 hash of the service type
97
- */
98
- computeServiceTypeHash(serviceType: string): string {
99
- // Validate length
100
- const MAX_SERVICE_TYPE_LENGTH = 64;
101
- if (serviceType.length > MAX_SERVICE_TYPE_LENGTH) {
102
- throw new ValidationError(
103
- 'serviceType',
104
- `Service type exceeds maximum length (${MAX_SERVICE_TYPE_LENGTH} characters)`
105
- );
106
- }
107
-
108
- // Validate format
109
- if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(serviceType)) {
110
- throw new ValidationError(
111
- 'serviceType',
112
- 'Must be lowercase alphanumeric with hyphens (e.g., "text-generation")'
113
- );
114
- }
115
- return keccak256(toUtf8Bytes(serviceType));
116
- }
117
-
118
- // ========== WRITE FUNCTIONS ==========
119
-
120
- /**
121
- * Register a new agent profile
122
- *
123
- * @param params - Registration parameters
124
- * @returns Transaction hash
125
- *
126
- * @example
127
- * ```typescript
128
- * await registry.registerAgent({
129
- * endpoint: 'https://myagent.example.com/webhook',
130
- * serviceDescriptors: [{
131
- * serviceTypeHash: registry.computeServiceTypeHash('text-generation'),
132
- * serviceType: 'text-generation',
133
- * schemaURI: 'ipfs://Qm...',
134
- * minPrice: 1_000_000n, // 1 USDC
135
- * maxPrice: 100_000_000n, // 100 USDC
136
- * avgCompletionTime: 60,
137
- * metadataCID: 'Qm...'
138
- * }]
139
- * });
140
- * ```
141
- */
142
- async registerAgent(params: RegisterAgentParams): Promise<string> {
143
- // SECURITY FIX (H-1): Await async endpoint validation (DNS resolution + SSRF check)
144
- await validateEndpointURL(params.endpoint, 'endpoint');
145
-
146
- if (!params.serviceDescriptors || params.serviceDescriptors.length === 0) {
147
- throw new ValidationError('serviceDescriptors', 'At least one service descriptor required');
148
- }
149
-
150
- const MAX_SERVICE_DESCRIPTORS = 100;
151
- if (params.serviceDescriptors.length > MAX_SERVICE_DESCRIPTORS) {
152
- throw new ValidationError(
153
- 'serviceDescriptors',
154
- `Too many service descriptors (max: ${MAX_SERVICE_DESCRIPTORS})`
155
- );
156
- }
157
-
158
- // Validate each service descriptor
159
- for (const sd of params.serviceDescriptors) {
160
- const expectedHash = this.computeServiceTypeHash(sd.serviceType);
161
- if (sd.serviceTypeHash !== expectedHash) {
162
- throw new ValidationError(
163
- 'serviceTypeHash',
164
- `Hash mismatch for ${sd.serviceType}. Expected ${expectedHash}`
165
- );
166
- }
167
-
168
- // C-2: Validate price range
169
- if (sd.minPrice < 0n) {
170
- throw new ValidationError('minPrice', 'Minimum price cannot be negative');
171
- }
172
- if (sd.maxPrice < 0n) {
173
- throw new ValidationError('maxPrice', 'Maximum price cannot be negative');
174
- }
175
- if (sd.minPrice > sd.maxPrice) {
176
- throw new ValidationError('priceRange', `minPrice (${sd.minPrice}) cannot exceed maxPrice (${sd.maxPrice})`);
177
- }
178
-
179
- // Sanity check: max $1M USDC
180
- const MAX_PRICE = 1_000_000_000_000n;
181
- if (sd.maxPrice > MAX_PRICE) {
182
- throw new ValidationError('maxPrice', 'Price exceeds maximum reasonable value ($1M USDC)');
183
- }
184
-
185
- // Validate completion time
186
- if (sd.avgCompletionTime <= 0) {
187
- throw new ValidationError('avgCompletionTime', 'Average completion time must be positive');
188
- }
189
- const MAX_COMPLETION_TIME = 30 * 24 * 60 * 60; // 30 days
190
- if (sd.avgCompletionTime > MAX_COMPLETION_TIME) {
191
- throw new ValidationError('avgCompletionTime', 'Completion time exceeds maximum (30 days)');
192
- }
193
- }
194
-
195
- try {
196
- const registerFunc = this.contract.getFunction('registerAgent');
197
-
198
- // Convert to contract format
199
- const descriptors = params.serviceDescriptors.map(sd => ({
200
- serviceTypeHash: sd.serviceTypeHash,
201
- serviceType: sd.serviceType,
202
- schemaURI: sd.schemaURI,
203
- minPrice: sd.minPrice,
204
- maxPrice: sd.maxPrice,
205
- avgCompletionTime: sd.avgCompletionTime,
206
- metadataCID: sd.metadataCID
207
- }));
208
-
209
- const estimatedGas = await registerFunc.estimateGas(params.endpoint, descriptors);
210
- const txOptions = this.buildTxOptions(estimatedGas, 'registerAgent');
211
-
212
- const tx = await registerFunc(params.endpoint, descriptors, txOptions);
213
- const receipt = await tx.wait(2); // L-2: Wait for 2 confirmations
214
-
215
- return receipt.hash;
216
- } catch (error: any) {
217
- throw new TransactionRevertedError(
218
- error.transactionHash,
219
- `Agent registration failed: ${error.reason || error.message}`
220
- );
221
- }
222
- }
223
-
224
- /**
225
- * Update agent endpoint URL
226
- *
227
- * @param newEndpoint - New webhook/IPFS gateway URL
228
- */
229
- async updateEndpoint(newEndpoint: string): Promise<string> {
230
- // SECURITY FIX (H-1): Await async endpoint validation (DNS resolution + SSRF check)
231
- await validateEndpointURL(newEndpoint, 'newEndpoint');
232
-
233
- try {
234
- const updateFunc = this.contract.getFunction('updateEndpoint');
235
- const estimatedGas = await updateFunc.estimateGas(newEndpoint);
236
- const txOptions = this.buildTxOptions(estimatedGas, 'updateEndpoint');
237
-
238
- const tx = await updateFunc(newEndpoint, txOptions);
239
- const receipt = await tx.wait(2); // L-2: Wait for 2 confirmations
240
-
241
- return receipt.hash;
242
- } catch (error: any) {
243
- throw new TransactionRevertedError(
244
- error.transactionHash,
245
- `Endpoint update failed: ${error.reason || error.message}`
246
- );
247
- }
248
- }
249
-
250
- /**
251
- * Add a new service type to agent profile
252
- *
253
- * @param serviceType - Lowercase service type (e.g., "code-review")
254
- */
255
- async addServiceType(serviceType: string): Promise<string> {
256
- // Validate format
257
- this.computeServiceTypeHash(serviceType);
258
-
259
- try {
260
- const addFunc = this.contract.getFunction('addServiceType');
261
- const estimatedGas = await addFunc.estimateGas(serviceType);
262
- const txOptions = this.buildTxOptions(estimatedGas, 'addServiceType');
263
-
264
- const tx = await addFunc(serviceType, txOptions);
265
- const receipt = await tx.wait(2); // L-2: Wait for 2 confirmations
266
-
267
- return receipt.hash;
268
- } catch (error: any) {
269
- throw new TransactionRevertedError(
270
- error.transactionHash,
271
- `Add service type failed: ${error.reason || error.message}`
272
- );
273
- }
274
- }
275
-
276
- /**
277
- * Remove a service type from agent profile
278
- *
279
- * @param serviceTypeHash - Hash of service type to remove
280
- */
281
- async removeServiceType(serviceTypeHash: string): Promise<string> {
282
- // C-3: Validate hash format (bytes32)
283
- if (!serviceTypeHash || !/^0x[a-fA-F0-9]{64}$/.test(serviceTypeHash)) {
284
- throw new ValidationError('serviceTypeHash', 'Must be valid bytes32 hex string (0x + 64 hex chars)');
285
- }
286
- if (serviceTypeHash === '0x0000000000000000000000000000000000000000000000000000000000000000') {
287
- throw new ValidationError('serviceTypeHash', 'Cannot be zero hash');
288
- }
289
-
290
- try {
291
- const removeFunc = this.contract.getFunction('removeServiceType');
292
- const estimatedGas = await removeFunc.estimateGas(serviceTypeHash);
293
- const txOptions = this.buildTxOptions(estimatedGas, 'removeServiceType');
294
-
295
- const tx = await removeFunc(serviceTypeHash, txOptions);
296
- const receipt = await tx.wait(2); // L-2: Wait for 2 confirmations
297
-
298
- return receipt.hash;
299
- } catch (error: any) {
300
- throw new TransactionRevertedError(
301
- error.transactionHash,
302
- `Remove service type failed: ${error.reason || error.message}`
303
- );
304
- }
305
- }
306
-
307
- /**
308
- * Set agent active/inactive status
309
- *
310
- * @param isActive - Whether agent is accepting new requests
311
- */
312
- async setActiveStatus(isActive: boolean): Promise<string> {
313
- try {
314
- const setFunc = this.contract.getFunction('setActiveStatus');
315
- const estimatedGas = await setFunc.estimateGas(isActive);
316
- const txOptions = this.buildTxOptions(estimatedGas, 'setActiveStatus');
317
-
318
- const tx = await setFunc(isActive, txOptions);
319
- const receipt = await tx.wait(2); // L-2: Wait for 2 confirmations
320
-
321
- return receipt.hash;
322
- } catch (error: any) {
323
- throw new TransactionRevertedError(
324
- error.transactionHash,
325
- `Set active status failed: ${error.reason || error.message}`
326
- );
327
- }
328
- }
329
-
330
- // ========== READ FUNCTIONS ==========
331
-
332
- /**
333
- * Get agent profile by address
334
- *
335
- * @param agentAddress - Agent's Ethereum address
336
- * @returns Agent profile or null if not registered
337
- */
338
- async getAgent(agentAddress: string): Promise<AgentProfile | null> {
339
- validateAddress(agentAddress, 'agentAddress');
340
-
341
- const profile = await this.contract.getAgent(agentAddress);
342
-
343
- // Check if registered (registeredAt > 0)
344
- if (profile.registeredAt === 0n) {
345
- return null;
346
- }
347
-
348
- return this._mapProfile(profile);
349
- }
350
-
351
- /**
352
- * Get agent profile by DID
353
- *
354
- * @param did - Agent's DID (e.g., "did:ethr:8453:0x...")
355
- * @returns Agent profile or null if not found
356
- */
357
- async getAgentByDID(did: string): Promise<AgentProfile | null> {
358
- // H-2: Strengthen DID validation
359
- const didPattern = /^did:ethr:(\d+):(0x[a-fA-F0-9]{40})$/;
360
- const match = did.match(didPattern);
361
-
362
- if (!match) {
363
- throw new ValidationError(
364
- 'did',
365
- 'Invalid DID format. Expected did:ethr:<chainId>:<address> (e.g., did:ethr:84532:0x1234...)'
366
- );
367
- }
368
-
369
- const [, chainIdStr] = match;
370
-
371
- // Validate chain ID matches
372
- const currentChainId = await this.getChainId();
373
- if (parseInt(chainIdStr, 10) !== currentChainId) {
374
- throw new ValidationError(
375
- 'did',
376
- `DID chain ID (${chainIdStr}) does not match registry chain ID (${currentChainId})`
377
- );
378
- }
379
-
380
- const profile = await this.contract.getAgentByDID(did);
381
-
382
- if (profile.registeredAt === 0n) {
383
- return null;
384
- }
385
-
386
- return this._mapProfile(profile);
387
- }
388
-
389
- /**
390
- * Query agents by service type
391
- *
392
- * **IMPORTANT - Query Cap Limitation (L-4)**:
393
- *
394
- * This method will throw `QueryCapExceededError` when the registry contains
395
- * more than 1000 agents. This is an intentional DoS prevention measure.
396
- *
397
- * When you encounter this error, migrate to an off-chain indexer:
398
- * - The Graph: https://thegraph.com/
399
- * - Goldsky: https://goldsky.com/
400
- * - Alchemy Subgraphs: https://docs.alchemy.com/docs/subgraphs-overview
401
- *
402
- * See `QueryCapExceededError` documentation for event schemas to index.
403
- *
404
- * @param params - Query parameters
405
- * @returns List of agent addresses matching criteria
406
- * @throws {QueryCapExceededError} When registry exceeds 1000 agents
407
- * @throws {ValidationError} For invalid parameters
408
- *
409
- * @example
410
- * ```typescript
411
- * try {
412
- * const agents = await registry.queryAgentsByService({
413
- * serviceTypeHash: registry.computeServiceTypeHash('text-generation'),
414
- * minReputation: 5000,
415
- * limit: 50
416
- * });
417
- * } catch (error) {
418
- * if (error instanceof QueryCapExceededError) {
419
- * console.log('Registry too large, use off-chain indexer');
420
- * // Fallback to your indexer implementation
421
- * }
422
- * }
423
- * ```
424
- */
425
- async queryAgentsByService(params: QueryAgentsParams): Promise<string[]> {
426
- // H-3: Add pagination bounds and validation
427
- // Validate serviceTypeHash format
428
- if (!params.serviceTypeHash || !/^0x[a-fA-F0-9]{64}$/.test(params.serviceTypeHash)) {
429
- throw new ValidationError('serviceTypeHash', 'Must be valid bytes32 hex string');
430
- }
431
-
432
- // Validate reputation bounds (0-10000 scale)
433
- const reputation = params.minReputation ?? 0;
434
- if (reputation < 0 || reputation > 10000) {
435
- throw new ValidationError('minReputation', 'Must be between 0 and 10000');
436
- }
437
-
438
- // Validate offset
439
- const offset = params.offset ?? 0;
440
- if (offset < 0) {
441
- throw new ValidationError('offset', 'Cannot be negative');
442
- }
443
-
444
- // Validate and cap limit
445
- const MAX_LIMIT = 1000;
446
- let limit = params.limit ?? 100;
447
- if (limit <= 0) {
448
- throw new ValidationError('limit', 'Must be positive');
449
- }
450
- if (limit > MAX_LIMIT) {
451
- limit = MAX_LIMIT; // Cap silently
452
- }
453
-
454
- try {
455
- const agents = await this.contract.queryAgentsByService(
456
- params.serviceTypeHash,
457
- reputation,
458
- offset,
459
- limit
460
- );
461
-
462
- return agents;
463
- } catch (error: any) {
464
- // [L-4] Catch query cap exceeded revert and throw descriptive error
465
- const message = error.message || error.reason || '';
466
- if (message.includes('Too many agents')) {
467
- // Extract approximate size from error or use default
468
- throw new QueryCapExceededError(1001, 1000);
469
- }
470
- throw error;
471
- }
472
- }
473
-
474
- /**
475
- * Get service descriptors for an agent
476
- *
477
- * @param agentAddress - Agent's Ethereum address
478
- * @returns List of service descriptors
479
- */
480
- async getServiceDescriptors(agentAddress: string): Promise<ServiceDescriptor[]> {
481
- validateAddress(agentAddress, 'agentAddress');
482
-
483
- const descriptors = await this.contract.getServiceDescriptors(agentAddress);
484
-
485
- return descriptors.map((d: any) => ({
486
- serviceTypeHash: d.serviceTypeHash,
487
- serviceType: d.serviceType,
488
- schemaURI: d.schemaURI,
489
- minPrice: d.minPrice,
490
- maxPrice: d.maxPrice,
491
- avgCompletionTime: Number(d.avgCompletionTime),
492
- metadataCID: d.metadataCID
493
- }));
494
- }
495
-
496
- /**
497
- * Check if agent supports a service type
498
- *
499
- * @param agentAddress - Agent's Ethereum address
500
- * @param serviceTypeHash - Service type hash
501
- * @returns True if supported
502
- */
503
- async supportsService(agentAddress: string, serviceTypeHash: string): Promise<boolean> {
504
- validateAddress(agentAddress, 'agentAddress');
505
- return await this.contract.supportsService(agentAddress, serviceTypeHash);
506
- }
507
-
508
- /**
509
- * Get the chain ID used for DID generation
510
- */
511
- async getChainId(): Promise<number> {
512
- return Number(await this.contract.chainId());
513
- }
514
-
515
- /**
516
- * Build a DID for an address on the current chain
517
- *
518
- * @param address - Ethereum address
519
- * @returns DID string (e.g., "did:ethr:8453:0x...")
520
- */
521
- async buildDID(address: string): Promise<string> {
522
- validateAddress(address, 'address');
523
- const chainId = await this.getChainId();
524
- return `did:ethr:${chainId}:${address.toLowerCase()}`;
525
- }
526
-
527
- // ========== PRIVATE HELPERS ==========
528
-
529
- /**
530
- * Safely convert bigint to number, throwing if precision would be lost
531
- */
532
- private safeToNumber(value: bigint | number, fieldName: string): number {
533
- const bigIntValue = typeof value === 'bigint' ? value : BigInt(value);
534
- if (bigIntValue > BigInt(Number.MAX_SAFE_INTEGER)) {
535
- throw new Error(`${fieldName} exceeds JavaScript MAX_SAFE_INTEGER`);
536
- }
537
- if (bigIntValue < BigInt(Number.MIN_SAFE_INTEGER)) {
538
- throw new Error(`${fieldName} below JavaScript MIN_SAFE_INTEGER`);
539
- }
540
- return Number(bigIntValue);
541
- }
542
-
543
- private _mapProfile(profile: any): AgentProfile {
544
- return {
545
- agentAddress: profile.agentAddress,
546
- did: profile.did,
547
- endpoint: profile.endpoint,
548
- serviceTypes: [...profile.serviceTypes], // H-4: Clone array
549
- stakedAmount: profile.stakedAmount,
550
- reputationScore: this.safeToNumber(profile.reputationScore, 'reputationScore'),
551
- totalTransactions: this.safeToNumber(profile.totalTransactions, 'totalTransactions'),
552
- disputedTransactions: this.safeToNumber(profile.disputedTransactions, 'disputedTransactions'),
553
- totalVolumeUSDC: profile.totalVolumeUSDC,
554
- registeredAt: this.safeToNumber(profile.registeredAt, 'registeredAt'),
555
- updatedAt: this.safeToNumber(profile.updatedAt, 'updatedAt'),
556
- isActive: Boolean(profile.isActive)
557
- };
558
- }
559
- }