hermes-swap 0.6.8 → 0.6.10

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}`);
@@ -144,6 +158,23 @@ var _Aggregator = class {
144
158
  params: [{ txs: rawTxs, blockNumber: `0x${targetBlock.toString(16)}` }]
145
159
  };
146
160
  }
161
+ buildFlashbotsMevBundleRequestBody(rawTxs, startBlock, endBlock) {
162
+ return {
163
+ jsonrpc: "2.0",
164
+ id: 1,
165
+ method: "mev_sendBundle",
166
+ params: [
167
+ {
168
+ version: "v0.1",
169
+ inclusion: {
170
+ block: `0x${startBlock.toString(16)}`,
171
+ maxBlock: `0x${endBlock.toString(16)}`
172
+ },
173
+ body: rawTxs.map((tx) => ({ tx, canRevert: false }))
174
+ }
175
+ ]
176
+ };
177
+ }
147
178
  async sendBundle(chain, rawTxs, targetBlock, signer) {
148
179
  const builderCfg = this.getBuilderConfig(chain);
149
180
  const body = this.buildBundleRequestBody(builderCfg, rawTxs, targetBlock);
@@ -294,10 +325,13 @@ var _Aggregator = class {
294
325
  }
295
326
  async swapAndBridge(params, txReq = {}) {
296
327
  var _a, _b;
328
+ const traceReq = txReq;
297
329
  const useBundle = this.shouldUseBundle(params.chain, txReq);
298
- _Aggregator.traceLog(
330
+ this.trace(
299
331
  params.chain,
332
+ traceReq,
300
333
  "log",
334
+ "hermes-swapAndBridge-开始",
301
335
  `swapAndBridge开始, user=${params.user}, amountInWeis=[${params.amountInWeis.join(",")}], bridgeType=${params.bridgeType}, destChain=${params.destChain}, useBundle=${useBundle}`
302
336
  );
303
337
  const startTs = Date.now();
@@ -340,14 +374,26 @@ var _Aggregator = class {
340
374
  const { gasLimit: _ignore, ...estimationOverrides } = txReq;
341
375
  const args = [params.user, params.amountInWeis, swapParamsList, params.minAmountOutLists, bridgeArgs, params.totalMinAmountOut];
342
376
  try {
377
+ this.trace(params.chain, traceReq, "log", "hermes-swapAndBridge-estimateGas-开始", "swapAndBridge estimateGas 开始");
343
378
  estimateGas = await aggregator.multiSwapAndBridge.estimateGas(...args, estimationOverrides);
379
+ this.trace(params.chain, traceReq, "log", "hermes-swapAndBridge-estimateGas-完成", `swapAndBridge estimateGas 完成, estimateGas=${estimateGas}`);
344
380
  } catch (error) {
345
- _Aggregator.traceLog(params.chain, "error", `swapAndBridge estimateGas失败, rpcUrl=${this.getRpcUrl(params.chain)}, error=${error}`);
381
+ this.trace(params.chain, traceReq, "error", "hermes-swapAndBridge-estimateGas-失败", `swapAndBridge estimateGas失败, rpcUrl=${this.getRpcUrl(params.chain)}, error=${error}`);
346
382
  throw error;
347
383
  }
348
384
  txReq = this.resolveGasLimit(txReq, estimateGas);
385
+ this.trace(params.chain, traceReq, "log", "hermes-resolveNonce-开始", "swapAndBridge resolveNonce 开始");
349
386
  txReq = await this.resolveNonce(wallet.provider, wallet.address, txReq);
387
+ this.trace(params.chain, traceReq, "log", "hermes-resolveNonce-完成", `swapAndBridge resolveNonce 完成, nonce=${txReq.nonce}`);
388
+ this.trace(params.chain, traceReq, "log", "hermes-resolvePricing-开始", "swapAndBridge resolvePricing 开始");
350
389
  txReq = await this.resolvePricing(wallet.provider, txReq);
390
+ this.trace(
391
+ params.chain,
392
+ traceReq,
393
+ "log",
394
+ "hermes-resolvePricing-完成",
395
+ `swapAndBridge resolvePricing 完成, gasPrice=${txReq.gasPrice ?? "n/a"}, maxFeePerGas=${txReq.maxFeePerGas ?? "n/a"}, maxPriorityFeePerGas=${txReq.maxPriorityFeePerGas ?? "n/a"}`
396
+ );
351
397
  const iface = new import_ethers.ethers.Interface(import_aggregator.default);
352
398
  let receipt;
353
399
  if (useBundle) {
@@ -356,7 +402,7 @@ var _Aggregator = class {
356
402
  receipt: flashbotsReceipt,
357
403
  txHash: bundleTxHash,
358
404
  targetBlocks
359
- } = await this.submitBundleTx(params.chain, wallet, txReq, aggregatorAddress, calldata, txReq.value ?? 0n, bundleBlockCount);
405
+ } = await this.submitBundleTx(params.chain, wallet, txReq, aggregatorAddress, calldata, txReq.value ?? 0n, bundleBlockCount, traceReq);
360
406
  txHash = bundleTxHash;
361
407
  if (!flashbotsReceipt) {
362
408
  throw new import_types.BundleNotIncludedError(`Bundle 未在目标区块上链, txHash=${bundleTxHash}, targetBlocks=[${targetBlocks.join(",")}]`, bundleTxHash);
@@ -366,7 +412,7 @@ var _Aggregator = class {
366
412
  }
367
413
  receipt = flashbotsReceipt;
368
414
  } else {
369
- receipt = await this.sendContractTx(params.chain, aggregator, "multiSwapAndBridge", args, txReq);
415
+ receipt = await this.sendContractTx(params.chain, aggregator, "multiSwapAndBridge", args, txReq, traceReq);
370
416
  txHash = receipt.hash;
371
417
  }
372
418
  let receiptUser = null;
@@ -394,7 +440,13 @@ var _Aggregator = class {
394
440
  const fromTokenAddress = ((_a = pathStart == null ? void 0 : pathStart[0]) == null ? void 0 : _a.fromCoinAddress) ?? "";
395
441
  const toTokenAddress = ((_b = pathEnd == null ? void 0 : pathEnd[pathEnd.length - 1]) == null ? void 0 : _b.toCoinAddress) ?? "";
396
442
  const totalAmountInFromEvent = amountInList.reduce((sum, amount) => sum + amount, 0n);
397
- _Aggregator.traceLog(params.chain, "log", `swapAndBridge完成, txHash=${receipt.hash}, amountOut=${amountOut}, useBundle=${useBundle}, 总耗时=${Date.now() - startTs}ms`);
443
+ this.trace(
444
+ params.chain,
445
+ traceReq,
446
+ "log",
447
+ "hermes-swapAndBridge-完成",
448
+ `swapAndBridge完成, txHash=${receipt.hash}, amountOut=${amountOut}, useBundle=${useBundle}, 总耗时=${Date.now() - startTs}ms`
449
+ );
398
450
  return {
399
451
  hash: receipt.hash,
400
452
  from: receipt.from,
@@ -403,14 +455,27 @@ var _Aggregator = class {
403
455
  receipt
404
456
  };
405
457
  } catch (error) {
406
- _Aggregator.traceLog(params.chain, "error", `swapAndBridge异常, rpcUrl=${this.getRpcUrl(params.chain)}, txHash=${txHash ?? "N/A"}, 总耗时=${Date.now() - startTs}ms, error=${error}`);
458
+ this.trace(
459
+ params.chain,
460
+ traceReq,
461
+ "error",
462
+ "hermes-swapAndBridge-异常",
463
+ `swapAndBridge异常, rpcUrl=${this.getRpcUrl(params.chain)}, txHash=${txHash ?? "N/A"}, 总耗时=${Date.now() - startTs}ms, error=${error}`
464
+ );
407
465
  throw error;
408
466
  }
409
467
  }
410
468
  async multiSwap(params, txReq = {}) {
411
469
  var _a;
470
+ const traceReq = txReq;
412
471
  const useBundle = this.shouldUseBundle(params.chain, txReq);
413
- _Aggregator.traceLog(params.chain, "log", `multiSwap开始, user=${params.user}, pathsCount=${params.paths.length}, amountInWeis=[${params.amountInWeis.join(",")}], useBundle=${useBundle}`);
472
+ this.trace(
473
+ params.chain,
474
+ traceReq,
475
+ "log",
476
+ "hermes-multiSwap-开始",
477
+ `multiSwap开始, user=${params.user}, pathsCount=${params.paths.length}, amountInWeis=[${params.amountInWeis.join(",")}], useBundle=${useBundle}`
478
+ );
414
479
  const startTs = Date.now();
415
480
  let txHash;
416
481
  try {
@@ -452,14 +517,26 @@ var _Aggregator = class {
452
517
  let estimateGas;
453
518
  const { gasLimit: _ignore, ...estimationOverrides } = txReq;
454
519
  try {
520
+ this.trace(params.chain, traceReq, "log", "hermes-multiSwap-estimateGas-开始", "multiSwap estimateGas 开始");
455
521
  estimateGas = await aggregator.multiSwap.estimateGas(params.user, params.amountInWeis, swapParamsList, params.minAmountOutLists, params.totalMinAmountOut, estimationOverrides);
522
+ this.trace(params.chain, traceReq, "log", "hermes-multiSwap-estimateGas-完成", `multiSwap estimateGas 完成, estimateGas=${estimateGas}`);
456
523
  } catch (error) {
457
- _Aggregator.traceLog(params.chain, "error", `multiSwap estimateGas失败, rpcUrl=${this.getRpcUrl(params.chain)}, error=${error}`);
524
+ this.trace(params.chain, traceReq, "error", "hermes-multiSwap-estimateGas-失败", `multiSwap estimateGas失败, rpcUrl=${this.getRpcUrl(params.chain)}, error=${error}`);
458
525
  throw error;
459
526
  }
460
527
  txReq = this.resolveGasLimit(txReq, estimateGas);
528
+ this.trace(params.chain, traceReq, "log", "hermes-resolveNonce-开始", "multiSwap resolveNonce 开始");
461
529
  txReq = await this.resolveNonce(wallet.provider, wallet.address, txReq);
530
+ this.trace(params.chain, traceReq, "log", "hermes-resolveNonce-完成", `multiSwap resolveNonce 完成, nonce=${txReq.nonce}`);
531
+ this.trace(params.chain, traceReq, "log", "hermes-resolvePricing-开始", "multiSwap resolvePricing 开始");
462
532
  txReq = await this.resolvePricing(wallet.provider, txReq);
533
+ this.trace(
534
+ params.chain,
535
+ traceReq,
536
+ "log",
537
+ "hermes-resolvePricing-完成",
538
+ `multiSwap resolvePricing 完成, gasPrice=${txReq.gasPrice ?? "n/a"}, maxFeePerGas=${txReq.maxFeePerGas ?? "n/a"}, maxPriorityFeePerGas=${txReq.maxPriorityFeePerGas ?? "n/a"}`
539
+ );
463
540
  const iface = new import_ethers.ethers.Interface(import_aggregator.default);
464
541
  let receipt;
465
542
  if (useBundle) {
@@ -468,7 +545,7 @@ var _Aggregator = class {
468
545
  receipt: flashbotsReceipt,
469
546
  txHash: bundleTxHash,
470
547
  targetBlocks
471
- } = await this.submitBundleTx(params.chain, wallet, txReq, aggregatorAddress, calldata, txReq.value ?? 0n, bundleBlockCount);
548
+ } = await this.submitBundleTx(params.chain, wallet, txReq, aggregatorAddress, calldata, txReq.value ?? 0n, bundleBlockCount, traceReq);
472
549
  txHash = bundleTxHash;
473
550
  if (!flashbotsReceipt) {
474
551
  throw new import_types.BundleNotIncludedError(`Bundle 未在目标区块上链, txHash=${bundleTxHash}, targetBlocks=[${targetBlocks.join(",")}]`, bundleTxHash);
@@ -478,7 +555,7 @@ var _Aggregator = class {
478
555
  }
479
556
  receipt = flashbotsReceipt;
480
557
  } else {
481
- receipt = await this.sendContractTx(params.chain, aggregator, "multiSwap", [params.user, params.amountInWeis, swapParamsList, params.minAmountOutLists, params.totalMinAmountOut], txReq);
558
+ receipt = await this.sendContractTx(params.chain, aggregator, "multiSwap", [params.user, params.amountInWeis, swapParamsList, params.minAmountOutLists, params.totalMinAmountOut], txReq, traceReq);
482
559
  txHash = receipt.hash;
483
560
  }
484
561
  let amountOutList = null;
@@ -496,7 +573,13 @@ var _Aggregator = class {
496
573
  }
497
574
  if (!amountOutList)
498
575
  throw new Error(`MultiSwapped event not found: ${receipt.hash}`);
499
- _Aggregator.traceLog(params.chain, "log", `multiSwap完成, txHash=${receipt.hash}, amountOutList=[${amountOutList.join(",")}], useBundle=${useBundle}, 总耗时=${Date.now() - startTs}ms`);
576
+ this.trace(
577
+ params.chain,
578
+ traceReq,
579
+ "log",
580
+ "hermes-multiSwap-完成",
581
+ `multiSwap完成, txHash=${receipt.hash}, amountOutList=[${amountOutList.join(",")}], useBundle=${useBundle}, 总耗时=${Date.now() - startTs}ms`
582
+ );
500
583
  return {
501
584
  hash: receipt.hash,
502
585
  from: receipt.from,
@@ -514,7 +597,13 @@ var _Aggregator = class {
514
597
  receipt
515
598
  };
516
599
  } catch (error) {
517
- _Aggregator.traceLog(params.chain, "error", `multiSwap异常, rpcUrl=${this.getRpcUrl(params.chain)}, txHash=${txHash ?? "N/A"}, 总耗时=${Date.now() - startTs}ms, error=${error}`);
600
+ this.trace(
601
+ params.chain,
602
+ traceReq,
603
+ "error",
604
+ "hermes-multiSwap-异常",
605
+ `multiSwap异常, rpcUrl=${this.getRpcUrl(params.chain)}, txHash=${txHash ?? "N/A"}, 总耗时=${Date.now() - startTs}ms, error=${error}`
606
+ );
518
607
  throw error;
519
608
  }
520
609
  }
@@ -1013,22 +1102,22 @@ var _Aggregator = class {
1013
1102
  );
1014
1103
  });
1015
1104
  }
1016
- async sendSignedTxToChannels(chain, signedTx, txHash, channels) {
1105
+ async sendSignedTxToChannels(chain, signedTx, txHash, channels, traceReq) {
1017
1106
  const results = await Promise.all(
1018
1107
  channels.map(async ({ label, send }) => {
1019
1108
  var _a, _b, _c, _d, _e;
1020
1109
  const sendTs = Date.now();
1021
1110
  try {
1022
1111
  await send();
1023
- _Aggregator.traceLog(chain, "log", `广播${label} OK, txHash=${txHash}, 耗时=${Date.now() - sendTs}ms`);
1112
+ this.trace(chain, traceReq, "log", "hermes-broadcast-channel-成功", `广播${label} OK, txHash=${txHash}, 耗时=${Date.now() - sendTs}ms`);
1024
1113
  return { label, ok: true };
1025
1114
  } catch (err) {
1026
1115
  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);
1027
1116
  if (_Aggregator.isBenignBroadcastError(msg)) {
1028
- _Aggregator.traceLog(chain, "log", `广播${label} already-known, txHash=${txHash}, 耗时=${Date.now() - sendTs}ms`);
1117
+ this.trace(chain, traceReq, "log", "hermes-broadcast-channel-已存在", `广播${label} already-known, txHash=${txHash}, 耗时=${Date.now() - sendTs}ms`);
1029
1118
  return { label, ok: true };
1030
1119
  }
1031
- _Aggregator.traceLog(chain, "warn", `广播${label} 失败: ${msg}, txHash=${txHash}, 耗时=${Date.now() - sendTs}ms`);
1120
+ this.trace(chain, traceReq, "warn", "hermes-broadcast-channel-失败", `广播${label} 失败: ${msg}, txHash=${txHash}, 耗时=${Date.now() - sendTs}ms`);
1032
1121
  return { label, ok: false, msg };
1033
1122
  }
1034
1123
  })
@@ -1039,7 +1128,7 @@ var _Aggregator = class {
1039
1128
  const detail = results.map((result) => `${result.label}: ${result.msg ?? "unknown error"}`).join("; ");
1040
1129
  throw new Error(`All broadcast channels failed for ${txHash}: ${detail}`);
1041
1130
  }
1042
- raceForReceipt(chain, txHash, providers, ownedProviders = [], timeoutMs = _Aggregator.TX_CONFIRM_TIMEOUT_MS) {
1131
+ raceForReceipt(chain, txHash, providers, ownedProviders = [], timeoutMs = _Aggregator.TX_CONFIRM_TIMEOUT_MS, traceReq) {
1043
1132
  return new Promise((resolve, reject) => {
1044
1133
  let settled = false;
1045
1134
  const blockHandlers = [];
@@ -1060,7 +1149,7 @@ var _Aggregator = class {
1060
1149
  settled = true;
1061
1150
  clearTimeout(timer);
1062
1151
  cleanup();
1063
- _Aggregator.traceLog(chain, "log", `${label} 先查到receipt, txHash=${txHash}, block=${receipt.blockNumber}`);
1152
+ this.trace(chain, traceReq, "log", "hermes-raceForReceipt-命中receipt", `${label} 先查到receipt, txHash=${txHash}, block=${receipt.blockNumber}`);
1064
1153
  resolve(receipt);
1065
1154
  };
1066
1155
  const checkReceipt = (provider, label) => {
@@ -1086,6 +1175,7 @@ var _Aggregator = class {
1086
1175
  return;
1087
1176
  settled = true;
1088
1177
  cleanup();
1178
+ this.trace(chain, traceReq, "warn", "hermes-raceForReceipt-超时", `Transaction not confirmed within ${timeoutMs}ms: ${txHash}`);
1089
1179
  reject(new Error(`Transaction not confirmed within ${timeoutMs}ms: ${txHash}`));
1090
1180
  }, timeoutMs);
1091
1181
  });
@@ -1098,11 +1188,7 @@ var _Aggregator = class {
1098
1188
  label: `Sequencer[${new URL(sequencerUrl).hostname}]`,
1099
1189
  send: async () => {
1100
1190
  var _a;
1101
- const resp = await import_axios.default.post(
1102
- sequencerUrl,
1103
- { jsonrpc: "2.0", id: 1, method: "eth_sendRawTransaction", params: [signedTx] },
1104
- { timeout: 5e3 }
1105
- );
1191
+ const resp = await import_axios.default.post(sequencerUrl, { jsonrpc: "2.0", id: 1, method: "eth_sendRawTransaction", params: [signedTx] }, { timeout: 5e3 });
1106
1192
  if ((_a = resp.data) == null ? void 0 : _a.error) {
1107
1193
  throw new Error(resp.data.error.message ?? JSON.stringify(resp.data.error));
1108
1194
  }
@@ -1110,26 +1196,35 @@ var _Aggregator = class {
1110
1196
  }
1111
1197
  };
1112
1198
  }
1113
- async sendContractTx(chain, contract, method, args, txReq) {
1199
+ async sendContractTx(chain, contract, method, args, txReq, traceReq) {
1114
1200
  const extraUrls = this.broadcastRpcsMap.get(chain);
1115
1201
  const hasSequencer = _Aggregator.SEQUENCER_CHAINS.has(chain) && this.sequencerRpcsMap.has(chain);
1116
1202
  if (!extraUrls || extraUrls.length === 0) {
1117
1203
  if (!hasSequencer) {
1118
- _Aggregator.traceLog(chain, "log", `渠道=单RPC直发, method=${method}, nonce=${txReq.nonce}, gasLimit=${txReq.gasLimit}`);
1204
+ this.trace(chain, traceReq, "log", "hermes-sendContractTx-发出前", `渠道=单RPC直发, method=${method}, nonce=${txReq.nonce}, gasLimit=${txReq.gasLimit}`);
1119
1205
  const sendTs = Date.now();
1206
+ this.trace(chain, traceReq, "log", "hermes-sendContractTx-RPC发送前", `单RPC准备发送, method=${method}`);
1120
1207
  const txResponse = await contract[method](...args, txReq);
1121
- _Aggregator.traceLog(chain, "log", `单RPC已发送, txHash=${txResponse.hash}, 耗时=${Date.now() - sendTs}ms`);
1208
+ this.trace(chain, traceReq, "log", "hermes-sendContractTx-RPC已发送", `单RPC已发送, txHash=${txResponse.hash}, 耗时=${Date.now() - sendTs}ms`);
1209
+ this.trace(chain, traceReq, "log", "hermes-sendContractTx-等待确认", `单RPC开始等待上链确认, txHash=${txResponse.hash}`);
1122
1210
  const receipt3 = await txResponse.wait();
1123
1211
  const singleProvider = this.providerClient.getProvider(chain);
1124
1212
  const blockTs3 = await this.getBlockTimestampStr(singleProvider, receipt3.blockNumber);
1125
- _Aggregator.traceLog(chain, "log", `单RPC上链确认, txHash=${receipt3.hash}, blockNumber=${receipt3.blockNumber}, 出块时间=${blockTs3}, gasUsed=${receipt3.gasUsed}, status=${receipt3.status}`);
1213
+ this.trace(
1214
+ chain,
1215
+ traceReq,
1216
+ "log",
1217
+ "hermes-sendContractTx-确认完成",
1218
+ `单RPC上链确认, txHash=${receipt3.hash}, blockNumber=${receipt3.blockNumber}, 出块时间=${blockTs3}, gasUsed=${receipt3.gasUsed}, status=${receipt3.status}`
1219
+ );
1126
1220
  if (receipt3.status === 0) {
1127
1221
  throw new import_types.TransactionRevertedError(receipt3.hash, receipt3.gasUsed);
1128
1222
  }
1129
1223
  return receipt3;
1130
1224
  }
1131
- _Aggregator.traceLog(chain, "log", `渠道=单RPC+排序器直发, method=${method}, nonce=${txReq.nonce}, gasLimit=${txReq.gasLimit}`);
1225
+ this.trace(chain, traceReq, "log", "hermes-sendContractTx-发出前", `渠道=单RPC+排序器直发, method=${method}, nonce=${txReq.nonce}, gasLimit=${txReq.gasLimit}`);
1132
1226
  const wallet2 = this.providerClient.getWallet(chain);
1227
+ this.trace(chain, traceReq, "log", "hermes-sendContractTx-构建交易", `开始 populateTransaction, method=${method}`);
1133
1228
  const populated2 = await contract[method].populateTransaction(...args, txReq);
1134
1229
  delete populated2.from;
1135
1230
  const network2 = await wallet2.provider.getNetwork();
@@ -1137,19 +1232,23 @@ var _Aggregator = class {
1137
1232
  const signedTx2 = await wallet2.signTransaction(populated2);
1138
1233
  const txHash2 = import_ethers.ethers.keccak256(signedTx2);
1139
1234
  const mainProvider2 = wallet2.provider;
1140
- _Aggregator.traceLog(chain, "log", `单RPC+排序器并行广播, txHash=${txHash2}`);
1235
+ this.trace(chain, traceReq, "log", "hermes-sendContractTx-RPC发送前", `单RPC+排序器并行广播, txHash=${txHash2}`);
1141
1236
  const broadcastTs2 = Date.now();
1142
1237
  const providers = [{ provider: mainProvider2, label: "RPC[0/主]" }];
1143
1238
  const sequencerChannel2 = this.buildSequencerChannel(chain, signedTx2);
1144
- const channels2 = [
1145
- ...providers.map(({ provider, label }) => ({ label, send: () => provider.broadcastTransaction(signedTx2) })),
1146
- ...sequencerChannel2 ? [sequencerChannel2] : []
1147
- ];
1148
- const receiptPromise2 = this.raceForReceipt(chain, txHash2, providers);
1149
- await this.sendSignedTxToChannels(chain, signedTx2, txHash2, channels2);
1239
+ const channels2 = [...providers.map(({ provider, label }) => ({ label, send: () => provider.broadcastTransaction(signedTx2) })), ...sequencerChannel2 ? [sequencerChannel2] : []];
1240
+ const receiptPromise2 = this.raceForReceipt(chain, txHash2, providers, [], _Aggregator.TX_CONFIRM_TIMEOUT_MS, traceReq);
1241
+ await this.sendSignedTxToChannels(chain, signedTx2, txHash2, channels2, traceReq);
1242
+ this.trace(chain, traceReq, "log", "hermes-sendContractTx-等待确认", `并行广播后开始等待确认, txHash=${txHash2}`);
1150
1243
  const receipt2 = await receiptPromise2;
1151
1244
  const blockTs2 = await this.getBlockTimestampStr(mainProvider2, receipt2.blockNumber);
1152
- _Aggregator.traceLog(chain, "log", `单RPC+排序器上链确认, txHash=${receipt2.hash}, blockNumber=${receipt2.blockNumber}, 出块时间=${blockTs2}, gasUsed=${receipt2.gasUsed}, status=${receipt2.status}, 总耗时=${Date.now() - broadcastTs2}ms`);
1245
+ this.trace(
1246
+ chain,
1247
+ traceReq,
1248
+ "log",
1249
+ "hermes-sendContractTx-确认完成",
1250
+ `单RPC+排序器上链确认, txHash=${receipt2.hash}, blockNumber=${receipt2.blockNumber}, 出块时间=${blockTs2}, gasUsed=${receipt2.gasUsed}, status=${receipt2.status}, 总耗时=${Date.now() - broadcastTs2}ms`
1251
+ );
1153
1252
  if (receipt2.status === 0) {
1154
1253
  throw new import_types.TransactionRevertedError(receipt2.hash, receipt2.gasUsed);
1155
1254
  }
@@ -1170,24 +1269,27 @@ var _Aggregator = class {
1170
1269
  }));
1171
1270
  const allProviders = [{ provider: mainProvider, label: "RPC[0/主]" }, ...extraProviders];
1172
1271
  const ownedProviders = extraProviders.map((e) => e.provider);
1173
- _Aggregator.traceLog(
1272
+ this.trace(
1174
1273
  chain,
1274
+ traceReq,
1175
1275
  "log",
1276
+ "hermes-sendContractTx-发出前",
1176
1277
  `渠道=多RPC广播, method=${method}, nonce=${txReq.nonce}, gasLimit=${txReq.gasLimit}, 当前区块=${currentBlock}, 期望上链区块=${currentBlock + 1}, txHash=${txHash}, RPC数量=${allProviders.length}(主1+副${extraUrls.length})${hasSequencer ? ", +排序器直发" : ""}`
1177
1278
  );
1178
1279
  const broadcastTs = Date.now();
1179
1280
  const sequencerChannel = this.buildSequencerChannel(chain, signedTx);
1180
- const channels = [
1181
- ...allProviders.map(({ provider, label }) => ({ label, send: () => provider.broadcastTransaction(signedTx) })),
1182
- ...sequencerChannel ? [sequencerChannel] : []
1183
- ];
1184
- const receiptPromise = this.raceForReceipt(chain, txHash, allProviders, ownedProviders);
1185
- await this.sendSignedTxToChannels(chain, signedTx, txHash, channels);
1281
+ const channels = [...allProviders.map(({ provider, label }) => ({ label, send: () => provider.broadcastTransaction(signedTx) })), ...sequencerChannel ? [sequencerChannel] : []];
1282
+ const receiptPromise = this.raceForReceipt(chain, txHash, allProviders, ownedProviders, _Aggregator.TX_CONFIRM_TIMEOUT_MS, traceReq);
1283
+ this.trace(chain, traceReq, "log", "hermes-sendContractTx-RPC发送前", `多RPC广播准备发送, txHash=${txHash}`);
1284
+ await this.sendSignedTxToChannels(chain, signedTx, txHash, channels, traceReq);
1285
+ this.trace(chain, traceReq, "log", "hermes-sendContractTx-等待确认", `多RPC广播后开始等待确认, txHash=${txHash}`);
1186
1286
  const receipt = await receiptPromise;
1187
1287
  const blockTs = await this.getBlockTimestampStr(mainProvider, receipt.blockNumber);
1188
- _Aggregator.traceLog(
1288
+ this.trace(
1189
1289
  chain,
1290
+ traceReq,
1190
1291
  "log",
1292
+ "hermes-sendContractTx-确认完成",
1191
1293
  `多RPC广播上链确认, txHash=${receipt.hash}, blockNumber=${receipt.blockNumber}, 出块时间=${blockTs}, gasUsed=${receipt.gasUsed}, status=${receipt.status}, 总耗时=${Date.now() - broadcastTs}ms`
1192
1294
  );
1193
1295
  if (receipt.status === 0) {
@@ -1195,7 +1297,7 @@ var _Aggregator = class {
1195
1297
  }
1196
1298
  return receipt;
1197
1299
  }
1198
- async submitBundleTx(chain, wallet, txReq, to, data, value, bundleBlockCount) {
1300
+ async submitBundleTx(chain, wallet, txReq, to, data, value, bundleBlockCount, traceReq) {
1199
1301
  if (bundleBlockCount == null) {
1200
1302
  bundleBlockCount = this.builderConfigMap.has(chain) ? 3 : 1;
1201
1303
  }
@@ -1229,55 +1331,92 @@ var _Aggregator = class {
1229
1331
  const targetBlocks = Array.from({ length: normalizedBundleBlockCount }, (_, i) => currentBlock + 1 + i);
1230
1332
  const builderNames = Object.keys(builderCfg.urls);
1231
1333
  const builderEntries = Object.entries(builderCfg.urls);
1232
- _Aggregator.traceLog(
1334
+ this.trace(
1233
1335
  chain,
1336
+ traceReq,
1234
1337
  "log",
1338
+ "hermes-submitBundleTx-发出前",
1235
1339
  `渠道=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}`
1236
1340
  );
1237
- const signer = builderCfg.authType === "flashbots" ? this.flashbotSigner : void 0;
1238
- const headers = { "Content-Type": "application/json" };
1239
- if (builderCfg.authType === "flashbots" && signer) {
1240
- const sampleBody = this.buildBundleRequestBody(builderCfg, rawTxs, targetBlocks[0]);
1241
- headers["X-Flashbots-Signature"] = await this.signRequestBody(sampleBody, signer);
1242
- }
1243
1341
  const sendBundleTs = Date.now();
1244
1342
  let totalSent = 0;
1245
1343
  let totalFailed = 0;
1246
- const fireAndForget = targetBlocks.flatMap((block) => {
1247
- const body = this.buildBundleRequestBody(builderCfg, rawTxs, block);
1248
- return builderEntries.map(async ([name, url]) => {
1344
+ const signer = builderCfg.authType === "flashbots" ? this.flashbotSigner : void 0;
1345
+ const fireAndForget = builderEntries.flatMap(([name, url]) => {
1346
+ if (name === "flashbots" && builderCfg.authType === "flashbots" && signer) {
1347
+ return [
1348
+ (async () => {
1349
+ const body = this.buildFlashbotsMevBundleRequestBody(rawTxs, targetBlocks[0], targetBlocks[targetBlocks.length - 1]);
1350
+ const headers = {
1351
+ "Content-Type": "application/json",
1352
+ "X-Flashbots-Signature": await this.signRequestBody(body, signer)
1353
+ };
1354
+ const ts = Date.now();
1355
+ try {
1356
+ const response = await import_axios.default.post(url, body, { headers, timeout: 3e3 });
1357
+ totalSent++;
1358
+ this.trace(chain, traceReq, "log", "hermes-submitBundleTx-builder-成功", `Builder[${name}] mev_sendBundle 发送成功, targetBlocks=[${targetBlocks.join(",")}], 耗时=${Date.now() - ts}ms, resp=${JSON.stringify(response.data)}`);
1359
+ } catch (error) {
1360
+ totalFailed++;
1361
+ const msg = (error == null ? void 0 : error.response) ? JSON.stringify(error.response.data) : (error == null ? void 0 : error.message) ?? String(error);
1362
+ this.trace(chain, traceReq, "error", "hermes-submitBundleTx-builder-失败", `Builder[${name}] mev_sendBundle 发送失败, targetBlocks=[${targetBlocks.join(",")}], 耗时=${Date.now() - ts}ms, error=${msg}`);
1363
+ }
1364
+ })()
1365
+ ];
1366
+ }
1367
+ return targetBlocks.map(async (block) => {
1368
+ const body = this.buildBundleRequestBody(builderCfg, rawTxs, block);
1369
+ const headers = { "Content-Type": "application/json" };
1370
+ if (builderCfg.authType === "flashbots" && signer) {
1371
+ headers["X-Flashbots-Signature"] = await this.signRequestBody(body, signer);
1372
+ }
1249
1373
  const ts = Date.now();
1250
1374
  try {
1251
1375
  const response = await import_axios.default.post(url, body, { headers, timeout: 3e3 });
1252
1376
  totalSent++;
1253
- _Aggregator.traceLog(chain, "log", `Builder[${name}] 发送成功, targetBlock=${block}, 耗时=${Date.now() - ts}ms, resp=${JSON.stringify(response.data)}`);
1377
+ this.trace(chain, traceReq, "log", "hermes-submitBundleTx-builder-成功", `Builder[${name}] 发送成功, targetBlock=${block}, 耗时=${Date.now() - ts}ms, resp=${JSON.stringify(response.data)}`);
1254
1378
  } catch (error) {
1255
1379
  totalFailed++;
1256
1380
  const msg = (error == null ? void 0 : error.response) ? JSON.stringify(error.response.data) : (error == null ? void 0 : error.message) ?? String(error);
1257
- _Aggregator.traceLog(chain, "error", `Builder[${name}] 发送失败, targetBlock=${block}, 耗时=${Date.now() - ts}ms, error=${msg}`);
1381
+ this.trace(chain, traceReq, "error", "hermes-submitBundleTx-builder-失败", `Builder[${name}] 发送失败, targetBlock=${block}, 耗时=${Date.now() - ts}ms, error=${msg}`);
1258
1382
  }
1259
1383
  });
1260
1384
  });
1261
1385
  const abortController = new AbortController();
1262
1386
  const inclusionPromise = this.waitForBundleInclusion(provider, txHash, targetBlocks, abortController.signal);
1263
1387
  const waitTs = Date.now();
1388
+ this.trace(chain, traceReq, "log", "hermes-submitBundleTx-RPC发送前", `Bundle 请求开始发送, txHash=${txHash}, targetBlocks=[${targetBlocks.join(",")}]`);
1264
1389
  const [receipt] = await Promise.all([
1265
1390
  inclusionPromise,
1266
1391
  Promise.all(fireAndForget).then(() => {
1267
- _Aggregator.traceLog(chain, "log", `Bundle发送完成, 成功=${totalSent}, 失败=${totalFailed}, 总请求=${fireAndForget.length}, 耗时=${Date.now() - sendBundleTs}ms, txHash=${txHash}`);
1392
+ this.trace(
1393
+ chain,
1394
+ traceReq,
1395
+ "log",
1396
+ "hermes-submitBundleTx-RPC已发送",
1397
+ `Bundle发送完成, 成功=${totalSent}, 失败=${totalFailed}, 总请求=${fireAndForget.length}, 耗时=${Date.now() - sendBundleTs}ms, txHash=${txHash}`
1398
+ );
1268
1399
  if (totalSent === 0)
1269
1400
  abortController.abort();
1270
1401
  })
1271
1402
  ]);
1272
1403
  if (receipt) {
1273
1404
  const blockTs = await this.getBlockTimestampStr(provider, receipt.blockNumber);
1274
- _Aggregator.traceLog(
1405
+ this.trace(
1275
1406
  chain,
1407
+ traceReq,
1276
1408
  "log",
1409
+ "hermes-submitBundleTx-确认完成",
1277
1410
  `Bundle上链确认, txHash=${receipt.hash}, blockNumber=${receipt.blockNumber}, 出块时间=${blockTs}, gasUsed=${receipt.gasUsed}, 等待耗时=${Date.now() - waitTs}ms`
1278
1411
  );
1279
1412
  } else {
1280
- _Aggregator.traceLog(chain, "warn", `Bundle未在目标区块上链, txHash=${txHash}, 目标区块=[${targetBlocks.join(",")}], 等待耗时=${Date.now() - waitTs}ms`);
1413
+ this.trace(
1414
+ chain,
1415
+ traceReq,
1416
+ "warn",
1417
+ "hermes-submitBundleTx-等待结束",
1418
+ `Bundle未在目标区块上链, txHash=${txHash}, 目标区块=[${targetBlocks.join(",")}], 等待耗时=${Date.now() - waitTs}ms`
1419
+ );
1281
1420
  }
1282
1421
  return { receipt, txHash, targetBlocks };
1283
1422
  }
@@ -1379,7 +1518,7 @@ var _Aggregator = class {
1379
1518
  }
1380
1519
  stripCustomFields(txReq, chain) {
1381
1520
  const defaultBlockCount = chain && this.builderConfigMap.has(chain) ? 3 : 1;
1382
- const { useBundle, bundleBlockCount = defaultBlockCount, ...pureTxReq } = txReq;
1521
+ const { useBundle, bundleBlockCount = defaultBlockCount, trace, ...pureTxReq } = txReq;
1383
1522
  return { pureTxReq, bundleBlockCount: this.normalizeBundleBlockCount(bundleBlockCount) };
1384
1523
  }
1385
1524
  normalizeBundleBlockCount(bundleBlockCount) {
@@ -1415,20 +1554,17 @@ Aggregator.DEFAULT_ETH_BUILDERS = {
1415
1554
  Titan: "https://rpc.titanbuilder.xyz",
1416
1555
  flashbots: "https://relay.flashbots.net",
1417
1556
  // bloXroute: 'https://mev.api.blxrbdn.com', // 旧 api.blxrbdn.com 也行,需配置 Authorization 头
1418
- lightspeedbuilder: "https://rpc.lightspeedbuilder.info",
1557
+ lightspeedbuilder: "https://rpc.lightspeedbuilder.info"
1419
1558
  // 新增,实测直接可用
1420
1559
  // buildernet: 'https://rpc.buildernet.org', // 新增,Flashbots 生态,需签名头(同 flashbots 格式)
1421
- nfactorial: "https://rpc.nfactorial.xyz"
1422
- // 新增,支持 bundle rebate
1560
+ // nfactorial: 'https://rpc.nfactorial.xyz', // 新增,支持 bundle rebate
1423
1561
  };
1424
1562
  Aggregator.DEFAULT_SEQUENCER_RPC_BY_CHAIN = {
1425
1563
  [import_types.ChainNameEnum.ARB]: "https://arb1-sequencer.arbitrum.io/rpc",
1426
1564
  [import_types.ChainNameEnum.BASE]: "https://mainnet-sequencer.base.org",
1427
1565
  [import_types.ChainNameEnum.ETHERLINK]: "https://relay.mainnet.etherlink.com"
1428
1566
  };
1429
- Aggregator.SEQUENCER_CHAINS = new Set(
1430
- Object.keys(_Aggregator.DEFAULT_SEQUENCER_RPC_BY_CHAIN)
1431
- );
1567
+ Aggregator.SEQUENCER_CHAINS = new Set(Object.keys(_Aggregator.DEFAULT_SEQUENCER_RPC_BY_CHAIN));
1432
1568
  Aggregator.TX_CONFIRM_TIMEOUT_MS = 12e4;
1433
1569
  Aggregator.BUNDLE_INCLUSION_TIMEOUT_MS = 3e4;
1434
1570
  var aggregator_default = Aggregator;
@@ -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;
@@ -26,6 +28,7 @@ declare class Aggregator {
26
28
  private getBuilderConfig;
27
29
  private getSequencerRpcs;
28
30
  private buildBundleRequestBody;
31
+ private buildFlashbotsMevBundleRequestBody;
29
32
  sendBundle(chain: ChainNameEnum, rawTxs: string[], targetBlock: number, signer?: ethers.Wallet): Promise<{
30
33
  sent: number;
31
34
  failed: number;
@@ -1,7 +1,7 @@
1
- import type { HermesTxRequest, IBatchMultiSwapParams, IBatchExpectParams, ISwapByPathParams, IExpectSplitOrderParams, IExpectSplitOrderResp, IBridgeParams, IBatchReceipt, IMultiSwapParams, IBatchMultiSwapAndBridgeParams, ISwapAndBridgeParams, IReceipt, IConfig, IBuilderConfig, IExpectPayload, IRouterPath, SupportContracts, IExpectByPathParams, IHermesSignalResponse, IHermesSignalQuoteBestData, IHermesSignalRoute } from './types.js';
1
+ import type { HermesTxRequest, IHermesExecutionTrace, IBatchMultiSwapParams, IBatchExpectParams, ISwapByPathParams, IExpectSplitOrderParams, IExpectSplitOrderResp, IBridgeParams, IBatchReceipt, IMultiSwapParams, IBatchMultiSwapAndBridgeParams, ISwapAndBridgeParams, IReceipt, IConfig, IBuilderConfig, IExpectPayload, IRouterPath, SupportContracts, IExpectByPathParams, IHermesSignalResponse, IHermesSignalQuoteBestData, IHermesSignalRoute } from './types.js';
2
2
  import { ChainNameEnum, AddressConst, DexType, BridgeType, IEstimateType, LayerZeroV1ChainIdMap, LayerZeroV1ChainNameMap, LayerZeroV2ChainIdMap, LayerZeroV2ChainNameMap } from './types.js';
3
3
  import { TransactionRequest } from 'ethers';
4
- export type { HermesTxRequest, IBatchMultiSwapAndBridgeParams, IExpectSplitOrderParams, IExpectSplitOrderResp, IMultiSwapParams, IBatchMultiSwapParams, IBatchExpectParams, IBatchReceipt, ISwapByPathParams as ISwapParams, IBridgeParams, ISwapAndBridgeParams, IReceipt, IConfig, IBuilderConfig, IExpectPayload, IRouterPath, SupportContracts, IHermesSignalResponse, IHermesSignalQuoteBestData, IHermesSignalRoute, };
4
+ export type { HermesTxRequest, IHermesExecutionTrace, IBatchMultiSwapAndBridgeParams, IExpectSplitOrderParams, IExpectSplitOrderResp, IMultiSwapParams, IBatchMultiSwapParams, IBatchExpectParams, IBatchReceipt, ISwapByPathParams as ISwapParams, IBridgeParams, ISwapAndBridgeParams, IReceipt, IConfig, IBuilderConfig, IExpectPayload, IRouterPath, SupportContracts, IHermesSignalResponse, IHermesSignalQuoteBestData, IHermesSignalRoute, };
5
5
  import { BundleNotIncludedError, TransactionRevertedError } from './types.js';
6
6
  export { ChainNameEnum, AddressConst, DexType, BridgeType, IEstimateType, LayerZeroV1ChainIdMap, LayerZeroV1ChainNameMap, LayerZeroV2ChainIdMap, LayerZeroV2ChainNameMap, BundleNotIncludedError, TransactionRevertedError };
7
7
  declare class Hermes {
@@ -377,10 +377,18 @@ export interface IRpcConfig {
377
377
  url: string;
378
378
  privateKey?: string;
379
379
  }
380
+ export interface IHermesExecutionTrace {
381
+ ts: number;
382
+ chain?: ChainNameEnum;
383
+ level: 'log' | 'warn' | 'error';
384
+ stage: string;
385
+ message: string;
386
+ }
380
387
  export type HermesTxRequest = TransactionRequest & {
381
388
  useBundle?: boolean;
382
389
  /** 尝试的区块数量(1-3),默认1(即仅 +1 区块)。传2则尝试 +1/+2 区块,传3则尝试 +1/+2/+3 区块 */
383
390
  bundleBlockCount?: number;
391
+ trace?: (event: IHermesExecutionTrace) => void;
384
392
  };
385
393
  export declare const AddressConst: {
386
394
  weth: {
@@ -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;
@@ -26,6 +28,7 @@ declare class Aggregator {
26
28
  private getBuilderConfig;
27
29
  private getSequencerRpcs;
28
30
  private buildBundleRequestBody;
31
+ private buildFlashbotsMevBundleRequestBody;
29
32
  sendBundle(chain: ChainNameEnum, rawTxs: string[], targetBlock: number, signer?: ethers.Wallet): Promise<{
30
33
  sent: number;
31
34
  failed: number;