four-flap-meme-sdk 1.5.28 → 1.5.29

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 CHANGED
@@ -53,4 +53,4 @@ export { submitDirectToRpc, submitDirectToRpcSequential, // ✅ 新增:顺序
53
53
  submitDirectToRpcParallel, type DirectSubmitConfig, type DirectSubmitResult, type DirectTxResult } from './contracts/tm-bundle-merkle/submit.js';
54
54
  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';
55
55
  export * as XLayer from './xlayer/index.js';
56
- export { bundleBuy as xlayerBundleBuy, bundleSell as xlayerBundleSell, bundleBuySell as xlayerBundleBuySell, bundleSwap as xlayerBundleSwap, bundleSwapSign as xlayerBundleSwapSign, bundleBatchSwap as xlayerBundleBatchSwap, bundleBatchSwapSign as xlayerBundleBatchSwapSign, createBundleExecutor as xlayerCreateBundleExecutor, BundleExecutor as XLayerBundleExecutor, makeVolume as xlayerMakeVolume, singleRoundVolume as xlayerSingleRoundVolume, createVolumeExecutor as xlayerCreateVolumeExecutor, VolumeExecutor as XLayerVolumeExecutor, createDexExecutor as xlayerCreateDexExecutor, createDexQuery as xlayerCreateDexQuery, quoteOkbToToken as xlayerQuoteOkbToToken, quoteTokenToOkb as xlayerQuoteTokenToOkb, DexExecutor as XLayerDexExecutor, DexQuery as XLayerDexQuery, 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 BundleSwapResult as XLayerBundleSwapResult, type BundleSwapSignResult as XLayerBundleSwapSignResult, type BundleBatchSwapResult as XLayerBundleBatchSwapResult, type BundleBatchSwapSignResult as XLayerBundleBatchSwapSignResult, type VolumeResult as XLayerVolumeResult, 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';
56
+ export { bundleBuy as xlayerBundleBuy, bundleSell as xlayerBundleSell, bundleBuySell as xlayerBundleBuySell, bundleSwapSign as xlayerBundleSwapSign, bundleBatchSwapSign as xlayerBundleBatchSwapSign, createBundleExecutor as xlayerCreateBundleExecutor, BundleExecutor as XLayerBundleExecutor, makeVolume as xlayerMakeVolume, singleRoundVolume as xlayerSingleRoundVolume, createVolumeExecutor as xlayerCreateVolumeExecutor, VolumeExecutor as XLayerVolumeExecutor, createDexExecutor as xlayerCreateDexExecutor, createDexQuery as xlayerCreateDexQuery, quoteOkbToToken as xlayerQuoteOkbToToken, quoteTokenToOkb as xlayerQuoteTokenToOkb, DexExecutor as XLayerDexExecutor, DexQuery as XLayerDexQuery, 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 BundleSwapResult as XLayerBundleSwapResult, type BundleSwapSignResult as XLayerBundleSwapSignResult, type BundleBatchSwapResult as XLayerBundleBatchSwapResult, type BundleBatchSwapSignResult as XLayerBundleBatchSwapSignResult, type VolumeResult as XLayerVolumeResult, 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
@@ -102,7 +102,7 @@ DIRECT_ROUTERS, } from './dex/index.js';
102
102
  export * as XLayer from './xlayer/index.js';
103
103
  export {
104
104
  // 捆绑交易
105
- bundleBuy as xlayerBundleBuy, bundleSell as xlayerBundleSell, bundleBuySell as xlayerBundleBuySell, bundleSwap as xlayerBundleSwap, bundleSwapSign as xlayerBundleSwapSign, bundleBatchSwap as xlayerBundleBatchSwap, bundleBatchSwapSign as xlayerBundleBatchSwapSign, createBundleExecutor as xlayerCreateBundleExecutor, BundleExecutor as XLayerBundleExecutor,
105
+ bundleBuy as xlayerBundleBuy, bundleSell as xlayerBundleSell, bundleBuySell as xlayerBundleBuySell, bundleSwapSign as xlayerBundleSwapSign, bundleBatchSwapSign as xlayerBundleBatchSwapSign, createBundleExecutor as xlayerCreateBundleExecutor, BundleExecutor as XLayerBundleExecutor,
106
106
  // 刷量
107
107
  makeVolume as xlayerMakeVolume, singleRoundVolume as xlayerSingleRoundVolume, createVolumeExecutor as xlayerCreateVolumeExecutor, VolumeExecutor as XLayerVolumeExecutor,
108
108
  // DEX 交易
@@ -165,6 +165,27 @@ export declare class AAAccountManager {
165
165
  useBundlerEstimate?: boolean;
166
166
  callGasLimit?: bigint;
167
167
  }): Promise<SignedUserOp>;
168
+ /**
169
+ * 获取 ERC20 代币余额
170
+ */
171
+ getErc20Balance(tokenAddress: string, accountAddress: string): Promise<bigint>;
172
+ /**
173
+ * 获取 ERC20 代币授权额度
174
+ */
175
+ getErc20Allowance(tokenAddress: string, owner: string, spender: string): Promise<bigint>;
176
+ /**
177
+ * 构建并签名 UserOperation(基于已有状态,支持 signOnly)
178
+ */
179
+ buildUserOpWithState(params: {
180
+ ownerWallet: Wallet;
181
+ sender: string;
182
+ nonce: bigint;
183
+ initCode: string;
184
+ callData: string;
185
+ signOnly?: boolean;
186
+ gasPolicy?: GasPolicy;
187
+ fixedGas?: FixedGasConfig;
188
+ }): Promise<SignedUserOp>;
168
189
  /**
169
190
  * 确保 Sender 有足够的 OKB 余额
170
191
  *
@@ -526,6 +526,58 @@ export class AAAccountManager {
526
526
  const signed = await this.signUserOp(userOp, params.ownerWallet);
527
527
  return { ...signed, prefundWei };
528
528
  }
529
+ /**
530
+ * 获取 ERC20 代币余额
531
+ */
532
+ async getErc20Balance(tokenAddress, accountAddress) {
533
+ const erc20 = new Contract(tokenAddress, ['function balanceOf(address) view returns (uint256)'], this.provider);
534
+ return await erc20.balanceOf(accountAddress);
535
+ }
536
+ /**
537
+ * 获取 ERC20 代币授权额度
538
+ */
539
+ async getErc20Allowance(tokenAddress, owner, spender) {
540
+ const erc20 = new Contract(tokenAddress, ['function allowance(address,address) view returns (uint256)'], this.provider);
541
+ return await erc20.allowance(owner, spender);
542
+ }
543
+ /**
544
+ * 构建并签名 UserOperation(基于已有状态,支持 signOnly)
545
+ */
546
+ async buildUserOpWithState(params) {
547
+ const { ownerWallet, sender, nonce, initCode, callData, signOnly = false } = params;
548
+ let userOp;
549
+ let prefundWei;
550
+ // 如果是 signOnly,强制使用 fixed gas 策略,避免触发 eth_estimateUserOperationGas (可能会因为没钱而失败)
551
+ const policy = signOnly ? 'fixed' : (params.gasPolicy || this.defaultGasPolicy || 'fixed');
552
+ const buildParams = {
553
+ ownerWallet,
554
+ sender,
555
+ nonce,
556
+ initCode,
557
+ callData,
558
+ };
559
+ if (policy === 'fixed') {
560
+ const result = await this.buildUserOpWithFixedGas({
561
+ ...buildParams,
562
+ deployed: initCode === '0x',
563
+ fixedGas: params.fixedGas || this.defaultFixedGas,
564
+ });
565
+ userOp = result.userOp;
566
+ prefundWei = result.prefundWei;
567
+ }
568
+ else {
569
+ // 默认用 localEstimate
570
+ const result = await this.buildUserOpWithLocalEstimate(buildParams);
571
+ userOp = result.userOp;
572
+ prefundWei = result.prefundWei;
573
+ }
574
+ // 只有非 signOnly 模式才执行 ensureSenderBalance
575
+ if (!signOnly) {
576
+ await this.ensureSenderBalance(ownerWallet, sender, prefundWei, 'buildUserOpWithState');
577
+ }
578
+ const signed = await this.signUserOp(userOp, ownerWallet);
579
+ return { ...signed, prefundWei };
580
+ }
529
581
  /**
530
582
  * 确保 Sender 有足够的 OKB 余额
531
583
  *
@@ -7,7 +7,7 @@
7
7
  * - 买卖一体化:买入 -> 授权 -> 卖出 -> 归集
8
8
  * - OKB 归集:将 sender 的 OKB 转回 owner
9
9
  */
10
- import type { XLayerConfig, BundleBuyParams, BundleBuyResult, BundleSellParams, BundleSellResult, BundleBuySellParams, BundleBuySellResult, BundleSwapParams, BundleSwapResult, BundleSwapSignParams, BundleSwapSignResult, BundleBatchSwapParams, BundleBatchSwapResult, BundleBatchSwapSignParams, BundleBatchSwapSignResult } from './types.js';
10
+ import type { XLayerConfig, BundleBuyParams, BundleBuyResult, BundleSellParams, BundleSellResult, BundleBuySellParams, BundleBuySellResult } from './types.js';
11
11
  import { AAAccountManager } from './aa-account.js';
12
12
  import { PortalQuery } from './portal-ops.js';
13
13
  /**
@@ -42,15 +42,6 @@ export declare class BundleExecutor {
42
42
  */
43
43
  private signHandleOpsTx;
44
44
  private getErc20Decimals;
45
- /**
46
- * 构建买入 UserOp(已知 sender/nonce/initCode 的版本)
47
- */
48
- private buildBuyUserOpWithState;
49
- /**
50
- * 构建换手所需的 UserOps(approve? + sell + buy),并返回元数据
51
- */
52
- private buildSwapOps;
53
- private buildBatchSwapOps;
54
45
  /**
55
46
  * 构建买入 UserOp
56
47
  */
@@ -94,24 +85,6 @@ export declare class BundleExecutor {
94
85
  * 完整流程:买入 -> 授权 -> 卖出 -> 归集
95
86
  */
96
87
  bundleBuySell(params: BundleBuySellParams): Promise<BundleBuySellResult>;
97
- /**
98
- * ✅ 捆绑换手(AA):卖方卖出 → 买方买入,同一笔 handleOps 原子执行
99
- */
100
- bundleSwap(params: BundleSwapParams): Promise<BundleSwapResult>;
101
- /**
102
- * ✅ 捆绑换手(AA,仅签名):返回 raw signed tx 列表(像 BSC 捆绑一样交给后端提交)
103
- * - signedTransactions[0] = handleOps tx(payer 签名)
104
- * - 如果传入 routeAddress,则 signedTransactions[1] = tailTx(nonce+1)
105
- */
106
- bundleSwapSign(params: BundleSwapSignParams): Promise<BundleSwapSignResult>;
107
- /**
108
- * ✅ 批量捆绑换手(AA):一卖多买,同一笔 handleOps 原子执行
109
- */
110
- bundleBatchSwap(params: BundleBatchSwapParams): Promise<BundleBatchSwapResult>;
111
- /**
112
- * ✅ 批量捆绑换手(AA,仅签名)
113
- */
114
- bundleBatchSwapSign(params: BundleBatchSwapSignParams): Promise<BundleBatchSwapSignResult>;
115
88
  }
116
89
  /**
117
90
  * 创建捆绑交易执行器
@@ -129,19 +102,3 @@ export declare function bundleSell(params: BundleSellParams): Promise<BundleSell
129
102
  * 快速捆绑买卖
130
103
  */
131
104
  export declare function bundleBuySell(params: BundleBuySellParams): Promise<BundleBuySellResult>;
132
- /**
133
- * 快速捆绑换手(链上执行)
134
- */
135
- export declare function bundleSwap(params: BundleSwapParams): Promise<BundleSwapResult>;
136
- /**
137
- * 快速捆绑换手(仅签名,返回 raw signed tx)
138
- */
139
- export declare function bundleSwapSign(params: BundleSwapSignParams): Promise<BundleSwapSignResult>;
140
- /**
141
- * 快速批量捆绑换手(链上执行)
142
- */
143
- export declare function bundleBatchSwap(params: BundleBatchSwapParams): Promise<BundleBatchSwapResult>;
144
- /**
145
- * 快速批量捆绑换手(仅签名)
146
- */
147
- export declare function bundleBatchSwapSign(params: BundleBatchSwapSignParams): Promise<BundleBatchSwapSignResult>;
@@ -7,7 +7,7 @@
7
7
  * - 买卖一体化:买入 -> 授权 -> 卖出 -> 归集
8
8
  * - OKB 归集:将 sender 的 OKB 转回 owner
9
9
  */
10
- import { Wallet, Interface, Contract, Transaction, ethers } from 'ethers';
10
+ import { Wallet, Interface, Contract } from 'ethers';
11
11
  import { FLAP_PORTAL, ENTRYPOINT_ABI, DEFAULT_CALL_GAS_LIMIT_SELL, DEFAULT_WITHDRAW_RESERVE, } from './constants.js';
12
12
  import { AAAccountManager, encodeExecute } from './aa-account.js';
13
13
  import { encodeBuyCall, encodeSellCall, encodeApproveCall, encodeTransferCall, PortalQuery, parseOkb, formatOkb, } from './portal-ops.js';
@@ -204,237 +204,6 @@ export class BundleExecutor {
204
204
  return 18;
205
205
  }
206
206
  }
207
- /**
208
- * 构建买入 UserOp(已知 sender/nonce/initCode 的版本)
209
- */
210
- async buildBuyUserOpWithState(params) {
211
- const swapData = encodeBuyCall(params.tokenAddress, params.buyAmountWei, 0n);
212
- const callData = encodeExecute(this.portalAddress, params.buyAmountWei, swapData);
213
- if (!params.signOnly) {
214
- // 估算前确保 sender 有足够余额(用于模拟);paymaster 场景会自动跳过
215
- await this.aaManager.ensureSenderBalance(params.ownerWallet, params.sender, params.buyAmountWei + parseOkb('0.0003'), `${params.ownerName ?? 'owner'}/swap-buy-prefund-before-estimate`);
216
- }
217
- const gasPolicyRaw = this.config.gasPolicy ?? 'bundlerEstimate';
218
- const gasPolicy = params.signOnly && gasPolicyRaw === 'bundlerEstimate' ? 'fixed' : gasPolicyRaw;
219
- const { userOp, prefundWei } = gasPolicy === 'fixed'
220
- ? await this.aaManager.buildUserOpWithFixedGas({
221
- ownerWallet: params.ownerWallet,
222
- sender: params.sender,
223
- callData,
224
- nonce: params.nonce,
225
- initCode: params.initCode,
226
- deployed: params.initCode === '0x',
227
- fixedGas: {
228
- ...(this.config.fixedGas ?? {}),
229
- callGasLimit: this.config.fixedGas?.callGasLimit ?? DEFAULT_CALL_GAS_LIMIT_BUY,
230
- },
231
- })
232
- : gasPolicy === 'localEstimate'
233
- ? await this.aaManager.buildUserOpWithLocalEstimate({
234
- ownerWallet: params.ownerWallet,
235
- sender: params.sender,
236
- callData,
237
- nonce: params.nonce,
238
- initCode: params.initCode,
239
- })
240
- : await this.aaManager.buildUserOpWithBundlerEstimate({
241
- ownerWallet: params.ownerWallet,
242
- sender: params.sender,
243
- callData,
244
- nonce: params.nonce,
245
- initCode: params.initCode,
246
- });
247
- if (!params.signOnly) {
248
- // 补足 prefund + 买入金额
249
- await this.aaManager.ensureSenderBalance(params.ownerWallet, params.sender, params.buyAmountWei + prefundWei + parseOkb('0.0002'), `${params.ownerName ?? 'owner'}/swap-buy-fund`);
250
- }
251
- const signed = await this.aaManager.signUserOp(userOp, params.ownerWallet);
252
- return { ...signed, prefundWei, ownerName: params.ownerName };
253
- }
254
- /**
255
- * 构建换手所需的 UserOps(approve? + sell + buy),并返回元数据
256
- */
257
- async buildSwapOps(params, opts) {
258
- const signOnly = opts?.signOnly ?? false;
259
- const { tokenAddress, sellerPrivateKey, buyerPrivateKey, sellAmount, sellPercent = 100, buyAmountOkb, slippageBps = 100, } = params;
260
- const provider = this.aaManager.getProvider();
261
- const sellerOwner = new Wallet(sellerPrivateKey, provider);
262
- const buyerOwner = new Wallet(buyerPrivateKey, provider);
263
- const [sellerAi, buyerAi] = await this.aaManager.getMultipleAccountInfo([sellerOwner.address, buyerOwner.address]);
264
- const sellerSender = sellerAi.sender;
265
- const buyerSender = buyerAi.sender;
266
- const nonceMap = new AANonceMap();
267
- nonceMap.init(sellerSender, sellerAi.nonce);
268
- nonceMap.init(buyerSender, buyerAi.nonce);
269
- // 计算卖出数量
270
- const decimals = await this.getErc20Decimals(tokenAddress);
271
- const sellerTokenBal = await this.portalQuery.getTokenBalance(tokenAddress, sellerSender);
272
- const sellAmountWei = (() => {
273
- if (sellAmount && String(sellAmount).trim().length > 0) {
274
- return ethers.parseUnits(String(sellAmount).trim(), decimals);
275
- }
276
- const pct = Math.max(0, Math.min(100, Math.floor(sellPercent)));
277
- return (sellerTokenBal * BigInt(pct)) / 100n;
278
- })();
279
- if (sellAmountWei <= 0n) {
280
- throw new Error('卖出数量为 0(请检查 sellAmount/sellPercent 或卖方余额)');
281
- }
282
- // 检查授权
283
- const allowance = await this.portalQuery.getAllowance(tokenAddress, sellerSender, this.portalAddress);
284
- const needApprove = allowance < sellAmountWei;
285
- // 计算买入 OKB 数量(默认用 previewSell(sellAmount) * (1 - slippage))
286
- const buyAmountWei = buyAmountOkb && String(buyAmountOkb).trim().length > 0
287
- ? parseOkb(String(buyAmountOkb).trim())
288
- : (() => {
289
- const bps = Math.max(0, Math.min(10000, Math.floor(slippageBps)));
290
- return 0n; // placeholder, filled below
291
- })();
292
- let finalBuyAmountWei = buyAmountWei;
293
- if (!(buyAmountOkb && String(buyAmountOkb).trim().length > 0)) {
294
- const quoted = await this.portalQuery.previewSell(tokenAddress, sellAmountWei);
295
- const bps = Math.max(0, Math.min(10000, Math.floor(slippageBps)));
296
- finalBuyAmountWei = (quoted * BigInt(10000 - bps)) / 10000n;
297
- }
298
- if (finalBuyAmountWei <= 0n) {
299
- throw new Error('买入 OKB 数量为 0(请检查 buyAmountOkb 或卖出预览输出)');
300
- }
301
- // initCode 处理:同一个 sender 的第一笔 op 才带 initCode
302
- const sellerInitCode0 = sellerAi.deployed ? '0x' : this.aaManager.generateInitCode(sellerOwner.address);
303
- const buyerInitCode0 = buyerAi.deployed ? '0x' : this.aaManager.generateInitCode(buyerOwner.address);
304
- let sellerInitCodeForNext = sellerInitCode0;
305
- let buyerInitCodeForNext = buyerInitCode0;
306
- const outOps = [];
307
- // 1) approve(可选)
308
- if (needApprove) {
309
- const approveNonce = nonceMap.next(sellerSender);
310
- const signedApprove = await this.buildApproveUserOp(sellerOwner, tokenAddress, this.portalAddress, sellerSender, approveNonce, sellerInitCodeForNext, 'seller', signOnly);
311
- outOps.push(signedApprove.userOp);
312
- sellerInitCodeForNext = '0x';
313
- }
314
- // 2) sell
315
- const sellNonce = nonceMap.next(sellerSender);
316
- const signedSell = await this.buildSellUserOp(sellerOwner, tokenAddress, sellAmountWei, sellerSender, sellNonce, sellerInitCodeForNext, needApprove, 'seller', signOnly);
317
- outOps.push(signedSell.userOp);
318
- sellerInitCodeForNext = '0x';
319
- // 3) buy(买方)
320
- const buyNonce = nonceMap.next(buyerSender);
321
- const signedBuy = await this.buildBuyUserOpWithState({
322
- ownerWallet: buyerOwner,
323
- sender: buyerSender,
324
- nonce: buyNonce,
325
- initCode: buyerInitCodeForNext,
326
- tokenAddress,
327
- buyAmountWei: finalBuyAmountWei,
328
- ownerName: 'buyer',
329
- signOnly,
330
- });
331
- outOps.push(signedBuy.userOp);
332
- buyerInitCodeForNext = '0x';
333
- return {
334
- ops: outOps,
335
- metadata: {
336
- sellerOwner: sellerOwner.address,
337
- sellerSender,
338
- buyerOwner: buyerOwner.address,
339
- buyerSender,
340
- sellAmountWei: sellAmountWei.toString(),
341
- buyAmountWei: finalBuyAmountWei.toString(),
342
- hasApprove: needApprove,
343
- },
344
- };
345
- }
346
- async buildBatchSwapOps(params, opts) {
347
- const signOnly = opts?.signOnly ?? false;
348
- const { tokenAddress, sellerPrivateKey, buyerPrivateKeys, buyAmountsOkb, sellAmount, sellPercent = 100, } = params;
349
- if (buyerPrivateKeys.length !== buyAmountsOkb.length) {
350
- throw new Error('buyerPrivateKeys 和 buyAmountsOkb 长度必须一致');
351
- }
352
- if (buyerPrivateKeys.length === 0) {
353
- throw new Error('至少需要 1 个 buyerPrivateKey');
354
- }
355
- const provider = this.aaManager.getProvider();
356
- const sellerOwner = new Wallet(sellerPrivateKey, provider);
357
- const buyerOwners = buyerPrivateKeys.map((pk) => new Wallet(pk, provider));
358
- const accountInfos = await this.aaManager.getMultipleAccountInfo([
359
- sellerOwner.address,
360
- ...buyerOwners.map((w) => w.address),
361
- ]);
362
- const sellerAi = accountInfos[0];
363
- const buyerAis = accountInfos.slice(1);
364
- const sellerSender = sellerAi.sender;
365
- const buyerSenders = buyerAis.map((ai) => ai.sender);
366
- const nonceMap = new AANonceMap();
367
- nonceMap.init(sellerSender, sellerAi.nonce);
368
- for (const ai of buyerAis)
369
- nonceMap.init(ai.sender, ai.nonce);
370
- // 计算卖出数量
371
- const decimals = await this.getErc20Decimals(tokenAddress);
372
- const sellerTokenBal = await this.portalQuery.getTokenBalance(tokenAddress, sellerSender);
373
- const sellAmountWei = (() => {
374
- if (sellAmount && String(sellAmount).trim().length > 0) {
375
- return ethers.parseUnits(String(sellAmount).trim(), decimals);
376
- }
377
- const pct = Math.max(0, Math.min(100, Math.floor(sellPercent)));
378
- return (sellerTokenBal * BigInt(pct)) / 100n;
379
- })();
380
- if (sellAmountWei <= 0n) {
381
- throw new Error('卖出数量为 0(请检查 sellAmount/sellPercent 或卖方余额)');
382
- }
383
- // 检查授权
384
- const allowance = await this.portalQuery.getAllowance(tokenAddress, sellerSender, this.portalAddress);
385
- const needApprove = allowance < sellAmountWei;
386
- // initCode:同一个 sender 的第一笔 op 才带 initCode
387
- let sellerInitCodeForNext = sellerAi.deployed ? '0x' : this.aaManager.generateInitCode(sellerOwner.address);
388
- const buyerInitCodes = buyerAis.map((ai, idx) => ai.deployed ? '0x' : this.aaManager.generateInitCode(buyerOwners[idx].address));
389
- const outOps = [];
390
- // 1) approve(可选)
391
- if (needApprove) {
392
- const approveNonce = nonceMap.next(sellerSender);
393
- const signedApprove = await this.buildApproveUserOp(sellerOwner, tokenAddress, this.portalAddress, sellerSender, approveNonce, sellerInitCodeForNext, 'seller', signOnly);
394
- outOps.push(signedApprove.userOp);
395
- sellerInitCodeForNext = '0x';
396
- }
397
- // 2) sell(一次)
398
- const sellNonce = nonceMap.next(sellerSender);
399
- const signedSell = await this.buildSellUserOp(sellerOwner, tokenAddress, sellAmountWei, sellerSender, sellNonce, sellerInitCodeForNext, needApprove, 'seller', signOnly);
400
- outOps.push(signedSell.userOp);
401
- sellerInitCodeForNext = '0x';
402
- // 3) buyers buy(多笔)
403
- const buyAmountsWei = buyAmountsOkb.map((v) => parseOkb(String(v)));
404
- const signedBuys = await mapWithConcurrency(buyerOwners, 4, async (w, i) => {
405
- const sender = buyerSenders[i];
406
- const initCode = buyerInitCodes[i];
407
- const nonce = nonceMap.next(sender);
408
- const amountWei = buyAmountsWei[i] ?? 0n;
409
- if (amountWei <= 0n) {
410
- throw new Error(`buyAmountsOkb[${i}] 无效(<=0)`);
411
- }
412
- return await this.buildBuyUserOpWithState({
413
- ownerWallet: w,
414
- sender,
415
- nonce,
416
- initCode,
417
- tokenAddress,
418
- buyAmountWei: amountWei,
419
- ownerName: `buyer${i + 1}`,
420
- signOnly,
421
- });
422
- });
423
- for (const s of signedBuys)
424
- outOps.push(s.userOp);
425
- return {
426
- ops: outOps,
427
- metadata: {
428
- sellerOwner: sellerOwner.address,
429
- sellerSender,
430
- buyerOwners: buyerOwners.map((w) => w.address),
431
- buyerSenders,
432
- sellAmountWei: sellAmountWei.toString(),
433
- buyAmountsWei: buyAmountsWei.map((x) => x.toString()),
434
- hasApprove: needApprove,
435
- },
436
- };
437
- }
438
207
  /**
439
208
  * 构建买入 UserOp
440
209
  */
@@ -1079,142 +848,6 @@ export class BundleExecutor {
1079
848
  const finalBalances = await this.portalQuery.getMultipleOkbBalances(senders);
1080
849
  return { buyResult, sellResult, withdrawResult, finalBalances };
1081
850
  }
1082
- /**
1083
- * ✅ 捆绑换手(AA):卖方卖出 → 买方买入,同一笔 handleOps 原子执行
1084
- */
1085
- async bundleSwap(params) {
1086
- const provider = this.aaManager.getProvider();
1087
- const payerWallet = new Wallet(params.sellerPrivateKey, provider);
1088
- const beneficiary = payerWallet.address;
1089
- const { ops, metadata } = await this.buildSwapOps(params, { signOnly: false });
1090
- const swapResult = await this.runHandleOps('swapBundle', ops, payerWallet, beneficiary);
1091
- if (!swapResult)
1092
- throw new Error('换手交易失败');
1093
- return { swapResult, metadata };
1094
- }
1095
- /**
1096
- * ✅ 捆绑换手(AA,仅签名):返回 raw signed tx 列表(像 BSC 捆绑一样交给后端提交)
1097
- * - signedTransactions[0] = handleOps tx(payer 签名)
1098
- * - 如果传入 routeAddress,则 signedTransactions[1] = tailTx(nonce+1)
1099
- */
1100
- async bundleSwapSign(params) {
1101
- const provider = this.aaManager.getProvider();
1102
- const payerPk = (params.payerPrivateKey ?? params.sellerPrivateKey);
1103
- const payerWallet = new Wallet(payerPk, provider);
1104
- const beneficiary = params.beneficiary ?? payerWallet.address;
1105
- const { ops, metadata } = await this.buildSwapOps(params, { signOnly: true });
1106
- // 先签 handleOps
1107
- const signedHandleOpsTx = await this.signHandleOpsTx({
1108
- ops,
1109
- payerWallet,
1110
- beneficiary,
1111
- nonce: params.payerStartNonce,
1112
- gasLimit: params.handleOpsGasLimit,
1113
- });
1114
- const signedTxs = [signedHandleOpsTx];
1115
- // 可选:追加 route 尾交易(nonce+1)
1116
- if (params.routeAddress) {
1117
- const tx = Transaction.from(signedHandleOpsTx);
1118
- const nonce = Number(tx.nonce);
1119
- const chainId = Number(tx.chainId || 0);
1120
- if (!Number.isFinite(nonce) || nonce < 0)
1121
- throw new Error('解析 handleOps nonce 失败');
1122
- if (!Number.isFinite(chainId) || chainId <= 0)
1123
- throw new Error('解析 handleOps chainId 失败');
1124
- const tailTx = await payerWallet.signTransaction({
1125
- to: params.routeAddress,
1126
- value: 0n,
1127
- data: '0x',
1128
- nonce: nonce + 1,
1129
- gasLimit: 21000n,
1130
- gasPrice: tx.gasPrice ?? undefined,
1131
- chainId,
1132
- type: 0,
1133
- });
1134
- signedTxs.push(tailTx);
1135
- }
1136
- return {
1137
- signedTransactions: signedTxs,
1138
- metadata: {
1139
- payer: payerWallet.address,
1140
- beneficiary,
1141
- sellerOwner: metadata.sellerOwner,
1142
- sellerSender: metadata.sellerSender,
1143
- buyerOwner: metadata.buyerOwner,
1144
- buyerSender: metadata.buyerSender,
1145
- sellAmountWei: metadata.sellAmountWei,
1146
- buyAmountWei: metadata.buyAmountWei,
1147
- hasApprove: metadata.hasApprove,
1148
- routeAddress: params.routeAddress,
1149
- },
1150
- };
1151
- }
1152
- /**
1153
- * ✅ 批量捆绑换手(AA):一卖多买,同一笔 handleOps 原子执行
1154
- */
1155
- async bundleBatchSwap(params) {
1156
- const provider = this.aaManager.getProvider();
1157
- const payerWallet = new Wallet(params.sellerPrivateKey, provider);
1158
- const beneficiary = payerWallet.address;
1159
- const { ops, metadata } = await this.buildBatchSwapOps(params, { signOnly: false });
1160
- const swapResult = await this.runHandleOps('batchSwapBundle', ops, payerWallet, beneficiary);
1161
- if (!swapResult)
1162
- throw new Error('批量换手交易失败');
1163
- return { swapResult, metadata };
1164
- }
1165
- /**
1166
- * ✅ 批量捆绑换手(AA,仅签名)
1167
- */
1168
- async bundleBatchSwapSign(params) {
1169
- const provider = this.aaManager.getProvider();
1170
- const payerPk = (params.payerPrivateKey ?? params.sellerPrivateKey);
1171
- const payerWallet = new Wallet(payerPk, provider);
1172
- const beneficiary = params.beneficiary ?? payerWallet.address;
1173
- const { ops, metadata } = await this.buildBatchSwapOps(params, { signOnly: true });
1174
- const signedHandleOpsTx = await this.signHandleOpsTx({
1175
- ops,
1176
- payerWallet,
1177
- beneficiary,
1178
- nonce: params.payerStartNonce,
1179
- gasLimit: params.handleOpsGasLimit,
1180
- });
1181
- const signedTxs = [signedHandleOpsTx];
1182
- if (params.routeAddress) {
1183
- const tx = Transaction.from(signedHandleOpsTx);
1184
- const nonce = Number(tx.nonce);
1185
- const chainId = Number(tx.chainId || 0);
1186
- if (!Number.isFinite(nonce) || nonce < 0)
1187
- throw new Error('解析 handleOps nonce 失败');
1188
- if (!Number.isFinite(chainId) || chainId <= 0)
1189
- throw new Error('解析 handleOps chainId 失败');
1190
- const tailTx = await payerWallet.signTransaction({
1191
- to: params.routeAddress,
1192
- value: 0n,
1193
- data: '0x',
1194
- nonce: nonce + 1,
1195
- gasLimit: 21000n,
1196
- gasPrice: tx.gasPrice ?? undefined,
1197
- chainId,
1198
- type: 0,
1199
- });
1200
- signedTxs.push(tailTx);
1201
- }
1202
- return {
1203
- signedTransactions: signedTxs,
1204
- metadata: {
1205
- payer: payerWallet.address,
1206
- beneficiary,
1207
- sellerOwner: metadata.sellerOwner,
1208
- sellerSender: metadata.sellerSender,
1209
- buyerOwners: metadata.buyerOwners,
1210
- buyerSenders: metadata.buyerSenders,
1211
- sellAmountWei: metadata.sellAmountWei,
1212
- buyAmountsWei: metadata.buyAmountsWei,
1213
- hasApprove: metadata.hasApprove,
1214
- routeAddress: params.routeAddress,
1215
- },
1216
- };
1217
- }
1218
851
  }
1219
852
  // ============================================================================
1220
853
  // 便捷函数
@@ -1246,31 +879,3 @@ export async function bundleBuySell(params) {
1246
879
  const executor = createBundleExecutor(params.config);
1247
880
  return executor.bundleBuySell(params);
1248
881
  }
1249
- /**
1250
- * 快速捆绑换手(链上执行)
1251
- */
1252
- export async function bundleSwap(params) {
1253
- const executor = createBundleExecutor(params.config);
1254
- return executor.bundleSwap(params);
1255
- }
1256
- /**
1257
- * 快速捆绑换手(仅签名,返回 raw signed tx)
1258
- */
1259
- export async function bundleSwapSign(params) {
1260
- const executor = createBundleExecutor(params.config);
1261
- return executor.bundleSwapSign(params);
1262
- }
1263
- /**
1264
- * 快速批量捆绑换手(链上执行)
1265
- */
1266
- export async function bundleBatchSwap(params) {
1267
- const executor = createBundleExecutor(params.config);
1268
- return executor.bundleBatchSwap(params);
1269
- }
1270
- /**
1271
- * 快速批量捆绑换手(仅签名)
1272
- */
1273
- export async function bundleBatchSwapSign(params) {
1274
- const executor = createBundleExecutor(params.config);
1275
- return executor.bundleBatchSwapSign(params);
1276
- }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * XLayer AA 外盘 (PotatoSwap/DEX) 捆绑换手实现
3
+ */
4
+ import { XLayerConfig, BundleSwapSignParams, BundleSwapSignResult, BundleBatchSwapSignParams, BundleBatchSwapSignResult } from './types.js';
5
+ /**
6
+ * XLayer AA 外盘换手执行器
7
+ */
8
+ export declare class AADexSwapExecutor {
9
+ private aaManager;
10
+ private dexQuery;
11
+ private bundleExecutor;
12
+ private config;
13
+ constructor(config?: XLayerConfig);
14
+ /**
15
+ * 获取 Router 地址
16
+ */
17
+ private getEffectiveRouter;
18
+ /**
19
+ * AA 外盘单钱包换手签名
20
+ */
21
+ bundleSwapSign(params: BundleSwapSignParams & {
22
+ skipApprovalCheck?: boolean;
23
+ }): Promise<BundleSwapSignResult>;
24
+ /**
25
+ * AA 外盘批量换手签名
26
+ */
27
+ bundleBatchSwapSign(params: BundleBatchSwapSignParams & {
28
+ skipApprovalCheck?: boolean;
29
+ }): Promise<BundleBatchSwapSignResult>;
30
+ }