four-flap-meme-sdk 1.7.22 → 1.7.23
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/dist/eip7702/xlayer/bundle-create.js +13 -10
- package/dist/eip7702/xlayer/bundle-sell.js +137 -18
- package/dist/eip7702/xlayer/bundle-swap.js +105 -11
- package/dist/eip7702/xlayer/constants.d.ts +2 -2
- package/dist/eip7702/xlayer/constants.js +2 -1
- package/dist/eip7702/xlayer/types.d.ts +22 -0
- package/package.json +1 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { ethers, Interface, Contract } from 'ethers';
|
|
12
12
|
import { FLAP_PORTAL_ADDRESS, UNIFIED_DELEGATE_ADDRESS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, WOKB_ADDRESS, POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, V3_FEE_TIERS, } from './constants.js';
|
|
13
|
-
import { getCachedProvider, createWallet, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync,
|
|
13
|
+
import { getCachedProvider, createWallet, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync, calculateProfitAmountByUserType, getProfitRecipient, } from './utils.js';
|
|
14
14
|
// ========================================
|
|
15
15
|
// 常量
|
|
16
16
|
// ========================================
|
|
@@ -125,7 +125,8 @@ function buildProfitCall(walletAddress, profitAmount, profitRecipient, delegateI
|
|
|
125
125
|
* @returns 只返回签名后的交易,不发送
|
|
126
126
|
*/
|
|
127
127
|
export async function bundleCreateBuy(params) {
|
|
128
|
-
const { tokenInfo, tokenAddress = ZERO_ADDRESS, devPrivateKey, buyerPrivateKeys, buyAmounts,
|
|
128
|
+
const { tokenInfo, tokenAddress = ZERO_ADDRESS, devPrivateKey, buyerPrivateKeys, buyAmounts, userType = 'v0', // ✅ 用户类型(影响利润率)
|
|
129
|
+
config, } = params;
|
|
129
130
|
if (buyerPrivateKeys.length !== buyAmounts.length) {
|
|
130
131
|
throw new Error('buyerPrivateKeys 和 buyAmounts 长度必须相同');
|
|
131
132
|
}
|
|
@@ -137,10 +138,10 @@ export async function bundleCreateBuy(params) {
|
|
|
137
138
|
const buyerWallets = buyerPrivateKeys.map(pk => createWallet(pk, provider));
|
|
138
139
|
const allWallets = [devWallet, ...buyerWallets];
|
|
139
140
|
const mainWallet = devWallet;
|
|
140
|
-
//
|
|
141
|
+
// 计算买入金额和利润(单边模式:只有买入)
|
|
141
142
|
const buyAmountsWei = buyAmounts.map(amt => ethers.parseEther(amt));
|
|
142
143
|
const totalBuyAmount = buyAmountsWei.reduce((sum, amt) => sum + amt, 0n);
|
|
143
|
-
const profitAmount =
|
|
144
|
+
const profitAmount = calculateProfitAmountByUserType(totalBuyAmount, userType, false); // ✅ 支持 userType
|
|
144
145
|
const profitRecipient = getProfitRecipient(config);
|
|
145
146
|
// 并行获取 nonce 和 fee data
|
|
146
147
|
const [nonces, feeData] = await Promise.all([
|
|
@@ -272,7 +273,8 @@ export async function bundleCreateBuy(params) {
|
|
|
272
273
|
* @returns 只返回签名后的交易,不发送
|
|
273
274
|
*/
|
|
274
275
|
export async function bundleCreateToDex(params) {
|
|
275
|
-
const { tokenInfo, tokenAddress = ZERO_ADDRESS, payerPrivateKey, curveBuyerPrivateKeys, curveBuyAmounts, enableDexBuy = false, dexBuyerPrivateKeys = [], dexBuyAmounts = [],
|
|
276
|
+
const { tokenInfo, tokenAddress = ZERO_ADDRESS, payerPrivateKey, curveBuyerPrivateKeys, curveBuyAmounts, enableDexBuy = false, dexBuyerPrivateKeys = [], dexBuyAmounts = [], userType = 'v0', // ✅ 用户类型(影响利润率)
|
|
277
|
+
config, } = params;
|
|
276
278
|
if (curveBuyerPrivateKeys.length !== curveBuyAmounts.length) {
|
|
277
279
|
throw new Error('curveBuyerPrivateKeys 和 curveBuyAmounts 长度必须相同');
|
|
278
280
|
}
|
|
@@ -294,13 +296,13 @@ export async function bundleCreateToDex(params) {
|
|
|
294
296
|
const allWallets = Array.from(allWalletsMap.values());
|
|
295
297
|
const mainWallet = payerWallet;
|
|
296
298
|
const mainWalletIndex = allWallets.findIndex(w => w.address.toLowerCase() === mainWallet.address.toLowerCase());
|
|
297
|
-
//
|
|
299
|
+
// 计算金额和利润(单边模式:只有买入)
|
|
298
300
|
const curveBuyAmountsWei = curveBuyAmounts.map(amt => ethers.parseEther(amt));
|
|
299
301
|
const dexBuyAmountsWei = dexBuyAmounts.map(amt => ethers.parseEther(amt));
|
|
300
302
|
const totalCurveBuyAmount = curveBuyAmountsWei.reduce((sum, amt) => sum + amt, 0n);
|
|
301
303
|
const totalDexBuyAmount = dexBuyAmountsWei.reduce((sum, amt) => sum + amt, 0n);
|
|
302
304
|
const totalBuyAmount = totalCurveBuyAmount + totalDexBuyAmount;
|
|
303
|
-
const profitAmount =
|
|
305
|
+
const profitAmount = calculateProfitAmountByUserType(totalBuyAmount, userType, false); // ✅ 支持 userType
|
|
304
306
|
const profitRecipient = getProfitRecipient(config);
|
|
305
307
|
// 并行获取 nonce 和 fee data
|
|
306
308
|
const [nonces, feeData] = await Promise.all([
|
|
@@ -426,7 +428,8 @@ export async function bundleCreateToDex(params) {
|
|
|
426
428
|
* @returns 只返回签名后的交易,不发送
|
|
427
429
|
*/
|
|
428
430
|
export async function bundleGraduateBuy(params) {
|
|
429
|
-
const { tokenAddress, privateKeys, payerPrivateKey, amountMode, totalBuyAmount, minAmount, maxAmount, walletAmounts, enableDexBuy = false, extensionData = '0x', curveAddresses, dexAddresses, graduationAmount,
|
|
431
|
+
const { tokenAddress, privateKeys, payerPrivateKey, amountMode, totalBuyAmount, minAmount, maxAmount, walletAmounts, enableDexBuy = false, extensionData = '0x', curveAddresses, dexAddresses, graduationAmount, userType = 'v0', // ✅ 用户类型(影响利润率)
|
|
432
|
+
config, } = params;
|
|
430
433
|
const provider = getCachedProvider(config?.rpcUrl);
|
|
431
434
|
const delegateAddress = (config?.delegateAddress && config.delegateAddress.trim()) || UNIFIED_DELEGATE_ADDRESS;
|
|
432
435
|
const delegateInterface = new Interface(UNIFIED_DELEGATE_ABI);
|
|
@@ -519,11 +522,11 @@ export async function bundleGraduateBuy(params) {
|
|
|
519
522
|
const allWallets = Array.from(allWalletsMap.values());
|
|
520
523
|
const mainWallet = payerWallet;
|
|
521
524
|
const mainWalletIndex = allWallets.findIndex(w => w.address.toLowerCase() === mainWallet.address.toLowerCase());
|
|
522
|
-
//
|
|
525
|
+
// 计算金额和利润(单边模式:只有买入)
|
|
523
526
|
const curveTotalWei = curveBuyers.reduce((sum, b) => sum + b.amount, 0n);
|
|
524
527
|
const dexTotalWei = dexBuyers.reduce((sum, b) => sum + b.amount, 0n);
|
|
525
528
|
const totalAmount = curveTotalWei + dexTotalWei;
|
|
526
|
-
const profitAmount =
|
|
529
|
+
const profitAmount = calculateProfitAmountByUserType(totalAmount, userType, false); // ✅ 支持 userType
|
|
527
530
|
const profitRecipient = getProfitRecipient(config);
|
|
528
531
|
// 并行获取 nonce 和 fee data
|
|
529
532
|
const [nonces, feeData] = await Promise.all([
|
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
*
|
|
7
7
|
* 只生成签名,由调用方决定如何提交
|
|
8
8
|
*/
|
|
9
|
-
import { ethers, Contract } from 'ethers';
|
|
10
|
-
import { getCachedProvider, createWallet, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync,
|
|
11
|
-
import { UNIFIED_DELEGATE_ADDRESS, POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, FLAP_PORTAL_ADDRESS, WOKB_ADDRESS, V3_FEE_TIERS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, ERC20_ABI, } from './constants.js';
|
|
9
|
+
import { ethers, Contract, JsonRpcProvider } from 'ethers';
|
|
10
|
+
import { getCachedProvider, createWallet, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync, calculateProfitAmountByUserType, getProfitRecipient, } from './utils.js';
|
|
11
|
+
import { UNIFIED_DELEGATE_ADDRESS, POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, FLAP_PORTAL_ADDRESS, WOKB_ADDRESS, V3_FEE_TIERS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, ERC20_ABI, XLAYER_RPC_URL, XLAYER_CHAIN_ID, } from './constants.js';
|
|
12
|
+
import { quoteV2, quoteV3 } from '../../utils/quote-helpers.js';
|
|
12
13
|
// ========================================
|
|
13
14
|
// 路由地址获取
|
|
14
15
|
// ========================================
|
|
@@ -23,6 +24,88 @@ function getDefaultRouter(tradeType) {
|
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
26
|
// ========================================
|
|
27
|
+
// 卖出报价(Token → OKB)
|
|
28
|
+
// ========================================
|
|
29
|
+
/**
|
|
30
|
+
* 获取卖出报价:预估卖出代币能得到多少 OKB
|
|
31
|
+
*
|
|
32
|
+
* @param totalSellAmount - 总卖出代币数量(wei)
|
|
33
|
+
* @param tokenAddress - 代币地址
|
|
34
|
+
* @param tradeType - 交易类型(FLAP/V2/V3)
|
|
35
|
+
* @param fee - V3 费率
|
|
36
|
+
* @param rpcUrl - RPC URL
|
|
37
|
+
* @returns 预估得到的 OKB 数量(wei)
|
|
38
|
+
*/
|
|
39
|
+
async function quoteSellOutput(totalSellAmount, tokenAddress, tradeType, fee, rpcUrl) {
|
|
40
|
+
if (totalSellAmount <= 0n)
|
|
41
|
+
return 0n;
|
|
42
|
+
const provider = new JsonRpcProvider(rpcUrl || XLAYER_RPC_URL, { chainId: XLAYER_CHAIN_ID, name: 'xlayer', ensAddress: undefined });
|
|
43
|
+
try {
|
|
44
|
+
switch (tradeType) {
|
|
45
|
+
case 'FLAP': {
|
|
46
|
+
// FLAP Portal 报价
|
|
47
|
+
const portalContract = new Contract(FLAP_PORTAL_ADDRESS, FLAP_PORTAL_ABI, provider);
|
|
48
|
+
// 先尝试 quoteSell(Token → OKB)
|
|
49
|
+
try {
|
|
50
|
+
const okbOut = await portalContract.quoteSell(tokenAddress, totalSellAmount);
|
|
51
|
+
console.log(`[Bundle Sell] FLAP quoteSell 成功: ${ethers.formatEther(okbOut)} OKB`);
|
|
52
|
+
return BigInt(okbOut.toString());
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
console.warn(`[Bundle Sell] FLAP quoteSell 失败,尝试 quoteExactInput...`);
|
|
56
|
+
}
|
|
57
|
+
// 再尝试 quoteExactInput
|
|
58
|
+
try {
|
|
59
|
+
const okbOut = await portalContract.quoteExactInput({
|
|
60
|
+
inputToken: tokenAddress,
|
|
61
|
+
outputToken: ethers.ZeroAddress, // OKB
|
|
62
|
+
inputAmount: totalSellAmount,
|
|
63
|
+
});
|
|
64
|
+
console.log(`[Bundle Sell] FLAP quoteExactInput 成功: ${ethers.formatEther(okbOut)} OKB`);
|
|
65
|
+
return BigInt(okbOut.toString());
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
console.warn(`[Bundle Sell] FLAP quoteExactInput 失败,尝试 V2...`);
|
|
69
|
+
}
|
|
70
|
+
// 回退到 V2
|
|
71
|
+
const v2Result = await quoteV2(provider, tokenAddress, WOKB_ADDRESS, totalSellAmount, 'XLAYER');
|
|
72
|
+
if (v2Result.amountOut > 0n) {
|
|
73
|
+
console.log(`[Bundle Sell] FLAP 回退 V2 报价成功: ${ethers.formatEther(v2Result.amountOut)} OKB`);
|
|
74
|
+
return v2Result.amountOut;
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
case 'V2': {
|
|
79
|
+
const result = await quoteV2(provider, tokenAddress, WOKB_ADDRESS, totalSellAmount, 'XLAYER');
|
|
80
|
+
if (result.amountOut > 0n) {
|
|
81
|
+
console.log(`[Bundle Sell] V2 报价成功: ${ethers.formatEther(result.amountOut)} OKB`);
|
|
82
|
+
return result.amountOut;
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
case 'V3': {
|
|
87
|
+
const result = await quoteV3(provider, tokenAddress, WOKB_ADDRESS, totalSellAmount, 'XLAYER', fee);
|
|
88
|
+
if (result.amountOut > 0n) {
|
|
89
|
+
console.log(`[Bundle Sell] V3 报价成功 (fee=${result.fee}): ${ethers.formatEther(result.amountOut)} OKB`);
|
|
90
|
+
return result.amountOut;
|
|
91
|
+
}
|
|
92
|
+
// 回退到 V2
|
|
93
|
+
const v2Result = await quoteV2(provider, tokenAddress, WOKB_ADDRESS, totalSellAmount, 'XLAYER');
|
|
94
|
+
if (v2Result.amountOut > 0n) {
|
|
95
|
+
console.log(`[Bundle Sell] V3 回退 V2 报价成功: ${ethers.formatEther(v2Result.amountOut)} OKB`);
|
|
96
|
+
return v2Result.amountOut;
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
console.error(`[Bundle Sell] 报价失败:`, error);
|
|
104
|
+
}
|
|
105
|
+
console.warn(`[Bundle Sell] 无法获取报价,利润计算可能不准确`);
|
|
106
|
+
return 0n;
|
|
107
|
+
}
|
|
108
|
+
// ========================================
|
|
26
109
|
// 代币余额查询
|
|
27
110
|
// ========================================
|
|
28
111
|
async function getTokenBalances(wallets, tokenAddress) {
|
|
@@ -255,27 +338,60 @@ export async function bundleSell(params) {
|
|
|
255
338
|
}
|
|
256
339
|
const totalSellAmount = sellAmountsWei.reduce((sum, amt) => sum + amt, 0n);
|
|
257
340
|
const validSellCount = wallets.filter((_, i) => sellAmountsWei[i] > 0n).length;
|
|
258
|
-
// ✅
|
|
259
|
-
//
|
|
260
|
-
const
|
|
341
|
+
// ✅ 修复:获取卖出报价,基于预估的 OKB 输出计算利润
|
|
342
|
+
// 1. 先获取报价:卖出 totalSellAmount 代币能得到多少 OKB
|
|
343
|
+
const estimatedOkbOut = await quoteSellOutput(totalSellAmount, tokenAddress, tradeType, fee, config?.rpcUrl);
|
|
344
|
+
// 2. 基于预估的 OKB 输出计算利润(单边模式,isDoubleMode = false)
|
|
345
|
+
const profitAmount = estimatedOkbOut > 0n
|
|
346
|
+
? calculateProfitAmountByUserType(estimatedOkbOut, userType, false)
|
|
347
|
+
: 0n;
|
|
348
|
+
console.log(`[Bundle Sell] 卖出数量: ${ethers.formatUnits(totalSellAmount, tokenDecimals)} Token`);
|
|
349
|
+
console.log(`[Bundle Sell] 预估获得: ${ethers.formatEther(estimatedOkbOut)} OKB`);
|
|
350
|
+
console.log(`[Bundle Sell] 利润: ${ethers.formatEther(profitAmount)} OKB (userType=${userType})`);
|
|
261
351
|
// ========================================
|
|
262
352
|
// 同步构建授权和调用
|
|
263
353
|
// ========================================
|
|
264
354
|
const authorizations = signAuthorizationsWithNonces(allWallets, walletNonces, delegateAddress, mainWalletIndex === -1 ? 0 : mainWalletIndex);
|
|
265
|
-
// ✅
|
|
355
|
+
// ✅ 计算每个钱包的利润(按卖出数量比例分配)
|
|
356
|
+
const profitPerWallet = [];
|
|
357
|
+
if (profitAmount > 0n && totalSellAmount > 0n) {
|
|
358
|
+
for (let i = 0; i < wallets.length; i++) {
|
|
359
|
+
const sellAmount = sellAmountsWei[i];
|
|
360
|
+
if (sellAmount > 0n) {
|
|
361
|
+
// 按比例分配利润
|
|
362
|
+
const walletProfit = (profitAmount * sellAmount) / totalSellAmount;
|
|
363
|
+
profitPerWallet.push(walletProfit);
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
profitPerWallet.push(0n);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
for (let i = 0; i < wallets.length; i++) {
|
|
372
|
+
profitPerWallet.push(0n);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
// ✅ 构建调用列表(卖出调用 + 每个钱包的利润刮取)
|
|
266
376
|
const calls = [];
|
|
267
|
-
//
|
|
268
|
-
// 由于 EIP-7702 是单笔交易原子执行,利润刮取调用放在卖出调用之后
|
|
377
|
+
// 1. 先执行卖出,每个钱包卖出代币获得 OKB
|
|
269
378
|
const sellCalls = buildSellCalls(wallets, sellAmountsWei, tokenAddress, tradeType, actualRouter, fee);
|
|
270
379
|
calls.push(...sellCalls);
|
|
271
|
-
// ✅
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
380
|
+
// 2. ✅ 每个卖出钱包从自己的卖出所得中扣除利润
|
|
381
|
+
// 使用 transferAmount 传递精确金额
|
|
382
|
+
for (let i = 0; i < wallets.length; i++) {
|
|
383
|
+
const walletProfit = profitPerWallet[i];
|
|
384
|
+
if (walletProfit > 0n) {
|
|
385
|
+
calls.push({
|
|
386
|
+
target: wallets[i].address,
|
|
387
|
+
allowFailure: false,
|
|
388
|
+
value: 0n,
|
|
389
|
+
callData: delegateInterface.encodeFunctionData('transferAmount', [
|
|
390
|
+
profitRecipient,
|
|
391
|
+
walletProfit,
|
|
392
|
+
]),
|
|
393
|
+
});
|
|
394
|
+
}
|
|
279
395
|
}
|
|
280
396
|
// ========================================
|
|
281
397
|
// 同步构建并签名交易
|
|
@@ -286,7 +402,10 @@ export async function bundleSell(params) {
|
|
|
286
402
|
metadata: {
|
|
287
403
|
totalSellAmount: ethers.formatUnits(totalSellAmount, tokenDecimals),
|
|
288
404
|
walletCount: validSellCount,
|
|
289
|
-
|
|
405
|
+
estimatedOkbOut: ethers.formatEther(estimatedOkbOut), // ✅ 预估卖出所得 OKB
|
|
406
|
+
profitAmount: ethers.formatEther(profitAmount), // ✅ 利润是 OKB,用 18 位精度
|
|
407
|
+
profitRecipient,
|
|
408
|
+
userType,
|
|
290
409
|
},
|
|
291
410
|
};
|
|
292
411
|
}
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
* 支持 FLAP 内盘、V2 和 V3 三种交易类型
|
|
10
10
|
* 只生成签名,由调用方决定如何提交
|
|
11
11
|
*/
|
|
12
|
-
import { ethers } from 'ethers';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
12
|
+
import { ethers, Contract, JsonRpcProvider } from 'ethers';
|
|
13
|
+
import { quoteV2, quoteV3 } from '../../utils/quote-helpers.js';
|
|
14
|
+
import { getCachedProvider, createWallet, signAuthorization, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync, calculateProfitAmountByUserType, calculateProfitAmountByTxCount, getProfitRecipient, generateRandomWallets, } from './utils.js';
|
|
15
|
+
import { UNIFIED_DELEGATE_ADDRESS, POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, FLAP_PORTAL_ADDRESS, WOKB_ADDRESS, V3_FEE_TIERS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, ERC20_ABI, XLAYER_RPC_URL, XLAYER_CHAIN_ID, } from './constants.js';
|
|
15
16
|
// ========================================
|
|
16
17
|
// 路由地址获取
|
|
17
18
|
// ========================================
|
|
@@ -26,6 +27,81 @@ function getDefaultRouter(tradeType) {
|
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
// ========================================
|
|
30
|
+
// 卖出报价(Token → OKB)
|
|
31
|
+
// ========================================
|
|
32
|
+
/**
|
|
33
|
+
* 获取卖出报价:预估卖出代币能得到多少 OKB
|
|
34
|
+
*/
|
|
35
|
+
async function quoteSellOutput(sellAmount, tokenAddress, tradeType, fee, rpcUrl) {
|
|
36
|
+
if (sellAmount <= 0n)
|
|
37
|
+
return 0n;
|
|
38
|
+
const provider = new JsonRpcProvider(rpcUrl || XLAYER_RPC_URL, { chainId: XLAYER_CHAIN_ID, name: 'xlayer', ensAddress: undefined });
|
|
39
|
+
try {
|
|
40
|
+
switch (tradeType) {
|
|
41
|
+
case 'FLAP': {
|
|
42
|
+
// FLAP Portal 报价
|
|
43
|
+
const portalContract = new Contract(FLAP_PORTAL_ADDRESS, FLAP_PORTAL_ABI, provider);
|
|
44
|
+
// 先尝试 quoteSell
|
|
45
|
+
try {
|
|
46
|
+
const okbOut = await portalContract.quoteSell(tokenAddress, sellAmount);
|
|
47
|
+
console.log(`[Bundle Swap] FLAP quoteSell 成功: ${ethers.formatEther(okbOut)} OKB`);
|
|
48
|
+
return BigInt(okbOut.toString());
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
console.warn(`[Bundle Swap] FLAP quoteSell 失败,尝试 quoteExactInput...`);
|
|
52
|
+
}
|
|
53
|
+
// 再尝试 quoteExactInput
|
|
54
|
+
try {
|
|
55
|
+
const okbOut = await portalContract.quoteExactInput({
|
|
56
|
+
inputToken: tokenAddress,
|
|
57
|
+
outputToken: ethers.ZeroAddress,
|
|
58
|
+
inputAmount: sellAmount,
|
|
59
|
+
});
|
|
60
|
+
console.log(`[Bundle Swap] FLAP quoteExactInput 成功: ${ethers.formatEther(okbOut)} OKB`);
|
|
61
|
+
return BigInt(okbOut.toString());
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
console.warn(`[Bundle Swap] FLAP quoteExactInput 失败,尝试 V2...`);
|
|
65
|
+
}
|
|
66
|
+
// 回退到 V2
|
|
67
|
+
const v2Result = await quoteV2(provider, tokenAddress, WOKB_ADDRESS, sellAmount, 'XLAYER');
|
|
68
|
+
if (v2Result.amountOut > 0n) {
|
|
69
|
+
console.log(`[Bundle Swap] FLAP 回退 V2 报价成功: ${ethers.formatEther(v2Result.amountOut)} OKB`);
|
|
70
|
+
return v2Result.amountOut;
|
|
71
|
+
}
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
case 'V2': {
|
|
75
|
+
const result = await quoteV2(provider, tokenAddress, WOKB_ADDRESS, sellAmount, 'XLAYER');
|
|
76
|
+
if (result.amountOut > 0n) {
|
|
77
|
+
console.log(`[Bundle Swap] V2 报价成功: ${ethers.formatEther(result.amountOut)} OKB`);
|
|
78
|
+
return result.amountOut;
|
|
79
|
+
}
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
case 'V3': {
|
|
83
|
+
const result = await quoteV3(provider, tokenAddress, WOKB_ADDRESS, sellAmount, 'XLAYER', fee);
|
|
84
|
+
if (result.amountOut > 0n) {
|
|
85
|
+
console.log(`[Bundle Swap] V3 报价成功 (fee=${result.fee}): ${ethers.formatEther(result.amountOut)} OKB`);
|
|
86
|
+
return result.amountOut;
|
|
87
|
+
}
|
|
88
|
+
// 回退到 V2
|
|
89
|
+
const v2Result = await quoteV2(provider, tokenAddress, WOKB_ADDRESS, sellAmount, 'XLAYER');
|
|
90
|
+
if (v2Result.amountOut > 0n) {
|
|
91
|
+
console.log(`[Bundle Swap] V3 回退 V2 报价成功: ${ethers.formatEther(v2Result.amountOut)} OKB`);
|
|
92
|
+
return v2Result.amountOut;
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
console.error(`[Bundle Swap] 报价失败:`, error);
|
|
100
|
+
}
|
|
101
|
+
console.warn(`[Bundle Swap] 无法获取报价,利润计算可能不准确`);
|
|
102
|
+
return 0n;
|
|
103
|
+
}
|
|
104
|
+
// ========================================
|
|
29
105
|
// 构建卖出并转账调用
|
|
30
106
|
// ========================================
|
|
31
107
|
function buildSellAndTransferCall(sellerWallet, sellAmount, tokenAddress, recipient, tradeType, routerAddress, fee) {
|
|
@@ -202,10 +278,16 @@ export async function bundleSwap(params) {
|
|
|
202
278
|
const hopWalletInfos = hopCount > 0 ? generateRandomWallets(hopCount) : [];
|
|
203
279
|
const hopWallets = hopWalletInfos.map(info => createWallet(info.privateKey, provider));
|
|
204
280
|
const sellAmountWei = ethers.parseUnits(sellAmount, tokenDecimals);
|
|
205
|
-
// ✅
|
|
206
|
-
const
|
|
207
|
-
|
|
281
|
+
// ✅ 修复:获取卖出报价,基于预估的 OKB 输出计算利润
|
|
282
|
+
const estimatedOkbOut = await quoteSellOutput(sellAmountWei, tokenAddress, tradeType, fee, config?.rpcUrl);
|
|
283
|
+
// 使用双边费率计算利润(换手 = 卖 + 买)
|
|
284
|
+
const profitAmount = estimatedOkbOut > 0n
|
|
285
|
+
? calculateProfitAmountByUserType(estimatedOkbOut, userType, true) // isDoubleMode = true
|
|
286
|
+
: 0n;
|
|
208
287
|
const profitRecipient = getProfitRecipient(config);
|
|
288
|
+
console.log(`[Bundle Swap] 卖出数量: ${sellAmount} Token`);
|
|
289
|
+
console.log(`[Bundle Swap] 预估获得: ${ethers.formatEther(estimatedOkbOut)} OKB`);
|
|
290
|
+
console.log(`[Bundle Swap] 利润: ${ethers.formatEther(profitAmount)} OKB (userType=${userType})`);
|
|
209
291
|
// ========================================
|
|
210
292
|
// 并行获取所有异步数据
|
|
211
293
|
// ========================================
|
|
@@ -260,8 +342,11 @@ export async function bundleSwap(params) {
|
|
|
260
342
|
sellerAddress: sellerWallet.address,
|
|
261
343
|
buyerAddresses: [buyerWallet.address],
|
|
262
344
|
sellAmount: ethers.formatUnits(sellAmountWei, tokenDecimals),
|
|
345
|
+
estimatedOkbOut: ethers.formatEther(estimatedOkbOut), // ✅ 预估卖出所得 OKB
|
|
263
346
|
estimatedBuyAmount: '0',
|
|
264
|
-
profitAmount: ethers.formatEther(profitAmount),
|
|
347
|
+
profitAmount: ethers.formatEther(profitAmount), // ✅ 利润是 OKB
|
|
348
|
+
profitRecipient,
|
|
349
|
+
userType,
|
|
265
350
|
},
|
|
266
351
|
};
|
|
267
352
|
}
|
|
@@ -296,10 +381,16 @@ export async function bundleBatchSwap(params) {
|
|
|
296
381
|
const hopWalletInfos = hopCount > 0 ? generateRandomWallets(hopCount) : [];
|
|
297
382
|
const hopWallets = hopWalletInfos.map(info => createWallet(info.privateKey, provider));
|
|
298
383
|
const sellAmountWei = ethers.parseUnits(sellAmount, tokenDecimals);
|
|
299
|
-
// ✅
|
|
300
|
-
const
|
|
301
|
-
|
|
384
|
+
// ✅ 修复:获取卖出报价,基于预估的 OKB 输出计算利润
|
|
385
|
+
const estimatedOkbOut = await quoteSellOutput(sellAmountWei, tokenAddress, tradeType, fee, config?.rpcUrl);
|
|
386
|
+
// 使用双边费率计算利润(换手 = 卖 + 买)
|
|
387
|
+
const profitAmount = estimatedOkbOut > 0n
|
|
388
|
+
? calculateProfitAmountByUserType(estimatedOkbOut, userType, true) // isDoubleMode = true
|
|
389
|
+
: 0n;
|
|
302
390
|
const profitRecipient = getProfitRecipient(config);
|
|
391
|
+
console.log(`[Bundle Batch Swap] 卖出数量: ${sellAmount} Token`);
|
|
392
|
+
console.log(`[Bundle Batch Swap] 预估获得: ${ethers.formatEther(estimatedOkbOut)} OKB`);
|
|
393
|
+
console.log(`[Bundle Batch Swap] 利润: ${ethers.formatEther(profitAmount)} OKB (userType=${userType})`);
|
|
303
394
|
// ========================================
|
|
304
395
|
// 并行获取所有异步数据
|
|
305
396
|
// ========================================
|
|
@@ -387,8 +478,11 @@ export async function bundleBatchSwap(params) {
|
|
|
387
478
|
sellerAddress: sellerWallet.address,
|
|
388
479
|
buyerAddresses: buyerWallets.map(w => w.address),
|
|
389
480
|
sellAmount: ethers.formatUnits(sellAmountWei, tokenDecimals),
|
|
481
|
+
estimatedOkbOut: ethers.formatEther(estimatedOkbOut), // ✅ 预估卖出所得 OKB
|
|
390
482
|
estimatedBuyAmount: '0',
|
|
391
|
-
profitAmount: ethers.formatEther(profitAmount),
|
|
483
|
+
profitAmount: ethers.formatEther(profitAmount), // ✅ 利润是 OKB
|
|
484
|
+
profitRecipient,
|
|
485
|
+
userType,
|
|
392
486
|
},
|
|
393
487
|
};
|
|
394
488
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
export declare const XLAYER_CHAIN_ID = 196;
|
|
5
5
|
export declare const XLAYER_RPC_URL = "https://xlayerrpc.okx.com";
|
|
6
6
|
/** 已部署的 UnifiedDelegate 合约地址 */
|
|
7
|
-
export declare const UNIFIED_DELEGATE_ADDRESS = "
|
|
7
|
+
export declare const UNIFIED_DELEGATE_ADDRESS = "0x33C577Fa6A701E30F35B4B0DE3A361bfF400F0Cc";
|
|
8
8
|
/** Multicall3 地址 (标准部署) */
|
|
9
9
|
export declare const MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
|
|
10
10
|
/** WOKB 地址 */
|
|
@@ -49,7 +49,7 @@ export declare const V3_FEE_TIERS: {
|
|
|
49
49
|
readonly STANDARD: 3000;
|
|
50
50
|
readonly HIGH: 10000;
|
|
51
51
|
};
|
|
52
|
-
export declare const UNIFIED_DELEGATE_ABI: readonly ["function transferTo(address recipient) external payable", "function executeBuy(address router, address wokb, address tokenOut, uint24 fee, uint256 amountIn) external payable", "function executeSell(address router, address tokenIn, address wokb, uint24 fee, uint256 amountIn) external", "function executeSellAndTransfer(address router, address tokenIn, address wokb, uint24 fee, uint256 amountIn, address recipient) external", "function executeBuyV2(address router, address[] calldata path, uint256 amountIn) external payable", "function executeSellV2(address router, address[] calldata path, uint256 amountIn) external", "function executeSellAndTransferV2(address router, address[] calldata path, uint256 amountIn, address payable recipient) external", "function executeApprove(address token, address spender, uint256 amount) external", "function executeTransfer(address token, address to, uint256 amount) external", "function executeTransferAll(address token, address to) external", "function executeBatch((address target, uint256 value, bytes data)[] calls) external payable"];
|
|
52
|
+
export declare const UNIFIED_DELEGATE_ABI: readonly ["function transferTo(address recipient) external payable", "function transferAmount(address recipient, uint256 amount) external", "function executeBuy(address router, address wokb, address tokenOut, uint24 fee, uint256 amountIn) external payable", "function executeSell(address router, address tokenIn, address wokb, uint24 fee, uint256 amountIn) external", "function executeSellAndTransfer(address router, address tokenIn, address wokb, uint24 fee, uint256 amountIn, address recipient) external", "function executeBuyV2(address router, address[] calldata path, uint256 amountIn) external payable", "function executeSellV2(address router, address[] calldata path, uint256 amountIn) external", "function executeSellAndTransferV2(address router, address[] calldata path, uint256 amountIn, address payable recipient) external", "function executeApprove(address token, address spender, uint256 amount) external", "function executeTransfer(address token, address to, uint256 amount) external", "function executeTransferAll(address token, address to) external", "function executeBatch((address target, uint256 value, bytes data)[] calls) external payable"];
|
|
53
53
|
export declare const ERC20_ABI: readonly ["function approve(address spender, uint256 amount) external returns (bool)", "function transfer(address to, uint256 amount) external returns (bool)", "function transferFrom(address from, address to, uint256 amount) external returns (bool)", "function balanceOf(address account) external view returns (uint256)", "function allowance(address owner, address spender) external view returns (uint256)", "function decimals() external view returns (uint8)", "function symbol() external view returns (string)"];
|
|
54
54
|
export declare const MULTICALL3_ABI: readonly ["function aggregate3Value((address target, bool allowFailure, uint256 value, bytes callData)[] calls) external payable returns ((bool success, bytes returnData)[] returnData)"];
|
|
55
55
|
export declare const V3_ROUTER_ABI: readonly ["function exactInputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96)) external payable returns (uint256 amountOut)", "function multicall(uint256 deadline, bytes[] data) external payable returns (bytes[] results)", "function unwrapWETH9(uint256 amountMinimum, address recipient) external payable", "function refundETH() external payable"];
|
|
@@ -10,7 +10,7 @@ export const XLAYER_RPC_URL = 'https://xlayerrpc.okx.com';
|
|
|
10
10
|
// 合约地址
|
|
11
11
|
// ========================================
|
|
12
12
|
/** 已部署的 UnifiedDelegate 合约地址 */
|
|
13
|
-
export const UNIFIED_DELEGATE_ADDRESS = '
|
|
13
|
+
export const UNIFIED_DELEGATE_ADDRESS = '0x33C577Fa6A701E30F35B4B0DE3A361bfF400F0Cc';
|
|
14
14
|
/** Multicall3 地址 (标准部署) */
|
|
15
15
|
export const MULTICALL3_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11';
|
|
16
16
|
/** WOKB 地址 */
|
|
@@ -75,6 +75,7 @@ export const UNIFIED_DELEGATE_ABI = [
|
|
|
75
75
|
// 转账功能
|
|
76
76
|
// ========================================
|
|
77
77
|
'function transferTo(address recipient) external payable',
|
|
78
|
+
'function transferAmount(address recipient, uint256 amount) external',
|
|
78
79
|
// ========================================
|
|
79
80
|
// V3 买卖功能 (PotatoSwap V3 / DYORSwap)
|
|
80
81
|
// ========================================
|
|
@@ -97,9 +97,18 @@ export interface BundleSellResult {
|
|
|
97
97
|
txHash?: string;
|
|
98
98
|
/** 元数据 */
|
|
99
99
|
metadata: {
|
|
100
|
+
/** 总卖出代币数量 */
|
|
100
101
|
totalSellAmount: string;
|
|
102
|
+
/** 参与卖出的钱包数量 */
|
|
101
103
|
walletCount: number;
|
|
104
|
+
/** 预估卖出获得的 OKB 数量 */
|
|
105
|
+
estimatedOkbOut: string;
|
|
106
|
+
/** 利润金额(OKB) */
|
|
102
107
|
profitAmount: string;
|
|
108
|
+
/** 利润接收地址 */
|
|
109
|
+
profitRecipient: string;
|
|
110
|
+
/** 用户类型 */
|
|
111
|
+
userType: string;
|
|
103
112
|
};
|
|
104
113
|
}
|
|
105
114
|
export interface BundleApproveParams {
|
|
@@ -186,8 +195,15 @@ export interface BundleSwapResult {
|
|
|
186
195
|
sellerAddress: string;
|
|
187
196
|
buyerAddresses: string[];
|
|
188
197
|
sellAmount: string;
|
|
198
|
+
/** 预估卖出获得的 OKB 数量 */
|
|
199
|
+
estimatedOkbOut?: string;
|
|
189
200
|
estimatedBuyAmount: string;
|
|
201
|
+
/** 利润金额(OKB) */
|
|
190
202
|
profitAmount: string;
|
|
203
|
+
/** 利润接收地址 */
|
|
204
|
+
profitRecipient?: string;
|
|
205
|
+
/** 用户类型 */
|
|
206
|
+
userType?: string;
|
|
191
207
|
};
|
|
192
208
|
}
|
|
193
209
|
export interface MultiHopTransferParams {
|
|
@@ -362,6 +378,8 @@ export interface BundleCreateBuyParams extends CreateTokenParams {
|
|
|
362
378
|
buyerPrivateKeys: string[];
|
|
363
379
|
/** 每个买入钱包的金额 (OKB) */
|
|
364
380
|
buyAmounts: string[];
|
|
381
|
+
/** 用户类型(影响利润率) */
|
|
382
|
+
userType?: 'v0' | 'v1' | 'default';
|
|
365
383
|
/** 配置 */
|
|
366
384
|
config?: EIP7702Config;
|
|
367
385
|
}
|
|
@@ -391,6 +409,8 @@ export interface BundleCreateToDexParams extends CreateTokenParams {
|
|
|
391
409
|
dexBuyerPrivateKeys?: string[];
|
|
392
410
|
/** 外盘买入金额 (OKB) */
|
|
393
411
|
dexBuyAmounts?: string[];
|
|
412
|
+
/** 用户类型(影响利润率) */
|
|
413
|
+
userType?: 'v0' | 'v1' | 'default';
|
|
394
414
|
/** 配置 */
|
|
395
415
|
config?: EIP7702Config;
|
|
396
416
|
}
|
|
@@ -438,6 +458,8 @@ export interface BundleGraduateBuyParams {
|
|
|
438
458
|
dexAddresses?: string[];
|
|
439
459
|
/** 毕业金额(OKB) */
|
|
440
460
|
graduationAmount?: number;
|
|
461
|
+
/** 用户类型(影响利润率) */
|
|
462
|
+
userType?: 'v0' | 'v1' | 'default';
|
|
441
463
|
/** 配置 */
|
|
442
464
|
config?: EIP7702Config;
|
|
443
465
|
}
|