@t402/erc8004 2.7.0
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/cjs/index.cjs +704 -0
- package/dist/cjs/index.d.ts +978 -0
- package/dist/esm/index.d.mts +978 -0
- package/dist/esm/index.mjs +647 -0
- package/package.json +73 -0
|
@@ -0,0 +1,647 @@
|
|
|
1
|
+
// src/constants.ts
|
|
2
|
+
var IDENTITY_REGISTRIES = {};
|
|
3
|
+
var REPUTATION_REGISTRIES = {};
|
|
4
|
+
var VALIDATION_REGISTRIES = {};
|
|
5
|
+
var ERC8004_EXTENSION_KEY = "erc8004";
|
|
6
|
+
var FEEDBACK_TAGS = {
|
|
7
|
+
/** tag1: Payment completed successfully */
|
|
8
|
+
PAYMENT_SUCCESS: "paymentSuccess",
|
|
9
|
+
/** tag1: Payment verification failed */
|
|
10
|
+
PAYMENT_FAILED: "paymentFailed",
|
|
11
|
+
/** tag1: Service quality rating */
|
|
12
|
+
SERVICE_QUALITY: "starred",
|
|
13
|
+
/** tag2: Response time measurement */
|
|
14
|
+
RESPONSE_TIME: "responseTime",
|
|
15
|
+
/** tag2: Uptime measurement */
|
|
16
|
+
UPTIME: "uptime"
|
|
17
|
+
};
|
|
18
|
+
var IDENTITY_REGISTRY_DOMAIN = {
|
|
19
|
+
name: "IdentityRegistry",
|
|
20
|
+
version: "1"
|
|
21
|
+
};
|
|
22
|
+
var SET_AGENT_WALLET_TYPES = {
|
|
23
|
+
SetAgentWallet: [
|
|
24
|
+
{ name: "agentId", type: "uint256" },
|
|
25
|
+
{ name: "newWallet", type: "address" },
|
|
26
|
+
{ name: "deadline", type: "uint256" },
|
|
27
|
+
{ name: "nonce", type: "uint256" }
|
|
28
|
+
]
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// src/abis.ts
|
|
32
|
+
var identityRegistryAbi = [
|
|
33
|
+
{
|
|
34
|
+
type: "function",
|
|
35
|
+
name: "register",
|
|
36
|
+
inputs: [
|
|
37
|
+
{ name: "agentURI", type: "string" },
|
|
38
|
+
{
|
|
39
|
+
name: "metadata",
|
|
40
|
+
type: "tuple[]",
|
|
41
|
+
components: [
|
|
42
|
+
{ name: "metadataKey", type: "string" },
|
|
43
|
+
{ name: "metadataValue", type: "bytes" }
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
outputs: [{ type: "uint256" }],
|
|
48
|
+
stateMutability: "nonpayable"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
type: "function",
|
|
52
|
+
name: "getAgentWallet",
|
|
53
|
+
inputs: [{ name: "agentId", type: "uint256" }],
|
|
54
|
+
outputs: [{ type: "address" }],
|
|
55
|
+
stateMutability: "view"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
type: "function",
|
|
59
|
+
name: "tokenURI",
|
|
60
|
+
inputs: [{ name: "tokenId", type: "uint256" }],
|
|
61
|
+
outputs: [{ type: "string" }],
|
|
62
|
+
stateMutability: "view"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
type: "function",
|
|
66
|
+
name: "ownerOf",
|
|
67
|
+
inputs: [{ name: "tokenId", type: "uint256" }],
|
|
68
|
+
outputs: [{ type: "address" }],
|
|
69
|
+
stateMutability: "view"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
type: "function",
|
|
73
|
+
name: "getMetadata",
|
|
74
|
+
inputs: [
|
|
75
|
+
{ name: "agentId", type: "uint256" },
|
|
76
|
+
{ name: "metadataKey", type: "string" }
|
|
77
|
+
],
|
|
78
|
+
outputs: [{ type: "bytes" }],
|
|
79
|
+
stateMutability: "view"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
type: "function",
|
|
83
|
+
name: "setAgentWallet",
|
|
84
|
+
inputs: [
|
|
85
|
+
{ name: "agentId", type: "uint256" },
|
|
86
|
+
{ name: "newWallet", type: "address" },
|
|
87
|
+
{ name: "deadline", type: "uint256" },
|
|
88
|
+
{ name: "signature", type: "bytes" }
|
|
89
|
+
],
|
|
90
|
+
outputs: [],
|
|
91
|
+
stateMutability: "nonpayable"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
type: "event",
|
|
95
|
+
name: "Registered",
|
|
96
|
+
inputs: [
|
|
97
|
+
{ name: "agentId", type: "uint256", indexed: true },
|
|
98
|
+
{ name: "agentURI", type: "string", indexed: false },
|
|
99
|
+
{ name: "owner", type: "address", indexed: true }
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
];
|
|
103
|
+
var reputationRegistryAbi = [
|
|
104
|
+
{
|
|
105
|
+
type: "function",
|
|
106
|
+
name: "giveFeedback",
|
|
107
|
+
inputs: [
|
|
108
|
+
{ name: "agentId", type: "uint256" },
|
|
109
|
+
{ name: "value", type: "int128" },
|
|
110
|
+
{ name: "valueDecimals", type: "uint8" },
|
|
111
|
+
{ name: "tag1", type: "string" },
|
|
112
|
+
{ name: "tag2", type: "string" },
|
|
113
|
+
{ name: "endpoint", type: "string" },
|
|
114
|
+
{ name: "feedbackURI", type: "string" },
|
|
115
|
+
{ name: "feedbackHash", type: "bytes32" }
|
|
116
|
+
],
|
|
117
|
+
outputs: [],
|
|
118
|
+
stateMutability: "nonpayable"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
type: "function",
|
|
122
|
+
name: "getSummary",
|
|
123
|
+
inputs: [
|
|
124
|
+
{ name: "agentId", type: "uint256" },
|
|
125
|
+
{ name: "clientAddresses", type: "address[]" },
|
|
126
|
+
{ name: "tag1", type: "string" },
|
|
127
|
+
{ name: "tag2", type: "string" }
|
|
128
|
+
],
|
|
129
|
+
outputs: [
|
|
130
|
+
{ name: "count", type: "uint64" },
|
|
131
|
+
{ name: "summaryValue", type: "int128" },
|
|
132
|
+
{ name: "summaryValueDecimals", type: "uint8" }
|
|
133
|
+
],
|
|
134
|
+
stateMutability: "view"
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
type: "function",
|
|
138
|
+
name: "revokeFeedback",
|
|
139
|
+
inputs: [
|
|
140
|
+
{ name: "agentId", type: "uint256" },
|
|
141
|
+
{ name: "feedbackIndex", type: "uint64" }
|
|
142
|
+
],
|
|
143
|
+
outputs: [],
|
|
144
|
+
stateMutability: "nonpayable"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
type: "function",
|
|
148
|
+
name: "getClients",
|
|
149
|
+
inputs: [{ name: "agentId", type: "uint256" }],
|
|
150
|
+
outputs: [{ type: "address[]" }],
|
|
151
|
+
stateMutability: "view"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
type: "event",
|
|
155
|
+
name: "NewFeedback",
|
|
156
|
+
inputs: [
|
|
157
|
+
{ name: "agentId", type: "uint256", indexed: true },
|
|
158
|
+
{ name: "clientAddress", type: "address", indexed: true },
|
|
159
|
+
{ name: "feedbackIndex", type: "uint64", indexed: false },
|
|
160
|
+
{ name: "value", type: "int128", indexed: false },
|
|
161
|
+
{ name: "valueDecimals", type: "uint8", indexed: false },
|
|
162
|
+
{ name: "indexedTag1", type: "string", indexed: true },
|
|
163
|
+
{ name: "tag1", type: "string", indexed: false },
|
|
164
|
+
{ name: "tag2", type: "string", indexed: false },
|
|
165
|
+
{ name: "endpoint", type: "string", indexed: false },
|
|
166
|
+
{ name: "feedbackURI", type: "string", indexed: false },
|
|
167
|
+
{ name: "feedbackHash", type: "bytes32", indexed: false }
|
|
168
|
+
]
|
|
169
|
+
}
|
|
170
|
+
];
|
|
171
|
+
var validationRegistryAbi = [
|
|
172
|
+
{
|
|
173
|
+
type: "function",
|
|
174
|
+
name: "validationRequest",
|
|
175
|
+
inputs: [
|
|
176
|
+
{ name: "validatorAddress", type: "address" },
|
|
177
|
+
{ name: "agentId", type: "uint256" },
|
|
178
|
+
{ name: "requestURI", type: "string" },
|
|
179
|
+
{ name: "requestHash", type: "bytes32" }
|
|
180
|
+
],
|
|
181
|
+
outputs: [],
|
|
182
|
+
stateMutability: "nonpayable"
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
type: "function",
|
|
186
|
+
name: "validationResponse",
|
|
187
|
+
inputs: [
|
|
188
|
+
{ name: "requestHash", type: "bytes32" },
|
|
189
|
+
{ name: "response", type: "uint8" },
|
|
190
|
+
{ name: "responseURI", type: "string" },
|
|
191
|
+
{ name: "responseHash", type: "bytes32" },
|
|
192
|
+
{ name: "tag", type: "string" }
|
|
193
|
+
],
|
|
194
|
+
outputs: [],
|
|
195
|
+
stateMutability: "nonpayable"
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
type: "function",
|
|
199
|
+
name: "getValidationStatus",
|
|
200
|
+
inputs: [{ name: "requestHash", type: "bytes32" }],
|
|
201
|
+
outputs: [
|
|
202
|
+
{ name: "validatorAddress", type: "address" },
|
|
203
|
+
{ name: "agentId", type: "uint256" },
|
|
204
|
+
{ name: "response", type: "uint8" },
|
|
205
|
+
{ name: "responseHash", type: "bytes32" },
|
|
206
|
+
{ name: "tag", type: "string" },
|
|
207
|
+
{ name: "lastUpdate", type: "uint256" }
|
|
208
|
+
],
|
|
209
|
+
stateMutability: "view"
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
type: "function",
|
|
213
|
+
name: "getSummary",
|
|
214
|
+
inputs: [
|
|
215
|
+
{ name: "agentId", type: "uint256" },
|
|
216
|
+
{ name: "validatorAddresses", type: "address[]" },
|
|
217
|
+
{ name: "tag", type: "string" }
|
|
218
|
+
],
|
|
219
|
+
outputs: [
|
|
220
|
+
{ name: "count", type: "uint64" },
|
|
221
|
+
{ name: "averageResponse", type: "uint8" }
|
|
222
|
+
],
|
|
223
|
+
stateMutability: "view"
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
type: "event",
|
|
227
|
+
name: "ValidationRequest",
|
|
228
|
+
inputs: [
|
|
229
|
+
{ name: "validatorAddress", type: "address", indexed: true },
|
|
230
|
+
{ name: "agentId", type: "uint256", indexed: true },
|
|
231
|
+
{ name: "requestURI", type: "string", indexed: false },
|
|
232
|
+
{ name: "requestHash", type: "bytes32", indexed: true }
|
|
233
|
+
]
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
type: "event",
|
|
237
|
+
name: "ValidationResponse",
|
|
238
|
+
inputs: [
|
|
239
|
+
{ name: "validatorAddress", type: "address", indexed: true },
|
|
240
|
+
{ name: "agentId", type: "uint256", indexed: true },
|
|
241
|
+
{ name: "requestHash", type: "bytes32", indexed: true },
|
|
242
|
+
{ name: "response", type: "uint8", indexed: false },
|
|
243
|
+
{ name: "responseURI", type: "string", indexed: false },
|
|
244
|
+
{ name: "responseHash", type: "bytes32", indexed: false },
|
|
245
|
+
{ name: "tag", type: "string", indexed: false }
|
|
246
|
+
]
|
|
247
|
+
}
|
|
248
|
+
];
|
|
249
|
+
|
|
250
|
+
// src/identity.ts
|
|
251
|
+
function parseAgentRegistry(registryId) {
|
|
252
|
+
const parts = registryId.split(":");
|
|
253
|
+
if (parts.length < 3) {
|
|
254
|
+
throw new Error(
|
|
255
|
+
`Invalid agent registry ID: ${registryId}. Expected format: namespace:chainId:address`
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
const namespace = parts[0];
|
|
259
|
+
const chainId = parts[1];
|
|
260
|
+
const address = parts.slice(2).join(":");
|
|
261
|
+
if (!namespace || !chainId || !address) {
|
|
262
|
+
throw new Error(
|
|
263
|
+
`Invalid agent registry ID: ${registryId}. All parts must be non-empty`
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
return { namespace, chainId, address, id: registryId };
|
|
267
|
+
}
|
|
268
|
+
async function getAgentIdentity(client, identityRegistry, agentId, registryId) {
|
|
269
|
+
const [agentWallet, owner, agentURI] = await Promise.all([
|
|
270
|
+
client.readContract({
|
|
271
|
+
address: identityRegistry,
|
|
272
|
+
abi: identityRegistryAbi,
|
|
273
|
+
functionName: "getAgentWallet",
|
|
274
|
+
args: [agentId]
|
|
275
|
+
}),
|
|
276
|
+
client.readContract({
|
|
277
|
+
address: identityRegistry,
|
|
278
|
+
abi: identityRegistryAbi,
|
|
279
|
+
functionName: "ownerOf",
|
|
280
|
+
args: [agentId]
|
|
281
|
+
}),
|
|
282
|
+
client.readContract({
|
|
283
|
+
address: identityRegistry,
|
|
284
|
+
abi: identityRegistryAbi,
|
|
285
|
+
functionName: "tokenURI",
|
|
286
|
+
args: [agentId]
|
|
287
|
+
})
|
|
288
|
+
]);
|
|
289
|
+
return {
|
|
290
|
+
agentId,
|
|
291
|
+
owner,
|
|
292
|
+
agentURI,
|
|
293
|
+
agentWallet,
|
|
294
|
+
registry: parseAgentRegistry(registryId)
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
async function fetchRegistrationFile(agentURI) {
|
|
298
|
+
const response = await fetch(agentURI);
|
|
299
|
+
if (!response.ok) {
|
|
300
|
+
throw new Error(
|
|
301
|
+
`Failed to fetch registration file from ${agentURI}: ${response.status}`
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
return response.json();
|
|
305
|
+
}
|
|
306
|
+
async function resolveAgent(client, identityRegistry, agentId, registryId) {
|
|
307
|
+
const identity = await getAgentIdentity(
|
|
308
|
+
client,
|
|
309
|
+
identityRegistry,
|
|
310
|
+
agentId,
|
|
311
|
+
registryId
|
|
312
|
+
);
|
|
313
|
+
const registration = await fetchRegistrationFile(identity.agentURI);
|
|
314
|
+
return { ...identity, registration };
|
|
315
|
+
}
|
|
316
|
+
async function verifyPayToMatchesAgent(client, identityRegistry, agentId, payTo) {
|
|
317
|
+
const agentWallet = await client.readContract({
|
|
318
|
+
address: identityRegistry,
|
|
319
|
+
abi: identityRegistryAbi,
|
|
320
|
+
functionName: "getAgentWallet",
|
|
321
|
+
args: [agentId]
|
|
322
|
+
});
|
|
323
|
+
return agentWallet.toLowerCase() === payTo.toLowerCase();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// src/reputation.ts
|
|
327
|
+
async function getReputationSummary(client, reputationRegistry, agentId, trustedReviewers, tag1 = "", tag2 = "") {
|
|
328
|
+
const result = await client.readContract({
|
|
329
|
+
address: reputationRegistry,
|
|
330
|
+
abi: reputationRegistryAbi,
|
|
331
|
+
functionName: "getSummary",
|
|
332
|
+
args: [agentId, trustedReviewers, tag1, tag2]
|
|
333
|
+
});
|
|
334
|
+
const [count, summaryValue, summaryValueDecimals] = result;
|
|
335
|
+
const divisor = 10 ** summaryValueDecimals;
|
|
336
|
+
const normalizedScore = count > 0n ? Math.min(100, Math.max(0, Number(summaryValue) / divisor)) : 0;
|
|
337
|
+
return {
|
|
338
|
+
agentId,
|
|
339
|
+
count,
|
|
340
|
+
summaryValue,
|
|
341
|
+
summaryValueDecimals,
|
|
342
|
+
normalizedScore
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
function buildFeedbackFile(agentId, agentRegistry, clientAddress, value, valueDecimals, tag1, tag2, proofOfPayment) {
|
|
346
|
+
return {
|
|
347
|
+
agentRegistry,
|
|
348
|
+
agentId,
|
|
349
|
+
clientAddress,
|
|
350
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
351
|
+
value,
|
|
352
|
+
valueDecimals,
|
|
353
|
+
tag1,
|
|
354
|
+
tag2,
|
|
355
|
+
...proofOfPayment && { proofOfPayment }
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
async function submitFeedback(client, reputationRegistry, params) {
|
|
359
|
+
return client.writeContract({
|
|
360
|
+
address: reputationRegistry,
|
|
361
|
+
abi: reputationRegistryAbi,
|
|
362
|
+
functionName: "giveFeedback",
|
|
363
|
+
args: [
|
|
364
|
+
params.agentId,
|
|
365
|
+
params.value,
|
|
366
|
+
params.valueDecimals,
|
|
367
|
+
params.tag1,
|
|
368
|
+
params.tag2,
|
|
369
|
+
params.endpoint ?? "",
|
|
370
|
+
params.feedbackURI ?? "",
|
|
371
|
+
params.feedbackHash ?? "0x" + "0".repeat(64)
|
|
372
|
+
]
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// src/validation.ts
|
|
377
|
+
async function submitValidationRequest(client, validationRegistry, params) {
|
|
378
|
+
return client.writeContract({
|
|
379
|
+
address: validationRegistry,
|
|
380
|
+
abi: validationRegistryAbi,
|
|
381
|
+
functionName: "validationRequest",
|
|
382
|
+
args: [
|
|
383
|
+
params.validatorAddress,
|
|
384
|
+
params.agentId,
|
|
385
|
+
params.requestURI,
|
|
386
|
+
params.requestHash
|
|
387
|
+
]
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
async function getValidationStatus(client, validationRegistry, requestHash) {
|
|
391
|
+
const result = await client.readContract({
|
|
392
|
+
address: validationRegistry,
|
|
393
|
+
abi: validationRegistryAbi,
|
|
394
|
+
functionName: "getValidationStatus",
|
|
395
|
+
args: [requestHash]
|
|
396
|
+
});
|
|
397
|
+
return {
|
|
398
|
+
validatorAddress: result[0],
|
|
399
|
+
agentId: result[1],
|
|
400
|
+
response: result[2],
|
|
401
|
+
responseHash: result[3],
|
|
402
|
+
tag: result[4],
|
|
403
|
+
lastUpdate: result[5]
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
async function getValidationSummary(client, validationRegistry, agentId, validatorAddresses, tag = "") {
|
|
407
|
+
const result = await client.readContract({
|
|
408
|
+
address: validationRegistry,
|
|
409
|
+
abi: validationRegistryAbi,
|
|
410
|
+
functionName: "getSummary",
|
|
411
|
+
args: [agentId, validatorAddresses, tag]
|
|
412
|
+
});
|
|
413
|
+
return {
|
|
414
|
+
count: result[0],
|
|
415
|
+
averageResponse: result[1]
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// src/extension.ts
|
|
420
|
+
function declareERC8004Extension(agentId, agentRegistry, agentWallet) {
|
|
421
|
+
return {
|
|
422
|
+
agentId,
|
|
423
|
+
agentRegistry,
|
|
424
|
+
...agentWallet && { agentWallet }
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
function getERC8004Extension(paymentRequired) {
|
|
428
|
+
return paymentRequired.extensions?.[ERC8004_EXTENSION_KEY];
|
|
429
|
+
}
|
|
430
|
+
function createERC8004PayloadExtension(agentId, agentRegistry, verified) {
|
|
431
|
+
return {
|
|
432
|
+
identityVerified: verified,
|
|
433
|
+
agentId,
|
|
434
|
+
agentRegistry
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
async function verifyAgentIdentity(client, paymentRequired) {
|
|
438
|
+
const ext = getERC8004Extension(paymentRequired);
|
|
439
|
+
if (!ext) return false;
|
|
440
|
+
const registry = ext.agentRegistry.split(":");
|
|
441
|
+
const registryAddress = registry.slice(2).join(":");
|
|
442
|
+
for (const accept of paymentRequired.accepts) {
|
|
443
|
+
const matches = await verifyPayToMatchesAgent(
|
|
444
|
+
client,
|
|
445
|
+
registryAddress,
|
|
446
|
+
BigInt(ext.agentId),
|
|
447
|
+
accept.payTo
|
|
448
|
+
);
|
|
449
|
+
if (!matches) return false;
|
|
450
|
+
}
|
|
451
|
+
return true;
|
|
452
|
+
}
|
|
453
|
+
function erc8004ResourceServerExtension(config) {
|
|
454
|
+
return {
|
|
455
|
+
key: ERC8004_EXTENSION_KEY,
|
|
456
|
+
enrichDeclaration: async (declaration) => {
|
|
457
|
+
let enriched = declaration;
|
|
458
|
+
if (config.reputationRegistry && config.trustedReviewers?.length) {
|
|
459
|
+
const summary = await getReputationSummary(
|
|
460
|
+
config.client,
|
|
461
|
+
config.reputationRegistry,
|
|
462
|
+
BigInt(enriched.agentId),
|
|
463
|
+
config.trustedReviewers
|
|
464
|
+
);
|
|
465
|
+
enriched = {
|
|
466
|
+
...enriched,
|
|
467
|
+
reputationScore: summary.normalizedScore,
|
|
468
|
+
feedbackCount: Number(summary.count)
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
if (config.validationRegistry && config.trustedValidators?.length) {
|
|
472
|
+
const summary = await getValidationSummary(
|
|
473
|
+
config.client,
|
|
474
|
+
config.validationRegistry,
|
|
475
|
+
BigInt(enriched.agentId),
|
|
476
|
+
config.trustedValidators
|
|
477
|
+
);
|
|
478
|
+
enriched = {
|
|
479
|
+
...enriched,
|
|
480
|
+
validationScore: summary.averageResponse
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
return enriched;
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// src/hooks.ts
|
|
489
|
+
import { getPaymentRequired } from "@t402/core/types";
|
|
490
|
+
function erc8004IdentityCheck(client, options = {}) {
|
|
491
|
+
const { abortOnFailure = true, abortOnMissing = false } = options;
|
|
492
|
+
return async (context) => {
|
|
493
|
+
const ext = getERC8004Extension(context.paymentRequired);
|
|
494
|
+
if (!ext) {
|
|
495
|
+
if (abortOnMissing) {
|
|
496
|
+
return {
|
|
497
|
+
abort: true,
|
|
498
|
+
reason: "ERC-8004 extension not present in payment requirements"
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
const verified = await verifyAgentIdentity(
|
|
504
|
+
client,
|
|
505
|
+
context.paymentRequired
|
|
506
|
+
);
|
|
507
|
+
if (!verified && abortOnFailure) {
|
|
508
|
+
return {
|
|
509
|
+
abort: true,
|
|
510
|
+
reason: `ERC-8004 identity verification failed for agent ${ext.agentId} on registry ${ext.agentRegistry}`
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
async function verifyAgentIdentityFromTask(client, task) {
|
|
516
|
+
const paymentRequired = getPaymentRequired(task);
|
|
517
|
+
if (!paymentRequired) return false;
|
|
518
|
+
return verifyAgentIdentity(client, paymentRequired);
|
|
519
|
+
}
|
|
520
|
+
function getPayloadExtension(context) {
|
|
521
|
+
return context.paymentPayload.extensions?.[ERC8004_EXTENSION_KEY];
|
|
522
|
+
}
|
|
523
|
+
function erc8004ReputationCheck(client, reputationRegistry, config) {
|
|
524
|
+
return async (context) => {
|
|
525
|
+
const ext = getPayloadExtension(context);
|
|
526
|
+
if (!ext) return;
|
|
527
|
+
const summary = await getReputationSummary(
|
|
528
|
+
client,
|
|
529
|
+
reputationRegistry,
|
|
530
|
+
BigInt(ext.agentId),
|
|
531
|
+
config.trustedReviewers,
|
|
532
|
+
config.tag1 ?? "",
|
|
533
|
+
config.tag2 ?? ""
|
|
534
|
+
);
|
|
535
|
+
if (summary.normalizedScore < config.minScore) {
|
|
536
|
+
const action = config.onBelowThreshold ?? "reject";
|
|
537
|
+
if (action === "reject") {
|
|
538
|
+
return {
|
|
539
|
+
abort: true,
|
|
540
|
+
reason: `Agent ${ext.agentId} reputation score ${summary.normalizedScore} is below minimum ${config.minScore}`
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
console.warn(
|
|
544
|
+
`[erc8004] Agent ${ext.agentId} reputation score ${summary.normalizedScore} is below minimum ${config.minScore}`
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
function erc8004ServerIdentityCheck(client) {
|
|
550
|
+
return async (context) => {
|
|
551
|
+
const ext = getPayloadExtension(context);
|
|
552
|
+
if (!ext) return;
|
|
553
|
+
const registry = ext.agentRegistry.split(":");
|
|
554
|
+
const registryAddress = registry.slice(2).join(":");
|
|
555
|
+
const matches = await verifyPayToMatchesAgent(
|
|
556
|
+
client,
|
|
557
|
+
registryAddress,
|
|
558
|
+
BigInt(ext.agentId),
|
|
559
|
+
context.requirements.payTo
|
|
560
|
+
);
|
|
561
|
+
if (!matches) {
|
|
562
|
+
return {
|
|
563
|
+
abort: true,
|
|
564
|
+
reason: `payTo address ${context.requirements.payTo} does not match on-chain agentWallet for agent ${ext.agentId}`
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
function erc8004SubmitFeedback(writeClient, reputationRegistry, config = {}) {
|
|
570
|
+
return async (context) => {
|
|
571
|
+
const ext = getPayloadExtension(context);
|
|
572
|
+
if (!ext) return;
|
|
573
|
+
if (!context.result.success) return;
|
|
574
|
+
const tag1 = config.tag1 ?? FEEDBACK_TAGS.PAYMENT_SUCCESS;
|
|
575
|
+
const tag2 = config.tag2 ?? "";
|
|
576
|
+
let feedbackURI = "";
|
|
577
|
+
const feedbackHash = "0x" + "0".repeat(64);
|
|
578
|
+
if (config.includeProofOfPayment && context.result.transaction) {
|
|
579
|
+
buildFeedbackFile(
|
|
580
|
+
ext.agentId,
|
|
581
|
+
ext.agentRegistry,
|
|
582
|
+
context.result.payer ?? "",
|
|
583
|
+
100,
|
|
584
|
+
0,
|
|
585
|
+
tag1,
|
|
586
|
+
tag2,
|
|
587
|
+
{
|
|
588
|
+
fromAddress: context.result.payer ?? "",
|
|
589
|
+
toAddress: context.requirements.payTo,
|
|
590
|
+
chainId: context.requirements.network,
|
|
591
|
+
txHash: context.result.transaction
|
|
592
|
+
}
|
|
593
|
+
);
|
|
594
|
+
if (config.feedbackBaseURI) {
|
|
595
|
+
feedbackURI = `${config.feedbackBaseURI}/${context.result.transaction}.json`;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
submitFeedback(writeClient, reputationRegistry, {
|
|
599
|
+
agentId: BigInt(ext.agentId),
|
|
600
|
+
value: 100n,
|
|
601
|
+
valueDecimals: 0,
|
|
602
|
+
tag1,
|
|
603
|
+
tag2,
|
|
604
|
+
endpoint: context.paymentPayload.resource?.url,
|
|
605
|
+
feedbackURI,
|
|
606
|
+
feedbackHash
|
|
607
|
+
}).catch((err) => {
|
|
608
|
+
console.warn(
|
|
609
|
+
`[erc8004] Failed to submit feedback for agent ${ext.agentId}:`,
|
|
610
|
+
err
|
|
611
|
+
);
|
|
612
|
+
});
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
export {
|
|
616
|
+
ERC8004_EXTENSION_KEY,
|
|
617
|
+
FEEDBACK_TAGS,
|
|
618
|
+
IDENTITY_REGISTRIES,
|
|
619
|
+
IDENTITY_REGISTRY_DOMAIN,
|
|
620
|
+
REPUTATION_REGISTRIES,
|
|
621
|
+
SET_AGENT_WALLET_TYPES,
|
|
622
|
+
VALIDATION_REGISTRIES,
|
|
623
|
+
buildFeedbackFile,
|
|
624
|
+
createERC8004PayloadExtension,
|
|
625
|
+
declareERC8004Extension,
|
|
626
|
+
erc8004IdentityCheck,
|
|
627
|
+
erc8004ReputationCheck,
|
|
628
|
+
erc8004ResourceServerExtension,
|
|
629
|
+
erc8004ServerIdentityCheck,
|
|
630
|
+
erc8004SubmitFeedback,
|
|
631
|
+
fetchRegistrationFile,
|
|
632
|
+
getAgentIdentity,
|
|
633
|
+
getERC8004Extension,
|
|
634
|
+
getReputationSummary,
|
|
635
|
+
getValidationStatus,
|
|
636
|
+
getValidationSummary,
|
|
637
|
+
identityRegistryAbi,
|
|
638
|
+
parseAgentRegistry,
|
|
639
|
+
reputationRegistryAbi,
|
|
640
|
+
resolveAgent,
|
|
641
|
+
submitFeedback,
|
|
642
|
+
submitValidationRequest,
|
|
643
|
+
validationRegistryAbi,
|
|
644
|
+
verifyAgentIdentity,
|
|
645
|
+
verifyAgentIdentityFromTask,
|
|
646
|
+
verifyPayToMatchesAgent
|
|
647
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@t402/erc8004",
|
|
3
|
+
"version": "2.7.0",
|
|
4
|
+
"description": "ERC-8004 Trustless Agents integration for t402 payment protocol",
|
|
5
|
+
"main": "./dist/cjs/index.cjs",
|
|
6
|
+
"module": "./dist/esm/index.mjs",
|
|
7
|
+
"types": "./dist/esm/index.d.mts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./dist/esm/index.d.mts",
|
|
12
|
+
"default": "./dist/esm/index.mjs"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./dist/cjs/index.d.cts",
|
|
16
|
+
"default": "./dist/cjs/index.cjs"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@t402/core": "2.7.0"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"viem": "^2.0.0"
|
|
29
|
+
},
|
|
30
|
+
"peerDependenciesMeta": {
|
|
31
|
+
"viem": {
|
|
32
|
+
"optional": true
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^25.2.3",
|
|
37
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
38
|
+
"tsup": "^8.5.1",
|
|
39
|
+
"typescript": "^5.9.3",
|
|
40
|
+
"vite-tsconfig-paths": "^6.1.1",
|
|
41
|
+
"vitest": "^3.2.4"
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"t402",
|
|
45
|
+
"erc8004",
|
|
46
|
+
"trustless-agents",
|
|
47
|
+
"agent-identity",
|
|
48
|
+
"reputation",
|
|
49
|
+
"payments",
|
|
50
|
+
"crypto"
|
|
51
|
+
],
|
|
52
|
+
"author": "T402 Contributors",
|
|
53
|
+
"license": "Apache-2.0",
|
|
54
|
+
"repository": {
|
|
55
|
+
"type": "git",
|
|
56
|
+
"url": "https://github.com/t402-io/t402.git",
|
|
57
|
+
"directory": "sdks/typescript/packages/erc8004"
|
|
58
|
+
},
|
|
59
|
+
"bugs": {
|
|
60
|
+
"url": "https://github.com/t402-io/t402/issues"
|
|
61
|
+
},
|
|
62
|
+
"homepage": "https://t402.io",
|
|
63
|
+
"publishConfig": {
|
|
64
|
+
"access": "public"
|
|
65
|
+
},
|
|
66
|
+
"scripts": {
|
|
67
|
+
"build": "tsup",
|
|
68
|
+
"test": "vitest run",
|
|
69
|
+
"test:watch": "vitest",
|
|
70
|
+
"typecheck": "tsc --noEmit",
|
|
71
|
+
"clean": "rm -rf dist coverage"
|
|
72
|
+
}
|
|
73
|
+
}
|