four-flap-meme-sdk 1.2.72 → 1.2.74
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,7 +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
|
|
5
|
+
import axios from 'axios';
|
|
6
6
|
import { getErrorMessage, getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, getProfitRecipient } from './config.js';
|
|
7
7
|
import { batchCheckAllowances } from '../../utils/erc20.js';
|
|
8
8
|
import { trySell } from '../tm.js';
|
|
@@ -141,35 +141,83 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
141
141
|
}
|
|
142
142
|
nonceManager.clearTemp();
|
|
143
143
|
console.log(`✅ 总共生成了 ${signedTxs.length} 个交易签名 (1创建 + ${buyerKeys.length}买入 + 利润)`);
|
|
144
|
-
// ✅
|
|
145
|
-
console.log('📡
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
144
|
+
// ✅ 使用 48.club 的 eth_sendRawTransaction 捆绑提交
|
|
145
|
+
console.log('📡 开始通过 48.club 捆绑提交交易...');
|
|
146
|
+
const club48Endpoint = config.club48Endpoint || 'https://puissant-bsc.48.club';
|
|
147
|
+
const txHashes = [];
|
|
148
|
+
// ✅ 使用 eth_sendRawTransaction 逐个提交(48.club 会自动捆绑连续的交易)
|
|
149
|
+
for (let i = 0; i < signedTxs.length; i++) {
|
|
150
|
+
try {
|
|
151
|
+
const tx = signedTxs[i];
|
|
152
|
+
console.log(`📤 提交第 ${i + 1}/${signedTxs.length} 个交易...`);
|
|
153
|
+
const response = await axios.post(club48Endpoint, {
|
|
154
|
+
jsonrpc: "2.0",
|
|
155
|
+
id: i + 1,
|
|
156
|
+
method: "eth_sendRawTransaction",
|
|
157
|
+
params: [tx]
|
|
158
|
+
});
|
|
159
|
+
if (response.data.error) {
|
|
160
|
+
throw new Error(`交易 ${i + 1} 提交失败: ${response.data.error.message}`);
|
|
161
|
+
}
|
|
162
|
+
const txHash = response.data.result;
|
|
163
|
+
txHashes.push(txHash);
|
|
164
|
+
console.log(`✅ 交易 ${i + 1} 提交成功: ${txHash}`);
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
console.error(`❌ 交易 ${i + 1} 提交失败:`, error.message);
|
|
168
|
+
throw new Error(`交易 ${i + 1} 提交失败: ${error.message}`);
|
|
169
|
+
}
|
|
151
170
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
171
|
+
console.log(`✅ 所有交易提交成功!共 ${txHashes.length} 笔`);
|
|
172
|
+
// 等待创建交易确认并解析代币地址
|
|
173
|
+
console.log('⏳ 等待创建交易确认并解析代币地址...');
|
|
174
|
+
const createTxHash = txHashes[0];
|
|
175
|
+
// 使用公共 RPC 查询交易收据(避免 CORS)
|
|
176
|
+
let receipt = null;
|
|
177
|
+
let retries = 0;
|
|
178
|
+
const maxRetries = 60; // 最多等待 60 秒
|
|
179
|
+
while (!receipt && retries < maxRetries) {
|
|
180
|
+
try {
|
|
181
|
+
receipt = await provider.getTransactionReceipt(createTxHash);
|
|
182
|
+
if (receipt)
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
catch (e) {
|
|
186
|
+
// 忽略查询错误
|
|
187
|
+
}
|
|
188
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
189
|
+
retries++;
|
|
190
|
+
if (retries % 5 === 0) {
|
|
191
|
+
console.log(`⏳ 等待交易确认... (${retries}/${maxRetries})`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (!receipt) {
|
|
195
|
+
throw new Error('等待交易确认超时');
|
|
196
|
+
}
|
|
197
|
+
console.log(`✅ 创建交易已确认,区块: ${receipt.blockNumber}`);
|
|
198
|
+
// 解析 TokenCreated 事件
|
|
199
|
+
let tokenAddress;
|
|
200
|
+
for (const log of receipt.logs) {
|
|
201
|
+
try {
|
|
202
|
+
// TokenCreated(address indexed token, ...)
|
|
203
|
+
if (log.topics[0] === ethers.id('TokenCreated(address,address,string,string,uint256,uint256,uint256)')) {
|
|
204
|
+
tokenAddress = ethers.getAddress('0x' + log.topics[1].slice(26));
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
catch (e) {
|
|
209
|
+
// 忽略解析错误
|
|
210
|
+
}
|
|
162
211
|
}
|
|
163
|
-
|
|
164
|
-
throw new Error('
|
|
212
|
+
if (!tokenAddress) {
|
|
213
|
+
throw new Error('无法从交易收据中解析代币地址');
|
|
165
214
|
}
|
|
166
|
-
|
|
167
|
-
const tokenAddress = await waitAndParseTokenAddress(provider, signedTxs[0], bundleUuid);
|
|
215
|
+
console.log(`✅ 代币地址: ${tokenAddress}`);
|
|
168
216
|
return {
|
|
169
217
|
signedTransactions: signedTxs,
|
|
170
218
|
tokenAddress,
|
|
171
219
|
metadata,
|
|
172
|
-
|
|
220
|
+
txHashes
|
|
173
221
|
};
|
|
174
222
|
}
|
|
175
223
|
/**
|
|
@@ -70,8 +70,8 @@ export type MerkleSignedResult = {
|
|
|
70
70
|
metadata?: {
|
|
71
71
|
[key: string]: any;
|
|
72
72
|
};
|
|
73
|
-
/**
|
|
74
|
-
|
|
73
|
+
/** 交易哈希数组(内部提交时返回) */
|
|
74
|
+
txHashes?: string[];
|
|
75
75
|
};
|
|
76
76
|
/** ✅ 创建代币 + 购买参数(Merkle 版本 - 完整) */
|
|
77
77
|
export type FourCreateWithBundleBuyMerkleParams = {
|
|
@@ -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
|
|
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 {
|