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