four-flap-meme-sdk 1.5.33 → 1.5.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,404 @@
1
+ /**
2
+ * XLayer AA Buy-First(外盘 / PotatoSwap V2)
3
+ *
4
+ * 对齐 BSC buy-first 思路:
5
+ * - 买方先买入(OKB -> token)
6
+ * - 卖方再卖出等值 token(token -> OKB,卖方需要预持仓)
7
+ * - ✅ 利润:在卖出后归集阶段刮取
8
+ */
9
+ import { Contract, Interface, Wallet, ethers } from 'ethers';
10
+ import { AANonceMap } from './types.js';
11
+ import { ENTRYPOINT_ABI, DEFAULT_WITHDRAW_RESERVE, POTATOSWAP_V2_ROUTER, WOKB, } from './constants.js';
12
+ import { AAAccountManager, encodeExecute, encodeExecuteBatch } from './aa-account.js';
13
+ import { DexQuery, encodeSwapExactETHForTokensSupportingFee, encodeSwapExactTokensForETHSupportingFee } from './dex.js';
14
+ import { PortalQuery, encodeApproveCall, parseOkb } from './portal-ops.js';
15
+ import { mapWithConcurrency } from '../utils/concurrency.js';
16
+ import { PROFIT_CONFIG } from '../utils/constants.js';
17
+ // 固定 gas(用于减少 RPC 与提高可控性)
18
+ const DEFAULT_CALL_GAS_LIMIT_BUY = 450000n;
19
+ const DEFAULT_CALL_GAS_LIMIT_SELL = 450000n;
20
+ const DEFAULT_CALL_GAS_LIMIT_APPROVE = 200000n;
21
+ const DEFAULT_CALL_GAS_LIMIT_WITHDRAW = 120000n;
22
+ function getDexDeadline(minutes = 20) {
23
+ return Math.floor(Date.now() / 1000) + minutes * 60;
24
+ }
25
+ function resolveProfitSettings(config) {
26
+ const extractProfit = config?.extractProfit !== false; // 默认 true
27
+ const profitBpsRaw = config?.profitBps ?? PROFIT_CONFIG.RATE_BPS_SWAP;
28
+ const profitBps = Math.max(0, Math.min(10000, Number(profitBpsRaw)));
29
+ const profitRecipient = config?.profitRecipient ?? PROFIT_CONFIG.RECIPIENT;
30
+ return { extractProfit, profitBps, profitRecipient };
31
+ }
32
+ function calculateProfitWei(amountWei, profitBps) {
33
+ if (amountWei <= 0n)
34
+ return 0n;
35
+ if (!Number.isFinite(profitBps) || profitBps <= 0)
36
+ return 0n;
37
+ return (amountWei * BigInt(profitBps)) / 10000n;
38
+ }
39
+ function shuffle(arr) {
40
+ const a = arr.slice();
41
+ for (let i = a.length - 1; i > 0; i--) {
42
+ const j = Math.floor(Math.random() * (i + 1));
43
+ [a[i], a[j]] = [a[j], a[i]];
44
+ }
45
+ return a;
46
+ }
47
+ function splitAmount(totalAmount, count) {
48
+ if (count <= 0)
49
+ throw new Error('拆分份数必须大于 0');
50
+ if (count === 1)
51
+ return [totalAmount];
52
+ if (totalAmount <= 0n)
53
+ return new Array(count).fill(0n);
54
+ const weights = [];
55
+ for (let i = 0; i < count; i++) {
56
+ const w = BigInt(500000 + Math.floor(Math.random() * 1000000)); // [0.5,1.5)
57
+ weights.push(w);
58
+ }
59
+ const totalWeight = weights.reduce((a, b) => a + b, 0n);
60
+ const amounts = [];
61
+ let allocated = 0n;
62
+ for (let i = 0; i < count - 1; i++) {
63
+ const amt = (totalAmount * weights[i]) / totalWeight;
64
+ amounts.push(amt);
65
+ allocated += amt;
66
+ }
67
+ amounts.push(totalAmount - allocated);
68
+ return shuffle(amounts);
69
+ }
70
+ export class AADexBuyFirstExecutor {
71
+ constructor(config = {}) {
72
+ this.config = config;
73
+ this.aaManager = new AAAccountManager(config);
74
+ this.portalQuery = new PortalQuery({ rpcUrl: config.rpcUrl, chainId: config.chainId });
75
+ this.dexQuery = new DexQuery({ rpcUrl: config.rpcUrl, chainId: config.chainId });
76
+ }
77
+ getEffectiveRouter(params) {
78
+ if (params.routerAddress)
79
+ return params.routerAddress;
80
+ if (params.dexKey === 'DYORSWAP') {
81
+ return '0xfb001fbbace32f09cb6d3c449b935183de53ee96';
82
+ }
83
+ return POTATOSWAP_V2_ROUTER;
84
+ }
85
+ async runHandleOps(label, ops, bundlerSigner, beneficiary) {
86
+ const provider = this.aaManager.getProvider();
87
+ const entryPointAddress = this.aaManager.getEntryPointAddress();
88
+ const feeData = await provider.getFeeData();
89
+ console.log(`\n[${label}] 发送 handleOps,ops=${ops.length} ...`);
90
+ const entryPointWithSigner = new Contract(entryPointAddress, ENTRYPOINT_ABI, bundlerSigner);
91
+ const tx = await entryPointWithSigner.handleOps(ops, beneficiary, { gasPrice: feeData.gasPrice ?? 100000000n });
92
+ console.log(`[${label}] txHash:`, tx.hash);
93
+ const receipt = await tx.wait();
94
+ console.log(`[${label}] mined block=${receipt.blockNumber} status=${receipt.status}`);
95
+ const epIface = new Interface(ENTRYPOINT_ABI);
96
+ const userOpEvents = [];
97
+ for (const log of receipt.logs) {
98
+ try {
99
+ const parsed = epIface.parseLog(log);
100
+ if (parsed?.name === 'UserOperationEvent') {
101
+ const e = parsed.args;
102
+ userOpEvents.push({
103
+ userOpHash: e.userOpHash,
104
+ sender: e.sender,
105
+ paymaster: e.paymaster,
106
+ success: e.success,
107
+ actualGasCost: e.actualGasCost,
108
+ actualGasUsed: e.actualGasUsed,
109
+ });
110
+ }
111
+ }
112
+ catch { }
113
+ }
114
+ return { txHash: tx.hash, blockNumber: receipt.blockNumber, status: receipt.status, userOpEvents };
115
+ }
116
+ pickWallets(privateKeys, n) {
117
+ const provider = this.aaManager.getProvider();
118
+ const wallets = privateKeys.map((pk) => new Wallet(pk, provider));
119
+ if (!n || n <= 0 || n >= wallets.length)
120
+ return wallets;
121
+ return wallets.slice(0, n);
122
+ }
123
+ async buildWithdrawUserOp(params) {
124
+ const senderBalance = params.senderBalance;
125
+ if (senderBalance <= params.reserveWei)
126
+ return null;
127
+ const effConfig = { ...(this.config ?? {}), ...(params.configOverride ?? {}) };
128
+ const gasPolicyRaw = effConfig.gasPolicy ?? 'bundlerEstimate';
129
+ const gasPolicy = gasPolicyRaw === 'bundlerEstimate' ? 'fixed' : gasPolicyRaw;
130
+ const tempCallData = encodeExecute(params.ownerWallet.address, 0n, '0x');
131
+ const { prefundWei } = gasPolicy === 'fixed'
132
+ ? await this.aaManager.buildUserOpWithFixedGas({
133
+ ownerWallet: params.ownerWallet,
134
+ sender: params.sender,
135
+ callData: tempCallData,
136
+ nonce: params.nonce,
137
+ initCode: params.initCode,
138
+ deployed: params.initCode === '0x',
139
+ fixedGas: {
140
+ ...(effConfig.fixedGas ?? {}),
141
+ callGasLimit: effConfig.fixedGas?.callGasLimit ?? DEFAULT_CALL_GAS_LIMIT_WITHDRAW,
142
+ },
143
+ })
144
+ : await this.aaManager.buildUserOpWithLocalEstimate({
145
+ ownerWallet: params.ownerWallet,
146
+ sender: params.sender,
147
+ callData: tempCallData,
148
+ nonce: params.nonce,
149
+ initCode: params.initCode,
150
+ });
151
+ const withdrawAmount = senderBalance > prefundWei + params.reserveWei
152
+ ? senderBalance - prefundWei - params.reserveWei
153
+ : 0n;
154
+ if (withdrawAmount <= 0n)
155
+ return null;
156
+ const { extractProfit, profitBps, profitRecipient } = resolveProfitSettings(effConfig);
157
+ const profitWei = extractProfit ? calculateProfitWei(withdrawAmount, profitBps) : 0n;
158
+ const toOwnerWei = withdrawAmount - profitWei;
159
+ const callData = extractProfit && profitWei > 0n
160
+ ? encodeExecuteBatch([profitRecipient, params.ownerWallet.address], [profitWei, toOwnerWei], ['0x', '0x'])
161
+ : encodeExecute(params.ownerWallet.address, withdrawAmount, '0x');
162
+ const { userOp } = gasPolicy === 'fixed'
163
+ ? await this.aaManager.buildUserOpWithFixedGas({
164
+ ownerWallet: params.ownerWallet,
165
+ sender: params.sender,
166
+ callData,
167
+ nonce: params.nonce,
168
+ initCode: params.initCode,
169
+ deployed: params.initCode === '0x',
170
+ fixedGas: {
171
+ ...(effConfig.fixedGas ?? {}),
172
+ callGasLimit: effConfig.fixedGas?.callGasLimit ?? DEFAULT_CALL_GAS_LIMIT_WITHDRAW,
173
+ },
174
+ })
175
+ : await this.aaManager.buildUserOpWithLocalEstimate({
176
+ ownerWallet: params.ownerWallet,
177
+ sender: params.sender,
178
+ callData,
179
+ nonce: params.nonce,
180
+ initCode: params.initCode,
181
+ });
182
+ return { userOp, prefundWei, profitWei };
183
+ }
184
+ async safeQuoteBuy(tokenAddress, okbAmount) {
185
+ if (okbAmount <= 0n)
186
+ return 0n;
187
+ try {
188
+ return await this.dexQuery.quoteOkbToToken(okbAmount, tokenAddress);
189
+ }
190
+ catch {
191
+ return 0n;
192
+ }
193
+ }
194
+ async execute(params) {
195
+ const { tradeType = 'V2', dexKey, routerAddress, deadlineMinutes = 20, tokenAddress, buyerPrivateKeys, sellerPrivateKeys, buyerFunds, buyCount, sellCount, slippageBps = 0, withdrawToOwner = true, withdrawReserve = DEFAULT_WITHDRAW_RESERVE, config, } = params;
196
+ if (tradeType !== 'V2') {
197
+ throw new Error('AADexBuyFirstExecutor 仅支持 tradeType=V2(外盘)');
198
+ }
199
+ const effectiveRouter = this.getEffectiveRouter({ dexKey, routerAddress });
200
+ const deadline = getDexDeadline(deadlineMinutes);
201
+ const buyers = this.pickWallets(buyerPrivateKeys, buyCount);
202
+ const sellers = this.pickWallets(sellerPrivateKeys, sellCount);
203
+ if (buyers.length === 0)
204
+ throw new Error('buyCount=0 或 buyerPrivateKeys 为空');
205
+ if (sellers.length === 0)
206
+ throw new Error('sellCount=0 或 sellerPrivateKeys 为空');
207
+ const effConfig = { ...(this.config ?? {}), ...(config ?? {}) };
208
+ const profitSettings = resolveProfitSettings(effConfig);
209
+ const totalBuyWei = parseOkb(String(buyerFunds));
210
+ if (totalBuyWei <= 0n)
211
+ throw new Error('buyerFunds 需要 > 0');
212
+ const buyAmountsWei = splitAmount(totalBuyWei, buyers.length);
213
+ const quotedPerBuy = await mapWithConcurrency(buyAmountsWei, 6, async (amt) => this.safeQuoteBuy(tokenAddress, amt));
214
+ const quotedTotalTokenOut = quotedPerBuy.reduce((a, b) => a + (b ?? 0n), 0n);
215
+ const estimatedTokenOutWei = (quotedTotalTokenOut * BigInt(10000 - Math.max(0, Math.min(10000, slippageBps)))) / 10000n;
216
+ if (estimatedTokenOutWei <= 0n) {
217
+ throw new Error('买入报价为 0,无法规划 sellAmount(可能无流动性)');
218
+ }
219
+ const sellAmountsWei = splitAmount(estimatedTokenOutWei, sellers.length);
220
+ // account infos
221
+ const buyerAis = await this.aaManager.getMultipleAccountInfo(buyers.map((w) => w.address));
222
+ const sellerAis = await this.aaManager.getMultipleAccountInfo(sellers.map((w) => w.address));
223
+ const nonceMap = new AANonceMap();
224
+ for (const ai of buyerAis)
225
+ nonceMap.init(ai.sender, ai.nonce);
226
+ for (const ai of sellerAis)
227
+ nonceMap.init(ai.sender, ai.nonce);
228
+ // initCode:确保同一 sender 只在第一笔 op 携带
229
+ const initCodeBySenderLower = new Map();
230
+ const initIfNeeded = (sender, deployed, ownerAddress) => {
231
+ const k = sender.toLowerCase();
232
+ if (initCodeBySenderLower.has(k))
233
+ return;
234
+ initCodeBySenderLower.set(k, deployed ? '0x' : this.aaManager.generateInitCode(ownerAddress));
235
+ };
236
+ const consumeInitCode = (sender) => {
237
+ const k = sender.toLowerCase();
238
+ const cur = initCodeBySenderLower.get(k);
239
+ if (cur === undefined)
240
+ throw new Error(`initCode not initialized for sender: ${sender}`);
241
+ if (cur !== '0x')
242
+ initCodeBySenderLower.set(k, '0x');
243
+ return cur;
244
+ };
245
+ for (let i = 0; i < buyers.length; i++)
246
+ initIfNeeded(buyerAis[i].sender, buyerAis[i].deployed, buyers[i].address);
247
+ for (let i = 0; i < sellers.length; i++)
248
+ initIfNeeded(sellerAis[i].sender, sellerAis[i].deployed, sellers[i].address);
249
+ // 卖方预持仓检查
250
+ const sellerSenders = sellerAis.map((ai) => ai.sender);
251
+ const sellerTokenBalances = await this.portalQuery.getMultipleTokenBalances(tokenAddress, sellerSenders);
252
+ for (let i = 0; i < sellers.length; i++) {
253
+ const sender = sellerSenders[i];
254
+ const needSell = sellAmountsWei[i] ?? 0n;
255
+ const bal = sellerTokenBalances.get(sender) ?? 0n;
256
+ if (needSell > bal) {
257
+ throw new Error(`卖方预持仓不足: sender=${sender} need=${needSell.toString()} bal=${bal.toString()}`);
258
+ }
259
+ }
260
+ // allowance(spender=router)
261
+ const sellerAllowances = await this.portalQuery.getMultipleAllowances(tokenAddress, sellerSenders, effectiveRouter);
262
+ const ops = [];
263
+ // buy ops
264
+ for (let i = 0; i < buyers.length; i++) {
265
+ const w = buyers[i];
266
+ const ai = buyerAis[i];
267
+ const sender = ai.sender;
268
+ const buyWei = buyAmountsWei[i] ?? 0n;
269
+ if (buyWei <= 0n)
270
+ continue;
271
+ const swapData = encodeSwapExactETHForTokensSupportingFee(0n, [WOKB, tokenAddress], sender, deadline);
272
+ const callData = encodeExecute(effectiveRouter, buyWei, swapData);
273
+ const { userOp, prefundWei } = await this.aaManager.buildUserOpWithFixedGas({
274
+ ownerWallet: w,
275
+ sender,
276
+ nonce: nonceMap.next(sender),
277
+ initCode: consumeInitCode(sender),
278
+ callData,
279
+ deployed: ai.deployed,
280
+ fixedGas: {
281
+ ...(effConfig.fixedGas ?? {}),
282
+ callGasLimit: effConfig.fixedGas?.callGasLimit ?? DEFAULT_CALL_GAS_LIMIT_BUY,
283
+ },
284
+ });
285
+ await this.aaManager.ensureSenderBalance(w, sender, buyWei + prefundWei + parseOkb('0.0001'), `buyFirstDex/buyer${i + 1}/fund`);
286
+ const signed = await this.aaManager.signUserOp(userOp, w);
287
+ ops.push(signed.userOp);
288
+ }
289
+ // seller approve + sell ops
290
+ for (let i = 0; i < sellers.length; i++) {
291
+ const w = sellers[i];
292
+ const ai = sellerAis[i];
293
+ const sender = ai.sender;
294
+ const sellWei = sellAmountsWei[i] ?? 0n;
295
+ if (sellWei <= 0n)
296
+ continue;
297
+ const allowance = sellerAllowances.get(sender) ?? 0n;
298
+ if (allowance < sellWei) {
299
+ const approveCallData = encodeExecute(tokenAddress, 0n, encodeApproveCall(effectiveRouter, ethers.MaxUint256));
300
+ const { userOp: approveOp, prefundWei } = await this.aaManager.buildUserOpWithFixedGas({
301
+ ownerWallet: w,
302
+ sender,
303
+ nonce: nonceMap.next(sender),
304
+ initCode: consumeInitCode(sender),
305
+ callData: approveCallData,
306
+ deployed: ai.deployed,
307
+ fixedGas: {
308
+ ...(effConfig.fixedGas ?? {}),
309
+ callGasLimit: effConfig.fixedGas?.callGasLimit ?? DEFAULT_CALL_GAS_LIMIT_APPROVE,
310
+ },
311
+ });
312
+ await this.aaManager.ensureSenderBalance(w, sender, prefundWei + parseOkb('0.0001'), `buyFirstDex/seller${i + 1}/approve-fund`);
313
+ const signedApprove = await this.aaManager.signUserOp(approveOp, w);
314
+ ops.push(signedApprove.userOp);
315
+ }
316
+ const sellSwapData = encodeSwapExactTokensForETHSupportingFee(sellWei, 0n, [tokenAddress, WOKB], sender, deadline);
317
+ const sellCallData = encodeExecute(effectiveRouter, 0n, sellSwapData);
318
+ const { userOp: sellOp, prefundWei: sellPrefund } = await this.aaManager.buildUserOpWithFixedGas({
319
+ ownerWallet: w,
320
+ sender,
321
+ nonce: nonceMap.next(sender),
322
+ initCode: consumeInitCode(sender),
323
+ callData: sellCallData,
324
+ deployed: ai.deployed,
325
+ fixedGas: {
326
+ ...(effConfig.fixedGas ?? {}),
327
+ callGasLimit: effConfig.fixedGas?.callGasLimit ?? DEFAULT_CALL_GAS_LIMIT_SELL,
328
+ },
329
+ });
330
+ await this.aaManager.ensureSenderBalance(w, sender, sellPrefund + parseOkb('0.0001'), `buyFirstDex/seller${i + 1}/sell-fund`);
331
+ const signedSell = await this.aaManager.signUserOp(sellOp, w);
332
+ ops.push(signedSell.userOp);
333
+ }
334
+ if (ops.length === 0)
335
+ throw new Error('本轮没有生成任何 UserOp');
336
+ const payerWallet = buyers[0];
337
+ const beneficiary = payerWallet.address;
338
+ const buySellResult = await this.runHandleOps('aa-buy-first/dex', ops, payerWallet, beneficiary);
339
+ let withdrawResult;
340
+ let finalSellerBalances;
341
+ let totalProfitWei = 0n;
342
+ if (withdrawToOwner) {
343
+ const reserveWei = parseOkb(withdrawReserve);
344
+ const sellerOkbBalances = await this.portalQuery.getMultipleOkbBalances(sellerSenders);
345
+ const withdrawOps = [];
346
+ const builtOps = await mapWithConcurrency(sellers, 3, async (w, i) => {
347
+ const sender = sellerAis[i].sender;
348
+ const senderBalance = sellerOkbBalances.get(sender) ?? 0n;
349
+ const usedNonce = nonceMap.peek(sender);
350
+ const built = await this.buildWithdrawUserOp({
351
+ ownerWallet: w,
352
+ sender,
353
+ nonce: usedNonce,
354
+ initCode: '0x',
355
+ senderBalance,
356
+ reserveWei,
357
+ configOverride: config,
358
+ });
359
+ if (!built)
360
+ return null;
361
+ nonceMap.commit(sender, usedNonce);
362
+ totalProfitWei += built.profitWei;
363
+ const signed = await this.aaManager.signUserOp(built.userOp, w);
364
+ return signed.userOp;
365
+ });
366
+ for (const op of builtOps)
367
+ if (op)
368
+ withdrawOps.push(op);
369
+ if (withdrawOps.length > 0) {
370
+ withdrawResult = await this.runHandleOps('aa-buy-first/dex-withdraw', withdrawOps, payerWallet, beneficiary);
371
+ }
372
+ finalSellerBalances = await this.portalQuery.getMultipleOkbBalances(sellerSenders);
373
+ }
374
+ return {
375
+ buySellResult,
376
+ withdrawResult,
377
+ finalSellerBalances,
378
+ profit: withdrawToOwner
379
+ ? {
380
+ extractProfit: profitSettings.extractProfit,
381
+ profitBps: profitSettings.profitBps,
382
+ profitRecipient: profitSettings.profitRecipient,
383
+ totalProfitWei: totalProfitWei.toString(),
384
+ }
385
+ : undefined,
386
+ metadata: {
387
+ tradeType: 'V2',
388
+ tokenAddress,
389
+ buyerOwners: buyers.map((w) => w.address),
390
+ buyerSenders: buyerAis.map((ai) => ai.sender),
391
+ sellerOwners: sellers.map((w) => w.address),
392
+ sellerSenders,
393
+ buyAmountsWei: buyAmountsWei.map((x) => x.toString()),
394
+ sellAmountsWei: sellAmountsWei.map((x) => x.toString()),
395
+ totalBuyWei: totalBuyWei.toString(),
396
+ estimatedTokenOutWei: estimatedTokenOutWei.toString(),
397
+ slippageBps,
398
+ },
399
+ };
400
+ }
401
+ }
402
+ export function createAADexBuyFirstExecutor(config) {
403
+ return new AADexBuyFirstExecutor(config);
404
+ }
@@ -66,6 +66,9 @@ export { BundleExecutor, createBundleExecutor, bundleBuy, bundleSell, bundleBuyS
66
66
  export { DexBundleExecutor, createDexBundleExecutor, dexBundleBuySell, type DexBundleConfig, type DexBundleBuySellParams, } from './dex-bundle.js';
67
67
  export { AAPortalSwapExecutor, } from './portal-bundle-swap.js';
68
68
  export { AADexSwapExecutor, } from './dex-bundle-swap.js';
69
+ export { AAPortalBuyFirstExecutor, createAAPortalBuyFirstExecutor, } from './portal-buy-first.js';
70
+ export { AADexBuyFirstExecutor, createAADexBuyFirstExecutor, } from './dex-buy-first.js';
71
+ export { BuyFirstVolumeExecutor, createBuyFirstVolumeExecutor, makeBuyFirstVolume, } from './buy-first-volume.js';
69
72
  /**
70
73
  * XLayer AA 换手签名 (卖 -> 买)
71
74
  */
@@ -91,6 +91,12 @@ export { DexBundleExecutor, createDexBundleExecutor, dexBundleBuySell, } from '.
91
91
  // ============================================================================
92
92
  export { AAPortalSwapExecutor, } from './portal-bundle-swap.js';
93
93
  export { AADexSwapExecutor, } from './dex-bundle-swap.js';
94
+ // ============================================================================
95
+ // AA Buy-First(刷量专用)
96
+ // ============================================================================
97
+ export { AAPortalBuyFirstExecutor, createAAPortalBuyFirstExecutor, } from './portal-buy-first.js';
98
+ export { AADexBuyFirstExecutor, createAADexBuyFirstExecutor, } from './dex-buy-first.js';
99
+ export { BuyFirstVolumeExecutor, createBuyFirstVolumeExecutor, makeBuyFirstVolume, } from './buy-first-volume.js';
94
100
  /**
95
101
  * XLayer AA 换手签名 (卖 -> 买)
96
102
  */
@@ -0,0 +1,30 @@
1
+ /**
2
+ * XLayer AA Buy-First(内盘 / Flap Portal)
3
+ *
4
+ * 对齐 BSC buy-first 思路:
5
+ * - 买方先买入(多钱包随机拆分资金)
6
+ * - 卖方再卖出“等值 token”(卖方需要预持仓)
7
+ * - ✅ 利润:在“卖出后归集 OKB”阶段刮取(executeBatch 拆分给 profitRecipient + owner)
8
+ *
9
+ * 说明:
10
+ * - buy + sell 会放在同一笔 handleOps 内,保持“buy-first 原子”语义
11
+ * - withdraw 需要知道 sell 后的余额,因此单独再发一笔 handleOps
12
+ */
13
+ import type { XLayerConfig } from './types.js';
14
+ import type { BuyFirstParams, BuyFirstResult } from './types.js';
15
+ export declare class AAPortalBuyFirstExecutor {
16
+ private aaManager;
17
+ private portalQuery;
18
+ private config;
19
+ constructor(config?: XLayerConfig);
20
+ private runHandleOps;
21
+ private pickWallets;
22
+ private getAccountInfos;
23
+ private safePreviewBuy;
24
+ private buildWithdrawUserOp;
25
+ /**
26
+ * 执行一轮 buy-first(内盘)
27
+ */
28
+ execute(params: BuyFirstParams): Promise<BuyFirstResult>;
29
+ }
30
+ export declare function createAAPortalBuyFirstExecutor(config?: XLayerConfig): AAPortalBuyFirstExecutor;