four-flap-meme-sdk 1.4.11 → 1.4.13

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.
@@ -0,0 +1,111 @@
1
+ /**
2
+ * V2/V3 报价工具函数
3
+ *
4
+ * 提供统一的代币报价功能,支持:
5
+ * - V2 Router 报价(直接路径 + 多跳路径)
6
+ * - V3 Quoter 报价(多费率尝试 + 多跳路径)
7
+ * - ERC20 → 原生代币转换报价
8
+ */
9
+ import { JsonRpcProvider } from 'ethers';
10
+ /** V3 常用费率档位 */
11
+ export declare const V3_FEE_TIERS: number[];
12
+ /** 各链的报价配置 */
13
+ export declare const QUOTE_CONFIG: {
14
+ readonly BSC: {
15
+ readonly v2Router: "0x10ED43C718714eb63d5aA57B78B54704E256024E";
16
+ readonly v3Quoter: "0xB048Bbc1Ee6b733FFfCFb9e9CeF7375518e25997";
17
+ readonly wrappedNative: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c";
18
+ readonly stableCoins: readonly ["0x55d398326f99059fF775485246999027B3197955", "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56"];
19
+ };
20
+ readonly MONAD: {
21
+ readonly v2Router: "0xb1bc24c34e88f7d43d5923034e3a14b24daacff9";
22
+ readonly v3Quoter: "";
23
+ readonly wrappedNative: "0x3bd359c1119da7da1d913d1c4d2b7c461115433a";
24
+ readonly stableCoins: readonly [];
25
+ };
26
+ readonly XLAYER: {
27
+ readonly v2Router: "0x881fb2f98c13d521009464e7d1cbf16e1b394e8e";
28
+ readonly v3Quoter: "";
29
+ readonly wrappedNative: "0xe538905cf8410324e03a5a23c1c177a474d59b2b";
30
+ readonly stableCoins: readonly ["0x1E4a5963aBFD975d8c9021ce480b42188849D41d"];
31
+ };
32
+ };
33
+ export type SupportedChain = keyof typeof QUOTE_CONFIG;
34
+ export interface QuoteParams {
35
+ provider: JsonRpcProvider;
36
+ tokenIn: string;
37
+ tokenOut: string;
38
+ amountIn: bigint;
39
+ chain: string;
40
+ version?: 'v2' | 'v3';
41
+ fee?: number;
42
+ }
43
+ export interface QuoteResult {
44
+ amountOut: bigint;
45
+ path?: string[];
46
+ fee?: number;
47
+ }
48
+ /**
49
+ * V2 报价(直接路径 + 多跳路径)
50
+ * ✅ 优化:并行尝试直接路径和多跳路径
51
+ *
52
+ * @param provider - Provider 实例
53
+ * @param tokenIn - 输入代币地址
54
+ * @param tokenOut - 输出代币地址
55
+ * @param amountIn - 输入数量(wei)
56
+ * @param chain - 链名称
57
+ * @returns 报价结果
58
+ */
59
+ export declare function quoteV2(provider: JsonRpcProvider, tokenIn: string, tokenOut: string, amountIn: bigint, chain: string): Promise<QuoteResult>;
60
+ /**
61
+ * V3 报价(多费率尝试 + 多跳路径)
62
+ * ✅ 优化:并行尝试多个费率
63
+ *
64
+ * @param provider - Provider 实例
65
+ * @param tokenIn - 输入代币地址
66
+ * @param tokenOut - 输出代币地址
67
+ * @param amountIn - 输入数量(wei)
68
+ * @param chain - 链名称
69
+ * @param preferredFee - 优先尝试的费率(可选)
70
+ * @returns 报价结果
71
+ */
72
+ export declare function quoteV3(provider: JsonRpcProvider, tokenIn: string, tokenOut: string, amountIn: bigint, chain: string, preferredFee?: number): Promise<QuoteResult>;
73
+ /**
74
+ * 统一报价接口(根据版本自动选择 V2 或 V3)
75
+ *
76
+ * @param params - 报价参数
77
+ * @returns 报价结果
78
+ */
79
+ export declare function quote(params: QuoteParams): Promise<QuoteResult>;
80
+ /**
81
+ * 获取 ERC20 代币 → 原生代币(WBNB/WMON 等)的报价
82
+ *
83
+ * @param provider - Provider 实例
84
+ * @param tokenAddress - ERC20 代币地址
85
+ * @param tokenAmount - 代币数量(wei)
86
+ * @param chain - 链名称
87
+ * @param version - 'v2' | 'v3'
88
+ * @param fee - V3 费率档位(仅 V3 时使用)
89
+ * @returns 等值的原生代币数量(wei),失败返回 0n
90
+ */
91
+ export declare function getTokenToNativeQuote(provider: JsonRpcProvider, tokenAddress: string, tokenAmount: bigint, chain: string, version?: 'v2' | 'v3', fee?: number): Promise<bigint>;
92
+ /**
93
+ * 获取原生代币 → ERC20 代币的报价
94
+ *
95
+ * @param provider - Provider 实例
96
+ * @param tokenAddress - ERC20 代币地址
97
+ * @param nativeAmount - 原生代币数量(wei)
98
+ * @param chain - 链名称
99
+ * @param version - 'v2' | 'v3'
100
+ * @param fee - V3 费率档位(仅 V3 时使用)
101
+ * @returns 等值的 ERC20 代币数量(wei),失败返回 0n
102
+ */
103
+ export declare function getNativeToTokenQuote(provider: JsonRpcProvider, tokenAddress: string, nativeAmount: bigint, chain: string, version?: 'v2' | 'v3', fee?: number): Promise<bigint>;
104
+ /**
105
+ * 获取链的包装原生代币地址
106
+ */
107
+ export declare function getWrappedNativeAddress(chain: string): string;
108
+ /**
109
+ * 获取链的稳定币列表
110
+ */
111
+ export declare function getStableCoins(chain: string): string[];
@@ -0,0 +1,358 @@
1
+ /**
2
+ * V2/V3 报价工具函数
3
+ *
4
+ * 提供统一的代币报价功能,支持:
5
+ * - V2 Router 报价(直接路径 + 多跳路径)
6
+ * - V3 Quoter 报价(多费率尝试 + 多跳路径)
7
+ * - ERC20 → 原生代币转换报价
8
+ */
9
+ import { ethers, Contract } from 'ethers';
10
+ // ============================================================================
11
+ // 常量配置
12
+ // ============================================================================
13
+ /** V2 Router ABI(用于报价) */
14
+ const V2_ROUTER_ABI = [
15
+ 'function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)'
16
+ ];
17
+ /** V3 QuoterV2 ABI(用于报价)- 使用结构体参数 */
18
+ const V3_QUOTER_ABI = [
19
+ {
20
+ "inputs": [{
21
+ "components": [
22
+ { "name": "tokenIn", "type": "address" },
23
+ { "name": "tokenOut", "type": "address" },
24
+ { "name": "amountIn", "type": "uint256" },
25
+ { "name": "fee", "type": "uint24" },
26
+ { "name": "sqrtPriceLimitX96", "type": "uint160" }
27
+ ],
28
+ "name": "params",
29
+ "type": "tuple"
30
+ }],
31
+ "name": "quoteExactInputSingle",
32
+ "outputs": [
33
+ { "name": "amountOut", "type": "uint256" },
34
+ { "name": "sqrtPriceX96After", "type": "uint160" },
35
+ { "name": "initializedTicksCrossed", "type": "uint32" },
36
+ { "name": "gasEstimate", "type": "uint256" }
37
+ ],
38
+ "stateMutability": "nonpayable",
39
+ "type": "function"
40
+ }
41
+ ];
42
+ /** V3 常用费率档位 */
43
+ export const V3_FEE_TIERS = [100, 500, 2500, 10000]; // 0.01%, 0.05%, 0.25%, 1%
44
+ /** 各链的报价配置 */
45
+ export const QUOTE_CONFIG = {
46
+ BSC: {
47
+ v2Router: '0x10ED43C718714eb63d5aA57B78B54704E256024E', // PancakeSwap V2 Router
48
+ v3Quoter: '0xB048Bbc1Ee6b733FFfCFb9e9CeF7375518e25997', // PancakeSwap V3 Quoter
49
+ wrappedNative: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c', // WBNB
50
+ stableCoins: [
51
+ '0x55d398326f99059fF775485246999027B3197955', // USDT
52
+ '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', // USDC
53
+ '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56', // BUSD
54
+ ],
55
+ },
56
+ MONAD: {
57
+ v2Router: '0xb1bc24c34e88f7d43d5923034e3a14b24daacff9', // PancakeSwap V2
58
+ v3Quoter: '', // TODO: 添加 Monad V3 Quoter
59
+ wrappedNative: '0x3bd359c1119da7da1d913d1c4d2b7c461115433a', // WMON
60
+ stableCoins: [],
61
+ },
62
+ XLAYER: {
63
+ v2Router: '0x881fb2f98c13d521009464e7d1cbf16e1b394e8e', // PotatoSwap V2
64
+ v3Quoter: '', // TODO: 添加 XLayer V3 Quoter
65
+ wrappedNative: '0xe538905cf8410324e03a5a23c1c177a474d59b2b', // WOKB
66
+ stableCoins: [
67
+ '0x1E4a5963aBFD975d8c9021ce480b42188849D41d', // USDT
68
+ ],
69
+ },
70
+ };
71
+ // ============================================================================
72
+ // V2 报价
73
+ // ============================================================================
74
+ /**
75
+ * V2 报价(直接路径 + 多跳路径)
76
+ * ✅ 优化:并行尝试直接路径和多跳路径
77
+ *
78
+ * @param provider - Provider 实例
79
+ * @param tokenIn - 输入代币地址
80
+ * @param tokenOut - 输出代币地址
81
+ * @param amountIn - 输入数量(wei)
82
+ * @param chain - 链名称
83
+ * @returns 报价结果
84
+ */
85
+ export async function quoteV2(provider, tokenIn, tokenOut, amountIn, chain) {
86
+ if (amountIn <= 0n) {
87
+ return { amountOut: 0n };
88
+ }
89
+ const chainUpper = chain.toUpperCase();
90
+ const config = QUOTE_CONFIG[chainUpper];
91
+ if (!config) {
92
+ console.warn(`[quoteV2] 不支持的链: ${chain}`);
93
+ return { amountOut: 0n };
94
+ }
95
+ const router = new Contract(config.v2Router, V2_ROUTER_ABI, provider);
96
+ const tokenInLower = tokenIn.toLowerCase();
97
+ const tokenOutLower = tokenOut.toLowerCase();
98
+ // ✅ 构建所有可能的路径
99
+ const paths = [];
100
+ // 直接路径
101
+ paths.push([tokenIn, tokenOut]);
102
+ // 多跳路径(通过稳定币)
103
+ for (const stableCoin of config.stableCoins) {
104
+ const stableLower = stableCoin.toLowerCase();
105
+ if (tokenInLower !== stableLower && tokenOutLower !== stableLower) {
106
+ paths.push([tokenIn, stableCoin, tokenOut]);
107
+ }
108
+ }
109
+ // ✅ 并行尝试所有路径
110
+ console.log(`[quoteV2] 输入: ${amountIn} wei, tokenIn=${tokenIn}, tokenOut=${tokenOut}`);
111
+ const quotePromises = paths.map(async (path) => {
112
+ try {
113
+ const amounts = await router.getAmountsOut(amountIn, path);
114
+ const amountOut = amounts[amounts.length - 1];
115
+ console.log(`[quoteV2] 路径 ${path.map(p => p.slice(0, 10) + '...').join(' → ')}: ${amountOut} wei`);
116
+ return { amountOut, path, success: amountOut > 0n };
117
+ }
118
+ catch (e) {
119
+ console.log(`[quoteV2] 路径失败 ${path.map(p => p.slice(0, 10) + '...').join(' → ')}: ${e.message?.slice(0, 50)}`);
120
+ return { amountOut: 0n, path, success: false };
121
+ }
122
+ });
123
+ const results = await Promise.all(quotePromises);
124
+ // 找到第一个成功的结果(优先直接路径)
125
+ for (const result of results) {
126
+ if (result.success) {
127
+ console.log(`[quoteV2] 路径成功 (${result.path.length === 2 ? '直接' : '多跳'}): ${ethers.formatEther(result.amountOut)}`);
128
+ return { amountOut: result.amountOut, path: result.path };
129
+ }
130
+ }
131
+ console.warn(`[quoteV2] 所有路径均失败`);
132
+ return { amountOut: 0n };
133
+ }
134
+ // ============================================================================
135
+ // V3 报价
136
+ // ============================================================================
137
+ /**
138
+ * V3 报价(多费率尝试 + 多跳路径)
139
+ * ✅ 优化:并行尝试多个费率
140
+ *
141
+ * @param provider - Provider 实例
142
+ * @param tokenIn - 输入代币地址
143
+ * @param tokenOut - 输出代币地址
144
+ * @param amountIn - 输入数量(wei)
145
+ * @param chain - 链名称
146
+ * @param preferredFee - 优先尝试的费率(可选)
147
+ * @returns 报价结果
148
+ */
149
+ export async function quoteV3(provider, tokenIn, tokenOut, amountIn, chain, preferredFee) {
150
+ if (amountIn <= 0n) {
151
+ return { amountOut: 0n };
152
+ }
153
+ const chainUpper = chain.toUpperCase();
154
+ const config = QUOTE_CONFIG[chainUpper];
155
+ if (!config) {
156
+ console.warn(`[quoteV3] 不支持的链: ${chain}`);
157
+ return { amountOut: 0n };
158
+ }
159
+ if (!config.v3Quoter) {
160
+ console.warn(`[quoteV3] 该链不支持 V3 Quoter: ${chain}`);
161
+ return { amountOut: 0n };
162
+ }
163
+ const quoter = new Contract(config.v3Quoter, V3_QUOTER_ABI, provider);
164
+ const tokenInLower = tokenIn.toLowerCase();
165
+ const tokenOutLower = tokenOut.toLowerCase();
166
+ // 如果指定了费率,优先尝试;否则尝试所有费率
167
+ const feesToTry = preferredFee ? [preferredFee, ...V3_FEE_TIERS.filter(f => f !== preferredFee)] : V3_FEE_TIERS;
168
+ console.log(`[quoteV3] 开始报价: tokenIn=${tokenIn.slice(0, 10)}..., tokenOut=${tokenOut.slice(0, 10)}..., feesToTry=${feesToTry}`);
169
+ // ✅ 策略 1:并行尝试所有费率的直接路径
170
+ const directQuotePromises = feesToTry.map(async (fee) => {
171
+ try {
172
+ const result = await quoter.quoteExactInputSingle.staticCall({
173
+ tokenIn,
174
+ tokenOut,
175
+ amountIn,
176
+ fee,
177
+ sqrtPriceLimitX96: 0n
178
+ });
179
+ const amountOut = Array.isArray(result) ? result[0] : result;
180
+ return { amountOut: amountOut || 0n, fee, success: amountOut && amountOut > 0n };
181
+ }
182
+ catch {
183
+ return { amountOut: 0n, fee, success: false };
184
+ }
185
+ });
186
+ const directResults = await Promise.all(directQuotePromises);
187
+ // 按费率优先级返回第一个成功的结果
188
+ for (const result of directResults) {
189
+ if (result.success) {
190
+ console.log(`[quoteV3] 直接路径成功 (fee=${result.fee}): ${ethers.formatEther(result.amountOut)}`);
191
+ return { amountOut: result.amountOut, fee: result.fee };
192
+ }
193
+ }
194
+ // ✅ 策略 2:并行尝试多跳路径(通过稳定币)
195
+ const validStableCoins = config.stableCoins.filter(sc => {
196
+ const stableLower = sc.toLowerCase();
197
+ return tokenInLower !== stableLower && tokenOutLower !== stableLower;
198
+ });
199
+ // 并行尝试所有稳定币的第一跳
200
+ const firstHopPromises = validStableCoins.flatMap(stableCoin => feesToTry.map(async (fee1) => {
201
+ try {
202
+ const midResult = await quoter.quoteExactInputSingle.staticCall({
203
+ tokenIn,
204
+ tokenOut: stableCoin,
205
+ amountIn,
206
+ fee: fee1,
207
+ sqrtPriceLimitX96: 0n
208
+ });
209
+ const midAmount = Array.isArray(midResult) ? midResult[0] : midResult;
210
+ return { midAmount: midAmount || 0n, stableCoin, fee1, success: midAmount && midAmount > 0n };
211
+ }
212
+ catch {
213
+ return { midAmount: 0n, stableCoin, fee1, success: false };
214
+ }
215
+ }));
216
+ const firstHopResults = await Promise.all(firstHopPromises);
217
+ const successfulFirstHops = firstHopResults.filter(r => r.success);
218
+ // 对成功的第一跳,并行尝试第二跳
219
+ const stableFees = [100, 500, 2500];
220
+ const secondHopPromises = successfulFirstHops.flatMap(hop => stableFees.map(async (fee2) => {
221
+ try {
222
+ const finalResult = await quoter.quoteExactInputSingle.staticCall({
223
+ tokenIn: hop.stableCoin,
224
+ tokenOut,
225
+ amountIn: hop.midAmount,
226
+ fee: fee2,
227
+ sqrtPriceLimitX96: 0n
228
+ });
229
+ const amountOut = Array.isArray(finalResult) ? finalResult[0] : finalResult;
230
+ return { amountOut: amountOut || 0n, fee1: hop.fee1, fee2, success: amountOut && amountOut > 0n };
231
+ }
232
+ catch {
233
+ return { amountOut: 0n, fee1: hop.fee1, fee2, success: false };
234
+ }
235
+ }));
236
+ const secondHopResults = await Promise.all(secondHopPromises);
237
+ // 返回第一个成功的多跳结果
238
+ for (const result of secondHopResults) {
239
+ if (result.success) {
240
+ console.log(`[quoteV3] 多跳路径成功 (${result.fee1}→${result.fee2}): ${ethers.formatEther(result.amountOut)}`);
241
+ return { amountOut: result.amountOut, fee: result.fee1 };
242
+ }
243
+ }
244
+ console.warn(`[quoteV3] 所有路径均失败`);
245
+ return { amountOut: 0n };
246
+ }
247
+ // ============================================================================
248
+ // 统一报价接口
249
+ // ============================================================================
250
+ /**
251
+ * 统一报价接口(根据版本自动选择 V2 或 V3)
252
+ *
253
+ * @param params - 报价参数
254
+ * @returns 报价结果
255
+ */
256
+ export async function quote(params) {
257
+ const { provider, tokenIn, tokenOut, amountIn, chain, version = 'v2', fee } = params;
258
+ if (version === 'v3') {
259
+ return quoteV3(provider, tokenIn, tokenOut, amountIn, chain, fee);
260
+ }
261
+ return quoteV2(provider, tokenIn, tokenOut, amountIn, chain);
262
+ }
263
+ // ============================================================================
264
+ // 代币 → 原生代币报价
265
+ // ============================================================================
266
+ /**
267
+ * 获取 ERC20 代币 → 原生代币(WBNB/WMON 等)的报价
268
+ *
269
+ * @param provider - Provider 实例
270
+ * @param tokenAddress - ERC20 代币地址
271
+ * @param tokenAmount - 代币数量(wei)
272
+ * @param chain - 链名称
273
+ * @param version - 'v2' | 'v3'
274
+ * @param fee - V3 费率档位(仅 V3 时使用)
275
+ * @returns 等值的原生代币数量(wei),失败返回 0n
276
+ */
277
+ export async function getTokenToNativeQuote(provider, tokenAddress, tokenAmount, chain, version = 'v2', fee) {
278
+ if (tokenAmount <= 0n)
279
+ return 0n;
280
+ const chainUpper = chain.toUpperCase();
281
+ const config = QUOTE_CONFIG[chainUpper];
282
+ if (!config) {
283
+ console.warn(`[getTokenToNativeQuote] 不支持的链: ${chain}`);
284
+ return 0n;
285
+ }
286
+ const tokenLower = tokenAddress.toLowerCase();
287
+ const wrappedNativeLower = config.wrappedNative.toLowerCase();
288
+ // 如果代币本身就是包装原生代币,直接返回
289
+ if (tokenLower === wrappedNativeLower) {
290
+ return tokenAmount;
291
+ }
292
+ const result = await quote({
293
+ provider,
294
+ tokenIn: tokenAddress,
295
+ tokenOut: config.wrappedNative,
296
+ amountIn: tokenAmount,
297
+ chain,
298
+ version,
299
+ fee
300
+ });
301
+ return result.amountOut;
302
+ }
303
+ /**
304
+ * 获取原生代币 → ERC20 代币的报价
305
+ *
306
+ * @param provider - Provider 实例
307
+ * @param tokenAddress - ERC20 代币地址
308
+ * @param nativeAmount - 原生代币数量(wei)
309
+ * @param chain - 链名称
310
+ * @param version - 'v2' | 'v3'
311
+ * @param fee - V3 费率档位(仅 V3 时使用)
312
+ * @returns 等值的 ERC20 代币数量(wei),失败返回 0n
313
+ */
314
+ export async function getNativeToTokenQuote(provider, tokenAddress, nativeAmount, chain, version = 'v2', fee) {
315
+ if (nativeAmount <= 0n)
316
+ return 0n;
317
+ const chainUpper = chain.toUpperCase();
318
+ const config = QUOTE_CONFIG[chainUpper];
319
+ if (!config) {
320
+ console.warn(`[getNativeToTokenQuote] 不支持的链: ${chain}`);
321
+ return 0n;
322
+ }
323
+ const tokenLower = tokenAddress.toLowerCase();
324
+ const wrappedNativeLower = config.wrappedNative.toLowerCase();
325
+ // 如果代币本身就是包装原生代币,直接返回
326
+ if (tokenLower === wrappedNativeLower) {
327
+ return nativeAmount;
328
+ }
329
+ const result = await quote({
330
+ provider,
331
+ tokenIn: config.wrappedNative,
332
+ tokenOut: tokenAddress,
333
+ amountIn: nativeAmount,
334
+ chain,
335
+ version,
336
+ fee
337
+ });
338
+ return result.amountOut;
339
+ }
340
+ // ============================================================================
341
+ // 工具函数
342
+ // ============================================================================
343
+ /**
344
+ * 获取链的包装原生代币地址
345
+ */
346
+ export function getWrappedNativeAddress(chain) {
347
+ const chainUpper = chain.toUpperCase();
348
+ const config = QUOTE_CONFIG[chainUpper];
349
+ return config?.wrappedNative || '';
350
+ }
351
+ /**
352
+ * 获取链的稳定币列表
353
+ */
354
+ export function getStableCoins(chain) {
355
+ const chainUpper = chain.toUpperCase();
356
+ const config = QUOTE_CONFIG[chainUpper];
357
+ return config?.stableCoins ? [...config.stableCoins] : [];
358
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.4.11",
3
+ "version": "1.4.13",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",