hermes-swap 0.6.9 → 0.6.11

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.
@@ -90,6 +90,20 @@ var _Aggregator = class {
90
90
  return "unknown";
91
91
  }
92
92
  }
93
+ emitTrace(txReq, chain, level, stage, message) {
94
+ var _a;
95
+ (_a = txReq == null ? void 0 : txReq.trace) == null ? void 0 : _a.call(txReq, {
96
+ ts: Date.now(),
97
+ chain,
98
+ level,
99
+ stage,
100
+ message
101
+ });
102
+ }
103
+ trace(chain, txReq, level, stage, message) {
104
+ _Aggregator.traceLog(chain, level, message);
105
+ this.emitTrace(txReq, chain, level, stage, message);
106
+ }
93
107
  getAggregator(chain) {
94
108
  if (!this.aggregatorMap.has(chain)) {
95
109
  throw new Error(`Aggregator not found for chain: ${chain}`);
@@ -118,6 +132,15 @@ var _Aggregator = class {
118
132
  }
119
133
  throw new Error(`No builder config for chain: ${chain}`);
120
134
  }
135
+ getDefaultBundleBlockCount(chain) {
136
+ if (!chain)
137
+ return 1;
138
+ if (this.builderConfigMap.has(chain))
139
+ return 3;
140
+ if (chain === import_types.ChainNameEnum.ETH && this.flashbotSigner)
141
+ return 3;
142
+ return 1;
143
+ }
121
144
  getSequencerRpcs(config) {
122
145
  const merged = {
123
146
  ..._Aggregator.DEFAULT_SEQUENCER_RPC_BY_CHAIN
@@ -161,6 +184,16 @@ var _Aggregator = class {
161
184
  ]
162
185
  };
163
186
  }
187
+ static getJsonRpcErrorMessage(data) {
188
+ var _a;
189
+ if (!(data == null ? void 0 : data.error))
190
+ return null;
191
+ if (typeof data.error === "string")
192
+ return data.error;
193
+ if (typeof ((_a = data.error) == null ? void 0 : _a.message) === "string")
194
+ return data.error.message;
195
+ return JSON.stringify(data.error);
196
+ }
164
197
  async sendBundle(chain, rawTxs, targetBlock, signer) {
165
198
  const builderCfg = this.getBuilderConfig(chain);
166
199
  const body = this.buildBundleRequestBody(builderCfg, rawTxs, targetBlock);
@@ -176,6 +209,14 @@ var _Aggregator = class {
176
209
  const ts = Date.now();
177
210
  try {
178
211
  const response = await import_axios.default.post(url, body, { headers });
212
+ const rpcErrorMessage = _Aggregator.getJsonRpcErrorMessage(response.data);
213
+ if (rpcErrorMessage) {
214
+ failed++;
215
+ const detail2 = `[FAIL] ${name}: ${rpcErrorMessage}`;
216
+ details.push(detail2);
217
+ _Aggregator.traceLog(chain, "error", `Builder[${name}] 发送失败, targetBlock=${targetBlock}, 耗时=${Date.now() - ts}ms, error=${rpcErrorMessage}, resp=${JSON.stringify(response.data)}`);
218
+ return;
219
+ }
179
220
  sent++;
180
221
  const detail = `[OK] ${name}`;
181
222
  details.push(detail);
@@ -311,10 +352,13 @@ var _Aggregator = class {
311
352
  }
312
353
  async swapAndBridge(params, txReq = {}) {
313
354
  var _a, _b;
355
+ const traceReq = txReq;
314
356
  const useBundle = this.shouldUseBundle(params.chain, txReq);
315
- _Aggregator.traceLog(
357
+ this.trace(
316
358
  params.chain,
359
+ traceReq,
317
360
  "log",
361
+ "hermes-swapAndBridge-开始",
318
362
  `swapAndBridge开始, user=${params.user}, amountInWeis=[${params.amountInWeis.join(",")}], bridgeType=${params.bridgeType}, destChain=${params.destChain}, useBundle=${useBundle}`
319
363
  );
320
364
  const startTs = Date.now();
@@ -357,14 +401,26 @@ var _Aggregator = class {
357
401
  const { gasLimit: _ignore, ...estimationOverrides } = txReq;
358
402
  const args = [params.user, params.amountInWeis, swapParamsList, params.minAmountOutLists, bridgeArgs, params.totalMinAmountOut];
359
403
  try {
404
+ this.trace(params.chain, traceReq, "log", "hermes-swapAndBridge-estimateGas-开始", "swapAndBridge estimateGas 开始");
360
405
  estimateGas = await aggregator.multiSwapAndBridge.estimateGas(...args, estimationOverrides);
406
+ this.trace(params.chain, traceReq, "log", "hermes-swapAndBridge-estimateGas-完成", `swapAndBridge estimateGas 完成, estimateGas=${estimateGas}`);
361
407
  } catch (error) {
362
- _Aggregator.traceLog(params.chain, "error", `swapAndBridge estimateGas失败, rpcUrl=${this.getRpcUrl(params.chain)}, error=${error}`);
408
+ this.trace(params.chain, traceReq, "error", "hermes-swapAndBridge-estimateGas-失败", `swapAndBridge estimateGas失败, rpcUrl=${this.getRpcUrl(params.chain)}, error=${error}`);
363
409
  throw error;
364
410
  }
365
411
  txReq = this.resolveGasLimit(txReq, estimateGas);
412
+ this.trace(params.chain, traceReq, "log", "hermes-resolveNonce-开始", "swapAndBridge resolveNonce 开始");
366
413
  txReq = await this.resolveNonce(wallet.provider, wallet.address, txReq);
414
+ this.trace(params.chain, traceReq, "log", "hermes-resolveNonce-完成", `swapAndBridge resolveNonce 完成, nonce=${txReq.nonce}`);
415
+ this.trace(params.chain, traceReq, "log", "hermes-resolvePricing-开始", "swapAndBridge resolvePricing 开始");
367
416
  txReq = await this.resolvePricing(wallet.provider, txReq);
417
+ this.trace(
418
+ params.chain,
419
+ traceReq,
420
+ "log",
421
+ "hermes-resolvePricing-完成",
422
+ `swapAndBridge resolvePricing 完成, gasPrice=${txReq.gasPrice ?? "n/a"}, maxFeePerGas=${txReq.maxFeePerGas ?? "n/a"}, maxPriorityFeePerGas=${txReq.maxPriorityFeePerGas ?? "n/a"}`
423
+ );
368
424
  const iface = new import_ethers.ethers.Interface(import_aggregator.default);
369
425
  let receipt;
370
426
  if (useBundle) {
@@ -373,7 +429,7 @@ var _Aggregator = class {
373
429
  receipt: flashbotsReceipt,
374
430
  txHash: bundleTxHash,
375
431
  targetBlocks
376
- } = await this.submitBundleTx(params.chain, wallet, txReq, aggregatorAddress, calldata, txReq.value ?? 0n, bundleBlockCount);
432
+ } = await this.submitBundleTx(params.chain, wallet, txReq, aggregatorAddress, calldata, txReq.value ?? 0n, bundleBlockCount, traceReq);
377
433
  txHash = bundleTxHash;
378
434
  if (!flashbotsReceipt) {
379
435
  throw new import_types.BundleNotIncludedError(`Bundle 未在目标区块上链, txHash=${bundleTxHash}, targetBlocks=[${targetBlocks.join(",")}]`, bundleTxHash);
@@ -383,7 +439,7 @@ var _Aggregator = class {
383
439
  }
384
440
  receipt = flashbotsReceipt;
385
441
  } else {
386
- receipt = await this.sendContractTx(params.chain, aggregator, "multiSwapAndBridge", args, txReq);
442
+ receipt = await this.sendContractTx(params.chain, aggregator, "multiSwapAndBridge", args, txReq, traceReq);
387
443
  txHash = receipt.hash;
388
444
  }
389
445
  let receiptUser = null;
@@ -411,7 +467,13 @@ var _Aggregator = class {
411
467
  const fromTokenAddress = ((_a = pathStart == null ? void 0 : pathStart[0]) == null ? void 0 : _a.fromCoinAddress) ?? "";
412
468
  const toTokenAddress = ((_b = pathEnd == null ? void 0 : pathEnd[pathEnd.length - 1]) == null ? void 0 : _b.toCoinAddress) ?? "";
413
469
  const totalAmountInFromEvent = amountInList.reduce((sum, amount) => sum + amount, 0n);
414
- _Aggregator.traceLog(params.chain, "log", `swapAndBridge完成, txHash=${receipt.hash}, amountOut=${amountOut}, useBundle=${useBundle}, 总耗时=${Date.now() - startTs}ms`);
470
+ this.trace(
471
+ params.chain,
472
+ traceReq,
473
+ "log",
474
+ "hermes-swapAndBridge-完成",
475
+ `swapAndBridge完成, txHash=${receipt.hash}, amountOut=${amountOut}, useBundle=${useBundle}, 总耗时=${Date.now() - startTs}ms`
476
+ );
415
477
  return {
416
478
  hash: receipt.hash,
417
479
  from: receipt.from,
@@ -420,14 +482,27 @@ var _Aggregator = class {
420
482
  receipt
421
483
  };
422
484
  } catch (error) {
423
- _Aggregator.traceLog(params.chain, "error", `swapAndBridge异常, rpcUrl=${this.getRpcUrl(params.chain)}, txHash=${txHash ?? "N/A"}, 总耗时=${Date.now() - startTs}ms, error=${error}`);
485
+ this.trace(
486
+ params.chain,
487
+ traceReq,
488
+ "error",
489
+ "hermes-swapAndBridge-异常",
490
+ `swapAndBridge异常, rpcUrl=${this.getRpcUrl(params.chain)}, txHash=${txHash ?? "N/A"}, 总耗时=${Date.now() - startTs}ms, error=${error}`
491
+ );
424
492
  throw error;
425
493
  }
426
494
  }
427
495
  async multiSwap(params, txReq = {}) {
428
496
  var _a;
497
+ const traceReq = txReq;
429
498
  const useBundle = this.shouldUseBundle(params.chain, txReq);
430
- _Aggregator.traceLog(params.chain, "log", `multiSwap开始, user=${params.user}, pathsCount=${params.paths.length}, amountInWeis=[${params.amountInWeis.join(",")}], useBundle=${useBundle}`);
499
+ this.trace(
500
+ params.chain,
501
+ traceReq,
502
+ "log",
503
+ "hermes-multiSwap-开始",
504
+ `multiSwap开始, user=${params.user}, pathsCount=${params.paths.length}, amountInWeis=[${params.amountInWeis.join(",")}], useBundle=${useBundle}`
505
+ );
431
506
  const startTs = Date.now();
432
507
  let txHash;
433
508
  try {
@@ -469,14 +544,26 @@ var _Aggregator = class {
469
544
  let estimateGas;
470
545
  const { gasLimit: _ignore, ...estimationOverrides } = txReq;
471
546
  try {
547
+ this.trace(params.chain, traceReq, "log", "hermes-multiSwap-estimateGas-开始", "multiSwap estimateGas 开始");
472
548
  estimateGas = await aggregator.multiSwap.estimateGas(params.user, params.amountInWeis, swapParamsList, params.minAmountOutLists, params.totalMinAmountOut, estimationOverrides);
549
+ this.trace(params.chain, traceReq, "log", "hermes-multiSwap-estimateGas-完成", `multiSwap estimateGas 完成, estimateGas=${estimateGas}`);
473
550
  } catch (error) {
474
- _Aggregator.traceLog(params.chain, "error", `multiSwap estimateGas失败, rpcUrl=${this.getRpcUrl(params.chain)}, error=${error}`);
551
+ this.trace(params.chain, traceReq, "error", "hermes-multiSwap-estimateGas-失败", `multiSwap estimateGas失败, rpcUrl=${this.getRpcUrl(params.chain)}, error=${error}`);
475
552
  throw error;
476
553
  }
477
554
  txReq = this.resolveGasLimit(txReq, estimateGas);
555
+ this.trace(params.chain, traceReq, "log", "hermes-resolveNonce-开始", "multiSwap resolveNonce 开始");
478
556
  txReq = await this.resolveNonce(wallet.provider, wallet.address, txReq);
557
+ this.trace(params.chain, traceReq, "log", "hermes-resolveNonce-完成", `multiSwap resolveNonce 完成, nonce=${txReq.nonce}`);
558
+ this.trace(params.chain, traceReq, "log", "hermes-resolvePricing-开始", "multiSwap resolvePricing 开始");
479
559
  txReq = await this.resolvePricing(wallet.provider, txReq);
560
+ this.trace(
561
+ params.chain,
562
+ traceReq,
563
+ "log",
564
+ "hermes-resolvePricing-完成",
565
+ `multiSwap resolvePricing 完成, gasPrice=${txReq.gasPrice ?? "n/a"}, maxFeePerGas=${txReq.maxFeePerGas ?? "n/a"}, maxPriorityFeePerGas=${txReq.maxPriorityFeePerGas ?? "n/a"}`
566
+ );
480
567
  const iface = new import_ethers.ethers.Interface(import_aggregator.default);
481
568
  let receipt;
482
569
  if (useBundle) {
@@ -485,7 +572,7 @@ var _Aggregator = class {
485
572
  receipt: flashbotsReceipt,
486
573
  txHash: bundleTxHash,
487
574
  targetBlocks
488
- } = await this.submitBundleTx(params.chain, wallet, txReq, aggregatorAddress, calldata, txReq.value ?? 0n, bundleBlockCount);
575
+ } = await this.submitBundleTx(params.chain, wallet, txReq, aggregatorAddress, calldata, txReq.value ?? 0n, bundleBlockCount, traceReq);
489
576
  txHash = bundleTxHash;
490
577
  if (!flashbotsReceipt) {
491
578
  throw new import_types.BundleNotIncludedError(`Bundle 未在目标区块上链, txHash=${bundleTxHash}, targetBlocks=[${targetBlocks.join(",")}]`, bundleTxHash);
@@ -495,7 +582,14 @@ var _Aggregator = class {
495
582
  }
496
583
  receipt = flashbotsReceipt;
497
584
  } else {
498
- receipt = await this.sendContractTx(params.chain, aggregator, "multiSwap", [params.user, params.amountInWeis, swapParamsList, params.minAmountOutLists, params.totalMinAmountOut], txReq);
585
+ receipt = await this.sendContractTx(
586
+ params.chain,
587
+ aggregator,
588
+ "multiSwap",
589
+ [params.user, params.amountInWeis, swapParamsList, params.minAmountOutLists, params.totalMinAmountOut],
590
+ txReq,
591
+ traceReq
592
+ );
499
593
  txHash = receipt.hash;
500
594
  }
501
595
  let amountOutList = null;
@@ -513,7 +607,13 @@ var _Aggregator = class {
513
607
  }
514
608
  if (!amountOutList)
515
609
  throw new Error(`MultiSwapped event not found: ${receipt.hash}`);
516
- _Aggregator.traceLog(params.chain, "log", `multiSwap完成, txHash=${receipt.hash}, amountOutList=[${amountOutList.join(",")}], useBundle=${useBundle}, 总耗时=${Date.now() - startTs}ms`);
610
+ this.trace(
611
+ params.chain,
612
+ traceReq,
613
+ "log",
614
+ "hermes-multiSwap-完成",
615
+ `multiSwap完成, txHash=${receipt.hash}, amountOutList=[${amountOutList.join(",")}], useBundle=${useBundle}, 总耗时=${Date.now() - startTs}ms`
616
+ );
517
617
  return {
518
618
  hash: receipt.hash,
519
619
  from: receipt.from,
@@ -531,7 +631,13 @@ var _Aggregator = class {
531
631
  receipt
532
632
  };
533
633
  } catch (error) {
534
- _Aggregator.traceLog(params.chain, "error", `multiSwap异常, rpcUrl=${this.getRpcUrl(params.chain)}, txHash=${txHash ?? "N/A"}, 总耗时=${Date.now() - startTs}ms, error=${error}`);
634
+ this.trace(
635
+ params.chain,
636
+ traceReq,
637
+ "error",
638
+ "hermes-multiSwap-异常",
639
+ `multiSwap异常, rpcUrl=${this.getRpcUrl(params.chain)}, txHash=${txHash ?? "N/A"}, 总耗时=${Date.now() - startTs}ms, error=${error}`
640
+ );
535
641
  throw error;
536
642
  }
537
643
  }
@@ -899,9 +1005,12 @@ var _Aggregator = class {
899
1005
  const block = await provider.getBlock(blockNumber, true);
900
1006
  if (!block)
901
1007
  return false;
902
- if (block.transactions.some((hash) => hash === txHash))
903
- return true;
904
- return block.prefetchedTransactions.some((tx) => tx.hash === txHash);
1008
+ const normalizedHash = txHash.toLowerCase();
1009
+ const transactions = block.prefetchedTransactions ?? block.transactions;
1010
+ return transactions.some((tx) => {
1011
+ const hash = typeof tx === "string" ? tx : tx.hash;
1012
+ return (hash == null ? void 0 : hash.toLowerCase()) === normalizedHash;
1013
+ });
905
1014
  } catch {
906
1015
  return false;
907
1016
  }
@@ -947,10 +1056,23 @@ var _Aggregator = class {
947
1056
  cleanup();
948
1057
  resolve(receipt);
949
1058
  };
1059
+ const finishWithFallbackCheck = async () => {
1060
+ if (settled)
1061
+ return;
1062
+ try {
1063
+ const receipt = await provider.getTransactionReceipt(txHash);
1064
+ if (receipt && targetBlockSet.has(receipt.blockNumber)) {
1065
+ finish(receipt);
1066
+ return;
1067
+ }
1068
+ } catch {
1069
+ }
1070
+ finish(null);
1071
+ };
950
1072
  const scheduleInspect = (fromBlock, toBlock) => {
951
1073
  if (fromBlock > toBlock) {
952
1074
  if (checkedBlocks.size === targetBlockSet.size && lastObservedBlock >= maxTargetBlock) {
953
- finish(null);
1075
+ finishWithFallbackCheck();
954
1076
  }
955
1077
  return;
956
1078
  }
@@ -980,7 +1102,7 @@ var _Aggregator = class {
980
1102
  return;
981
1103
  }
982
1104
  if (checkedBlocks.size === targetBlockSet.size && lastObservedBlock >= maxTargetBlock) {
983
- finish(null);
1105
+ finishWithFallbackCheck();
984
1106
  }
985
1107
  };
986
1108
  const onBlock = (blockNumber) => {
@@ -993,7 +1115,7 @@ var _Aggregator = class {
993
1115
  }
994
1116
  if (fromBlock > toBlock) {
995
1117
  if (checkedBlocks.size === targetBlockSet.size && lastObservedBlock >= maxTargetBlock) {
996
- finish(null);
1118
+ finishWithFallbackCheck();
997
1119
  }
998
1120
  return;
999
1121
  }
@@ -1011,7 +1133,8 @@ var _Aggregator = class {
1011
1133
  const toBlock = Math.min(latestBlock, maxTargetBlock);
1012
1134
  scheduleInspect(sortedTargetBlocks[0], toBlock);
1013
1135
  await inspectionChain;
1014
- finish(null);
1136
+ if (!settled)
1137
+ await finishWithFallbackCheck();
1015
1138
  }, _Aggregator.BUNDLE_INCLUSION_TIMEOUT_MS);
1016
1139
  provider.on("block", onBlock);
1017
1140
  provider.getBlockNumber().then(
@@ -1030,22 +1153,22 @@ var _Aggregator = class {
1030
1153
  );
1031
1154
  });
1032
1155
  }
1033
- async sendSignedTxToChannels(chain, signedTx, txHash, channels) {
1156
+ async sendSignedTxToChannels(chain, signedTx, txHash, channels, traceReq) {
1034
1157
  const results = await Promise.all(
1035
1158
  channels.map(async ({ label, send }) => {
1036
1159
  var _a, _b, _c, _d, _e;
1037
1160
  const sendTs = Date.now();
1038
1161
  try {
1039
1162
  await send();
1040
- _Aggregator.traceLog(chain, "log", `广播${label} OK, txHash=${txHash}, 耗时=${Date.now() - sendTs}ms`);
1163
+ this.trace(chain, traceReq, "log", "hermes-broadcast-channel-成功", `广播${label} OK, txHash=${txHash}, 耗时=${Date.now() - sendTs}ms`);
1041
1164
  return { label, ok: true };
1042
1165
  } catch (err) {
1043
1166
  const msg = ((_c = (_b = (_a = err == null ? void 0 : err.response) == null ? void 0 : _a.data) == null ? void 0 : _b.error) == null ? void 0 : _c.message) ?? ((_e = (_d = err == null ? void 0 : err.response) == null ? void 0 : _d.data) == null ? void 0 : _e.message) ?? (err == null ? void 0 : err.message) ?? String(err);
1044
1167
  if (_Aggregator.isBenignBroadcastError(msg)) {
1045
- _Aggregator.traceLog(chain, "log", `广播${label} already-known, txHash=${txHash}, 耗时=${Date.now() - sendTs}ms`);
1168
+ this.trace(chain, traceReq, "log", "hermes-broadcast-channel-已存在", `广播${label} already-known, txHash=${txHash}, 耗时=${Date.now() - sendTs}ms`);
1046
1169
  return { label, ok: true };
1047
1170
  }
1048
- _Aggregator.traceLog(chain, "warn", `广播${label} 失败: ${msg}, txHash=${txHash}, 耗时=${Date.now() - sendTs}ms`);
1171
+ this.trace(chain, traceReq, "warn", "hermes-broadcast-channel-失败", `广播${label} 失败: ${msg}, txHash=${txHash}, 耗时=${Date.now() - sendTs}ms`);
1049
1172
  return { label, ok: false, msg };
1050
1173
  }
1051
1174
  })
@@ -1056,7 +1179,7 @@ var _Aggregator = class {
1056
1179
  const detail = results.map((result) => `${result.label}: ${result.msg ?? "unknown error"}`).join("; ");
1057
1180
  throw new Error(`All broadcast channels failed for ${txHash}: ${detail}`);
1058
1181
  }
1059
- raceForReceipt(chain, txHash, providers, ownedProviders = [], timeoutMs = _Aggregator.TX_CONFIRM_TIMEOUT_MS) {
1182
+ raceForReceipt(chain, txHash, providers, ownedProviders = [], timeoutMs = _Aggregator.TX_CONFIRM_TIMEOUT_MS, traceReq) {
1060
1183
  return new Promise((resolve, reject) => {
1061
1184
  let settled = false;
1062
1185
  const blockHandlers = [];
@@ -1077,7 +1200,7 @@ var _Aggregator = class {
1077
1200
  settled = true;
1078
1201
  clearTimeout(timer);
1079
1202
  cleanup();
1080
- _Aggregator.traceLog(chain, "log", `${label} 先查到receipt, txHash=${txHash}, block=${receipt.blockNumber}`);
1203
+ this.trace(chain, traceReq, "log", "hermes-raceForReceipt-命中receipt", `${label} 先查到receipt, txHash=${txHash}, block=${receipt.blockNumber}`);
1081
1204
  resolve(receipt);
1082
1205
  };
1083
1206
  const checkReceipt = (provider, label) => {
@@ -1103,6 +1226,7 @@ var _Aggregator = class {
1103
1226
  return;
1104
1227
  settled = true;
1105
1228
  cleanup();
1229
+ this.trace(chain, traceReq, "warn", "hermes-raceForReceipt-超时", `Transaction not confirmed within ${timeoutMs}ms: ${txHash}`);
1106
1230
  reject(new Error(`Transaction not confirmed within ${timeoutMs}ms: ${txHash}`));
1107
1231
  }, timeoutMs);
1108
1232
  });
@@ -1123,26 +1247,35 @@ var _Aggregator = class {
1123
1247
  }
1124
1248
  };
1125
1249
  }
1126
- async sendContractTx(chain, contract, method, args, txReq) {
1250
+ async sendContractTx(chain, contract, method, args, txReq, traceReq) {
1127
1251
  const extraUrls = this.broadcastRpcsMap.get(chain);
1128
1252
  const hasSequencer = _Aggregator.SEQUENCER_CHAINS.has(chain) && this.sequencerRpcsMap.has(chain);
1129
1253
  if (!extraUrls || extraUrls.length === 0) {
1130
1254
  if (!hasSequencer) {
1131
- _Aggregator.traceLog(chain, "log", `渠道=单RPC直发, method=${method}, nonce=${txReq.nonce}, gasLimit=${txReq.gasLimit}`);
1255
+ this.trace(chain, traceReq, "log", "hermes-sendContractTx-发出前", `渠道=单RPC直发, method=${method}, nonce=${txReq.nonce}, gasLimit=${txReq.gasLimit}`);
1132
1256
  const sendTs = Date.now();
1257
+ this.trace(chain, traceReq, "log", "hermes-sendContractTx-RPC发送前", `单RPC准备发送, method=${method}`);
1133
1258
  const txResponse = await contract[method](...args, txReq);
1134
- _Aggregator.traceLog(chain, "log", `单RPC已发送, txHash=${txResponse.hash}, 耗时=${Date.now() - sendTs}ms`);
1259
+ this.trace(chain, traceReq, "log", "hermes-sendContractTx-RPC已发送", `单RPC已发送, txHash=${txResponse.hash}, 耗时=${Date.now() - sendTs}ms`);
1260
+ this.trace(chain, traceReq, "log", "hermes-sendContractTx-等待确认", `单RPC开始等待上链确认, txHash=${txResponse.hash}`);
1135
1261
  const receipt3 = await txResponse.wait();
1136
1262
  const singleProvider = this.providerClient.getProvider(chain);
1137
1263
  const blockTs3 = await this.getBlockTimestampStr(singleProvider, receipt3.blockNumber);
1138
- _Aggregator.traceLog(chain, "log", `单RPC上链确认, txHash=${receipt3.hash}, blockNumber=${receipt3.blockNumber}, 出块时间=${blockTs3}, gasUsed=${receipt3.gasUsed}, status=${receipt3.status}`);
1264
+ this.trace(
1265
+ chain,
1266
+ traceReq,
1267
+ "log",
1268
+ "hermes-sendContractTx-确认完成",
1269
+ `单RPC上链确认, txHash=${receipt3.hash}, blockNumber=${receipt3.blockNumber}, 出块时间=${blockTs3}, gasUsed=${receipt3.gasUsed}, status=${receipt3.status}`
1270
+ );
1139
1271
  if (receipt3.status === 0) {
1140
1272
  throw new import_types.TransactionRevertedError(receipt3.hash, receipt3.gasUsed);
1141
1273
  }
1142
1274
  return receipt3;
1143
1275
  }
1144
- _Aggregator.traceLog(chain, "log", `渠道=单RPC+排序器直发, method=${method}, nonce=${txReq.nonce}, gasLimit=${txReq.gasLimit}`);
1276
+ this.trace(chain, traceReq, "log", "hermes-sendContractTx-发出前", `渠道=单RPC+排序器直发, method=${method}, nonce=${txReq.nonce}, gasLimit=${txReq.gasLimit}`);
1145
1277
  const wallet2 = this.providerClient.getWallet(chain);
1278
+ this.trace(chain, traceReq, "log", "hermes-sendContractTx-构建交易", `开始 populateTransaction, method=${method}`);
1146
1279
  const populated2 = await contract[method].populateTransaction(...args, txReq);
1147
1280
  delete populated2.from;
1148
1281
  const network2 = await wallet2.provider.getNetwork();
@@ -1150,18 +1283,21 @@ var _Aggregator = class {
1150
1283
  const signedTx2 = await wallet2.signTransaction(populated2);
1151
1284
  const txHash2 = import_ethers.ethers.keccak256(signedTx2);
1152
1285
  const mainProvider2 = wallet2.provider;
1153
- _Aggregator.traceLog(chain, "log", `单RPC+排序器并行广播, txHash=${txHash2}`);
1286
+ this.trace(chain, traceReq, "log", "hermes-sendContractTx-RPC发送前", `单RPC+排序器并行广播, txHash=${txHash2}`);
1154
1287
  const broadcastTs2 = Date.now();
1155
1288
  const providers = [{ provider: mainProvider2, label: "RPC[0/主]" }];
1156
1289
  const sequencerChannel2 = this.buildSequencerChannel(chain, signedTx2);
1157
1290
  const channels2 = [...providers.map(({ provider, label }) => ({ label, send: () => provider.broadcastTransaction(signedTx2) })), ...sequencerChannel2 ? [sequencerChannel2] : []];
1158
- const receiptPromise2 = this.raceForReceipt(chain, txHash2, providers);
1159
- await this.sendSignedTxToChannels(chain, signedTx2, txHash2, channels2);
1291
+ const receiptPromise2 = this.raceForReceipt(chain, txHash2, providers, [], _Aggregator.TX_CONFIRM_TIMEOUT_MS, traceReq);
1292
+ await this.sendSignedTxToChannels(chain, signedTx2, txHash2, channels2, traceReq);
1293
+ this.trace(chain, traceReq, "log", "hermes-sendContractTx-等待确认", `并行广播后开始等待确认, txHash=${txHash2}`);
1160
1294
  const receipt2 = await receiptPromise2;
1161
1295
  const blockTs2 = await this.getBlockTimestampStr(mainProvider2, receipt2.blockNumber);
1162
- _Aggregator.traceLog(
1296
+ this.trace(
1163
1297
  chain,
1298
+ traceReq,
1164
1299
  "log",
1300
+ "hermes-sendContractTx-确认完成",
1165
1301
  `单RPC+排序器上链确认, txHash=${receipt2.hash}, blockNumber=${receipt2.blockNumber}, 出块时间=${blockTs2}, gasUsed=${receipt2.gasUsed}, status=${receipt2.status}, 总耗时=${Date.now() - broadcastTs2}ms`
1166
1302
  );
1167
1303
  if (receipt2.status === 0) {
@@ -1184,21 +1320,27 @@ var _Aggregator = class {
1184
1320
  }));
1185
1321
  const allProviders = [{ provider: mainProvider, label: "RPC[0/主]" }, ...extraProviders];
1186
1322
  const ownedProviders = extraProviders.map((e) => e.provider);
1187
- _Aggregator.traceLog(
1323
+ this.trace(
1188
1324
  chain,
1325
+ traceReq,
1189
1326
  "log",
1327
+ "hermes-sendContractTx-发出前",
1190
1328
  `渠道=多RPC广播, method=${method}, nonce=${txReq.nonce}, gasLimit=${txReq.gasLimit}, 当前区块=${currentBlock}, 期望上链区块=${currentBlock + 1}, txHash=${txHash}, RPC数量=${allProviders.length}(主1+副${extraUrls.length})${hasSequencer ? ", +排序器直发" : ""}`
1191
1329
  );
1192
1330
  const broadcastTs = Date.now();
1193
1331
  const sequencerChannel = this.buildSequencerChannel(chain, signedTx);
1194
1332
  const channels = [...allProviders.map(({ provider, label }) => ({ label, send: () => provider.broadcastTransaction(signedTx) })), ...sequencerChannel ? [sequencerChannel] : []];
1195
- const receiptPromise = this.raceForReceipt(chain, txHash, allProviders, ownedProviders);
1196
- await this.sendSignedTxToChannels(chain, signedTx, txHash, channels);
1333
+ const receiptPromise = this.raceForReceipt(chain, txHash, allProviders, ownedProviders, _Aggregator.TX_CONFIRM_TIMEOUT_MS, traceReq);
1334
+ this.trace(chain, traceReq, "log", "hermes-sendContractTx-RPC发送前", `多RPC广播准备发送, txHash=${txHash}`);
1335
+ await this.sendSignedTxToChannels(chain, signedTx, txHash, channels, traceReq);
1336
+ this.trace(chain, traceReq, "log", "hermes-sendContractTx-等待确认", `多RPC广播后开始等待确认, txHash=${txHash}`);
1197
1337
  const receipt = await receiptPromise;
1198
1338
  const blockTs = await this.getBlockTimestampStr(mainProvider, receipt.blockNumber);
1199
- _Aggregator.traceLog(
1339
+ this.trace(
1200
1340
  chain,
1341
+ traceReq,
1201
1342
  "log",
1343
+ "hermes-sendContractTx-确认完成",
1202
1344
  `多RPC广播上链确认, txHash=${receipt.hash}, blockNumber=${receipt.blockNumber}, 出块时间=${blockTs}, gasUsed=${receipt.gasUsed}, status=${receipt.status}, 总耗时=${Date.now() - broadcastTs}ms`
1203
1345
  );
1204
1346
  if (receipt.status === 0) {
@@ -1206,9 +1348,9 @@ var _Aggregator = class {
1206
1348
  }
1207
1349
  return receipt;
1208
1350
  }
1209
- async submitBundleTx(chain, wallet, txReq, to, data, value, bundleBlockCount) {
1351
+ async submitBundleTx(chain, wallet, txReq, to, data, value, bundleBlockCount, traceReq) {
1210
1352
  if (bundleBlockCount == null) {
1211
- bundleBlockCount = this.builderConfigMap.has(chain) ? 3 : 1;
1353
+ bundleBlockCount = this.getDefaultBundleBlockCount(chain);
1212
1354
  }
1213
1355
  const builderCfg = this.getBuilderConfig(chain);
1214
1356
  if (builderCfg.authType === "flashbots" && !this.flashbotSigner) {
@@ -1240,9 +1382,11 @@ var _Aggregator = class {
1240
1382
  const targetBlocks = Array.from({ length: normalizedBundleBlockCount }, (_, i) => currentBlock + 1 + i);
1241
1383
  const builderNames = Object.keys(builderCfg.urls);
1242
1384
  const builderEntries = Object.entries(builderCfg.urls);
1243
- _Aggregator.traceLog(
1385
+ this.trace(
1244
1386
  chain,
1387
+ traceReq,
1245
1388
  "log",
1389
+ "hermes-submitBundleTx-发出前",
1246
1390
  `渠道=Builder Bundle, authType=${builderCfg.authType}, method=${builderCfg.method ?? "eth_sendBundle"}, nonce=${txReq.nonce}, gasLimit=${txReq.gasLimit}, chainId=${network.chainId}, 当前区块=${currentBlock}, 目标区块=[${targetBlocks.join(",")}], builders=[${builderNames.join(",")}](${builderNames.length}个), txHash=${txHash}`
1247
1391
  );
1248
1392
  const sendBundleTs = Date.now();
@@ -1261,12 +1405,36 @@ var _Aggregator = class {
1261
1405
  const ts = Date.now();
1262
1406
  try {
1263
1407
  const response = await import_axios.default.post(url, body, { headers, timeout: 3e3 });
1408
+ const rpcErrorMessage = _Aggregator.getJsonRpcErrorMessage(response.data);
1409
+ if (rpcErrorMessage) {
1410
+ totalFailed++;
1411
+ this.trace(
1412
+ chain,
1413
+ traceReq,
1414
+ "error",
1415
+ "hermes-submitBundleTx-builder-失败",
1416
+ `Builder[${name}] mev_sendBundle 发送失败, targetBlocks=[${targetBlocks.join(",")}], 耗时=${Date.now() - ts}ms, error=${rpcErrorMessage}, resp=${JSON.stringify(response.data)}`
1417
+ );
1418
+ return;
1419
+ }
1264
1420
  totalSent++;
1265
- _Aggregator.traceLog(chain, "log", `Builder[${name}] mev_sendBundle 发送成功, targetBlocks=[${targetBlocks.join(",")}], 耗时=${Date.now() - ts}ms, resp=${JSON.stringify(response.data)}`);
1421
+ this.trace(
1422
+ chain,
1423
+ traceReq,
1424
+ "log",
1425
+ "hermes-submitBundleTx-builder-成功",
1426
+ `Builder[${name}] mev_sendBundle 发送成功, targetBlocks=[${targetBlocks.join(",")}], 耗时=${Date.now() - ts}ms, resp=${JSON.stringify(response.data)}`
1427
+ );
1266
1428
  } catch (error) {
1267
1429
  totalFailed++;
1268
1430
  const msg = (error == null ? void 0 : error.response) ? JSON.stringify(error.response.data) : (error == null ? void 0 : error.message) ?? String(error);
1269
- _Aggregator.traceLog(chain, "error", `Builder[${name}] mev_sendBundle 发送失败, targetBlocks=[${targetBlocks.join(",")}], 耗时=${Date.now() - ts}ms, error=${msg}`);
1431
+ this.trace(
1432
+ chain,
1433
+ traceReq,
1434
+ "error",
1435
+ "hermes-submitBundleTx-builder-失败",
1436
+ `Builder[${name}] mev_sendBundle 发送失败, targetBlocks=[${targetBlocks.join(",")}], 耗时=${Date.now() - ts}ms, error=${msg}`
1437
+ );
1270
1438
  }
1271
1439
  })()
1272
1440
  ];
@@ -1280,35 +1448,56 @@ var _Aggregator = class {
1280
1448
  const ts = Date.now();
1281
1449
  try {
1282
1450
  const response = await import_axios.default.post(url, body, { headers, timeout: 3e3 });
1451
+ const rpcErrorMessage = _Aggregator.getJsonRpcErrorMessage(response.data);
1452
+ if (rpcErrorMessage) {
1453
+ totalFailed++;
1454
+ this.trace(
1455
+ chain,
1456
+ traceReq,
1457
+ "error",
1458
+ "hermes-submitBundleTx-builder-失败",
1459
+ `Builder[${name}] 发送失败, targetBlock=${block}, 耗时=${Date.now() - ts}ms, error=${rpcErrorMessage}, resp=${JSON.stringify(response.data)}`
1460
+ );
1461
+ return;
1462
+ }
1283
1463
  totalSent++;
1284
- _Aggregator.traceLog(chain, "log", `Builder[${name}] 发送成功, targetBlock=${block}, 耗时=${Date.now() - ts}ms, resp=${JSON.stringify(response.data)}`);
1464
+ this.trace(chain, traceReq, "log", "hermes-submitBundleTx-builder-成功", `Builder[${name}] 发送成功, targetBlock=${block}, 耗时=${Date.now() - ts}ms, resp=${JSON.stringify(response.data)}`);
1285
1465
  } catch (error) {
1286
1466
  totalFailed++;
1287
1467
  const msg = (error == null ? void 0 : error.response) ? JSON.stringify(error.response.data) : (error == null ? void 0 : error.message) ?? String(error);
1288
- _Aggregator.traceLog(chain, "error", `Builder[${name}] 发送失败, targetBlock=${block}, 耗时=${Date.now() - ts}ms, error=${msg}`);
1468
+ this.trace(chain, traceReq, "error", "hermes-submitBundleTx-builder-失败", `Builder[${name}] 发送失败, targetBlock=${block}, 耗时=${Date.now() - ts}ms, error=${msg}`);
1289
1469
  }
1290
1470
  });
1291
1471
  });
1292
1472
  const abortController = new AbortController();
1293
1473
  const inclusionPromise = this.waitForBundleInclusion(provider, txHash, targetBlocks, abortController.signal);
1294
1474
  const waitTs = Date.now();
1475
+ this.trace(chain, traceReq, "log", "hermes-submitBundleTx-RPC发送前", `Bundle 请求开始发送, txHash=${txHash}, targetBlocks=[${targetBlocks.join(",")}]`);
1295
1476
  const [receipt] = await Promise.all([
1296
1477
  inclusionPromise,
1297
1478
  Promise.all(fireAndForget).then(() => {
1298
- _Aggregator.traceLog(chain, "log", `Bundle发送完成, 成功=${totalSent}, 失败=${totalFailed}, 总请求=${fireAndForget.length}, 耗时=${Date.now() - sendBundleTs}ms, txHash=${txHash}`);
1479
+ this.trace(
1480
+ chain,
1481
+ traceReq,
1482
+ "log",
1483
+ "hermes-submitBundleTx-RPC已发送",
1484
+ `Bundle发送完成, 成功=${totalSent}, 失败=${totalFailed}, 总请求=${fireAndForget.length}, 耗时=${Date.now() - sendBundleTs}ms, txHash=${txHash}`
1485
+ );
1299
1486
  if (totalSent === 0)
1300
1487
  abortController.abort();
1301
1488
  })
1302
1489
  ]);
1303
1490
  if (receipt) {
1304
1491
  const blockTs = await this.getBlockTimestampStr(provider, receipt.blockNumber);
1305
- _Aggregator.traceLog(
1492
+ this.trace(
1306
1493
  chain,
1494
+ traceReq,
1307
1495
  "log",
1496
+ "hermes-submitBundleTx-确认完成",
1308
1497
  `Bundle上链确认, txHash=${receipt.hash}, blockNumber=${receipt.blockNumber}, 出块时间=${blockTs}, gasUsed=${receipt.gasUsed}, 等待耗时=${Date.now() - waitTs}ms`
1309
1498
  );
1310
1499
  } else {
1311
- _Aggregator.traceLog(chain, "warn", `Bundle未在目标区块上链, txHash=${txHash}, 目标区块=[${targetBlocks.join(",")}], 等待耗时=${Date.now() - waitTs}ms`);
1500
+ this.trace(chain, traceReq, "warn", "hermes-submitBundleTx-等待结束", `Bundle未在目标区块上链, txHash=${txHash}, 目标区块=[${targetBlocks.join(",")}], 等待耗时=${Date.now() - waitTs}ms`);
1312
1501
  }
1313
1502
  return { receipt, txHash, targetBlocks };
1314
1503
  }
@@ -1409,8 +1598,8 @@ var _Aggregator = class {
1409
1598
  return chain === import_types.ChainNameEnum.ETH && !!this.flashbotSigner;
1410
1599
  }
1411
1600
  stripCustomFields(txReq, chain) {
1412
- const defaultBlockCount = chain && this.builderConfigMap.has(chain) ? 3 : 1;
1413
- const { useBundle, bundleBlockCount = defaultBlockCount, ...pureTxReq } = txReq;
1601
+ const defaultBlockCount = this.getDefaultBundleBlockCount(chain);
1602
+ const { useBundle, bundleBlockCount = defaultBlockCount, trace, ...pureTxReq } = txReq;
1414
1603
  return { pureTxReq, bundleBlockCount: this.normalizeBundleBlockCount(bundleBlockCount) };
1415
1604
  }
1416
1605
  normalizeBundleBlockCount(bundleBlockCount) {
@@ -1444,11 +1633,23 @@ var Aggregator = _Aggregator;
1444
1633
  Aggregator.DEFAULT_ETH_BUILDERS = {
1445
1634
  "beaverbuild.org": "https://rpc.beaverbuild.org",
1446
1635
  Titan: "https://rpc.titanbuilder.xyz",
1636
+ "Titan-EU": "https://eu.rpc.titanbuilder.xyz",
1447
1637
  flashbots: "https://relay.flashbots.net",
1448
1638
  // bloXroute: 'https://mev.api.blxrbdn.com', // 旧 api.blxrbdn.com 也行,需配置 Authorization 头
1449
- lightspeedbuilder: "https://rpc.lightspeedbuilder.info"
1639
+ "Builder+": "https://rpc.btcs.com",
1640
+ // BTCS 官方文档支持 eth_sendBundle
1641
+ turbobuilder: "https://rpc.turbobuilder.xyz",
1642
+ // 官方文档支持 eth_sendBundle
1643
+ lightspeedbuilder: "https://rpc.lightspeedbuilder.info",
1450
1644
  // 新增,实测直接可用
1451
- // buildernet: 'https://rpc.buildernet.org', // 新增,Flashbots 生态,需签名头(同 flashbots 格式)
1645
+ buildernet: "https://rpc.buildernet.org",
1646
+ // Flashbots 生态,使用 eth_sendBundle + X-Flashbots-Signature
1647
+ quasar: "https://rpc.quasar.win",
1648
+ // Quasar 文档支持 eth_sendBundle,认证沿用 X-Flashbots-Signature
1649
+ bananabuild: "https://rpc.bananabuild.org",
1650
+ // 官方文档支持 eth_sendBundle
1651
+ snailbuilder: "https://rpc.snailbuilder.sh"
1652
+ // 官网声明支持 Flashbots 同结构 bundle API
1452
1653
  // nfactorial: 'https://rpc.nfactorial.xyz', // 新增,支持 bundle rebate
1453
1654
  };
1454
1655
  Aggregator.DEFAULT_SEQUENCER_RPC_BY_CHAIN = {
@@ -16,6 +16,8 @@ declare class Aggregator {
16
16
  private static traceLog;
17
17
  private static formatBlockTimestamp;
18
18
  private getBlockTimestampStr;
19
+ private emitTrace;
20
+ private trace;
19
21
  private static readonly DEFAULT_ETH_BUILDERS;
20
22
  private static readonly DEFAULT_SEQUENCER_RPC_BY_CHAIN;
21
23
  private static readonly SEQUENCER_CHAINS;
@@ -24,9 +26,11 @@ declare class Aggregator {
24
26
  getAggregatorAddress(chain: ChainNameEnum): string;
25
27
  signRequestBody(body: unknown, signer: ethers.Wallet): Promise<string>;
26
28
  private getBuilderConfig;
29
+ private getDefaultBundleBlockCount;
27
30
  private getSequencerRpcs;
28
31
  private buildBundleRequestBody;
29
32
  private buildFlashbotsMevBundleRequestBody;
33
+ private static getJsonRpcErrorMessage;
30
34
  sendBundle(chain: ChainNameEnum, rawTxs: string[], targetBlock: number, signer?: ethers.Wallet): Promise<{
31
35
  sent: number;
32
36
  failed: number;