four-flap-meme-sdk 1.6.9 → 1.6.10
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/index.d.ts +1 -1
- package/dist/index.js +0 -2
- package/dist/xlayer/aa-account.d.ts +0 -6
- package/dist/xlayer/aa-account.js +0 -12
- package/dist/xlayer/dex-aa-sell.js +94 -145
- package/dist/xlayer/dex-bundle-swap.js +27 -5
- package/dist/xlayer/index.d.ts +0 -1
- package/dist/xlayer/index.js +0 -4
- package/dist/xlayer/portal-bundle-swap.js +27 -5
- package/dist/xlayer/types.d.ts +8 -2
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -54,4 +54,4 @@ submitDirectToRpcSequentialNoWait, // ✅ 顺序广播但不等待确认(用
|
|
|
54
54
|
submitDirectToRpcParallel, type DirectSubmitConfig, type DirectSubmitResult, type DirectTxResult } from './contracts/tm-bundle-merkle/submit.js';
|
|
55
55
|
export { directV2BatchBuy, directV2BatchSell, directV3BatchBuy, directV3BatchSell, getRouterAddress, DIRECT_ROUTERS, type DirectV2BuyParams, type DirectV2SellParams, type DirectV3BuyParams, type DirectV3SellParams, type DirectRouterResult, type DirectRouterSignConfig, type DexKey, type RouterVersion, } from './dex/index.js';
|
|
56
56
|
export * as XLayer from './xlayer/index.js';
|
|
57
|
-
export { bundleBuy as xlayerBundleBuy, bundleSell as xlayerBundleSell, bundleBuySell as xlayerBundleBuySell, bundleCreateBuySign as xlayerBundleCreateBuySign, bundleCreateToDexSign as xlayerBundleCreateToDexSign, bundleGraduateBuy as xlayerBundleGraduateBuy, bundleSwapSign as xlayerBundleSwapSign, bundleBatchSwapSign as xlayerBundleBatchSwapSign, createBundleExecutor as xlayerCreateBundleExecutor, BundleExecutor as XLayerBundleExecutor, makeVolume as xlayerMakeVolume, singleRoundVolume as xlayerSingleRoundVolume, createVolumeExecutor as xlayerCreateVolumeExecutor, VolumeExecutor as XLayerVolumeExecutor, makeBuyFirstVolume as xlayerMakeBuyFirstVolume, BuyFirstVolumeExecutor as XLayerBuyFirstVolumeExecutor, AAPortalBuyFirstExecutor as XLayerAAPortalBuyFirstExecutor, AADexBuyFirstExecutor as XLayerAADexBuyFirstExecutor, AAVolumeBuyFirstExecutor as XLayerAAVolumeBuyFirstExecutor, createAAVolumeBuyFirstExecutor as xlayerCreateAAVolumeBuyFirstExecutor, type VolumeBuyFirstParams as XLayerVolumeBuyFirstParams, type VolumeBuyFirstResult as XLayerVolumeBuyFirstResult, type VolumeContinuousParams as XLayerVolumeContinuousParams, type VolumeContinuousResult as XLayerVolumeContinuousResult, type VolumeProgress as XLayerVolumeProgress, createDexExecutor as xlayerCreateDexExecutor, createDexQuery as xlayerCreateDexQuery, quoteOkbToToken as xlayerQuoteOkbToToken, quoteTokenToOkb as xlayerQuoteTokenToOkb, DexExecutor as XLayerDexExecutor, DexQuery as XLayerDexQuery, buildDexBatchBuyOps, buildDexBatchBuyOpsV3, type DexBatchBuyParams, type DexBatchBuyV3Params, type DexBatchBuyResult, buildDexBatchSellOps, buildDexBatchSellOpsV3, type DexBatchSellParams, type DexBatchSellV3Params, type DexBatchSellResult,
|
|
57
|
+
export { bundleBuy as xlayerBundleBuy, bundleSell as xlayerBundleSell, bundleBuySell as xlayerBundleBuySell, bundleCreateBuySign as xlayerBundleCreateBuySign, bundleCreateToDexSign as xlayerBundleCreateToDexSign, bundleGraduateBuy as xlayerBundleGraduateBuy, bundleSwapSign as xlayerBundleSwapSign, bundleBatchSwapSign as xlayerBundleBatchSwapSign, createBundleExecutor as xlayerCreateBundleExecutor, BundleExecutor as XLayerBundleExecutor, makeVolume as xlayerMakeVolume, singleRoundVolume as xlayerSingleRoundVolume, createVolumeExecutor as xlayerCreateVolumeExecutor, VolumeExecutor as XLayerVolumeExecutor, makeBuyFirstVolume as xlayerMakeBuyFirstVolume, BuyFirstVolumeExecutor as XLayerBuyFirstVolumeExecutor, AAPortalBuyFirstExecutor as XLayerAAPortalBuyFirstExecutor, AADexBuyFirstExecutor as XLayerAADexBuyFirstExecutor, AAVolumeBuyFirstExecutor as XLayerAAVolumeBuyFirstExecutor, createAAVolumeBuyFirstExecutor as xlayerCreateAAVolumeBuyFirstExecutor, type VolumeBuyFirstParams as XLayerVolumeBuyFirstParams, type VolumeBuyFirstResult as XLayerVolumeBuyFirstResult, type VolumeContinuousParams as XLayerVolumeContinuousParams, type VolumeContinuousResult as XLayerVolumeContinuousResult, type VolumeProgress as XLayerVolumeProgress, createDexExecutor as xlayerCreateDexExecutor, createDexQuery as xlayerCreateDexQuery, quoteOkbToToken as xlayerQuoteOkbToToken, quoteTokenToOkb as xlayerQuoteTokenToOkb, DexExecutor as XLayerDexExecutor, DexQuery as XLayerDexQuery, buildDexBatchBuyOps, buildDexBatchBuyOpsV3, type DexBatchBuyParams, type DexBatchBuyV3Params, type DexBatchBuyResult, buildDexBatchSellOps, buildDexBatchSellOpsV3, type DexBatchSellParams, type DexBatchSellV3Params, type DexBatchSellResult, createAAAccountManager as xlayerCreateAAAccountManager, predictSender as xlayerPredictSender, createWallet as xlayerCreateWallet, AAAccountManager as XLayerAAAccountManager, generateAAWallets as xlayerGenerateAAWallets, generateAAWalletsFromMnemonic as xlayerGenerateAAWalletsFromMnemonic, predictSendersFromPrivateKeys as xlayerPredictSendersFromPrivateKeys, createBundlerClient as xlayerCreateBundlerClient, BundlerClient as XLayerBundlerClient, createPortalQuery as xlayerCreatePortalQuery, PortalQuery as XLayerPortalQuery, encodeBuyCall as xlayerEncodeBuyCall, encodeSellCall as xlayerEncodeSellCall, encodeApproveCall as xlayerEncodeApproveCall, parseOkb as xlayerParseOkb, formatOkb as xlayerFormatOkb, XLAYER_CHAIN_ID, FLAP_PORTAL as XLAYER_FLAP_PORTAL, ENTRYPOINT_V06 as XLAYER_ENTRYPOINT, SIMPLE_ACCOUNT_FACTORY as XLAYER_FACTORY, PARTICLE_BUNDLER_URL as XLAYER_BUNDLER_URL, WOKB as XLAYER_WOKB, POTATOSWAP_V2_ROUTER as XLAYER_POTATOSWAP_ROUTER, type XLayerConfig, type BundleBuyParams as XLayerBundleBuyParams, type BundleSellParams as XLayerBundleSellParams, type BundleBuySellParams as XLayerBundleBuySellParams, type BundleSwapParams as XLayerBundleSwapParams, type BundleSwapSignParams as XLayerBundleSwapSignParams, type BundleBatchSwapParams as XLayerBundleBatchSwapParams, type BundleBatchSwapSignParams as XLayerBundleBatchSwapSignParams, type VolumeParams as XLayerVolumeParams, type BundleBuyResult as XLayerBundleBuyResult, type BundleSellResult as XLayerBundleSellResult, type BundleBuySellResult as XLayerBundleBuySellResult, type BundleCreateBuySignParams as XLayerBundleCreateBuySignParams, type BundleCreateBuySignResult as XLayerBundleCreateBuySignResult, type BundleCreateToDexSignParams as XLayerBundleCreateToDexSignParams, type BundleCreateToDexSignResult as XLayerBundleCreateToDexSignResult, type BundleGraduateBuyParams as XLayerBundleGraduateBuyParams, type BundleGraduateBuyResult as XLayerBundleGraduateBuyResult, type BundleSwapResult as XLayerBundleSwapResult, type BundleSwapSignResult as XLayerBundleSwapSignResult, type BundleBatchSwapResult as XLayerBundleBatchSwapResult, type BundleBatchSwapSignResult as XLayerBundleBatchSwapSignResult, type VolumeResult as XLayerVolumeResult, type BuyFirstParams as XLayerBuyFirstParams, type BuyFirstResult as XLayerBuyFirstResult, type BuyFirstVolumeParams as XLayerBuyFirstVolumeParams, type BuyFirstVolumeResult as XLayerBuyFirstVolumeResult, type HandleOpsResult as XLayerHandleOpsResult, type AAAccount as XLayerAAAccount, type UserOperation as XLayerUserOperation, type GeneratedAAWallet as XLayerGeneratedAAWallet, type GenerateAAWalletsParams as XLayerGenerateAAWalletsParams, type GenerateAAWalletsResult as XLayerGenerateAAWalletsResult, } from './xlayer/index.js';
|
package/dist/index.js
CHANGED
|
@@ -116,8 +116,6 @@ createDexExecutor as xlayerCreateDexExecutor, createDexQuery as xlayerCreateDexQ
|
|
|
116
116
|
buildDexBatchBuyOps, buildDexBatchBuyOpsV3,
|
|
117
117
|
// ✅ AA 批量卖出(安全版:利润配置硬编码在 SDK 内部)
|
|
118
118
|
buildDexBatchSellOps, buildDexBatchSellOpsV3,
|
|
119
|
-
// ✅ AA 分发/归集(安全版:利润配置硬编码在 SDK 内部)
|
|
120
|
-
buildDisperseFromSingleOwnerOpsWithProfit, buildTransfersWithProfit,
|
|
121
119
|
// AA 账户管理
|
|
122
120
|
createAAAccountManager as xlayerCreateAAAccountManager, predictSender as xlayerPredictSender, createWallet as xlayerCreateWallet, AAAccountManager as XLayerAAAccountManager,
|
|
123
121
|
// 批量生成钱包工具
|
|
@@ -36,15 +36,9 @@ export declare class AAAccountManager {
|
|
|
36
36
|
private deployedSenderSet;
|
|
37
37
|
private feeDataCache?;
|
|
38
38
|
private readonly feeDataCacheTtlMs;
|
|
39
|
-
private entryPointSynced;
|
|
40
39
|
private defaultGasPolicy?;
|
|
41
40
|
private defaultFixedGas?;
|
|
42
41
|
constructor(config?: XLayerConfig);
|
|
43
|
-
/**
|
|
44
|
-
* ✅ 同步 EntryPoint(带缓存,避免重复 RPC 调用)
|
|
45
|
-
*
|
|
46
|
-
* 优化:EntryPoint 地址在运行期间几乎不会变化,只需同步一次
|
|
47
|
-
*/
|
|
48
42
|
private syncEntryPointIfNeeded;
|
|
49
43
|
/**
|
|
50
44
|
* 获取 Provider
|
|
@@ -29,8 +29,6 @@ export class AAAccountManager {
|
|
|
29
29
|
// PERF: 本文件的改造会尽量减少逐地址 RPC 调用
|
|
30
30
|
this.deployedSenderSet = new Set(); // key: senderLower(只缓存 deployed=true)
|
|
31
31
|
this.feeDataCacheTtlMs = 1200;
|
|
32
|
-
// ✅ 优化:缓存 EntryPoint 同步状态,避免重复 RPC 调用
|
|
33
|
-
this.entryPointSynced = false;
|
|
34
32
|
this.chainId = config.chainId ?? XLAYER_CHAIN_ID;
|
|
35
33
|
const rpcUrl = config.rpcUrl ?? DEFAULT_RPC_URL;
|
|
36
34
|
this.rpcUrl = rpcUrl;
|
|
@@ -57,15 +55,7 @@ export class AAAccountManager {
|
|
|
57
55
|
timeoutMs: config.timeoutMs,
|
|
58
56
|
});
|
|
59
57
|
}
|
|
60
|
-
/**
|
|
61
|
-
* ✅ 同步 EntryPoint(带缓存,避免重复 RPC 调用)
|
|
62
|
-
*
|
|
63
|
-
* 优化:EntryPoint 地址在运行期间几乎不会变化,只需同步一次
|
|
64
|
-
*/
|
|
65
58
|
async syncEntryPointIfNeeded() {
|
|
66
|
-
// ✅ 如果已同步过,直接返回(避免重复 RPC 调用)
|
|
67
|
-
if (this.entryPointSynced)
|
|
68
|
-
return;
|
|
69
59
|
try {
|
|
70
60
|
const eps = await this.bundler.getSupportedEntryPoints();
|
|
71
61
|
if (Array.isArray(eps) && eps.length > 0) {
|
|
@@ -76,8 +66,6 @@ export class AAAccountManager {
|
|
|
76
66
|
this.bundler.setEntryPoint(this.entryPointAddress);
|
|
77
67
|
}
|
|
78
68
|
}
|
|
79
|
-
// ✅ 标记为已同步
|
|
80
|
-
this.entryPointSynced = true;
|
|
81
69
|
}
|
|
82
70
|
catch { }
|
|
83
71
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { ethers } from 'ethers';
|
|
14
14
|
import { PROFIT_CONFIG } from '../utils/constants.js';
|
|
15
|
-
import { WOKB as XLAYER_WOKB
|
|
15
|
+
import { WOKB as XLAYER_WOKB } from './constants.js';
|
|
16
16
|
import { createAAAccountManager, createWallet, encodeExecute } from './aa-account.js';
|
|
17
17
|
import { DexQuery, encodeSwapExactTokensForETH } from './dex.js';
|
|
18
18
|
import { PortalQuery } from './portal-ops.js';
|
|
@@ -22,65 +22,13 @@ const V3_ROUTER02_SELL_ABI = [
|
|
|
22
22
|
'function multicall(uint256 deadline, bytes[] data) external payable returns (bytes[] results)',
|
|
23
23
|
'function unwrapWETH9(uint256 amountMinimum, address recipient) external payable',
|
|
24
24
|
];
|
|
25
|
-
// ============================================================================
|
|
26
|
-
// V3 Slot0 报价(用于 XLayer 没有 V3 Quoter 的情况)
|
|
27
|
-
// ============================================================================
|
|
28
|
-
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
29
|
-
const V3_FACTORY_ABI = [
|
|
30
|
-
'function getPool(address tokenA, address tokenB, uint24 fee) view returns (address pool)',
|
|
31
|
-
];
|
|
32
|
-
const V3_POOL_ABI = [
|
|
33
|
-
'function token0() view returns (address)',
|
|
34
|
-
'function token1() view returns (address)',
|
|
35
|
-
'function slot0() view returns (uint160 sqrtPriceX96,int24 tick,uint16 observationIndex,uint16 observationCardinality,uint16 observationCardinalityNext,uint8 feeProtocol,bool unlocked)',
|
|
36
|
-
];
|
|
37
|
-
async function quoteV3ViaSlot0(params) {
|
|
38
|
-
const { provider, tokenIn, amountIn, fee } = params;
|
|
39
|
-
if (amountIn <= 0n)
|
|
40
|
-
return 0n;
|
|
41
|
-
const wokbLower = XLAYER_WOKB.toLowerCase();
|
|
42
|
-
const tokenInLower = tokenIn.toLowerCase();
|
|
43
|
-
if (tokenInLower === wokbLower)
|
|
44
|
-
return amountIn;
|
|
45
|
-
const factory = new ethers.Contract(POTATOSWAP_V3_FACTORY, V3_FACTORY_ABI, provider);
|
|
46
|
-
const poolAddr = await factory.getPool(tokenIn, XLAYER_WOKB, fee);
|
|
47
|
-
if (!poolAddr || poolAddr.toLowerCase() === ZERO_ADDRESS.toLowerCase())
|
|
48
|
-
return 0n;
|
|
49
|
-
const pool = new ethers.Contract(poolAddr, V3_POOL_ABI, provider);
|
|
50
|
-
const [t0, t1, slot0] = await Promise.all([pool.token0(), pool.token1(), pool.slot0()]);
|
|
51
|
-
if (!t0 || !t1 || !slot0)
|
|
52
|
-
return 0n;
|
|
53
|
-
const sqrtPriceX96 = BigInt(slot0[0]);
|
|
54
|
-
if (sqrtPriceX96 <= 0n)
|
|
55
|
-
return 0n;
|
|
56
|
-
const Q96 = 2n ** 96n;
|
|
57
|
-
const priceX192 = sqrtPriceX96 * sqrtPriceX96; // Q192
|
|
58
|
-
const t0Lower = String(t0).toLowerCase();
|
|
59
|
-
const t1Lower = String(t1).toLowerCase();
|
|
60
|
-
let out;
|
|
61
|
-
if (t0Lower === tokenInLower && t1Lower === wokbLower) {
|
|
62
|
-
// price = token1/token0
|
|
63
|
-
out = (amountIn * priceX192) / (Q96 * Q96);
|
|
64
|
-
}
|
|
65
|
-
else if (t0Lower === wokbLower && t1Lower === tokenInLower) {
|
|
66
|
-
// price = token0/token1 => out = amountIn / (token1/token0)
|
|
67
|
-
out = (amountIn * (Q96 * Q96)) / priceX192;
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
return 0n;
|
|
71
|
-
}
|
|
72
|
-
return out > 0n ? out : 0n;
|
|
73
|
-
}
|
|
74
25
|
function encodeExactInputSingleForV3Sell(params) {
|
|
75
26
|
const iface = new ethers.Interface(V3_ROUTER02_SELL_ABI);
|
|
76
|
-
// ✅ 修复:address(2) 需要用完整的地址格式,而不是 bigint
|
|
77
|
-
// V3 Router 使用 address(2) 表示将 WETH 发送到 Router 自己,然后通过 unwrapWETH9 转回原生币
|
|
78
|
-
const ADDRESS_THIS = '0x0000000000000000000000000000000000000002';
|
|
79
27
|
const exactInputSingleData = iface.encodeFunctionData('exactInputSingle', [{
|
|
80
28
|
tokenIn: params.tokenIn,
|
|
81
29
|
tokenOut: params.tokenOut,
|
|
82
30
|
fee: params.fee,
|
|
83
|
-
recipient:
|
|
31
|
+
recipient: 2n, // address(2) = Router 自己(需要 unwrap)
|
|
84
32
|
amountIn: params.amountIn,
|
|
85
33
|
amountOutMinimum: params.amountOutMinimum,
|
|
86
34
|
sqrtPriceLimitX96: 0n,
|
|
@@ -167,36 +115,32 @@ export async function buildDexBatchSellOps(params) {
|
|
|
167
115
|
? new Map()
|
|
168
116
|
: await portalQuery.getMultipleAllowances(params.tokenAddress, senders, router);
|
|
169
117
|
// ✅ 预估卖出输出,计算利润
|
|
170
|
-
|
|
171
|
-
const
|
|
172
|
-
const str = String(amtStr || '0').trim();
|
|
173
|
-
if (!str || str === '0')
|
|
174
|
-
return 0n;
|
|
175
|
-
return ethers.parseUnits(str, dec);
|
|
176
|
-
});
|
|
177
|
-
const totalSellWei = sellWeis.reduce((sum, w) => sum + w, 0n);
|
|
178
|
-
// ⚡ 只调用一次报价(避免 N 次串行 RPC 调用)
|
|
118
|
+
const sellWeis = [];
|
|
119
|
+
const estimatedOkbOuts = [];
|
|
179
120
|
let totalEstimatedOkbOut = 0n;
|
|
180
|
-
|
|
121
|
+
for (let i = 0; i < params.sellAmounts.length; i++) {
|
|
122
|
+
const amtStr = String(params.sellAmounts[i] || '0').trim();
|
|
123
|
+
if (!amtStr || amtStr === '0') {
|
|
124
|
+
sellWeis.push(0n);
|
|
125
|
+
estimatedOkbOuts.push(0n);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const sellWei = ethers.parseUnits(amtStr, dec);
|
|
129
|
+
sellWeis.push(sellWei);
|
|
130
|
+
// 预估卖出输出
|
|
181
131
|
try {
|
|
182
|
-
|
|
132
|
+
const estimatedOut = await dexQuery.quoteTokenToOkb(sellWei, params.tokenAddress);
|
|
133
|
+
estimatedOkbOuts.push(estimatedOut);
|
|
134
|
+
totalEstimatedOkbOut += estimatedOut;
|
|
183
135
|
}
|
|
184
136
|
catch {
|
|
185
|
-
|
|
137
|
+
estimatedOkbOuts.push(0n);
|
|
186
138
|
}
|
|
187
139
|
}
|
|
188
|
-
// ✅
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
return (totalEstimatedOkbOut * w) / totalSellWei;
|
|
193
|
-
});
|
|
194
|
-
const profitWeiByWallet = estimatedOutByWallet.map((out) => {
|
|
195
|
-
if (out <= 0n)
|
|
196
|
-
return 0n;
|
|
197
|
-
return (out * BigInt(profitBps)) / 10000n;
|
|
198
|
-
});
|
|
199
|
-
const totalProfitWei = profitWeiByWallet.reduce((a, b) => a + b, 0n);
|
|
140
|
+
// ✅ 计算总利润
|
|
141
|
+
const totalProfitWei = totalEstimatedOkbOut > 0n
|
|
142
|
+
? (totalEstimatedOkbOut * BigInt(profitBps)) / 10000n
|
|
143
|
+
: 0n;
|
|
200
144
|
const unsignedOps = [];
|
|
201
145
|
const opOwnerIndex = [];
|
|
202
146
|
for (let i = 0; i < ownerWallets.length; i++) {
|
|
@@ -245,23 +189,35 @@ export async function buildDexBatchSellOps(params) {
|
|
|
245
189
|
});
|
|
246
190
|
unsignedOps.push(builtSell.userOp);
|
|
247
191
|
opOwnerIndex.push(i);
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
192
|
+
}
|
|
193
|
+
// ✅ 添加利润转账 UserOp(使用第一个有卖出的钱包)
|
|
194
|
+
if (totalProfitWei > 0n && ownerWallets.length > 0) {
|
|
195
|
+
// 找到第一个有卖出的钱包
|
|
196
|
+
let profitSenderIndex = 0;
|
|
197
|
+
for (let i = 0; i < sellWeis.length; i++) {
|
|
198
|
+
if (sellWeis[i] > 0n) {
|
|
199
|
+
profitSenderIndex = i;
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const profitSender = senders[profitSenderIndex] || '';
|
|
204
|
+
const profitOwner = ownerWallets[profitSenderIndex];
|
|
205
|
+
const profitAi = accountInfos[profitSenderIndex];
|
|
206
|
+
if (profitSender && profitOwner) {
|
|
207
|
+
const profitCallData = encodeExecute(profitRecipient, totalProfitWei, '0x');
|
|
208
|
+
const profitNonce = nextNonce(profitSender, BigInt(profitAi?.nonce ?? 0n));
|
|
209
|
+
const profitInitCode = consumeInitCode(profitSender, !!profitAi?.deployed, profitOwner.address);
|
|
210
|
+
const profitBuilt = await aaManager.buildUserOpWithFixedGas({
|
|
211
|
+
ownerWallet: profitOwner,
|
|
212
|
+
sender: profitSender,
|
|
257
213
|
callData: profitCallData,
|
|
258
|
-
nonce:
|
|
259
|
-
initCode:
|
|
260
|
-
deployed:
|
|
214
|
+
nonce: profitNonce,
|
|
215
|
+
initCode: profitInitCode,
|
|
216
|
+
deployed: profitInitCode === '0x',
|
|
261
217
|
fixedGas: { callGasLimit: 120000n },
|
|
262
218
|
});
|
|
263
|
-
unsignedOps.push(
|
|
264
|
-
opOwnerIndex.push(
|
|
219
|
+
unsignedOps.push(profitBuilt.userOp);
|
|
220
|
+
opOwnerIndex.push(profitSenderIndex);
|
|
265
221
|
}
|
|
266
222
|
}
|
|
267
223
|
// 签名
|
|
@@ -301,7 +257,6 @@ export async function buildDexBatchSellOpsV3(params) {
|
|
|
301
257
|
const profitBps = PROFIT_CONFIG.RATE_BPS; // 40 bps = 0.4%
|
|
302
258
|
const profitRecipient = PROFIT_CONFIG.RECIPIENT;
|
|
303
259
|
const aaManager = createAAAccountManager(config);
|
|
304
|
-
const provider = aaManager.getProvider();
|
|
305
260
|
const dexQuery = new DexQuery(config);
|
|
306
261
|
const portalQuery = new PortalQuery({ rpcUrl: config.rpcUrl, chainId: config.chainId });
|
|
307
262
|
const ownerWallets = params.ownerPrivateKeys.map((pk) => createWallet(String(pk), config));
|
|
@@ -345,50 +300,32 @@ export async function buildDexBatchSellOpsV3(params) {
|
|
|
345
300
|
? new Map()
|
|
346
301
|
: await portalQuery.getMultipleAllowances(params.tokenAddress, senders, router);
|
|
347
302
|
// ✅ 预估卖出输出,计算利润
|
|
348
|
-
|
|
349
|
-
const
|
|
350
|
-
const str = String(amtStr || '0').trim();
|
|
351
|
-
if (!str || str === '0')
|
|
352
|
-
return 0n;
|
|
353
|
-
return ethers.parseUnits(str, dec);
|
|
354
|
-
});
|
|
355
|
-
const totalSellWei = sellWeis.reduce((sum, w) => sum + w, 0n);
|
|
356
|
-
// ⚡ 只调用一次报价(避免 N 次串行 RPC 调用)
|
|
357
|
-
// ✅ 优先用 V3 slot0 报价,失败再回退 V2 getAmountsOut
|
|
303
|
+
const sellWeis = [];
|
|
304
|
+
const estimatedOkbOuts = [];
|
|
358
305
|
let totalEstimatedOkbOut = 0n;
|
|
359
|
-
|
|
306
|
+
for (let i = 0; i < params.sellAmounts.length; i++) {
|
|
307
|
+
const amtStr = String(params.sellAmounts[i] || '0').trim();
|
|
308
|
+
if (!amtStr || amtStr === '0') {
|
|
309
|
+
sellWeis.push(0n);
|
|
310
|
+
estimatedOkbOuts.push(0n);
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
const sellWei = ethers.parseUnits(amtStr, dec);
|
|
314
|
+
sellWeis.push(sellWei);
|
|
315
|
+
// 预估卖出输出(使用 V2 报价作为估算,因为 V3 报价需要额外逻辑)
|
|
360
316
|
try {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
amountIn: totalSellWei,
|
|
365
|
-
fee,
|
|
366
|
-
});
|
|
317
|
+
const estimatedOut = await dexQuery.quoteTokenToOkb(sellWei, params.tokenAddress);
|
|
318
|
+
estimatedOkbOuts.push(estimatedOut);
|
|
319
|
+
totalEstimatedOkbOut += estimatedOut;
|
|
367
320
|
}
|
|
368
321
|
catch {
|
|
369
|
-
|
|
370
|
-
}
|
|
371
|
-
if (totalEstimatedOkbOut <= 0n) {
|
|
372
|
-
try {
|
|
373
|
-
totalEstimatedOkbOut = await dexQuery.quoteTokenToOkb(totalSellWei, params.tokenAddress);
|
|
374
|
-
}
|
|
375
|
-
catch {
|
|
376
|
-
totalEstimatedOkbOut = 0n;
|
|
377
|
-
}
|
|
322
|
+
estimatedOkbOuts.push(0n);
|
|
378
323
|
}
|
|
379
324
|
}
|
|
380
|
-
// ✅
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
return (totalEstimatedOkbOut * w) / totalSellWei;
|
|
385
|
-
});
|
|
386
|
-
const profitWeiByWallet = estimatedOutByWallet.map((out) => {
|
|
387
|
-
if (out <= 0n)
|
|
388
|
-
return 0n;
|
|
389
|
-
return (out * BigInt(profitBps)) / 10000n;
|
|
390
|
-
});
|
|
391
|
-
const totalProfitWei = profitWeiByWallet.reduce((a, b) => a + b, 0n);
|
|
325
|
+
// ✅ 计算总利润
|
|
326
|
+
const totalProfitWei = totalEstimatedOkbOut > 0n
|
|
327
|
+
? (totalEstimatedOkbOut * BigInt(profitBps)) / 10000n
|
|
328
|
+
: 0n;
|
|
392
329
|
const unsignedOps = [];
|
|
393
330
|
const opOwnerIndex = [];
|
|
394
331
|
for (let i = 0; i < ownerWallets.length; i++) {
|
|
@@ -444,23 +381,35 @@ export async function buildDexBatchSellOpsV3(params) {
|
|
|
444
381
|
});
|
|
445
382
|
unsignedOps.push(builtSell.userOp);
|
|
446
383
|
opOwnerIndex.push(i);
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
384
|
+
}
|
|
385
|
+
// ✅ 添加利润转账 UserOp(使用第一个有卖出的钱包)
|
|
386
|
+
if (totalProfitWei > 0n && ownerWallets.length > 0) {
|
|
387
|
+
// 找到第一个有卖出的钱包
|
|
388
|
+
let profitSenderIndex = 0;
|
|
389
|
+
for (let i = 0; i < sellWeis.length; i++) {
|
|
390
|
+
if (sellWeis[i] > 0n) {
|
|
391
|
+
profitSenderIndex = i;
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
const profitSender = senders[profitSenderIndex] || '';
|
|
396
|
+
const profitOwner = ownerWallets[profitSenderIndex];
|
|
397
|
+
const profitAi = accountInfos[profitSenderIndex];
|
|
398
|
+
if (profitSender && profitOwner) {
|
|
399
|
+
const profitCallData = encodeExecute(profitRecipient, totalProfitWei, '0x');
|
|
400
|
+
const profitNonce = nextNonce(profitSender, BigInt(profitAi?.nonce ?? 0n));
|
|
401
|
+
const profitInitCode = consumeInitCode(profitSender, !!profitAi?.deployed, profitOwner.address);
|
|
402
|
+
const profitBuilt = await aaManager.buildUserOpWithFixedGas({
|
|
403
|
+
ownerWallet: profitOwner,
|
|
404
|
+
sender: profitSender,
|
|
456
405
|
callData: profitCallData,
|
|
457
|
-
nonce:
|
|
458
|
-
initCode:
|
|
459
|
-
deployed:
|
|
406
|
+
nonce: profitNonce,
|
|
407
|
+
initCode: profitInitCode,
|
|
408
|
+
deployed: profitInitCode === '0x',
|
|
460
409
|
fixedGas: { callGasLimit: 120000n },
|
|
461
410
|
});
|
|
462
|
-
unsignedOps.push(
|
|
463
|
-
opOwnerIndex.push(
|
|
411
|
+
unsignedOps.push(profitBuilt.userOp);
|
|
412
|
+
opOwnerIndex.push(profitSenderIndex);
|
|
464
413
|
}
|
|
465
414
|
}
|
|
466
415
|
// 签名
|
|
@@ -411,7 +411,8 @@ export class AADexSwapExecutor {
|
|
|
411
411
|
* ✅ 支持 ERC20 稳定币(USDT/USDC/USDT0)
|
|
412
412
|
*/
|
|
413
413
|
async bundleBatchSwapSign(params) {
|
|
414
|
-
const { dexKey, routerAddress: routerAddressIn, tokenAddress, sellerPrivateKey, buyerPrivateKeys, buyAmountsOkb, sellAmount, sellPercent = 100,
|
|
414
|
+
const { dexKey, routerAddress: routerAddressIn, tokenAddress, sellerPrivateKey, buyerPrivateKeys, buyAmountsOkb, sellAmount, sellPercent = 100, capitalMode = true, // ✅ 默认资金利用率模式(卖出→分发→买入)
|
|
415
|
+
disperseHopCount: disperseHopCountIn, payerPrivateKey, beneficiary: beneficiaryIn, payerStartNonce, routeAddress, skipApprovalCheck = false, tradeType, lpFeeProfile = 0, quoteToken, quoteTokenDecimals = 6, // XLayer USDT/USDC/USDT0 都是 6 位精度
|
|
415
416
|
} = params;
|
|
416
417
|
const effectiveConfig = { ...(this.config ?? {}), ...(params.config ?? {}) };
|
|
417
418
|
const { extractProfit, profitBps, profitRecipient } = resolveProfitSettings(effectiveConfig);
|
|
@@ -547,7 +548,8 @@ export class AADexSwapExecutor {
|
|
|
547
548
|
const profitCap = quotedSellOutWei > totalBuyWei ? (quotedSellOutWei - totalBuyWei) : 0n;
|
|
548
549
|
const profitWei = profitWeiRaw > profitCap ? profitCap : profitWeiRaw;
|
|
549
550
|
// ✅ 利润在分发阶段通过 AA 内部刮取
|
|
550
|
-
// ✅
|
|
551
|
+
// ✅ 资金分发逻辑(仅 capitalMode=true 时执行,对应 BSC flapQuickBatchSwapMerkle)
|
|
552
|
+
// capitalMode=false 时跳过分发,买方用自己的 OKB(对应 BSC flapBatchSwapMerkle)
|
|
551
553
|
// 原生代币模式:使用 Multicall3 批量转账
|
|
552
554
|
// ERC20 模式:使用 ERC20 transfer 批量转账
|
|
553
555
|
const buyerSenders = buyerAis.map(ai => ai.sender);
|
|
@@ -555,7 +557,7 @@ export class AADexSwapExecutor {
|
|
|
555
557
|
const hopCount = Math.min(hopCountRaw, buyerSenders.length);
|
|
556
558
|
const maxPerOp = Math.max(1, Math.floor(Number(effectiveConfig.maxTransfersPerUserOpNative ?? 30)));
|
|
557
559
|
const erc20Iface = new ethers.Interface(['function transfer(address to, uint256 amount) returns (bool)']);
|
|
558
|
-
if (buyerSenders.length > 0 && totalBuyWei > 0n) {
|
|
560
|
+
if (capitalMode && buyerSenders.length > 0 && totalBuyWei > 0n) {
|
|
559
561
|
if (hopCount <= 0) {
|
|
560
562
|
// ✅ 直接分发模式:将利润接收者加入分发列表(AA 内部刮取)
|
|
561
563
|
const items = buyerSenders.map((to, i) => ({ to, value: buyAmountsWei[i] ?? 0n })).filter(x => x.value > 0n);
|
|
@@ -689,6 +691,26 @@ export class AADexSwapExecutor {
|
|
|
689
691
|
}
|
|
690
692
|
}
|
|
691
693
|
}
|
|
694
|
+
else if (!capitalMode && extractProfit && profitWei > 0n) {
|
|
695
|
+
// ✅ capitalMode=false(买方自带资金):利润从 seller AA Sender 直接转账到利润地址(无多跳)
|
|
696
|
+
let profitCallData;
|
|
697
|
+
if (useNativeToken) {
|
|
698
|
+
profitCallData = encodeExecute(profitRecipient, profitWei, '0x');
|
|
699
|
+
}
|
|
700
|
+
else {
|
|
701
|
+
const transferData = erc20Iface.encodeFunctionData('transfer', [profitRecipient, profitWei]);
|
|
702
|
+
profitCallData = encodeExecute(quoteToken, 0n, transferData);
|
|
703
|
+
}
|
|
704
|
+
const signedProfitTransfer = await this.aaManager.buildUserOpWithState({
|
|
705
|
+
ownerWallet: sellerOwner,
|
|
706
|
+
sender: sellerAi.sender,
|
|
707
|
+
nonce: nonceMap.next(sellerAi.sender),
|
|
708
|
+
initCode: consumeInitCode(sellerAi.sender),
|
|
709
|
+
callData: profitCallData,
|
|
710
|
+
signOnly: true,
|
|
711
|
+
});
|
|
712
|
+
outOps.push(signedProfitTransfer.userOp);
|
|
713
|
+
}
|
|
692
714
|
// Batch Buy ops(分发后再执行)- ✅ 支持 V2/V3,支持 ERC20 稳定币
|
|
693
715
|
for (let i = 0; i < buyerOwners.length; i++) {
|
|
694
716
|
const ai = buyerAis[i];
|
|
@@ -736,7 +758,7 @@ export class AADexSwapExecutor {
|
|
|
736
758
|
beneficiary,
|
|
737
759
|
nonce: payerStartNonce,
|
|
738
760
|
});
|
|
739
|
-
// ✅ 利润已在 AA
|
|
761
|
+
// ✅ 利润已在 AA 内部刮取(capitalMode=true 在分发阶段;capitalMode=false 单独转账),不需要 Tail Tx
|
|
740
762
|
const signedTransactions = [signedHandleOps];
|
|
741
763
|
return {
|
|
742
764
|
signedTransactions,
|
|
@@ -756,7 +778,7 @@ export class AADexSwapExecutor {
|
|
|
756
778
|
profitRecipient,
|
|
757
779
|
profitWei: profitWei.toString(),
|
|
758
780
|
quotedSellOutWei: quotedSellOutWei.toString(),
|
|
759
|
-
disperseHopCount: String(hopCount),
|
|
781
|
+
disperseHopCount: capitalMode ? String(hopCount) : '0', // ✅ capitalMode=false 时无分发
|
|
760
782
|
},
|
|
761
783
|
};
|
|
762
784
|
}
|
package/dist/xlayer/index.d.ts
CHANGED
|
@@ -87,7 +87,6 @@ export { DexVolumeExecutor, createDexVolumeExecutor, makeDexVolume, type DexVolu
|
|
|
87
87
|
export { DexQuery, DexExecutor, createDexQuery, createDexExecutor, quoteOkbToToken, quoteTokenToOkb, encodeSwapExactETHForTokens, encodeSwapExactTokensForETH, encodeSwapExactTokensForTokens, encodeSwapExactETHForTokensSupportingFee, encodeSwapExactTokensForETHSupportingFee, encodeSwapExactTokensForTokensSupportingFee, type DexConfig, } from './dex.js';
|
|
88
88
|
export { buildDexBatchBuyOps, buildDexBatchBuyOpsV3, type DexBatchBuyParams, type DexBatchBuyV3Params, type DexBatchBuyResult, } from './dex-aa-buy.js';
|
|
89
89
|
export { buildDexBatchSellOps, buildDexBatchSellOpsV3, type DexBatchSellParams, type DexBatchSellV3Params, type DexBatchSellResult, } from './dex-aa-sell.js';
|
|
90
|
-
export { buildDisperseFromSingleOwnerOpsWithProfit, buildTransfersWithProfit, } from './aa-transfer-profit.js';
|
|
91
90
|
export declare const xlayer: {
|
|
92
91
|
bundleBuy: (params: import("./types.js").BundleBuyParams) => Promise<import("./types.js").BundleBuyResult>;
|
|
93
92
|
bundleSell: (params: import("./types.js").BundleSellParams) => Promise<import("./types.js").BundleSellResult>;
|
package/dist/xlayer/index.js
CHANGED
|
@@ -152,10 +152,6 @@ export { buildDexBatchBuyOps, buildDexBatchBuyOpsV3, } from './dex-aa-buy.js';
|
|
|
152
152
|
// ============================================================================
|
|
153
153
|
export { buildDexBatchSellOps, buildDexBatchSellOpsV3, } from './dex-aa-sell.js';
|
|
154
154
|
// ============================================================================
|
|
155
|
-
// ✅ AA 分发/归集(安全版:利润配置硬编码在 SDK 内部)
|
|
156
|
-
// ============================================================================
|
|
157
|
-
export { buildDisperseFromSingleOwnerOpsWithProfit, buildTransfersWithProfit, } from './aa-transfer-profit.js';
|
|
158
|
-
// ============================================================================
|
|
159
155
|
// 便捷重导出
|
|
160
156
|
// ============================================================================
|
|
161
157
|
// 将最常用的函数放在默认导出对象中
|
|
@@ -264,7 +264,8 @@ export class AAPortalSwapExecutor {
|
|
|
264
264
|
* ✅ 支持 ERC20 稳定币
|
|
265
265
|
*/
|
|
266
266
|
async bundleBatchSwapSign(params) {
|
|
267
|
-
const { tokenAddress, sellerPrivateKey, buyerPrivateKeys, buyAmountsOkb, sellAmount, sellPercent = 100,
|
|
267
|
+
const { tokenAddress, sellerPrivateKey, buyerPrivateKeys, buyAmountsOkb, sellAmount, sellPercent = 100, capitalMode = true, // ✅ 默认资金利用率模式(卖出→分发→买入)
|
|
268
|
+
disperseHopCount: disperseHopCountIn, payerPrivateKey, beneficiary: beneficiaryIn, payerStartNonce, routeAddress, skipApprovalCheck = false, quoteToken, quoteTokenDecimals = 6, } = params;
|
|
268
269
|
const effectiveConfig = { ...(this.config ?? {}), ...(params.config ?? {}) };
|
|
269
270
|
const { extractProfit, profitBps, profitRecipient } = resolveProfitSettings(effectiveConfig);
|
|
270
271
|
// ✅ ERC20 稳定币支持
|
|
@@ -359,13 +360,14 @@ export class AAPortalSwapExecutor {
|
|
|
359
360
|
const profitCap = quotedSellOutWei - totalBuyWei;
|
|
360
361
|
const profitWei = profitWeiRaw > profitCap ? profitCap : profitWeiRaw;
|
|
361
362
|
// ✅ 利润在分发阶段通过 AA 内部刮取
|
|
362
|
-
// ✅
|
|
363
|
+
// ✅ 资金分发逻辑(仅 capitalMode=true 时执行,对应 BSC flapQuickBatchSwapMerkle)
|
|
364
|
+
// capitalMode=false 时跳过分发,买方用自己的 OKB(对应 BSC flapBatchSwapMerkle)
|
|
363
365
|
const buyerSenders = buyerAis.map(ai => ai.sender);
|
|
364
366
|
const hopCountRaw = Math.max(0, Math.floor(Number(disperseHopCountIn ?? 0)));
|
|
365
367
|
const hopCount = Math.min(hopCountRaw, buyerSenders.length);
|
|
366
368
|
const maxPerOp = Math.max(1, Math.floor(Number(effectiveConfig.maxTransfersPerUserOpNative ?? 30)));
|
|
367
369
|
const erc20Iface = new ethers.Interface(['function transfer(address to, uint256 amount) returns (bool)']);
|
|
368
|
-
if (buyerSenders.length > 0 && totalBuyWei > 0n) {
|
|
370
|
+
if (capitalMode && buyerSenders.length > 0 && totalBuyWei > 0n) {
|
|
369
371
|
if (hopCount <= 0) {
|
|
370
372
|
// ✅ 直接分发模式:将利润接收者加入分发列表(AA 内部刮取)
|
|
371
373
|
const items = buyerSenders.map((to, i) => ({ to, value: buyAmountsWei[i] ?? 0n })).filter(x => x.value > 0n);
|
|
@@ -497,6 +499,26 @@ export class AAPortalSwapExecutor {
|
|
|
497
499
|
}
|
|
498
500
|
}
|
|
499
501
|
}
|
|
502
|
+
else if (!capitalMode && extractProfit && profitWei > 0n) {
|
|
503
|
+
// ✅ capitalMode=false(买方自带资金):利润从 seller AA Sender 直接转账到利润地址(无多跳)
|
|
504
|
+
let profitCallData;
|
|
505
|
+
if (useNativeToken) {
|
|
506
|
+
profitCallData = encodeExecute(profitRecipient, profitWei, '0x');
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
const transferData = erc20Iface.encodeFunctionData('transfer', [profitRecipient, profitWei]);
|
|
510
|
+
profitCallData = encodeExecute(quoteToken, 0n, transferData);
|
|
511
|
+
}
|
|
512
|
+
const signedProfitTransfer = await this.aaManager.buildUserOpWithState({
|
|
513
|
+
ownerWallet: sellerOwner,
|
|
514
|
+
sender: sellerAi.sender,
|
|
515
|
+
nonce: nonceMap.next(sellerAi.sender),
|
|
516
|
+
initCode: consumeInitCode(sellerAi.sender),
|
|
517
|
+
callData: profitCallData,
|
|
518
|
+
signOnly: true,
|
|
519
|
+
});
|
|
520
|
+
outOps.push(signedProfitTransfer.userOp);
|
|
521
|
+
}
|
|
500
522
|
// Batch Buy ops - ✅ 支持 ERC20 稳定币
|
|
501
523
|
for (let i = 0; i < buyerOwners.length; i++) {
|
|
502
524
|
const ai = buyerAis[i];
|
|
@@ -528,7 +550,7 @@ export class AAPortalSwapExecutor {
|
|
|
528
550
|
beneficiary,
|
|
529
551
|
nonce: payerStartNonce,
|
|
530
552
|
});
|
|
531
|
-
// ✅ 利润已在 AA
|
|
553
|
+
// ✅ 利润已在 AA 内部刮取(capitalMode=true 在分发阶段;capitalMode=false 单独转账),不需要 Tail Tx
|
|
532
554
|
const signedTransactions = [signedHandleOps];
|
|
533
555
|
return {
|
|
534
556
|
signedTransactions,
|
|
@@ -548,7 +570,7 @@ export class AAPortalSwapExecutor {
|
|
|
548
570
|
profitRecipient,
|
|
549
571
|
profitWei: profitWei.toString(),
|
|
550
572
|
quotedSellOutWei: quotedSellOutWei.toString(),
|
|
551
|
-
disperseHopCount: String(hopCount),
|
|
573
|
+
disperseHopCount: capitalMode ? String(hopCount) : '0', // ✅ capitalMode=false 时无分发
|
|
552
574
|
},
|
|
553
575
|
};
|
|
554
576
|
}
|
package/dist/xlayer/types.d.ts
CHANGED
|
@@ -505,11 +505,17 @@ export interface BundleBatchSwapParams {
|
|
|
505
505
|
/** ✅ ERC20 稳定币精度(默认 6,XLayer USDT/USDC/USDT0 都是 6 位精度) */
|
|
506
506
|
quoteTokenDecimals?: number;
|
|
507
507
|
/**
|
|
508
|
-
* ✅
|
|
508
|
+
* ✅ 资金利用率模式(对齐 BSC)
|
|
509
|
+
* - true(默认):卖出 → 分发 → 买入(资金来自卖出所得,对应 BSC flapQuickBatchSwapMerkle)
|
|
510
|
+
* - false:卖出 → 买入(买方自带资金,对应 BSC flapBatchSwapMerkle)
|
|
511
|
+
*/
|
|
512
|
+
capitalMode?: boolean;
|
|
513
|
+
/**
|
|
514
|
+
* ✅ 转账多跳数(AA 专用,仅 capitalMode=true 时生效)
|
|
509
515
|
*
|
|
510
516
|
* 用途:卖出所得 OKB 需要在同一笔 handleOps 内分发给多个买方 AA(Sender)。
|
|
511
517
|
* - 0:卖方 AA(Sender) 直接分发给所有买方 AA(Sender)
|
|
512
|
-
* - >0
|
|
518
|
+
* - >0:按"多跳"方式分发(实现位于 xlayer/*,不会影响 BSC bundle/merkle)
|
|
513
519
|
*/
|
|
514
520
|
disperseHopCount?: number;
|
|
515
521
|
/** 配置覆盖 */
|