four-flap-meme-sdk 1.4.62 → 1.4.63

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 (109) hide show
  1. package/dist/contracts/tm-bundle-merkle/swap-buy-first.d.ts +4 -0
  2. package/dist/contracts/tm-bundle-merkle/swap-buy-first.js +133 -78
  3. package/dist/flap/portal-bundle-merkle/encryption.d.ts +16 -0
  4. package/dist/flap/portal-bundle-merkle/encryption.js +146 -0
  5. package/dist/flap/portal-bundle-merkle/swap-buy-first.d.ts +4 -0
  6. package/dist/flap/portal-bundle-merkle/swap-buy-first.js +160 -41
  7. package/dist/index.d.ts +0 -1
  8. package/dist/index.js +0 -1
  9. package/dist/pancake/bundle-buy-first.d.ts +4 -0
  10. package/dist/pancake/bundle-buy-first.js +185 -35
  11. package/package.json +3 -38
  12. package/dist/sol/constants.d.ts +0 -126
  13. package/dist/sol/constants.js +0 -145
  14. package/dist/sol/dex/index.d.ts +0 -8
  15. package/dist/sol/dex/index.js +0 -12
  16. package/dist/sol/dex/meteora/client.d.ts +0 -76
  17. package/dist/sol/dex/meteora/client.js +0 -219
  18. package/dist/sol/dex/meteora/damm-v1-bundle.d.ts +0 -61
  19. package/dist/sol/dex/meteora/damm-v1-bundle.js +0 -112
  20. package/dist/sol/dex/meteora/damm-v1.d.ts +0 -118
  21. package/dist/sol/dex/meteora/damm-v1.js +0 -315
  22. package/dist/sol/dex/meteora/damm-v2-bundle.d.ts +0 -82
  23. package/dist/sol/dex/meteora/damm-v2-bundle.js +0 -242
  24. package/dist/sol/dex/meteora/damm-v2.d.ts +0 -172
  25. package/dist/sol/dex/meteora/damm-v2.js +0 -632
  26. package/dist/sol/dex/meteora/dbc-bundle.d.ts +0 -123
  27. package/dist/sol/dex/meteora/dbc-bundle.js +0 -304
  28. package/dist/sol/dex/meteora/dbc.d.ts +0 -192
  29. package/dist/sol/dex/meteora/dbc.js +0 -619
  30. package/dist/sol/dex/meteora/dlmm-bundle.d.ts +0 -39
  31. package/dist/sol/dex/meteora/dlmm-bundle.js +0 -189
  32. package/dist/sol/dex/meteora/dlmm.d.ts +0 -157
  33. package/dist/sol/dex/meteora/dlmm.js +0 -671
  34. package/dist/sol/dex/meteora/index.d.ts +0 -25
  35. package/dist/sol/dex/meteora/index.js +0 -65
  36. package/dist/sol/dex/meteora/types.d.ts +0 -787
  37. package/dist/sol/dex/meteora/types.js +0 -110
  38. package/dist/sol/dex/orca/index.d.ts +0 -10
  39. package/dist/sol/dex/orca/index.js +0 -16
  40. package/dist/sol/dex/orca/orca-bundle.d.ts +0 -41
  41. package/dist/sol/dex/orca/orca-bundle.js +0 -173
  42. package/dist/sol/dex/orca/orca.d.ts +0 -65
  43. package/dist/sol/dex/orca/orca.js +0 -474
  44. package/dist/sol/dex/orca/types.d.ts +0 -263
  45. package/dist/sol/dex/orca/types.js +0 -38
  46. package/dist/sol/dex/orca/wavebreak-bundle.d.ts +0 -34
  47. package/dist/sol/dex/orca/wavebreak-bundle.js +0 -198
  48. package/dist/sol/dex/orca/wavebreak-types.d.ts +0 -227
  49. package/dist/sol/dex/orca/wavebreak-types.js +0 -23
  50. package/dist/sol/dex/orca/wavebreak.d.ts +0 -78
  51. package/dist/sol/dex/orca/wavebreak.js +0 -497
  52. package/dist/sol/dex/pump/index.d.ts +0 -9
  53. package/dist/sol/dex/pump/index.js +0 -14
  54. package/dist/sol/dex/pump/pump-bundle.d.ts +0 -92
  55. package/dist/sol/dex/pump/pump-bundle.js +0 -383
  56. package/dist/sol/dex/pump/pump-swap-bundle.d.ts +0 -103
  57. package/dist/sol/dex/pump/pump-swap-bundle.js +0 -380
  58. package/dist/sol/dex/pump/pump-swap.d.ts +0 -46
  59. package/dist/sol/dex/pump/pump-swap.js +0 -199
  60. package/dist/sol/dex/pump/pump.d.ts +0 -35
  61. package/dist/sol/dex/pump/pump.js +0 -352
  62. package/dist/sol/dex/pump/types.d.ts +0 -215
  63. package/dist/sol/dex/pump/types.js +0 -5
  64. package/dist/sol/dex/raydium/index.d.ts +0 -8
  65. package/dist/sol/dex/raydium/index.js +0 -12
  66. package/dist/sol/dex/raydium/launchlab.d.ts +0 -68
  67. package/dist/sol/dex/raydium/launchlab.js +0 -210
  68. package/dist/sol/dex/raydium/raydium-bundle.d.ts +0 -64
  69. package/dist/sol/dex/raydium/raydium-bundle.js +0 -324
  70. package/dist/sol/dex/raydium/raydium.d.ts +0 -40
  71. package/dist/sol/dex/raydium/raydium.js +0 -366
  72. package/dist/sol/dex/raydium/types.d.ts +0 -240
  73. package/dist/sol/dex/raydium/types.js +0 -5
  74. package/dist/sol/index.d.ts +0 -10
  75. package/dist/sol/index.js +0 -16
  76. package/dist/sol/jito/bundle.d.ts +0 -90
  77. package/dist/sol/jito/bundle.js +0 -263
  78. package/dist/sol/jito/index.d.ts +0 -7
  79. package/dist/sol/jito/index.js +0 -7
  80. package/dist/sol/jito/tip.d.ts +0 -51
  81. package/dist/sol/jito/tip.js +0 -83
  82. package/dist/sol/jito/types.d.ts +0 -100
  83. package/dist/sol/jito/types.js +0 -5
  84. package/dist/sol/token/create-complete.d.ts +0 -115
  85. package/dist/sol/token/create-complete.js +0 -235
  86. package/dist/sol/token/create-token.d.ts +0 -57
  87. package/dist/sol/token/create-token.js +0 -230
  88. package/dist/sol/token/index.d.ts +0 -9
  89. package/dist/sol/token/index.js +0 -14
  90. package/dist/sol/token/metadata-upload.d.ts +0 -86
  91. package/dist/sol/token/metadata-upload.js +0 -173
  92. package/dist/sol/token/metadata.d.ts +0 -92
  93. package/dist/sol/token/metadata.js +0 -274
  94. package/dist/sol/token/types.d.ts +0 -153
  95. package/dist/sol/token/types.js +0 -5
  96. package/dist/sol/types.d.ts +0 -176
  97. package/dist/sol/types.js +0 -7
  98. package/dist/sol/utils/balance.d.ts +0 -160
  99. package/dist/sol/utils/balance.js +0 -638
  100. package/dist/sol/utils/connection.d.ts +0 -78
  101. package/dist/sol/utils/connection.js +0 -168
  102. package/dist/sol/utils/index.d.ts +0 -9
  103. package/dist/sol/utils/index.js +0 -9
  104. package/dist/sol/utils/lp-inspect.d.ts +0 -129
  105. package/dist/sol/utils/lp-inspect.js +0 -900
  106. package/dist/sol/utils/transfer.d.ts +0 -125
  107. package/dist/sol/utils/transfer.js +0 -220
  108. package/dist/sol/utils/wallet.d.ts +0 -107
  109. package/dist/sol/utils/wallet.js +0 -210
@@ -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>;