four-flap-meme-sdk 1.5.91 → 1.5.93
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/xlayer/bundle.js
CHANGED
|
@@ -14,7 +14,7 @@ import { encodeBuyCall, encodeBuyCallV3, encodeSellCall, encodeApproveCall, enco
|
|
|
14
14
|
import { mapWithConcurrency } from '../utils/concurrency.js';
|
|
15
15
|
import { PROFIT_CONFIG, ZERO_ADDRESS } from '../utils/constants.js';
|
|
16
16
|
import { DexQuery } from './dex.js';
|
|
17
|
-
import { encodeSwapExactETHForTokensSupportingFee, encodeSwapExactETHForTokensV3, } from './dex.js';
|
|
17
|
+
import { encodeSwapExactETHForTokensSupportingFee, encodeSwapExactETHForTokensV3, encodeSwapExactTokensForETHSupportingFee, encodeSwapExactTokensForETHV3, } from './dex.js';
|
|
18
18
|
// ============================================================================
|
|
19
19
|
// AA Nonce(EntryPoint nonce)本地分配器
|
|
20
20
|
// ============================================================================
|
|
@@ -557,6 +557,10 @@ export class BundleExecutor {
|
|
|
557
557
|
if (profitSettings.extractProfit && nativeProfitAmount > 0n) {
|
|
558
558
|
console.log(`[利润提取] 总利润: ${useNativeToken ? formatOkb(nativeProfitAmount) : `${formatOkb(totalProfitWei)} (ERC20) -> ${formatOkb(nativeProfitAmount)} (OKB)`} -> ${profitSettings.profitRecipient}`);
|
|
559
559
|
}
|
|
560
|
+
// ✅ 获取代币状态,判断是走内盘 Portal 还是外盘 DEX
|
|
561
|
+
const tokenState = await this.portalQuery.getTokenV7(tokenAddress);
|
|
562
|
+
const isGraduated = tokenState.status === 4; // DEX = 4 表示已毕业
|
|
563
|
+
console.log(`[BundleBuy] Token status: ${tokenState.status} (${isGraduated ? 'DEX/已毕业' : '内盘'})`);
|
|
560
564
|
// 1. 预取账户信息(并行),并批量估算 gas(减少对 bundler 的 N 次请求)
|
|
561
565
|
const accountInfos = await this.aaManager.getMultipleAccountInfo(wallets.map((w) => w.address));
|
|
562
566
|
const nonceMap = new AANonceMap();
|
|
@@ -575,30 +579,62 @@ export class BundleExecutor {
|
|
|
575
579
|
// TODO: 如果需要,可以在这里检查并转账 ERC20 代币到 sender
|
|
576
580
|
}
|
|
577
581
|
});
|
|
578
|
-
// ✅ 构建买入 callData
|
|
579
|
-
const buyCallDatas = buyWeis.map((buyWei) => {
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
582
|
+
// ✅ 构建买入 callData:根据代币状态选择 Portal(内盘)或 DEX(外盘)
|
|
583
|
+
const buyCallDatas = buyWeis.map((buyWei, i) => {
|
|
584
|
+
if (isGraduated) {
|
|
585
|
+
// ✅ 已毕业:使用 DEX Router 买入
|
|
586
|
+
const deadline = Math.floor(Date.now() / 1000) + 1200; // 20 min
|
|
587
|
+
const dexType = (tokenState.lpFeeProfile !== undefined && tokenState.lpFeeProfile >= 0) ? 'V3' : 'V2';
|
|
588
|
+
const recipient = accountInfos[i].sender;
|
|
589
|
+
let swapData;
|
|
590
|
+
let routerAddress;
|
|
591
|
+
if (dexType === 'V3') {
|
|
592
|
+
routerAddress = POTATOSWAP_V3_ROUTER;
|
|
593
|
+
const v3Fee = lpFeeProfileToV3Fee(tokenState.lpFeeProfile);
|
|
594
|
+
swapData = encodeSwapExactETHForTokensV3({
|
|
595
|
+
tokenIn: WOKB,
|
|
596
|
+
tokenOut: tokenAddress,
|
|
597
|
+
fee: v3Fee,
|
|
598
|
+
recipient,
|
|
599
|
+
deadline,
|
|
600
|
+
amountIn: buyWei,
|
|
601
|
+
amountOutMinimum: 0n,
|
|
602
|
+
sqrtPriceLimitX96: 0n
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
routerAddress = POTATOSWAP_V2_ROUTER;
|
|
607
|
+
swapData = encodeSwapExactETHForTokensSupportingFee(0n, [WOKB, tokenAddress], recipient, deadline);
|
|
608
|
+
}
|
|
609
|
+
if (i < 3) {
|
|
610
|
+
console.log(`[BundleBuy] Wallet[${i}]: 使用 ${dexType} DEX Router 买入, fee=${dexType === 'V3' ? lpFeeProfileToV3Fee(tokenState.lpFeeProfile) : 'N/A'}`);
|
|
611
|
+
}
|
|
612
|
+
return encodeExecute(routerAddress, buyWei, swapData);
|
|
597
613
|
}
|
|
598
614
|
else {
|
|
599
|
-
//
|
|
600
|
-
const
|
|
601
|
-
|
|
615
|
+
// ✅ 未毕业:使用 Portal 的 swapExactInput
|
|
616
|
+
const portalIface = new Interface([
|
|
617
|
+
'function swapExactInput((address inputToken,address outputToken,uint256 inputAmount,uint256 minOutputAmount,bytes permitData) params) payable returns (uint256)',
|
|
618
|
+
]);
|
|
619
|
+
const swapData = portalIface.encodeFunctionData('swapExactInput', [
|
|
620
|
+
{
|
|
621
|
+
inputToken,
|
|
622
|
+
outputToken: tokenAddress,
|
|
623
|
+
inputAmount: buyWei,
|
|
624
|
+
minOutputAmount: 0n,
|
|
625
|
+
permitData: '0x',
|
|
626
|
+
},
|
|
627
|
+
]);
|
|
628
|
+
// ✅ ERC20 代币需要先 approve,使用 executeBatch 将 approve + swap 合并为一个 UserOp
|
|
629
|
+
if (useNativeToken) {
|
|
630
|
+
// 原生代币:直接 swap
|
|
631
|
+
return encodeExecute(this.portalAddress, buyWei, swapData);
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
// ERC20 代币:approve + swap 批量调用
|
|
635
|
+
const approveData = encodeApproveCall(this.portalAddress, buyWei);
|
|
636
|
+
return encodeExecuteBatch([inputToken, this.portalAddress], [0n, 0n], [approveData, swapData]);
|
|
637
|
+
}
|
|
602
638
|
}
|
|
603
639
|
});
|
|
604
640
|
const initCodes = accountInfos.map((ai, i) => ai.deployed ? '0x' : this.aaManager.generateInitCode(wallets[i].address));
|
|
@@ -730,6 +766,10 @@ export class BundleExecutor {
|
|
|
730
766
|
console.log('token:', tokenAddress);
|
|
731
767
|
console.log('owners:', wallets.length);
|
|
732
768
|
console.log('sellPercent:', sellPercent);
|
|
769
|
+
// ✅ 获取代币状态,判断是走内盘 Portal 还是外盘 DEX
|
|
770
|
+
const tokenState = await this.portalQuery.getTokenV7(tokenAddress);
|
|
771
|
+
const isGraduated = tokenState.status === 4; // DEX = 4 表示已毕业
|
|
772
|
+
console.log(`[BundleSell] Token status: ${tokenState.status} (${isGraduated ? 'DEX/已毕业' : '内盘'})`);
|
|
733
773
|
// ✅ 批量获取 accountInfo(含 sender/nonce/deployed),避免循环内重复 getAccountInfo
|
|
734
774
|
const accountInfos = await this.aaManager.getMultipleAccountInfo(wallets.map((w) => w.address));
|
|
735
775
|
const senders = accountInfos.map((ai) => ai.sender);
|
|
@@ -757,18 +797,71 @@ export class BundleExecutor {
|
|
|
757
797
|
touched[i] = true;
|
|
758
798
|
}
|
|
759
799
|
if (sellItems.length > 0) {
|
|
760
|
-
const
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
800
|
+
const effConfig = { ...(this.config ?? {}), ...(config ?? {}) };
|
|
801
|
+
if (isGraduated) {
|
|
802
|
+
// ✅ 已毕业:使用 DEX Router 卖出
|
|
803
|
+
const deadline = Math.floor(Date.now() / 1000) + 1200; // 20 min
|
|
804
|
+
const dexType = (tokenState.lpFeeProfile !== undefined && tokenState.lpFeeProfile >= 0) ? 'V3' : 'V2';
|
|
805
|
+
const signedSells = await mapWithConcurrency(sellItems, 4, async (it) => {
|
|
806
|
+
const i = it.i;
|
|
807
|
+
let swapData;
|
|
808
|
+
let routerAddress;
|
|
809
|
+
if (dexType === 'V3') {
|
|
810
|
+
routerAddress = POTATOSWAP_V3_ROUTER;
|
|
811
|
+
const v3Fee = lpFeeProfileToV3Fee(tokenState.lpFeeProfile);
|
|
812
|
+
swapData = encodeSwapExactTokensForETHV3({
|
|
813
|
+
tokenIn: tokenAddress,
|
|
814
|
+
tokenOut: WOKB,
|
|
815
|
+
fee: v3Fee,
|
|
816
|
+
recipient: it.sender,
|
|
817
|
+
deadline,
|
|
818
|
+
amountIn: it.sellAmount,
|
|
819
|
+
amountOutMinimum: 0n,
|
|
820
|
+
sqrtPriceLimitX96: 0n
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
else {
|
|
824
|
+
routerAddress = POTATOSWAP_V2_ROUTER;
|
|
825
|
+
swapData = encodeSwapExactTokensForETHSupportingFee(it.sellAmount, 0n, [tokenAddress, WOKB], it.sender, deadline);
|
|
826
|
+
}
|
|
827
|
+
if (i < 3) {
|
|
828
|
+
console.log(`[BundleSell] Wallet[${i}]: 使用 ${dexType} DEX Router 卖出, fee=${dexType === 'V3' ? lpFeeProfileToV3Fee(tokenState.lpFeeProfile) : 'N/A'}`);
|
|
829
|
+
}
|
|
830
|
+
const sellCallData = encodeExecute(routerAddress, 0n, swapData);
|
|
831
|
+
// 确保有足够余额
|
|
832
|
+
await this.aaManager.ensureSenderBalance(wallets[i], it.sender, parseOkb('0.0003'), `owner${i + 1}/sell-prefund`);
|
|
833
|
+
const { userOp } = await this.aaManager.buildUserOpWithFixedGas({
|
|
834
|
+
ownerWallet: wallets[i],
|
|
835
|
+
sender: it.sender,
|
|
836
|
+
callData: sellCallData,
|
|
837
|
+
nonce: it.nonce,
|
|
838
|
+
initCode: it.initCode,
|
|
839
|
+
deployed: it.initCode === '0x',
|
|
840
|
+
fixedGas: {
|
|
841
|
+
...(effConfig.fixedGas ?? {}),
|
|
842
|
+
callGasLimit: effConfig.fixedGas?.callGasLimit ?? DEFAULT_CALL_GAS_LIMIT_SELL,
|
|
843
|
+
},
|
|
844
|
+
});
|
|
845
|
+
const signed = await this.aaManager.signUserOp(userOp, wallets[i]);
|
|
846
|
+
return signed.userOp;
|
|
847
|
+
});
|
|
848
|
+
sellOps.push(...signedSells);
|
|
849
|
+
}
|
|
850
|
+
else {
|
|
851
|
+
// ✅ 未毕业:使用 Portal 卖出
|
|
852
|
+
const signedSells = await mapWithConcurrency(sellItems, 4, async (it) => {
|
|
853
|
+
const i = it.i;
|
|
854
|
+
const signed = await this.buildSellUserOp(wallets[i], tokenAddress, it.sellAmount, it.sender, it.nonce, it.initCode, false, // needApprove=false,假设已预先授权
|
|
855
|
+
`owner${i + 1}`);
|
|
856
|
+
return signed.userOp;
|
|
857
|
+
});
|
|
858
|
+
sellOps.push(...signedSells);
|
|
859
|
+
}
|
|
767
860
|
}
|
|
768
|
-
const
|
|
861
|
+
const effConfigForSell = { ...(this.config ?? {}), ...(config ?? {}) };
|
|
769
862
|
const sellResult = await this.runHandleOps('sellBundle', sellOps, bundlerSigner, beneficiary, {
|
|
770
|
-
gasLimit:
|
|
771
|
-
gasPrice:
|
|
863
|
+
gasLimit: effConfigForSell.gasLimit,
|
|
864
|
+
gasPrice: effConfigForSell.minGasPriceGwei ? ethers.parseUnits(effConfigForSell.minGasPriceGwei.toString(), 'gwei') : undefined
|
|
772
865
|
});
|
|
773
866
|
if (!sellResult) {
|
|
774
867
|
throw new Error('卖出交易失败');
|
|
@@ -819,8 +912,8 @@ export class BundleExecutor {
|
|
|
819
912
|
}
|
|
820
913
|
if (withdrawOps.length > 0) {
|
|
821
914
|
withdrawResult = await this.runHandleOps('withdrawBundle', withdrawOps, bundlerSigner, beneficiary, {
|
|
822
|
-
gasLimit:
|
|
823
|
-
gasPrice:
|
|
915
|
+
gasLimit: effConfigForSell.gasLimit,
|
|
916
|
+
gasPrice: effConfigForSell.minGasPriceGwei ? ethers.parseUnits(effConfigForSell.minGasPriceGwei.toString(), 'gwei') : undefined
|
|
824
917
|
}) ?? undefined;
|
|
825
918
|
}
|
|
826
919
|
}
|
|
@@ -1736,7 +1829,7 @@ export class BundleExecutor {
|
|
|
1736
1829
|
// ✅ 判断是否是最后一笔内盘买入(触发毕业的那笔)
|
|
1737
1830
|
const isGraduationTrigger = (i === curveBuyers.length - 1);
|
|
1738
1831
|
const callGasLimit = isGraduationTrigger
|
|
1739
|
-
?
|
|
1832
|
+
? 8000000n // 毕业触发交易需要更高 gas
|
|
1740
1833
|
: (effConfig.fixedGas?.callGasLimit ?? 500000n); // 其他买入使用前端配置或默认值
|
|
1741
1834
|
const buyOpRes = await aaManager.buildUserOpWithFixedGas({
|
|
1742
1835
|
ownerWallet: wallet,
|
|
@@ -1773,10 +1866,13 @@ export class BundleExecutor {
|
|
|
1773
1866
|
let routerAddress;
|
|
1774
1867
|
if (dexType === 'V3') {
|
|
1775
1868
|
routerAddress = POTATOSWAP_V3_ROUTER;
|
|
1869
|
+
// ✅ 使用 getTokenV7 获取的 lpFeeProfile 动态计算 V3 fee
|
|
1870
|
+
// lpFeeProfile: 0 → 2500 (0.25%), 1 → 100 (0.01%), 2 → 10000 (1%)
|
|
1871
|
+
const v3Fee = lpFeeProfileToV3Fee(tokenState.lpFeeProfile);
|
|
1776
1872
|
swapData = encodeSwapExactETHForTokensV3({
|
|
1777
1873
|
tokenIn: WOKB,
|
|
1778
1874
|
tokenOut: tokenAddress,
|
|
1779
|
-
fee:
|
|
1875
|
+
fee: v3Fee,
|
|
1780
1876
|
recipient: info.sender,
|
|
1781
1877
|
deadline,
|
|
1782
1878
|
amountIn: amount,
|
|
@@ -69,7 +69,7 @@ export declare const ENTRYPOINT_ABI: readonly ["function getNonce(address sender
|
|
|
69
69
|
/** SimpleAccount ABI */
|
|
70
70
|
export declare const SIMPLE_ACCOUNT_ABI: readonly ["function execute(address dest, uint256 value, bytes func) external", "function executeBatch(address[] calldata dest, bytes[] calldata func) external", "function executeBatch(address[] calldata dest, uint256[] calldata values, bytes[] calldata func) external"];
|
|
71
71
|
/** Flap Portal ABI */
|
|
72
|
-
export declare const PORTAL_ABI: readonly ["function swapExactInput((address inputToken,address outputToken,uint256 inputAmount,uint256 minOutputAmount,bytes permitData) params) payable returns (uint256)", "function swapExactInputV3((address inputToken,address outputToken,uint256 inputAmount,uint256 minOutputAmount,bytes permitData,bytes extensionData)) external payable returns (uint256)", "function buy(address token, address to, uint256 minAmount) external payable returns (uint256)", "function sell(address token, uint256 amount, uint256 minEth) external returns (uint256)", "function quoteExactInput((address inputToken,address outputToken,uint256 inputAmount)) external returns (uint256)", "function previewBuy(address token, uint256 ethAmount) external view returns (uint256)", "function previewSell(address token, uint256 tokenAmount) external view returns (uint256)", "function getTokenV5(address token) external view returns ((uint8,uint256,uint256,uint256,uint8,uint256,uint256,uint256,uint256,address,bool,bytes32))", "function getTokenV7(address token) external view returns ((uint8,uint256,uint256,uint256,uint8,uint256,uint256,uint256,uint256,address,bool,bytes32,
|
|
72
|
+
export declare const PORTAL_ABI: readonly ["function swapExactInput((address inputToken,address outputToken,uint256 inputAmount,uint256 minOutputAmount,bytes permitData) params) payable returns (uint256)", "function swapExactInputV3((address inputToken,address outputToken,uint256 inputAmount,uint256 minOutputAmount,bytes permitData,bytes extensionData)) external payable returns (uint256)", "function buy(address token, address to, uint256 minAmount) external payable returns (uint256)", "function sell(address token, uint256 amount, uint256 minEth) external returns (uint256)", "function quoteExactInput((address inputToken,address outputToken,uint256 inputAmount)) external returns (uint256)", "function previewBuy(address token, uint256 ethAmount) external view returns (uint256)", "function previewSell(address token, uint256 tokenAmount) external view returns (uint256)", "function getTokenV5(address token) external view returns ((uint8,uint256,uint256,uint256,uint8,uint256,uint256,uint256,uint256,address,bool,bytes32))", "function getTokenV7(address token) external view returns ((uint8,uint256,uint256,uint256,uint8,uint256,uint256,uint256,uint256,address,bool,bytes32,uint16,address,uint256,uint8))", "function newTokenV2((string name,string symbol,string meta,uint8 dexThresh,bytes32 salt,uint16 taxRate,uint8 migratorType,address quoteToken,uint256 quoteAmt,address beneficiary,bytes permitData) params) external payable returns (address)", "function newTokenV3((string name,string symbol,string meta,uint8 dexThresh,bytes32 salt,uint16 taxRate,uint8 migratorType,address quoteToken,uint256 quoteAmt,address beneficiary,bytes permitData,bytes32 extensionID,bytes extensionData) params) external payable returns (address)", "function newTokenV4((string name,string symbol,string meta,uint8 dexThresh,bytes32 salt,uint16 taxRate,uint8 migratorType,address quoteToken,uint256 quoteAmt,address beneficiary,bytes permitData,bytes32 extensionID,bytes extensionData,uint8 dexId,uint8 lpFeeProfile) params) external payable returns (address)"];
|
|
73
73
|
/** ERC20 ABI */
|
|
74
74
|
export declare const ERC20_ABI: readonly ["function balanceOf(address account) view returns (uint256)", "function allowance(address owner, address spender) view returns (uint256)", "function approve(address spender, uint256 amount) returns (bool)", "function transfer(address to, uint256 amount) returns (bool)", "function decimals() view returns (uint8)", "function symbol() view returns (string)", "function name() view returns (string)"];
|
|
75
75
|
/** PotatoSwap V2 Router ABI */
|
package/dist/xlayer/constants.js
CHANGED
|
@@ -115,7 +115,8 @@ export const PORTAL_ABI = [
|
|
|
115
115
|
'function previewBuy(address token, uint256 ethAmount) external view returns (uint256)',
|
|
116
116
|
'function previewSell(address token, uint256 tokenAmount) external view returns (uint256)',
|
|
117
117
|
'function getTokenV5(address token) external view returns ((uint8,uint256,uint256,uint256,uint8,uint256,uint256,uint256,uint256,address,bool,bytes32))',
|
|
118
|
-
|
|
118
|
+
// ✅ 修复:getTokenV7 返回 16 个字段,与 BSC portal.ts 一致
|
|
119
|
+
'function getTokenV7(address token) external view returns ((uint8,uint256,uint256,uint256,uint8,uint256,uint256,uint256,uint256,address,bool,bytes32,uint16,address,uint256,uint8))',
|
|
119
120
|
'function newTokenV2((string name,string symbol,string meta,uint8 dexThresh,bytes32 salt,uint16 taxRate,uint8 migratorType,address quoteToken,uint256 quoteAmt,address beneficiary,bytes permitData) params) external payable returns (address)',
|
|
120
121
|
'function newTokenV3((string name,string symbol,string meta,uint8 dexThresh,bytes32 salt,uint16 taxRate,uint8 migratorType,address quoteToken,uint256 quoteAmt,address beneficiary,bytes permitData,bytes32 extensionID,bytes extensionData) params) external payable returns (address)',
|
|
121
122
|
'function newTokenV4((string name,string symbol,string meta,uint8 dexThresh,bytes32 salt,uint16 taxRate,uint8 migratorType,address quoteToken,uint256 quoteAmt,address beneficiary,bytes permitData,bytes32 extensionID,bytes extensionData,uint8 dexId,uint8 lpFeeProfile) params) external payable returns (address)',
|
|
@@ -180,6 +180,8 @@ export declare class PortalQuery {
|
|
|
180
180
|
}>;
|
|
181
181
|
/**
|
|
182
182
|
* 获取代币状态 V7 (包含 lpFeeProfile)
|
|
183
|
+
*
|
|
184
|
+
* ✅ 修复:与 BSC portal.ts 对齐,使用 16 字段结构
|
|
183
185
|
*/
|
|
184
186
|
getTokenV7(tokenAddress: string): Promise<{
|
|
185
187
|
status: number;
|
|
@@ -194,7 +196,9 @@ export declare class PortalQuery {
|
|
|
194
196
|
quoteTokenAddress: string;
|
|
195
197
|
nativeToQuoteSwapEnabled: boolean;
|
|
196
198
|
extensionID: string;
|
|
197
|
-
|
|
199
|
+
taxRate: number;
|
|
200
|
+
pool: string;
|
|
201
|
+
progress: bigint;
|
|
198
202
|
lpFeeProfile: number;
|
|
199
203
|
}>;
|
|
200
204
|
/**
|
|
@@ -224,6 +224,8 @@ export class PortalQuery {
|
|
|
224
224
|
}
|
|
225
225
|
/**
|
|
226
226
|
* 获取代币状态 V7 (包含 lpFeeProfile)
|
|
227
|
+
*
|
|
228
|
+
* ✅ 修复:与 BSC portal.ts 对齐,使用 16 字段结构
|
|
227
229
|
*/
|
|
228
230
|
async getTokenV7(tokenAddress) {
|
|
229
231
|
const raw = await this.portal.getTokenV7(tokenAddress);
|
|
@@ -240,8 +242,10 @@ export class PortalQuery {
|
|
|
240
242
|
quoteTokenAddress: raw[9],
|
|
241
243
|
nativeToQuoteSwapEnabled: raw[10],
|
|
242
244
|
extensionID: raw[11],
|
|
243
|
-
|
|
244
|
-
|
|
245
|
+
taxRate: Number(raw[12]),
|
|
246
|
+
pool: raw[13],
|
|
247
|
+
progress: raw[14],
|
|
248
|
+
lpFeeProfile: Number(raw[15]),
|
|
245
249
|
};
|
|
246
250
|
}
|
|
247
251
|
/**
|