@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.
- package/dist/ACTPClient.d.ts +18 -0
- package/dist/ACTPClient.d.ts.map +1 -1
- package/dist/ACTPClient.js +72 -23
- package/dist/ACTPClient.js.map +1 -1
- package/dist/adapters/BasicAdapter.d.ts +15 -0
- package/dist/adapters/BasicAdapter.d.ts.map +1 -1
- package/dist/adapters/BasicAdapter.js +33 -4
- package/dist/adapters/BasicAdapter.js.map +1 -1
- package/dist/adapters/StandardAdapter.d.ts +20 -3
- package/dist/adapters/StandardAdapter.d.ts.map +1 -1
- package/dist/adapters/StandardAdapter.js +90 -12
- package/dist/adapters/StandardAdapter.js.map +1 -1
- package/dist/cli/commands/publish.js +16 -4
- package/dist/cli/commands/publish.js.map +1 -1
- package/dist/cli/commands/register.js +16 -4
- package/dist/cli/commands/register.js.map +1 -1
- package/dist/cli/commands/tx.js +31 -3
- package/dist/cli/commands/tx.js.map +1 -1
- package/dist/config/networks.d.ts +10 -2
- package/dist/config/networks.d.ts.map +1 -1
- package/dist/config/networks.js +31 -22
- package/dist/config/networks.js.map +1 -1
- package/dist/level0/request.d.ts.map +1 -1
- package/dist/level0/request.js +2 -1
- package/dist/level0/request.js.map +1 -1
- package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
- package/dist/runtime/BlockchainRuntime.js +11 -5
- package/dist/runtime/BlockchainRuntime.js.map +1 -1
- package/dist/utils/IPFSClient.d.ts +3 -1
- package/dist/utils/IPFSClient.d.ts.map +1 -1
- package/dist/utils/IPFSClient.js +27 -7
- package/dist/utils/IPFSClient.js.map +1 -1
- package/dist/wallet/AutoWalletProvider.d.ts +11 -1
- package/dist/wallet/AutoWalletProvider.d.ts.map +1 -1
- package/dist/wallet/AutoWalletProvider.js +84 -19
- package/dist/wallet/AutoWalletProvider.js.map +1 -1
- package/dist/wallet/IWalletProvider.d.ts +34 -0
- package/dist/wallet/IWalletProvider.d.ts.map +1 -1
- package/dist/wallet/SmartWalletRouter.d.ts +128 -0
- package/dist/wallet/SmartWalletRouter.d.ts.map +1 -0
- package/dist/wallet/SmartWalletRouter.js +248 -0
- package/dist/wallet/SmartWalletRouter.js.map +1 -0
- package/dist/wallet/aa/DualNonceManager.d.ts +26 -1
- package/dist/wallet/aa/DualNonceManager.d.ts.map +1 -1
- package/dist/wallet/aa/DualNonceManager.js +140 -6
- package/dist/wallet/aa/DualNonceManager.js.map +1 -1
- package/package.json +3 -6
- package/src/ACTPClient.ts +0 -1579
- package/src/abi/ACTPKernel.json +0 -1356
- package/src/abi/AgentRegistry.json +0 -915
- package/src/abi/ERC20.json +0 -40
- package/src/abi/EscrowVault.json +0 -134
- package/src/abi/IdentityRegistry.json +0 -316
- package/src/adapters/AdapterRegistry.ts +0 -173
- package/src/adapters/AdapterRouter.ts +0 -416
- package/src/adapters/BaseAdapter.ts +0 -498
- package/src/adapters/BasicAdapter.ts +0 -514
- package/src/adapters/IAdapter.ts +0 -292
- package/src/adapters/StandardAdapter.ts +0 -555
- package/src/adapters/X402Adapter.ts +0 -731
- package/src/adapters/index.ts +0 -60
- package/src/builders/DeliveryProofBuilder.ts +0 -327
- package/src/builders/QuoteBuilder.ts +0 -483
- package/src/builders/index.ts +0 -17
- package/src/cli/commands/balance.ts +0 -110
- package/src/cli/commands/batch.ts +0 -487
- package/src/cli/commands/config.ts +0 -231
- package/src/cli/commands/deploy-check.ts +0 -364
- package/src/cli/commands/deploy-env.ts +0 -120
- package/src/cli/commands/diff.ts +0 -141
- package/src/cli/commands/init.ts +0 -469
- package/src/cli/commands/mint.ts +0 -116
- package/src/cli/commands/pay.ts +0 -113
- package/src/cli/commands/publish.ts +0 -475
- package/src/cli/commands/pull.ts +0 -124
- package/src/cli/commands/register.ts +0 -247
- package/src/cli/commands/simulate.ts +0 -345
- package/src/cli/commands/time.ts +0 -302
- package/src/cli/commands/tx.ts +0 -448
- package/src/cli/commands/watch.ts +0 -211
- package/src/cli/index.ts +0 -134
- package/src/cli/utils/client.ts +0 -252
- package/src/cli/utils/config.ts +0 -389
- package/src/cli/utils/output.ts +0 -465
- package/src/cli/utils/wallet.ts +0 -109
- package/src/config/agirailsmd.ts +0 -262
- package/src/config/networks.ts +0 -275
- package/src/config/pendingPublish.ts +0 -237
- package/src/config/publishPipeline.ts +0 -359
- package/src/config/syncOperations.ts +0 -279
- package/src/erc8004/ERC8004Bridge.ts +0 -462
- package/src/erc8004/ReputationReporter.ts +0 -468
- package/src/erc8004/index.ts +0 -61
- package/src/errors/index.ts +0 -427
- package/src/index.ts +0 -364
- package/src/level0/Provider.ts +0 -117
- package/src/level0/ServiceDirectory.ts +0 -131
- package/src/level0/index.ts +0 -10
- package/src/level0/provide.ts +0 -132
- package/src/level0/request.ts +0 -432
- package/src/level1/Agent.ts +0 -1426
- package/src/level1/index.ts +0 -10
- package/src/level1/pricing/PriceCalculator.ts +0 -255
- package/src/level1/pricing/PricingStrategy.ts +0 -198
- package/src/level1/types/Job.ts +0 -179
- package/src/level1/types/Options.ts +0 -291
- package/src/level1/types/index.ts +0 -8
- package/src/protocol/ACTPKernel.ts +0 -808
- package/src/protocol/AgentRegistry.ts +0 -559
- package/src/protocol/DIDManager.ts +0 -629
- package/src/protocol/DIDResolver.ts +0 -554
- package/src/protocol/EASHelper.ts +0 -378
- package/src/protocol/EscrowVault.ts +0 -255
- package/src/protocol/EventMonitor.ts +0 -204
- package/src/protocol/MessageSigner.ts +0 -510
- package/src/protocol/ProofGenerator.ts +0 -339
- package/src/protocol/QuoteBuilder.ts +0 -15
- package/src/registry/AgentRegistryClient.ts +0 -202
- package/src/runtime/BlockchainRuntime.ts +0 -1015
- package/src/runtime/IACTPRuntime.ts +0 -306
- package/src/runtime/MockRuntime.ts +0 -1298
- package/src/runtime/MockStateManager.ts +0 -577
- package/src/runtime/index.ts +0 -25
- package/src/runtime/types/MockState.ts +0 -237
- package/src/storage/ArchiveBundleBuilder.ts +0 -561
- package/src/storage/ArweaveClient.ts +0 -946
- package/src/storage/FilebaseClient.ts +0 -790
- package/src/storage/index.ts +0 -96
- package/src/storage/types.ts +0 -348
- package/src/types/adapter.ts +0 -310
- package/src/types/agent.ts +0 -79
- package/src/types/did.ts +0 -223
- package/src/types/eip712.ts +0 -175
- package/src/types/erc8004.ts +0 -293
- package/src/types/escrow.ts +0 -27
- package/src/types/index.ts +0 -17
- package/src/types/message.ts +0 -145
- package/src/types/state.ts +0 -87
- package/src/types/transaction.ts +0 -69
- package/src/types/x402.ts +0 -251
- package/src/utils/ErrorRecoveryGuide.ts +0 -676
- package/src/utils/Helpers.ts +0 -688
- package/src/utils/IPFSClient.ts +0 -368
- package/src/utils/Logger.ts +0 -484
- package/src/utils/NonceManager.ts +0 -591
- package/src/utils/RateLimiter.ts +0 -534
- package/src/utils/ReceivedNonceTracker.ts +0 -567
- package/src/utils/SDKLifecycle.ts +0 -416
- package/src/utils/SecureNonce.ts +0 -78
- package/src/utils/Semaphore.ts +0 -276
- package/src/utils/UsedAttestationTracker.ts +0 -385
- package/src/utils/canonicalJson.ts +0 -38
- package/src/utils/circuitBreaker.ts +0 -324
- package/src/utils/computeTypeHash.ts +0 -48
- package/src/utils/fsSafe.ts +0 -80
- package/src/utils/index.ts +0 -80
- package/src/utils/retry.ts +0 -364
- package/src/utils/security.ts +0 -418
- package/src/utils/validation.ts +0 -540
- package/src/wallet/AutoWalletProvider.ts +0 -299
- package/src/wallet/EOAWalletProvider.ts +0 -69
- package/src/wallet/IWalletProvider.ts +0 -135
- package/src/wallet/aa/BundlerClient.ts +0 -274
- package/src/wallet/aa/DualNonceManager.ts +0 -173
- package/src/wallet/aa/PaymasterClient.ts +0 -174
- package/src/wallet/aa/TransactionBatcher.ts +0 -353
- package/src/wallet/aa/UserOpBuilder.ts +0 -246
- package/src/wallet/aa/constants.ts +0 -60
- 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
|
-
}
|