@perkos/scheme-deferred 1.1.0 → 1.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.
- package/README.md +357 -0
- package/package.json +3 -2
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@perkos/scheme-deferred",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
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",
|