@wtflabs/x402-detector 0.0.1-beta.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,332 @@
1
+ # @wtflabs/x402-detector
2
+
3
+ Token payment capability detection SDK for the x402 protocol. Automatically detects which payment authorization methods (EIP-2612 Permit, EIP-3009, Permit2) are supported by ERC-20 tokens.
4
+
5
+ ## Features
6
+
7
+ ✅ **Comprehensive Detection**
8
+ - EIP-2612 Permit detection
9
+ - EIP-3009 (transferWithAuthorization) detection
10
+ - Uniswap Permit2 support detection
11
+ - Token name and version extraction for EIP-712 signing
12
+
13
+ ✅ **Proxy Contract Support**
14
+ - EIP-1967 transparent proxy detection
15
+ - EIP-1822 UUPS proxy detection
16
+ - Automatic implementation contract analysis
17
+
18
+ ✅ **Performance Optimized**
19
+ - Built-in caching mechanism (永久缓存)
20
+ - Parallel detection for multiple tokens
21
+ - First call: 2-5s, cached calls: <1ms
22
+
23
+ ✅ **Simple & Clean API**
24
+ - One class, minimal methods
25
+ - TypeScript-first with full type safety
26
+ - Zero external dependencies (except viem)
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ npm install @wtflabs/x402-detector viem
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ### Basic Usage (Recommended)
37
+
38
+ ```typescript
39
+ import { createPublicClient, http } from "viem";
40
+ import { bscTestnet } from "viem/chains";
41
+ import { TokenDetector } from "@wtflabs/x402-detector";
42
+
43
+ // Create viem client
44
+ const client = createPublicClient({
45
+ chain: bscTestnet,
46
+ transport: http(),
47
+ });
48
+
49
+ // Create detector
50
+ const detector = new TokenDetector(client);
51
+
52
+ // Detect token (first call: detects from blockchain)
53
+ const result = await detector.detect("0x25d066c4C68C8A6332DfDB4230263608305Ca991");
54
+ console.log(result);
55
+ // {
56
+ // address: "0x25d066c4c68c8a6332dfdb4230263608305ca991",
57
+ // supportedMethods: ["permit", "permit2", "permit2-witness"],
58
+ // details: { hasEIP3009: false, hasPermit: true, hasPermit2Approval: true },
59
+ // name: "USD Coin",
60
+ // version: "1"
61
+ // }
62
+
63
+ // Second call: returns from cache (<1ms)
64
+ const result2 = await detector.detect("0x25d066c4C68C8A6332DfDB4230263608305Ca991");
65
+
66
+ // Get recommended payment method
67
+ const method = await detector.getRecommendedMethod(tokenAddress);
68
+ console.log(method); // "permit"
69
+ ```
70
+
71
+ ### Batch Detection (Cache Warming)
72
+
73
+ ```typescript
74
+ const detector = new TokenDetector(client);
75
+
76
+ // Pre-warm cache for multiple tokens (parallel)
77
+ const tokens = [
78
+ "0x25d066c4C68C8A6332DfDB4230263608305Ca991", // USDC
79
+ "0xcea4eaef42afd4d6e12660b59018e90fa3ab28f4", // DAI
80
+ ];
81
+
82
+ const results = await detector.initialize(tokens);
83
+ // 🔥 Warming up cache for 2 tokens...
84
+ // ✅ Successfully detected 2/2 tokens
85
+
86
+ // Subsequent calls are instant (<1ms from cache)
87
+ for (const token of tokens) {
88
+ const info = await detector.detect(token);
89
+ console.log(info.name, info.supportedMethods);
90
+ }
91
+ ```
92
+
93
+ ### Server Integration Example
94
+
95
+ ```typescript
96
+ import { TokenDetector } from "@wtflabs/x402-detector";
97
+
98
+ class PaymentServer {
99
+ private detector: TokenDetector;
100
+
101
+ constructor(client) {
102
+ this.detector = new TokenDetector(client);
103
+ }
104
+
105
+ async initialize() {
106
+ // Pre-warm cache on startup
107
+ await this.detector.initialize([
108
+ "0x25d066c4C68C8A6332DfDB4230263608305Ca991", // USDC
109
+ "0xcea4eaef42afd4d6e12660b59018e90fa3ab28f4", // DAI
110
+ ]);
111
+ }
112
+
113
+ async createPaymentRequirements(tokenAddress: string, amount: string) {
114
+ // Fast lookup from cache
115
+ const result = await this.detector.detect(tokenAddress);
116
+ const method = await this.detector.getRecommendedMethod(tokenAddress);
117
+
118
+ return {
119
+ scheme: "exact",
120
+ network: "bsc-testnet",
121
+ maxAmountRequired: amount,
122
+ asset: tokenAddress,
123
+ paymentType: method,
124
+ extra: {
125
+ name: result.name,
126
+ version: result.version,
127
+ },
128
+ };
129
+ }
130
+ }
131
+ ```
132
+
133
+ ## API Reference
134
+
135
+ ### TokenDetector Class
136
+
137
+ #### `constructor(client: PublicClient)`
138
+
139
+ 创建检测器实例。
140
+
141
+ **Parameters:**
142
+ - `client: PublicClient` - viem PublicClient
143
+
144
+ **Example:**
145
+ ```typescript
146
+ import { createPublicClient, http } from "viem";
147
+ import { bscTestnet } from "viem/chains";
148
+
149
+ const client = createPublicClient({
150
+ chain: bscTestnet,
151
+ transport: http(),
152
+ });
153
+
154
+ const detector = new TokenDetector(client);
155
+ ```
156
+
157
+ #### `detect(tokenAddress: string): Promise<TokenDetectionResult>`
158
+
159
+ 完整检测(支付能力 + Token 信息)。优先从缓存读取,缓存未命中时执行检测并缓存结果。
160
+
161
+ **Returns:**
162
+ ```typescript
163
+ interface TokenDetectionResult {
164
+ address: string;
165
+ supportedMethods: PaymentMethod[];
166
+ details: {
167
+ hasEIP3009: boolean;
168
+ hasPermit: boolean;
169
+ hasPermit2Approval: boolean;
170
+ };
171
+ name: string;
172
+ version: string;
173
+ }
174
+ ```
175
+
176
+ #### `getRecommendedMethod(tokenAddress: string): Promise<"eip3009" | "permit" | "permit2" | null>`
177
+
178
+ 获取推荐的支付方式。优先级:eip3009 > permit > permit2。
179
+
180
+ **Example:**
181
+ ```typescript
182
+ const method = await detector.getRecommendedMethod(tokenAddress);
183
+ console.log(method); // "permit"
184
+ ```
185
+
186
+ #### `initialize(tokenAddresses: string[]): Promise<TokenDetectionResult[]>`
187
+
188
+ 批量检测多个 Token 并缓存结果(并行执行)。
189
+
190
+ **Example:**
191
+ ```typescript
192
+ const results = await detector.initialize([token1, token2, token3]);
193
+ ```
194
+
195
+ #### `clearCache(tokenAddress?: string): Promise<void>`
196
+
197
+ 清除缓存。不提供参数时清除所有缓存。
198
+
199
+ **Example:**
200
+ ```typescript
201
+ // Clear specific token
202
+ await detector.clearCache(tokenAddress);
203
+
204
+ // Clear all cache
205
+ await detector.clearCache();
206
+ ```
207
+
208
+ #### `getCacheStats(): { size: number; keys: string[] }`
209
+
210
+ 获取缓存统计信息。
211
+
212
+ **Example:**
213
+ ```typescript
214
+ const stats = detector.getCacheStats();
215
+ console.log(stats.size); // 3
216
+ console.log(stats.keys); // ["56:0x...", "56:0x...", ...]
217
+ ```
218
+
219
+ ### Standalone Functions
220
+
221
+ 如果只需要一次性检测(不需要缓存),可以使用独立函数:
222
+
223
+ ```typescript
224
+ import {
225
+ detectTokenPaymentMethods,
226
+ getRecommendedPaymentMethod,
227
+ getTokenInfo,
228
+ } from "@wtflabs/x402-detector";
229
+
230
+ // 检测支付能力
231
+ const capabilities = await detectTokenPaymentMethods(tokenAddress, client);
232
+
233
+ // 获取推荐方法
234
+ const method = await getRecommendedPaymentMethod(tokenAddress, client);
235
+
236
+ // 获取 Token 信息
237
+ const info = await getTokenInfo(tokenAddress, client);
238
+ ```
239
+
240
+ ## Performance
241
+
242
+ | Operation | First Call | Cached Call |
243
+ |-----------|-----------|-------------|
244
+ | `detect()` | 2-5s | <1ms |
245
+ | `getRecommendedMethod()` | 2-5s | <1ms |
246
+ | `initialize(10 tokens)` | ~5s | N/A |
247
+
248
+ **💡 Tips:**
249
+ - 缓存永久有效,除非手动清除
250
+ - 建议在服务启动时调用 `initialize()` 预热缓存
251
+ - 使用 `TokenDetector` 类以获得最佳性能
252
+
253
+ ## Supported Features
254
+
255
+ ### Payment Methods
256
+ - ✅ **EIP-3009** - transferWithAuthorization (USDC native)
257
+ - ✅ **EIP-2612** - Permit (standard ERC-20)
258
+ - ✅ **Permit2** - Uniswap universal approval
259
+
260
+ ### Proxy Contracts
261
+ - ✅ EIP-1967 Transparent Proxy
262
+ - ✅ EIP-1822 UUPS Proxy
263
+ - ✅ Custom implementation() function
264
+
265
+ ### Token Info
266
+ - ✅ Token name extraction
267
+ - ✅ Token version extraction (EIP-5267 & fallback)
268
+ - ✅ Proxy-aware reading
269
+
270
+ ## Preset Tokens
271
+
272
+ 预设配置可避免重复检测已知 Token:
273
+
274
+ ```typescript
275
+ import { PRESET_TOKEN_CAPABILITIES } from "@wtflabs/x402-detector";
276
+
277
+ // Example preset
278
+ PRESET_TOKEN_CAPABILITIES["0x8d0d000ee44948fc98c9b98a4fa4921476f08b0d"] = {
279
+ supportedMethods: ["permit"],
280
+ supportedNetworks: [56], // BSC
281
+ description: "World Liberty Financial USD",
282
+ };
283
+ ```
284
+
285
+ ## Error Handling
286
+
287
+ ```typescript
288
+ try {
289
+ const result = await detector.detect(tokenAddress);
290
+ console.log(result);
291
+ } catch (error) {
292
+ console.error("Detection failed:", error.message);
293
+ }
294
+ ```
295
+
296
+ ## TypeScript Support
297
+
298
+ ```typescript
299
+ import type {
300
+ PaymentMethod,
301
+ TokenInfo,
302
+ TokenPaymentCapabilities,
303
+ TokenDetectionResult,
304
+ PresetTokenConfig,
305
+ } from "@wtflabs/x402-detector";
306
+ ```
307
+
308
+ ## Cache Management
309
+
310
+ 缓存基于 `chainId:address` 键存储,永久有效直到手动清除:
311
+
312
+ ```typescript
313
+ // Get cache stats
314
+ const stats = detector.getCacheStats();
315
+ console.log(`Cached ${stats.size} tokens`);
316
+
317
+ // Clear specific token
318
+ await detector.clearCache("0x...");
319
+
320
+ // Clear all
321
+ await detector.clearCache();
322
+ ```
323
+
324
+ ## License
325
+
326
+ Apache-2.0
327
+
328
+ ## Related Packages
329
+
330
+ - `@wtflabs/x402` - Core x402 protocol implementation
331
+ - `@wtflabs/x402-server` - Server SDK (uses this detector)
332
+ - `@wtflabs/x402-client` - Client SDK
@@ -0,0 +1,209 @@
1
+ import { PublicClient, Address } from 'viem';
2
+
3
+ /**
4
+ * 支持的支付方式
5
+ */
6
+ type PaymentMethod = "eip3009" | "permit" | "permit2" | "permit2-witness";
7
+ /**
8
+ * Token 信息
9
+ */
10
+ interface TokenInfo {
11
+ name: string;
12
+ version: string;
13
+ }
14
+ /**
15
+ * Token 支付能力检测结果
16
+ */
17
+ interface TokenPaymentCapabilities {
18
+ /** Token 地址 */
19
+ address: string;
20
+ /** 支持的支付方式列表 */
21
+ supportedMethods: PaymentMethod[];
22
+ /** 详细检测结果 */
23
+ details: {
24
+ /** 是否支持 EIP-3009 (transferWithAuthorization) */
25
+ hasEIP3009: boolean;
26
+ /** 是否支持 EIP-2612 (permit) */
27
+ hasPermit: boolean;
28
+ /** 是否支持 Permit2 (通用授权) */
29
+ hasPermit2Approval: boolean;
30
+ };
31
+ }
32
+ /**
33
+ * 完整的 Token 检测结果(包含 name 和 version)
34
+ */
35
+ interface TokenDetectionResult extends TokenPaymentCapabilities {
36
+ /** Token 名称 */
37
+ name: string;
38
+ /** Token version(用于 EIP-712 签名) */
39
+ version: string;
40
+ }
41
+ /**
42
+ * 预设 Token 配置
43
+ */
44
+ interface PresetTokenConfig {
45
+ /** 支持的支付方式 */
46
+ supportedMethods: PaymentMethod[];
47
+ /** 支持的网络 ID 列表 */
48
+ supportedNetworks: number[];
49
+ /** Token 描述(可选) */
50
+ description?: string;
51
+ }
52
+ /**
53
+ * Logger 接口
54
+ */
55
+ interface Logger {
56
+ log: (message: string) => void;
57
+ error: (message: string, error?: unknown) => void;
58
+ }
59
+ /**
60
+ * TokenDetector 配置选项
61
+ */
62
+ interface TokenDetectorOptions {
63
+ /** 自定义 logger,默认使用 console */
64
+ logger?: Logger | null;
65
+ }
66
+
67
+ /**
68
+ * EIP-3009 方法签名
69
+ * - transferWithAuthorization(address,address,uint256,uint256,uint256,bytes32,uint8,bytes32,bytes32)
70
+ *
71
+ * 支持多个方法签名变体,以兼容不同的实现:
72
+ * - 0xe3ee160e: 标准 EIP-3009 实现
73
+ * - 0xcf092995: 某些代币的替代实现
74
+ */
75
+ declare const EIP3009_SIGNATURES: readonly ["0xe3ee160e", "0xcf092995"];
76
+ /**
77
+ * EIP-2612 Permit 方法签名
78
+ * - permit(address,address,uint256,uint256,uint8,bytes32,bytes32)
79
+ */
80
+ declare const EIP2612_PERMIT: "0xd505accf";
81
+ /**
82
+ * Uniswap Permit2 合约地址(所有链相同)
83
+ */
84
+ declare const PERMIT2_ADDRESS: "0x000000000022D473030F116dDEE9F6B43aC78BA3";
85
+ /**
86
+ * EIP-1967 标准实现槽位
87
+ * keccak256("eip1967.proxy.implementation") - 1
88
+ */
89
+ declare const EIP1967_IMPLEMENTATION_SLOT: "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
90
+ /**
91
+ * EIP-1822 UUPS 实现槽位
92
+ * keccak256("PROXIABLE")
93
+ */
94
+ declare const EIP1822_IMPLEMENTATION_SLOT: "0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3";
95
+ /**
96
+ * 预设 Token 配置
97
+ * 用于已知的特殊 Token,避免重复检测
98
+ */
99
+ declare const PRESET_TOKEN_CAPABILITIES: Record<string, PresetTokenConfig>;
100
+
101
+ /**
102
+ * 检测 Token 支持的支付方式
103
+ *
104
+ * @param tokenAddress - Token 地址
105
+ * @param client - viem PublicClient
106
+ * @param logger - 可选的 logger
107
+ * @returns 检测结果
108
+ */
109
+ declare function detectTokenPaymentMethods(tokenAddress: string, client: PublicClient, logger?: Logger | null): Promise<TokenPaymentCapabilities>;
110
+ /**
111
+ * 获取推荐的支付方式(仅返回 schema 支持的类型)
112
+ * 按优先级排序:eip3009 > permit > permit2
113
+ * 注意:permit2-witness 会被映射为 permit2,因为它们在 schema 中是同一种支付类型
114
+ *
115
+ * @param tokenAddress - Token 地址
116
+ * @param client - viem PublicClient
117
+ * @param logger - 可选的 logger
118
+ * @returns 推荐的支付方式
119
+ */
120
+ declare function getRecommendedPaymentMethod(tokenAddress: string, client: PublicClient, logger?: Logger | null): Promise<"eip3009" | "permit2" | "permit" | null>;
121
+ /**
122
+ * 获取 Token 的 name 和 version 信息(用于 EIP-712 签名)
123
+ * 支持代理合约(会自动从代理合约读取,因为代理合约会 delegatecall 到实现合约)
124
+ *
125
+ * @param tokenAddress - Token 地址
126
+ * @param client - viem PublicClient
127
+ * @param logger - 可选的 logger
128
+ * @returns Token 的 name 和 version
129
+ */
130
+ declare function getTokenInfo(tokenAddress: string, client: PublicClient, logger?: Logger | null): Promise<TokenInfo>;
131
+
132
+ /**
133
+ * 检测合约是否是代理合约,并获取实现合约地址
134
+ *
135
+ * @param client - viem PublicClient
136
+ * @param proxyAddress - 代理合约地址
137
+ * @param logger - 可选的 logger
138
+ * @returns 实现合约地址或 null
139
+ */
140
+ declare function getImplementationAddress(client: PublicClient, proxyAddress: Address, logger?: Logger | null): Promise<Address | null>;
141
+
142
+ /**
143
+ * Token 检测器 - 带缓存功能的 SDK
144
+ *
145
+ * 主要用于 x402-server,也可以独立使用
146
+ */
147
+ declare class TokenDetector {
148
+ /** 缓存存储 (chainId:address -> TokenDetectionResult) */
149
+ private cache;
150
+ /** viem PublicClient */
151
+ private client;
152
+ /** Logger 实例 */
153
+ private logger;
154
+ /**
155
+ * 构造函数
156
+ *
157
+ * @param client - viem PublicClient
158
+ * @param options - 可选配置
159
+ */
160
+ constructor(client: PublicClient, options?: TokenDetectorOptions);
161
+ /**
162
+ * 完整检测(同时获取支付能力和 Token 信息)
163
+ * 优先从缓存读取,缓存未命中时执行检测并缓存结果
164
+ *
165
+ * @param tokenAddress - Token 地址
166
+ * @returns 完整的检测结果
167
+ */
168
+ detect(tokenAddress: string): Promise<TokenDetectionResult>;
169
+ /**
170
+ * 获取推荐的支付方式
171
+ * 优先级:eip3009 > permit > permit2
172
+ *
173
+ * @param tokenAddress - Token 地址
174
+ * @returns 推荐的支付方式
175
+ */
176
+ getRecommendedMethod(tokenAddress: string): Promise<"eip3009" | "permit" | "permit2" | null>;
177
+ /**
178
+ * 批量初始化(预热缓存)
179
+ * 并行检测多个 Token 并缓存结果
180
+ *
181
+ * @param tokenAddresses - Token 地址列表
182
+ * @returns 检测结果数组
183
+ */
184
+ initialize(tokenAddresses: string[]): Promise<TokenDetectionResult[]>;
185
+ /**
186
+ * 清除缓存
187
+ *
188
+ * @param tokenAddress - 可选,指定要清除的 Token 地址
189
+ */
190
+ clearCache(tokenAddress?: string): Promise<void>;
191
+ /**
192
+ * 获取缓存统计
193
+ *
194
+ * @returns 缓存统计信息
195
+ */
196
+ getCacheStats(): {
197
+ size: number;
198
+ keys: string[];
199
+ };
200
+ /**
201
+ * 生成缓存键
202
+ *
203
+ * @param tokenAddress - Token 地址
204
+ * @returns 缓存键
205
+ */
206
+ private getCacheKey;
207
+ }
208
+
209
+ export { EIP1822_IMPLEMENTATION_SLOT, EIP1967_IMPLEMENTATION_SLOT, EIP2612_PERMIT, EIP3009_SIGNATURES, type Logger, PERMIT2_ADDRESS, PRESET_TOKEN_CAPABILITIES, type PaymentMethod, type PresetTokenConfig, type TokenDetectionResult, TokenDetector, type TokenDetectorOptions, type TokenInfo, type TokenPaymentCapabilities, detectTokenPaymentMethods, getImplementationAddress, getRecommendedPaymentMethod, getTokenInfo };