four-flap-meme-sdk 1.4.77 → 1.4.79

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 -2
  2. package/dist/contracts/tm-bundle-merkle/swap-buy-first.js +195 -12
  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 -2
  6. package/dist/flap/portal-bundle-merkle/swap-buy-first.js +222 -9
  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 -2
  10. package/dist/pancake/bundle-buy-first.js +293 -14
  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
@@ -18,10 +18,12 @@ export interface FourBuyFirstConfig extends CommonBundleConfig {
18
18
  waitTimeoutMs?: number;
19
19
  }
20
20
  export interface FourBundleBuyFirstSignParams {
21
- buyerPrivateKey: string;
21
+ buyerPrivateKey?: string;
22
+ buyerPrivateKeys?: string[];
22
23
  buyerFunds?: string;
23
24
  buyerFundsPercentage?: number;
24
- sellerPrivateKey: string;
25
+ sellerPrivateKey?: string;
26
+ sellerPrivateKeys?: string[];
25
27
  tokenAddress: string;
26
28
  config: FourBuyFirstSignConfig;
27
29
  buyCount?: number;
@@ -26,8 +26,8 @@ const BRIBE_TX_COUNT = 1;
26
26
  const PROFIT_TX_COUNT = PROFIT_HOP_COUNT + 2; // 2 + 2 = 4
27
27
  /** 最大买卖交易数 */
28
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;
29
+ /** 每笔交易利润比例(基点):6 bps = 0.06% = 万分之六 */
30
+ const PROFIT_RATE_PER_TX_BPS = 6;
31
31
  /**
32
32
  * 验证买卖笔数
33
33
  */
@@ -83,10 +83,26 @@ function splitAmount(totalAmount, count) {
83
83
  return amounts;
84
84
  }
85
85
  export async function fourBundleBuyFirstMerkle(params) {
86
- const { buyerPrivateKey, buyerFunds, buyerFundsPercentage, sellerPrivateKey, tokenAddress, config, buyCount: _buyCount, sellCount: _sellCount } = params;
87
- // ✅ 解析并验证买卖笔数
86
+ const { buyerPrivateKey, buyerPrivateKeys, buyerFunds, buyerFundsPercentage, sellerPrivateKey, sellerPrivateKeys, tokenAddress, config, buyCount: _buyCount, sellCount: _sellCount } = params;
87
+ // ✅ 判断是否为多钱包模式
88
+ const isMultiWalletMode = !!(buyerPrivateKeys && buyerPrivateKeys.length > 0) ||
89
+ !!(sellerPrivateKeys && sellerPrivateKeys.length > 0);
88
90
  const buyCount = _buyCount ?? 1;
89
91
  const sellCount = _sellCount ?? 1;
92
+ // ✅ 多钱包模式:使用单独的处理逻辑
93
+ if (isMultiWalletMode) {
94
+ return await fourBundleBuyFirstMultiWallet({
95
+ buyerPrivateKeys: buyerPrivateKeys || (buyerPrivateKey ? [buyerPrivateKey] : []),
96
+ sellerPrivateKeys: sellerPrivateKeys || (sellerPrivateKey ? [sellerPrivateKey] : []),
97
+ tokenAddress,
98
+ buyerFunds,
99
+ config
100
+ });
101
+ }
102
+ // ✅ 单钱包模式(向后兼容)
103
+ if (!buyerPrivateKey || !sellerPrivateKey) {
104
+ throw new Error('单钱包模式需要提供 buyerPrivateKey 和 sellerPrivateKey');
105
+ }
90
106
  validateSwapCounts(buyCount, sellCount);
91
107
  // ✅ 计算利润比例:每笔万分之3
92
108
  const totalTxCount = buyCount + sellCount;
@@ -139,14 +155,8 @@ export async function fourBundleBuyFirstMerkle(params) {
139
155
  if (!estimatedTokenAmount || estimatedTokenAmount <= 0n) {
140
156
  throw new Error('报价失败:无法估算可买入的代币数量');
141
157
  }
142
- // ✅ 多笔交易时添加滑点保护,避免因价格变动导致代币不足
143
- // 原因:多笔买入会累积滑点,实际获得的代币少于一次性报价
144
- // 滑点比例:buyCount * 1%(每多一笔买入增加 1% 保护)
145
- let sellAmountWei = estimatedTokenAmount;
146
- if (buyCount > 1 || sellCount > 1) {
147
- const slippageBps = BigInt(buyCount * 100); // buyCount=3 → 3%
148
- sellAmountWei = estimatedTokenAmount * (10000n - slippageBps) / 10000n;
149
- }
158
+ // ✅ 卖出数量直接使用估算的代币数量(无滑点保护)
159
+ const sellAmountWei = estimatedTokenAmount;
150
160
  // 卖方余额检查
151
161
  if (!sameAddress && sellerTokenBal < sellAmountWei) {
152
162
  throw new Error(`卖方代币余额不足: 需要 ${ethers.formatUnits(sellAmountWei, decimals)},实际 ${ethers.formatUnits(sellerTokenBal, decimals)}`);
@@ -298,3 +308,176 @@ async function planMultiNonces({ buyer, seller, buyCount, sellCount, extractProf
298
308
  const profitNonce = extractProfit ? sellerNoncesAll[idx] : undefined;
299
309
  return { buyerNonces, sellerNonces, bribeNonce, profitNonce };
300
310
  }
311
+ /**
312
+ * ✅ Four 多钱包捆绑换手
313
+ * - 多个买方钱包执行买入(每个钱包1笔)
314
+ * - 多个卖方钱包执行卖出(每个钱包1笔)
315
+ * - 买入总价值 = 卖出总价值
316
+ */
317
+ async function fourBundleBuyFirstMultiWallet(params) {
318
+ const { buyerPrivateKeys, sellerPrivateKeys, tokenAddress, buyerFunds, config } = params;
319
+ const buyCount = buyerPrivateKeys.length;
320
+ const sellCount = sellerPrivateKeys.length;
321
+ if (buyCount === 0)
322
+ throw new Error('买方钱包数量不能为0');
323
+ if (sellCount === 0)
324
+ throw new Error('卖方钱包数量不能为0');
325
+ // 验证总交易数不超过限制
326
+ validateSwapCounts(buyCount, sellCount);
327
+ // ✅ 计算利润比例:每笔万分之6
328
+ const totalTxCount = buyCount + sellCount;
329
+ const profitRateBps = PROFIT_RATE_PER_TX_BPS * totalTxCount;
330
+ const chainIdNum = config.chainId ?? 56;
331
+ const provider = new ethers.JsonRpcProvider(config.rpcUrl, {
332
+ chainId: chainIdNum,
333
+ name: 'BSC'
334
+ });
335
+ const nonceManager = new NonceManager(provider);
336
+ // 创建所有钱包实例
337
+ const buyers = buyerPrivateKeys.map(pk => new Wallet(pk, provider));
338
+ const sellers = sellerPrivateKeys.map(pk => new Wallet(pk, provider));
339
+ // 使用第一个卖方作为主卖方(支付贿赂和利润)
340
+ const mainSeller = sellers[0];
341
+ // ✅ 计算总交易金额
342
+ if (!buyerFunds) {
343
+ throw new Error('多钱包模式必须提供 buyerFunds(总交易金额)');
344
+ }
345
+ const totalFundsWei = ethers.parseEther(String(buyerFunds));
346
+ if (totalFundsWei <= 0n) {
347
+ throw new Error('交易金额必须大于0');
348
+ }
349
+ // ✅ 获取报价:买入能获得多少代币
350
+ const helper3 = new Contract(ADDRESSES.BSC.TokenManagerHelper3, HELPER3_ABI, provider);
351
+ const buyQuote = await helper3.tryBuy(tokenAddress, 0n, totalFundsWei);
352
+ const estimatedTokenAmount = buyQuote.estimatedAmount ?? buyQuote[2];
353
+ if (!estimatedTokenAmount || estimatedTokenAmount <= 0n) {
354
+ throw new Error('报价失败:无法估算可买入的代币数量');
355
+ }
356
+ // ✅ 将总金额平均分配给买方
357
+ const buyAmountsWei = splitAmount(totalFundsWei, buyCount);
358
+ // ✅ 将代币平均分配给卖方
359
+ const sellAmountsWei = splitAmount(estimatedTokenAmount, sellCount);
360
+ const finalGasLimit = getGasLimit(config);
361
+ const gasPrice = await getOptimizedGasPrice(provider, getGasPriceConfig(config));
362
+ const txType = getTxType(config);
363
+ // ✅ 估算利润
364
+ const sellResult = await trySell('BSC', config.rpcUrl, tokenAddress, estimatedTokenAmount);
365
+ const estimatedSellFunds = sellResult.funds;
366
+ const profitAmount = (estimatedSellFunds * BigInt(profitRateBps)) / 10000n;
367
+ // ✅ 获取贿赂金额
368
+ const bribeAmount = getBribeAmount(config);
369
+ const needBribeTx = bribeAmount > 0n;
370
+ // ✅ 获取所有钱包的 nonces
371
+ const noncesMap = new Map();
372
+ await Promise.all([...sellers, ...buyers].map(async (wallet) => {
373
+ const addr = wallet.address.toLowerCase();
374
+ if (!noncesMap.has(addr)) {
375
+ const nonce = await nonceManager.getNextNonce(wallet);
376
+ noncesMap.set(addr, nonce);
377
+ }
378
+ }));
379
+ // ✅ 构建交易列表
380
+ const allTransactions = [];
381
+ // 1. 贿赂交易(由主卖方支付)
382
+ if (needBribeTx) {
383
+ const mainSellerAddr = mainSeller.address.toLowerCase();
384
+ const bribeNonce = noncesMap.get(mainSellerAddr);
385
+ noncesMap.set(mainSellerAddr, bribeNonce + 1);
386
+ const bribeTx = await mainSeller.signTransaction({
387
+ to: BLOCKRAZOR_BUILDER_EOA,
388
+ value: bribeAmount,
389
+ nonce: bribeNonce,
390
+ gasPrice,
391
+ gasLimit: 21000n,
392
+ chainId: chainIdNum,
393
+ type: txType
394
+ });
395
+ allTransactions.push(bribeTx);
396
+ }
397
+ // 2. 构建所有买入交易
398
+ const tm = new Contract(TM_ADDRESS, TM_ABI, provider);
399
+ const buyTxPromises = buyers.map(async (buyer, i) => {
400
+ const buyAmount = buyAmountsWei[i];
401
+ const buyerAddr = buyer.address.toLowerCase();
402
+ const nonce = noncesMap.get(buyerAddr);
403
+ noncesMap.set(buyerAddr, nonce + 1);
404
+ const tmBuyer = tm.connect(buyer);
405
+ const unsigned = await tmBuyer.buyTokenAMAP.populateTransaction(0n, tokenAddress, buyer.address, buyAmount, 0n, { value: buyAmount });
406
+ return buyer.signTransaction({
407
+ ...unsigned,
408
+ from: buyer.address,
409
+ nonce,
410
+ gasLimit: finalGasLimit,
411
+ gasPrice,
412
+ chainId: chainIdNum,
413
+ type: txType,
414
+ value: buyAmount
415
+ });
416
+ });
417
+ // 3. 构建所有卖出交易
418
+ const sellTxPromises = sellers.map(async (seller, i) => {
419
+ const sellAmount = sellAmountsWei[i];
420
+ const sellerAddr = seller.address.toLowerCase();
421
+ const nonce = noncesMap.get(sellerAddr);
422
+ noncesMap.set(sellerAddr, nonce + 1);
423
+ const tmSeller = tm.connect(seller);
424
+ const unsigned = await tmSeller.sellToken.populateTransaction(0n, tokenAddress, sellAmount, 0n);
425
+ return seller.signTransaction({
426
+ ...unsigned,
427
+ from: seller.address,
428
+ nonce,
429
+ gasLimit: finalGasLimit,
430
+ gasPrice,
431
+ chainId: chainIdNum,
432
+ type: txType,
433
+ value: 0n
434
+ });
435
+ });
436
+ // ✅ 并行签名所有买卖交易
437
+ const [signedBuys, signedSells] = await Promise.all([
438
+ Promise.all(buyTxPromises),
439
+ Promise.all(sellTxPromises)
440
+ ]);
441
+ // 先买后卖:买入交易在前
442
+ allTransactions.push(...signedBuys, ...signedSells);
443
+ // 4. 利润多跳转账(由主卖方支付)
444
+ let profitHopWallets;
445
+ if (profitAmount > 0n) {
446
+ const mainSellerAddr = mainSeller.address.toLowerCase();
447
+ const profitNonce = noncesMap.get(mainSellerAddr);
448
+ const profitHopResult = await buildProfitHopTransactions({
449
+ provider,
450
+ payerWallet: mainSeller,
451
+ profitAmount,
452
+ profitRecipient: getProfitRecipient(),
453
+ hopCount: PROFIT_HOP_COUNT,
454
+ gasPrice,
455
+ chainId: chainIdNum,
456
+ txType,
457
+ startNonce: profitNonce
458
+ });
459
+ allTransactions.push(...profitHopResult.signedTransactions);
460
+ profitHopWallets = profitHopResult.hopWallets;
461
+ }
462
+ nonceManager.clearTemp();
463
+ // 获取代币精度用于显示
464
+ const erc20 = new Contract(tokenAddress, [
465
+ 'function decimals() view returns (uint8)'
466
+ ], provider);
467
+ const decimals = await erc20.decimals();
468
+ return {
469
+ signedTransactions: allTransactions,
470
+ profitHopWallets,
471
+ metadata: {
472
+ buyerAddress: buyers.map(b => b.address).join(','),
473
+ sellerAddress: sellers.map(s => s.address).join(','),
474
+ buyAmount: ethers.formatEther(totalFundsWei),
475
+ sellAmount: ethers.formatUnits(estimatedTokenAmount, decimals),
476
+ profitAmount: profitAmount > 0n ? ethers.formatEther(profitAmount) : undefined,
477
+ buyCount,
478
+ sellCount,
479
+ buyAmounts: buyAmountsWei.map(amt => ethers.formatEther(amt)),
480
+ sellAmounts: sellAmountsWei.map(amt => ethers.formatUnits(amt, decimals))
481
+ }
482
+ };
483
+ }
@@ -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
+ }
@@ -22,8 +22,10 @@ export interface FlapBuyFirstConfig extends CommonBundleConfig {
22
22
  }
23
23
  export interface FlapBundleBuyFirstSignParams {
24
24
  chain: FlapChain;
25
- buyerPrivateKey: string;
26
- sellerPrivateKey: string;
25
+ buyerPrivateKey?: string;
26
+ buyerPrivateKeys?: string[];
27
+ sellerPrivateKey?: string;
28
+ sellerPrivateKeys?: string[];
27
29
  tokenAddress: string;
28
30
  buyerFunds?: string;
29
31
  buyerFundsPercentage?: number;