hermes-swap 0.6.7 → 0.6.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -144,6 +144,23 @@ var _Aggregator = class {
144
144
  params: [{ txs: rawTxs, blockNumber: `0x${targetBlock.toString(16)}` }]
145
145
  };
146
146
  }
147
+ buildFlashbotsMevBundleRequestBody(rawTxs, startBlock, endBlock) {
148
+ return {
149
+ jsonrpc: "2.0",
150
+ id: 1,
151
+ method: "mev_sendBundle",
152
+ params: [
153
+ {
154
+ version: "v0.1",
155
+ inclusion: {
156
+ block: `0x${startBlock.toString(16)}`,
157
+ maxBlock: `0x${endBlock.toString(16)}`
158
+ },
159
+ body: rawTxs.map((tx) => ({ tx, canRevert: false }))
160
+ }
161
+ ]
162
+ };
163
+ }
147
164
  async sendBundle(chain, rawTxs, targetBlock, signer) {
148
165
  const builderCfg = this.getBuilderConfig(chain);
149
166
  const body = this.buildBundleRequestBody(builderCfg, rawTxs, targetBlock);
@@ -877,8 +894,19 @@ var _Aggregator = class {
877
894
  static isBenignBroadcastError(message) {
878
895
  return message.includes("already known") || message.includes("nonce too low") || message.includes("nonce has already been used") || message.includes("ALREADY_EXISTS") || message.includes("NONCE_EXPIRED");
879
896
  }
880
- async waitForBundleInclusion(provider, txHash, maxTargetBlock, signal) {
881
- const deadline = Date.now() + _Aggregator.BUNDLE_INCLUSION_TIMEOUT_MS;
897
+ async blockContainsTxHash(provider, blockNumber, txHash) {
898
+ try {
899
+ const block = await provider.getBlock(blockNumber, true);
900
+ if (!block)
901
+ return false;
902
+ if (block.transactions.some((hash) => hash === txHash))
903
+ return true;
904
+ return block.prefetchedTransactions.some((tx) => tx.hash === txHash);
905
+ } catch {
906
+ return false;
907
+ }
908
+ }
909
+ async waitForReceipt(provider, txHash, deadline, signal) {
882
910
  while (Date.now() < deadline) {
883
911
  if (signal == null ? void 0 : signal.aborted)
884
912
  return null;
@@ -888,12 +916,119 @@ var _Aggregator = class {
888
916
  return receipt;
889
917
  } catch {
890
918
  }
891
- const currentBlock = await provider.getBlockNumber().catch(() => 0);
892
- if (currentBlock > maxTargetBlock + 1)
893
- return null;
894
- await new Promise((r) => setTimeout(r, _Aggregator.BUNDLE_POLL_INTERVAL_MS));
919
+ await new Promise((r) => setTimeout(r, 200));
920
+ }
921
+ try {
922
+ const receipt = await provider.getTransactionReceipt(txHash);
923
+ return receipt ? receipt : null;
924
+ } catch {
925
+ return null;
895
926
  }
896
- return null;
927
+ }
928
+ async waitForBundleInclusion(provider, txHash, targetBlocks, signal) {
929
+ const deadline = Date.now() + _Aggregator.BUNDLE_INCLUSION_TIMEOUT_MS;
930
+ const sortedTargetBlocks = [...targetBlocks].sort((a, b) => a - b);
931
+ const targetBlockSet = new Set(sortedTargetBlocks);
932
+ const maxTargetBlock = sortedTargetBlocks[sortedTargetBlocks.length - 1];
933
+ return new Promise((resolve) => {
934
+ let settled = false;
935
+ let lastObservedBlock = sortedTargetBlocks[0] - 1;
936
+ const checkedBlocks = /* @__PURE__ */ new Set();
937
+ let matchedTargetBlock = false;
938
+ let inspectionChain = Promise.resolve();
939
+ const cleanup = () => {
940
+ clearTimeout(timer);
941
+ provider.off("block", onBlock);
942
+ };
943
+ const finish = (receipt) => {
944
+ if (settled)
945
+ return;
946
+ settled = true;
947
+ cleanup();
948
+ resolve(receipt);
949
+ };
950
+ const scheduleInspect = (fromBlock, toBlock) => {
951
+ if (fromBlock > toBlock) {
952
+ if (checkedBlocks.size === targetBlockSet.size && lastObservedBlock >= maxTargetBlock) {
953
+ finish(null);
954
+ }
955
+ return;
956
+ }
957
+ inspectionChain = inspectionChain.then(() => inspectTargetBlocks(fromBlock, toBlock)).catch(() => {
958
+ });
959
+ };
960
+ const inspectTargetBlocks = async (fromBlock, toBlock) => {
961
+ if (settled || (signal == null ? void 0 : signal.aborted)) {
962
+ finish(null);
963
+ return;
964
+ }
965
+ for (let blockNumber = fromBlock; blockNumber <= toBlock; blockNumber++) {
966
+ if (settled || (signal == null ? void 0 : signal.aborted)) {
967
+ finish(null);
968
+ return;
969
+ }
970
+ if (!targetBlockSet.has(blockNumber) || checkedBlocks.has(blockNumber))
971
+ continue;
972
+ checkedBlocks.add(blockNumber);
973
+ const matched = await this.blockContainsTxHash(provider, blockNumber, txHash);
974
+ if (!matched)
975
+ continue;
976
+ matchedTargetBlock = true;
977
+ clearTimeout(timer);
978
+ const receipt = await this.waitForReceipt(provider, txHash, Date.now() + _Aggregator.TX_CONFIRM_TIMEOUT_MS, signal);
979
+ finish(receipt);
980
+ return;
981
+ }
982
+ if (checkedBlocks.size === targetBlockSet.size && lastObservedBlock >= maxTargetBlock) {
983
+ finish(null);
984
+ }
985
+ };
986
+ const onBlock = (blockNumber) => {
987
+ if (settled)
988
+ return;
989
+ const fromBlock = Math.max(lastObservedBlock + 1, sortedTargetBlocks[0]);
990
+ const toBlock = Math.min(blockNumber, maxTargetBlock);
991
+ if (blockNumber > lastObservedBlock) {
992
+ lastObservedBlock = blockNumber;
993
+ }
994
+ if (fromBlock > toBlock) {
995
+ if (checkedBlocks.size === targetBlockSet.size && lastObservedBlock >= maxTargetBlock) {
996
+ finish(null);
997
+ }
998
+ return;
999
+ }
1000
+ scheduleInspect(fromBlock, toBlock);
1001
+ };
1002
+ const timer = setTimeout(async () => {
1003
+ if (settled)
1004
+ return;
1005
+ if (matchedTargetBlock)
1006
+ return;
1007
+ const latestBlock = await provider.getBlockNumber().catch(() => lastObservedBlock);
1008
+ if (latestBlock > lastObservedBlock) {
1009
+ lastObservedBlock = latestBlock;
1010
+ }
1011
+ const toBlock = Math.min(latestBlock, maxTargetBlock);
1012
+ scheduleInspect(sortedTargetBlocks[0], toBlock);
1013
+ await inspectionChain;
1014
+ finish(null);
1015
+ }, _Aggregator.BUNDLE_INCLUSION_TIMEOUT_MS);
1016
+ provider.on("block", onBlock);
1017
+ provider.getBlockNumber().then(
1018
+ (currentBlock) => {
1019
+ if (settled)
1020
+ return;
1021
+ lastObservedBlock = currentBlock;
1022
+ const fromBlock = sortedTargetBlocks[0];
1023
+ const toBlock = Math.min(currentBlock, maxTargetBlock);
1024
+ if (fromBlock <= toBlock) {
1025
+ scheduleInspect(fromBlock, toBlock);
1026
+ }
1027
+ },
1028
+ () => {
1029
+ }
1030
+ );
1031
+ });
897
1032
  }
898
1033
  async sendSignedTxToChannels(chain, signedTx, txHash, channels) {
899
1034
  const results = await Promise.all(
@@ -980,11 +1115,7 @@ var _Aggregator = class {
980
1115
  label: `Sequencer[${new URL(sequencerUrl).hostname}]`,
981
1116
  send: async () => {
982
1117
  var _a;
983
- const resp = await import_axios.default.post(
984
- sequencerUrl,
985
- { jsonrpc: "2.0", id: 1, method: "eth_sendRawTransaction", params: [signedTx] },
986
- { timeout: 5e3 }
987
- );
1118
+ const resp = await import_axios.default.post(sequencerUrl, { jsonrpc: "2.0", id: 1, method: "eth_sendRawTransaction", params: [signedTx] }, { timeout: 5e3 });
988
1119
  if ((_a = resp.data) == null ? void 0 : _a.error) {
989
1120
  throw new Error(resp.data.error.message ?? JSON.stringify(resp.data.error));
990
1121
  }
@@ -1023,15 +1154,16 @@ var _Aggregator = class {
1023
1154
  const broadcastTs2 = Date.now();
1024
1155
  const providers = [{ provider: mainProvider2, label: "RPC[0/主]" }];
1025
1156
  const sequencerChannel2 = this.buildSequencerChannel(chain, signedTx2);
1026
- const channels2 = [
1027
- ...providers.map(({ provider, label }) => ({ label, send: () => provider.broadcastTransaction(signedTx2) })),
1028
- ...sequencerChannel2 ? [sequencerChannel2] : []
1029
- ];
1157
+ const channels2 = [...providers.map(({ provider, label }) => ({ label, send: () => provider.broadcastTransaction(signedTx2) })), ...sequencerChannel2 ? [sequencerChannel2] : []];
1030
1158
  const receiptPromise2 = this.raceForReceipt(chain, txHash2, providers);
1031
1159
  await this.sendSignedTxToChannels(chain, signedTx2, txHash2, channels2);
1032
1160
  const receipt2 = await receiptPromise2;
1033
1161
  const blockTs2 = await this.getBlockTimestampStr(mainProvider2, receipt2.blockNumber);
1034
- _Aggregator.traceLog(chain, "log", `单RPC+排序器上链确认, txHash=${receipt2.hash}, blockNumber=${receipt2.blockNumber}, 出块时间=${blockTs2}, gasUsed=${receipt2.gasUsed}, status=${receipt2.status}, 总耗时=${Date.now() - broadcastTs2}ms`);
1162
+ _Aggregator.traceLog(
1163
+ chain,
1164
+ "log",
1165
+ `单RPC+排序器上链确认, txHash=${receipt2.hash}, blockNumber=${receipt2.blockNumber}, 出块时间=${blockTs2}, gasUsed=${receipt2.gasUsed}, status=${receipt2.status}, 总耗时=${Date.now() - broadcastTs2}ms`
1166
+ );
1035
1167
  if (receipt2.status === 0) {
1036
1168
  throw new import_types.TransactionRevertedError(receipt2.hash, receipt2.gasUsed);
1037
1169
  }
@@ -1059,10 +1191,7 @@ var _Aggregator = class {
1059
1191
  );
1060
1192
  const broadcastTs = Date.now();
1061
1193
  const sequencerChannel = this.buildSequencerChannel(chain, signedTx);
1062
- const channels = [
1063
- ...allProviders.map(({ provider, label }) => ({ label, send: () => provider.broadcastTransaction(signedTx) })),
1064
- ...sequencerChannel ? [sequencerChannel] : []
1065
- ];
1194
+ const channels = [...allProviders.map(({ provider, label }) => ({ label, send: () => provider.broadcastTransaction(signedTx) })), ...sequencerChannel ? [sequencerChannel] : []];
1066
1195
  const receiptPromise = this.raceForReceipt(chain, txHash, allProviders, ownedProviders);
1067
1196
  await this.sendSignedTxToChannels(chain, signedTx, txHash, channels);
1068
1197
  const receipt = await receiptPromise;
@@ -1116,18 +1245,38 @@ var _Aggregator = class {
1116
1245
  "log",
1117
1246
  `渠道=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}`
1118
1247
  );
1119
- const signer = builderCfg.authType === "flashbots" ? this.flashbotSigner : void 0;
1120
- const headers = { "Content-Type": "application/json" };
1121
- if (builderCfg.authType === "flashbots" && signer) {
1122
- const sampleBody = this.buildBundleRequestBody(builderCfg, rawTxs, targetBlocks[0]);
1123
- headers["X-Flashbots-Signature"] = await this.signRequestBody(sampleBody, signer);
1124
- }
1125
1248
  const sendBundleTs = Date.now();
1126
1249
  let totalSent = 0;
1127
1250
  let totalFailed = 0;
1128
- const fireAndForget = targetBlocks.flatMap((block) => {
1129
- const body = this.buildBundleRequestBody(builderCfg, rawTxs, block);
1130
- return builderEntries.map(async ([name, url]) => {
1251
+ const signer = builderCfg.authType === "flashbots" ? this.flashbotSigner : void 0;
1252
+ const fireAndForget = builderEntries.flatMap(([name, url]) => {
1253
+ if (name === "flashbots" && builderCfg.authType === "flashbots" && signer) {
1254
+ return [
1255
+ (async () => {
1256
+ const body = this.buildFlashbotsMevBundleRequestBody(rawTxs, targetBlocks[0], targetBlocks[targetBlocks.length - 1]);
1257
+ const headers = {
1258
+ "Content-Type": "application/json",
1259
+ "X-Flashbots-Signature": await this.signRequestBody(body, signer)
1260
+ };
1261
+ const ts = Date.now();
1262
+ try {
1263
+ const response = await import_axios.default.post(url, body, { headers, timeout: 3e3 });
1264
+ totalSent++;
1265
+ _Aggregator.traceLog(chain, "log", `Builder[${name}] mev_sendBundle 发送成功, targetBlocks=[${targetBlocks.join(",")}], 耗时=${Date.now() - ts}ms, resp=${JSON.stringify(response.data)}`);
1266
+ } catch (error) {
1267
+ totalFailed++;
1268
+ 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}`);
1270
+ }
1271
+ })()
1272
+ ];
1273
+ }
1274
+ return targetBlocks.map(async (block) => {
1275
+ const body = this.buildBundleRequestBody(builderCfg, rawTxs, block);
1276
+ const headers = { "Content-Type": "application/json" };
1277
+ if (builderCfg.authType === "flashbots" && signer) {
1278
+ headers["X-Flashbots-Signature"] = await this.signRequestBody(body, signer);
1279
+ }
1131
1280
  const ts = Date.now();
1132
1281
  try {
1133
1282
  const response = await import_axios.default.post(url, body, { headers, timeout: 3e3 });
@@ -1141,8 +1290,7 @@ var _Aggregator = class {
1141
1290
  });
1142
1291
  });
1143
1292
  const abortController = new AbortController();
1144
- const maxTargetBlock = Math.max(...targetBlocks);
1145
- const inclusionPromise = this.waitForBundleInclusion(provider, txHash, maxTargetBlock, abortController.signal);
1293
+ const inclusionPromise = this.waitForBundleInclusion(provider, txHash, targetBlocks, abortController.signal);
1146
1294
  const waitTs = Date.now();
1147
1295
  const [receipt] = await Promise.all([
1148
1296
  inclusionPromise,
@@ -1298,22 +1446,18 @@ Aggregator.DEFAULT_ETH_BUILDERS = {
1298
1446
  Titan: "https://rpc.titanbuilder.xyz",
1299
1447
  flashbots: "https://relay.flashbots.net",
1300
1448
  // bloXroute: 'https://mev.api.blxrbdn.com', // 旧 api.blxrbdn.com 也行,需配置 Authorization 头
1301
- lightspeedbuilder: "https://rpc.lightspeedbuilder.info",
1449
+ lightspeedbuilder: "https://rpc.lightspeedbuilder.info"
1302
1450
  // 新增,实测直接可用
1303
1451
  // buildernet: 'https://rpc.buildernet.org', // 新增,Flashbots 生态,需签名头(同 flashbots 格式)
1304
- nfactorial: "https://rpc.nfactorial.xyz"
1305
- // 新增,支持 bundle rebate
1452
+ // nfactorial: 'https://rpc.nfactorial.xyz', // 新增,支持 bundle rebate
1306
1453
  };
1307
1454
  Aggregator.DEFAULT_SEQUENCER_RPC_BY_CHAIN = {
1308
1455
  [import_types.ChainNameEnum.ARB]: "https://arb1-sequencer.arbitrum.io/rpc",
1309
1456
  [import_types.ChainNameEnum.BASE]: "https://mainnet-sequencer.base.org",
1310
1457
  [import_types.ChainNameEnum.ETHERLINK]: "https://relay.mainnet.etherlink.com"
1311
1458
  };
1312
- Aggregator.SEQUENCER_CHAINS = new Set(
1313
- Object.keys(_Aggregator.DEFAULT_SEQUENCER_RPC_BY_CHAIN)
1314
- );
1459
+ Aggregator.SEQUENCER_CHAINS = new Set(Object.keys(_Aggregator.DEFAULT_SEQUENCER_RPC_BY_CHAIN));
1315
1460
  Aggregator.TX_CONFIRM_TIMEOUT_MS = 12e4;
1316
- Aggregator.BUNDLE_POLL_INTERVAL_MS = 500;
1317
1461
  Aggregator.BUNDLE_INCLUSION_TIMEOUT_MS = 3e4;
1318
1462
  var aggregator_default = Aggregator;
1319
1463
  // Annotate the CommonJS export names for ESM import in node:
@@ -26,6 +26,7 @@ declare class Aggregator {
26
26
  private getBuilderConfig;
27
27
  private getSequencerRpcs;
28
28
  private buildBundleRequestBody;
29
+ private buildFlashbotsMevBundleRequestBody;
29
30
  sendBundle(chain: ChainNameEnum, rawTxs: string[], targetBlock: number, signer?: ethers.Wallet): Promise<{
30
31
  sent: number;
31
32
  failed: number;
@@ -55,9 +56,10 @@ declare class Aggregator {
55
56
  estimateGas(estimateType: IEstimateType, params: IBridgeParams | ISwapByPathParams | ISwapAndBridgeParams | IBatchMultiSwapParams): Promise<bigint>;
56
57
  getAggregatorSupportContracts(chain: ChainNameEnum): Promise<SupportContracts[]>;
57
58
  private static readonly TX_CONFIRM_TIMEOUT_MS;
58
- private static readonly BUNDLE_POLL_INTERVAL_MS;
59
59
  private static readonly BUNDLE_INCLUSION_TIMEOUT_MS;
60
60
  private static isBenignBroadcastError;
61
+ private blockContainsTxHash;
62
+ private waitForReceipt;
61
63
  private waitForBundleInclusion;
62
64
  private sendSignedTxToChannels;
63
65
  private raceForReceipt;
@@ -32,6 +32,7 @@ var Provider = class {
32
32
  for (const [chainName, rpc] of Object.entries(this.config.rpc)) {
33
33
  const chain = chainName;
34
34
  const provider = new import_ethers.ethers.JsonRpcProvider(rpc.url);
35
+ provider.pollingInterval = 500;
35
36
  this.providerMap.set(chain, provider);
36
37
  if (rpc.privateKey) {
37
38
  this.walletMap.set(chain, new import_ethers.ethers.Wallet(rpc.privateKey, provider));
@@ -26,6 +26,7 @@ declare class Aggregator {
26
26
  private getBuilderConfig;
27
27
  private getSequencerRpcs;
28
28
  private buildBundleRequestBody;
29
+ private buildFlashbotsMevBundleRequestBody;
29
30
  sendBundle(chain: ChainNameEnum, rawTxs: string[], targetBlock: number, signer?: ethers.Wallet): Promise<{
30
31
  sent: number;
31
32
  failed: number;
@@ -55,9 +56,10 @@ declare class Aggregator {
55
56
  estimateGas(estimateType: IEstimateType, params: IBridgeParams | ISwapByPathParams | ISwapAndBridgeParams | IBatchMultiSwapParams): Promise<bigint>;
56
57
  getAggregatorSupportContracts(chain: ChainNameEnum): Promise<SupportContracts[]>;
57
58
  private static readonly TX_CONFIRM_TIMEOUT_MS;
58
- private static readonly BUNDLE_POLL_INTERVAL_MS;
59
59
  private static readonly BUNDLE_INCLUSION_TIMEOUT_MS;
60
60
  private static isBenignBroadcastError;
61
+ private blockContainsTxHash;
62
+ private waitForReceipt;
61
63
  private waitForBundleInclusion;
62
64
  private sendSignedTxToChannels;
63
65
  private raceForReceipt;