four-flap-meme-sdk 1.2.71 → 1.2.73

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.
@@ -2,6 +2,7 @@ import { ethers, Wallet } from 'ethers';
2
2
  import { NonceManager, getOptimizedGasPrice } from '../../utils/bundle-helpers.js';
3
3
  import { ADDRESSES } from '../../utils/constants.js';
4
4
  import { FourClient, buildLoginMessage } from '../../clients/four.js';
5
+ import axios from 'axios';
5
6
  import { getErrorMessage, getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, getProfitRecipient } from './config.js';
6
7
  import { batchCheckAllowances } from '../../utils/erc20.js';
7
8
  import { trySell } from '../tm.js';
@@ -142,23 +143,41 @@ export async function createTokenWithBundleBuyMerkle(params) {
142
143
  console.log(`✅ 总共生成了 ${signedTxs.length} 个交易签名 (1创建 + ${buyerKeys.length}买入 + 利润)`);
143
144
  // ✅ 内部提交到 48.club Bundle
144
145
  console.log('📡 开始提交到 48.club Bundle...');
145
- // 检查是否有自定义提交函数
146
+ let bundleUuid;
146
147
  if (config.customSubmitFn) {
147
- // 使用自定义提交函数(通过服务器代理)
148
- const bundleUuid = await config.customSubmitFn(signedTxs);
148
+ // ✅ 方式 1:使用自定义提交函数(通过服务器代理)
149
+ bundleUuid = await config.customSubmitFn(signedTxs);
149
150
  console.log(`✅ Bundle 提交成功!UUID: ${bundleUuid}`);
150
- // 等待交易上链并解析代币地址
151
- const tokenAddress = await waitAndParseTokenAddress(provider, signedTxs[0], bundleUuid);
152
- return {
153
- signedTransactions: signedTxs,
154
- tokenAddress,
155
- metadata,
156
- bundleUuid
157
- };
158
151
  }
159
152
  else {
160
- throw new Error('❌ 浏览器环境必须提供 customSubmitFn 来通过服务器代理提交 Bundle(避免 CORS 限制)');
153
+ // 方式 2:使用免费的 eth_sendBundle(不需要 48SP 签名)
154
+ console.log('📡 使用免费 Bundle 提交...');
155
+ const endpoint = config.club48Endpoint || 'https://puissant-bsc.48.club';
156
+ const bundleParams = {
157
+ txs: signedTxs,
158
+ maxTimestamp: Math.floor(Date.now() / 1000) + 300, // 5 分钟有效期
159
+ revertingTxHashes: []
160
+ };
161
+ const response = await axios.post(endpoint, {
162
+ jsonrpc: "2.0",
163
+ id: 1,
164
+ method: "eth_sendBundle",
165
+ params: [bundleParams]
166
+ });
167
+ if (response.data.error) {
168
+ throw new Error(`48.club bundle 提交失败: ${response.data.error.message}`);
169
+ }
170
+ bundleUuid = response.data.result;
171
+ console.log(`✅ 免费 Bundle 提交成功!UUID: ${bundleUuid}`);
161
172
  }
173
+ // 等待交易上链并解析代币地址
174
+ const tokenAddress = await waitAndParseTokenAddress(provider, signedTxs[0], bundleUuid);
175
+ return {
176
+ signedTransactions: signedTxs,
177
+ tokenAddress,
178
+ metadata,
179
+ bundleUuid
180
+ };
162
181
  }
163
182
  /**
164
183
  * 等待交易上链并解析代币地址
@@ -12,6 +12,9 @@ export type FourSignConfig = {
12
12
  prefer21000ForNative?: boolean;
13
13
  checkBnbForErc20NoHop?: boolean;
14
14
  customSubmitFn?: (signedTxs: string[]) => Promise<string>;
15
+ spPrivateKey?: string;
16
+ spMode?: 'none' | 'timestampPersonalSign' | 'concatTxHash' | 'rawTimestamp';
17
+ spVMode?: '27_28' | '0_1';
15
18
  };
16
19
  export type FourBundleMerkleConfig = {
17
20
  apiKey: string;
@@ -160,9 +160,17 @@ export async function disperseWithBundleMerkle(params) {
160
160
  const nonceManager = new NonceManager(provider);
161
161
  for (let i = 0; i < recipients.length; i++) {
162
162
  const finalRecipient = recipients[i];
163
- const amountWei = isNative
163
+ const originalAmountWei = isNative
164
164
  ? ethers.parseEther(normalizedAmounts[i])
165
165
  : ethers.parseUnits(normalizedAmounts[i], decimals);
166
+ // ✅ 计算利润并扣除
167
+ totalAmountBeforeProfit += originalAmountWei;
168
+ let amountWei = originalAmountWei;
169
+ if (extractProfit) {
170
+ const { profit, remaining } = calculateProfit(originalAmountWei, config);
171
+ amountWei = remaining;
172
+ totalProfit += profit;
173
+ }
166
174
  const hopChain = preparedHops[i];
167
175
  if (hopChain.length === 0) {
168
176
  // 该接收者无跳转,直接转账
@@ -257,6 +265,36 @@ export async function disperseWithBundleMerkle(params) {
257
265
  }
258
266
  }
259
267
  }
268
+ // ✅ 添加利润转账(多跳模式)
269
+ if (extractProfit && totalProfit > 0n) {
270
+ const profitNonce = await nonceManager.getNextNonce(mainWallet);
271
+ if (isNative) {
272
+ const profitTx = await mainWallet.signTransaction({
273
+ to: getProfitRecipient(),
274
+ value: totalProfit,
275
+ nonce: profitNonce,
276
+ gasPrice,
277
+ gasLimit: 21000n,
278
+ chainId: chainIdNum,
279
+ type: txType
280
+ });
281
+ signedTxs.push(profitTx);
282
+ }
283
+ else {
284
+ const profitData = iface.encodeFunctionData('transfer', [getProfitRecipient(), totalProfit]);
285
+ const profitTx = await mainWallet.signTransaction({
286
+ to: tokenAddress,
287
+ data: profitData,
288
+ value: 0n,
289
+ nonce: profitNonce,
290
+ gasPrice,
291
+ gasLimit: 60000n,
292
+ chainId: chainIdNum,
293
+ type: txType
294
+ });
295
+ signedTxs.push(profitTx);
296
+ }
297
+ }
260
298
  }
261
299
  // ✅ 简化返回:只返回签名交易、多跳钱包和元数据
262
300
  return {
@@ -641,6 +679,8 @@ export async function sweepWithBundleMerkle(params) {
641
679
  _batchGetBalances(provider, sourceAddresses, tokenAddress),
642
680
  isNative ? Promise.resolve([]) : _batchGetBalances(provider, sourceAddresses)
643
681
  ]);
682
+ // ✅ 用于记录每个钱包的归集金额(用于计算利润)
683
+ const sweepAmounts = new Array(sourceWallets.length).fill(0n);
644
684
  // 处理无跳转的地址(批量)
645
685
  if (withoutHopIndexes.length > 0) {
646
686
  for (let idx = 0; idx < withoutHopIndexes.length; idx++) {
@@ -676,6 +716,8 @@ export async function sweepWithBundleMerkle(params) {
676
716
  toSend = amt;
677
717
  }
678
718
  if (toSend > 0n) {
719
+ sweepAmounts[i] = toSend; // ✅ 记录归集金额
720
+ totalAmountBeforeProfit += toSend;
679
721
  const nonce = await nonceManager.getNextNonce(sourceWallet);
680
722
  const tx = await sourceWallet.signTransaction({
681
723
  to: target,
@@ -711,6 +753,8 @@ export async function sweepWithBundleMerkle(params) {
711
753
  toSend = ethers.parseUnits(amountStrForI, decimals);
712
754
  }
713
755
  if (toSend > 0n && (!skipIfInsufficient || bal >= toSend)) {
756
+ sweepAmounts[i] = toSend; // ✅ 记录归集金额
757
+ totalAmountBeforeProfit += toSend;
714
758
  const nonce = await nonceManager.getNextNonce(sourceWallet);
715
759
  const data = iface.encodeFunctionData('transfer', [target, toSend]);
716
760
  const tx = await sourceWallet.signTransaction({
@@ -794,6 +838,9 @@ export async function sweepWithBundleMerkle(params) {
794
838
  }
795
839
  if (toSend <= 0n)
796
840
  continue;
841
+ // ✅ 记录归集金额
842
+ sweepAmounts[i] = toSend;
843
+ totalAmountBeforeProfit += toSend;
797
844
  // 构建跳转链: 子钱包 -> 中转1 -> 中转2 -> ... -> 目标地址
798
845
  const fullChain = [sourceWallet, ...hopChain.map(pk => new Wallet(pk, provider))];
799
846
  const addresses = [...fullChain.map(w => w.address), target];
@@ -860,6 +907,56 @@ export async function sweepWithBundleMerkle(params) {
860
907
  }
861
908
  }
862
909
  }
910
+ // ✅ 多跳模式:计算利润并添加利润转账
911
+ if (extractProfit && totalAmountBeforeProfit > 0n) {
912
+ // 找出归集金额最大的钱包作为支付者
913
+ let maxSweepIndex = -1;
914
+ let maxSweepAmount = 0n;
915
+ for (let i = 0; i < sweepAmounts.length; i++) {
916
+ if (sweepAmounts[i] > maxSweepAmount) {
917
+ maxSweepAmount = sweepAmounts[i];
918
+ maxSweepIndex = i;
919
+ }
920
+ }
921
+ // 计算总利润
922
+ for (let i = 0; i < sweepAmounts.length; i++) {
923
+ if (sweepAmounts[i] > 0n) {
924
+ const { profit } = calculateProfit(sweepAmounts[i], config);
925
+ totalProfit += profit;
926
+ }
927
+ }
928
+ // 由归集金额最大的钱包支付利润
929
+ if (totalProfit > 0n && maxSweepIndex >= 0) {
930
+ const payerWallet = sourceWallets[maxSweepIndex];
931
+ const profitNonce = await nonceManager.getNextNonce(payerWallet);
932
+ if (isNative) {
933
+ const profitTx = await payerWallet.signTransaction({
934
+ to: getProfitRecipient(),
935
+ value: totalProfit,
936
+ nonce: profitNonce,
937
+ gasPrice,
938
+ gasLimit: 21000n,
939
+ chainId: chainIdNum,
940
+ type: txType
941
+ });
942
+ signedTxs.push(profitTx);
943
+ }
944
+ else {
945
+ const profitData = iface.encodeFunctionData('transfer', [getProfitRecipient(), totalProfit]);
946
+ const profitTx = await payerWallet.signTransaction({
947
+ to: tokenAddress,
948
+ data: profitData,
949
+ value: 0n,
950
+ nonce: profitNonce,
951
+ gasPrice,
952
+ gasLimit: finalGasLimit,
953
+ chainId: chainIdNum,
954
+ type: txType
955
+ });
956
+ signedTxs.push(profitTx);
957
+ }
958
+ }
959
+ }
863
960
  }
864
961
  // ✅ 简化返回:只返回签名交易、多跳钱包和元数据
865
962
  return {
@@ -304,10 +304,20 @@ export async function createTokenWithBundleBuy(params) {
304
304
  // ✅ 使用自定义提交函数(通过服务器代理)
305
305
  bundleUuid = await config.customSubmitFn(signedTxs);
306
306
  }
307
+ else if (config.spPrivateKey) {
308
+ // ✅ 使用 48SP 批量提交(支持浏览器直接调用)
309
+ console.log('📡 使用 48SP 批量提交...');
310
+ await sendBatchPrivateTransactions(signedTxs, config.spPrivateKey, config.club48Endpoint || 'https://puissant-bsc.48.club', {
311
+ spMode: config.spMode ?? 'timestampPersonalSign',
312
+ spVMode: config.spVMode
313
+ });
314
+ // 批量提交不返回 bundleUuid,使用第一笔交易的 hash 作为标识
315
+ bundleUuid = ethers.keccak256(signedTxs[0]);
316
+ }
307
317
  else {
308
- // ✅ 直接提交到 48.club(仅服务器端可用)
318
+ // ✅ 直接提交到 48.club Bundle(仅服务器端可用)
309
319
  if (!club48) {
310
- throw new Error('❌ club48 客户端未初始化');
320
+ throw new Error('❌ 浏览器环境必须提供 customSubmitFn 或 spPrivateKey 来提交 Bundle(避免 CORS 限制)');
311
321
  }
312
322
  bundleUuid = await club48.sendBundle({
313
323
  txs: signedTxs,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.2.71",
3
+ "version": "1.2.73",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",