four-flap-meme-sdk 1.5.52 → 1.5.53

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,27 @@
1
+ /**
2
+ * XLayer AA 账户信息批量获取器
3
+ */
4
+ import type { Wallet } from 'ethers';
5
+ import type { AAAccountManager } from './aa-account.js';
6
+ import type { AAAccount } from './types.js';
7
+ export interface AccountInfoBatch {
8
+ buyerAis: AAAccount[];
9
+ sellerAis: AAAccount[];
10
+ sellerSenders: string[];
11
+ sellerTokenBalances: Map<string, bigint>;
12
+ sellerAllowances: Map<string, bigint>;
13
+ }
14
+ /**
15
+ * ✅ 优化 1: 并行获取所有账户信息
16
+ */
17
+ export declare function fetchAccountInfoBatch(params: {
18
+ buyers: Wallet[];
19
+ sellers: Wallet[];
20
+ tokenAddress: string;
21
+ effectiveRouter: string;
22
+ aaManager: AAAccountManager;
23
+ portalQuery: {
24
+ getMultipleTokenBalances: (token: string, addresses: string[]) => Promise<Map<string, bigint>>;
25
+ getMultipleAllowances: (token: string, owners: string[], spender: string) => Promise<Map<string, bigint>>;
26
+ };
27
+ }): Promise<AccountInfoBatch>;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * XLayer AA 账户信息批量获取器
3
+ */
4
+ /**
5
+ * ✅ 优化 1: 并行获取所有账户信息
6
+ */
7
+ export async function fetchAccountInfoBatch(params) {
8
+ const { buyers, sellers, tokenAddress, effectiveRouter, aaManager, portalQuery } = params;
9
+ // ✅ 并行获取买方和卖方的账户信息
10
+ const [buyerAis, sellerAis] = await Promise.all([
11
+ aaManager.getMultipleAccountInfo(buyers.map((w) => w.address)),
12
+ aaManager.getMultipleAccountInfo(sellers.map((w) => w.address))
13
+ ]);
14
+ const sellerSenders = sellerAis.map((ai) => ai.sender);
15
+ // ✅ 并行获取卖方的代币余额和授权额度
16
+ const [sellerTokenBalances, sellerAllowances] = await Promise.all([
17
+ portalQuery.getMultipleTokenBalances(tokenAddress, sellerSenders),
18
+ portalQuery.getMultipleAllowances(tokenAddress, sellerSenders, effectiveRouter)
19
+ ]);
20
+ return {
21
+ buyerAis,
22
+ sellerAis,
23
+ sellerSenders,
24
+ sellerTokenBalances,
25
+ sellerAllowances
26
+ };
27
+ }
@@ -24,6 +24,10 @@ export declare const FLAP_PORTAL = "0xb30D8c4216E1f21F27444D2FfAee3ad577808678";
24
24
  export declare const FLAP_TOKEN_IMPL = "0x12Dc83157Bf1cfCB8Db5952b3ba5bb56Cc38f8C9";
25
25
  /** WOKB 原生包装代币 */
26
26
  export declare const WOKB = "0xe538905cf8410324e03a5a23c1c177a474d59b2b";
27
+ /** ✅ USDT 代币地址(XLayer,6 位精度) */
28
+ export declare const USDT = "0x1e4a5963abfd975d8c9021ce480b42188849d41d";
29
+ /** ✅ 零地址(表示原生代币) */
30
+ export declare const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
27
31
  /** Multicall3 合约 */
28
32
  export declare const MULTICALL3 = "0xca11bde05977b3631167028862be2a173976ca11";
29
33
  /** PotatoSwap V2 Router */
@@ -34,14 +38,10 @@ export declare const POTATOSWAP_SWAP_ROUTER02 = "0xB45D0149249488333E3F3f9F35980
34
38
  export declare const POTATOSWAP_V3_ROUTER = "0xBB069e9465BcabC4F488d21e793BDEf0F2d41D41";
35
39
  /** PotatoSwap V3 Factory */
36
40
  export declare const POTATOSWAP_V3_FACTORY = "0xa1415fAe79c4B196d087F02b8aD5a622B8A827E5";
37
- /** USDT (6位精度) */
38
- export declare const USDT = "0x1e4a5963abfd975d8c9021ce480b42188849d41d";
39
41
  /** USDC (6位精度) */
40
42
  export declare const USDC = "0x74b7f16337b8972027f6196a17a631ac6de26d22";
41
43
  /** USD₮0 / USDT0 (6位精度) - Flap xLayer 支持的稳定币计价 */
42
44
  export declare const USDT0 = "0x779ded0c9e1022225f8e0630b35a9b54be713736";
43
- /** 零地址(表示原生代币 OKB) */
44
- export declare const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
45
45
  /** 默认 Gas Price 兜底值(0.1 gwei) */
46
46
  export declare const DEFAULT_GAS_PRICE = 100000000n;
47
47
  /** 默认 Salt(用于 AA 账户派生) */
@@ -36,6 +36,10 @@ export const FLAP_TOKEN_IMPL = '0x12Dc83157Bf1cfCB8Db5952b3ba5bb56Cc38f8C9';
36
36
  // ============================================================================
37
37
  /** WOKB 原生包装代币 */
38
38
  export const WOKB = '0xe538905cf8410324e03a5a23c1c177a474d59b2b';
39
+ /** ✅ USDT 代币地址(XLayer,6 位精度) */
40
+ export const USDT = '0x1e4a5963abfd975d8c9021ce480b42188849d41d';
41
+ /** ✅ 零地址(表示原生代币) */
42
+ export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
39
43
  /** Multicall3 合约 */
40
44
  export const MULTICALL3 = '0xca11bde05977b3631167028862be2a173976ca11';
41
45
  /** PotatoSwap V2 Router */
@@ -46,21 +50,11 @@ export const POTATOSWAP_SWAP_ROUTER02 = '0xB45D0149249488333E3F3f9F359807F4b810C
46
50
  export const POTATOSWAP_V3_ROUTER = '0xBB069e9465BcabC4F488d21e793BDEf0F2d41D41';
47
51
  /** PotatoSwap V3 Factory */
48
52
  export const POTATOSWAP_V3_FACTORY = '0xa1415fAe79c4B196d087F02b8aD5a622B8A827E5';
49
- // ============================================================================
50
- // 稳定币地址
51
- // ============================================================================
52
- /** USDT (6位精度) */
53
- export const USDT = '0x1e4a5963abfd975d8c9021ce480b42188849d41d';
54
53
  /** USDC (6位精度) */
55
54
  export const USDC = '0x74b7f16337b8972027f6196a17a631ac6de26d22';
56
55
  /** USD₮0 / USDT0 (6位精度) - Flap xLayer 支持的稳定币计价 */
57
56
  export const USDT0 = '0x779ded0c9e1022225f8e0630b35a9b54be713736';
58
57
  // ============================================================================
59
- // 零地址
60
- // ============================================================================
61
- /** 零地址(表示原生代币 OKB) */
62
- export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
63
- // ============================================================================
64
58
  // Gas 配置
65
59
  // ============================================================================
66
60
  /** 默认 Gas Price 兜底值(0.1 gwei) */
@@ -1,10 +1,6 @@
1
1
  /**
2
2
  * XLayer AA Buy-First(外盘 / PotatoSwap V2)
3
- *
4
- * 对齐 BSC buy-first 思路:
5
- * - 买方先买入(OKB -> token)
6
- * - 卖方再卖出等值 token(token -> OKB,卖方需要预持仓)
7
- * - ✅ 利润:在卖出后归集阶段刮取
3
+ * ✅ 优化版本:并行化 RPC、批量签名、合并 approve+sell
8
4
  */
9
5
  import type { XLayerConfig } from './types.js';
10
6
  import type { BuyFirstParams, BuyFirstResult } from './types.js';
@@ -14,11 +10,11 @@ export declare class AADexBuyFirstExecutor {
14
10
  private dexQuery;
15
11
  private config;
16
12
  constructor(config?: XLayerConfig);
17
- private getEffectiveRouter;
18
13
  private runHandleOps;
19
14
  private pickWallets;
20
15
  private buildWithdrawUserOp;
21
16
  private safeQuoteBuy;
17
+ private initNonceAndInitCode;
22
18
  execute(params: BuyFirstParams): Promise<BuyFirstResult>;
23
19
  }
24
20
  export declare function createAADexBuyFirstExecutor(config?: XLayerConfig): AADexBuyFirstExecutor;
@@ -1,72 +1,21 @@
1
1
  /**
2
2
  * XLayer AA Buy-First(外盘 / PotatoSwap V2)
3
- *
4
- * 对齐 BSC buy-first 思路:
5
- * - 买方先买入(OKB -> token)
6
- * - 卖方再卖出等值 token(token -> OKB,卖方需要预持仓)
7
- * - ✅ 利润:在卖出后归集阶段刮取
3
+ * ✅ 优化版本:并行化 RPC、批量签名、合并 approve+sell
8
4
  */
9
5
  import { Contract, Interface, Wallet, ethers } from 'ethers';
10
6
  import { AANonceMap } from './types.js';
11
- import { ENTRYPOINT_ABI, DEFAULT_WITHDRAW_RESERVE, POTATOSWAP_V2_ROUTER, WOKB, } from './constants.js';
7
+ import { ENTRYPOINT_ABI, DEFAULT_WITHDRAW_RESERVE, POTATOSWAP_V2_ROUTER, WOKB, ZERO_ADDRESS, } from './constants.js';
12
8
  import { AAAccountManager, encodeExecute } from './aa-account.js';
13
- import { DexQuery, encodeSwapExactETHForTokensSupportingFee, encodeSwapExactTokensForETHSupportingFee } from './dex.js';
14
- import { PortalQuery, encodeApproveCall, parseOkb } from './portal-ops.js';
9
+ import { DexQuery } from './dex.js';
10
+ import { PortalQuery, parseOkb } from './portal-ops.js';
15
11
  import { mapWithConcurrency } from '../utils/concurrency.js';
16
- import { PROFIT_CONFIG } from '../utils/constants.js';
17
- // 固定 gas(用于减少 RPC 与提高可控性)
12
+ import { getDexDeadline, resolveProfitSettings, calculateProfitWei, splitAmount, getEffectiveRouter } from './dex-helpers.js';
13
+ import { fetchAccountInfoBatch } from './account-fetcher.js';
14
+ import { buildBuyUserOps, buildSellUserOps } from './userOp-builder.js';
18
15
  const DEFAULT_CALL_GAS_LIMIT_BUY = 450000n;
19
16
  const DEFAULT_CALL_GAS_LIMIT_SELL = 450000n;
20
17
  const DEFAULT_CALL_GAS_LIMIT_APPROVE = 200000n;
21
18
  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
19
  export class AADexBuyFirstExecutor {
71
20
  constructor(config = {}) {
72
21
  this.config = config;
@@ -74,14 +23,6 @@ export class AADexBuyFirstExecutor {
74
23
  this.portalQuery = new PortalQuery({ rpcUrl: config.rpcUrl, chainId: config.chainId });
75
24
  this.dexQuery = new DexQuery({ rpcUrl: config.rpcUrl, chainId: config.chainId });
76
25
  }
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
26
  async runHandleOps(label, ops, bundlerSigner, beneficiary) {
86
27
  const provider = this.aaManager.getProvider();
87
28
  const entryPointAddress = this.aaManager.getEntryPointAddress();
@@ -153,9 +94,8 @@ export class AADexBuyFirstExecutor {
153
94
  : 0n;
154
95
  if (withdrawAmount <= 0n)
155
96
  return null;
156
- const { extractProfit, profitBps, profitRecipient } = resolveProfitSettings(effConfig);
97
+ const { extractProfit, profitBps } = resolveProfitSettings(effConfig);
157
98
  const profitWei = extractProfit ? calculateProfitWei(withdrawAmount, profitBps) : 0n;
158
- const toOwnerWei = withdrawAmount - profitWei;
159
99
  const callData = encodeExecute(params.ownerWallet.address, withdrawAmount, '0x');
160
100
  const { userOp } = gasPolicy === 'fixed'
161
101
  ? await this.aaManager.buildUserOpWithFixedGas({
@@ -189,12 +129,49 @@ export class AADexBuyFirstExecutor {
189
129
  return 0n;
190
130
  }
191
131
  }
132
+ initNonceAndInitCode(params) {
133
+ const { buyers, sellers, buyerAis, sellerAis } = params;
134
+ const nonceMap = new AANonceMap();
135
+ // ✅ 使用 Set 去重,避免重复初始化
136
+ const uniqueSenders = new Set();
137
+ for (const ai of [...buyerAis, ...sellerAis]) {
138
+ const senderLower = ai.sender.toLowerCase();
139
+ if (!uniqueSenders.has(senderLower)) {
140
+ nonceMap.init(ai.sender, ai.nonce);
141
+ uniqueSenders.add(senderLower);
142
+ }
143
+ }
144
+ const initCodeBySenderLower = new Map();
145
+ const initIfNeeded = (sender, deployed, ownerAddress) => {
146
+ const k = sender.toLowerCase();
147
+ if (initCodeBySenderLower.has(k))
148
+ return;
149
+ initCodeBySenderLower.set(k, deployed ? '0x' : this.aaManager.generateInitCode(ownerAddress));
150
+ };
151
+ const consumeInitCode = (sender) => {
152
+ const k = sender.toLowerCase();
153
+ const cur = initCodeBySenderLower.get(k);
154
+ if (cur === undefined)
155
+ throw new Error(`initCode not initialized for sender: ${sender}`);
156
+ if (cur !== '0x')
157
+ initCodeBySenderLower.set(k, '0x');
158
+ return cur;
159
+ };
160
+ for (let i = 0; i < buyers.length; i++)
161
+ initIfNeeded(buyerAis[i].sender, buyerAis[i].deployed, buyers[i].address);
162
+ for (let i = 0; i < sellers.length; i++)
163
+ initIfNeeded(sellerAis[i].sender, sellerAis[i].deployed, sellers[i].address);
164
+ return { nonceMap, consumeInitCode };
165
+ }
192
166
  async execute(params) {
193
- const { tradeType = 'V2', dexKey, routerAddress, deadlineMinutes = 20, tokenAddress, buyerPrivateKeys, sellerPrivateKeys, buyerFunds, buyCount, sellCount, slippageBps = 0, withdrawToOwner = true, withdrawReserve = DEFAULT_WITHDRAW_RESERVE, config, } = params;
167
+ const { tradeType = 'V2', routerVersion = 'V2', // 新增
168
+ dexKey, routerAddress, deadlineMinutes = 20, tokenAddress, buyerPrivateKeys, sellerPrivateKeys, buyerFunds, buyCount, sellCount, slippageBps = 0, withdrawToOwner = true, withdrawReserve = DEFAULT_WITHDRAW_RESERVE, config, quoteToken, // ✅ 新增
169
+ quoteTokenDecimals = 18, // ✅ 新增
170
+ } = params;
194
171
  if (tradeType !== 'V2') {
195
172
  throw new Error('AADexBuyFirstExecutor 仅支持 tradeType=V2(外盘)');
196
173
  }
197
- const effectiveRouter = this.getEffectiveRouter({ dexKey, routerAddress });
174
+ const effectiveRouter = getEffectiveRouter({ dexKey, routerAddress, routerVersion }, POTATOSWAP_V2_ROUTER);
198
175
  const deadline = getDexDeadline(deadlineMinutes);
199
176
  const buyers = this.pickWallets(buyerPrivateKeys, buyCount);
200
177
  const sellers = this.pickWallets(sellerPrivateKeys, sellCount);
@@ -204,49 +181,60 @@ export class AADexBuyFirstExecutor {
204
181
  throw new Error('sellCount=0 或 sellerPrivateKeys 为空');
205
182
  const effConfig = { ...(this.config ?? {}), ...(config ?? {}) };
206
183
  const profitSettings = resolveProfitSettings(effConfig);
207
- const totalBuyWei = parseOkb(String(buyerFunds));
184
+ const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
185
+ // ✅ 解析买入金额(根据 quoteToken 精度)
186
+ const totalBuyWei = useNativeToken
187
+ ? parseOkb(String(buyerFunds))
188
+ : ethers.parseUnits(String(buyerFunds), quoteTokenDecimals);
208
189
  if (totalBuyWei <= 0n)
209
190
  throw new Error('buyerFunds 需要 > 0');
210
191
  const buyAmountsWei = splitAmount(totalBuyWei, buyers.length);
211
- const quotedPerBuy = await mapWithConcurrency(buyAmountsWei, 6, async (amt) => this.safeQuoteBuy(tokenAddress, amt));
192
+ // 并行报价(支持 OKB USDT)
193
+ const quotedPerBuy = await mapWithConcurrency(buyAmountsWei, 6, async (amt) => {
194
+ if (useNativeToken) {
195
+ return await this.safeQuoteBuy(tokenAddress, amt);
196
+ }
197
+ else {
198
+ // USDT → Token 报价
199
+ try {
200
+ return await this.dexQuery.quoteTokenToToken(quoteToken, tokenAddress, amt);
201
+ }
202
+ catch {
203
+ return 0n;
204
+ }
205
+ }
206
+ });
212
207
  const quotedTotalTokenOut = quotedPerBuy.reduce((a, b) => a + (b ?? 0n), 0n);
213
208
  const estimatedTokenOutWei = (quotedTotalTokenOut * BigInt(10000 - Math.max(0, Math.min(10000, slippageBps)))) / 10000n;
214
209
  if (estimatedTokenOutWei <= 0n) {
215
210
  throw new Error('买入报价为 0,无法规划 sellAmount(可能无流动性)');
216
211
  }
217
212
  const sellAmountsWei = splitAmount(estimatedTokenOutWei, sellers.length);
218
- // account infos
219
- const buyerAis = await this.aaManager.getMultipleAccountInfo(buyers.map((w) => w.address));
220
- const sellerAis = await this.aaManager.getMultipleAccountInfo(sellers.map((w) => w.address));
221
- const nonceMap = new AANonceMap();
222
- for (const ai of buyerAis)
223
- nonceMap.init(ai.sender, ai.nonce);
224
- for (const ai of sellerAis)
225
- nonceMap.init(ai.sender, ai.nonce);
226
- // initCode:确保同一 sender 只在第一笔 op 携带
227
- const initCodeBySenderLower = new Map();
228
- const initIfNeeded = (sender, deployed, ownerAddress) => {
229
- const k = sender.toLowerCase();
230
- if (initCodeBySenderLower.has(k))
231
- return;
232
- initCodeBySenderLower.set(k, deployed ? '0x' : this.aaManager.generateInitCode(ownerAddress));
233
- };
234
- const consumeInitCode = (sender) => {
235
- const k = sender.toLowerCase();
236
- const cur = initCodeBySenderLower.get(k);
237
- if (cur === undefined)
238
- throw new Error(`initCode not initialized for sender: ${sender}`);
239
- if (cur !== '0x')
240
- initCodeBySenderLower.set(k, '0x');
241
- return cur;
242
- };
243
- for (let i = 0; i < buyers.length; i++)
244
- initIfNeeded(buyerAis[i].sender, buyerAis[i].deployed, buyers[i].address);
245
- for (let i = 0; i < sellers.length; i++)
246
- initIfNeeded(sellerAis[i].sender, sellerAis[i].deployed, sellers[i].address);
247
- // 卖方预持仓检查
248
- const sellerSenders = sellerAis.map((ai) => ai.sender);
249
- const sellerTokenBalances = await this.portalQuery.getMultipleTokenBalances(tokenAddress, sellerSenders);
213
+ // 优化 1: 并行获取所有账户信息
214
+ const accountInfo = await fetchAccountInfoBatch({
215
+ buyers,
216
+ sellers,
217
+ tokenAddress,
218
+ effectiveRouter,
219
+ aaManager: this.aaManager,
220
+ portalQuery: this.portalQuery
221
+ });
222
+ const { buyerAis, sellerAis, sellerSenders, sellerTokenBalances, sellerAllowances } = accountInfo;
223
+ // 验证买方余额(OKB USDT)
224
+ if (!useNativeToken) {
225
+ const buyerSenders = buyerAis.map(ai => ai.sender);
226
+ const buyerQuoteBalances = await this.portalQuery.getMultipleTokenBalances(quoteToken, buyerSenders);
227
+ for (let i = 0; i < buyers.length; i++) {
228
+ const sender = buyerSenders[i];
229
+ const needBuy = buyAmountsWei[i] ?? 0n;
230
+ const bal = buyerQuoteBalances.get(sender) ?? 0n;
231
+ if (needBuy > bal) {
232
+ const tokenName = quoteTokenDecimals === 6 ? 'USDT' : 'ERC20';
233
+ throw new Error(`买方 ${tokenName} 余额不足: sender=${sender} need=${needBuy.toString()} bal=${bal.toString()}`);
234
+ }
235
+ }
236
+ }
237
+ // 验证卖方余额
250
238
  for (let i = 0; i < sellers.length; i++) {
251
239
  const sender = sellerSenders[i];
252
240
  const needSell = sellAmountsWei[i] ?? 0n;
@@ -255,80 +243,41 @@ export class AADexBuyFirstExecutor {
255
243
  throw new Error(`卖方预持仓不足: sender=${sender} need=${needSell.toString()} bal=${bal.toString()}`);
256
244
  }
257
245
  }
258
- // allowance(spender=router)
259
- const sellerAllowances = await this.portalQuery.getMultipleAllowances(tokenAddress, sellerSenders, effectiveRouter);
260
- const ops = [];
261
- // buy ops
262
- for (let i = 0; i < buyers.length; i++) {
263
- const w = buyers[i];
264
- const ai = buyerAis[i];
265
- const sender = ai.sender;
266
- const buyWei = buyAmountsWei[i] ?? 0n;
267
- if (buyWei <= 0n)
268
- continue;
269
- const swapData = encodeSwapExactETHForTokensSupportingFee(0n, [WOKB, tokenAddress], sender, deadline);
270
- const callData = encodeExecute(effectiveRouter, buyWei, swapData);
271
- const { userOp, prefundWei } = await this.aaManager.buildUserOpWithFixedGas({
272
- ownerWallet: w,
273
- sender,
274
- nonce: nonceMap.next(sender),
275
- initCode: consumeInitCode(sender),
276
- callData,
277
- deployed: ai.deployed,
278
- fixedGas: {
279
- ...(effConfig.fixedGas ?? {}),
280
- callGasLimit: effConfig.fixedGas?.callGasLimit ?? DEFAULT_CALL_GAS_LIMIT_BUY,
281
- },
282
- });
283
- await this.aaManager.ensureSenderBalance(w, sender, buyWei + prefundWei + parseOkb('0.0001'), `buyFirstDex/buyer${i + 1}/fund`);
284
- const signed = await this.aaManager.signUserOp(userOp, w);
285
- ops.push(signed.userOp);
286
- }
287
- // seller approve + sell ops
288
- for (let i = 0; i < sellers.length; i++) {
289
- const w = sellers[i];
290
- const ai = sellerAis[i];
291
- const sender = ai.sender;
292
- const sellWei = sellAmountsWei[i] ?? 0n;
293
- if (sellWei <= 0n)
294
- continue;
295
- const allowance = sellerAllowances.get(sender) ?? 0n;
296
- if (allowance < sellWei) {
297
- const approveCallData = encodeExecute(tokenAddress, 0n, encodeApproveCall(effectiveRouter, ethers.MaxUint256));
298
- const { userOp: approveOp, prefundWei } = await this.aaManager.buildUserOpWithFixedGas({
299
- ownerWallet: w,
300
- sender,
301
- nonce: nonceMap.next(sender),
302
- initCode: consumeInitCode(sender),
303
- callData: approveCallData,
304
- deployed: ai.deployed,
305
- fixedGas: {
306
- ...(effConfig.fixedGas ?? {}),
307
- callGasLimit: effConfig.fixedGas?.callGasLimit ?? DEFAULT_CALL_GAS_LIMIT_APPROVE,
308
- },
309
- });
310
- await this.aaManager.ensureSenderBalance(w, sender, prefundWei + parseOkb('0.0001'), `buyFirstDex/seller${i + 1}/approve-fund`);
311
- const signedApprove = await this.aaManager.signUserOp(approveOp, w);
312
- ops.push(signedApprove.userOp);
313
- }
314
- const sellSwapData = encodeSwapExactTokensForETHSupportingFee(sellWei, 0n, [tokenAddress, WOKB], sender, deadline);
315
- const sellCallData = encodeExecute(effectiveRouter, 0n, sellSwapData);
316
- const { userOp: sellOp, prefundWei: sellPrefund } = await this.aaManager.buildUserOpWithFixedGas({
317
- ownerWallet: w,
318
- sender,
319
- nonce: nonceMap.next(sender),
320
- initCode: consumeInitCode(sender),
321
- callData: sellCallData,
322
- deployed: ai.deployed,
323
- fixedGas: {
324
- ...(effConfig.fixedGas ?? {}),
325
- callGasLimit: effConfig.fixedGas?.callGasLimit ?? DEFAULT_CALL_GAS_LIMIT_SELL,
326
- },
327
- });
328
- await this.aaManager.ensureSenderBalance(w, sender, sellPrefund + parseOkb('0.0001'), `buyFirstDex/seller${i + 1}/sell-fund`);
329
- const signedSell = await this.aaManager.signUserOp(sellOp, w);
330
- ops.push(signedSell.userOp);
331
- }
246
+ const { nonceMap, consumeInitCode } = this.initNonceAndInitCode({ buyers, sellers, buyerAis, sellerAis });
247
+ // 优化 2 + 3: 并行构建买入和卖出 UserOp(卖出合并 approve+sell)
248
+ const [buyOps, sellOps] = await Promise.all([
249
+ buildBuyUserOps({
250
+ buyers,
251
+ buyerAis,
252
+ buyAmountsWei,
253
+ tokenAddress,
254
+ effectiveRouter,
255
+ deadline,
256
+ wokbAddress: WOKB,
257
+ nonceMap,
258
+ consumeInitCode,
259
+ aaManager: this.aaManager,
260
+ config: effConfig,
261
+ defaultCallGasLimit: DEFAULT_CALL_GAS_LIMIT_BUY
262
+ }),
263
+ buildSellUserOps({
264
+ sellers,
265
+ sellerAis,
266
+ sellAmountsWei,
267
+ tokenAddress,
268
+ effectiveRouter,
269
+ deadline,
270
+ wokbAddress: WOKB,
271
+ sellerAllowances,
272
+ nonceMap,
273
+ consumeInitCode,
274
+ aaManager: this.aaManager,
275
+ config: effConfig,
276
+ defaultApproveGasLimit: DEFAULT_CALL_GAS_LIMIT_APPROVE,
277
+ defaultSellGasLimit: DEFAULT_CALL_GAS_LIMIT_SELL
278
+ })
279
+ ]);
280
+ const ops = [...buyOps, ...sellOps];
332
281
  if (ops.length === 0)
333
282
  throw new Error('本轮没有生成任何 UserOp');
334
283
  const payerWallet = buyers[0];
@@ -0,0 +1,20 @@
1
+ /**
2
+ * XLayer AA DEX 工具函数
3
+ */
4
+ export declare function getDexDeadline(minutes?: number): number;
5
+ export declare function calculateProfitWei(amountWei: bigint, profitBps: number): bigint;
6
+ export declare function resolveProfitSettings(config?: {
7
+ extractProfit?: boolean;
8
+ profitBps?: number;
9
+ profitRecipient?: string;
10
+ }): {
11
+ extractProfit: boolean;
12
+ profitBps: number;
13
+ profitRecipient: string;
14
+ };
15
+ export declare function splitAmount(totalAmount: bigint, count: number): bigint[];
16
+ export declare function getEffectiveRouter(params: {
17
+ dexKey?: string;
18
+ routerAddress?: string;
19
+ routerVersion?: 'V2' | 'V3';
20
+ }, defaultRouter: string): string;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * XLayer AA DEX 工具函数
3
+ */
4
+ import { PROFIT_CONFIG } from '../utils/constants.js';
5
+ export function getDexDeadline(minutes = 20) {
6
+ return Math.floor(Date.now() / 1000) + minutes * 60;
7
+ }
8
+ export function calculateProfitWei(amountWei, profitBps) {
9
+ if (amountWei <= 0n)
10
+ return 0n;
11
+ if (!Number.isFinite(profitBps) || profitBps <= 0)
12
+ return 0n;
13
+ return (amountWei * BigInt(profitBps)) / 10000n;
14
+ }
15
+ export function resolveProfitSettings(config) {
16
+ const extractProfit = config?.extractProfit !== false;
17
+ const profitBpsRaw = config?.profitBps ?? PROFIT_CONFIG.RATE_BPS_SWAP;
18
+ const profitBps = Math.max(0, Math.min(10000, Number(profitBpsRaw)));
19
+ const profitRecipient = config?.profitRecipient ?? PROFIT_CONFIG.RECIPIENT;
20
+ return { extractProfit, profitBps, profitRecipient };
21
+ }
22
+ function shuffle(arr) {
23
+ const a = arr.slice();
24
+ for (let i = a.length - 1; i > 0; i--) {
25
+ const j = Math.floor(Math.random() * (i + 1));
26
+ [a[i], a[j]] = [a[j], a[i]];
27
+ }
28
+ return a;
29
+ }
30
+ export function splitAmount(totalAmount, count) {
31
+ if (count <= 0)
32
+ throw new Error('拆分份数必须大于 0');
33
+ if (count === 1)
34
+ return [totalAmount];
35
+ if (totalAmount <= 0n)
36
+ return new Array(count).fill(0n);
37
+ const weights = [];
38
+ for (let i = 0; i < count; i++) {
39
+ const w = BigInt(500000 + Math.floor(Math.random() * 1000000));
40
+ weights.push(w);
41
+ }
42
+ const totalWeight = weights.reduce((a, b) => a + b, 0n);
43
+ const amounts = [];
44
+ let allocated = 0n;
45
+ for (let i = 0; i < count - 1; i++) {
46
+ const amt = (totalAmount * weights[i]) / totalWeight;
47
+ amounts.push(amt);
48
+ allocated += amt;
49
+ }
50
+ amounts.push(totalAmount - allocated);
51
+ return shuffle(amounts);
52
+ }
53
+ export function getEffectiveRouter(params, defaultRouter) {
54
+ // 优先使用显式指定的地址
55
+ if (params.routerAddress)
56
+ return params.routerAddress;
57
+ // 根据 dexKey 选择
58
+ if (params.dexKey === 'DYORSWAP') {
59
+ return '0xfb001fbbace32f09cb6d3c449b935183de53ee96';
60
+ }
61
+ // 根据 routerVersion 选择
62
+ if (params.routerVersion === 'V3') {
63
+ return '0xBB069e9465BcabC4F488d21e793BDEf0F2d41D41'; // POTATOSWAP_V3_ROUTER
64
+ }
65
+ // 默认使用传入的 defaultRouter(通常是 V2)
66
+ return defaultRouter;
67
+ }
@@ -64,6 +64,10 @@ export declare class DexQuery {
64
64
  * 获取 Token -> OKB 的预期输出
65
65
  */
66
66
  quoteTokenToOkb(tokenAmount: bigint, tokenAddress: string): Promise<bigint>;
67
+ /**
68
+ * ✅ 获取 Token → Token 的预期输出(支持 USDT → Token)
69
+ */
70
+ quoteTokenToToken(inputToken: string, outputToken: string, inputAmount: bigint): Promise<bigint>;
67
71
  /**
68
72
  * 获取 WOKB 地址
69
73
  */
@@ -129,6 +129,14 @@ export class DexQuery {
129
129
  const amounts = await this.getAmountsOut(tokenAmount, path);
130
130
  return amounts[amounts.length - 1];
131
131
  }
132
+ /**
133
+ * ✅ 获取 Token → Token 的预期输出(支持 USDT → Token)
134
+ */
135
+ async quoteTokenToToken(inputToken, outputToken, inputAmount) {
136
+ const path = [inputToken, outputToken];
137
+ const amounts = await this.getAmountsOut(inputAmount, path);
138
+ return amounts[amounts.length - 1];
139
+ }
132
140
  /**
133
141
  * 获取 WOKB 地址
134
142
  */
@@ -0,0 +1,38 @@
1
+ /**
2
+ * XLayer Router 管理器
3
+ * 统一管理 V2/V3 Router 地址和授权目标选择逻辑
4
+ */
5
+ import type { RouterVersion, ApprovalTarget } from './types.js';
6
+ /**
7
+ * ✅ 获取 Router 地址
8
+ * @param params.routerVersion - Router 版本(V2 或 V3)
9
+ * @param params.dexKey - DEX 标识(如 DYORSWAP)
10
+ * @param params.routerAddress - 显式指定的 Router 地址(优先级最高)
11
+ * @returns Router 地址
12
+ */
13
+ export declare function getRouterAddress(params: {
14
+ routerVersion?: RouterVersion;
15
+ dexKey?: string;
16
+ routerAddress?: string;
17
+ }): string;
18
+ /**
19
+ * ✅ 获取授权目标地址
20
+ * @param target - 授权目标类型
21
+ * @returns 目标合约地址
22
+ */
23
+ export declare function getApprovalTargetAddress(target: ApprovalTarget): string;
24
+ /**
25
+ * ✅ 获取所有授权目标地址
26
+ * @param targets - 授权目标类型数组
27
+ * @returns 目标信息数组
28
+ */
29
+ export declare function getAllApprovalTargets(targets: ApprovalTarget[]): Array<{
30
+ target: ApprovalTarget;
31
+ address: string;
32
+ }>;
33
+ /**
34
+ * ✅ 根据交易类型推断授权目标
35
+ * @param tradeType - 交易类型(FLAP/V2/V3)
36
+ * @returns 授权目标类型
37
+ */
38
+ export declare function inferApprovalTarget(tradeType: string): ApprovalTarget;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * XLayer Router 管理器
3
+ * 统一管理 V2/V3 Router 地址和授权目标选择逻辑
4
+ */
5
+ import { POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, FLAP_PORTAL, } from './constants.js';
6
+ /**
7
+ * ✅ 获取 Router 地址
8
+ * @param params.routerVersion - Router 版本(V2 或 V3)
9
+ * @param params.dexKey - DEX 标识(如 DYORSWAP)
10
+ * @param params.routerAddress - 显式指定的 Router 地址(优先级最高)
11
+ * @returns Router 地址
12
+ */
13
+ export function getRouterAddress(params) {
14
+ // 1. 优先使用显式指定的地址
15
+ if (params.routerAddress)
16
+ return params.routerAddress;
17
+ // 2. 根据 dexKey 选择
18
+ if (params.dexKey === 'DYORSWAP') {
19
+ return '0xfb001fbbace32f09cb6d3c449b935183de53ee96';
20
+ }
21
+ // 3. 根据 routerVersion 选择
22
+ const version = params.routerVersion ?? 'V2';
23
+ if (version === 'V3') {
24
+ return POTATOSWAP_V3_ROUTER;
25
+ }
26
+ // 4. 默认 V2
27
+ return POTATOSWAP_V2_ROUTER;
28
+ }
29
+ /**
30
+ * ✅ 获取授权目标地址
31
+ * @param target - 授权目标类型
32
+ * @returns 目标合约地址
33
+ */
34
+ export function getApprovalTargetAddress(target) {
35
+ switch (target) {
36
+ case 'FLAP_PORTAL':
37
+ return FLAP_PORTAL;
38
+ case 'V2_ROUTER':
39
+ return POTATOSWAP_V2_ROUTER;
40
+ case 'V3_ROUTER':
41
+ return POTATOSWAP_V3_ROUTER;
42
+ default:
43
+ throw new Error(`Unknown approval target: ${target}`);
44
+ }
45
+ }
46
+ /**
47
+ * ✅ 获取所有授权目标地址
48
+ * @param targets - 授权目标类型数组
49
+ * @returns 目标信息数组
50
+ */
51
+ export function getAllApprovalTargets(targets) {
52
+ return targets.map(target => ({
53
+ target,
54
+ address: getApprovalTargetAddress(target)
55
+ }));
56
+ }
57
+ /**
58
+ * ✅ 根据交易类型推断授权目标
59
+ * @param tradeType - 交易类型(FLAP/V2/V3)
60
+ * @returns 授权目标类型
61
+ */
62
+ export function inferApprovalTarget(tradeType) {
63
+ const type = String(tradeType || '').toUpperCase();
64
+ if (type === 'V2')
65
+ return 'V2_ROUTER';
66
+ if (type === 'V3')
67
+ return 'V3_ROUTER';
68
+ return 'FLAP_PORTAL'; // 默认内盘
69
+ }
@@ -661,8 +661,14 @@ export interface VolumeResult {
661
661
  totalVolume: bigint;
662
662
  }
663
663
  export type BuyFirstTradeType = 'FLAP' | 'V2';
664
+ /** ✅ Router 版本类型 */
665
+ export type RouterVersion = 'V2' | 'V3';
666
+ /** ✅ 授权目标类型 */
667
+ export type ApprovalTarget = 'FLAP_PORTAL' | 'V2_ROUTER' | 'V3_ROUTER';
664
668
  export interface BuyFirstParams {
665
669
  tradeType?: BuyFirstTradeType;
670
+ /** ✅ 新增:Router 版本(V2 或 V3,默认 V2) */
671
+ routerVersion?: RouterVersion;
666
672
  /** DEX 标识(仅 V2 时使用,用于选择 Router) */
667
673
  dexKey?: string;
668
674
  /** 显式指定 Router 地址(仅 V2 时使用) */
@@ -672,10 +678,14 @@ export interface BuyFirstParams {
672
678
  tokenAddress: string;
673
679
  buyerPrivateKeys: string[];
674
680
  sellerPrivateKeys: string[];
675
- /** 本轮总买入资金(OKB) */
681
+ /** 本轮总买入资金(OKB 或 quoteToken) */
676
682
  buyerFunds: string;
677
683
  buyCount?: number;
678
684
  sellCount?: number;
685
+ /** ✅ 新增:买入使用的代币地址(零地址或不传表示使用 OKB,非零地址表示使用 ERC20 如 USDT) */
686
+ quoteToken?: string;
687
+ /** ✅ 新增:quoteToken 的精度(默认 18,USDT 在 XLayer 上是 6 位) */
688
+ quoteTokenDecimals?: number;
679
689
  /** 报价滑点(仅用于估算 sellAmount),默认 0 */
680
690
  slippageBps?: number;
681
691
  withdrawToOwner?: boolean;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * XLayer AA UserOp 批量构建器
3
+ */
4
+ import type { Wallet } from 'ethers';
5
+ import type { AAAccountManager } from './aa-account.js';
6
+ import type { AANonceMap } from './types.js';
7
+ import type { UserOperation, XLayerConfig } from './types.js';
8
+ export interface BuildBuyOpsParams {
9
+ buyers: Wallet[];
10
+ buyerAis: Array<{
11
+ sender: string;
12
+ deployed: boolean;
13
+ }>;
14
+ buyAmountsWei: bigint[];
15
+ tokenAddress: string;
16
+ effectiveRouter: string;
17
+ deadline: number;
18
+ wokbAddress: string;
19
+ nonceMap: AANonceMap;
20
+ consumeInitCode: (sender: string) => string;
21
+ aaManager: AAAccountManager;
22
+ config: XLayerConfig;
23
+ defaultCallGasLimit: bigint;
24
+ }
25
+ export interface BuildSellOpsParams {
26
+ sellers: Wallet[];
27
+ sellerAis: Array<{
28
+ sender: string;
29
+ deployed: boolean;
30
+ }>;
31
+ sellAmountsWei: bigint[];
32
+ tokenAddress: string;
33
+ effectiveRouter: string;
34
+ deadline: number;
35
+ wokbAddress: string;
36
+ sellerAllowances: Map<string, bigint>;
37
+ nonceMap: AANonceMap;
38
+ consumeInitCode: (sender: string) => string;
39
+ aaManager: AAAccountManager;
40
+ config: XLayerConfig;
41
+ defaultApproveGasLimit: bigint;
42
+ defaultSellGasLimit: bigint;
43
+ }
44
+ /**
45
+ * ✅ 优化 1: 并行构建所有买入 UserOp
46
+ */
47
+ export declare function buildBuyUserOps(params: BuildBuyOpsParams): Promise<UserOperation[]>;
48
+ /**
49
+ * ✅ 优化 3: 合并 Approve 和 Sell
50
+ */
51
+ export declare function buildSellUserOps(params: BuildSellOpsParams): Promise<UserOperation[]>;
@@ -0,0 +1,97 @@
1
+ /**
2
+ * XLayer AA UserOp 批量构建器
3
+ */
4
+ import { ethers } from 'ethers';
5
+ import { encodeExecute, encodeExecuteBatch } from './aa-account.js';
6
+ import { encodeSwapExactETHForTokensSupportingFee, encodeSwapExactTokensForETHSupportingFee } from './dex.js';
7
+ import { encodeApproveCall, parseOkb } from './portal-ops.js';
8
+ /**
9
+ * ✅ 优化 1: 并行构建所有买入 UserOp
10
+ */
11
+ export async function buildBuyUserOps(params) {
12
+ const { buyers, buyerAis, buyAmountsWei, tokenAddress, effectiveRouter, deadline, wokbAddress, nonceMap, consumeInitCode, aaManager, config, defaultCallGasLimit } = params;
13
+ const buyOpPromises = buyers.map(async (w, i) => {
14
+ const ai = buyerAis[i];
15
+ const sender = ai.sender;
16
+ const buyWei = buyAmountsWei[i] ?? 0n;
17
+ if (buyWei <= 0n)
18
+ return null;
19
+ const swapData = encodeSwapExactETHForTokensSupportingFee(0n, [wokbAddress, tokenAddress], sender, deadline);
20
+ const callData = encodeExecute(effectiveRouter, buyWei, swapData);
21
+ const { userOp, prefundWei } = await aaManager.buildUserOpWithFixedGas({
22
+ ownerWallet: w,
23
+ sender,
24
+ nonce: nonceMap.next(sender),
25
+ initCode: consumeInitCode(sender),
26
+ callData,
27
+ deployed: ai.deployed,
28
+ fixedGas: {
29
+ ...(config.fixedGas ?? {}),
30
+ callGasLimit: config.fixedGas?.callGasLimit ?? defaultCallGasLimit,
31
+ },
32
+ });
33
+ await aaManager.ensureSenderBalance(w, sender, buyWei + prefundWei + parseOkb('0.0001'), `buyFirstDex/buyer${i + 1}/fund`);
34
+ const signed = await aaManager.signUserOp(userOp, w);
35
+ return signed.userOp;
36
+ });
37
+ const ops = await Promise.all(buyOpPromises);
38
+ return ops.filter((op) => op !== null);
39
+ }
40
+ /**
41
+ * ✅ 优化 3: 合并 Approve 和 Sell
42
+ */
43
+ export async function buildSellUserOps(params) {
44
+ const { sellers, sellerAis, sellAmountsWei, tokenAddress, effectiveRouter, deadline, wokbAddress, sellerAllowances, nonceMap, consumeInitCode, aaManager, config, defaultApproveGasLimit, defaultSellGasLimit } = params;
45
+ const sellOpPromises = sellers.map(async (w, i) => {
46
+ const ai = sellerAis[i];
47
+ const sender = ai.sender;
48
+ const sellWei = sellAmountsWei[i] ?? 0n;
49
+ if (sellWei <= 0n)
50
+ return null;
51
+ const allowance = sellerAllowances.get(sender) ?? 0n;
52
+ const needApprove = allowance < sellWei;
53
+ if (needApprove) {
54
+ // ✅ 合并 approve + sell 到一个 UserOp
55
+ const approveData = encodeApproveCall(effectiveRouter, ethers.MaxUint256);
56
+ const sellSwapData = encodeSwapExactTokensForETHSupportingFee(sellWei, 0n, [tokenAddress, wokbAddress], sender, deadline);
57
+ const batchCallData = encodeExecuteBatch([tokenAddress, effectiveRouter], [0n, 0n], [approveData, sellSwapData]);
58
+ const { userOp, prefundWei } = await aaManager.buildUserOpWithFixedGas({
59
+ ownerWallet: w,
60
+ sender,
61
+ nonce: nonceMap.next(sender),
62
+ initCode: consumeInitCode(sender),
63
+ callData: batchCallData,
64
+ deployed: ai.deployed,
65
+ fixedGas: {
66
+ ...(config.fixedGas ?? {}),
67
+ callGasLimit: config.fixedGas?.callGasLimit ?? (defaultApproveGasLimit + defaultSellGasLimit),
68
+ },
69
+ });
70
+ await aaManager.ensureSenderBalance(w, sender, prefundWei + parseOkb('0.0001'), `buyFirstDex/seller${i + 1}/batch-fund`);
71
+ const signed = await aaManager.signUserOp(userOp, w);
72
+ return signed.userOp;
73
+ }
74
+ else {
75
+ // 只需要 sell
76
+ const sellSwapData = encodeSwapExactTokensForETHSupportingFee(sellWei, 0n, [tokenAddress, wokbAddress], sender, deadline);
77
+ const sellCallData = encodeExecute(effectiveRouter, 0n, sellSwapData);
78
+ const { userOp, prefundWei } = await aaManager.buildUserOpWithFixedGas({
79
+ ownerWallet: w,
80
+ sender,
81
+ nonce: nonceMap.next(sender),
82
+ initCode: consumeInitCode(sender),
83
+ callData: sellCallData,
84
+ deployed: ai.deployed,
85
+ fixedGas: {
86
+ ...(config.fixedGas ?? {}),
87
+ callGasLimit: config.fixedGas?.callGasLimit ?? defaultSellGasLimit,
88
+ },
89
+ });
90
+ await aaManager.ensureSenderBalance(w, sender, prefundWei + parseOkb('0.0001'), `buyFirstDex/seller${i + 1}/sell-fund`);
91
+ const signed = await aaManager.signUserOp(userOp, w);
92
+ return signed.userOp;
93
+ }
94
+ });
95
+ const ops = await Promise.all(sellOpPromises);
96
+ return ops.filter((op) => op !== null);
97
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.5.52",
3
+ "version": "1.5.53",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",