hermes-swap 0.6.1 → 0.6.3

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.
@@ -168,12 +168,9 @@ var _Aggregator = class {
168
168
  this.validateParams(params);
169
169
  const useBundle = this.shouldUseBundle(params.chain, txReq);
170
170
  const pathDesc = params.path.map((p) => `${p.dexType}:${p.poolAddress.slice(0, 10)}`).join("→");
171
- _Aggregator.traceLog(
172
- params.chain,
173
- "log",
174
- `swapByPath开始, user=${params.user}, amountIn=${params.amountInWei}, path=[${pathDesc}], useBundle=${useBundle}`
175
- );
171
+ _Aggregator.traceLog(params.chain, "log", `swapByPath开始, user=${params.user}, amountIn=${params.amountInWei}, path=[${pathDesc}], useBundle=${useBundle}`);
176
172
  const startTs = Date.now();
173
+ let txHash;
177
174
  try {
178
175
  if (useBundle) {
179
176
  const { pureTxReq: pureTxReq2, bundleBlockCount } = this.stripCustomFields(txReq, params.chain);
@@ -181,6 +178,7 @@ var _Aggregator = class {
181
178
  if (!flashbotsReceipt) {
182
179
  throw new Error("Bundle 未在目标区块上链");
183
180
  }
181
+ txHash = flashbotsReceipt.hash;
184
182
  _Aggregator.traceLog(params.chain, "log", `swapByPath完成(bundle), txHash=${flashbotsReceipt.hash}, amountOut=${flashbotsReceipt.amountOut}, 总耗时=${Date.now() - startTs}ms`);
185
183
  return flashbotsReceipt;
186
184
  }
@@ -206,13 +204,14 @@ var _Aggregator = class {
206
204
  try {
207
205
  estimateGas = await aggregator.swap.estimateGas(params.user, params.amountInWei, swapParams, params.minAmountOutList, estimationOverrides);
208
206
  } catch (error) {
209
- _Aggregator.traceLog(params.chain, "error", `swapByPath estimateGas失败, error=${error}`);
207
+ _Aggregator.traceLog(params.chain, "error", `swapByPath estimateGas失败, rpcUrl=${this.getRpcUrl(params.chain)}, error=${error}`);
210
208
  throw error;
211
209
  }
212
210
  txReq = this.resolveGasLimit(txReq, estimateGas);
213
211
  txReq = await this.resolveNonce(wallet.provider, wallet.address, txReq);
214
212
  txReq = await this.resolvePricing(wallet.provider, txReq);
215
213
  const receipt = await this.sendContractTx(params.chain, aggregator, "swap", [params.user, params.amountInWei, swapParams, params.minAmountOutList], txReq);
214
+ txHash = receipt.hash;
216
215
  const iface = new import_ethers.ethers.Interface(import_aggregator.default);
217
216
  let amountOut = null;
218
217
  for (const log of receipt.logs) {
@@ -240,7 +239,7 @@ var _Aggregator = class {
240
239
  to: receipt.from
241
240
  };
242
241
  } catch (error) {
243
- _Aggregator.traceLog(params.chain, "error", `swapByPath异常, 总耗时=${Date.now() - startTs}ms, error=${error}`);
242
+ _Aggregator.traceLog(params.chain, "error", `swapByPath异常, rpcUrl=${this.getRpcUrl(params.chain)}, txHash=${txHash ?? "N/A"}, 总耗时=${Date.now() - startTs}ms, error=${error}`);
244
243
  throw error;
245
244
  }
246
245
  }
@@ -279,7 +278,7 @@ var _Aggregator = class {
279
278
  });
280
279
  return this.batchMultiSwap({ chain, users, amountInWeis, paths, minAmountOutLists });
281
280
  } catch (error) {
282
- throw new Error(`aggregator swap error: ${error}`);
281
+ throw new Error(`aggregator swap error: chain=${chain}, rpcUrl=${this.getRpcUrl(chain)}, ${error}`);
283
282
  }
284
283
  }
285
284
  async swapAndBridge(params, txReq = {}) {
@@ -291,6 +290,7 @@ var _Aggregator = class {
291
290
  `swapAndBridge开始, user=${params.user}, amountInWeis=[${params.amountInWeis.join(",")}], bridgeType=${params.bridgeType}, destChain=${params.destChain}, useBundle=${useBundle}`
292
291
  );
293
292
  const startTs = Date.now();
293
+ let txHash;
294
294
  try {
295
295
  const { pureTxReq, bundleBlockCount } = this.stripCustomFields(txReq, params.chain);
296
296
  txReq = pureTxReq;
@@ -331,7 +331,7 @@ var _Aggregator = class {
331
331
  try {
332
332
  estimateGas = await aggregator.multiSwapAndBridge.estimateGas(...args, estimationOverrides);
333
333
  } catch (error) {
334
- _Aggregator.traceLog(params.chain, "error", `swapAndBridge estimateGas失败, error=${error}`);
334
+ _Aggregator.traceLog(params.chain, "error", `swapAndBridge estimateGas失败, rpcUrl=${this.getRpcUrl(params.chain)}, error=${error}`);
335
335
  throw error;
336
336
  }
337
337
  txReq = this.resolveGasLimit(txReq, estimateGas);
@@ -341,13 +341,22 @@ var _Aggregator = class {
341
341
  let receipt;
342
342
  if (useBundle) {
343
343
  const calldata = iface.encodeFunctionData("multiSwapAndBridge", args);
344
- const { receipt: flashbotsReceipt, txHash, targetBlocks } = await this.submitBundleTx(params.chain, wallet, txReq, aggregatorAddress, calldata, txReq.value ?? 0n, bundleBlockCount);
344
+ const {
345
+ receipt: flashbotsReceipt,
346
+ txHash: bundleTxHash,
347
+ targetBlocks
348
+ } = await this.submitBundleTx(params.chain, wallet, txReq, aggregatorAddress, calldata, txReq.value ?? 0n, bundleBlockCount);
349
+ txHash = bundleTxHash;
345
350
  if (!flashbotsReceipt) {
346
- throw new import_types.BundleNotIncludedError(`Bundle 未在目标区块上链, txHash=${txHash}, targetBlocks=[${targetBlocks.join(",")}]`, txHash);
351
+ throw new import_types.BundleNotIncludedError(`Bundle 未在目标区块上链, txHash=${bundleTxHash}, targetBlocks=[${targetBlocks.join(",")}]`, bundleTxHash);
352
+ }
353
+ if (flashbotsReceipt.status === 0) {
354
+ throw new import_types.TransactionRevertedError(flashbotsReceipt.hash, flashbotsReceipt.gasUsed);
347
355
  }
348
356
  receipt = flashbotsReceipt;
349
357
  } else {
350
358
  receipt = await this.sendContractTx(params.chain, aggregator, "multiSwapAndBridge", args, txReq);
359
+ txHash = receipt.hash;
351
360
  }
352
361
  let receiptUser = null;
353
362
  let amountInList = null;
@@ -374,11 +383,7 @@ var _Aggregator = class {
374
383
  const fromTokenAddress = ((_a = pathStart == null ? void 0 : pathStart[0]) == null ? void 0 : _a.fromCoinAddress) ?? "";
375
384
  const toTokenAddress = ((_b = pathEnd == null ? void 0 : pathEnd[pathEnd.length - 1]) == null ? void 0 : _b.toCoinAddress) ?? "";
376
385
  const totalAmountInFromEvent = amountInList.reduce((sum, amount) => sum + amount, 0n);
377
- _Aggregator.traceLog(
378
- params.chain,
379
- "log",
380
- `swapAndBridge完成, txHash=${receipt.hash}, amountOut=${amountOut}, useBundle=${useBundle}, 总耗时=${Date.now() - startTs}ms`
381
- );
386
+ _Aggregator.traceLog(params.chain, "log", `swapAndBridge完成, txHash=${receipt.hash}, amountOut=${amountOut}, useBundle=${useBundle}, 总耗时=${Date.now() - startTs}ms`);
382
387
  return {
383
388
  hash: receipt.hash,
384
389
  from: receipt.from,
@@ -387,19 +392,16 @@ var _Aggregator = class {
387
392
  receipt
388
393
  };
389
394
  } catch (error) {
390
- _Aggregator.traceLog(params.chain, "error", `swapAndBridge异常, 总耗时=${Date.now() - startTs}ms, error=${error}`);
395
+ _Aggregator.traceLog(params.chain, "error", `swapAndBridge异常, rpcUrl=${this.getRpcUrl(params.chain)}, txHash=${txHash ?? "N/A"}, 总耗时=${Date.now() - startTs}ms, error=${error}`);
391
396
  throw error;
392
397
  }
393
398
  }
394
399
  async multiSwap(params, txReq = {}) {
395
400
  var _a;
396
401
  const useBundle = this.shouldUseBundle(params.chain, txReq);
397
- _Aggregator.traceLog(
398
- params.chain,
399
- "log",
400
- `multiSwap开始, user=${params.user}, pathsCount=${params.paths.length}, amountInWeis=[${params.amountInWeis.join(",")}], useBundle=${useBundle}`
401
- );
402
+ _Aggregator.traceLog(params.chain, "log", `multiSwap开始, user=${params.user}, pathsCount=${params.paths.length}, amountInWeis=[${params.amountInWeis.join(",")}], useBundle=${useBundle}`);
402
403
  const startTs = Date.now();
404
+ let txHash;
403
405
  try {
404
406
  const { pureTxReq, bundleBlockCount } = this.stripCustomFields(txReq, params.chain);
405
407
  txReq = pureTxReq;
@@ -441,7 +443,7 @@ var _Aggregator = class {
441
443
  try {
442
444
  estimateGas = await aggregator.multiSwap.estimateGas(params.user, params.amountInWeis, swapParamsList, params.minAmountOutLists, params.totalMinAmountOut, estimationOverrides);
443
445
  } catch (error) {
444
- _Aggregator.traceLog(params.chain, "error", `multiSwap estimateGas失败, error=${error}`);
446
+ _Aggregator.traceLog(params.chain, "error", `multiSwap estimateGas失败, rpcUrl=${this.getRpcUrl(params.chain)}, error=${error}`);
445
447
  throw error;
446
448
  }
447
449
  txReq = this.resolveGasLimit(txReq, estimateGas);
@@ -451,13 +453,22 @@ var _Aggregator = class {
451
453
  let receipt;
452
454
  if (useBundle) {
453
455
  const calldata = iface.encodeFunctionData("multiSwap", [params.user, params.amountInWeis, swapParamsList, params.minAmountOutLists, params.totalMinAmountOut]);
454
- const { receipt: flashbotsReceipt, txHash, targetBlocks } = await this.submitBundleTx(params.chain, wallet, txReq, aggregatorAddress, calldata, txReq.value ?? 0n, bundleBlockCount);
456
+ const {
457
+ receipt: flashbotsReceipt,
458
+ txHash: bundleTxHash,
459
+ targetBlocks
460
+ } = await this.submitBundleTx(params.chain, wallet, txReq, aggregatorAddress, calldata, txReq.value ?? 0n, bundleBlockCount);
461
+ txHash = bundleTxHash;
455
462
  if (!flashbotsReceipt) {
456
- throw new import_types.BundleNotIncludedError(`Bundle 未在目标区块上链, txHash=${txHash}, targetBlocks=[${targetBlocks.join(",")}]`, txHash);
463
+ throw new import_types.BundleNotIncludedError(`Bundle 未在目标区块上链, txHash=${bundleTxHash}, targetBlocks=[${targetBlocks.join(",")}]`, bundleTxHash);
464
+ }
465
+ if (flashbotsReceipt.status === 0) {
466
+ throw new import_types.TransactionRevertedError(flashbotsReceipt.hash, flashbotsReceipt.gasUsed);
457
467
  }
458
468
  receipt = flashbotsReceipt;
459
469
  } else {
460
470
  receipt = await this.sendContractTx(params.chain, aggregator, "multiSwap", [params.user, params.amountInWeis, swapParamsList, params.minAmountOutLists, params.totalMinAmountOut], txReq);
471
+ txHash = receipt.hash;
461
472
  }
462
473
  let amountOutList = null;
463
474
  for (const log of receipt.logs) {
@@ -474,11 +485,7 @@ var _Aggregator = class {
474
485
  }
475
486
  if (!amountOutList)
476
487
  throw new Error(`MultiSwapped event not found: ${receipt.hash}`);
477
- _Aggregator.traceLog(
478
- params.chain,
479
- "log",
480
- `multiSwap完成, txHash=${receipt.hash}, amountOutList=[${amountOutList.join(",")}], useBundle=${useBundle}, 总耗时=${Date.now() - startTs}ms`
481
- );
488
+ _Aggregator.traceLog(params.chain, "log", `multiSwap完成, txHash=${receipt.hash}, amountOutList=[${amountOutList.join(",")}], useBundle=${useBundle}, 总耗时=${Date.now() - startTs}ms`);
482
489
  return {
483
490
  hash: receipt.hash,
484
491
  from: receipt.from,
@@ -496,7 +503,7 @@ var _Aggregator = class {
496
503
  receipt
497
504
  };
498
505
  } catch (error) {
499
- _Aggregator.traceLog(params.chain, "error", `multiSwap异常, 总耗时=${Date.now() - startTs}ms, error=${error}`);
506
+ _Aggregator.traceLog(params.chain, "error", `multiSwap异常, rpcUrl=${this.getRpcUrl(params.chain)}, txHash=${txHash ?? "N/A"}, 总耗时=${Date.now() - startTs}ms, error=${error}`);
500
507
  throw error;
501
508
  }
502
509
  }
@@ -508,6 +515,7 @@ var _Aggregator = class {
508
515
  `batchMultiSwap开始, users=${params.users.length}, pathsCount=${params.paths.length}, amountInWeis=[${params.amountInWeis.join(",")}], useBundle=${useBundle}`
509
516
  );
510
517
  const startTs = Date.now();
518
+ let txHash;
511
519
  try {
512
520
  const { pureTxReq, bundleBlockCount } = this.stripCustomFields(txReq, params.chain);
513
521
  txReq = pureTxReq;
@@ -533,7 +541,7 @@ var _Aggregator = class {
533
541
  try {
534
542
  estimateGas = await aggregator.batchMultiSwap.estimateGas(params.users, params.amountInWeis, swapParamsList, params.minAmountOutLists, estimationOverrides);
535
543
  } catch (error) {
536
- _Aggregator.traceLog(params.chain, "error", `batchMultiSwap estimateGas失败, error=${error}`);
544
+ _Aggregator.traceLog(params.chain, "error", `batchMultiSwap estimateGas失败, rpcUrl=${this.getRpcUrl(params.chain)}, error=${error}`);
537
545
  throw error;
538
546
  }
539
547
  txReq = this.resolveGasLimit(txReq, estimateGas);
@@ -543,13 +551,22 @@ var _Aggregator = class {
543
551
  let receipt;
544
552
  if (useBundle) {
545
553
  const calldata = iface.encodeFunctionData("batchMultiSwap", [params.users, params.amountInWeis, swapParamsList, params.minAmountOutLists]);
546
- const { receipt: flashbotsReceipt, txHash, targetBlocks } = await this.submitBundleTx(params.chain, wallet, txReq, aggregatorAddress, calldata, txReq.value ?? 0n, bundleBlockCount);
554
+ const {
555
+ receipt: flashbotsReceipt,
556
+ txHash: bundleTxHash,
557
+ targetBlocks
558
+ } = await this.submitBundleTx(params.chain, wallet, txReq, aggregatorAddress, calldata, txReq.value ?? 0n, bundleBlockCount);
559
+ txHash = bundleTxHash;
547
560
  if (!flashbotsReceipt) {
548
- throw new import_types.BundleNotIncludedError(`Bundle 未在目标区块上链, txHash=${txHash}, targetBlocks=[${targetBlocks.join(",")}]`, txHash);
561
+ throw new import_types.BundleNotIncludedError(`Bundle 未在目标区块上链, txHash=${bundleTxHash}, targetBlocks=[${targetBlocks.join(",")}]`, bundleTxHash);
562
+ }
563
+ if (flashbotsReceipt.status === 0) {
564
+ throw new import_types.TransactionRevertedError(flashbotsReceipt.hash, flashbotsReceipt.gasUsed);
549
565
  }
550
566
  receipt = flashbotsReceipt;
551
567
  } else {
552
568
  receipt = await this.sendContractTx(params.chain, aggregator, "batchMultiSwap", [params.users, params.amountInWeis, swapParamsList, params.minAmountOutLists], txReq);
569
+ txHash = receipt.hash;
553
570
  }
554
571
  let userList = null;
555
572
  let amountInList = null;
@@ -573,11 +590,7 @@ var _Aggregator = class {
573
590
  if (userList.length !== params.users.length || amountOutList.length !== params.users.length) {
574
591
  throw new Error(`MultiSwapped event length mismatch: ${receipt.hash}`);
575
592
  }
576
- _Aggregator.traceLog(
577
- params.chain,
578
- "log",
579
- `batchMultiSwap完成, txHash=${receipt.hash}, amountOutList=[${amountOutList.join(",")}], useBundle=${useBundle}, 总耗时=${Date.now() - startTs}ms`
580
- );
593
+ _Aggregator.traceLog(params.chain, "log", `batchMultiSwap完成, txHash=${receipt.hash}, amountOutList=[${amountOutList.join(",")}], useBundle=${useBundle}, 总耗时=${Date.now() - startTs}ms`);
581
594
  return {
582
595
  hash: receipt.hash,
583
596
  from: receipt.from,
@@ -595,7 +608,7 @@ var _Aggregator = class {
595
608
  receipt
596
609
  };
597
610
  } catch (error) {
598
- _Aggregator.traceLog(params.chain, "error", `batchMultiSwap异常, 总耗时=${Date.now() - startTs}ms, error=${error}`);
611
+ _Aggregator.traceLog(params.chain, "error", `batchMultiSwap异常, rpcUrl=${this.getRpcUrl(params.chain)}, txHash=${txHash ?? "N/A"}, 总耗时=${Date.now() - startTs}ms, error=${error}`);
599
612
  throw error;
600
613
  }
601
614
  }
@@ -630,6 +643,7 @@ var _Aggregator = class {
630
643
  `bridge开始, user=${params.user}, bridgeType=${params.bridgeType}, token=${params.tokenAddress}, amountInWei=${params.amountInWei}, destChain=${params.destChain}, useBundle=${useBundle}`
631
644
  );
632
645
  const startTs = Date.now();
646
+ let txHash;
633
647
  try {
634
648
  const { pureTxReq, bundleBlockCount } = this.stripCustomFields(txReq, params.chain);
635
649
  txReq = pureTxReq;
@@ -661,7 +675,7 @@ var _Aggregator = class {
661
675
  try {
662
676
  estimateGas = await aggregator.bridge.estimateGas(params.user, bridgeArgs, estimationOverrides);
663
677
  } catch (error) {
664
- _Aggregator.traceLog(params.chain, "error", `bridge estimateGas失败, error=${error}`);
678
+ _Aggregator.traceLog(params.chain, "error", `bridge estimateGas失败, rpcUrl=${this.getRpcUrl(params.chain)}, error=${error}`);
665
679
  throw error;
666
680
  }
667
681
  txReq = this.resolveGasLimit(txReq, estimateGas);
@@ -671,19 +685,24 @@ var _Aggregator = class {
671
685
  if (useBundle) {
672
686
  const iface = new import_ethers.ethers.Interface(import_aggregator.default);
673
687
  const calldata = iface.encodeFunctionData("bridge", [params.user, bridgeArgs]);
674
- const { receipt: flashbotsReceipt, txHash, targetBlocks } = await this.submitBundleTx(params.chain, wallet, txReq, aggregatorAddress, calldata, txReq.value ?? 0n, bundleBlockCount);
688
+ const {
689
+ receipt: flashbotsReceipt,
690
+ txHash: bundleTxHash,
691
+ targetBlocks
692
+ } = await this.submitBundleTx(params.chain, wallet, txReq, aggregatorAddress, calldata, txReq.value ?? 0n, bundleBlockCount);
693
+ txHash = bundleTxHash;
675
694
  if (!flashbotsReceipt) {
676
- throw new import_types.BundleNotIncludedError(`Bundle 未在目标区块上链, txHash=${txHash}, targetBlocks=[${targetBlocks.join(",")}]`, txHash);
695
+ throw new import_types.BundleNotIncludedError(`Bundle 未在目标区块上链, txHash=${bundleTxHash}, targetBlocks=[${targetBlocks.join(",")}]`, bundleTxHash);
696
+ }
697
+ if (flashbotsReceipt.status === 0) {
698
+ throw new import_types.TransactionRevertedError(flashbotsReceipt.hash, flashbotsReceipt.gasUsed);
677
699
  }
678
700
  txReceipt = flashbotsReceipt;
679
701
  } else {
680
702
  txReceipt = await this.sendContractTx(params.chain, aggregator, "bridge", [params.user, bridgeArgs], txReq);
703
+ txHash = txReceipt.hash;
681
704
  }
682
- _Aggregator.traceLog(
683
- params.chain,
684
- "log",
685
- `bridge完成, txHash=${txReceipt.hash}, useBundle=${useBundle}, 总耗时=${Date.now() - startTs}ms`
686
- );
705
+ _Aggregator.traceLog(params.chain, "log", `bridge完成, txHash=${txReceipt.hash}, useBundle=${useBundle}, 总耗时=${Date.now() - startTs}ms`);
687
706
  return {
688
707
  fromToken: params.tokenAddress,
689
708
  toToken: params.tokenAddress,
@@ -693,7 +712,7 @@ var _Aggregator = class {
693
712
  to: txReceipt.to ?? ""
694
713
  };
695
714
  } catch (error) {
696
- _Aggregator.traceLog(params.chain, "error", `bridge异常, 总耗时=${Date.now() - startTs}ms, error=${error}`);
715
+ _Aggregator.traceLog(params.chain, "error", `bridge异常, rpcUrl=${this.getRpcUrl(params.chain)}, txHash=${txHash ?? "N/A"}, 总耗时=${Date.now() - startTs}ms, error=${error}`);
697
716
  throw error;
698
717
  }
699
718
  }
@@ -862,17 +881,87 @@ var _Aggregator = class {
862
881
  }
863
882
  return null;
864
883
  }
884
+ broadcastToAll(chain, signedTx, txHash, providers) {
885
+ for (const { provider, label } of providers) {
886
+ const sendTs = Date.now();
887
+ provider.broadcastTransaction(signedTx).then(
888
+ () => _Aggregator.traceLog(chain, "log", `广播${label} OK, txHash=${txHash}, 耗时=${Date.now() - sendTs}ms`),
889
+ (err) => {
890
+ const msg = (err == null ? void 0 : err.message) ?? String(err);
891
+ if (msg.includes("already known") || msg.includes("nonce too low") || msg.includes("nonce has already been used") || msg.includes("ALREADY_EXISTS") || msg.includes("NONCE_EXPIRED")) {
892
+ _Aggregator.traceLog(chain, "log", `广播${label} already-known, txHash=${txHash}, 耗时=${Date.now() - sendTs}ms`);
893
+ } else {
894
+ _Aggregator.traceLog(chain, "warn", `广播${label} 失败: ${msg}, txHash=${txHash}, 耗时=${Date.now() - sendTs}ms`);
895
+ }
896
+ }
897
+ );
898
+ }
899
+ }
900
+ raceForReceipt(chain, txHash, providers, ownedProviders = [], timeoutMs = _Aggregator.TX_CONFIRM_TIMEOUT_MS) {
901
+ return new Promise((resolve, reject) => {
902
+ let settled = false;
903
+ const blockHandlers = [];
904
+ const cleanup = () => {
905
+ for (const { provider, handler } of blockHandlers) {
906
+ provider.off("block", handler);
907
+ }
908
+ for (const p of ownedProviders) {
909
+ try {
910
+ p.destroy();
911
+ } catch {
912
+ }
913
+ }
914
+ };
915
+ const onFound = (receipt, label) => {
916
+ if (settled)
917
+ return;
918
+ settled = true;
919
+ clearTimeout(timer);
920
+ cleanup();
921
+ _Aggregator.traceLog(chain, "log", `${label} 先查到receipt, txHash=${txHash}, block=${receipt.blockNumber}`);
922
+ resolve(receipt);
923
+ };
924
+ const checkReceipt = (provider, label) => {
925
+ if (settled)
926
+ return;
927
+ provider.getTransactionReceipt(txHash).then(
928
+ (r) => {
929
+ if (r)
930
+ onFound(r, label);
931
+ },
932
+ () => {
933
+ }
934
+ );
935
+ };
936
+ for (const { provider, label } of providers) {
937
+ const handler = () => checkReceipt(provider, label);
938
+ provider.on("block", handler);
939
+ blockHandlers.push({ provider, handler });
940
+ checkReceipt(provider, label);
941
+ }
942
+ const timer = setTimeout(() => {
943
+ if (settled)
944
+ return;
945
+ settled = true;
946
+ cleanup();
947
+ reject(new Error(`Transaction not confirmed within ${timeoutMs}ms: ${txHash}`));
948
+ }, timeoutMs);
949
+ });
950
+ }
865
951
  async sendContractTx(chain, contract, method, args, txReq) {
866
952
  const extraUrls = this.broadcastRpcsMap.get(chain);
867
953
  if (!extraUrls || extraUrls.length === 0) {
868
954
  _Aggregator.traceLog(chain, "log", `渠道=单RPC直发, method=${method}, nonce=${txReq.nonce}, gasLimit=${txReq.gasLimit}`);
869
955
  const sendTs = Date.now();
870
- const txResponse2 = await contract[method](...args, txReq);
871
- _Aggregator.traceLog(chain, "log", `单RPC已发送, txHash=${txResponse2.hash}, 耗时=${Date.now() - sendTs}ms`);
872
- const receipt2 = await txResponse2.wait();
956
+ const txResponse = await contract[method](...args, txReq);
957
+ _Aggregator.traceLog(chain, "log", `单RPC已发送, txHash=${txResponse.hash}, 耗时=${Date.now() - sendTs}ms`);
958
+ const receipt2 = await txResponse.wait();
873
959
  const singleProvider = this.providerClient.getProvider(chain);
874
960
  const blockTs2 = await this.getBlockTimestampStr(singleProvider, receipt2.blockNumber);
875
- _Aggregator.traceLog(chain, "log", `单RPC上链确认, txHash=${receipt2.hash}, blockNumber=${receipt2.blockNumber}, 出块时间=${blockTs2}, gasUsed=${receipt2.gasUsed}`);
961
+ _Aggregator.traceLog(chain, "log", `单RPC上链确认, txHash=${receipt2.hash}, blockNumber=${receipt2.blockNumber}, 出块时间=${blockTs2}, gasUsed=${receipt2.gasUsed}, status=${receipt2.status}`);
962
+ if (receipt2.status === 0) {
963
+ throw new import_types.TransactionRevertedError(receipt2.hash, receipt2.gasUsed);
964
+ }
876
965
  return receipt2;
877
966
  }
878
967
  const wallet = this.providerClient.getWallet(chain);
@@ -882,68 +971,31 @@ var _Aggregator = class {
882
971
  populated.chainId = network.chainId;
883
972
  const signedTx = await wallet.signTransaction(populated);
884
973
  const txHash = import_ethers.ethers.keccak256(signedTx);
885
- const provider = wallet.provider;
886
- const currentBlock = await provider.getBlockNumber();
974
+ const mainProvider = wallet.provider;
975
+ const currentBlock = await mainProvider.getBlockNumber();
976
+ const extraProviders = extraUrls.map((url, i) => ({
977
+ provider: new import_ethers.ethers.JsonRpcProvider(url),
978
+ label: `RPC[${i + 1}/${new URL(url).hostname}]`
979
+ }));
980
+ const allProviders = [{ provider: mainProvider, label: "RPC[0/主]" }, ...extraProviders];
981
+ const ownedProviders = extraProviders.map((e) => e.provider);
887
982
  _Aggregator.traceLog(
888
983
  chain,
889
984
  "log",
890
- `渠道=多RPC广播, method=${method}, nonce=${txReq.nonce}, gasLimit=${txReq.gasLimit}, 当前区块=${currentBlock}, 期望上链区块=${currentBlock + 1}, txHash=${txHash}, 主RPC=1, 额外RPC=${extraUrls.length}`
985
+ `渠道=多RPC广播, method=${method}, nonce=${txReq.nonce}, gasLimit=${txReq.gasLimit}, 当前区块=${currentBlock}, 期望上链区块=${currentBlock + 1}, txHash=${txHash}, RPC数量=${allProviders.length}(主1+副${extraUrls.length})`
891
986
  );
892
- const broadcastToExtra = (url, index) => {
893
- const label = `extra[${index}]`;
894
- const rpc = new import_ethers.ethers.JsonRpcProvider(url);
895
- const sendTs = Date.now();
896
- rpc.broadcastTransaction(signedTx).then(
897
- () => _Aggregator.traceLog(chain, "log", `广播${label} OK, txHash=${txHash}, 耗时=${Date.now() - sendTs}ms`),
898
- (err) => {
899
- const msg = (err == null ? void 0 : err.message) ?? String(err);
900
- if (msg.includes("already known") || msg.includes("nonce too low") || msg.includes("ALREADY_EXISTS") || msg.includes("nonce has already been used")) {
901
- _Aggregator.traceLog(chain, "log", `广播${label} already known, txHash=${txHash}, 耗时=${Date.now() - sendTs}ms`);
902
- } else {
903
- _Aggregator.traceLog(chain, "warn", `广播${label} 失败: ${msg}, txHash=${txHash}, 耗时=${Date.now() - sendTs}ms`);
904
- }
905
- }
906
- );
907
- };
908
- const mainSendTs = Date.now();
909
- extraUrls.forEach((url, i) => broadcastToExtra(url, i));
910
- let txResponse;
911
- try {
912
- txResponse = await provider.broadcastTransaction(signedTx);
913
- _Aggregator.traceLog(chain, "log", `广播主RPC已发送, txHash=${txHash}, 耗时=${Date.now() - mainSendTs}ms`);
914
- } catch (err) {
915
- const msg = (err == null ? void 0 : err.message) ?? String(err);
916
- if (msg.includes("already known") || msg.includes("nonce too low") || msg.includes("ALREADY_EXISTS") || msg.includes("nonce has already been used")) {
917
- _Aggregator.traceLog(chain, "log", `广播主RPC already known (可能额外RPC已上链), txHash=${txHash}, 耗时=${Date.now() - mainSendTs}ms, 开始轮询receipt`);
918
- const existingReceipt = await provider.getTransactionReceipt(txHash);
919
- if (existingReceipt) {
920
- _Aggregator.traceLog(chain, "log", `交易已上链, txHash=${txHash}, blockNumber=${existingReceipt.blockNumber}`);
921
- const blockTs2 = await this.getBlockTimestampStr(provider, existingReceipt.blockNumber);
922
- _Aggregator.traceLog(
923
- chain,
924
- "log",
925
- `多RPC广播上链确认, txHash=${existingReceipt.hash}, blockNumber=${existingReceipt.blockNumber}, 出块时间=${blockTs2}, gasUsed=${existingReceipt.gasUsed}, 总耗时=${Date.now() - mainSendTs}ms`
926
- );
927
- return existingReceipt;
928
- }
929
- txResponse = await provider.getTransaction(txHash);
930
- if (!txResponse) {
931
- throw new Error(`Transaction not found after broadcast error: ${txHash}`);
932
- }
933
- } else {
934
- _Aggregator.traceLog(chain, "error", `广播主RPC失败: ${msg}, txHash=${txHash}, 耗时=${Date.now() - mainSendTs}ms`);
935
- throw err;
936
- }
937
- }
938
- const receipt = await txResponse.wait();
939
- if (!receipt)
940
- throw new Error(`Transaction receipt is null: ${txHash}`);
941
- const blockTs = await this.getBlockTimestampStr(provider, receipt.blockNumber);
987
+ const broadcastTs = Date.now();
988
+ this.broadcastToAll(chain, signedTx, txHash, allProviders);
989
+ const receipt = await this.raceForReceipt(chain, txHash, allProviders, ownedProviders);
990
+ const blockTs = await this.getBlockTimestampStr(mainProvider, receipt.blockNumber);
942
991
  _Aggregator.traceLog(
943
992
  chain,
944
993
  "log",
945
- `多RPC广播上链确认, txHash=${receipt.hash}, blockNumber=${receipt.blockNumber}, 出块时间=${blockTs}, gasUsed=${receipt.gasUsed}, 总耗时=${Date.now() - mainSendTs}ms`
994
+ `多RPC广播上链确认, txHash=${receipt.hash}, blockNumber=${receipt.blockNumber}, 出块时间=${blockTs}, gasUsed=${receipt.gasUsed}, status=${receipt.status}, 总耗时=${Date.now() - broadcastTs}ms`
946
995
  );
996
+ if (receipt.status === 0) {
997
+ throw new import_types.TransactionRevertedError(receipt.hash, receipt.gasUsed);
998
+ }
947
999
  return receipt;
948
1000
  }
949
1001
  async submitBundleTx(chain, wallet, txReq, to, data, value, bundleBlockCount) {
@@ -973,10 +1025,7 @@ var _Aggregator = class {
973
1025
  baseTx.gasPrice = txReq.gasPrice;
974
1026
  baseTx.type = 0;
975
1027
  }
976
- const [signTx, currentBlock] = await Promise.all([
977
- wallet.signTransaction(baseTx),
978
- provider.getBlockNumber()
979
- ]);
1028
+ const [signTx, currentBlock] = await Promise.all([wallet.signTransaction(baseTx), provider.getBlockNumber()]);
980
1029
  const rawTxs = [signTx];
981
1030
  const txHash = import_ethers.ethers.keccak256(rawTxs[0]);
982
1031
  const normalizedBundleBlockCount = this.normalizeBundleBlockCount(bundleBlockCount);
@@ -1019,11 +1068,7 @@ var _Aggregator = class {
1019
1068
  const [receipt] = await Promise.all([
1020
1069
  inclusionPromise,
1021
1070
  Promise.all(fireAndForget).then(() => {
1022
- _Aggregator.traceLog(
1023
- chain,
1024
- "log",
1025
- `Bundle发送完成, 成功=${totalSent}, 失败=${totalFailed}, 总请求=${fireAndForget.length}, 耗时=${Date.now() - sendBundleTs}ms, txHash=${txHash}`
1026
- );
1071
+ _Aggregator.traceLog(chain, "log", `Bundle发送完成, 成功=${totalSent}, 失败=${totalFailed}, 总请求=${fireAndForget.length}, 耗时=${Date.now() - sendBundleTs}ms, txHash=${txHash}`);
1027
1072
  if (totalSent === 0)
1028
1073
  abortController.abort();
1029
1074
  })
@@ -1036,11 +1081,7 @@ var _Aggregator = class {
1036
1081
  `Bundle上链确认, txHash=${receipt.hash}, blockNumber=${receipt.blockNumber}, 出块时间=${blockTs}, gasUsed=${receipt.gasUsed}, 等待耗时=${Date.now() - waitTs}ms`
1037
1082
  );
1038
1083
  } else {
1039
- _Aggregator.traceLog(
1040
- chain,
1041
- "warn",
1042
- `Bundle未在目标区块上链, txHash=${txHash}, 目标区块=[${targetBlocks.join(",")}], 等待耗时=${Date.now() - waitTs}ms`
1043
- );
1084
+ _Aggregator.traceLog(chain, "warn", `Bundle未在目标区块上链, txHash=${txHash}, 目标区块=[${targetBlocks.join(",")}], 等待耗时=${Date.now() - waitTs}ms`);
1044
1085
  }
1045
1086
  return { receipt, txHash, targetBlocks };
1046
1087
  }
@@ -1061,7 +1102,7 @@ var _Aggregator = class {
1061
1102
  try {
1062
1103
  estimateGas = await aggregator.swap.estimateGas(params.user, params.amountInWei, swapParams, params.minAmountOutList, estimationOverrides);
1063
1104
  } catch (error) {
1064
- _Aggregator.traceLog(params.chain, "error", `swapByFlashbots estimateGas失败, error=${error}`);
1105
+ _Aggregator.traceLog(params.chain, "error", `swapByFlashbots estimateGas失败, rpcUrl=${this.getRpcUrl(params.chain)}, error=${error}`);
1065
1106
  throw error;
1066
1107
  }
1067
1108
  _Aggregator.traceLog(params.chain, "log", `swapByFlashbots estimateGas=${estimateGas}`);
@@ -1124,6 +1165,10 @@ var _Aggregator = class {
1124
1165
  }
1125
1166
  return tx;
1126
1167
  }
1168
+ getRpcUrl(chain) {
1169
+ var _a;
1170
+ return ((_a = this.config.rpc[chain]) == null ? void 0 : _a.url) ?? "unknown";
1171
+ }
1127
1172
  validateParams(params) {
1128
1173
  if (!params.chain)
1129
1174
  throw new Error("Chain not found");
@@ -1169,6 +1214,7 @@ Aggregator.DEFAULT_ETH_BUILDERS = {
1169
1214
  tbuilder: "https://relay.tbuilder.xyz",
1170
1215
  bobabuilder: "https://relay.boba-builder.com"
1171
1216
  };
1217
+ Aggregator.TX_CONFIRM_TIMEOUT_MS = 12e4;
1172
1218
  Aggregator.BUNDLE_POLL_INTERVAL_MS = 500;
1173
1219
  Aggregator.BUNDLE_INCLUSION_TIMEOUT_MS = 3e4;
1174
1220
  var aggregator_default = Aggregator;
@@ -50,9 +50,12 @@ declare class Aggregator {
50
50
  };
51
51
  estimateGas(estimateType: IEstimateType, params: IBridgeParams | ISwapByPathParams | ISwapAndBridgeParams | IBatchMultiSwapParams): Promise<bigint>;
52
52
  getAggregatorSupportContracts(chain: ChainNameEnum): Promise<SupportContracts[]>;
53
+ private static readonly TX_CONFIRM_TIMEOUT_MS;
53
54
  private static readonly BUNDLE_POLL_INTERVAL_MS;
54
55
  private static readonly BUNDLE_INCLUSION_TIMEOUT_MS;
55
56
  private waitForBundleInclusion;
57
+ private broadcastToAll;
58
+ private raceForReceipt;
56
59
  private sendContractTx;
57
60
  private submitBundleTx;
58
61
  private swapByFlashbots;
@@ -60,6 +63,7 @@ declare class Aggregator {
60
63
  private resolveNonce;
61
64
  private resolvePricing;
62
65
  private sanitizePricing;
66
+ private getRpcUrl;
63
67
  private validateParams;
64
68
  private shouldUseBundle;
65
69
  private stripCustomFields;
@@ -40,6 +40,7 @@ __export(src_exports, {
40
40
  LayerZeroV1ChainNameMap: () => import_types.LayerZeroV1ChainNameMap,
41
41
  LayerZeroV2ChainIdMap: () => import_types.LayerZeroV2ChainIdMap,
42
42
  LayerZeroV2ChainNameMap: () => import_types.LayerZeroV2ChainNameMap,
43
+ TransactionRevertedError: () => import_types2.TransactionRevertedError,
43
44
  default: () => src_default
44
45
  });
45
46
  module.exports = __toCommonJS(src_exports);
@@ -144,5 +145,6 @@ var src_default = Hermes;
144
145
  LayerZeroV1ChainIdMap,
145
146
  LayerZeroV1ChainNameMap,
146
147
  LayerZeroV2ChainIdMap,
147
- LayerZeroV2ChainNameMap
148
+ LayerZeroV2ChainNameMap,
149
+ TransactionRevertedError
148
150
  });
@@ -2,8 +2,8 @@ import type { HermesTxRequest, IBatchMultiSwapParams, IBatchExpectParams, ISwapB
2
2
  import { ChainNameEnum, AddressConst, DexType, BridgeType, IEstimateType, LayerZeroV1ChainIdMap, LayerZeroV1ChainNameMap, LayerZeroV2ChainIdMap, LayerZeroV2ChainNameMap } from './types.js';
3
3
  import { TransactionRequest } from 'ethers';
4
4
  export type { HermesTxRequest, IBatchMultiSwapAndBridgeParams, IExpectSplitOrderParams, IExpectSplitOrderResp, IMultiSwapParams, IBatchMultiSwapParams, IBatchExpectParams, IBatchReceipt, ISwapByPathParams as ISwapParams, IBridgeParams, ISwapAndBridgeParams, IReceipt, IConfig, IBuilderConfig, IExpectPayload, IRouterPath, SupportContracts, IHermesSignalResponse, IHermesSignalRoute, };
5
- import { BundleNotIncludedError } from './types.js';
6
- export { ChainNameEnum, AddressConst, DexType, BridgeType, IEstimateType, LayerZeroV1ChainIdMap, LayerZeroV1ChainNameMap, LayerZeroV2ChainIdMap, LayerZeroV2ChainNameMap, BundleNotIncludedError };
5
+ import { BundleNotIncludedError, TransactionRevertedError } from './types.js';
6
+ export { ChainNameEnum, AddressConst, DexType, BridgeType, IEstimateType, LayerZeroV1ChainIdMap, LayerZeroV1ChainNameMap, LayerZeroV2ChainIdMap, LayerZeroV2ChainNameMap, BundleNotIncludedError, TransactionRevertedError };
7
7
  declare class Hermes {
8
8
  private readonly config;
9
9
  private readonly providerClient;
@@ -28,7 +28,8 @@ __export(types_exports, {
28
28
  LayerZeroV1ChainIdMap: () => LayerZeroV1ChainIdMap,
29
29
  LayerZeroV1ChainNameMap: () => LayerZeroV1ChainNameMap,
30
30
  LayerZeroV2ChainIdMap: () => LayerZeroV2ChainIdMap,
31
- LayerZeroV2ChainNameMap: () => LayerZeroV2ChainNameMap
31
+ LayerZeroV2ChainNameMap: () => LayerZeroV2ChainNameMap,
32
+ TransactionRevertedError: () => TransactionRevertedError
32
33
  });
33
34
  module.exports = __toCommonJS(types_exports);
34
35
  var import_lz_definitions = require("@layerzerolabs/lz-definitions");
@@ -1050,6 +1051,14 @@ var BundleNotIncludedError = class extends Error {
1050
1051
  this.txHash = txHash;
1051
1052
  }
1052
1053
  };
1054
+ var TransactionRevertedError = class extends Error {
1055
+ constructor(txHash, gasUsed) {
1056
+ super(`Transaction reverted: ${txHash}, gasUsed=${gasUsed}`);
1057
+ this.name = "TransactionRevertedError";
1058
+ this.txHash = txHash;
1059
+ this.gasUsed = gasUsed;
1060
+ }
1061
+ };
1053
1062
  // Annotate the CommonJS export names for ESM import in node:
1054
1063
  0 && (module.exports = {
1055
1064
  AddressConst,
@@ -1061,5 +1070,6 @@ var BundleNotIncludedError = class extends Error {
1061
1070
  LayerZeroV1ChainIdMap,
1062
1071
  LayerZeroV1ChainNameMap,
1063
1072
  LayerZeroV2ChainIdMap,
1064
- LayerZeroV2ChainNameMap
1073
+ LayerZeroV2ChainNameMap,
1074
+ TransactionRevertedError
1065
1075
  });
@@ -516,3 +516,8 @@ export declare class BundleNotIncludedError extends Error {
516
516
  readonly txHash: string;
517
517
  constructor(message: string, txHash: string);
518
518
  }
519
+ export declare class TransactionRevertedError extends Error {
520
+ readonly txHash: string;
521
+ readonly gasUsed: bigint;
522
+ constructor(txHash: string, gasUsed: bigint);
523
+ }
@@ -50,9 +50,12 @@ declare class Aggregator {
50
50
  };
51
51
  estimateGas(estimateType: IEstimateType, params: IBridgeParams | ISwapByPathParams | ISwapAndBridgeParams | IBatchMultiSwapParams): Promise<bigint>;
52
52
  getAggregatorSupportContracts(chain: ChainNameEnum): Promise<SupportContracts[]>;
53
+ private static readonly TX_CONFIRM_TIMEOUT_MS;
53
54
  private static readonly BUNDLE_POLL_INTERVAL_MS;
54
55
  private static readonly BUNDLE_INCLUSION_TIMEOUT_MS;
55
56
  private waitForBundleInclusion;
57
+ private broadcastToAll;
58
+ private raceForReceipt;
56
59
  private sendContractTx;
57
60
  private submitBundleTx;
58
61
  private swapByFlashbots;
@@ -60,6 +63,7 @@ declare class Aggregator {
60
63
  private resolveNonce;
61
64
  private resolvePricing;
62
65
  private sanitizePricing;
66
+ private getRpcUrl;
63
67
  private validateParams;
64
68
  private shouldUseBundle;
65
69
  private stripCustomFields;