@t402/wdk 2.0.0

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,511 @@
1
+ # @t402/wdk
2
+
3
+ > T402 integration with Tether Wallet Development Kit (WDK)
4
+
5
+ This package provides seamless integration between the T402 payment protocol and [Tether's WDK](https://wallet.tether.io/), enabling self-custodial USDT0 payments with EIP-3009 gasless transfers.
6
+
7
+ ## Features
8
+
9
+ - **Multi-chain Wallets** - Manage wallets across Ethereum, Arbitrum, Base, and more
10
+ - **T402-Compatible Signers** - Ready-to-use signers for T402 HTTP payments
11
+ - **Balance Aggregation** - View balances across all configured chains
12
+ - **Cross-chain Bridging** - Bridge USDT0 between chains via LayerZero OFT
13
+ - **Balance Caching** - Reduce RPC calls with configurable TTL caching
14
+ - **Comprehensive Error Handling** - Typed errors with retry logic
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install @t402/wdk
20
+ # or
21
+ pnpm add @t402/wdk
22
+ ```
23
+
24
+ You'll also need Tether WDK packages:
25
+
26
+ ```bash
27
+ npm install @tetherto/wdk @tetherto/wdk-wallet-evm
28
+ # Optional: for bridging support
29
+ npm install @tetherto/wdk-protocol-bridge-usdt0-evm
30
+ ```
31
+
32
+ ## Quick Start
33
+
34
+ ### 1. Register WDK Modules
35
+
36
+ Before using T402WDK, register the Tether WDK modules once at app startup:
37
+
38
+ ```typescript
39
+ import WDK from '@tetherto/wdk';
40
+ import WalletManagerEvm from '@tetherto/wdk-wallet-evm';
41
+ import BridgeUsdt0Evm from '@tetherto/wdk-protocol-bridge-usdt0-evm'; // Optional
42
+ import { T402WDK } from '@t402/wdk';
43
+
44
+ // Register WDK modules (once at app startup)
45
+ T402WDK.registerWDK(WDK, WalletManagerEvm, BridgeUsdt0Evm);
46
+ ```
47
+
48
+ ### 2. Create a Wallet
49
+
50
+ ```typescript
51
+ // Generate a new seed phrase
52
+ const seedPhrase = T402WDK.generateSeedPhrase();
53
+
54
+ // Or use an existing seed phrase
55
+ const seedPhrase = 'your twelve word seed phrase here ...';
56
+
57
+ // Create wallet with chain configurations
58
+ const wallet = new T402WDK(seedPhrase, {
59
+ arbitrum: 'https://arb1.arbitrum.io/rpc',
60
+ ethereum: 'https://eth.llamarpc.com',
61
+ base: 'https://mainnet.base.org',
62
+ });
63
+ ```
64
+
65
+ ### 3. Get a Signer for T402 Payments
66
+
67
+ ```typescript
68
+ import { createT402HTTPClient } from '@t402/core';
69
+
70
+ // Get signer for Arbitrum
71
+ const signer = await wallet.getSigner('arbitrum');
72
+
73
+ // Use with T402 HTTP client
74
+ const client = createT402HTTPClient({
75
+ signers: [{ scheme: 'exact', signer }],
76
+ });
77
+
78
+ // Make paid requests
79
+ const response = await client.fetch('https://api.example.com/premium');
80
+ ```
81
+
82
+ ## API Reference
83
+
84
+ ### T402WDK Class
85
+
86
+ #### Static Methods
87
+
88
+ ```typescript
89
+ // Register WDK modules (required before creating instances)
90
+ T402WDK.registerWDK(WDK, WalletManagerEvm, BridgeUsdt0Evm?);
91
+
92
+ // Check registration status
93
+ T402WDK.isWDKRegistered(): boolean;
94
+ T402WDK.isWalletManagerRegistered(): boolean;
95
+ T402WDK.isBridgeRegistered(): boolean;
96
+
97
+ // Generate a new BIP-39 seed phrase
98
+ T402WDK.generateSeedPhrase(): string;
99
+ ```
100
+
101
+ #### Constructor
102
+
103
+ ```typescript
104
+ const wallet = new T402WDK(
105
+ seedPhrase: string, // BIP-39 mnemonic (12, 15, 18, 21, or 24 words)
106
+ config: T402WDKConfig, // Chain RPC configurations
107
+ options?: T402WDKOptions // Optional: cache settings
108
+ );
109
+ ```
110
+
111
+ **Config Example:**
112
+
113
+ ```typescript
114
+ const wallet = new T402WDK(seedPhrase, {
115
+ // String shorthand (uses default chain settings)
116
+ arbitrum: 'https://arb1.arbitrum.io/rpc',
117
+
118
+ // Full config object
119
+ ethereum: {
120
+ provider: 'https://eth.llamarpc.com',
121
+ chainId: 1,
122
+ network: 'eip155:1',
123
+ },
124
+ }, {
125
+ // Optional cache configuration
126
+ cache: {
127
+ enabled: true,
128
+ tokenBalanceTTL: 30000, // 30 seconds
129
+ nativeBalanceTTL: 15000, // 15 seconds
130
+ aggregatedBalanceTTL: 60000, // 60 seconds
131
+ },
132
+ });
133
+ ```
134
+
135
+ #### Instance Properties
136
+
137
+ ```typescript
138
+ wallet.isInitialized: boolean; // True if WDK is ready
139
+ wallet.initializationError: Error | null; // Error if initialization failed
140
+ wallet.isCacheEnabled: boolean; // True if balance caching is enabled
141
+ ```
142
+
143
+ #### Chain Management
144
+
145
+ ```typescript
146
+ // Get all configured chain names
147
+ wallet.getConfiguredChains(): string[];
148
+ // Returns: ['arbitrum', 'ethereum', 'base']
149
+
150
+ // Get chain configuration
151
+ wallet.getChainConfig('arbitrum'): NormalizedChainConfig | undefined;
152
+
153
+ // Check if chain is configured
154
+ wallet.isChainConfigured('arbitrum'): boolean;
155
+
156
+ // Get chains that support USDT0
157
+ wallet.getUsdt0Chains(): string[];
158
+
159
+ // Get chains that support bridging
160
+ wallet.getBridgeableChains(): string[];
161
+ ```
162
+
163
+ #### Signers
164
+
165
+ ```typescript
166
+ // Get a T402-compatible signer for a chain
167
+ const signer = await wallet.getSigner('arbitrum', accountIndex?: number);
168
+
169
+ // Get wallet address
170
+ const address = await wallet.getAddress('arbitrum');
171
+
172
+ // Clear signer cache (forces re-initialization)
173
+ wallet.clearSignerCache();
174
+ ```
175
+
176
+ #### Balance Operations
177
+
178
+ ```typescript
179
+ // Get USDT0 balance on a chain
180
+ const usdt0Balance = await wallet.getUsdt0Balance('arbitrum');
181
+
182
+ // Get USDC balance on a chain
183
+ const usdcBalance = await wallet.getUsdcBalance('base');
184
+
185
+ // Get all balances for a chain
186
+ const chainBalance = await wallet.getChainBalances('arbitrum');
187
+ // Returns: { chain, network, native, tokens: [...] }
188
+
189
+ // Get aggregated balances across all chains
190
+ const allBalances = await wallet.getAggregatedBalances();
191
+ // Returns: { totalUsdt0, totalUsdc, chains: [...] }
192
+
193
+ // Find best chain for a payment amount
194
+ const best = await wallet.findBestChainForPayment(1000000n, 'USDT0');
195
+ // Returns: { chain: 'arbitrum', token: 'USDT0', balance: 5000000n } | null
196
+ ```
197
+
198
+ #### Bridging
199
+
200
+ ```typescript
201
+ // Check if bridging is possible
202
+ wallet.canBridge('arbitrum', 'ethereum'): boolean;
203
+
204
+ // Get available destinations from a chain
205
+ wallet.getBridgeDestinations('arbitrum'): string[];
206
+
207
+ // Bridge USDT0 between chains
208
+ const result = await wallet.bridgeUsdt0({
209
+ fromChain: 'ethereum',
210
+ toChain: 'arbitrum',
211
+ amount: 100_000000n, // 100 USDT0 (6 decimals)
212
+ recipient?: '0x...', // Optional, defaults to same wallet
213
+ });
214
+ // Returns: { txHash: '0x...', estimatedTime: 300 }
215
+ ```
216
+
217
+ #### Cache Management
218
+
219
+ ```typescript
220
+ // Get cache configuration
221
+ wallet.getCacheConfig(): BalanceCacheConfig;
222
+
223
+ // Get cache statistics
224
+ wallet.getCacheStats(): BalanceCacheStats;
225
+
226
+ // Invalidate all cached balances
227
+ wallet.invalidateBalanceCache();
228
+
229
+ // Invalidate cache for a specific chain
230
+ wallet.invalidateChainCache('arbitrum');
231
+
232
+ // Invalidate cache for a specific address
233
+ wallet.invalidateAddressCache('0x1234...');
234
+
235
+ // Dispose resources (call when done)
236
+ wallet.dispose();
237
+ ```
238
+
239
+ ### WDKSigner Class
240
+
241
+ The signer returned by `wallet.getSigner()` implements the T402 `ClientEvmSigner` interface:
242
+
243
+ ```typescript
244
+ interface WDKSigner {
245
+ readonly address: Address;
246
+
247
+ // Sign EIP-712 typed data (used by T402 for EIP-3009)
248
+ signTypedData(message: {
249
+ domain: Record<string, unknown>;
250
+ types: Record<string, unknown>;
251
+ primaryType: string;
252
+ message: Record<string, unknown>;
253
+ }): Promise<`0x${string}`>;
254
+
255
+ // Sign a personal message
256
+ signMessage(message: string | Uint8Array): Promise<`0x${string}`>;
257
+
258
+ // Get native token balance
259
+ getBalance(): Promise<bigint>;
260
+
261
+ // Get ERC20 token balance
262
+ getTokenBalance(tokenAddress: Address): Promise<bigint>;
263
+
264
+ // Estimate gas for a transaction
265
+ estimateGas(params: { to: Address; value?: bigint; data?: string }): Promise<bigint>;
266
+
267
+ // Send a transaction
268
+ sendTransaction(params: { to: Address; value?: bigint; data?: string }): Promise<{ hash: `0x${string}` }>;
269
+
270
+ // Utility methods
271
+ getChain(): string;
272
+ getChainId(): number;
273
+ getAccountIndex(): number;
274
+ isInitialized: boolean;
275
+ }
276
+ ```
277
+
278
+ ## Error Handling
279
+
280
+ All errors are typed and include context information:
281
+
282
+ ```typescript
283
+ import {
284
+ WDKError,
285
+ WDKInitializationError,
286
+ ChainError,
287
+ SignerError,
288
+ SigningError,
289
+ BalanceError,
290
+ TransactionError,
291
+ BridgeError,
292
+ RPCError,
293
+ WDKErrorCode,
294
+ isWDKError,
295
+ hasErrorCode,
296
+ wrapError,
297
+ withRetry,
298
+ withTimeout,
299
+ } from '@t402/wdk';
300
+
301
+ try {
302
+ const signer = await wallet.getSigner('polygon');
303
+ } catch (error) {
304
+ if (isWDKError(error)) {
305
+ console.error(`Error code: ${error.code}`);
306
+ console.error(`Message: ${error.message}`);
307
+ console.error(`Context:`, error.context);
308
+
309
+ if (hasErrorCode(error, WDKErrorCode.CHAIN_NOT_CONFIGURED)) {
310
+ console.error('Chain is not configured');
311
+ }
312
+
313
+ if (error.isRetryable()) {
314
+ // Can retry this operation
315
+ }
316
+ }
317
+ }
318
+ ```
319
+
320
+ ### Error Codes
321
+
322
+ | Code Range | Category |
323
+ |------------|----------|
324
+ | 1xxx | Initialization errors |
325
+ | 2xxx | Chain configuration errors |
326
+ | 3xxx | Signer errors |
327
+ | 4xxx | Signing errors |
328
+ | 5xxx | Balance errors |
329
+ | 6xxx | Transaction errors |
330
+ | 7xxx | Bridge errors |
331
+ | 8xxx | RPC errors |
332
+
333
+ ### Retry Utilities
334
+
335
+ ```typescript
336
+ import { withRetry, withTimeout } from '@t402/wdk';
337
+
338
+ // Retry an operation with exponential backoff
339
+ const balance = await withRetry(
340
+ () => signer.getBalance(),
341
+ { maxRetries: 3, baseDelay: 500 }
342
+ );
343
+
344
+ // Add timeout to a promise
345
+ const result = await withTimeout(
346
+ someAsyncOperation(),
347
+ 30000, // 30 second timeout
348
+ 'Operation description'
349
+ );
350
+ ```
351
+
352
+ ## Supported Chains
353
+
354
+ | Chain | Chain ID | USDT0 | Bridging |
355
+ |-------|----------|-------|----------|
356
+ | Ethereum | 1 | ✅ | ✅ |
357
+ | Arbitrum | 42161 | ✅ | ✅ |
358
+ | Base | 8453 | ✅ | ✅ |
359
+ | Ink | 57073 | ✅ | ✅ |
360
+ | Berachain | 80094 | ✅ | ✅ |
361
+ | Unichain | 130 | ✅ | ✅ |
362
+
363
+ ## Examples
364
+
365
+ ### Complete Payment Flow
366
+
367
+ ```typescript
368
+ import WDK from '@tetherto/wdk';
369
+ import WalletManagerEvm from '@tetherto/wdk-wallet-evm';
370
+ import { T402WDK } from '@t402/wdk';
371
+ import { createT402HTTPClient } from '@t402/core';
372
+
373
+ // 1. Setup (once at app startup)
374
+ T402WDK.registerWDK(WDK, WalletManagerEvm);
375
+
376
+ // 2. Create wallet
377
+ const wallet = new T402WDK(seedPhrase, {
378
+ arbitrum: 'https://arb1.arbitrum.io/rpc',
379
+ });
380
+
381
+ // 3. Check balance before payment
382
+ const balance = await wallet.getUsdt0Balance('arbitrum');
383
+ console.log(`USDT0 Balance: ${balance / 1000000n} USDT0`);
384
+
385
+ // 4. Get signer and create client
386
+ const signer = await wallet.getSigner('arbitrum');
387
+ const client = createT402HTTPClient({
388
+ signers: [{ scheme: 'exact', signer }],
389
+ });
390
+
391
+ // 5. Make paid request
392
+ const response = await client.fetch('https://api.example.com/premium');
393
+
394
+ // 6. Invalidate cache after payment
395
+ wallet.invalidateChainCache('arbitrum');
396
+ ```
397
+
398
+ ### Multi-Chain Balance Check
399
+
400
+ ```typescript
401
+ const wallet = new T402WDK(seedPhrase, {
402
+ arbitrum: 'https://arb1.arbitrum.io/rpc',
403
+ ethereum: 'https://eth.llamarpc.com',
404
+ base: 'https://mainnet.base.org',
405
+ });
406
+
407
+ // Get all balances
408
+ const balances = await wallet.getAggregatedBalances();
409
+
410
+ console.log(`Total USDT0: ${balances.totalUsdt0 / 1000000n}`);
411
+ console.log(`Total USDC: ${balances.totalUsdc / 1000000n}`);
412
+
413
+ for (const chain of balances.chains) {
414
+ console.log(`\n${chain.chain}:`);
415
+ console.log(` Native: ${chain.native}`);
416
+ for (const token of chain.tokens) {
417
+ console.log(` ${token.symbol}: ${token.formatted}`);
418
+ }
419
+ }
420
+ ```
421
+
422
+ ### Auto-Select Best Chain
423
+
424
+ ```typescript
425
+ const amount = 50_000000n; // 50 USDT0
426
+
427
+ // Find the best chain with sufficient balance
428
+ const best = await wallet.findBestChainForPayment(amount);
429
+
430
+ if (best) {
431
+ console.log(`Use ${best.token} on ${best.chain}`);
432
+ const signer = await wallet.getSigner(best.chain);
433
+ // Use signer for payment...
434
+ } else {
435
+ console.log('Insufficient balance on all chains');
436
+ }
437
+ ```
438
+
439
+ ### Cross-Chain Bridge
440
+
441
+ ```typescript
442
+ import BridgeUsdt0Evm from '@tetherto/wdk-protocol-bridge-usdt0-evm';
443
+
444
+ // Register with bridge support
445
+ T402WDK.registerWDK(WDK, WalletManagerEvm, BridgeUsdt0Evm);
446
+
447
+ const wallet = new T402WDK(seedPhrase, {
448
+ ethereum: 'https://eth.llamarpc.com',
449
+ arbitrum: 'https://arb1.arbitrum.io/rpc',
450
+ });
451
+
452
+ // Check if bridging is possible
453
+ if (wallet.canBridge('ethereum', 'arbitrum')) {
454
+ // Bridge 100 USDT0 from Ethereum to Arbitrum
455
+ const result = await wallet.bridgeUsdt0({
456
+ fromChain: 'ethereum',
457
+ toChain: 'arbitrum',
458
+ amount: 100_000000n,
459
+ });
460
+
461
+ console.log(`Bridge tx: ${result.txHash}`);
462
+ console.log(`Estimated time: ${result.estimatedTime}s`);
463
+ }
464
+ ```
465
+
466
+ ## Testing
467
+
468
+ For testing without actual WDK, use the MockWDKSigner:
469
+
470
+ ```typescript
471
+ import { MockWDKSigner } from '@t402/wdk';
472
+
473
+ const mockSigner = new MockWDKSigner(
474
+ '0x1234567890123456789012345678901234567890',
475
+ '0xprivatekey...'
476
+ );
477
+
478
+ // Use in tests
479
+ const signature = await mockSigner.signTypedData({...});
480
+ ```
481
+
482
+ ## TypeScript Support
483
+
484
+ Full TypeScript support with exported types:
485
+
486
+ ```typescript
487
+ import type {
488
+ T402WDKConfig,
489
+ T402WDKOptions,
490
+ T402BalanceCacheConfig,
491
+ NormalizedChainConfig,
492
+ TokenBalance,
493
+ ChainBalance,
494
+ AggregatedBalance,
495
+ BridgeParams,
496
+ BridgeResult,
497
+ T402WDKSigner,
498
+ WDKAccount,
499
+ WDKInstance,
500
+ WDKConstructor,
501
+ CacheConfig,
502
+ CacheStats,
503
+ BalanceCacheConfig,
504
+ BalanceCacheStats,
505
+ RetryConfig,
506
+ } from '@t402/wdk';
507
+ ```
508
+
509
+ ## License
510
+
511
+ Apache 2.0