@antseed/node 0.2.25 → 0.2.27

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 (111) hide show
  1. package/README.md +74 -1
  2. package/dist/discovery/announcer.d.ts +2 -0
  3. package/dist/discovery/announcer.d.ts.map +1 -1
  4. package/dist/discovery/announcer.js +10 -6
  5. package/dist/discovery/announcer.js.map +1 -1
  6. package/dist/discovery/index.d.ts +0 -1
  7. package/dist/discovery/index.d.ts.map +1 -1
  8. package/dist/discovery/index.js +0 -1
  9. package/dist/discovery/index.js.map +1 -1
  10. package/dist/discovery/reputation-verifier.d.ts +7 -4
  11. package/dist/discovery/reputation-verifier.d.ts.map +1 -1
  12. package/dist/discovery/reputation-verifier.js +33 -9
  13. package/dist/discovery/reputation-verifier.js.map +1 -1
  14. package/dist/index.d.ts +12 -3
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +6 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/node.d.ts +60 -33
  19. package/dist/node.d.ts.map +1 -1
  20. package/dist/node.js +640 -324
  21. package/dist/node.js.map +1 -1
  22. package/dist/p2p/connection-manager.d.ts.map +1 -1
  23. package/dist/p2p/connection-manager.js +6 -0
  24. package/dist/p2p/connection-manager.js.map +1 -1
  25. package/dist/p2p/identity.d.ts +27 -3
  26. package/dist/p2p/identity.d.ts.map +1 -1
  27. package/dist/p2p/identity.js +52 -13
  28. package/dist/p2p/identity.js.map +1 -1
  29. package/dist/p2p/index.d.ts +0 -2
  30. package/dist/p2p/index.d.ts.map +1 -1
  31. package/dist/p2p/index.js +0 -2
  32. package/dist/p2p/index.js.map +1 -1
  33. package/dist/p2p/payment-codec.d.ts +7 -13
  34. package/dist/p2p/payment-codec.d.ts.map +1 -1
  35. package/dist/p2p/payment-codec.js +32 -50
  36. package/dist/p2p/payment-codec.js.map +1 -1
  37. package/dist/p2p/payment-mux.d.ts +11 -20
  38. package/dist/p2p/payment-mux.d.ts.map +1 -1
  39. package/dist/p2p/payment-mux.js +36 -52
  40. package/dist/p2p/payment-mux.js.map +1 -1
  41. package/dist/payments/buyer-payment-manager.d.ts +34 -93
  42. package/dist/payments/buyer-payment-manager.d.ts.map +1 -1
  43. package/dist/payments/buyer-payment-manager.js +184 -189
  44. package/dist/payments/buyer-payment-manager.js.map +1 -1
  45. package/dist/payments/chain-config.d.ts +38 -0
  46. package/dist/payments/chain-config.d.ts.map +1 -0
  47. package/dist/payments/chain-config.js +60 -0
  48. package/dist/payments/chain-config.js.map +1 -0
  49. package/dist/payments/evm/ants-token-client.d.ts +16 -0
  50. package/dist/payments/evm/ants-token-client.d.ts.map +1 -0
  51. package/dist/payments/evm/ants-token-client.js +66 -0
  52. package/dist/payments/evm/ants-token-client.js.map +1 -0
  53. package/dist/payments/evm/base-evm-client.d.ts +13 -0
  54. package/dist/payments/evm/base-evm-client.d.ts.map +1 -0
  55. package/dist/payments/evm/base-evm-client.js +38 -0
  56. package/dist/payments/evm/base-evm-client.js.map +1 -0
  57. package/dist/payments/evm/emissions-client.d.ts +23 -0
  58. package/dist/payments/evm/emissions-client.d.ts.map +1 -0
  59. package/dist/payments/evm/emissions-client.js +84 -0
  60. package/dist/payments/evm/emissions-client.js.map +1 -0
  61. package/dist/payments/evm/escrow-client.d.ts +57 -36
  62. package/dist/payments/evm/escrow-client.d.ts.map +1 -1
  63. package/dist/payments/evm/escrow-client.js +200 -93
  64. package/dist/payments/evm/escrow-client.js.map +1 -1
  65. package/dist/payments/evm/identity-client.d.ts +34 -0
  66. package/dist/payments/evm/identity-client.d.ts.map +1 -0
  67. package/dist/payments/evm/identity-client.js +125 -0
  68. package/dist/payments/evm/identity-client.js.map +1 -0
  69. package/dist/payments/evm/signatures.d.ts +18 -5
  70. package/dist/payments/evm/signatures.d.ts.map +1 -1
  71. package/dist/payments/evm/signatures.js +21 -12
  72. package/dist/payments/evm/signatures.js.map +1 -1
  73. package/dist/payments/evm/subpool-client.d.ts +30 -0
  74. package/dist/payments/evm/subpool-client.d.ts.map +1 -0
  75. package/dist/payments/evm/subpool-client.js +158 -0
  76. package/dist/payments/evm/subpool-client.js.map +1 -0
  77. package/dist/payments/index.d.ts +20 -7
  78. package/dist/payments/index.d.ts.map +1 -1
  79. package/dist/payments/index.js +17 -6
  80. package/dist/payments/index.js.map +1 -1
  81. package/dist/payments/readiness.d.ts +12 -0
  82. package/dist/payments/readiness.d.ts.map +1 -0
  83. package/dist/payments/readiness.js +64 -0
  84. package/dist/payments/readiness.js.map +1 -0
  85. package/dist/payments/seller-payment-manager.d.ts +74 -34
  86. package/dist/payments/seller-payment-manager.d.ts.map +1 -1
  87. package/dist/payments/seller-payment-manager.js +327 -118
  88. package/dist/payments/seller-payment-manager.js.map +1 -1
  89. package/dist/payments/session-store.d.ts +65 -0
  90. package/dist/payments/session-store.d.ts.map +1 -0
  91. package/dist/payments/session-store.js +243 -0
  92. package/dist/payments/session-store.js.map +1 -0
  93. package/dist/payments/usdc-utils.d.ts +9 -0
  94. package/dist/payments/usdc-utils.d.ts.map +1 -0
  95. package/dist/payments/usdc-utils.js +17 -0
  96. package/dist/payments/usdc-utils.js.map +1 -0
  97. package/dist/types/http.d.ts +2 -0
  98. package/dist/types/http.d.ts.map +1 -1
  99. package/dist/types/http.js +2 -0
  100. package/dist/types/http.js.map +1 -1
  101. package/dist/types/index.d.ts +0 -1
  102. package/dist/types/index.d.ts.map +1 -1
  103. package/dist/types/index.js +0 -1
  104. package/dist/types/index.js.map +1 -1
  105. package/dist/types/metering.d.ts +1 -1
  106. package/dist/types/metering.d.ts.map +1 -1
  107. package/dist/types/protocol.d.ts +43 -60
  108. package/dist/types/protocol.d.ts.map +1 -1
  109. package/dist/types/protocol.js +5 -8
  110. package/dist/types/protocol.js.map +1 -1
  111. package/package.json +3 -2
@@ -1,122 +1,63 @@
1
- import { type AbstractSigner, Wallet } from 'ethers';
1
+ import { type AbstractSigner } from 'ethers';
2
2
  import type { Identity } from '../p2p/identity.js';
3
3
  import type { PaymentMux } from '../p2p/payment-mux.js';
4
- import type { SessionLockConfirmPayload, SessionLockRejectPayload, SellerReceiptPayload, TopUpRequestPayload } from '../types/protocol.js';
4
+ import type { AuthAckPayload, SellerReceiptPayload, TopUpRequestPayload } from '../types/protocol.js';
5
5
  import { BaseEscrowClient } from './evm/escrow-client.js';
6
+ import { IdentityClient } from './evm/identity-client.js';
7
+ import { SessionStore, type StoredSession } from './session-store.js';
6
8
  export interface BuyerPaymentConfig {
7
- /** Default lock amount in USDC base units (6 decimals). e.g. "1000000" = 1 USDC */
8
- defaultLockAmountUSDC: string;
9
- /** Base JSON-RPC endpoint */
10
9
  rpcUrl: string;
11
- /** Deployed AntseedEscrow contract address */
12
10
  contractAddress: string;
13
- /** USDC token contract address */
14
11
  usdcAddress: string;
15
- /** Auto-acknowledge seller receipts. Default: true */
16
- autoAck?: boolean;
17
- /** Auto-approve top-up requests. Default: true */
18
- autoTopUp?: boolean;
19
- /** Maximum total amount the buyer will commit per session (USDC base units). Default: "10000000" (10 USDC) */
20
- maxSessionBudgetUSDC?: string;
21
- }
22
- export type BuyerSessionStatus = 'pending' | 'confirmed' | 'active' | 'ending' | 'ended';
23
- export interface BuyerSessionState {
24
- sessionId: string;
25
- sellerPeerId: string;
26
- sellerEvmAddress: string;
27
- lockedAmount: bigint;
28
- status: BuyerSessionStatus;
29
- txSignature: string | null;
30
- lastRunningTotal: bigint;
31
- lastRequestCount: number;
32
- createdAt: number;
33
- updatedAt: number;
12
+ identityAddress: string;
13
+ chainId: number;
14
+ defaultMaxAmountUsdc: bigint;
15
+ defaultAuthDurationSecs: number;
16
+ autoAck: boolean;
17
+ dataDir: string;
34
18
  }
35
19
  /**
36
- * Manages buyer-side bilateral payment sessions across seller connections.
37
- *
38
- * Handles the full lifecycle: lock initiation, receipt acknowledgement,
39
- * top-up approval, and session settlement.
20
+ * Manages buyer-side payment sessions using EIP-712 SpendingAuth
21
+ * with persistent session storage.
40
22
  */
41
23
  export declare class BuyerPaymentManager {
42
24
  private readonly _identity;
43
25
  private _signer;
44
26
  private readonly _escrowClient;
45
27
  private readonly _config;
46
- private readonly _sessions;
47
- constructor(identity: Identity, config: BuyerPaymentConfig);
28
+ private readonly _sessionStore;
29
+ /** In-memory map of active confirmed sessions by seller peerId for fast lookups. */
30
+ private readonly _confirmedPeers;
31
+ /** Peers that explicitly rejected our spending auth. */
32
+ private readonly _rejectedPeers;
33
+ private _nonceCounter;
34
+ constructor(identity: Identity, config: BuyerPaymentConfig, sessionStore: SessionStore);
48
35
  get signer(): AbstractSigner;
49
- /** @deprecated Use .signer instead */
50
- get wallet(): Wallet;
51
- /** Replace the signer at runtime (e.g. with a WalletConnect signer). */
52
36
  setSigner(signer: AbstractSigner): void;
53
37
  get escrowClient(): BaseEscrowClient;
54
- /** Get a snapshot of all active sessions. */
55
- getActiveSessions(): BuyerSessionState[];
56
- /** Get the session for a given seller peer, if it exists. */
57
- getSession(sellerPeerId: string): BuyerSessionState | undefined;
58
- /**
59
- * Generate a session ID, sign a lock authorization, and send it
60
- * to the seller via PaymentMux.
61
- */
62
- initiateLock(sellerPeerId: string, sellerEvmAddress: string, paymentMux: PaymentMux, lockAmount?: string): Promise<string>;
63
- /**
64
- * Called when the seller confirms the lock was committed on-chain.
65
- */
66
- handleLockConfirm(sellerPeerId: string, payload: SessionLockConfirmPayload): void;
67
- /**
68
- * Called when the seller rejects the lock.
69
- */
70
- handleLockReject(sellerPeerId: string, payload: SessionLockRejectPayload): void;
71
38
  /**
72
- * Handle a running-total receipt from the seller.
73
- * If autoAck is enabled, automatically counter-sign and send BuyerAck.
39
+ * Sign and send an EIP-712 SpendingAuth to a seller.
40
+ * Loads the latest session to build the proof chain.
74
41
  */
42
+ authorizeSpending(sellerPeerId: string, sellerEvmAddr: string, paymentMux: PaymentMux, maxAmount?: bigint): Promise<string>;
43
+ handleAuthAck(sellerPeerId: string, payload: AuthAckPayload): void;
75
44
  handleSellerReceipt(sellerPeerId: string, receipt: SellerReceiptPayload, paymentMux: PaymentMux): Promise<void>;
76
- /**
77
- * Handle a top-up request from the seller.
78
- * If autoTopUp is enabled and budget allows, sign and send TopUpAuth.
79
- * Otherwise, end the session.
80
- */
81
45
  handleTopUpRequest(sellerPeerId: string, request: TopUpRequestPayload, paymentMux: PaymentMux): Promise<void>;
82
- /**
83
- * End a session with the given seller. Signs a settlement message
84
- * with ECDSA and sends SessionEnd.
85
- */
86
- endSession(sellerPeerId: string, paymentMux: PaymentMux, score?: number): Promise<void>;
87
- /**
88
- * Deposit USDC into the escrow contract.
89
- * @param amount Amount in USDC base units (6 decimals).
90
- */
46
+ /** Check if a session has been confirmed via AuthAck. */
47
+ isAuthorized(sellerPeerId: string): boolean;
48
+ /** Alias for isAuthorized (used by polling loop). */
49
+ isLockConfirmed(sellerPeerId: string): boolean;
50
+ /** Check if the lock was explicitly rejected (not just never-contacted). */
51
+ isLockRejected(sellerPeerId: string): boolean;
52
+ /** Mark a peer as having rejected our spending auth. */
53
+ markRejected(sellerPeerId: string): void;
54
+ getSessionHistory(sellerPeerId: string): StoredSession[];
91
55
  deposit(amount: bigint): Promise<string>;
92
- /**
93
- * Withdraw USDC from the escrow contract.
94
- * @param amount Amount in USDC base units (6 decimals).
95
- */
96
56
  withdraw(amount: bigint): Promise<string>;
97
- /**
98
- * Get the buyer's on-chain escrow balance.
99
- */
100
57
  getBalance(): Promise<{
101
- deposited: bigint;
102
- committed: bigint;
103
58
  available: bigint;
59
+ reserved: bigint;
104
60
  }>;
105
- /**
106
- * Release an expired lock (buyer reclaims funds).
107
- */
108
- releaseExpiredLock(sessionId: string): Promise<string>;
109
- /**
110
- * Respond to a dispute opened by the seller.
111
- */
112
- respondToDispute(sessionId: string): Promise<string>;
113
- /**
114
- * Check if a session lock has been confirmed (for polling).
115
- */
116
- isLockConfirmed(sellerPeerId: string): boolean;
117
- /**
118
- * Check if a session lock has been rejected (for polling).
119
- */
120
- isLockRejected(sellerPeerId: string): boolean;
61
+ submitFeedback(sellerPeerId: string, qualityScore: number, identityClient: IdentityClient): Promise<string | null>;
121
62
  }
122
63
  //# sourceMappingURL=buyer-payment-manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"buyer-payment-manager.d.ts","sourceRoot":"","sources":["../../src/payments/buyer-payment-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"buyer-payment-manager.d.ts","sourceRoot":"","sources":["../../src/payments/buyer-payment-manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,cAAc,EAAuB,MAAM,QAAQ,CAAC;AAClE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EACV,cAAc,EACd,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAa1D,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEtE,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,uBAAuB,EAAE,MAAM,CAAC;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAID;;;GAGG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAmB;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAe;IAC7C,oFAAoF;IACpF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;IACrD,wDAAwD;IACxD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,aAAa,CAAS;gBAElB,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,kBAAkB,EAAE,YAAY,EAAE,YAAY;IAetF,IAAI,MAAM,IAAI,cAAc,CAE3B;IAED,SAAS,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAIvC,IAAI,YAAY,IAAI,gBAAgB,CAEnC;IAID;;;OAGG;IACG,iBAAiB,CACrB,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,UAAU,EACtB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC;IAsFlB,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI;IAiB5D,mBAAmB,CACvB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,oBAAoB,EAC7B,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,IAAI,CAAC;IAuFV,kBAAkB,CACtB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,mBAAmB,EAC5B,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,IAAI,CAAC;IA2BhB,yDAAyD;IACzD,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO;IAI3C,qDAAqD;IACrD,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO;IAI9C,4EAA4E;IAC5E,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO;IAI7C,wDAAwD;IACxD,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAKxC,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,aAAa,EAAE;IAelD,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKxC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKzC,UAAU,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAQ9D,cAAc,CAClB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,cAAc,GAC7B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;CAS1B"}
@@ -1,22 +1,27 @@
1
1
  import { randomBytes } from 'node:crypto';
2
+ import { encodeBytes32String } from 'ethers';
2
3
  import { BaseEscrowClient } from './evm/escrow-client.js';
3
4
  import { identityToEvmWallet, identityToEvmAddress } from './evm/keypair.js';
4
- import { buildLockMessageHash, buildSettlementMessageHash, buildExtendLockMessageHash, signMessageEcdsa, buildAckMessage, signMessageEd25519, } from './evm/signatures.js';
5
+ import { signSpendingAuth, makeEscrowDomain, buildAckMessage, signMessageEd25519, verifyMessageEd25519, buildReceiptMessage, } from './evm/signatures.js';
5
6
  import { bytesToHex, hexToBytes } from '../utils/hex.js';
6
7
  import { debugLog, debugWarn } from '../utils/debug.js';
8
+ const ZERO_SESSION_ID = '0x' + '0'.repeat(64);
7
9
  /**
8
- * Manages buyer-side bilateral payment sessions across seller connections.
9
- *
10
- * Handles the full lifecycle: lock initiation, receipt acknowledgement,
11
- * top-up approval, and session settlement.
10
+ * Manages buyer-side payment sessions using EIP-712 SpendingAuth
11
+ * with persistent session storage.
12
12
  */
13
13
  export class BuyerPaymentManager {
14
14
  _identity;
15
15
  _signer;
16
16
  _escrowClient;
17
17
  _config;
18
- _sessions = new Map();
19
- constructor(identity, config) {
18
+ _sessionStore;
19
+ /** In-memory map of active confirmed sessions by seller peerId for fast lookups. */
20
+ _confirmedPeers = new Set();
21
+ /** Peers that explicitly rejected our spending auth. */
22
+ _rejectedPeers = new Set();
23
+ _nonceCounter;
24
+ constructor(identity, config, sessionStore) {
20
25
  this._identity = identity;
21
26
  this._config = config;
22
27
  this._signer = identityToEvmWallet(identity);
@@ -25,121 +30,165 @@ export class BuyerPaymentManager {
25
30
  contractAddress: config.contractAddress,
26
31
  usdcAddress: config.usdcAddress,
27
32
  });
33
+ this._sessionStore = sessionStore;
34
+ // Restore nonce counter from persisted sessions to avoid duplicates across restarts
35
+ this._nonceCounter = sessionStore.getMaxNonce('buyer');
28
36
  }
29
37
  get signer() {
30
38
  return this._signer;
31
39
  }
32
- /** @deprecated Use .signer instead */
33
- get wallet() {
34
- return this._signer;
35
- }
36
- /** Replace the signer at runtime (e.g. with a WalletConnect signer). */
37
40
  setSigner(signer) {
38
41
  this._signer = signer;
39
42
  }
40
43
  get escrowClient() {
41
44
  return this._escrowClient;
42
45
  }
43
- /** Get a snapshot of all active sessions. */
44
- getActiveSessions() {
45
- return [...this._sessions.values()].filter((s) => s.status !== 'ended');
46
- }
47
- /** Get the session for a given seller peer, if it exists. */
48
- getSession(sellerPeerId) {
49
- return this._sessions.get(sellerPeerId);
50
- }
51
- // ── Lock initiation ─────────────────────────────────────────────
46
+ // ── Spending Authorization ────────────────────────────────────
52
47
  /**
53
- * Generate a session ID, sign a lock authorization, and send it
54
- * to the seller via PaymentMux.
48
+ * Sign and send an EIP-712 SpendingAuth to a seller.
49
+ * Loads the latest session to build the proof chain.
55
50
  */
56
- async initiateLock(sellerPeerId, sellerEvmAddress, paymentMux, lockAmount) {
57
- const amount = lockAmount ?? this._config.defaultLockAmountUSDC;
58
- const amountBigInt = BigInt(amount);
59
- // Generate a 32-byte session ID as 0x-prefixed hex (bytes32)
51
+ async authorizeSpending(sellerPeerId, sellerEvmAddr, paymentMux, maxAmount) {
52
+ const amount = maxAmount ?? this._config.defaultMaxAmountUsdc;
53
+ // Clear confirmation state so we wait for a fresh AuthAck on the new session
54
+ this._confirmedPeers.delete(sellerPeerId);
55
+ // Load latest session to build proof chain.
56
+ // Only chain if the session is settled or active-with-delivery (the seller
57
+ // will settle it on-chain before reserving the new one).
58
+ // Fall back to first-sign for timed-out/ghost sessions.
59
+ const latestSession = this._sessionStore.getLatestSession(sellerPeerId, 'buyer');
60
+ const canChain = latestSession
61
+ && latestSession.status !== 'timeout'
62
+ && latestSession.status !== 'ghost'
63
+ && BigInt(latestSession.tokensDelivered) > 0n;
64
+ const previousConsumption = canChain
65
+ ? BigInt(latestSession.tokensDelivered)
66
+ : 0n;
67
+ const previousSessionId = canChain
68
+ ? latestSession.sessionId
69
+ : ZERO_SESSION_ID;
70
+ // Generate a 32-byte session ID
60
71
  const sessionIdBytes = randomBytes(32);
61
72
  const sessionId = '0x' + sessionIdBytes.toString('hex');
62
- debugLog(`[BuyerPayment] Initiating lock: session=${sessionId.slice(0, 18)}... seller=${sellerPeerId.slice(0, 12)}... amount=${amount}`);
63
- // Sign the lock message with ECDSA (for on-chain verification)
64
- const messageHash = buildLockMessageHash(sessionId, sellerEvmAddress, amountBigInt);
65
- const buyerSig = await signMessageEcdsa(this._signer, messageHash);
66
- // Store session state
73
+ const nonce = ++this._nonceCounter;
74
+ const deadline = Math.floor(Date.now() / 1000) + this._config.defaultAuthDurationSecs;
75
+ debugLog(`[BuyerPayment] authorizeSpending: session=${sessionId.slice(0, 18)}... seller=${sellerPeerId.slice(0, 12)}... amount=${amount}`);
76
+ // Sign EIP-712 SpendingAuth
77
+ const domain = makeEscrowDomain(this._config.chainId, this._config.contractAddress);
78
+ const msg = {
79
+ seller: sellerEvmAddr,
80
+ sessionId,
81
+ maxAmount: amount,
82
+ nonce,
83
+ deadline,
84
+ previousConsumption,
85
+ previousSessionId,
86
+ };
87
+ const buyerSig = await signSpendingAuth(this._signer, domain, msg);
88
+ const buyerEvmAddr = identityToEvmAddress(this._identity);
89
+ // Store session
67
90
  const now = Date.now();
68
91
  const session = {
69
92
  sessionId,
70
- sellerPeerId,
71
- sellerEvmAddress,
72
- lockedAmount: amountBigInt,
73
- status: 'pending',
74
- txSignature: null,
75
- lastRunningTotal: 0n,
76
- lastRequestCount: 0,
93
+ peerId: sellerPeerId,
94
+ role: 'buyer',
95
+ sellerEvmAddr,
96
+ buyerEvmAddr,
97
+ nonce,
98
+ authMax: amount.toString(),
99
+ deadline,
100
+ previousSessionId,
101
+ previousConsumption: previousConsumption.toString(),
102
+ tokensDelivered: '0',
103
+ requestCount: 0,
104
+ reservedAt: now,
105
+ settledAt: null,
106
+ settledAmount: null,
107
+ status: 'active',
77
108
  createdAt: now,
78
109
  updatedAt: now,
79
110
  };
80
- this._sessions.set(sellerPeerId, session);
81
- // Send the lock auth message
82
- paymentMux.sendSessionLockAuth({
111
+ this._sessionStore.upsertSession(session);
112
+ // Send SpendingAuth via PaymentMux
113
+ paymentMux.sendSpendingAuth({
83
114
  sessionId,
84
- lockedAmount: amount,
115
+ maxAmountUsdc: amount.toString(),
116
+ nonce,
117
+ deadline,
85
118
  buyerSig,
119
+ buyerEvmAddr,
120
+ previousConsumption: previousConsumption.toString(),
121
+ previousSessionId,
86
122
  });
87
123
  return sessionId;
88
124
  }
89
- // ── Lock confirmation / rejection handlers ──────────────────────
90
- /**
91
- * Called when the seller confirms the lock was committed on-chain.
92
- */
93
- handleLockConfirm(sellerPeerId, payload) {
94
- const session = this._sessions.get(sellerPeerId);
125
+ // ── AuthAck handler ───────────────────────────────────────────
126
+ handleAuthAck(sellerPeerId, payload) {
127
+ const session = this._sessionStore.getActiveSessionByPeer(sellerPeerId, 'buyer');
95
128
  if (!session) {
96
- debugWarn(`[BuyerPayment] Lock confirm for unknown seller: ${sellerPeerId.slice(0, 12)}...`);
129
+ debugWarn(`[BuyerPayment] AuthAck for unknown seller: ${sellerPeerId.slice(0, 12)}...`);
97
130
  return;
98
131
  }
99
132
  if (session.sessionId !== payload.sessionId) {
100
- debugWarn(`[BuyerPayment] Lock confirm session mismatch: expected=${session.sessionId.slice(0, 18)}... got=${payload.sessionId.slice(0, 18)}...`);
133
+ debugWarn(`[BuyerPayment] AuthAck session mismatch: expected=${session.sessionId.slice(0, 18)}... got=${payload.sessionId.slice(0, 18)}...`);
101
134
  return;
102
135
  }
103
- session.status = 'confirmed';
104
- session.txSignature = payload.txSignature;
105
- session.updatedAt = Date.now();
106
- debugLog(`[BuyerPayment] Lock confirmed: session=${session.sessionId.slice(0, 18)}... tx=${payload.txSignature.slice(0, 12)}...`);
136
+ this._confirmedPeers.add(sellerPeerId);
137
+ debugLog(`[BuyerPayment] AuthAck confirmed: session=${session.sessionId.slice(0, 18)}...`);
107
138
  }
108
- /**
109
- * Called when the seller rejects the lock.
110
- */
111
- handleLockReject(sellerPeerId, payload) {
112
- const session = this._sessions.get(sellerPeerId);
113
- if (!session) {
114
- debugWarn(`[BuyerPayment] Lock reject for unknown seller: ${sellerPeerId.slice(0, 12)}...`);
115
- return;
116
- }
117
- debugWarn(`[BuyerPayment] Lock rejected: session=${session.sessionId.slice(0, 18)}... reason=${payload.reason}`);
118
- this._sessions.delete(sellerPeerId);
119
- }
120
- // ── Receipt handling ────────────────────────────────────────────
121
- /**
122
- * Handle a running-total receipt from the seller.
123
- * If autoAck is enabled, automatically counter-sign and send BuyerAck.
124
- */
139
+ // ── Seller Receipt handler ────────────────────────────────────
125
140
  async handleSellerReceipt(sellerPeerId, receipt, paymentMux) {
126
- const session = this._sessions.get(sellerPeerId);
141
+ const session = this._sessionStore.getActiveSessionByPeer(sellerPeerId, 'buyer');
127
142
  if (!session) {
128
143
  debugWarn(`[BuyerPayment] Receipt for unknown seller: ${sellerPeerId.slice(0, 12)}...`);
129
144
  return;
130
145
  }
131
- if (session.status === 'confirmed') {
132
- session.status = 'active';
146
+ if (session.sessionId !== receipt.sessionId) {
147
+ debugWarn(`[BuyerPayment] Receipt session ID mismatch: active=${session.sessionId.slice(0, 18)}... receipt=${receipt.sessionId.slice(0, 18)}... — discarding stale receipt`);
148
+ return;
133
149
  }
134
- // Update running total
135
- session.lastRunningTotal = BigInt(receipt.runningTotal);
136
- session.lastRequestCount = receipt.requestCount;
137
- session.updatedAt = Date.now();
150
+ // Verify seller's Ed25519 signature
151
+ try {
152
+ const sellerPublicKey = hexToBytes(sellerPeerId);
153
+ const sessionIdBytes = hexToBytes(receipt.sessionId.replace(/^0x/, ''));
154
+ const responseHashBytes = hexToBytes(receipt.responseHash);
155
+ const receiptMsg = buildReceiptMessage(sessionIdBytes, BigInt(receipt.runningTotal), receipt.requestCount, responseHashBytes);
156
+ const sigBytes = hexToBytes(receipt.sellerSig);
157
+ const valid = await verifyMessageEd25519(sellerPublicKey, sigBytes, receiptMsg);
158
+ if (!valid) {
159
+ debugWarn(`[BuyerPayment] Invalid seller receipt signature from ${sellerPeerId.slice(0, 12)}...`);
160
+ return;
161
+ }
162
+ }
163
+ catch (err) {
164
+ debugWarn(`[BuyerPayment] Failed to verify receipt: ${err instanceof Error ? err.message : err}`);
165
+ return;
166
+ }
167
+ // Validate monotonic increase: runningTotal must exceed previous
168
+ const newTotal = BigInt(receipt.runningTotal);
169
+ const prevTotal = BigInt(session.tokensDelivered);
170
+ if (newTotal <= prevTotal) {
171
+ debugWarn(`[BuyerPayment] Receipt runningTotal not monotonic: new=${newTotal} prev=${prevTotal}`);
172
+ return;
173
+ }
174
+ // Note: we don't compare token count against authMax (USDC) here because
175
+ // they're in different units (tokens vs USDC base units). The on-chain
176
+ // settle() caps chargeAmount = min(tokenCount * tokenRate, maxAmount),
177
+ // so the buyer's EIP-712 signature is the real USDC protection.
138
178
  debugLog(`[BuyerPayment] Receipt: session=${session.sessionId.slice(0, 18)}... total=${receipt.runningTotal} count=${receipt.requestCount}`);
139
- const autoAck = this._config.autoAck ?? true;
140
- if (autoAck) {
141
- // Build ack message and sign with Ed25519
142
- const sessionIdBytes = hexToBytes(session.sessionId.startsWith('0x') ? session.sessionId.slice(2) : session.sessionId);
179
+ // Atomically update tokens delivered and store receipt
180
+ this._sessionStore.updateDeliveredAndInsertReceipt(session.sessionId, receipt.runningTotal, receipt.requestCount, {
181
+ sessionId: session.sessionId,
182
+ runningTotal: receipt.runningTotal,
183
+ requestCount: receipt.requestCount,
184
+ responseHash: receipt.responseHash,
185
+ sellerSig: receipt.sellerSig,
186
+ buyerAckSig: null,
187
+ createdAt: Date.now(),
188
+ });
189
+ // Auto-ack if configured
190
+ if (this._config.autoAck) {
191
+ const sessionIdBytes = hexToBytes(session.sessionId.replace(/^0x/, ''));
143
192
  const ackMsg = buildAckMessage(sessionIdBytes, BigInt(receipt.runningTotal), receipt.requestCount);
144
193
  const sigBytes = await signMessageEd25519(this._identity, ackMsg);
145
194
  const buyerSig = bytesToHex(sigBytes);
@@ -152,129 +201,75 @@ export class BuyerPaymentManager {
152
201
  debugLog(`[BuyerPayment] Auto-ack sent for session=${session.sessionId.slice(0, 18)}...`);
153
202
  }
154
203
  }
155
- // ── Top-up handling ─────────────────────────────────────────────
156
- /**
157
- * Handle a top-up request from the seller.
158
- * If autoTopUp is enabled and budget allows, sign and send TopUpAuth.
159
- * Otherwise, end the session.
160
- */
204
+ // ── TopUp handler ─────────────────────────────────────────────
161
205
  async handleTopUpRequest(sellerPeerId, request, paymentMux) {
162
- const session = this._sessions.get(sellerPeerId);
206
+ const session = this._sessionStore.getActiveSessionByPeer(sellerPeerId, 'buyer');
163
207
  if (!session) {
164
208
  debugWarn(`[BuyerPayment] Top-up for unknown seller: ${sellerPeerId.slice(0, 12)}...`);
165
209
  return;
166
210
  }
167
- const additionalAmount = BigInt(request.additionalAmount);
168
- const maxBudget = BigInt(this._config.maxSessionBudgetUSDC ?? '10000000');
169
- const newTotal = session.lockedAmount + additionalAmount;
170
- const autoTopUp = this._config.autoTopUp ?? true;
171
- debugLog(`[BuyerPayment] Top-up request: session=${session.sessionId.slice(0, 18)}... additional=${request.additionalAmount} newTotal=${newTotal}`);
172
- if (autoTopUp && newTotal <= maxBudget) {
173
- // Check on-chain balance
174
- const buyerAddr = identityToEvmAddress(this._identity);
175
- const account = await this._escrowClient.getBuyerAccount(buyerAddr);
176
- if (account.available >= additionalAmount) {
177
- // Sign extend-lock authorization
178
- const messageHash = buildExtendLockMessageHash(session.sessionId, session.sellerEvmAddress, additionalAmount);
179
- const buyerSig = await signMessageEcdsa(this._signer, messageHash);
180
- session.lockedAmount = newTotal;
181
- session.updatedAt = Date.now();
182
- paymentMux.sendTopUpAuth({
183
- sessionId: session.sessionId,
184
- additionalAmount: request.additionalAmount,
185
- buyerSig,
186
- });
187
- debugLog(`[BuyerPayment] Top-up authorized: session=${session.sessionId.slice(0, 18)}...`);
188
- return;
189
- }
190
- debugWarn(`[BuyerPayment] Insufficient balance for top-up. Available=${account.available}, requested=${additionalAmount}`);
191
- }
192
- // Cannot or will not top up — end the session
193
- debugLog(`[BuyerPayment] Declining top-up, ending session=${session.sessionId.slice(0, 18)}...`);
194
- await this.endSession(sellerPeerId, paymentMux, 80);
211
+ debugLog(`[BuyerPayment] TopUp request: session=${session.sessionId.slice(0, 18)}... currentUsed=${request.currentUsed} currentMax=${request.currentMax}`);
212
+ // Sign a new SpendingAuth with increased cap
213
+ const currentMax = BigInt(session.authMax);
214
+ const additionalAmount = BigInt(request.requestedAdditional);
215
+ const newMax = currentMax + additionalAmount;
216
+ // The new auth embeds the current consumption as previousConsumption
217
+ await this.authorizeSpending(sellerPeerId, session.sellerEvmAddr, paymentMux, newMax);
218
+ debugLog(`[BuyerPayment] TopUp authorized: new auth sent with max=${newMax}`);
195
219
  }
196
- // ── Session end ─────────────────────────────────────────────────
197
- /**
198
- * End a session with the given seller. Signs a settlement message
199
- * with ECDSA and sends SessionEnd.
200
- */
201
- async endSession(sellerPeerId, paymentMux, score = 80) {
202
- const session = this._sessions.get(sellerPeerId);
203
- if (!session) {
204
- debugWarn(`[BuyerPayment] Cannot end session for unknown seller: ${sellerPeerId.slice(0, 12)}...`);
205
- return;
206
- }
207
- if (session.status === 'ending' || session.status === 'ended') {
208
- return;
220
+ // ── Queries ───────────────────────────────────────────────────
221
+ /** Check if a session has been confirmed via AuthAck. */
222
+ isAuthorized(sellerPeerId) {
223
+ return this._confirmedPeers.has(sellerPeerId);
224
+ }
225
+ /** Alias for isAuthorized (used by polling loop). */
226
+ isLockConfirmed(sellerPeerId) {
227
+ return this.isAuthorized(sellerPeerId);
228
+ }
229
+ /** Check if the lock was explicitly rejected (not just never-contacted). */
230
+ isLockRejected(sellerPeerId) {
231
+ return this._rejectedPeers.has(sellerPeerId);
232
+ }
233
+ /** Mark a peer as having rejected our spending auth. */
234
+ markRejected(sellerPeerId) {
235
+ this._rejectedPeers.add(sellerPeerId);
236
+ debugLog(`[BuyerPayment] Peer ${sellerPeerId.slice(0, 12)}... marked as rejected`);
237
+ }
238
+ getSessionHistory(sellerPeerId) {
239
+ const sessions = [];
240
+ const seen = new Set();
241
+ let session = this._sessionStore.getLatestSession(sellerPeerId, 'buyer');
242
+ while (session && !seen.has(session.sessionId)) {
243
+ seen.add(session.sessionId);
244
+ sessions.unshift(session);
245
+ if (session.previousSessionId === ZERO_SESSION_ID)
246
+ break;
247
+ session = this._sessionStore.getSession(session.previousSessionId);
209
248
  }
210
- session.status = 'ending';
211
- session.updatedAt = Date.now();
212
- debugLog(`[BuyerPayment] Ending session=${session.sessionId.slice(0, 18)}... total=${session.lastRunningTotal} score=${score}`);
213
- // Sign settlement message with ECDSA
214
- const messageHash = buildSettlementMessageHash(session.sessionId, session.lastRunningTotal, score);
215
- const buyerSig = await signMessageEcdsa(this._signer, messageHash);
216
- paymentMux.sendSessionEnd({
217
- sessionId: session.sessionId,
218
- runningTotal: session.lastRunningTotal.toString(),
219
- requestCount: session.lastRequestCount,
220
- score,
221
- buyerSig,
222
- });
223
- session.status = 'ended';
224
- session.updatedAt = Date.now();
225
- debugLog(`[BuyerPayment] Session ended: ${session.sessionId.slice(0, 18)}...`);
249
+ return sessions;
226
250
  }
227
- // ── Escrow operations ───────────────────────────────────────────
228
- /**
229
- * Deposit USDC into the escrow contract.
230
- * @param amount Amount in USDC base units (6 decimals).
231
- */
251
+ // ── Escrow operations ─────────────────────────────────────────
232
252
  async deposit(amount) {
233
253
  debugLog(`[BuyerPayment] Depositing ${amount} to escrow`);
234
254
  return this._escrowClient.deposit(this._signer, amount);
235
255
  }
236
- /**
237
- * Withdraw USDC from the escrow contract.
238
- * @param amount Amount in USDC base units (6 decimals).
239
- */
240
256
  async withdraw(amount) {
241
- debugLog(`[BuyerPayment] Withdrawing ${amount} from escrow`);
242
- return this._escrowClient.withdraw(this._signer, amount);
257
+ debugLog(`[BuyerPayment] Requesting withdrawal of ${amount} from escrow`);
258
+ return this._escrowClient.requestWithdrawal(this._signer, amount);
243
259
  }
244
- /**
245
- * Get the buyer's on-chain escrow balance.
246
- */
247
260
  async getBalance() {
248
261
  const buyerAddr = identityToEvmAddress(this._identity);
249
- return this._escrowClient.getBuyerAccount(buyerAddr);
250
- }
251
- // ── Dispute helpers ─────────────────────────────────────────────
252
- /**
253
- * Release an expired lock (buyer reclaims funds).
254
- */
255
- async releaseExpiredLock(sessionId) {
256
- debugLog(`[BuyerPayment] Releasing expired lock: session=${sessionId.slice(0, 18)}...`);
257
- return this._escrowClient.releaseExpiredLock(this._signer, sessionId);
258
- }
259
- /**
260
- * Respond to a dispute opened by the seller.
261
- */
262
- async respondToDispute(sessionId) {
263
- debugLog(`[BuyerPayment] Responding to dispute: session=${sessionId.slice(0, 18)}...`);
264
- return this._escrowClient.respondDispute(this._signer, sessionId);
265
- }
266
- /**
267
- * Check if a session lock has been confirmed (for polling).
268
- */
269
- isLockConfirmed(sellerPeerId) {
270
- const session = this._sessions.get(sellerPeerId);
271
- return session?.status === 'confirmed' || session?.status === 'active';
262
+ const info = await this._escrowClient.getBuyerBalance(buyerAddr);
263
+ return { available: info.available, reserved: info.reserved };
272
264
  }
273
- /**
274
- * Check if a session lock has been rejected (for polling).
275
- */
276
- isLockRejected(sellerPeerId) {
277
- return !this._sessions.has(sellerPeerId);
265
+ // ── Feedback (Task 6) ─────────────────────────────────────────
266
+ async submitFeedback(sellerPeerId, qualityScore, identityClient) {
267
+ const session = this._sessionStore.getLatestSession(sellerPeerId, 'buyer');
268
+ if (!session || session.status !== 'settled')
269
+ return null;
270
+ const tokenId = await identityClient.getTokenId(session.sellerEvmAddr);
271
+ const tag = encodeBytes32String('quality');
272
+ return identityClient.submitFeedback(this._signer, tokenId, qualityScore, tag);
278
273
  }
279
274
  }
280
275
  //# sourceMappingURL=buyer-payment-manager.js.map