pay-lobster 1.0.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/README.md +401 -0
- package/README.md.bak +401 -0
- package/dist/agent.d.ts +132 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +224 -0
- package/dist/agent.js.map +1 -0
- package/dist/analytics.d.ts +120 -0
- package/dist/analytics.d.ts.map +1 -0
- package/dist/analytics.js +345 -0
- package/dist/analytics.js.map +1 -0
- package/dist/approvals.d.ts +168 -0
- package/dist/approvals.d.ts.map +1 -0
- package/dist/approvals.js +406 -0
- package/dist/approvals.js.map +1 -0
- package/dist/circle-client.d.ts +152 -0
- package/dist/circle-client.d.ts.map +1 -0
- package/dist/circle-client.js +266 -0
- package/dist/circle-client.js.map +1 -0
- package/dist/commission.d.ts +191 -0
- package/dist/commission.d.ts.map +1 -0
- package/dist/commission.js +475 -0
- package/dist/commission.js.map +1 -0
- package/dist/condition-builder.d.ts +98 -0
- package/dist/condition-builder.d.ts.map +1 -0
- package/dist/condition-builder.js +193 -0
- package/dist/condition-builder.js.map +1 -0
- package/dist/contacts.d.ts +179 -0
- package/dist/contacts.d.ts.map +1 -0
- package/dist/contacts.js +445 -0
- package/dist/contacts.js.map +1 -0
- package/dist/easy.d.ts +22 -0
- package/dist/easy.d.ts.map +1 -0
- package/dist/easy.js +40 -0
- package/dist/easy.js.map +1 -0
- package/dist/erc8004/constants.d.ts +152 -0
- package/dist/erc8004/constants.d.ts.map +1 -0
- package/dist/erc8004/constants.js +114 -0
- package/dist/erc8004/constants.js.map +1 -0
- package/dist/erc8004/discovery.d.ts +84 -0
- package/dist/erc8004/discovery.d.ts.map +1 -0
- package/dist/erc8004/discovery.js +217 -0
- package/dist/erc8004/discovery.js.map +1 -0
- package/dist/erc8004/identity.d.ts +91 -0
- package/dist/erc8004/identity.d.ts.map +1 -0
- package/dist/erc8004/identity.js +250 -0
- package/dist/erc8004/identity.js.map +1 -0
- package/dist/erc8004/index.d.ts +147 -0
- package/dist/erc8004/index.d.ts.map +1 -0
- package/dist/erc8004/index.js +225 -0
- package/dist/erc8004/index.js.map +1 -0
- package/dist/erc8004/reputation.d.ts +133 -0
- package/dist/erc8004/reputation.d.ts.map +1 -0
- package/dist/erc8004/reputation.js +277 -0
- package/dist/erc8004/reputation.js.map +1 -0
- package/dist/escrow-templates.d.ts +38 -0
- package/dist/escrow-templates.d.ts.map +1 -0
- package/dist/escrow-templates.js +419 -0
- package/dist/escrow-templates.js.map +1 -0
- package/dist/escrow.d.ts +320 -0
- package/dist/escrow.d.ts.map +1 -0
- package/dist/escrow.js +854 -0
- package/dist/escrow.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/invoices.d.ts +212 -0
- package/dist/invoices.d.ts.map +1 -0
- package/dist/invoices.js +393 -0
- package/dist/invoices.js.map +1 -0
- package/dist/notifications.d.ts +141 -0
- package/dist/notifications.d.ts.map +1 -0
- package/dist/notifications.js +350 -0
- package/dist/notifications.js.map +1 -0
- package/dist/tips.d.ts +171 -0
- package/dist/tips.d.ts.map +1 -0
- package/dist/tips.js +390 -0
- package/dist/tips.js.map +1 -0
- package/dist/types.d.ts +100 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/x402-client.d.ts +127 -0
- package/dist/x402-client.d.ts.map +1 -0
- package/dist/x402-client.js +350 -0
- package/dist/x402-client.js.map +1 -0
- package/dist/x402-server.d.ts +133 -0
- package/dist/x402-server.d.ts.map +1 -0
- package/dist/x402-server.js +330 -0
- package/dist/x402-server.js.map +1 -0
- package/lib/agent.ts +273 -0
- package/lib/analytics.ts +474 -0
- package/lib/analytics.ts.bak +474 -0
- package/lib/approvals.ts +585 -0
- package/lib/approvals.ts.bak +585 -0
- package/lib/circle-client.ts +376 -0
- package/lib/circle-client.ts.bak +376 -0
- package/lib/commission.ts +680 -0
- package/lib/commission.ts.bak +680 -0
- package/lib/condition-builder.ts +223 -0
- package/lib/condition-builder.ts.bak +223 -0
- package/lib/contacts.ts +615 -0
- package/lib/contacts.ts.bak +615 -0
- package/lib/easy.ts +46 -0
- package/lib/easy.ts.bak +352 -0
- package/lib/erc8004/constants.ts +175 -0
- package/lib/erc8004/discovery.ts +299 -0
- package/lib/erc8004/identity.ts +327 -0
- package/lib/erc8004/index.ts +285 -0
- package/lib/erc8004/reputation.ts +368 -0
- package/lib/escrow-templates.ts +462 -0
- package/lib/escrow.ts +1216 -0
- package/lib/index.ts +13 -0
- package/lib/invoices.ts +588 -0
- package/lib/notifications.ts +484 -0
- package/lib/tips.ts +570 -0
- package/lib/types.ts +108 -0
- package/lib/x402-client.ts +471 -0
- package/lib/x402-server.ts +462 -0
- package/package.json +58 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ERC-8004 Agent Discovery Service
|
|
3
|
+
*
|
|
4
|
+
* Find and verify agents for trustless interactions.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { IdentityClient, RegisteredAgent } from './identity';
|
|
8
|
+
import { ReputationClient, ReputationSummary } from './reputation';
|
|
9
|
+
import { SupportedChain, AgentRegistration } from './constants';
|
|
10
|
+
|
|
11
|
+
export interface DiscoveryConfig {
|
|
12
|
+
chain: SupportedChain;
|
|
13
|
+
privateKey?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface AgentSearchOptions {
|
|
17
|
+
// Filter by capabilities
|
|
18
|
+
capabilities?: string[];
|
|
19
|
+
|
|
20
|
+
// Filter by service type (MCP, A2A, x402)
|
|
21
|
+
serviceType?: string;
|
|
22
|
+
|
|
23
|
+
// Filter by x402 support
|
|
24
|
+
x402Support?: boolean;
|
|
25
|
+
|
|
26
|
+
// Filter by escrow support
|
|
27
|
+
escrowSupport?: boolean;
|
|
28
|
+
|
|
29
|
+
// Minimum trust level required
|
|
30
|
+
minTrustLevel?: ReputationSummary['trustLevel'];
|
|
31
|
+
|
|
32
|
+
// Minimum trust score (0-100)
|
|
33
|
+
minTrustScore?: number;
|
|
34
|
+
|
|
35
|
+
// Maximum results
|
|
36
|
+
limit?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface DiscoveredAgent {
|
|
40
|
+
agent: RegisteredAgent;
|
|
41
|
+
reputation: ReputationSummary;
|
|
42
|
+
trustScore: number;
|
|
43
|
+
paymentAddress?: string;
|
|
44
|
+
x402Endpoint?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export class DiscoveryService {
|
|
48
|
+
private identityClient: IdentityClient;
|
|
49
|
+
private reputationClient: ReputationClient;
|
|
50
|
+
private chain: SupportedChain;
|
|
51
|
+
|
|
52
|
+
constructor(config: DiscoveryConfig) {
|
|
53
|
+
this.chain = config.chain;
|
|
54
|
+
|
|
55
|
+
this.identityClient = new IdentityClient({
|
|
56
|
+
chain: config.chain,
|
|
57
|
+
privateKey: config.privateKey,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
this.reputationClient = new ReputationClient({
|
|
61
|
+
chain: config.chain,
|
|
62
|
+
privateKey: config.privateKey,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Search for agents matching criteria with reputation data
|
|
68
|
+
*/
|
|
69
|
+
async searchAgents(options: AgentSearchOptions = {}): Promise<DiscoveredAgent[]> {
|
|
70
|
+
// First, get agents from identity registry
|
|
71
|
+
const agents = await this.identityClient.searchAgents({
|
|
72
|
+
capability: options.capabilities?.[0], // Simple single capability for now
|
|
73
|
+
service: options.serviceType,
|
|
74
|
+
x402Support: options.x402Support,
|
|
75
|
+
limit: (options.limit || 20) * 2, // Fetch extra to account for filtering
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Enrich with reputation data and filter
|
|
79
|
+
const enrichedAgents: DiscoveredAgent[] = [];
|
|
80
|
+
|
|
81
|
+
for (const agent of agents) {
|
|
82
|
+
if (enrichedAgents.length >= (options.limit || 20)) break;
|
|
83
|
+
|
|
84
|
+
const [reputation, trustScore] = await Promise.all([
|
|
85
|
+
this.reputationClient.getReputationSummary(agent.agentId),
|
|
86
|
+
this.reputationClient.calculateTrustScore(agent.agentId),
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
// Filter by trust level
|
|
90
|
+
if (options.minTrustLevel) {
|
|
91
|
+
const meetsMinTrust = await this.reputationClient.meetsMinimumTrust(
|
|
92
|
+
agent.agentId,
|
|
93
|
+
options.minTrustLevel
|
|
94
|
+
);
|
|
95
|
+
if (!meetsMinTrust) continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Filter by trust score
|
|
99
|
+
if (options.minTrustScore && trustScore < options.minTrustScore) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Filter by escrow support
|
|
104
|
+
if (options.escrowSupport !== undefined) {
|
|
105
|
+
const hasEscrow = agent.registration?.usdcAgent?.escrowSupport;
|
|
106
|
+
if (hasEscrow !== options.escrowSupport) continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Filter by all capabilities (not just first)
|
|
110
|
+
if (options.capabilities && options.capabilities.length > 1) {
|
|
111
|
+
const agentCaps = agent.registration?.usdcAgent?.capabilities || [];
|
|
112
|
+
const hasAllCaps = options.capabilities.every(cap => agentCaps.includes(cap));
|
|
113
|
+
if (!hasAllCaps) continue;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
enrichedAgents.push({
|
|
117
|
+
agent,
|
|
118
|
+
reputation,
|
|
119
|
+
trustScore,
|
|
120
|
+
paymentAddress: agent.registration?.usdcAgent?.paymentAddress,
|
|
121
|
+
x402Endpoint: agent.registration?.usdcAgent?.x402Endpoint,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Sort by trust score (highest first)
|
|
126
|
+
return enrichedAgents.sort((a, b) => b.trustScore - a.trustScore);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Find agents that can receive USDC payments
|
|
131
|
+
*/
|
|
132
|
+
async findPaymentAgents(options: {
|
|
133
|
+
minTrustScore?: number;
|
|
134
|
+
limit?: number;
|
|
135
|
+
} = {}): Promise<DiscoveredAgent[]> {
|
|
136
|
+
return this.searchAgents({
|
|
137
|
+
x402Support: true,
|
|
138
|
+
minTrustScore: options.minTrustScore || 50, // Default to requiring some trust
|
|
139
|
+
limit: options.limit,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Find agents with escrow capability
|
|
145
|
+
*/
|
|
146
|
+
async findEscrowAgents(options: {
|
|
147
|
+
minTrustLevel?: ReputationSummary['trustLevel'];
|
|
148
|
+
limit?: number;
|
|
149
|
+
} = {}): Promise<DiscoveredAgent[]> {
|
|
150
|
+
return this.searchAgents({
|
|
151
|
+
escrowSupport: true,
|
|
152
|
+
minTrustLevel: options.minTrustLevel || 'emerging',
|
|
153
|
+
limit: options.limit,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Verify an agent before transacting
|
|
159
|
+
*
|
|
160
|
+
* Returns verification result with recommendation
|
|
161
|
+
*/
|
|
162
|
+
async verifyAgent(agentId: number): Promise<{
|
|
163
|
+
verified: boolean;
|
|
164
|
+
trustScore: number;
|
|
165
|
+
reputation: ReputationSummary;
|
|
166
|
+
recommendation: 'safe' | 'caution' | 'risky' | 'avoid';
|
|
167
|
+
reasons: string[];
|
|
168
|
+
}> {
|
|
169
|
+
const [agent, reputation, trustScore] = await Promise.all([
|
|
170
|
+
this.identityClient.getAgent(agentId),
|
|
171
|
+
this.reputationClient.getReputationSummary(agentId),
|
|
172
|
+
this.reputationClient.calculateTrustScore(agentId),
|
|
173
|
+
]);
|
|
174
|
+
|
|
175
|
+
const reasons: string[] = [];
|
|
176
|
+
let recommendation: 'safe' | 'caution' | 'risky' | 'avoid' = 'safe';
|
|
177
|
+
|
|
178
|
+
// Check if agent exists
|
|
179
|
+
if (!agent) {
|
|
180
|
+
return {
|
|
181
|
+
verified: false,
|
|
182
|
+
trustScore: 0,
|
|
183
|
+
reputation: {
|
|
184
|
+
agentId,
|
|
185
|
+
averageScore: 0,
|
|
186
|
+
totalFeedback: 0,
|
|
187
|
+
recentFeedback: [],
|
|
188
|
+
trustLevel: 'untrusted',
|
|
189
|
+
},
|
|
190
|
+
recommendation: 'avoid',
|
|
191
|
+
reasons: ['Agent not found in registry'],
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Check registration status
|
|
196
|
+
if (!agent.registration?.active) {
|
|
197
|
+
reasons.push('Agent registration is not active');
|
|
198
|
+
recommendation = 'avoid';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Check reputation
|
|
202
|
+
if (reputation.totalFeedback === 0) {
|
|
203
|
+
reasons.push('No feedback history - new agent');
|
|
204
|
+
recommendation = recommendation === 'safe' ? 'caution' : recommendation;
|
|
205
|
+
} else if (reputation.averageScore < 0) {
|
|
206
|
+
reasons.push(`Negative average score: ${reputation.averageScore}`);
|
|
207
|
+
recommendation = 'risky';
|
|
208
|
+
} else if (reputation.averageScore < 50) {
|
|
209
|
+
reasons.push(`Low average score: ${reputation.averageScore}`);
|
|
210
|
+
recommendation = recommendation === 'safe' ? 'caution' : recommendation;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Check trust level
|
|
214
|
+
if (reputation.trustLevel === 'untrusted') {
|
|
215
|
+
reasons.push('Trust level: untrusted');
|
|
216
|
+
recommendation = 'avoid';
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Check recent feedback
|
|
220
|
+
const recentNegative = reputation.recentFeedback.filter(f => f.score < 0);
|
|
221
|
+
if (recentNegative.length >= 3) {
|
|
222
|
+
reasons.push(`${recentNegative.length} negative reviews in recent feedback`);
|
|
223
|
+
recommendation = 'risky';
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Positive signals
|
|
227
|
+
if (trustScore >= 80) {
|
|
228
|
+
reasons.push(`High trust score: ${trustScore}`);
|
|
229
|
+
}
|
|
230
|
+
if (reputation.trustLevel === 'verified' || reputation.trustLevel === 'trusted') {
|
|
231
|
+
reasons.push(`Strong trust level: ${reputation.trustLevel}`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
verified: recommendation !== 'avoid',
|
|
236
|
+
trustScore,
|
|
237
|
+
reputation,
|
|
238
|
+
recommendation,
|
|
239
|
+
reasons,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Check if it's safe to pay an agent a certain amount
|
|
245
|
+
*
|
|
246
|
+
* Higher amounts require higher trust
|
|
247
|
+
*/
|
|
248
|
+
async checkPaymentSafety(agentId: number, amountUsdc: number): Promise<{
|
|
249
|
+
safe: boolean;
|
|
250
|
+
trustScore: number;
|
|
251
|
+
maxRecommendedAmount: number;
|
|
252
|
+
reason: string;
|
|
253
|
+
}> {
|
|
254
|
+
const trustScore = await this.reputationClient.calculateTrustScore(agentId);
|
|
255
|
+
|
|
256
|
+
// Payment tiers based on trust score
|
|
257
|
+
const maxAmounts: [number, number][] = [
|
|
258
|
+
[90, 10000], // Trust 90+ = up to $10,000
|
|
259
|
+
[80, 1000], // Trust 80+ = up to $1,000
|
|
260
|
+
[70, 500], // Trust 70+ = up to $500
|
|
261
|
+
[50, 100], // Trust 50+ = up to $100
|
|
262
|
+
[30, 25], // Trust 30+ = up to $25
|
|
263
|
+
[0, 5], // Any trust = up to $5
|
|
264
|
+
];
|
|
265
|
+
|
|
266
|
+
let maxRecommendedAmount = 5;
|
|
267
|
+
for (const [minTrust, amount] of maxAmounts) {
|
|
268
|
+
if (trustScore >= minTrust) {
|
|
269
|
+
maxRecommendedAmount = amount;
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const safe = amountUsdc <= maxRecommendedAmount;
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
safe,
|
|
278
|
+
trustScore,
|
|
279
|
+
maxRecommendedAmount,
|
|
280
|
+
reason: safe
|
|
281
|
+
? `Payment amount within safe limit for trust score ${trustScore}`
|
|
282
|
+
: `Payment amount $${amountUsdc} exceeds recommended limit of $${maxRecommendedAmount} for trust score ${trustScore}`,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Get the identity client for direct access
|
|
288
|
+
*/
|
|
289
|
+
getIdentityClient(): IdentityClient {
|
|
290
|
+
return this.identityClient;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Get the reputation client for direct access
|
|
295
|
+
*/
|
|
296
|
+
getReputationClient(): ReputationClient {
|
|
297
|
+
return this.reputationClient;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ERC-8004 Identity Registry Client
|
|
3
|
+
*
|
|
4
|
+
* Register agents, manage identities, and resolve agent information.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { ethers } from 'ethers';
|
|
8
|
+
import {
|
|
9
|
+
CHAIN_CONFIG,
|
|
10
|
+
IDENTITY_REGISTRY_ABI,
|
|
11
|
+
SupportedChain,
|
|
12
|
+
AgentRegistration,
|
|
13
|
+
} from './constants';
|
|
14
|
+
|
|
15
|
+
export interface IdentityClientConfig {
|
|
16
|
+
chain: SupportedChain;
|
|
17
|
+
privateKey?: string;
|
|
18
|
+
provider?: ethers.Provider;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface RegisteredAgent {
|
|
22
|
+
agentId: number;
|
|
23
|
+
owner: string;
|
|
24
|
+
uri: string;
|
|
25
|
+
registration?: AgentRegistration;
|
|
26
|
+
agentRegistry: string; // Full registry identifier
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class IdentityClient {
|
|
30
|
+
private chain: SupportedChain;
|
|
31
|
+
private provider: ethers.Provider;
|
|
32
|
+
private signer?: ethers.Wallet;
|
|
33
|
+
private contract: ethers.Contract;
|
|
34
|
+
private readOnlyContract: ethers.Contract;
|
|
35
|
+
|
|
36
|
+
constructor(config: IdentityClientConfig) {
|
|
37
|
+
this.chain = config.chain;
|
|
38
|
+
const chainConfig = CHAIN_CONFIG[config.chain];
|
|
39
|
+
|
|
40
|
+
this.provider = config.provider || new ethers.JsonRpcProvider(chainConfig.rpcUrl);
|
|
41
|
+
|
|
42
|
+
if (config.privateKey) {
|
|
43
|
+
this.signer = new ethers.Wallet(config.privateKey, this.provider);
|
|
44
|
+
this.contract = new ethers.Contract(
|
|
45
|
+
chainConfig.contracts.identityRegistry,
|
|
46
|
+
IDENTITY_REGISTRY_ABI,
|
|
47
|
+
this.signer
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
this.readOnlyContract = new ethers.Contract(
|
|
52
|
+
chainConfig.contracts.identityRegistry,
|
|
53
|
+
IDENTITY_REGISTRY_ABI,
|
|
54
|
+
this.provider
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get the full agent registry identifier (namespace:chainId:address)
|
|
60
|
+
*/
|
|
61
|
+
getAgentRegistry(): string {
|
|
62
|
+
const chainConfig = CHAIN_CONFIG[this.chain];
|
|
63
|
+
return `${chainConfig.namespace}:${chainConfig.chainId}:${chainConfig.contracts.identityRegistry}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Register a new agent with the Identity Registry
|
|
68
|
+
*
|
|
69
|
+
* @param registration Agent registration data
|
|
70
|
+
* @returns The newly assigned agentId
|
|
71
|
+
*/
|
|
72
|
+
async register(registration: AgentRegistration): Promise<number> {
|
|
73
|
+
if (!this.contract || !this.signer) {
|
|
74
|
+
throw new Error('Signer required for registration');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Ensure registration includes this chain's registry
|
|
78
|
+
const agentRegistry = this.getAgentRegistry();
|
|
79
|
+
|
|
80
|
+
// Host registration file (could be IPFS, HTTPS, or data: URI)
|
|
81
|
+
const uri = await this.hostRegistrationFile(registration);
|
|
82
|
+
|
|
83
|
+
console.log(`Registering agent with URI: ${uri}`);
|
|
84
|
+
|
|
85
|
+
const tx = await this.contract.register(uri);
|
|
86
|
+
const receipt = await tx.wait();
|
|
87
|
+
|
|
88
|
+
// Extract agentId from AgentRegistered event
|
|
89
|
+
const event = receipt.logs.find((log: any) => {
|
|
90
|
+
try {
|
|
91
|
+
const parsed = this.contract.interface.parseLog(log);
|
|
92
|
+
return parsed?.name === 'AgentRegistered';
|
|
93
|
+
} catch {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
if (!event) {
|
|
99
|
+
throw new Error('AgentRegistered event not found in transaction');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const parsed = this.contract.interface.parseLog(event);
|
|
103
|
+
const agentId = Number(parsed?.args?.agentId);
|
|
104
|
+
|
|
105
|
+
console.log(`Agent registered with ID: ${agentId}`);
|
|
106
|
+
|
|
107
|
+
return agentId;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Update an agent's registration URI
|
|
112
|
+
*/
|
|
113
|
+
async updateRegistration(agentId: number, registration: AgentRegistration): Promise<void> {
|
|
114
|
+
if (!this.contract) {
|
|
115
|
+
throw new Error('Signer required for update');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const uri = await this.hostRegistrationFile(registration);
|
|
119
|
+
const tx = await this.contract.setAgentURI(agentId, uri);
|
|
120
|
+
await tx.wait();
|
|
121
|
+
|
|
122
|
+
console.log(`Agent ${agentId} registration updated`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get agent information by ID
|
|
127
|
+
*/
|
|
128
|
+
async getAgent(agentId: number): Promise<RegisteredAgent | null> {
|
|
129
|
+
try {
|
|
130
|
+
const [owner, uri] = await Promise.all([
|
|
131
|
+
this.readOnlyContract.getAgentOwner(agentId),
|
|
132
|
+
this.readOnlyContract.getAgentURI(agentId),
|
|
133
|
+
]);
|
|
134
|
+
|
|
135
|
+
let registration: AgentRegistration | undefined;
|
|
136
|
+
try {
|
|
137
|
+
registration = await this.fetchRegistrationFile(uri);
|
|
138
|
+
} catch (e) {
|
|
139
|
+
console.warn(`Failed to fetch registration for agent ${agentId}:`, e);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
agentId,
|
|
144
|
+
owner,
|
|
145
|
+
uri,
|
|
146
|
+
registration,
|
|
147
|
+
agentRegistry: this.getAgentRegistry(),
|
|
148
|
+
};
|
|
149
|
+
} catch (e) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get all agents owned by an address
|
|
156
|
+
*/
|
|
157
|
+
async getAgentsByOwner(owner: string): Promise<RegisteredAgent[]> {
|
|
158
|
+
const agentIds: bigint[] = await this.readOnlyContract.getAgentsByOwner(owner);
|
|
159
|
+
|
|
160
|
+
const agents = await Promise.all(
|
|
161
|
+
agentIds.map(id => this.getAgent(Number(id)))
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
return agents.filter((a): a is RegisteredAgent => a !== null);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get total number of registered agents
|
|
169
|
+
*/
|
|
170
|
+
async getTotalAgents(): Promise<number> {
|
|
171
|
+
const total = await this.readOnlyContract.totalSupply();
|
|
172
|
+
return Number(total);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Search for agents by capability (searches registration files)
|
|
177
|
+
*/
|
|
178
|
+
async searchAgents(options: {
|
|
179
|
+
capability?: string;
|
|
180
|
+
service?: string;
|
|
181
|
+
x402Support?: boolean;
|
|
182
|
+
limit?: number;
|
|
183
|
+
}): Promise<RegisteredAgent[]> {
|
|
184
|
+
const total = await this.getTotalAgents();
|
|
185
|
+
const limit = options.limit || 50;
|
|
186
|
+
const results: RegisteredAgent[] = [];
|
|
187
|
+
|
|
188
|
+
// Note: In production, you'd want an indexer for this
|
|
189
|
+
// For demo purposes, we iterate through recent registrations
|
|
190
|
+
for (let i = total; i > 0 && results.length < limit; i--) {
|
|
191
|
+
try {
|
|
192
|
+
const agent = await this.getAgent(i);
|
|
193
|
+
if (!agent || !agent.registration) continue;
|
|
194
|
+
|
|
195
|
+
const reg = agent.registration;
|
|
196
|
+
|
|
197
|
+
// Filter by x402 support
|
|
198
|
+
if (options.x402Support !== undefined && reg.x402Support !== options.x402Support) {
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Filter by capability
|
|
203
|
+
if (options.capability) {
|
|
204
|
+
const hasCapability = reg.usdcAgent?.capabilities?.includes(options.capability);
|
|
205
|
+
if (!hasCapability) continue;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Filter by service type
|
|
209
|
+
if (options.service) {
|
|
210
|
+
const hasService = reg.services.some(s => s.name === options.service);
|
|
211
|
+
if (!hasService) continue;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
results.push(agent);
|
|
215
|
+
} catch {
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return results;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Host a registration file and return its URI
|
|
225
|
+
*
|
|
226
|
+
* For simplicity, we use data: URIs (fully on-chain)
|
|
227
|
+
* In production, you might use IPFS or HTTPS
|
|
228
|
+
*/
|
|
229
|
+
private async hostRegistrationFile(registration: AgentRegistration): Promise<string> {
|
|
230
|
+
const json = JSON.stringify(registration);
|
|
231
|
+
const base64 = Buffer.from(json).toString('base64');
|
|
232
|
+
return `data:application/json;base64,${base64}`;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Fetch and parse a registration file from its URI
|
|
237
|
+
*/
|
|
238
|
+
private async fetchRegistrationFile(uri: string): Promise<AgentRegistration> {
|
|
239
|
+
if (uri.startsWith('data:')) {
|
|
240
|
+
// Parse data: URI
|
|
241
|
+
const match = uri.match(/^data:application\/json;base64,(.+)$/);
|
|
242
|
+
if (!match) throw new Error('Invalid data URI format');
|
|
243
|
+
const json = Buffer.from(match[1], 'base64').toString('utf-8');
|
|
244
|
+
return JSON.parse(json);
|
|
245
|
+
} else if (uri.startsWith('ipfs://')) {
|
|
246
|
+
// Fetch from IPFS gateway
|
|
247
|
+
const cid = uri.replace('ipfs://', '');
|
|
248
|
+
const response = await fetch(`https://ipfs.io/ipfs/${cid}`);
|
|
249
|
+
return response.json();
|
|
250
|
+
} else if (uri.startsWith('https://')) {
|
|
251
|
+
// Fetch from HTTPS
|
|
252
|
+
const response = await fetch(uri);
|
|
253
|
+
return response.json();
|
|
254
|
+
} else {
|
|
255
|
+
throw new Error(`Unsupported URI scheme: ${uri}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Create a standard Pay Lobster registration file
|
|
262
|
+
*/
|
|
263
|
+
export function createLobsterAgentRegistration(options: {
|
|
264
|
+
name: string;
|
|
265
|
+
description: string;
|
|
266
|
+
image?: string;
|
|
267
|
+
agentId?: number;
|
|
268
|
+
chain: SupportedChain;
|
|
269
|
+
capabilities: string[];
|
|
270
|
+
paymentAddress?: string;
|
|
271
|
+
x402Endpoint?: string;
|
|
272
|
+
escrowSupport?: boolean;
|
|
273
|
+
mcpEndpoint?: string;
|
|
274
|
+
a2aEndpoint?: string;
|
|
275
|
+
}): AgentRegistration {
|
|
276
|
+
const chainConfig = CHAIN_CONFIG[options.chain];
|
|
277
|
+
const agentRegistry = `${chainConfig.namespace}:${chainConfig.chainId}:${chainConfig.contracts.identityRegistry}`;
|
|
278
|
+
|
|
279
|
+
const services: AgentRegistration['services'] = [];
|
|
280
|
+
|
|
281
|
+
if (options.mcpEndpoint) {
|
|
282
|
+
services.push({
|
|
283
|
+
name: 'MCP',
|
|
284
|
+
endpoint: options.mcpEndpoint,
|
|
285
|
+
version: '2025-06-18',
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (options.a2aEndpoint) {
|
|
290
|
+
services.push({
|
|
291
|
+
name: 'A2A',
|
|
292
|
+
endpoint: options.a2aEndpoint,
|
|
293
|
+
version: '0.3.0',
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (options.x402Endpoint) {
|
|
298
|
+
services.push({
|
|
299
|
+
name: 'x402',
|
|
300
|
+
endpoint: options.x402Endpoint,
|
|
301
|
+
version: '1.0',
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
type: 'https://eips.ethereum.org/EIPS/eip-8004#registration-v1',
|
|
307
|
+
name: options.name,
|
|
308
|
+
description: options.description,
|
|
309
|
+
image: options.image,
|
|
310
|
+
services,
|
|
311
|
+
x402Support: !!options.x402Endpoint || !!options.paymentAddress,
|
|
312
|
+
active: true,
|
|
313
|
+
registrations: options.agentId !== undefined ? [{
|
|
314
|
+
agentId: options.agentId,
|
|
315
|
+
agentRegistry,
|
|
316
|
+
}] : [],
|
|
317
|
+
supportedTrust: ['reputation'],
|
|
318
|
+
usdcAgent: {
|
|
319
|
+
version: '1.0.0',
|
|
320
|
+
capabilities: options.capabilities,
|
|
321
|
+
supportedChains: [options.chain],
|
|
322
|
+
paymentAddress: options.paymentAddress,
|
|
323
|
+
escrowSupport: options.escrowSupport ?? true,
|
|
324
|
+
x402Endpoint: options.x402Endpoint,
|
|
325
|
+
},
|
|
326
|
+
};
|
|
327
|
+
}
|