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.
Files changed (120) hide show
  1. package/README.md +401 -0
  2. package/README.md.bak +401 -0
  3. package/dist/agent.d.ts +132 -0
  4. package/dist/agent.d.ts.map +1 -0
  5. package/dist/agent.js +224 -0
  6. package/dist/agent.js.map +1 -0
  7. package/dist/analytics.d.ts +120 -0
  8. package/dist/analytics.d.ts.map +1 -0
  9. package/dist/analytics.js +345 -0
  10. package/dist/analytics.js.map +1 -0
  11. package/dist/approvals.d.ts +168 -0
  12. package/dist/approvals.d.ts.map +1 -0
  13. package/dist/approvals.js +406 -0
  14. package/dist/approvals.js.map +1 -0
  15. package/dist/circle-client.d.ts +152 -0
  16. package/dist/circle-client.d.ts.map +1 -0
  17. package/dist/circle-client.js +266 -0
  18. package/dist/circle-client.js.map +1 -0
  19. package/dist/commission.d.ts +191 -0
  20. package/dist/commission.d.ts.map +1 -0
  21. package/dist/commission.js +475 -0
  22. package/dist/commission.js.map +1 -0
  23. package/dist/condition-builder.d.ts +98 -0
  24. package/dist/condition-builder.d.ts.map +1 -0
  25. package/dist/condition-builder.js +193 -0
  26. package/dist/condition-builder.js.map +1 -0
  27. package/dist/contacts.d.ts +179 -0
  28. package/dist/contacts.d.ts.map +1 -0
  29. package/dist/contacts.js +445 -0
  30. package/dist/contacts.js.map +1 -0
  31. package/dist/easy.d.ts +22 -0
  32. package/dist/easy.d.ts.map +1 -0
  33. package/dist/easy.js +40 -0
  34. package/dist/easy.js.map +1 -0
  35. package/dist/erc8004/constants.d.ts +152 -0
  36. package/dist/erc8004/constants.d.ts.map +1 -0
  37. package/dist/erc8004/constants.js +114 -0
  38. package/dist/erc8004/constants.js.map +1 -0
  39. package/dist/erc8004/discovery.d.ts +84 -0
  40. package/dist/erc8004/discovery.d.ts.map +1 -0
  41. package/dist/erc8004/discovery.js +217 -0
  42. package/dist/erc8004/discovery.js.map +1 -0
  43. package/dist/erc8004/identity.d.ts +91 -0
  44. package/dist/erc8004/identity.d.ts.map +1 -0
  45. package/dist/erc8004/identity.js +250 -0
  46. package/dist/erc8004/identity.js.map +1 -0
  47. package/dist/erc8004/index.d.ts +147 -0
  48. package/dist/erc8004/index.d.ts.map +1 -0
  49. package/dist/erc8004/index.js +225 -0
  50. package/dist/erc8004/index.js.map +1 -0
  51. package/dist/erc8004/reputation.d.ts +133 -0
  52. package/dist/erc8004/reputation.d.ts.map +1 -0
  53. package/dist/erc8004/reputation.js +277 -0
  54. package/dist/erc8004/reputation.js.map +1 -0
  55. package/dist/escrow-templates.d.ts +38 -0
  56. package/dist/escrow-templates.d.ts.map +1 -0
  57. package/dist/escrow-templates.js +419 -0
  58. package/dist/escrow-templates.js.map +1 -0
  59. package/dist/escrow.d.ts +320 -0
  60. package/dist/escrow.d.ts.map +1 -0
  61. package/dist/escrow.js +854 -0
  62. package/dist/escrow.js.map +1 -0
  63. package/dist/index.d.ts +11 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +33 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/invoices.d.ts +212 -0
  68. package/dist/invoices.d.ts.map +1 -0
  69. package/dist/invoices.js +393 -0
  70. package/dist/invoices.js.map +1 -0
  71. package/dist/notifications.d.ts +141 -0
  72. package/dist/notifications.d.ts.map +1 -0
  73. package/dist/notifications.js +350 -0
  74. package/dist/notifications.js.map +1 -0
  75. package/dist/tips.d.ts +171 -0
  76. package/dist/tips.d.ts.map +1 -0
  77. package/dist/tips.js +390 -0
  78. package/dist/tips.js.map +1 -0
  79. package/dist/types.d.ts +100 -0
  80. package/dist/types.d.ts.map +1 -0
  81. package/dist/types.js +6 -0
  82. package/dist/types.js.map +1 -0
  83. package/dist/x402-client.d.ts +127 -0
  84. package/dist/x402-client.d.ts.map +1 -0
  85. package/dist/x402-client.js +350 -0
  86. package/dist/x402-client.js.map +1 -0
  87. package/dist/x402-server.d.ts +133 -0
  88. package/dist/x402-server.d.ts.map +1 -0
  89. package/dist/x402-server.js +330 -0
  90. package/dist/x402-server.js.map +1 -0
  91. package/lib/agent.ts +273 -0
  92. package/lib/analytics.ts +474 -0
  93. package/lib/analytics.ts.bak +474 -0
  94. package/lib/approvals.ts +585 -0
  95. package/lib/approvals.ts.bak +585 -0
  96. package/lib/circle-client.ts +376 -0
  97. package/lib/circle-client.ts.bak +376 -0
  98. package/lib/commission.ts +680 -0
  99. package/lib/commission.ts.bak +680 -0
  100. package/lib/condition-builder.ts +223 -0
  101. package/lib/condition-builder.ts.bak +223 -0
  102. package/lib/contacts.ts +615 -0
  103. package/lib/contacts.ts.bak +615 -0
  104. package/lib/easy.ts +46 -0
  105. package/lib/easy.ts.bak +352 -0
  106. package/lib/erc8004/constants.ts +175 -0
  107. package/lib/erc8004/discovery.ts +299 -0
  108. package/lib/erc8004/identity.ts +327 -0
  109. package/lib/erc8004/index.ts +285 -0
  110. package/lib/erc8004/reputation.ts +368 -0
  111. package/lib/escrow-templates.ts +462 -0
  112. package/lib/escrow.ts +1216 -0
  113. package/lib/index.ts +13 -0
  114. package/lib/invoices.ts +588 -0
  115. package/lib/notifications.ts +484 -0
  116. package/lib/tips.ts +570 -0
  117. package/lib/types.ts +108 -0
  118. package/lib/x402-client.ts +471 -0
  119. package/lib/x402-server.ts +462 -0
  120. 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
+ }