four-flap-meme-sdk 1.5.8 → 1.5.9
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.
|
@@ -250,6 +250,30 @@ export async function submitMultipleBundlesToBlockRazorParallel(bundles, config)
|
|
|
250
250
|
const promises = bundles.map(signedTransactions => submitBundleToBlockRazor(signedTransactions, config));
|
|
251
251
|
return await Promise.all(promises);
|
|
252
252
|
}
|
|
253
|
+
function shouldTreatBroadcastErrorAsMaybeSent(msg) {
|
|
254
|
+
const m = String(msg || '').toLowerCase();
|
|
255
|
+
// 常见:交易已被节点接收/已在 mempool/已上链,但节点返回报错
|
|
256
|
+
return (m.includes('already known') ||
|
|
257
|
+
m.includes('known transaction') ||
|
|
258
|
+
m.includes('nonce too low') ||
|
|
259
|
+
m.includes('nonce has already been used') ||
|
|
260
|
+
m.includes('nonce expired') ||
|
|
261
|
+
m.includes('replacement transaction underpriced'));
|
|
262
|
+
}
|
|
263
|
+
async function recoverTxHashIfAlreadySeen(provider, signedTx, errorMessage) {
|
|
264
|
+
if (!shouldTreatBroadcastErrorAsMaybeSent(errorMessage))
|
|
265
|
+
return undefined;
|
|
266
|
+
try {
|
|
267
|
+
const hash = ethers.keccak256(signedTx);
|
|
268
|
+
if (!hash)
|
|
269
|
+
return undefined;
|
|
270
|
+
const tx = await provider.getTransaction(hash);
|
|
271
|
+
return tx ? hash : undefined;
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
return undefined;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
253
277
|
/**
|
|
254
278
|
* 并行广播到 RPC(用于不支持 Bundle 的链,如 Monad)
|
|
255
279
|
*
|
|
@@ -333,6 +357,17 @@ export async function submitDirectToRpc(signedTransactions, config) {
|
|
|
333
357
|
catch (error) {
|
|
334
358
|
const errorMessage = error?.message || String(error);
|
|
335
359
|
console.error(`❌ [${chainName}] 交易 ${i + 1}/${totalTransactions} 广播失败:`, errorMessage);
|
|
360
|
+
// ✅ 兼容:nonce too low / already known 等情况下,交易可能已经被接收
|
|
361
|
+
const recovered = await recoverTxHashIfAlreadySeen(provider, signedTx, errorMessage);
|
|
362
|
+
if (recovered) {
|
|
363
|
+
if (config.waitForConfirmation) {
|
|
364
|
+
try {
|
|
365
|
+
await provider.waitForTransaction(recovered, 1, config.confirmationTimeout ?? 30000);
|
|
366
|
+
}
|
|
367
|
+
catch { }
|
|
368
|
+
}
|
|
369
|
+
return { index: i, success: true, txHash: recovered, error: errorMessage };
|
|
370
|
+
}
|
|
336
371
|
return {
|
|
337
372
|
index: i,
|
|
338
373
|
success: false,
|
|
@@ -431,6 +466,29 @@ export async function submitDirectToRpcSequential(signedTransactions, config) {
|
|
|
431
466
|
}
|
|
432
467
|
catch (error) {
|
|
433
468
|
const errorMessage = error?.message || String(error);
|
|
469
|
+
// ✅ 兼容:nonce too low / already known 等情况下,交易可能已经被接收
|
|
470
|
+
const recovered = await recoverTxHashIfAlreadySeen(provider, signedTx, errorMessage);
|
|
471
|
+
if (recovered) {
|
|
472
|
+
// 顺序模式依赖确认:尽量等待一下
|
|
473
|
+
try {
|
|
474
|
+
const receipt = await provider.waitForTransaction(recovered, 1, confirmationTimeout);
|
|
475
|
+
if (receipt && receipt.status === 1) {
|
|
476
|
+
results.push({ index: i, success: true, txHash: recovered, error: errorMessage });
|
|
477
|
+
txHashes.push(recovered);
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
const errorMsg = `交易执行失败(status=${receipt?.status})`;
|
|
481
|
+
results.push({ index: i, success: false, txHash: recovered, error: `${errorMessage} | ${errorMsg}` });
|
|
482
|
+
errors.push(`交易 ${i + 1}: ${errorMessage} | ${errorMsg}`);
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
catch {
|
|
486
|
+
// 等待超时也先认为“已广播”,交给上层自行确认
|
|
487
|
+
results.push({ index: i, success: true, txHash: recovered, error: errorMessage });
|
|
488
|
+
txHashes.push(recovered);
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
434
492
|
results.push({
|
|
435
493
|
index: i,
|
|
436
494
|
success: false,
|
|
@@ -22,7 +22,8 @@ export async function flapPrivateBuyMerkle(params) {
|
|
|
22
22
|
// ✅ 并行获取 gasPrice、nonce 和构建未签名交易
|
|
23
23
|
const [gasPrice, currentNonce, unsigned] = await Promise.all([
|
|
24
24
|
getOptimizedGasPrice(provider, getGasPriceConfig(config)),
|
|
25
|
-
|
|
25
|
+
// ✅ 使用 pending nonce,避免 xLayer 等链上“已有 pending 交易”导致 nonce too low
|
|
26
|
+
provider.getTransactionCount(wallet.address, 'pending'),
|
|
26
27
|
portal.swapExactInput.populateTransaction({
|
|
27
28
|
inputToken: ZERO_ADDRESS,
|
|
28
29
|
outputToken: tokenAddress,
|
|
@@ -101,7 +102,8 @@ export async function flapPrivateSellMerkle(params) {
|
|
|
101
102
|
// ✅ 并行获取 gasPrice、nonce、报价和构建未签名交易
|
|
102
103
|
const [gasPrice, currentNonce, { quotedOutput }, unsigned] = await Promise.all([
|
|
103
104
|
getOptimizedGasPrice(provider, getGasPriceConfig(config)),
|
|
104
|
-
|
|
105
|
+
// ✅ 使用 pending nonce,避免 xLayer 等链上“已有 pending 交易”导致 nonce too low
|
|
106
|
+
provider.getTransactionCount(wallet.address, 'pending'),
|
|
105
107
|
resolveSingleSellOutputs(portal, tokenAddress, amountWei, minOutputAmount),
|
|
106
108
|
portal.swapExactInput.populateTransaction({
|
|
107
109
|
inputToken: tokenAddress,
|
|
@@ -198,7 +200,8 @@ export async function flapBatchPrivateBuyMerkle(params) {
|
|
|
198
200
|
// ✅ 并行获取 gasPrice、所有 nonces 和构建未签名交易
|
|
199
201
|
const [gasPrice, initialNonces, unsignedList] = await Promise.all([
|
|
200
202
|
getOptimizedGasPrice(provider, getGasPriceConfig(config)),
|
|
201
|
-
|
|
203
|
+
// ✅ 使用 pending nonce,避免 xLayer 等链上“已有 pending 交易”导致 nonce too low
|
|
204
|
+
Promise.all(wallets.map(w => provider.getTransactionCount(w.address, 'pending'))),
|
|
202
205
|
Promise.all(portals.map((portal, i) => portal.swapExactInput.populateTransaction({
|
|
203
206
|
inputToken: ZERO_ADDRESS,
|
|
204
207
|
outputToken: tokenAddress,
|
|
@@ -287,7 +290,8 @@ export async function flapBatchPrivateSellMerkle(params) {
|
|
|
287
290
|
// ✅ 并行获取 gasPrice、所有 nonces、所有报价和构建未签名交易
|
|
288
291
|
const [gasPrice, initialNonces, quotedOutputs, unsignedList] = await Promise.all([
|
|
289
292
|
getOptimizedGasPrice(provider, getGasPriceConfig(config)),
|
|
290
|
-
|
|
293
|
+
// ✅ 使用 pending nonce,避免 xLayer 等链上“已有 pending 交易”导致 nonce too low
|
|
294
|
+
Promise.all(wallets.map(w => provider.getTransactionCount(w.address, 'pending'))),
|
|
291
295
|
Promise.all(amountsWei.map(amount => portal.quoteExactInput.staticCall({
|
|
292
296
|
inputToken: tokenAddress,
|
|
293
297
|
outputToken: ZERO_ADDRESS,
|