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,285 @@
1
+ /**
2
+ * ERC-8004 Trustless Agents - Main Export
3
+ *
4
+ * Complete integration for agent identity, reputation, and discovery.
5
+ * Designed for seamless integration with USDC payments and x402 protocol.
6
+ */
7
+
8
+ export * from './constants';
9
+ export * from './identity';
10
+ export * from './reputation';
11
+ export * from './discovery';
12
+
13
+ import { ethers } from 'ethers';
14
+ import {
15
+ SupportedChain,
16
+ AgentRegistration,
17
+ CHAIN_CONFIG,
18
+ } from './constants';
19
+ import { IdentityClient, createLobsterAgentRegistration, RegisteredAgent } from './identity';
20
+ import { ReputationClient, FeedbackTemplates, ReputationSummary } from './reputation';
21
+ import { DiscoveryService, DiscoveredAgent } from './discovery';
22
+
23
+ export interface ERC8004ClientConfig {
24
+ chain: SupportedChain;
25
+ privateKey?: string;
26
+ paymentAddress?: string;
27
+ x402Endpoint?: string;
28
+ }
29
+
30
+ /**
31
+ * Unified ERC-8004 Client
32
+ *
33
+ * Provides high-level API for agent registration, discovery, and trust management.
34
+ */
35
+ export class ERC8004Client {
36
+ readonly chain: SupportedChain;
37
+ readonly identity: IdentityClient;
38
+ readonly reputation: ReputationClient;
39
+ readonly discovery: DiscoveryService;
40
+
41
+ private config: ERC8004ClientConfig;
42
+ private myAgentId?: number;
43
+
44
+ constructor(config: ERC8004ClientConfig) {
45
+ this.chain = config.chain;
46
+ this.config = config;
47
+
48
+ this.identity = new IdentityClient({
49
+ chain: config.chain,
50
+ privateKey: config.privateKey,
51
+ });
52
+
53
+ this.reputation = new ReputationClient({
54
+ chain: config.chain,
55
+ privateKey: config.privateKey,
56
+ });
57
+
58
+ this.discovery = new DiscoveryService({
59
+ chain: config.chain,
60
+ privateKey: config.privateKey,
61
+ });
62
+ }
63
+
64
+ /**
65
+ * Register this agent with the ERC-8004 Identity Registry
66
+ */
67
+ async registerAgent(options: {
68
+ name: string;
69
+ description: string;
70
+ image?: string;
71
+ capabilities: string[];
72
+ mcpEndpoint?: string;
73
+ a2aEndpoint?: string;
74
+ }): Promise<number> {
75
+ const registration = createLobsterAgentRegistration({
76
+ name: options.name,
77
+ description: options.description,
78
+ image: options.image,
79
+ chain: this.chain,
80
+ capabilities: options.capabilities,
81
+ paymentAddress: this.config.paymentAddress,
82
+ x402Endpoint: this.config.x402Endpoint,
83
+ escrowSupport: true,
84
+ mcpEndpoint: options.mcpEndpoint,
85
+ a2aEndpoint: options.a2aEndpoint,
86
+ });
87
+
88
+ this.myAgentId = await this.identity.register(registration);
89
+ return this.myAgentId;
90
+ }
91
+
92
+ /**
93
+ * Get my agent ID (if registered)
94
+ */
95
+ getMyAgentId(): number | undefined {
96
+ return this.myAgentId;
97
+ }
98
+
99
+ /**
100
+ * Set agent ID (if already registered)
101
+ */
102
+ setMyAgentId(agentId: number): void {
103
+ this.myAgentId = agentId;
104
+ }
105
+
106
+ /**
107
+ * Verify an agent before transacting
108
+ */
109
+ async verifyAgent(agentId: number) {
110
+ return this.discovery.verifyAgent(agentId);
111
+ }
112
+
113
+ /**
114
+ * Check if payment is safe for an agent
115
+ */
116
+ async isPaymentSafe(agentId: number, amountUsdc: number) {
117
+ return this.discovery.checkPaymentSafety(agentId, amountUsdc);
118
+ }
119
+
120
+ /**
121
+ * Post feedback after a transaction
122
+ */
123
+ async postFeedback(options: {
124
+ agentId: number;
125
+ score: number;
126
+ context: string;
127
+ txHash?: string;
128
+ }) {
129
+ return this.reputation.postFeedback({
130
+ agentId: options.agentId,
131
+ score: options.score,
132
+ context: options.context,
133
+ taskHash: options.txHash,
134
+ });
135
+ }
136
+
137
+ /**
138
+ * Post positive feedback after successful payment
139
+ */
140
+ async postPaymentSuccess(agentId: number, txHash: string, amount: string) {
141
+ const { score, context } = FeedbackTemplates.paymentSuccessful(txHash, amount);
142
+ return this.postFeedback({ agentId, score, context, txHash });
143
+ }
144
+
145
+ /**
146
+ * Post negative feedback after failed payment
147
+ */
148
+ async postPaymentFailure(agentId: number, reason: string) {
149
+ const { score, context } = FeedbackTemplates.paymentFailed(reason);
150
+ return this.postFeedback({ agentId, score, context });
151
+ }
152
+
153
+ /**
154
+ * Post feedback after escrow completion
155
+ */
156
+ async postEscrowFeedback(agentId: number, escrowId: string, outcome: 'released' | 'refunded' | 'disputed', reason?: string) {
157
+ if (outcome === 'disputed') {
158
+ const { score, context } = FeedbackTemplates.escrowDisputed(escrowId, reason || 'Unknown');
159
+ return this.postFeedback({ agentId, score, context });
160
+ }
161
+ const { score, context } = FeedbackTemplates.escrowCompleted(escrowId, outcome);
162
+ return this.postFeedback({ agentId, score, context });
163
+ }
164
+
165
+ /**
166
+ * Find agents for payment
167
+ */
168
+ async findPaymentAgents(minTrustScore?: number) {
169
+ return this.discovery.findPaymentAgents({ minTrustScore });
170
+ }
171
+
172
+ /**
173
+ * Find agents with escrow capability
174
+ */
175
+ async findEscrowAgents(minTrustLevel?: ReputationSummary['trustLevel']) {
176
+ return this.discovery.findEscrowAgents({ minTrustLevel });
177
+ }
178
+
179
+ /**
180
+ * Get my reputation summary
181
+ */
182
+ async getMyReputation(): Promise<ReputationSummary | null> {
183
+ if (!this.myAgentId) return null;
184
+ return this.reputation.getReputationSummary(this.myAgentId);
185
+ }
186
+
187
+ /**
188
+ * Get contract addresses for this chain
189
+ */
190
+ getContractAddresses() {
191
+ return CHAIN_CONFIG[this.chain].contracts;
192
+ }
193
+
194
+ /**
195
+ * Get the agent registry identifier
196
+ */
197
+ getAgentRegistry(): string {
198
+ return this.identity.getAgentRegistry();
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Integration helper: Wrap x402 payment with trust verification
204
+ */
205
+ export async function createTrustedX402Payment(options: {
206
+ erc8004: ERC8004Client;
207
+ targetAgentId: number;
208
+ amountUsdc: number;
209
+ endpoint: string;
210
+ paymentFn: () => Promise<{ success: boolean; txHash?: string; error?: string }>;
211
+ }): Promise<{
212
+ success: boolean;
213
+ txHash?: string;
214
+ trustScore: number;
215
+ feedbackPosted: boolean;
216
+ error?: string;
217
+ }> {
218
+ // Verify agent first
219
+ const verification = await options.erc8004.verifyAgent(options.targetAgentId);
220
+
221
+ if (!verification.verified) {
222
+ return {
223
+ success: false,
224
+ trustScore: verification.trustScore,
225
+ feedbackPosted: false,
226
+ error: `Agent verification failed: ${verification.reasons.join(', ')}`,
227
+ };
228
+ }
229
+
230
+ // Check payment safety
231
+ const safety = await options.erc8004.isPaymentSafe(options.targetAgentId, options.amountUsdc);
232
+
233
+ if (!safety.safe) {
234
+ console.warn(`Payment exceeds recommended limit: ${safety.reason}`);
235
+ // Continue anyway, but warn
236
+ }
237
+
238
+ // Execute payment
239
+ const result = await options.paymentFn();
240
+
241
+ // Post feedback
242
+ let feedbackPosted = false;
243
+ try {
244
+ if (result.success && result.txHash) {
245
+ await options.erc8004.postPaymentSuccess(
246
+ options.targetAgentId,
247
+ result.txHash,
248
+ options.amountUsdc.toString()
249
+ );
250
+ feedbackPosted = true;
251
+ } else if (!result.success) {
252
+ await options.erc8004.postPaymentFailure(
253
+ options.targetAgentId,
254
+ result.error || 'Unknown error'
255
+ );
256
+ feedbackPosted = true;
257
+ }
258
+ } catch (e) {
259
+ console.warn('Failed to post feedback:', e);
260
+ }
261
+
262
+ return {
263
+ success: result.success,
264
+ txHash: result.txHash,
265
+ trustScore: verification.trustScore,
266
+ feedbackPosted,
267
+ error: result.error,
268
+ };
269
+ }
270
+
271
+ /**
272
+ * Quick setup helper
273
+ */
274
+ export function createERC8004Client(
275
+ chain: SupportedChain,
276
+ privateKey?: string,
277
+ options?: { paymentAddress?: string; x402Endpoint?: string }
278
+ ): ERC8004Client {
279
+ return new ERC8004Client({
280
+ chain,
281
+ privateKey,
282
+ paymentAddress: options?.paymentAddress,
283
+ x402Endpoint: options?.x402Endpoint,
284
+ });
285
+ }
@@ -0,0 +1,368 @@
1
+ /**
2
+ * ERC-8004 Reputation Registry Client
3
+ *
4
+ * Post feedback, query reputation, and manage trust levels.
5
+ */
6
+
7
+ import { ethers } from 'ethers';
8
+ import {
9
+ CHAIN_CONFIG,
10
+ REPUTATION_REGISTRY_ABI,
11
+ SupportedChain,
12
+ Feedback,
13
+ ReputationSummary,
14
+ TRUST_LEVELS,
15
+ } from './constants';
16
+
17
+ export interface ReputationClientConfig {
18
+ chain: SupportedChain;
19
+ privateKey?: string;
20
+ provider?: ethers.Provider;
21
+ }
22
+
23
+ export interface FeedbackInput {
24
+ agentId: number;
25
+ score: number; // -100 to 100
26
+ context: string;
27
+ taskHash?: string; // Hash of the task/transaction being rated
28
+ }
29
+
30
+ export class ReputationClient {
31
+ private chain: SupportedChain;
32
+ private provider: ethers.Provider;
33
+ private signer?: ethers.Wallet;
34
+ private contract: ethers.Contract;
35
+ private readOnlyContract: ethers.Contract;
36
+
37
+ constructor(config: ReputationClientConfig) {
38
+ this.chain = config.chain;
39
+ const chainConfig = CHAIN_CONFIG[config.chain];
40
+
41
+ this.provider = config.provider || new ethers.JsonRpcProvider(chainConfig.rpcUrl);
42
+
43
+ if (config.privateKey) {
44
+ this.signer = new ethers.Wallet(config.privateKey, this.provider);
45
+ this.contract = new ethers.Contract(
46
+ chainConfig.contracts.reputationRegistry,
47
+ REPUTATION_REGISTRY_ABI,
48
+ this.signer
49
+ );
50
+ }
51
+
52
+ this.readOnlyContract = new ethers.Contract(
53
+ chainConfig.contracts.reputationRegistry,
54
+ REPUTATION_REGISTRY_ABI,
55
+ this.provider
56
+ );
57
+ }
58
+
59
+ /**
60
+ * Post feedback for an agent
61
+ *
62
+ * @param feedback Feedback to post
63
+ * @returns The feedbackId
64
+ */
65
+ async postFeedback(feedback: FeedbackInput): Promise<number> {
66
+ if (!this.contract || !this.signer) {
67
+ throw new Error('Signer required to post feedback');
68
+ }
69
+
70
+ // Validate score range
71
+ if (feedback.score < -100 || feedback.score > 100) {
72
+ throw new Error('Score must be between -100 and 100');
73
+ }
74
+
75
+ // Create task hash if not provided
76
+ const taskHash = feedback.taskHash
77
+ ? ethers.id(feedback.taskHash)
78
+ : ethers.id(`feedback-${Date.now()}-${Math.random()}`);
79
+
80
+ console.log(`Posting feedback for agent ${feedback.agentId}: score=${feedback.score}`);
81
+
82
+ const tx = await this.contract.postFeedback(
83
+ feedback.agentId,
84
+ feedback.score,
85
+ feedback.context,
86
+ taskHash
87
+ );
88
+ const receipt = await tx.wait();
89
+
90
+ // Extract feedbackId from event
91
+ const event = receipt.logs.find((log: any) => {
92
+ try {
93
+ const parsed = this.contract.interface.parseLog(log);
94
+ return parsed?.name === 'FeedbackPosted';
95
+ } catch {
96
+ return false;
97
+ }
98
+ });
99
+
100
+ if (!event) {
101
+ throw new Error('FeedbackPosted event not found');
102
+ }
103
+
104
+ const parsed = this.contract.interface.parseLog(event);
105
+ const feedbackId = Number(parsed?.args?.feedbackId);
106
+
107
+ console.log(`Feedback posted with ID: ${feedbackId}`);
108
+
109
+ return feedbackId;
110
+ }
111
+
112
+ /**
113
+ * Post feedback after a successful x402 payment
114
+ *
115
+ * Links the feedback to the payment transaction for verification
116
+ */
117
+ async postPaymentFeedback(options: {
118
+ agentId: number;
119
+ score: number;
120
+ context: string;
121
+ paymentTxHash: string;
122
+ paymentAmount: string;
123
+ serviceUsed: string;
124
+ }): Promise<number> {
125
+ const enrichedContext = JSON.stringify({
126
+ type: 'x402-payment-feedback',
127
+ context: options.context,
128
+ payment: {
129
+ txHash: options.paymentTxHash,
130
+ amount: options.paymentAmount,
131
+ service: options.serviceUsed,
132
+ },
133
+ timestamp: new Date().toISOString(),
134
+ });
135
+
136
+ return this.postFeedback({
137
+ agentId: options.agentId,
138
+ score: options.score,
139
+ context: enrichedContext,
140
+ taskHash: options.paymentTxHash,
141
+ });
142
+ }
143
+
144
+ /**
145
+ * Get a specific feedback entry
146
+ */
147
+ async getFeedback(feedbackId: number): Promise<Feedback | null> {
148
+ try {
149
+ const result = await this.readOnlyContract.getFeedback(feedbackId);
150
+
151
+ return {
152
+ feedbackId,
153
+ agentId: Number(result.agentId),
154
+ author: result.author,
155
+ score: Number(result.score),
156
+ context: result.context,
157
+ taskHash: result.taskHash,
158
+ timestamp: Number(result.timestamp),
159
+ };
160
+ } catch {
161
+ return null;
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Get feedback count for an agent
167
+ */
168
+ async getFeedbackCount(agentId: number): Promise<number> {
169
+ const count = await this.readOnlyContract.getFeedbackCount(agentId);
170
+ return Number(count);
171
+ }
172
+
173
+ /**
174
+ * Get recent feedback for an agent
175
+ */
176
+ async getRecentFeedback(agentId: number, limit: number = 10): Promise<Feedback[]> {
177
+ const feedbackIds: bigint[] = await this.readOnlyContract.getFeedbackByAgent(
178
+ agentId,
179
+ 0,
180
+ limit
181
+ );
182
+
183
+ const feedbacks = await Promise.all(
184
+ feedbackIds.map(id => this.getFeedback(Number(id)))
185
+ );
186
+
187
+ return feedbacks.filter((f): f is Feedback => f !== null);
188
+ }
189
+
190
+ /**
191
+ * Get average score for an agent
192
+ */
193
+ async getAverageScore(agentId: number): Promise<{ average: number; count: number }> {
194
+ const [average, count] = await this.readOnlyContract.getAverageScore(agentId);
195
+ return {
196
+ average: Number(average),
197
+ count: Number(count),
198
+ };
199
+ }
200
+
201
+ /**
202
+ * Get comprehensive reputation summary for an agent
203
+ */
204
+ async getReputationSummary(agentId: number): Promise<ReputationSummary> {
205
+ const [{ average, count }, recentFeedback] = await Promise.all([
206
+ this.getAverageScore(agentId),
207
+ this.getRecentFeedback(agentId, 5),
208
+ ]);
209
+
210
+ // Determine trust level based on score and feedback count
211
+ let trustLevel: ReputationSummary['trustLevel'] = 'untrusted';
212
+
213
+ if (count >= TRUST_LEVELS.verified.minFeedback && average >= TRUST_LEVELS.verified.minScore) {
214
+ trustLevel = 'verified';
215
+ } else if (count >= TRUST_LEVELS.trusted.minFeedback && average >= TRUST_LEVELS.trusted.minScore) {
216
+ trustLevel = 'trusted';
217
+ } else if (count >= TRUST_LEVELS.established.minFeedback && average >= TRUST_LEVELS.established.minScore) {
218
+ trustLevel = 'established';
219
+ } else if (count >= TRUST_LEVELS.emerging.minFeedback && average >= TRUST_LEVELS.emerging.minScore) {
220
+ trustLevel = 'emerging';
221
+ } else if (count >= TRUST_LEVELS.new.minFeedback && average >= TRUST_LEVELS.new.minScore) {
222
+ trustLevel = 'new';
223
+ }
224
+
225
+ return {
226
+ agentId,
227
+ averageScore: average,
228
+ totalFeedback: count,
229
+ recentFeedback,
230
+ trustLevel,
231
+ };
232
+ }
233
+
234
+ /**
235
+ * Authorize an address to post feedback for your agent
236
+ */
237
+ async authorizeFeedbackAuthor(agentId: number, author: string): Promise<void> {
238
+ if (!this.contract) {
239
+ throw new Error('Signer required');
240
+ }
241
+
242
+ const tx = await this.contract.authorizeFeedback(agentId, author);
243
+ await tx.wait();
244
+
245
+ console.log(`Authorized ${author} to post feedback for agent ${agentId}`);
246
+ }
247
+
248
+ /**
249
+ * Revoke feedback authorization
250
+ */
251
+ async revokeFeedbackAuthorization(agentId: number, author: string): Promise<void> {
252
+ if (!this.contract) {
253
+ throw new Error('Signer required');
254
+ }
255
+
256
+ const tx = await this.contract.revokeFeedbackAuthorization(agentId, author);
257
+ await tx.wait();
258
+
259
+ console.log(`Revoked feedback authorization for ${author} on agent ${agentId}`);
260
+ }
261
+
262
+ /**
263
+ * Check if an address is authorized to post feedback
264
+ */
265
+ async isAuthorizedFeedbackAuthor(agentId: number, author: string): Promise<boolean> {
266
+ return this.readOnlyContract.isAuthorizedFeedbackAuthor(agentId, author);
267
+ }
268
+
269
+ /**
270
+ * Check if an agent meets minimum trust requirements
271
+ */
272
+ async meetsMinimumTrust(agentId: number, minLevel: ReputationSummary['trustLevel']): Promise<boolean> {
273
+ const summary = await this.getReputationSummary(agentId);
274
+
275
+ const levelOrder: ReputationSummary['trustLevel'][] = [
276
+ 'untrusted', 'new', 'emerging', 'established', 'trusted', 'verified'
277
+ ];
278
+
279
+ return levelOrder.indexOf(summary.trustLevel) >= levelOrder.indexOf(minLevel);
280
+ }
281
+
282
+ /**
283
+ * Calculate trust score for payment decisions
284
+ *
285
+ * Returns a 0-100 score combining reputation and feedback volume
286
+ */
287
+ async calculateTrustScore(agentId: number): Promise<number> {
288
+ const summary = await this.getReputationSummary(agentId);
289
+
290
+ // Weight: 70% average score, 30% volume bonus
291
+ const scoreComponent = Math.max(0, (summary.averageScore + 100) / 2); // Normalize -100..100 to 0..100
292
+ const volumeBonus = Math.min(30, summary.totalFeedback * 0.3); // Max 30 points from volume
293
+
294
+ return Math.round(scoreComponent * 0.7 + volumeBonus);
295
+ }
296
+ }
297
+
298
+ /**
299
+ * Standard feedback templates for common scenarios
300
+ */
301
+ export const FeedbackTemplates = {
302
+ // Positive feedback
303
+ excellentService: (details?: string) => ({
304
+ score: 95,
305
+ context: `Excellent service. ${details || 'Highly recommended.'}`,
306
+ }),
307
+
308
+ goodService: (details?: string) => ({
309
+ score: 75,
310
+ context: `Good service. ${details || 'Would use again.'}`,
311
+ }),
312
+
313
+ satisfactory: (details?: string) => ({
314
+ score: 50,
315
+ context: `Satisfactory service. ${details || 'Met expectations.'}`,
316
+ }),
317
+
318
+ // Negative feedback
319
+ poorService: (details?: string) => ({
320
+ score: -50,
321
+ context: `Poor service. ${details || 'Did not meet expectations.'}`,
322
+ }),
323
+
324
+ failed: (details?: string) => ({
325
+ score: -90,
326
+ context: `Service failed. ${details || 'Would not recommend.'}`,
327
+ }),
328
+
329
+ // Payment-specific
330
+ paymentSuccessful: (txHash: string, amount: string) => ({
331
+ score: 80,
332
+ context: JSON.stringify({
333
+ type: 'payment-feedback',
334
+ result: 'success',
335
+ txHash,
336
+ amount,
337
+ }),
338
+ }),
339
+
340
+ paymentFailed: (reason: string) => ({
341
+ score: -70,
342
+ context: JSON.stringify({
343
+ type: 'payment-feedback',
344
+ result: 'failed',
345
+ reason,
346
+ }),
347
+ }),
348
+
349
+ // Escrow-specific
350
+ escrowCompleted: (escrowId: string, outcome: 'released' | 'refunded') => ({
351
+ score: outcome === 'released' ? 85 : 40,
352
+ context: JSON.stringify({
353
+ type: 'escrow-feedback',
354
+ escrowId,
355
+ outcome,
356
+ }),
357
+ }),
358
+
359
+ escrowDisputed: (escrowId: string, reason: string) => ({
360
+ score: -30,
361
+ context: JSON.stringify({
362
+ type: 'escrow-feedback',
363
+ escrowId,
364
+ outcome: 'disputed',
365
+ reason,
366
+ }),
367
+ }),
368
+ };