@perkos/scheme-deferred 1.0.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/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -1
- package/dist/index.mjs +4 -1
- package/package.json +4 -3
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 } from '@perkos/types-x402';
|
|
2
|
+
export { Address, DeferredPayload, Hex, PaymentRequirements, VerifyResponse, Voucher, getPaymentAmount } 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 } from '@perkos/types-x402';
|
|
2
|
+
export { Address, DeferredPayload, Hex, PaymentRequirements, VerifyResponse, Voucher, getPaymentAmount } from '@perkos/types-x402';
|
|
3
3
|
import { SupportedNetwork } from '@perkos/util-chains';
|
|
4
4
|
|
|
5
5
|
/**
|
package/dist/index.js
CHANGED
|
@@ -32,11 +32,14 @@ __export(index_exports, {
|
|
|
32
32
|
createVoucherMessage: () => createVoucherMessage,
|
|
33
33
|
createVoucherTuple: () => createVoucherTuple,
|
|
34
34
|
generateVoucherId: () => generateVoucherId,
|
|
35
|
+
getPaymentAmount: () => import_types_x4022.getPaymentAmount,
|
|
35
36
|
parseSignature: () => parseSignature
|
|
36
37
|
});
|
|
37
38
|
module.exports = __toCommonJS(index_exports);
|
|
38
39
|
var import_viem = require("viem");
|
|
40
|
+
var import_types_x402 = require("@perkos/types-x402");
|
|
39
41
|
var import_util_chains = require("@perkos/util-chains");
|
|
42
|
+
var import_types_x4022 = require("@perkos/types-x402");
|
|
40
43
|
var VOUCHER_TYPE_DEF = [
|
|
41
44
|
{ name: "id", type: "bytes32" },
|
|
42
45
|
{ name: "buyer", type: "address" },
|
|
@@ -212,7 +215,7 @@ var DeferredSchemeVerifier = class {
|
|
|
212
215
|
return false;
|
|
213
216
|
}
|
|
214
217
|
const valueAggregate = BigInt(voucher.valueAggregate);
|
|
215
|
-
const maxAmount = BigInt(
|
|
218
|
+
const maxAmount = BigInt((0, import_types_x402.getPaymentAmount)(requirements));
|
|
216
219
|
if (valueAggregate > maxAmount) {
|
|
217
220
|
return false;
|
|
218
221
|
}
|
|
@@ -372,5 +375,6 @@ function createVoucherTuple(voucher) {
|
|
|
372
375
|
createVoucherMessage,
|
|
373
376
|
createVoucherTuple,
|
|
374
377
|
generateVoucherId,
|
|
378
|
+
getPaymentAmount,
|
|
375
379
|
parseSignature
|
|
376
380
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -4,11 +4,13 @@ import {
|
|
|
4
4
|
http,
|
|
5
5
|
recoverTypedDataAddress
|
|
6
6
|
} from "viem";
|
|
7
|
+
import { getPaymentAmount } from "@perkos/types-x402";
|
|
7
8
|
import {
|
|
8
9
|
getChainById,
|
|
9
10
|
getChainIdFromNetwork,
|
|
10
11
|
getRpcUrl
|
|
11
12
|
} from "@perkos/util-chains";
|
|
13
|
+
import { getPaymentAmount as getPaymentAmount2 } from "@perkos/types-x402";
|
|
12
14
|
var VOUCHER_TYPE_DEF = [
|
|
13
15
|
{ name: "id", type: "bytes32" },
|
|
14
16
|
{ name: "buyer", type: "address" },
|
|
@@ -184,7 +186,7 @@ var DeferredSchemeVerifier = class {
|
|
|
184
186
|
return false;
|
|
185
187
|
}
|
|
186
188
|
const valueAggregate = BigInt(voucher.valueAggregate);
|
|
187
|
-
const maxAmount = BigInt(requirements
|
|
189
|
+
const maxAmount = BigInt(getPaymentAmount(requirements));
|
|
188
190
|
if (valueAggregate > maxAmount) {
|
|
189
191
|
return false;
|
|
190
192
|
}
|
|
@@ -343,5 +345,6 @@ export {
|
|
|
343
345
|
createVoucherMessage,
|
|
344
346
|
createVoucherTuple,
|
|
345
347
|
generateVoucherId,
|
|
348
|
+
getPaymentAmount2 as getPaymentAmount,
|
|
346
349
|
parseSignature
|
|
347
350
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@perkos/scheme-deferred",
|
|
3
|
-
"version": "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",
|
|
@@ -36,7 +37,7 @@
|
|
|
36
37
|
"url": "https://github.com/PerkOS-xyz/pkg-scheme-deferred"
|
|
37
38
|
},
|
|
38
39
|
"dependencies": {
|
|
39
|
-
"@perkos/types-x402": "^1.
|
|
40
|
+
"@perkos/types-x402": "^1.1.0",
|
|
40
41
|
"@perkos/util-chains": "^1.0.0",
|
|
41
42
|
"viem": "^2.28.2"
|
|
42
43
|
},
|