@t402/streaming-payments 1.0.0-beta.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 +422 -0
- package/dist/channels/index.d.ts +1560 -0
- package/dist/channels/index.js +1135 -0
- package/dist/channels/index.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3482 -0
- package/dist/index.js.map +1 -0
- package/dist/settlement/index.d.ts +867 -0
- package/dist/settlement/index.js +1030 -0
- package/dist/settlement/index.js.map +1 -0
- package/dist/streaming/index.d.ts +1004 -0
- package/dist/streaming/index.js +1321 -0
- package/dist/streaming/index.js.map +1 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
# @t402-internal/streaming-payments
|
|
2
|
+
|
|
3
|
+
Streaming payments with payment channels for T402 Protocol.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This package provides per-second settlement using payment channel technology. It enables continuous value transfer with off-chain state updates and on-chain settlement, similar to Lightning Network concepts adapted for stablecoin HTTP payments.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add @t402-internal/streaming-payments
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import {
|
|
19
|
+
ChannelOpener,
|
|
20
|
+
ChannelStateMachine,
|
|
21
|
+
FlowController,
|
|
22
|
+
CheckpointManager,
|
|
23
|
+
FinalSettlementManager,
|
|
24
|
+
} from '@t402-internal/streaming-payments';
|
|
25
|
+
|
|
26
|
+
// Create a payment channel
|
|
27
|
+
const opener = new ChannelOpener();
|
|
28
|
+
const { channel, stateMachine, fundingRequired } = opener.create({
|
|
29
|
+
chain: 'eip155:8453',
|
|
30
|
+
asset: '0xUSDT',
|
|
31
|
+
payerAddress: '0xPayer',
|
|
32
|
+
payeeAddress: '0xPayee',
|
|
33
|
+
depositAmount: '1000000000', // 1000 USDT
|
|
34
|
+
ratePerSecond: '1000', // 0.001 USDT per second
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Fund the channel
|
|
38
|
+
opener.processFunding(stateMachine, '0xTxHash', '1000000000');
|
|
39
|
+
opener.confirmFunding(stateMachine, { /* confirmation */ });
|
|
40
|
+
|
|
41
|
+
// Start streaming
|
|
42
|
+
const flow = new FlowController(channel.id, channel.ratePerSecond);
|
|
43
|
+
flow.start();
|
|
44
|
+
|
|
45
|
+
// Get current streamed amount
|
|
46
|
+
const currentAmount = flow.getCurrentAmount();
|
|
47
|
+
|
|
48
|
+
// Pause/resume
|
|
49
|
+
flow.pause();
|
|
50
|
+
flow.resume();
|
|
51
|
+
|
|
52
|
+
// Stop and get final amount
|
|
53
|
+
const { finalAmount } = flow.stop();
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Modules
|
|
57
|
+
|
|
58
|
+
### Channels
|
|
59
|
+
|
|
60
|
+
Payment channel lifecycle management with state machine.
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import {
|
|
64
|
+
ChannelOpener,
|
|
65
|
+
ChannelCloser,
|
|
66
|
+
ChannelStateMachine,
|
|
67
|
+
ChannelRecovery,
|
|
68
|
+
isChannelActive,
|
|
69
|
+
isChannelTerminal,
|
|
70
|
+
} from '@t402-internal/streaming-payments/channels';
|
|
71
|
+
|
|
72
|
+
// Open channel
|
|
73
|
+
const opener = new ChannelOpener({
|
|
74
|
+
fundingTimeout: 3600000, // 1 hour
|
|
75
|
+
confirmations: 3,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const result = opener.create({
|
|
79
|
+
chain: 'eip155:8453',
|
|
80
|
+
asset: '0xUSDT',
|
|
81
|
+
payerAddress: '0xPayer',
|
|
82
|
+
payeeAddress: '0xPayee',
|
|
83
|
+
depositAmount: '1000000000',
|
|
84
|
+
ratePerSecond: '1000',
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Process state transitions
|
|
88
|
+
const stateMachine = result.stateMachine;
|
|
89
|
+
stateMachine.process({ type: 'FUND', txHash: '0x...', amount: '1000000000' });
|
|
90
|
+
stateMachine.process({ type: 'CONFIRM_FUNDING' });
|
|
91
|
+
|
|
92
|
+
// Close channel
|
|
93
|
+
const closer = new ChannelCloser({
|
|
94
|
+
challengePeriod: 86400, // 24 hours
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
closer.initiateClose(stateMachine, '0xPayer', '0xSignature');
|
|
98
|
+
closer.finalize(stateMachine);
|
|
99
|
+
|
|
100
|
+
// Recovery
|
|
101
|
+
const recovery = new ChannelRecovery();
|
|
102
|
+
const recoveryResult = recovery.recoverFromCheckpoints(channel, checkpoints);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Channel States
|
|
106
|
+
|
|
107
|
+
| State | Description |
|
|
108
|
+
|-------|-------------|
|
|
109
|
+
| `created` | Channel created, awaiting funding |
|
|
110
|
+
| `funding` | Funding transaction submitted |
|
|
111
|
+
| `open` | Channel active and streaming |
|
|
112
|
+
| `paused` | Temporarily paused |
|
|
113
|
+
| `closing` | Close initiated, in challenge period |
|
|
114
|
+
| `disputing` | Dispute in progress |
|
|
115
|
+
| `closed` | Fully settled |
|
|
116
|
+
| `expired` | Timed out |
|
|
117
|
+
|
|
118
|
+
### Streaming
|
|
119
|
+
|
|
120
|
+
Continuous flow control with metering and billing.
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import {
|
|
124
|
+
FlowController,
|
|
125
|
+
createFixedRateFlow,
|
|
126
|
+
createTieredRateFlow,
|
|
127
|
+
RateController,
|
|
128
|
+
MeteringManager,
|
|
129
|
+
BillingManager,
|
|
130
|
+
} from '@t402-internal/streaming-payments/streaming';
|
|
131
|
+
|
|
132
|
+
// Fixed rate streaming
|
|
133
|
+
const flow = createFixedRateFlow('ch_123', '1000');
|
|
134
|
+
flow.start();
|
|
135
|
+
|
|
136
|
+
// Get current amount
|
|
137
|
+
console.log(flow.getCurrentAmount());
|
|
138
|
+
|
|
139
|
+
// Tiered rate streaming
|
|
140
|
+
const tieredFlow = createTieredRateFlow(
|
|
141
|
+
'ch_123',
|
|
142
|
+
'1000', // Base rate
|
|
143
|
+
[
|
|
144
|
+
{ threshold: '1000000', rate: '800' }, // Discount after 1M
|
|
145
|
+
{ threshold: '5000000', rate: '600' }, // Further discount after 5M
|
|
146
|
+
],
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
// Rate adjustment
|
|
150
|
+
const rateController = new RateController({
|
|
151
|
+
type: 'fixed',
|
|
152
|
+
baseRate: '1000',
|
|
153
|
+
minRate: '500',
|
|
154
|
+
maxRate: '2000',
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
rateController.adjustRate({
|
|
158
|
+
sessionId: 'ss_123',
|
|
159
|
+
newRate: '1200',
|
|
160
|
+
reason: 'Peak demand',
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Metering
|
|
164
|
+
const metering = new MeteringManager('ss_123');
|
|
165
|
+
metering.record(60, '60000', '1000'); // 60 seconds at 1000/s
|
|
166
|
+
|
|
167
|
+
const metrics = metering.getMetrics();
|
|
168
|
+
console.log(metrics.totalAmount);
|
|
169
|
+
console.log(metrics.averageRate);
|
|
170
|
+
|
|
171
|
+
// Billing
|
|
172
|
+
const billing = new BillingManager(
|
|
173
|
+
'ch_123',
|
|
174
|
+
'0xPayer',
|
|
175
|
+
'0xPayee',
|
|
176
|
+
{ period: 'hour', minimumCharge: '100000' },
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
const invoice = billing.generateInvoice(
|
|
180
|
+
metering.getRecords(),
|
|
181
|
+
startTime,
|
|
182
|
+
endTime,
|
|
183
|
+
);
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Streaming Events
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
flow.onEvent(event => {
|
|
190
|
+
switch (event.type) {
|
|
191
|
+
case 'started':
|
|
192
|
+
console.log('Stream started at rate:', event.rate);
|
|
193
|
+
break;
|
|
194
|
+
case 'paused':
|
|
195
|
+
console.log('Stream paused, total:', event.totalStreamed);
|
|
196
|
+
break;
|
|
197
|
+
case 'checkpoint':
|
|
198
|
+
console.log('Checkpoint created:', event.checkpointId);
|
|
199
|
+
break;
|
|
200
|
+
case 'completed':
|
|
201
|
+
console.log('Stream completed:', event.totalAmount);
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Settlement
|
|
208
|
+
|
|
209
|
+
Checkpoint-based settlement with dispute resolution.
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
import {
|
|
213
|
+
CheckpointManager,
|
|
214
|
+
FinalSettlementManager,
|
|
215
|
+
DisputeManager,
|
|
216
|
+
signCheckpoint,
|
|
217
|
+
createCheckpointEvidence,
|
|
218
|
+
} from '@t402-internal/streaming-payments/settlement';
|
|
219
|
+
|
|
220
|
+
// Checkpoint management
|
|
221
|
+
const checkpointMgr = new CheckpointManager(settlementConfig, {
|
|
222
|
+
autoCheckpoint: true,
|
|
223
|
+
intervalSeconds: 3600,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const checkpoint = checkpointMgr.create({
|
|
227
|
+
channelId: 'ch_123',
|
|
228
|
+
sequence: 0,
|
|
229
|
+
payerBalance: '900000000',
|
|
230
|
+
payeeBalance: '100000000',
|
|
231
|
+
totalStreamed: '100000000',
|
|
232
|
+
payerSignature: '0xsig',
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Validate checkpoint chain
|
|
236
|
+
const validation = checkpointMgr.verifyCheckpointChain(checkpoints);
|
|
237
|
+
|
|
238
|
+
// Final settlement
|
|
239
|
+
const settlementMgr = new FinalSettlementManager(
|
|
240
|
+
checkpointMgr,
|
|
241
|
+
settlementConfig,
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
// Initiate settlement
|
|
245
|
+
settlementMgr.initiate({
|
|
246
|
+
channelId: 'ch_123',
|
|
247
|
+
initiator: '0xPayer',
|
|
248
|
+
finalCheckpoint: checkpoint,
|
|
249
|
+
reason: 'unilateral',
|
|
250
|
+
signature: '0xsig',
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// Mutual close (instant, no challenge period)
|
|
254
|
+
settlementMgr.processMutual(
|
|
255
|
+
'ch_123',
|
|
256
|
+
payerSignature,
|
|
257
|
+
payeeSignature,
|
|
258
|
+
finalCheckpoint,
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
// Finalize after challenge period
|
|
262
|
+
const { canFinalize, timeRemaining } = settlementMgr.canFinalize('ch_123');
|
|
263
|
+
if (canFinalize) {
|
|
264
|
+
settlementMgr.finalize('ch_123');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Dispute handling
|
|
268
|
+
const disputeMgr = new DisputeManager(checkpointMgr, settlementConfig);
|
|
269
|
+
|
|
270
|
+
// Raise dispute
|
|
271
|
+
disputeMgr.raise({
|
|
272
|
+
channelId: 'ch_123',
|
|
273
|
+
initiator: '0xPayee',
|
|
274
|
+
respondent: '0xPayer',
|
|
275
|
+
reason: 'stale_state',
|
|
276
|
+
description: 'Payer submitted old checkpoint',
|
|
277
|
+
claimedPayerBalance: '800000000',
|
|
278
|
+
claimedPayeeBalance: '200000000',
|
|
279
|
+
claimedCheckpoint: newerCheckpoint,
|
|
280
|
+
evidence: [
|
|
281
|
+
createCheckpointEvidence(newerCheckpoint, 'Newer signed checkpoint'),
|
|
282
|
+
],
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// Respond to dispute
|
|
286
|
+
disputeMgr.respond({
|
|
287
|
+
disputeId: 'dsp_123',
|
|
288
|
+
responder: '0xPayer',
|
|
289
|
+
evidence: [...],
|
|
290
|
+
signature: '0xsig',
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// Resolve dispute
|
|
294
|
+
disputeMgr.resolve(
|
|
295
|
+
'dsp_123',
|
|
296
|
+
'0xPayee', // Winner
|
|
297
|
+
'800000000', // Final payer balance
|
|
298
|
+
'200000000', // Final payee balance
|
|
299
|
+
'Challenger had newer checkpoint',
|
|
300
|
+
);
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Settlement Flow
|
|
304
|
+
|
|
305
|
+
```
|
|
306
|
+
1. Channel Open
|
|
307
|
+
- Payer deposits funds
|
|
308
|
+
- Channel enters 'open' state
|
|
309
|
+
|
|
310
|
+
2. Streaming
|
|
311
|
+
- Continuous per-second billing
|
|
312
|
+
- Periodic checkpoints
|
|
313
|
+
- Off-chain state updates
|
|
314
|
+
|
|
315
|
+
3. Close Initiation
|
|
316
|
+
- Either party can initiate
|
|
317
|
+
- Submit final checkpoint
|
|
318
|
+
|
|
319
|
+
4. Challenge Period (24h default)
|
|
320
|
+
- Counter-party can dispute
|
|
321
|
+
- Submit newer checkpoint to win
|
|
322
|
+
|
|
323
|
+
5. Finalization
|
|
324
|
+
- After challenge period
|
|
325
|
+
- On-chain settlement
|
|
326
|
+
- Funds distributed
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## API Reference
|
|
330
|
+
|
|
331
|
+
### ChannelOpener
|
|
332
|
+
|
|
333
|
+
- `create(request)` - Create new channel
|
|
334
|
+
- `processFunding(stateMachine, txHash, amount)` - Process funding tx
|
|
335
|
+
- `confirmFunding(stateMachine, confirmation)` - Confirm funding
|
|
336
|
+
|
|
337
|
+
### ChannelStateMachine
|
|
338
|
+
|
|
339
|
+
- `getChannel()` - Get current channel state
|
|
340
|
+
- `getState()` - Get channel state enum
|
|
341
|
+
- `process(event)` - Process state transition
|
|
342
|
+
- `canTransition(state)` - Check if transition allowed
|
|
343
|
+
- `updateBalance(payer, payee)` - Update balances
|
|
344
|
+
- `addCheckpoint(checkpoint)` - Add checkpoint
|
|
345
|
+
- `onStateChange(state, callback)` - Subscribe to changes
|
|
346
|
+
|
|
347
|
+
### FlowController
|
|
348
|
+
|
|
349
|
+
- `start()` - Start streaming
|
|
350
|
+
- `pause()` - Pause streaming
|
|
351
|
+
- `resume()` - Resume streaming
|
|
352
|
+
- `stop()` - Stop and get final amount
|
|
353
|
+
- `cancel(reason)` - Cancel streaming
|
|
354
|
+
- `getCurrentAmount()` - Get current streamed amount
|
|
355
|
+
- `getCurrentRate()` - Get effective rate
|
|
356
|
+
- `createCheckpoint()` - Create manual checkpoint
|
|
357
|
+
- `onEvent(callback)` - Subscribe to events
|
|
358
|
+
|
|
359
|
+
### CheckpointManager
|
|
360
|
+
|
|
361
|
+
- `create(request)` - Create checkpoint
|
|
362
|
+
- `getLatest(channelId)` - Get latest checkpoint
|
|
363
|
+
- `validate(checkpoint)` - Validate checkpoint
|
|
364
|
+
- `verifyCheckpointChain(checkpoints)` - Verify integrity
|
|
365
|
+
- `needsCheckpoint(channelId, balance)` - Check if needed
|
|
366
|
+
|
|
367
|
+
### FinalSettlementManager
|
|
368
|
+
|
|
369
|
+
- `initiate(request)` - Start settlement
|
|
370
|
+
- `processMutual(...)` - Mutual (instant) close
|
|
371
|
+
- `canFinalize(channelId)` - Check if can finalize
|
|
372
|
+
- `finalize(channelId)` - Complete settlement
|
|
373
|
+
|
|
374
|
+
### DisputeManager
|
|
375
|
+
|
|
376
|
+
- `raise(request)` - Raise dispute
|
|
377
|
+
- `respond(response)` - Respond to dispute
|
|
378
|
+
- `resolve(...)` - Resolve dispute
|
|
379
|
+
- `addEvidence(...)` - Add evidence
|
|
380
|
+
- `evaluateEvidence(disputeId)` - Evaluate evidence
|
|
381
|
+
|
|
382
|
+
## Dispute Reasons
|
|
383
|
+
|
|
384
|
+
| Reason | Description |
|
|
385
|
+
|--------|-------------|
|
|
386
|
+
| `invalid_checkpoint` | Checkpoint signature/data invalid |
|
|
387
|
+
| `stale_state` | Challenger has newer valid state |
|
|
388
|
+
| `balance_mismatch` | Balance doesn't match expected |
|
|
389
|
+
| `unauthorized_close` | Unauthorized party initiated close |
|
|
390
|
+
| `fraud` | Fraudulent activity detected |
|
|
391
|
+
|
|
392
|
+
## Configuration
|
|
393
|
+
|
|
394
|
+
### Settlement Config
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
const settlementConfig = {
|
|
398
|
+
challengePeriod: 86400, // 24 hours
|
|
399
|
+
disputeResponsePeriod: 43200, // 12 hours
|
|
400
|
+
disputeResolutionPeriod: 172800, // 48 hours
|
|
401
|
+
minCheckpointInterval: 60, // 60 seconds
|
|
402
|
+
maxCheckpointsStored: 100,
|
|
403
|
+
settlementFee: '0',
|
|
404
|
+
disputeBond: '0',
|
|
405
|
+
};
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
### Billing Config
|
|
409
|
+
|
|
410
|
+
```typescript
|
|
411
|
+
const billingConfig = {
|
|
412
|
+
period: 'realtime', // 'second' | 'minute' | 'hour' | 'day'
|
|
413
|
+
minimumCharge: '0',
|
|
414
|
+
roundingMode: 'floor', // 'ceil' | 'round'
|
|
415
|
+
gracePeriod: 0,
|
|
416
|
+
invoiceInterval: 86400, // Generate invoice daily
|
|
417
|
+
};
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## License
|
|
421
|
+
|
|
422
|
+
Internal use only - T402 Protocol
|