@perkos/scheme-deferred 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,357 @@
1
+ # @perkos/scheme-deferred
2
+
3
+ EIP-712 voucher-based deferred payment verification utilities for x402 deferred scheme. Provides signature verification, escrow balance checking, and voucher validation for off-chain payment aggregation.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @perkos/scheme-deferred
9
+ ```
10
+
11
+ ## Overview
12
+
13
+ The deferred scheme enables off-chain voucher signing with on-chain batch settlement:
14
+
15
+ 1. **Client deposits** funds into escrow contract
16
+ 2. **Client signs vouchers** (EIP-712) for each payment
17
+ 3. **Facilitator verifies** signatures and escrow balance
18
+ 4. **Seller claims vouchers** via escrow contract
19
+
20
+ ## Usage
21
+
22
+ ### Basic Verification
23
+
24
+ ```typescript
25
+ import { DeferredSchemeVerifier } from '@perkos/scheme-deferred';
26
+ import type { DeferredPayload, PaymentRequirements } from '@perkos/scheme-deferred';
27
+
28
+ const verifier = new DeferredSchemeVerifier({
29
+ network: 'base',
30
+ escrowAddress: '0x...',
31
+ rpcUrl: 'https://mainnet.base.org' // optional
32
+ });
33
+
34
+ const payload: DeferredPayload = {
35
+ voucher: {
36
+ id: '0x...',
37
+ buyer: '0x...',
38
+ seller: '0x...',
39
+ valueAggregate: '5000000',
40
+ asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
41
+ timestamp: '1735689600',
42
+ nonce: '1',
43
+ escrow: '0x...',
44
+ chainId: '8453'
45
+ },
46
+ signature: '0x...'
47
+ };
48
+
49
+ const requirements: PaymentRequirements = {
50
+ scheme: 'deferred',
51
+ network: 'base',
52
+ maxAmountRequired: '1000000',
53
+ resource: '/api/service',
54
+ payTo: '0x...',
55
+ maxTimeoutSeconds: 3600,
56
+ asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'
57
+ };
58
+
59
+ const result = await verifier.verify(payload, requirements);
60
+
61
+ if (result.isValid) {
62
+ console.log('Valid voucher from:', result.payer);
63
+ } else {
64
+ console.error('Invalid:', result.invalidReason);
65
+ }
66
+ ```
67
+
68
+ ### Create Voucher for Signing
69
+
70
+ ```typescript
71
+ import {
72
+ generateVoucherId,
73
+ createVoucherMessage,
74
+ createEIP712Domain,
75
+ VOUCHER_TYPES
76
+ } from '@perkos/scheme-deferred';
77
+ import { signTypedData } from 'viem/accounts';
78
+
79
+ // Generate unique voucher ID
80
+ const voucherId = generateVoucherId();
81
+
82
+ // Create voucher message
83
+ const message = createVoucherMessage(
84
+ voucherId,
85
+ '0x...buyer', // buyer
86
+ '0x...seller', // seller
87
+ '5000000', // valueAggregate
88
+ '0x...usdc', // asset
89
+ Math.floor(Date.now() / 1000), // timestamp
90
+ '1', // nonce
91
+ '0x...escrow', // escrow
92
+ 8453 // chainId
93
+ );
94
+
95
+ // Create EIP-712 domain
96
+ const domain = createEIP712Domain(
97
+ 8453, // chainId
98
+ '0x...escrow' // escrow contract address
99
+ );
100
+
101
+ // Sign the voucher (client-side)
102
+ const signature = await signTypedData({
103
+ domain,
104
+ types: VOUCHER_TYPES,
105
+ primaryType: 'Voucher',
106
+ message,
107
+ privateKey: '0x...'
108
+ });
109
+ ```
110
+
111
+ ### Check Escrow Balance
112
+
113
+ ```typescript
114
+ const verifier = new DeferredSchemeVerifier({
115
+ network: 'base',
116
+ escrowAddress: '0x...'
117
+ });
118
+
119
+ const balance = await verifier.getEscrowBalance(
120
+ '0x...buyer',
121
+ '0x...seller',
122
+ '0x...asset'
123
+ );
124
+
125
+ console.log('Available balance:', balance.toString());
126
+ ```
127
+
128
+ ### Check Voucher Status
129
+
130
+ ```typescript
131
+ const claimed = await verifier.isVoucherClaimed(
132
+ '0x...voucherId',
133
+ 1n // nonce
134
+ );
135
+
136
+ if (claimed) {
137
+ console.log('Voucher already claimed');
138
+ }
139
+ ```
140
+
141
+ ### Recover Signer from Voucher
142
+
143
+ ```typescript
144
+ const signer = await verifier.recoverSigner(voucher, signature);
145
+
146
+ if (signer && signer.toLowerCase() === voucher.buyer.toLowerCase()) {
147
+ console.log('Valid signature from buyer');
148
+ }
149
+ ```
150
+
151
+ ## API Reference
152
+
153
+ ### DeferredSchemeVerifier
154
+
155
+ ```typescript
156
+ class DeferredSchemeVerifier {
157
+ constructor(config: DeferredSchemeConfig);
158
+
159
+ // Verification
160
+ verify(payload: DeferredPayload, requirements: PaymentRequirements): Promise<VerifyResponse>;
161
+ validateVoucher(voucher: Voucher, requirements: PaymentRequirements): boolean;
162
+ recoverSigner(voucher: Voucher, signature: Hex): Promise<Address | null>;
163
+
164
+ // Escrow operations
165
+ getEscrowBalance(buyer: Address, seller: Address, asset: Address): Promise<bigint>;
166
+ isVoucherClaimed(voucherId: Hex, nonce: bigint): Promise<boolean>;
167
+
168
+ // Getters
169
+ getNetwork(): SupportedNetwork;
170
+ getChainId(): number;
171
+ getEscrowAddress(): Address;
172
+ getEIP712Domain(): EIP712Domain;
173
+ }
174
+ ```
175
+
176
+ ### DeferredSchemeConfig
177
+
178
+ ```typescript
179
+ interface DeferredSchemeConfig {
180
+ network: SupportedNetwork;
181
+ escrowAddress: Address;
182
+ rpcUrl?: string;
183
+ domainName?: string; // default: "X402DeferredEscrow"
184
+ domainVersion?: string; // default: "1"
185
+ }
186
+ ```
187
+
188
+ ### EIP712Domain
189
+
190
+ ```typescript
191
+ interface EIP712Domain {
192
+ name: string;
193
+ version: string;
194
+ chainId: number;
195
+ verifyingContract: Address;
196
+ }
197
+ ```
198
+
199
+ ### SignatureParts
200
+
201
+ ```typescript
202
+ interface SignatureParts {
203
+ v: number;
204
+ r: Hex;
205
+ s: Hex;
206
+ }
207
+ ```
208
+
209
+ ## Utility Functions
210
+
211
+ ### generateVoucherId
212
+
213
+ Generate a random bytes32 voucher ID.
214
+
215
+ ```typescript
216
+ import { generateVoucherId } from '@perkos/scheme-deferred';
217
+
218
+ const voucherId = generateVoucherId();
219
+ // => '0x1234...abcd' (32 bytes hex)
220
+ ```
221
+
222
+ ### createVoucherMessage
223
+
224
+ Create a voucher message object for EIP-712 signing.
225
+
226
+ ```typescript
227
+ import { createVoucherMessage } from '@perkos/scheme-deferred';
228
+
229
+ const message = createVoucherMessage(
230
+ '0x...', // id
231
+ '0x...', // buyer
232
+ '0x...', // seller
233
+ '5000000', // valueAggregate
234
+ '0x...', // asset
235
+ 1735689600, // timestamp
236
+ '1', // nonce
237
+ '0x...', // escrow
238
+ 8453 // chainId
239
+ );
240
+ ```
241
+
242
+ ### createVoucherTuple
243
+
244
+ Convert a Voucher object to a tuple format for contract calls.
245
+
246
+ ```typescript
247
+ import { createVoucherTuple } from '@perkos/scheme-deferred';
248
+
249
+ const tuple = createVoucherTuple(voucher);
250
+ // Use with escrow contract claimVoucher function
251
+ ```
252
+
253
+ ### createEIP712Domain
254
+
255
+ Create an EIP-712 domain for voucher signing.
256
+
257
+ ```typescript
258
+ import { createEIP712Domain } from '@perkos/scheme-deferred';
259
+
260
+ const domain = createEIP712Domain(
261
+ 8453, // chainId
262
+ '0x...', // escrowAddress
263
+ 'CustomName', // optional domain name
264
+ '2' // optional version
265
+ );
266
+ ```
267
+
268
+ ### parseSignature
269
+
270
+ Parse a signature into v, r, s components.
271
+
272
+ ```typescript
273
+ import { parseSignature } from '@perkos/scheme-deferred';
274
+
275
+ const { v, r, s } = parseSignature('0x...');
276
+ ```
277
+
278
+ ## EIP-712 Type Definition
279
+
280
+ The voucher type definition used for EIP-712 signing:
281
+
282
+ ```typescript
283
+ import { VOUCHER_TYPES, VOUCHER_TYPE_DEF } from '@perkos/scheme-deferred';
284
+
285
+ // VOUCHER_TYPE_DEF structure:
286
+ [
287
+ { name: "id", type: "bytes32" },
288
+ { name: "buyer", type: "address" },
289
+ { name: "seller", type: "address" },
290
+ { name: "valueAggregate", type: "uint256" },
291
+ { name: "asset", type: "address" },
292
+ { name: "timestamp", type: "uint64" },
293
+ { name: "nonce", type: "uint256" },
294
+ { name: "escrow", type: "address" },
295
+ { name: "chainId", type: "uint256" }
296
+ ]
297
+ ```
298
+
299
+ ## Escrow Contract ABIs
300
+
301
+ The package exports ABIs for interacting with the deferred escrow contract:
302
+
303
+ ```typescript
304
+ import {
305
+ DEFERRED_ESCROW_ABI,
306
+ DEFERRED_ESCROW_GET_BALANCE_ABI,
307
+ DEFERRED_ESCROW_VOUCHER_CLAIMED_ABI,
308
+ DEFERRED_ESCROW_CLAIM_VOUCHER_ABI,
309
+ ERC20_BALANCE_ABI
310
+ } from '@perkos/scheme-deferred';
311
+ ```
312
+
313
+ ### Available Functions
314
+
315
+ | ABI | Function | Description |
316
+ |-----|----------|-------------|
317
+ | `DEFERRED_ESCROW_GET_BALANCE_ABI` | `getAvailableBalance(buyer, seller, asset)` | Get escrow balance |
318
+ | `DEFERRED_ESCROW_VOUCHER_CLAIMED_ABI` | `voucherClaimed(voucherId, nonce)` | Check if claimed |
319
+ | `DEFERRED_ESCROW_CLAIM_VOUCHER_ABI` | `claimVoucher(voucher, signature)` | Claim voucher |
320
+
321
+ ## Verification Flow
322
+
323
+ The `verify()` method performs these checks in order:
324
+
325
+ 1. **Voucher Validation**: Checks escrow address, chainId, seller, amount, and asset
326
+ 2. **Signature Recovery**: Recovers signer using EIP-712 typed data
327
+ 3. **Signer Verification**: Ensures signer matches voucher buyer
328
+ 4. **Claim Status**: Checks if voucher already claimed on-chain
329
+ 5. **Balance Check**: Verifies sufficient escrow balance
330
+
331
+ ## Re-exported Types
332
+
333
+ ```typescript
334
+ import type {
335
+ DeferredPayload,
336
+ Voucher,
337
+ VerifyResponse,
338
+ PaymentRequirements,
339
+ Address,
340
+ Hex
341
+ } from '@perkos/scheme-deferred';
342
+
343
+ // V2 helper
344
+ import { getPaymentAmount } from '@perkos/scheme-deferred';
345
+ ```
346
+
347
+ ## Related Packages
348
+
349
+ - [@perkos/types-x402](https://www.npmjs.com/package/@perkos/types-x402) - Core x402 types
350
+ - [@perkos/util-chains](https://www.npmjs.com/package/@perkos/util-chains) - Chain utilities
351
+ - [@perkos/scheme-exact](https://www.npmjs.com/package/@perkos/scheme-exact) - Exact payment scheme
352
+ - [@perkos/contracts-escrow](https://www.npmjs.com/package/@perkos/contracts-escrow) - Escrow contract ABI
353
+ - [@perkos/service-x402](https://www.npmjs.com/package/@perkos/service-x402) - x402 service orchestrator
354
+
355
+ ## License
356
+
357
+ MIT
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Address, Hex, DeferredPayload, PaymentRequirements, VerifyResponse, Voucher } from '@perkos/types-x402';
2
- export { Address, DeferredPayload, Hex, PaymentRequirements, VerifyResponse, Voucher, getPaymentAmount } from '@perkos/types-x402';
2
+ export { Address, DeferredPayload, Hex, PaymentRequirements, VerifyResponse, Voucher } from '@perkos/types-x402';
3
3
  import { SupportedNetwork } from '@perkos/util-chains';
4
4
 
5
5
  /**
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Address, Hex, DeferredPayload, PaymentRequirements, VerifyResponse, Voucher } from '@perkos/types-x402';
2
- export { Address, DeferredPayload, Hex, PaymentRequirements, VerifyResponse, Voucher, getPaymentAmount } from '@perkos/types-x402';
2
+ export { Address, DeferredPayload, Hex, PaymentRequirements, VerifyResponse, Voucher } from '@perkos/types-x402';
3
3
  import { SupportedNetwork } from '@perkos/util-chains';
4
4
 
5
5
  /**
package/dist/index.js CHANGED
@@ -32,14 +32,11 @@ __export(index_exports, {
32
32
  createVoucherMessage: () => createVoucherMessage,
33
33
  createVoucherTuple: () => createVoucherTuple,
34
34
  generateVoucherId: () => generateVoucherId,
35
- getPaymentAmount: () => import_types_x4022.getPaymentAmount,
36
35
  parseSignature: () => parseSignature
37
36
  });
38
37
  module.exports = __toCommonJS(index_exports);
39
38
  var import_viem = require("viem");
40
- var import_types_x402 = require("@perkos/types-x402");
41
39
  var import_util_chains = require("@perkos/util-chains");
42
- var import_types_x4022 = require("@perkos/types-x402");
43
40
  var VOUCHER_TYPE_DEF = [
44
41
  { name: "id", type: "bytes32" },
45
42
  { name: "buyer", type: "address" },
@@ -215,7 +212,7 @@ var DeferredSchemeVerifier = class {
215
212
  return false;
216
213
  }
217
214
  const valueAggregate = BigInt(voucher.valueAggregate);
218
- const maxAmount = BigInt((0, import_types_x402.getPaymentAmount)(requirements));
215
+ const maxAmount = BigInt(requirements.maxAmountRequired || "0");
219
216
  if (valueAggregate > maxAmount) {
220
217
  return false;
221
218
  }
@@ -332,7 +329,7 @@ function createEIP712Domain(chainId, escrowAddress, domainName, domainVersion) {
332
329
  }
333
330
  function generateVoucherId() {
334
331
  const bytes = new Uint8Array(32);
335
- crypto.getRandomValues(bytes);
332
+ globalThis.crypto.getRandomValues(bytes);
336
333
  return `0x${Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("")}`;
337
334
  }
338
335
  function createVoucherMessage(id, buyer, seller, valueAggregate, asset, timestamp, nonce, escrow, chainId) {
@@ -375,6 +372,5 @@ function createVoucherTuple(voucher) {
375
372
  createVoucherMessage,
376
373
  createVoucherTuple,
377
374
  generateVoucherId,
378
- getPaymentAmount,
379
375
  parseSignature
380
376
  });
package/dist/index.mjs CHANGED
@@ -4,13 +4,11 @@ import {
4
4
  http,
5
5
  recoverTypedDataAddress
6
6
  } from "viem";
7
- import { getPaymentAmount } from "@perkos/types-x402";
8
7
  import {
9
8
  getChainById,
10
9
  getChainIdFromNetwork,
11
10
  getRpcUrl
12
11
  } from "@perkos/util-chains";
13
- import { getPaymentAmount as getPaymentAmount2 } from "@perkos/types-x402";
14
12
  var VOUCHER_TYPE_DEF = [
15
13
  { name: "id", type: "bytes32" },
16
14
  { name: "buyer", type: "address" },
@@ -186,7 +184,7 @@ var DeferredSchemeVerifier = class {
186
184
  return false;
187
185
  }
188
186
  const valueAggregate = BigInt(voucher.valueAggregate);
189
- const maxAmount = BigInt(getPaymentAmount(requirements));
187
+ const maxAmount = BigInt(requirements.maxAmountRequired || "0");
190
188
  if (valueAggregate > maxAmount) {
191
189
  return false;
192
190
  }
@@ -303,7 +301,7 @@ function createEIP712Domain(chainId, escrowAddress, domainName, domainVersion) {
303
301
  }
304
302
  function generateVoucherId() {
305
303
  const bytes = new Uint8Array(32);
306
- crypto.getRandomValues(bytes);
304
+ globalThis.crypto.getRandomValues(bytes);
307
305
  return `0x${Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("")}`;
308
306
  }
309
307
  function createVoucherMessage(id, buyer, seller, valueAggregate, asset, timestamp, nonce, escrow, chainId) {
@@ -345,6 +343,5 @@ export {
345
343
  createVoucherMessage,
346
344
  createVoucherTuple,
347
345
  generateVoucherId,
348
- getPaymentAmount2 as getPaymentAmount,
349
346
  parseSignature
350
347
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@perkos/scheme-deferred",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "EIP-712 Voucher-based deferred payment verification utilities for x402 deferred scheme",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -13,7 +13,8 @@
13
13
  }
14
14
  },
15
15
  "files": [
16
- "dist"
16
+ "dist",
17
+ "README.md"
17
18
  ],
18
19
  "scripts": {
19
20
  "build": "tsup src/index.ts --format cjs,esm --dts",
@@ -37,7 +38,7 @@
37
38
  },
38
39
  "dependencies": {
39
40
  "@perkos/types-x402": "^1.1.0",
40
- "@perkos/util-chains": "^1.0.0",
41
+ "@perkos/util-chains": "^1.1.0",
41
42
  "viem": "^2.28.2"
42
43
  },
43
44
  "devDependencies": {