four-flap-meme-sdk 1.4.62 → 1.4.64

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.
Files changed (111) hide show
  1. package/dist/abis/common.d.ts +1 -1
  2. package/dist/abis/common.js +2 -1
  3. package/dist/contracts/tm-bundle-merkle/swap-buy-first.d.ts +4 -0
  4. package/dist/contracts/tm-bundle-merkle/swap-buy-first.js +133 -78
  5. package/dist/flap/portal-bundle-merkle/encryption.d.ts +16 -0
  6. package/dist/flap/portal-bundle-merkle/encryption.js +146 -0
  7. package/dist/flap/portal-bundle-merkle/swap-buy-first.d.ts +4 -0
  8. package/dist/flap/portal-bundle-merkle/swap-buy-first.js +160 -41
  9. package/dist/index.d.ts +0 -1
  10. package/dist/index.js +0 -1
  11. package/dist/pancake/bundle-buy-first.d.ts +4 -0
  12. package/dist/pancake/bundle-buy-first.js +185 -35
  13. package/package.json +3 -38
  14. package/dist/sol/constants.d.ts +0 -126
  15. package/dist/sol/constants.js +0 -145
  16. package/dist/sol/dex/index.d.ts +0 -8
  17. package/dist/sol/dex/index.js +0 -12
  18. package/dist/sol/dex/meteora/client.d.ts +0 -76
  19. package/dist/sol/dex/meteora/client.js +0 -219
  20. package/dist/sol/dex/meteora/damm-v1-bundle.d.ts +0 -61
  21. package/dist/sol/dex/meteora/damm-v1-bundle.js +0 -112
  22. package/dist/sol/dex/meteora/damm-v1.d.ts +0 -118
  23. package/dist/sol/dex/meteora/damm-v1.js +0 -315
  24. package/dist/sol/dex/meteora/damm-v2-bundle.d.ts +0 -82
  25. package/dist/sol/dex/meteora/damm-v2-bundle.js +0 -242
  26. package/dist/sol/dex/meteora/damm-v2.d.ts +0 -172
  27. package/dist/sol/dex/meteora/damm-v2.js +0 -632
  28. package/dist/sol/dex/meteora/dbc-bundle.d.ts +0 -123
  29. package/dist/sol/dex/meteora/dbc-bundle.js +0 -304
  30. package/dist/sol/dex/meteora/dbc.d.ts +0 -192
  31. package/dist/sol/dex/meteora/dbc.js +0 -619
  32. package/dist/sol/dex/meteora/dlmm-bundle.d.ts +0 -39
  33. package/dist/sol/dex/meteora/dlmm-bundle.js +0 -189
  34. package/dist/sol/dex/meteora/dlmm.d.ts +0 -157
  35. package/dist/sol/dex/meteora/dlmm.js +0 -671
  36. package/dist/sol/dex/meteora/index.d.ts +0 -25
  37. package/dist/sol/dex/meteora/index.js +0 -65
  38. package/dist/sol/dex/meteora/types.d.ts +0 -787
  39. package/dist/sol/dex/meteora/types.js +0 -110
  40. package/dist/sol/dex/orca/index.d.ts +0 -10
  41. package/dist/sol/dex/orca/index.js +0 -16
  42. package/dist/sol/dex/orca/orca-bundle.d.ts +0 -41
  43. package/dist/sol/dex/orca/orca-bundle.js +0 -173
  44. package/dist/sol/dex/orca/orca.d.ts +0 -65
  45. package/dist/sol/dex/orca/orca.js +0 -474
  46. package/dist/sol/dex/orca/types.d.ts +0 -263
  47. package/dist/sol/dex/orca/types.js +0 -38
  48. package/dist/sol/dex/orca/wavebreak-bundle.d.ts +0 -34
  49. package/dist/sol/dex/orca/wavebreak-bundle.js +0 -198
  50. package/dist/sol/dex/orca/wavebreak-types.d.ts +0 -227
  51. package/dist/sol/dex/orca/wavebreak-types.js +0 -23
  52. package/dist/sol/dex/orca/wavebreak.d.ts +0 -78
  53. package/dist/sol/dex/orca/wavebreak.js +0 -497
  54. package/dist/sol/dex/pump/index.d.ts +0 -9
  55. package/dist/sol/dex/pump/index.js +0 -14
  56. package/dist/sol/dex/pump/pump-bundle.d.ts +0 -92
  57. package/dist/sol/dex/pump/pump-bundle.js +0 -383
  58. package/dist/sol/dex/pump/pump-swap-bundle.d.ts +0 -103
  59. package/dist/sol/dex/pump/pump-swap-bundle.js +0 -380
  60. package/dist/sol/dex/pump/pump-swap.d.ts +0 -46
  61. package/dist/sol/dex/pump/pump-swap.js +0 -199
  62. package/dist/sol/dex/pump/pump.d.ts +0 -35
  63. package/dist/sol/dex/pump/pump.js +0 -352
  64. package/dist/sol/dex/pump/types.d.ts +0 -215
  65. package/dist/sol/dex/pump/types.js +0 -5
  66. package/dist/sol/dex/raydium/index.d.ts +0 -8
  67. package/dist/sol/dex/raydium/index.js +0 -12
  68. package/dist/sol/dex/raydium/launchlab.d.ts +0 -68
  69. package/dist/sol/dex/raydium/launchlab.js +0 -210
  70. package/dist/sol/dex/raydium/raydium-bundle.d.ts +0 -64
  71. package/dist/sol/dex/raydium/raydium-bundle.js +0 -324
  72. package/dist/sol/dex/raydium/raydium.d.ts +0 -40
  73. package/dist/sol/dex/raydium/raydium.js +0 -366
  74. package/dist/sol/dex/raydium/types.d.ts +0 -240
  75. package/dist/sol/dex/raydium/types.js +0 -5
  76. package/dist/sol/index.d.ts +0 -10
  77. package/dist/sol/index.js +0 -16
  78. package/dist/sol/jito/bundle.d.ts +0 -90
  79. package/dist/sol/jito/bundle.js +0 -263
  80. package/dist/sol/jito/index.d.ts +0 -7
  81. package/dist/sol/jito/index.js +0 -7
  82. package/dist/sol/jito/tip.d.ts +0 -51
  83. package/dist/sol/jito/tip.js +0 -83
  84. package/dist/sol/jito/types.d.ts +0 -100
  85. package/dist/sol/jito/types.js +0 -5
  86. package/dist/sol/token/create-complete.d.ts +0 -115
  87. package/dist/sol/token/create-complete.js +0 -235
  88. package/dist/sol/token/create-token.d.ts +0 -57
  89. package/dist/sol/token/create-token.js +0 -230
  90. package/dist/sol/token/index.d.ts +0 -9
  91. package/dist/sol/token/index.js +0 -14
  92. package/dist/sol/token/metadata-upload.d.ts +0 -86
  93. package/dist/sol/token/metadata-upload.js +0 -173
  94. package/dist/sol/token/metadata.d.ts +0 -92
  95. package/dist/sol/token/metadata.js +0 -274
  96. package/dist/sol/token/types.d.ts +0 -153
  97. package/dist/sol/token/types.js +0 -5
  98. package/dist/sol/types.d.ts +0 -176
  99. package/dist/sol/types.js +0 -7
  100. package/dist/sol/utils/balance.d.ts +0 -160
  101. package/dist/sol/utils/balance.js +0 -638
  102. package/dist/sol/utils/connection.d.ts +0 -78
  103. package/dist/sol/utils/connection.js +0 -168
  104. package/dist/sol/utils/index.d.ts +0 -9
  105. package/dist/sol/utils/index.js +0 -9
  106. package/dist/sol/utils/lp-inspect.d.ts +0 -129
  107. package/dist/sol/utils/lp-inspect.js +0 -900
  108. package/dist/sol/utils/transfer.d.ts +0 -125
  109. package/dist/sol/utils/transfer.js +0 -220
  110. package/dist/sol/utils/wallet.d.ts +0 -107
  111. package/dist/sol/utils/wallet.js +0 -210
@@ -5,7 +5,7 @@
5
5
  */
6
6
  /** 完整 ERC20 ABI */
7
7
  export declare const ERC20_ABI: string[];
8
- /** 精简 ERC20 ABI(只有余额查询) */
8
+ /** 精简 ERC20 ABI(余额查询 + decimals) */
9
9
  export declare const ERC20_BALANCE_ABI: string[];
10
10
  /** ERC20 授权相关 ABI */
11
11
  export declare const ERC20_ALLOWANCE_ABI: string[];
@@ -18,9 +18,10 @@ export const ERC20_ABI = [
18
18
  'function transfer(address to, uint256 amount) external returns (bool)',
19
19
  'function transferFrom(address from, address to, uint256 amount) external returns (bool)',
20
20
  ];
21
- /** 精简 ERC20 ABI(只有余额查询) */
21
+ /** 精简 ERC20 ABI(余额查询 + decimals) */
22
22
  export const ERC20_BALANCE_ABI = [
23
23
  'function balanceOf(address account) external view returns (uint256)',
24
+ 'function decimals() external view returns (uint8)',
24
25
  ];
25
26
  /** ERC20 授权相关 ABI */
26
27
  export const ERC20_ALLOWANCE_ABI = [
@@ -24,6 +24,8 @@ export interface FourBundleBuyFirstSignParams {
24
24
  sellerPrivateKey: string;
25
25
  tokenAddress: string;
26
26
  config: FourBuyFirstSignConfig;
27
+ buyCount?: number;
28
+ sellCount?: number;
27
29
  }
28
30
  export interface FourBundleBuyFirstParams {
29
31
  buyerPrivateKey: string;
@@ -43,6 +45,8 @@ export type FourBuyFirstResult = {
43
45
  buyAmount: string;
44
46
  sellAmount: string;
45
47
  profitAmount?: string;
48
+ buyCount?: number;
49
+ sellCount?: number;
46
50
  };
47
51
  };
48
52
  export declare function fourBundleBuyFirstMerkle(params: FourBundleBuyFirstSignParams): Promise<FourBuyFirstResult>;
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { ethers, Contract, Wallet } from 'ethers';
7
7
  import { NonceManager, getOptimizedGasPrice, buildProfitHopTransactions, PROFIT_HOP_COUNT } from '../../utils/bundle-helpers.js';
8
- import { ADDRESSES, PROFIT_CONFIG, BLOCKRAZOR_BUILDER_EOA } from '../../utils/constants.js';
8
+ import { ADDRESSES, BLOCKRAZOR_BUILDER_EOA } from '../../utils/constants.js';
9
9
  import { TM_ABI, HELPER3_ABI, TM_ADDRESS } from './swap-internal.js';
10
10
  import { getTxType, getGasPriceConfig, getProfitRecipient, getBribeAmount } from './config.js';
11
11
  import { trySell } from '../tm.js';
@@ -17,8 +17,61 @@ function getGasLimit(config, defaultGas = 800000) {
17
17
  const multiplier = config.gasLimitMultiplier ?? 1.0;
18
18
  return BigInt(Math.ceil(defaultGas * multiplier));
19
19
  }
20
+ // ==================== 多笔买卖常量 ====================
21
+ /** 最大 Bundle 签名数 */
22
+ const MAX_BUNDLE_SIGNATURES = 50;
23
+ /** 贿赂交易数 */
24
+ const BRIBE_TX_COUNT = 1;
25
+ /** 利润中转交易数(支付者 1 笔 + 中转钱包 PROFIT_HOP_COUNT 笔 + 最终接收 1 笔) */
26
+ const PROFIT_TX_COUNT = PROFIT_HOP_COUNT + 2; // 2 + 2 = 4
27
+ /** 最大买卖交易数 */
28
+ const MAX_SWAP_TX_COUNT = MAX_BUNDLE_SIGNATURES - BRIBE_TX_COUNT - PROFIT_TX_COUNT; // 50 - 1 - 4 = 45
29
+ /** 每笔交易利润比例(基点):3 bps = 0.03% = 万分之三 */
30
+ const PROFIT_RATE_PER_TX_BPS = 3;
31
+ /**
32
+ * 验证买卖笔数
33
+ */
34
+ function validateSwapCounts(buyCount, sellCount) {
35
+ if (buyCount < 1) {
36
+ throw new Error(`买入笔数必须至少为 1,当前为 ${buyCount}`);
37
+ }
38
+ if (sellCount < 1) {
39
+ throw new Error(`卖出笔数必须至少为 1,当前为 ${sellCount}`);
40
+ }
41
+ const totalSwaps = buyCount + sellCount;
42
+ if (totalSwaps > MAX_SWAP_TX_COUNT) {
43
+ throw new Error(`买卖笔数总和 (${totalSwaps}) 超出限制 (${MAX_SWAP_TX_COUNT}),` +
44
+ `最大签名数为 ${MAX_BUNDLE_SIGNATURES}(${BRIBE_TX_COUNT} 贿赂 + ${PROFIT_TX_COUNT} 利润 + ${MAX_SWAP_TX_COUNT} 买卖)`);
45
+ }
46
+ }
47
+ /**
48
+ * 将金额均匀拆分成多份
49
+ */
50
+ function splitAmount(totalAmount, count) {
51
+ if (count <= 0) {
52
+ throw new Error('拆分份数必须大于 0');
53
+ }
54
+ if (count === 1) {
55
+ return [totalAmount];
56
+ }
57
+ const baseAmount = totalAmount / BigInt(count);
58
+ const remainder = totalAmount % BigInt(count);
59
+ const amounts = [];
60
+ for (let i = 0; i < count - 1; i++) {
61
+ amounts.push(baseAmount);
62
+ }
63
+ amounts.push(baseAmount + remainder);
64
+ return amounts;
65
+ }
20
66
  export async function fourBundleBuyFirstMerkle(params) {
21
- const { buyerPrivateKey, buyerFunds, buyerFundsPercentage, sellerPrivateKey, tokenAddress, config } = params;
67
+ const { buyerPrivateKey, buyerFunds, buyerFundsPercentage, sellerPrivateKey, tokenAddress, config, buyCount: _buyCount, sellCount: _sellCount } = params;
68
+ // ✅ 解析并验证买卖笔数
69
+ const buyCount = _buyCount ?? 1;
70
+ const sellCount = _sellCount ?? 1;
71
+ validateSwapCounts(buyCount, sellCount);
72
+ // ✅ 计算利润比例:每笔万分之3
73
+ const totalTxCount = buyCount + sellCount;
74
+ const profitRateBps = PROFIT_RATE_PER_TX_BPS * totalTxCount;
22
75
  const chainIdNum = config.chainId ?? 56;
23
76
  const provider = new ethers.JsonRpcProvider(config.rpcUrl, {
24
77
  chainId: chainIdNum,
@@ -75,115 +128,82 @@ export async function fourBundleBuyFirstMerkle(params) {
75
128
  // ✅ 优化:第三批并行 - trySell、构建交易、获取 nonces
76
129
  const tmBuyer = new Contract(TM_ADDRESS, TM_ABI, buyer);
77
130
  const tmSeller = new Contract(TM_ADDRESS, TM_ABI, seller);
131
+ // ✅ 拆分买入和卖出金额
132
+ const buyAmountsWei = splitAmount(buyerFundsWei, buyCount);
133
+ const sellAmountsWei = splitAmount(sellAmountWei, sellCount);
78
134
  // 预先规划 nonces
79
135
  const extractProfit = true;
80
- const profitRateBps = PROFIT_CONFIG.RATE_BPS_SWAP; // 万分之六
81
136
  // ✅ 获取贿赂金额
82
137
  const bribeAmount = getBribeAmount(config);
83
138
  const needBribeTx = bribeAmount > 0n;
84
- // 计算需要的 nonce 数量
85
- let buyerNonceCount = 1; // 买入交易
86
- let sellerNonceCount = 1; // 卖出交易
87
- if (needBribeTx)
88
- sellerNonceCount++; // 贿赂交易(由卖方发送)
89
- if (extractProfit)
90
- sellerNonceCount++; // 利润交易
91
- // ✅ 优化:使用批量 nonce 获取(单次网络往返)
92
- const getNoncesPromise = sameAddress
93
- ? nonceManager.getNextNonceBatch(seller, buyerNonceCount + sellerNonceCount)
94
- .then(nonces => ({ buyerNonces: [], sellerNonces: nonces }))
95
- : nonceManager.getNextNoncesForWallets([buyer, seller])
96
- .then(initialNonces => ({
97
- // buyer 只需要 1 个 nonce
98
- buyerNonces: [initialNonces[0]],
99
- // seller 需要 sellerNonceCount 个连续 nonce,从初始 nonce 开始
100
- sellerNonces: Array.from({ length: sellerNonceCount }, (_, i) => initialNonces[1] + i)
101
- }));
102
- const [sellResult, buyUnsigned, sellUnsigned, noncesResult] = await Promise.all([
139
+ // 构建多笔买入和卖出交易
140
+ const buyUnsignedPromises = buyAmountsWei.map(amount => tmBuyer.buyTokenAMAP.populateTransaction(0n, tokenAddress, buyer.address, amount, 0n, { value: amount }));
141
+ const sellUnsignedPromises = sellAmountsWei.map(amount => tmSeller.sellToken.populateTransaction(0n, tokenAddress, amount, 0n));
142
+ // ✅ 规划多笔交易 nonce
143
+ const multiNoncePlan = await planMultiNonces({
144
+ buyer,
145
+ seller,
146
+ buyCount,
147
+ sellCount,
148
+ extractProfit,
149
+ needBribeTx,
150
+ sameAddress,
151
+ nonceManager
152
+ });
153
+ const [sellResult, buyUnsignedArray, sellUnsignedArray] = await Promise.all([
103
154
  trySell('BSC', config.rpcUrl, tokenAddress, sellAmountWei),
104
- tmBuyer.buyTokenAMAP.populateTransaction(0n, tokenAddress, buyer.address, buyerFundsWei, 0n, { value: buyerFundsWei }),
105
- tmSeller.sellToken.populateTransaction(0n, tokenAddress, sellAmountWei, 0n),
106
- getNoncesPromise
155
+ Promise.all(buyUnsignedPromises),
156
+ Promise.all(sellUnsignedPromises)
107
157
  ]);
108
- const { buyerNonces, sellerNonces } = noncesResult;
109
158
  const estimatedSellFunds = sellResult.funds;
110
159
  const profitAmount = extractProfit ? (estimatedSellFunds * BigInt(profitRateBps)) / 10000n : 0n;
111
- // 分配 nonces
112
- let buyerNonce;
113
- let sellerNonce;
114
- let bribeNonce;
115
- let profitNonce;
116
- if (sameAddress) {
117
- let idx = 0;
118
- if (needBribeTx)
119
- bribeNonce = sellerNonces[idx++];
120
- buyerNonce = sellerNonces[idx++];
121
- sellerNonce = sellerNonces[idx++];
122
- if (extractProfit)
123
- profitNonce = sellerNonces[idx];
124
- }
125
- else {
126
- buyerNonce = buyerNonces[0];
127
- let idx = 0;
128
- if (needBribeTx)
129
- bribeNonce = sellerNonces[idx++];
130
- sellerNonce = sellerNonces[idx++];
131
- if (extractProfit)
132
- profitNonce = sellerNonces[idx];
133
- }
134
- // ✅ 并行签名所有交易
135
- const signPromises = [];
136
160
  // ✅ 贿赂交易放在首位
137
161
  let bribeTx = null;
138
- if (needBribeTx && bribeNonce !== undefined) {
139
- signPromises.push(seller.signTransaction({
162
+ if (needBribeTx && multiNoncePlan.bribeNonce !== undefined) {
163
+ bribeTx = await seller.signTransaction({
140
164
  to: BLOCKRAZOR_BUILDER_EOA,
141
165
  value: bribeAmount,
142
- nonce: bribeNonce,
166
+ nonce: multiNoncePlan.bribeNonce,
143
167
  gasPrice,
144
168
  gasLimit: 21000n,
145
169
  chainId: chainIdNum,
146
170
  type: txType
147
- }));
171
+ });
148
172
  }
149
- // 买入和卖出交易
150
- signPromises.push(buyer.signTransaction({
151
- ...buyUnsigned,
173
+ // ✅ 并行签名所有买入和卖出交易
174
+ const buySignPromises = buyUnsignedArray.map((unsigned, i) => buyer.signTransaction({
175
+ ...unsigned,
152
176
  from: buyer.address,
153
- nonce: buyerNonce,
177
+ nonce: multiNoncePlan.buyerNonces[i],
154
178
  gasLimit: finalGasLimit,
155
179
  gasPrice,
156
180
  chainId: chainIdNum,
157
181
  type: txType,
158
- value: buyerFundsWei
159
- }),
160
- // 卖出交易 value 必须为 0,不能发送原生代币
161
- seller.signTransaction({
162
- ...sellUnsigned,
182
+ value: buyAmountsWei[i]
183
+ }));
184
+ const sellSignPromises = sellUnsignedArray.map((unsigned, i) => seller.signTransaction({
185
+ ...unsigned,
163
186
  from: seller.address,
164
- nonce: sellerNonce,
187
+ nonce: multiNoncePlan.sellerNonces[i],
165
188
  gasLimit: finalGasLimit,
166
189
  gasPrice,
167
190
  chainId: chainIdNum,
168
191
  type: txType,
169
- value: 0n // ✅ 卖出交易不发送原生代币
192
+ value: 0n
170
193
  }));
171
- const signedTxs = await Promise.all(signPromises);
172
- // 解析签名结果
173
- let idx = 0;
174
- if (needBribeTx)
175
- bribeTx = signedTxs[idx++];
176
- const signedBuy = signedTxs[idx++];
177
- const signedSell = signedTxs[idx++];
194
+ const [signedBuys, signedSells] = await Promise.all([
195
+ Promise.all(buySignPromises),
196
+ Promise.all(sellSignPromises)
197
+ ]);
178
198
  nonceManager.clearTemp();
179
- // ✅ 组装交易列表:贿赂 → 买入 → 卖出
199
+ // ✅ 组装交易列表:贿赂 → 买入(多笔) → 卖出(多笔)
180
200
  const allTransactions = [];
181
201
  if (bribeTx)
182
202
  allTransactions.push(bribeTx);
183
- allTransactions.push(signedBuy, signedSell);
203
+ allTransactions.push(...signedBuys, ...signedSells);
184
204
  // ✅ 利润多跳转账(强制 2 跳中转)
185
205
  let profitHopWallets;
186
- if (extractProfit && profitAmount > 0n && profitNonce !== undefined) {
206
+ if (extractProfit && profitAmount > 0n && multiNoncePlan.profitNonce !== undefined) {
187
207
  const profitHopResult = await buildProfitHopTransactions({
188
208
  provider,
189
209
  payerWallet: seller,
@@ -193,7 +213,7 @@ export async function fourBundleBuyFirstMerkle(params) {
193
213
  gasPrice,
194
214
  chainId: chainIdNum,
195
215
  txType,
196
- startNonce: profitNonce
216
+ startNonce: multiNoncePlan.profitNonce
197
217
  });
198
218
  allTransactions.push(...profitHopResult.signedTransactions);
199
219
  profitHopWallets = profitHopResult.hopWallets; // ✅ 收集利润多跳钱包
@@ -206,7 +226,42 @@ export async function fourBundleBuyFirstMerkle(params) {
206
226
  sellerAddress: seller.address,
207
227
  buyAmount: ethers.formatEther(buyerFundsWei),
208
228
  sellAmount: ethers.formatUnits(sellAmountWei, decimals),
209
- profitAmount: extractProfit ? ethers.formatEther(profitAmount) : undefined
229
+ profitAmount: extractProfit ? ethers.formatEther(profitAmount) : undefined,
230
+ // ✅ 返回多笔买卖信息
231
+ buyCount,
232
+ sellCount
210
233
  }
211
234
  };
212
235
  }
236
+ /**
237
+ * ✅ 规划多笔交易 nonce
238
+ * 交易顺序:贿赂 → 买入(多笔) → 卖出(多笔) → 利润
239
+ */
240
+ async function planMultiNonces({ buyer, seller, buyCount, sellCount, extractProfit, needBribeTx, sameAddress, nonceManager }) {
241
+ const profitNonceCount = extractProfit ? 1 : 0;
242
+ if (sameAddress) {
243
+ const bribeTxCount = needBribeTx ? 1 : 0;
244
+ const totalTxCount = bribeTxCount + buyCount + sellCount + profitNonceCount;
245
+ const nonces = await nonceManager.getNextNonceBatch(buyer, totalTxCount);
246
+ let idx = 0;
247
+ const bribeNonce = needBribeTx ? nonces[idx++] : undefined;
248
+ const buyerNonces = nonces.slice(idx, idx + buyCount);
249
+ idx += buyCount;
250
+ const sellerNonces = nonces.slice(idx, idx + sellCount);
251
+ idx += sellCount;
252
+ const profitNonce = extractProfit ? nonces[idx] : undefined;
253
+ return { buyerNonces, sellerNonces, bribeNonce, profitNonce };
254
+ }
255
+ const bribeTxCount = needBribeTx ? 1 : 0;
256
+ const sellerTxCount = bribeTxCount + sellCount + profitNonceCount;
257
+ const [buyerNonces, sellerNoncesAll] = await Promise.all([
258
+ nonceManager.getNextNonceBatch(buyer, buyCount),
259
+ nonceManager.getNextNonceBatch(seller, sellerTxCount)
260
+ ]);
261
+ let idx = 0;
262
+ const bribeNonce = needBribeTx ? sellerNoncesAll[idx++] : undefined;
263
+ const sellerNonces = sellerNoncesAll.slice(idx, idx + sellCount);
264
+ idx += sellCount;
265
+ const profitNonce = extractProfit ? sellerNoncesAll[idx] : undefined;
266
+ return { buyerNonces, sellerNonces, bribeNonce, profitNonce };
267
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * ECDH + AES-GCM 加密工具(浏览器兼容)
3
+ * 用于将签名交易用服务器公钥加密
4
+ */
5
+ /**
6
+ * 用服务器公钥加密签名交易(ECDH + AES-GCM)
7
+ *
8
+ * @param signedTransactions 签名后的交易数组
9
+ * @param publicKeyBase64 服务器提供的公钥(Base64 格式)
10
+ * @returns JSON 字符串 {e: 临时公钥, i: IV, d: 密文}
11
+ */
12
+ export declare function encryptWithPublicKey(signedTransactions: string[], publicKeyBase64: string): Promise<string>;
13
+ /**
14
+ * 验证公钥格式(Base64)
15
+ */
16
+ export declare function validatePublicKey(publicKeyBase64: string): boolean;
@@ -0,0 +1,146 @@
1
+ /**
2
+ * ECDH + AES-GCM 加密工具(浏览器兼容)
3
+ * 用于将签名交易用服务器公钥加密
4
+ */
5
+ /**
6
+ * 获取全局 crypto 对象(最简单直接的方式)
7
+ */
8
+ function getCryptoAPI() {
9
+ // 尝试所有可能的全局对象,优先浏览器环境
10
+ const cryptoObj = (typeof window !== 'undefined' && window.crypto) ||
11
+ (typeof self !== 'undefined' && self.crypto) ||
12
+ (typeof global !== 'undefined' && global.crypto) ||
13
+ (typeof globalThis !== 'undefined' && globalThis.crypto);
14
+ if (!cryptoObj) {
15
+ const env = typeof window !== 'undefined' ? 'Browser' : 'Node.js';
16
+ const protocol = typeof location !== 'undefined' ? location.protocol : 'unknown';
17
+ throw new Error(`❌ Crypto API 不可用。环境: ${env}, 协议: ${protocol}. ` +
18
+ '请确保在 HTTPS 或 localhost 下运行');
19
+ }
20
+ return cryptoObj;
21
+ }
22
+ /**
23
+ * 获取 SubtleCrypto(用于加密操作)
24
+ */
25
+ function getSubtleCrypto() {
26
+ const crypto = getCryptoAPI();
27
+ if (!crypto.subtle) {
28
+ const protocol = typeof location !== 'undefined' ? location.protocol : 'unknown';
29
+ const hostname = typeof location !== 'undefined' ? location.hostname : 'unknown';
30
+ throw new Error(`❌ SubtleCrypto API 不可用。协议: ${protocol}, 主机: ${hostname}. ` +
31
+ '请确保:1) 使用 HTTPS (或 localhost);2) 浏览器支持 Web Crypto API;' +
32
+ '3) 不在无痕/隐私浏览模式下');
33
+ }
34
+ return crypto.subtle;
35
+ }
36
+ /**
37
+ * Base64 转 ArrayBuffer(优先使用浏览器 API)
38
+ */
39
+ function base64ToArrayBuffer(base64) {
40
+ // 浏览器环境(优先)
41
+ if (typeof atob !== 'undefined') {
42
+ const binaryString = atob(base64);
43
+ const bytes = new Uint8Array(binaryString.length);
44
+ for (let i = 0; i < binaryString.length; i++) {
45
+ bytes[i] = binaryString.charCodeAt(i);
46
+ }
47
+ return bytes.buffer;
48
+ }
49
+ // Node.js 环境(fallback)
50
+ if (typeof Buffer !== 'undefined') {
51
+ return Buffer.from(base64, 'base64').buffer;
52
+ }
53
+ throw new Error('❌ Base64 解码不可用');
54
+ }
55
+ /**
56
+ * ArrayBuffer 转 Base64(优先使用浏览器 API)
57
+ */
58
+ function arrayBufferToBase64(buffer) {
59
+ // 浏览器环境(优先)
60
+ if (typeof btoa !== 'undefined') {
61
+ const bytes = new Uint8Array(buffer);
62
+ let binary = '';
63
+ for (let i = 0; i < bytes.length; i++) {
64
+ binary += String.fromCharCode(bytes[i]);
65
+ }
66
+ return btoa(binary);
67
+ }
68
+ // Node.js 环境(fallback)
69
+ if (typeof Buffer !== 'undefined') {
70
+ return Buffer.from(buffer).toString('base64');
71
+ }
72
+ throw new Error('❌ Base64 编码不可用');
73
+ }
74
+ /**
75
+ * 生成随机 Hex 字符串
76
+ */
77
+ function randomHex(length) {
78
+ const crypto = getCryptoAPI();
79
+ const array = new Uint8Array(length);
80
+ crypto.getRandomValues(array);
81
+ return Array.from(array)
82
+ .map(b => b.toString(16).padStart(2, '0'))
83
+ .join('');
84
+ }
85
+ /**
86
+ * 用服务器公钥加密签名交易(ECDH + AES-GCM)
87
+ *
88
+ * @param signedTransactions 签名后的交易数组
89
+ * @param publicKeyBase64 服务器提供的公钥(Base64 格式)
90
+ * @returns JSON 字符串 {e: 临时公钥, i: IV, d: 密文}
91
+ */
92
+ export async function encryptWithPublicKey(signedTransactions, publicKeyBase64) {
93
+ try {
94
+ // 0. 获取 SubtleCrypto 和 Crypto API
95
+ const subtle = getSubtleCrypto();
96
+ const crypto = getCryptoAPI();
97
+ // 1. 准备数据
98
+ const payload = {
99
+ signedTransactions,
100
+ timestamp: Date.now(),
101
+ nonce: randomHex(8)
102
+ };
103
+ const plaintext = JSON.stringify(payload);
104
+ // 2. 生成临时 ECDH 密钥对
105
+ const ephemeralKeyPair = await subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256' }, true, ['deriveKey']);
106
+ // 3. 导入服务器公钥
107
+ const publicKeyBuffer = base64ToArrayBuffer(publicKeyBase64);
108
+ const publicKey = await subtle.importKey('raw', publicKeyBuffer, { name: 'ECDH', namedCurve: 'P-256' }, false, []);
109
+ // 4. 派生共享密钥(AES-256)
110
+ const sharedKey = await subtle.deriveKey({ name: 'ECDH', public: publicKey }, ephemeralKeyPair.privateKey, { name: 'AES-GCM', length: 256 }, false, ['encrypt']);
111
+ // 5. AES-GCM 加密
112
+ const iv = crypto.getRandomValues(new Uint8Array(12));
113
+ const encrypted = await subtle.encrypt({ name: 'AES-GCM', iv }, sharedKey, new TextEncoder().encode(plaintext));
114
+ // 6. 导出临时公钥
115
+ const ephemeralPublicKeyRaw = await subtle.exportKey('raw', ephemeralKeyPair.publicKey);
116
+ // 7. 返回加密包(JSON 格式)
117
+ return JSON.stringify({
118
+ e: arrayBufferToBase64(ephemeralPublicKeyRaw), // 临时公钥
119
+ i: arrayBufferToBase64(iv.buffer), // IV
120
+ d: arrayBufferToBase64(encrypted) // 密文
121
+ });
122
+ }
123
+ catch (error) {
124
+ throw new Error(`加密失败: ${error?.message || String(error)}`);
125
+ }
126
+ }
127
+ /**
128
+ * 验证公钥格式(Base64)
129
+ */
130
+ export function validatePublicKey(publicKeyBase64) {
131
+ try {
132
+ if (!publicKeyBase64)
133
+ return false;
134
+ // Base64 字符集验证
135
+ if (!/^[A-Za-z0-9+/=]+$/.test(publicKeyBase64))
136
+ return false;
137
+ // ECDH P-256 公钥固定长度 65 字节(未压缩)
138
+ // Base64 编码后约 88 字符
139
+ if (publicKeyBase64.length < 80 || publicKeyBase64.length > 100)
140
+ return false;
141
+ return true;
142
+ }
143
+ catch {
144
+ return false;
145
+ }
146
+ }
@@ -30,6 +30,8 @@ export interface FlapBundleBuyFirstSignParams {
30
30
  config: FlapBuyFirstSignConfig;
31
31
  quoteToken?: string;
32
32
  quoteTokenDecimals?: number;
33
+ buyCount?: number;
34
+ sellCount?: number;
33
35
  }
34
36
  export interface FlapBundleBuyFirstParams {
35
37
  chain: FlapChain;
@@ -50,6 +52,8 @@ export type FlapBuyFirstResult = {
50
52
  buyAmount: string;
51
53
  sellAmount: string;
52
54
  profitAmount?: string;
55
+ buyCount?: number;
56
+ sellCount?: number;
53
57
  };
54
58
  };
55
59
  export declare function flapBundleBuyFirstMerkle(params: FlapBundleBuyFirstSignParams): Promise<FlapBuyFirstResult>;