@antseed/node 0.1.0 → 0.1.1

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 (130) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/interfaces/seller-provider.d.ts +13 -1
  6. package/dist/interfaces/seller-provider.d.ts.map +1 -1
  7. package/dist/node.d.ts +13 -3
  8. package/dist/node.d.ts.map +1 -1
  9. package/dist/node.js +123 -15
  10. package/dist/node.js.map +1 -1
  11. package/dist/proxy/proxy-mux.d.ts +3 -1
  12. package/dist/proxy/proxy-mux.d.ts.map +1 -1
  13. package/dist/proxy/proxy-mux.js +9 -5
  14. package/dist/proxy/proxy-mux.js.map +1 -1
  15. package/dist/types/http.d.ts +1 -0
  16. package/dist/types/http.d.ts.map +1 -1
  17. package/dist/types/http.js +1 -1
  18. package/dist/types/http.js.map +1 -1
  19. package/package.json +14 -10
  20. package/contracts/AntseedEscrow.sol +0 -310
  21. package/contracts/MockUSDC.sol +0 -64
  22. package/contracts/README.md +0 -102
  23. package/src/config/encryption.test.ts +0 -49
  24. package/src/config/encryption.ts +0 -53
  25. package/src/config/plugin-config-manager.test.ts +0 -92
  26. package/src/config/plugin-config-manager.ts +0 -153
  27. package/src/config/plugin-loader.ts +0 -90
  28. package/src/discovery/announcer.ts +0 -169
  29. package/src/discovery/bootstrap.ts +0 -57
  30. package/src/discovery/default-metadata-resolver.ts +0 -18
  31. package/src/discovery/dht-health.ts +0 -136
  32. package/src/discovery/dht-node.ts +0 -191
  33. package/src/discovery/http-metadata-resolver.ts +0 -47
  34. package/src/discovery/index.ts +0 -15
  35. package/src/discovery/metadata-codec.ts +0 -453
  36. package/src/discovery/metadata-resolver.ts +0 -7
  37. package/src/discovery/metadata-server.ts +0 -73
  38. package/src/discovery/metadata-validator.ts +0 -172
  39. package/src/discovery/peer-lookup.ts +0 -122
  40. package/src/discovery/peer-metadata.ts +0 -34
  41. package/src/discovery/peer-selector.ts +0 -134
  42. package/src/discovery/profile-manager.ts +0 -131
  43. package/src/discovery/profile-search.ts +0 -100
  44. package/src/discovery/reputation-verifier.ts +0 -54
  45. package/src/index.ts +0 -61
  46. package/src/interfaces/buyer-router.ts +0 -21
  47. package/src/interfaces/plugin.ts +0 -36
  48. package/src/interfaces/seller-provider.ts +0 -81
  49. package/src/metering/index.ts +0 -6
  50. package/src/metering/receipt-generator.ts +0 -105
  51. package/src/metering/receipt-verifier.ts +0 -102
  52. package/src/metering/session-tracker.ts +0 -145
  53. package/src/metering/storage.ts +0 -600
  54. package/src/metering/token-counter.ts +0 -127
  55. package/src/metering/usage-aggregator.ts +0 -236
  56. package/src/node.ts +0 -1698
  57. package/src/p2p/connection-auth.ts +0 -152
  58. package/src/p2p/connection-manager.ts +0 -916
  59. package/src/p2p/handshake.ts +0 -162
  60. package/src/p2p/ice-config.ts +0 -59
  61. package/src/p2p/identity.ts +0 -110
  62. package/src/p2p/index.ts +0 -11
  63. package/src/p2p/keepalive.ts +0 -118
  64. package/src/p2p/message-protocol.ts +0 -171
  65. package/src/p2p/nat-traversal.ts +0 -169
  66. package/src/p2p/payment-codec.ts +0 -165
  67. package/src/p2p/payment-mux.ts +0 -153
  68. package/src/p2p/reconnect.ts +0 -117
  69. package/src/payments/balance-manager.ts +0 -77
  70. package/src/payments/buyer-payment-manager.ts +0 -414
  71. package/src/payments/disputes.ts +0 -72
  72. package/src/payments/evm/escrow-client.ts +0 -263
  73. package/src/payments/evm/keypair.ts +0 -31
  74. package/src/payments/evm/signatures.ts +0 -103
  75. package/src/payments/evm/wallet.ts +0 -42
  76. package/src/payments/index.ts +0 -50
  77. package/src/payments/settlement.ts +0 -40
  78. package/src/payments/types.ts +0 -79
  79. package/src/proxy/index.ts +0 -3
  80. package/src/proxy/provider-detection.ts +0 -78
  81. package/src/proxy/proxy-mux.ts +0 -173
  82. package/src/proxy/request-codec.ts +0 -294
  83. package/src/reputation/index.ts +0 -6
  84. package/src/reputation/rating-manager.ts +0 -118
  85. package/src/reputation/report-manager.ts +0 -91
  86. package/src/reputation/trust-engine.ts +0 -120
  87. package/src/reputation/trust-score.ts +0 -74
  88. package/src/reputation/uptime-tracker.ts +0 -155
  89. package/src/routing/default-router.ts +0 -75
  90. package/src/types/bittorrent-dht.d.ts +0 -19
  91. package/src/types/buyer.ts +0 -37
  92. package/src/types/capability.ts +0 -34
  93. package/src/types/connection.ts +0 -29
  94. package/src/types/http.ts +0 -20
  95. package/src/types/index.ts +0 -14
  96. package/src/types/metering.ts +0 -175
  97. package/src/types/nat-api.d.ts +0 -29
  98. package/src/types/peer-profile.ts +0 -25
  99. package/src/types/peer.ts +0 -62
  100. package/src/types/plugin-config.ts +0 -31
  101. package/src/types/protocol.ts +0 -162
  102. package/src/types/provider.ts +0 -40
  103. package/src/types/rating.ts +0 -23
  104. package/src/types/report.ts +0 -30
  105. package/src/types/seller.ts +0 -38
  106. package/src/types/staking.ts +0 -23
  107. package/src/utils/debug.ts +0 -30
  108. package/src/utils/hex.ts +0 -14
  109. package/tests/balance-manager.test.ts +0 -156
  110. package/tests/bootstrap.test.ts +0 -108
  111. package/tests/buyer-payment-manager.test.ts +0 -358
  112. package/tests/connection-auth.test.ts +0 -87
  113. package/tests/default-router.test.ts +0 -148
  114. package/tests/evm-keypair.test.ts +0 -173
  115. package/tests/identity.test.ts +0 -133
  116. package/tests/message-protocol.test.ts +0 -212
  117. package/tests/metadata-codec.test.ts +0 -165
  118. package/tests/metadata-validator.test.ts +0 -261
  119. package/tests/metering-storage.test.ts +0 -244
  120. package/tests/payment-codec.test.ts +0 -95
  121. package/tests/payment-mux.test.ts +0 -191
  122. package/tests/peer-selector.test.ts +0 -184
  123. package/tests/provider-detection.test.ts +0 -107
  124. package/tests/proxy-mux-security.test.ts +0 -38
  125. package/tests/receipt.test.ts +0 -215
  126. package/tests/reputation-integration.test.ts +0 -195
  127. package/tests/request-codec.test.ts +0 -144
  128. package/tests/token-counter.test.ts +0 -122
  129. package/tsconfig.json +0 -9
  130. package/vitest.config.ts +0 -7
@@ -1,414 +0,0 @@
1
- import { randomBytes } from 'node:crypto';
2
- import { type AbstractSigner, Wallet } from 'ethers';
3
- import type { Identity } from '../p2p/identity.js';
4
- import type { PaymentMux } from '../p2p/payment-mux.js';
5
- import type {
6
- SessionLockConfirmPayload,
7
- SessionLockRejectPayload,
8
- SellerReceiptPayload,
9
- TopUpRequestPayload,
10
- } from '../types/protocol.js';
11
- import { BaseEscrowClient } from './evm/escrow-client.js';
12
- import { identityToEvmWallet, identityToEvmAddress } from './evm/keypair.js';
13
- import {
14
- buildLockMessageHash,
15
- buildSettlementMessageHash,
16
- buildExtendLockMessageHash,
17
- signMessageEcdsa,
18
- buildAckMessage,
19
- signMessageEd25519,
20
- } from './evm/signatures.js';
21
- import { bytesToHex, hexToBytes } from '../utils/hex.js';
22
- import { debugLog, debugWarn } from '../utils/debug.js';
23
-
24
- export interface BuyerPaymentConfig {
25
- /** Default lock amount in USDC base units (6 decimals). e.g. "1000000" = 1 USDC */
26
- defaultLockAmountUSDC: string;
27
- /** Base JSON-RPC endpoint */
28
- rpcUrl: string;
29
- /** Deployed AntseedEscrow contract address */
30
- contractAddress: string;
31
- /** USDC token contract address */
32
- usdcAddress: string;
33
- /** Auto-acknowledge seller receipts. Default: true */
34
- autoAck?: boolean;
35
- /** Auto-approve top-up requests. Default: true */
36
- autoTopUp?: boolean;
37
- /** Maximum total amount the buyer will commit per session (USDC base units). Default: "10000000" (10 USDC) */
38
- maxSessionBudgetUSDC?: string;
39
- }
40
-
41
- export type BuyerSessionStatus = 'pending' | 'confirmed' | 'active' | 'ending' | 'ended';
42
-
43
- export interface BuyerSessionState {
44
- sessionId: string;
45
- sellerPeerId: string;
46
- sellerEvmAddress: string;
47
- lockedAmount: bigint;
48
- status: BuyerSessionStatus;
49
- txSignature: string | null;
50
- lastRunningTotal: bigint;
51
- lastRequestCount: number;
52
- createdAt: number;
53
- updatedAt: number;
54
- }
55
-
56
- /**
57
- * Manages buyer-side bilateral payment sessions across seller connections.
58
- *
59
- * Handles the full lifecycle: lock initiation, receipt acknowledgement,
60
- * top-up approval, and session settlement.
61
- */
62
- export class BuyerPaymentManager {
63
- private readonly _identity: Identity;
64
- private _signer: AbstractSigner;
65
- private readonly _escrowClient: BaseEscrowClient;
66
- private readonly _config: BuyerPaymentConfig;
67
- private readonly _sessions = new Map<string, BuyerSessionState>();
68
-
69
- constructor(identity: Identity, config: BuyerPaymentConfig) {
70
- this._identity = identity;
71
- this._config = config;
72
- this._signer = identityToEvmWallet(identity);
73
- this._escrowClient = new BaseEscrowClient({
74
- rpcUrl: config.rpcUrl,
75
- contractAddress: config.contractAddress,
76
- usdcAddress: config.usdcAddress,
77
- });
78
- }
79
-
80
- get signer(): AbstractSigner {
81
- return this._signer;
82
- }
83
-
84
- /** @deprecated Use .signer instead */
85
- get wallet(): Wallet {
86
- return this._signer as Wallet;
87
- }
88
-
89
- /** Replace the signer at runtime (e.g. with a WalletConnect signer). */
90
- setSigner(signer: AbstractSigner): void {
91
- this._signer = signer;
92
- }
93
-
94
- get escrowClient(): BaseEscrowClient {
95
- return this._escrowClient;
96
- }
97
-
98
- /** Get a snapshot of all active sessions. */
99
- getActiveSessions(): BuyerSessionState[] {
100
- return [...this._sessions.values()].filter(
101
- (s) => s.status !== 'ended',
102
- );
103
- }
104
-
105
- /** Get the session for a given seller peer, if it exists. */
106
- getSession(sellerPeerId: string): BuyerSessionState | undefined {
107
- return this._sessions.get(sellerPeerId);
108
- }
109
-
110
- // ── Lock initiation ─────────────────────────────────────────────
111
-
112
- /**
113
- * Generate a session ID, sign a lock authorization, and send it
114
- * to the seller via PaymentMux.
115
- */
116
- async initiateLock(
117
- sellerPeerId: string,
118
- sellerEvmAddress: string,
119
- paymentMux: PaymentMux,
120
- lockAmount?: string,
121
- ): Promise<string> {
122
- const amount = lockAmount ?? this._config.defaultLockAmountUSDC;
123
- const amountBigInt = BigInt(amount);
124
-
125
- // Generate a 32-byte session ID as 0x-prefixed hex (bytes32)
126
- const sessionIdBytes = randomBytes(32);
127
- const sessionId = '0x' + sessionIdBytes.toString('hex');
128
-
129
- debugLog(`[BuyerPayment] Initiating lock: session=${sessionId.slice(0, 18)}... seller=${sellerPeerId.slice(0, 12)}... amount=${amount}`);
130
-
131
- // Sign the lock message with ECDSA (for on-chain verification)
132
- const messageHash = buildLockMessageHash(sessionId, sellerEvmAddress, amountBigInt);
133
- const buyerSig = await signMessageEcdsa(this._signer, messageHash);
134
-
135
- // Store session state
136
- const now = Date.now();
137
- const session: BuyerSessionState = {
138
- sessionId,
139
- sellerPeerId,
140
- sellerEvmAddress,
141
- lockedAmount: amountBigInt,
142
- status: 'pending',
143
- txSignature: null,
144
- lastRunningTotal: 0n,
145
- lastRequestCount: 0,
146
- createdAt: now,
147
- updatedAt: now,
148
- };
149
- this._sessions.set(sellerPeerId, session);
150
-
151
- // Send the lock auth message
152
- paymentMux.sendSessionLockAuth({
153
- sessionId,
154
- lockedAmount: amount,
155
- buyerSig,
156
- });
157
-
158
- return sessionId;
159
- }
160
-
161
- // ── Lock confirmation / rejection handlers ──────────────────────
162
-
163
- /**
164
- * Called when the seller confirms the lock was committed on-chain.
165
- */
166
- handleLockConfirm(sellerPeerId: string, payload: SessionLockConfirmPayload): void {
167
- const session = this._sessions.get(sellerPeerId);
168
- if (!session) {
169
- debugWarn(`[BuyerPayment] Lock confirm for unknown seller: ${sellerPeerId.slice(0, 12)}...`);
170
- return;
171
- }
172
- if (session.sessionId !== payload.sessionId) {
173
- debugWarn(`[BuyerPayment] Lock confirm session mismatch: expected=${session.sessionId.slice(0, 18)}... got=${payload.sessionId.slice(0, 18)}...`);
174
- return;
175
- }
176
-
177
- session.status = 'confirmed';
178
- session.txSignature = payload.txSignature;
179
- session.updatedAt = Date.now();
180
- debugLog(`[BuyerPayment] Lock confirmed: session=${session.sessionId.slice(0, 18)}... tx=${payload.txSignature.slice(0, 12)}...`);
181
- }
182
-
183
- /**
184
- * Called when the seller rejects the lock.
185
- */
186
- handleLockReject(sellerPeerId: string, payload: SessionLockRejectPayload): void {
187
- const session = this._sessions.get(sellerPeerId);
188
- if (!session) {
189
- debugWarn(`[BuyerPayment] Lock reject for unknown seller: ${sellerPeerId.slice(0, 12)}...`);
190
- return;
191
- }
192
-
193
- debugWarn(`[BuyerPayment] Lock rejected: session=${session.sessionId.slice(0, 18)}... reason=${payload.reason}`);
194
- this._sessions.delete(sellerPeerId);
195
- }
196
-
197
- // ── Receipt handling ────────────────────────────────────────────
198
-
199
- /**
200
- * Handle a running-total receipt from the seller.
201
- * If autoAck is enabled, automatically counter-sign and send BuyerAck.
202
- */
203
- async handleSellerReceipt(
204
- sellerPeerId: string,
205
- receipt: SellerReceiptPayload,
206
- paymentMux: PaymentMux,
207
- ): Promise<void> {
208
- const session = this._sessions.get(sellerPeerId);
209
- if (!session) {
210
- debugWarn(`[BuyerPayment] Receipt for unknown seller: ${sellerPeerId.slice(0, 12)}...`);
211
- return;
212
- }
213
-
214
- if (session.status === 'confirmed') {
215
- session.status = 'active';
216
- }
217
-
218
- // Update running total
219
- session.lastRunningTotal = BigInt(receipt.runningTotal);
220
- session.lastRequestCount = receipt.requestCount;
221
- session.updatedAt = Date.now();
222
-
223
- debugLog(`[BuyerPayment] Receipt: session=${session.sessionId.slice(0, 18)}... total=${receipt.runningTotal} count=${receipt.requestCount}`);
224
-
225
- const autoAck = this._config.autoAck ?? true;
226
- if (autoAck) {
227
- // Build ack message and sign with Ed25519
228
- const sessionIdBytes = hexToBytes(session.sessionId.startsWith('0x') ? session.sessionId.slice(2) : session.sessionId);
229
- const ackMsg = buildAckMessage(
230
- sessionIdBytes,
231
- BigInt(receipt.runningTotal),
232
- receipt.requestCount,
233
- );
234
- const sigBytes = await signMessageEd25519(this._identity, ackMsg);
235
- const buyerSig = bytesToHex(sigBytes);
236
-
237
- paymentMux.sendBuyerAck({
238
- sessionId: session.sessionId,
239
- runningTotal: receipt.runningTotal,
240
- requestCount: receipt.requestCount,
241
- buyerSig,
242
- });
243
-
244
- debugLog(`[BuyerPayment] Auto-ack sent for session=${session.sessionId.slice(0, 18)}...`);
245
- }
246
- }
247
-
248
- // ── Top-up handling ─────────────────────────────────────────────
249
-
250
- /**
251
- * Handle a top-up request from the seller.
252
- * If autoTopUp is enabled and budget allows, sign and send TopUpAuth.
253
- * Otherwise, end the session.
254
- */
255
- async handleTopUpRequest(
256
- sellerPeerId: string,
257
- request: TopUpRequestPayload,
258
- paymentMux: PaymentMux,
259
- ): Promise<void> {
260
- const session = this._sessions.get(sellerPeerId);
261
- if (!session) {
262
- debugWarn(`[BuyerPayment] Top-up for unknown seller: ${sellerPeerId.slice(0, 12)}...`);
263
- return;
264
- }
265
-
266
- const additionalAmount = BigInt(request.additionalAmount);
267
- const maxBudget = BigInt(this._config.maxSessionBudgetUSDC ?? '10000000');
268
- const newTotal = session.lockedAmount + additionalAmount;
269
- const autoTopUp = this._config.autoTopUp ?? true;
270
-
271
- debugLog(`[BuyerPayment] Top-up request: session=${session.sessionId.slice(0, 18)}... additional=${request.additionalAmount} newTotal=${newTotal}`);
272
-
273
- if (autoTopUp && newTotal <= maxBudget) {
274
- // Check on-chain balance
275
- const buyerAddr = identityToEvmAddress(this._identity);
276
- const account = await this._escrowClient.getBuyerAccount(buyerAddr);
277
- if (account.available >= additionalAmount) {
278
- // Sign extend-lock authorization
279
- const messageHash = buildExtendLockMessageHash(
280
- session.sessionId,
281
- session.sellerEvmAddress,
282
- additionalAmount,
283
- );
284
- const buyerSig = await signMessageEcdsa(this._signer, messageHash);
285
-
286
- session.lockedAmount = newTotal;
287
- session.updatedAt = Date.now();
288
-
289
- paymentMux.sendTopUpAuth({
290
- sessionId: session.sessionId,
291
- additionalAmount: request.additionalAmount,
292
- buyerSig,
293
- });
294
-
295
- debugLog(`[BuyerPayment] Top-up authorized: session=${session.sessionId.slice(0, 18)}...`);
296
- return;
297
- }
298
-
299
- debugWarn(`[BuyerPayment] Insufficient balance for top-up. Available=${account.available}, requested=${additionalAmount}`);
300
- }
301
-
302
- // Cannot or will not top up — end the session
303
- debugLog(`[BuyerPayment] Declining top-up, ending session=${session.sessionId.slice(0, 18)}...`);
304
- await this.endSession(sellerPeerId, paymentMux, 80);
305
- }
306
-
307
- // ── Session end ─────────────────────────────────────────────────
308
-
309
- /**
310
- * End a session with the given seller. Signs a settlement message
311
- * with ECDSA and sends SessionEnd.
312
- */
313
- async endSession(
314
- sellerPeerId: string,
315
- paymentMux: PaymentMux,
316
- score: number = 80,
317
- ): Promise<void> {
318
- const session = this._sessions.get(sellerPeerId);
319
- if (!session) {
320
- debugWarn(`[BuyerPayment] Cannot end session for unknown seller: ${sellerPeerId.slice(0, 12)}...`);
321
- return;
322
- }
323
-
324
- if (session.status === 'ending' || session.status === 'ended') {
325
- return;
326
- }
327
-
328
- session.status = 'ending';
329
- session.updatedAt = Date.now();
330
-
331
- debugLog(`[BuyerPayment] Ending session=${session.sessionId.slice(0, 18)}... total=${session.lastRunningTotal} score=${score}`);
332
-
333
- // Sign settlement message with ECDSA
334
- const messageHash = buildSettlementMessageHash(
335
- session.sessionId,
336
- session.lastRunningTotal,
337
- score,
338
- );
339
- const buyerSig = await signMessageEcdsa(this._signer, messageHash);
340
-
341
- paymentMux.sendSessionEnd({
342
- sessionId: session.sessionId,
343
- runningTotal: session.lastRunningTotal.toString(),
344
- requestCount: session.lastRequestCount,
345
- score,
346
- buyerSig,
347
- });
348
-
349
- session.status = 'ended';
350
- session.updatedAt = Date.now();
351
- debugLog(`[BuyerPayment] Session ended: ${session.sessionId.slice(0, 18)}...`);
352
- }
353
-
354
- // ── Escrow operations ───────────────────────────────────────────
355
-
356
- /**
357
- * Deposit USDC into the escrow contract.
358
- * @param amount Amount in USDC base units (6 decimals).
359
- */
360
- async deposit(amount: bigint): Promise<string> {
361
- debugLog(`[BuyerPayment] Depositing ${amount} to escrow`);
362
- return this._escrowClient.deposit(this._signer, amount);
363
- }
364
-
365
- /**
366
- * Withdraw USDC from the escrow contract.
367
- * @param amount Amount in USDC base units (6 decimals).
368
- */
369
- async withdraw(amount: bigint): Promise<string> {
370
- debugLog(`[BuyerPayment] Withdrawing ${amount} from escrow`);
371
- return this._escrowClient.withdraw(this._signer, amount);
372
- }
373
-
374
- /**
375
- * Get the buyer's on-chain escrow balance.
376
- */
377
- async getBalance(): Promise<{ deposited: bigint; committed: bigint; available: bigint }> {
378
- const buyerAddr = identityToEvmAddress(this._identity);
379
- return this._escrowClient.getBuyerAccount(buyerAddr);
380
- }
381
-
382
- // ── Dispute helpers ─────────────────────────────────────────────
383
-
384
- /**
385
- * Release an expired lock (buyer reclaims funds).
386
- */
387
- async releaseExpiredLock(sessionId: string): Promise<string> {
388
- debugLog(`[BuyerPayment] Releasing expired lock: session=${sessionId.slice(0, 18)}...`);
389
- return this._escrowClient.releaseExpiredLock(this._signer, sessionId);
390
- }
391
-
392
- /**
393
- * Respond to a dispute opened by the seller.
394
- */
395
- async respondToDispute(sessionId: string): Promise<string> {
396
- debugLog(`[BuyerPayment] Responding to dispute: session=${sessionId.slice(0, 18)}...`);
397
- return this._escrowClient.respondDispute(this._signer, sessionId);
398
- }
399
-
400
- /**
401
- * Check if a session lock has been confirmed (for polling).
402
- */
403
- isLockConfirmed(sellerPeerId: string): boolean {
404
- const session = this._sessions.get(sellerPeerId);
405
- return session?.status === 'confirmed' || session?.status === 'active';
406
- }
407
-
408
- /**
409
- * Check if a session lock has been rejected (for polling).
410
- */
411
- isLockRejected(sellerPeerId: string): boolean {
412
- return !this._sessions.has(sellerPeerId);
413
- }
414
- }
@@ -1,72 +0,0 @@
1
- import { randomUUID } from 'node:crypto';
2
- import type { PeerId } from '../types/peer.js';
3
- import type { UsageReceipt } from '../types/metering.js';
4
- import type { PaymentDispute } from './types.js';
5
-
6
- export const DISPUTE_TIMEOUT_MS = 72 * 60 * 60 * 1000;
7
-
8
- export function createDispute(
9
- channel: { channelId: string; sessionId: string },
10
- initiatorPeerId: PeerId,
11
- reason: string,
12
- buyerReceipts: UsageReceipt[],
13
- sellerReceipts: UsageReceipt[],
14
- ): PaymentDispute {
15
- return {
16
- disputeId: randomUUID(),
17
- channelId: channel.channelId,
18
- sessionId: channel.sessionId,
19
- initiatorPeerId,
20
- reason,
21
- status: 'open',
22
- buyerReceipts,
23
- sellerReceipts,
24
- createdAt: Date.now(),
25
- resolvedAt: null,
26
- resolution: null,
27
- };
28
- }
29
-
30
- export function detectDiscrepancy(
31
- buyerReceipts: UsageReceipt[],
32
- sellerReceipts: UsageReceipt[],
33
- thresholdPercent: number,
34
- ): { discrepancyDetected: boolean; buyerTotal: number; sellerTotal: number; diffPercent: number } {
35
- const buyerTotal = buyerReceipts.reduce((sum, r) => sum + r.tokens.totalTokens, 0);
36
- const sellerTotal = sellerReceipts.reduce((sum, r) => sum + r.tokens.totalTokens, 0);
37
- const max = Math.max(buyerTotal, sellerTotal);
38
- const diffPercent = max === 0 ? 0 : (Math.abs(buyerTotal - sellerTotal) / max) * 100;
39
- const discrepancyDetected = diffPercent > thresholdPercent;
40
-
41
- return {
42
- discrepancyDetected,
43
- buyerTotal,
44
- sellerTotal,
45
- diffPercent,
46
- };
47
- }
48
-
49
- export function resolveDispute(
50
- dispute: PaymentDispute,
51
- resolution: string,
52
- ): PaymentDispute {
53
- return {
54
- ...dispute,
55
- status: 'resolved',
56
- resolvedAt: Date.now(),
57
- resolution,
58
- };
59
- }
60
-
61
- export function isDisputeExpired(dispute: PaymentDispute): boolean {
62
- return Date.now() - dispute.createdAt > DISPUTE_TIMEOUT_MS;
63
- }
64
-
65
- export function calculateDisputedAmount(
66
- buyerReceipts: UsageReceipt[],
67
- sellerReceipts: UsageReceipt[],
68
- ): number {
69
- const buyerTotalUsd = buyerReceipts.reduce((sum, r) => sum + r.costCents, 0) / 100;
70
- const sellerTotalUsd = sellerReceipts.reduce((sum, r) => sum + r.costCents, 0) / 100;
71
- return Math.abs(buyerTotalUsd - sellerTotalUsd);
72
- }