@across-protocol/sdk 4.0.0-beta.18 → 4.0.0-beta.2

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.
Files changed (93) hide show
  1. package/dist/cjs/clients/BundleDataClient/BundleDataClient.d.ts +4 -5
  2. package/dist/cjs/clients/BundleDataClient/BundleDataClient.js +146 -241
  3. package/dist/cjs/clients/BundleDataClient/BundleDataClient.js.map +1 -1
  4. package/dist/cjs/clients/BundleDataClient/utils/FillUtils.d.ts +1 -3
  5. package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js +1 -33
  6. package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
  7. package/dist/cjs/clients/SpokePoolClient.d.ts +0 -1
  8. package/dist/cjs/clients/SpokePoolClient.js +4 -13
  9. package/dist/cjs/clients/SpokePoolClient.js.map +1 -1
  10. package/dist/cjs/constants.d.ts +0 -1
  11. package/dist/cjs/constants.js +1 -2
  12. package/dist/cjs/constants.js.map +1 -1
  13. package/dist/cjs/providers/index.d.ts +0 -1
  14. package/dist/cjs/providers/index.js +0 -2
  15. package/dist/cjs/providers/index.js.map +1 -1
  16. package/dist/cjs/utils/AddressUtils.d.ts +0 -1
  17. package/dist/cjs/utils/AddressUtils.js +1 -14
  18. package/dist/cjs/utils/AddressUtils.js.map +1 -1
  19. package/dist/cjs/utils/CachingUtils.js +1 -1
  20. package/dist/cjs/utils/CachingUtils.js.map +1 -1
  21. package/dist/cjs/utils/DepositUtils.d.ts +1 -2
  22. package/dist/cjs/utils/DepositUtils.js +3 -12
  23. package/dist/cjs/utils/DepositUtils.js.map +1 -1
  24. package/dist/cjs/utils/NetworkUtils.d.ts +0 -1
  25. package/dist/cjs/utils/NetworkUtils.js +1 -6
  26. package/dist/cjs/utils/NetworkUtils.js.map +1 -1
  27. package/dist/cjs/utils/SpokeUtils.js +3 -3
  28. package/dist/cjs/utils/SpokeUtils.js.map +1 -1
  29. package/dist/esm/clients/BundleDataClient/BundleDataClient.d.ts +4 -5
  30. package/dist/esm/clients/BundleDataClient/BundleDataClient.js +181 -305
  31. package/dist/esm/clients/BundleDataClient/BundleDataClient.js.map +1 -1
  32. package/dist/esm/clients/BundleDataClient/utils/FillUtils.d.ts +1 -3
  33. package/dist/esm/clients/BundleDataClient/utils/FillUtils.js +1 -42
  34. package/dist/esm/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
  35. package/dist/esm/clients/SpokePoolClient.d.ts +0 -8
  36. package/dist/esm/clients/SpokePoolClient.js +4 -20
  37. package/dist/esm/clients/SpokePoolClient.js.map +1 -1
  38. package/dist/esm/constants.d.ts +0 -1
  39. package/dist/esm/constants.js +1 -2
  40. package/dist/esm/constants.js.map +1 -1
  41. package/dist/esm/providers/index.d.ts +0 -1
  42. package/dist/esm/providers/index.js +0 -2
  43. package/dist/esm/providers/index.js.map +1 -1
  44. package/dist/esm/utils/AddressUtils.d.ts +0 -1
  45. package/dist/esm/utils/AddressUtils.js +0 -16
  46. package/dist/esm/utils/AddressUtils.js.map +1 -1
  47. package/dist/esm/utils/CachingUtils.js +1 -1
  48. package/dist/esm/utils/CachingUtils.js.map +1 -1
  49. package/dist/esm/utils/DepositUtils.d.ts +1 -2
  50. package/dist/esm/utils/DepositUtils.js +3 -12
  51. package/dist/esm/utils/DepositUtils.js.map +1 -1
  52. package/dist/esm/utils/NetworkUtils.d.ts +0 -6
  53. package/dist/esm/utils/NetworkUtils.js +0 -10
  54. package/dist/esm/utils/NetworkUtils.js.map +1 -1
  55. package/dist/esm/utils/SpokeUtils.js +4 -4
  56. package/dist/esm/utils/SpokeUtils.js.map +1 -1
  57. package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts +4 -5
  58. package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts.map +1 -1
  59. package/dist/types/clients/BundleDataClient/utils/FillUtils.d.ts +1 -3
  60. package/dist/types/clients/BundleDataClient/utils/FillUtils.d.ts.map +1 -1
  61. package/dist/types/clients/SpokePoolClient.d.ts +0 -8
  62. package/dist/types/clients/SpokePoolClient.d.ts.map +1 -1
  63. package/dist/types/constants.d.ts +0 -1
  64. package/dist/types/constants.d.ts.map +1 -1
  65. package/dist/types/providers/index.d.ts +0 -1
  66. package/dist/types/providers/index.d.ts.map +1 -1
  67. package/dist/types/utils/AddressUtils.d.ts +0 -1
  68. package/dist/types/utils/AddressUtils.d.ts.map +1 -1
  69. package/dist/types/utils/DepositUtils.d.ts +1 -2
  70. package/dist/types/utils/DepositUtils.d.ts.map +1 -1
  71. package/dist/types/utils/NetworkUtils.d.ts +0 -6
  72. package/dist/types/utils/NetworkUtils.d.ts.map +1 -1
  73. package/dist/types/utils/SpokeUtils.d.ts.map +1 -1
  74. package/package.json +1 -1
  75. package/src/clients/BundleDataClient/BundleDataClient.ts +132 -251
  76. package/src/clients/BundleDataClient/utils/FillUtils.ts +2 -47
  77. package/src/clients/SpokePoolClient.ts +6 -19
  78. package/src/constants.ts +1 -3
  79. package/src/providers/index.ts +0 -1
  80. package/src/utils/AddressUtils.ts +0 -16
  81. package/src/utils/CachingUtils.ts +1 -1
  82. package/src/utils/DepositUtils.ts +3 -12
  83. package/src/utils/NetworkUtils.ts +0 -11
  84. package/src/utils/SpokeUtils.ts +5 -6
  85. package/dist/cjs/providers/mockProvider.d.ts +0 -19
  86. package/dist/cjs/providers/mockProvider.js +0 -70
  87. package/dist/cjs/providers/mockProvider.js.map +0 -1
  88. package/dist/esm/providers/mockProvider.d.ts +0 -23
  89. package/dist/esm/providers/mockProvider.js +0 -73
  90. package/dist/esm/providers/mockProvider.js.map +0 -1
  91. package/dist/types/providers/mockProvider.d.ts +0 -24
  92. package/dist/types/providers/mockProvider.d.ts.map +0 -1
  93. package/src/providers/mockProvider.ts +0 -77
@@ -33,13 +33,10 @@ import {
33
33
  getImpliedBundleBlockRanges,
34
34
  isSlowFill,
35
35
  mapAsync,
36
- filterAsync,
37
36
  bnUint32Max,
38
37
  isZeroValueDeposit,
39
38
  findFillEvent,
40
39
  isZeroValueFillOrSlowFillRequest,
41
- chainIsEvm,
42
- isValidEvmAddress,
43
40
  } from "../../utils";
44
41
  import winston from "winston";
45
42
  import {
@@ -54,9 +51,7 @@ import {
54
51
  prettyPrintV3SpokePoolEvents,
55
52
  V3DepositWithBlock,
56
53
  V3FillWithBlock,
57
- verifyFillRepayment,
58
54
  } from "./utils";
59
- import { PRE_FILL_MIN_CONFIG_STORE_VERSION } from "../../constants";
60
55
 
61
56
  // max(uint256) - 1
62
57
  export const INFINITE_FILL_DEADLINE = bnUint32Max;
@@ -65,10 +60,6 @@ type DataCache = Record<string, Promise<LoadDataReturnValue>>;
65
60
 
66
61
  // V3 dictionary helper functions
67
62
  function updateExpiredDepositsV3(dict: ExpiredDepositsToRefundV3, deposit: V3DepositWithBlock): void {
68
- // A deposit refund for a deposit is invalid if the depositor has a bytes32 address input for an EVM chain. It is valid otherwise.
69
- if (chainIsEvm(deposit.originChainId) && !isValidEvmAddress(deposit.depositor)) {
70
- return;
71
- }
72
63
  const { originChainId, inputToken } = deposit;
73
64
  if (!dict?.[originChainId]?.[inputToken]) {
74
65
  assign(dict, [originChainId, inputToken], []);
@@ -89,13 +80,8 @@ function updateBundleFillsV3(
89
80
  fill: V3FillWithBlock,
90
81
  lpFeePct: BigNumber,
91
82
  repaymentChainId: number,
92
- repaymentToken: string,
93
- repaymentAddress: string
83
+ repaymentToken: string
94
84
  ): void {
95
- // It is impossible to refund a deposit if the repayment chain is EVM and the relayer is a non-evm address.
96
- if (chainIsEvm(repaymentChainId) && !isValidEvmAddress(repaymentAddress)) {
97
- return;
98
- }
99
85
  if (!dict?.[repaymentChainId]?.[repaymentToken]) {
100
86
  assign(dict, [repaymentChainId, repaymentToken], {
101
87
  fills: [],
@@ -105,19 +91,19 @@ function updateBundleFillsV3(
105
91
  });
106
92
  }
107
93
 
108
- const bundleFill: BundleFillV3 = { ...fill, lpFeePct, relayer: repaymentAddress };
94
+ const bundleFill: BundleFillV3 = { ...fill, lpFeePct };
109
95
 
110
96
  // Add all fills, slow and fast, to dictionary.
111
97
  assign(dict, [repaymentChainId, repaymentToken, "fills"], [bundleFill]);
112
98
 
113
99
  // All fills update the bundle LP fees.
114
100
  const refundObj = dict[repaymentChainId][repaymentToken];
115
- const realizedLpFee = bundleFill.inputAmount.mul(bundleFill.lpFeePct).div(fixedPointAdjustment);
101
+ const realizedLpFee = fill.inputAmount.mul(bundleFill.lpFeePct).div(fixedPointAdjustment);
116
102
  refundObj.realizedLpFees = refundObj.realizedLpFees ? refundObj.realizedLpFees.add(realizedLpFee) : realizedLpFee;
117
103
 
118
104
  // Only fast fills get refunded.
119
- if (!isSlowFill(bundleFill)) {
120
- const refundAmount = bundleFill.inputAmount.mul(fixedPointAdjustment.sub(lpFeePct)).div(fixedPointAdjustment);
105
+ if (!isSlowFill(fill)) {
106
+ const refundAmount = fill.inputAmount.mul(fixedPointAdjustment.sub(lpFeePct)).div(fixedPointAdjustment);
121
107
  refundObj.totalRefundAmount = refundObj.totalRefundAmount
122
108
  ? refundObj.totalRefundAmount.add(refundAmount)
123
109
  : refundAmount;
@@ -125,10 +111,10 @@ function updateBundleFillsV3(
125
111
  // Instantiate dictionary if it doesn't exist.
126
112
  refundObj.refunds ??= {};
127
113
 
128
- if (refundObj.refunds[bundleFill.relayer]) {
129
- refundObj.refunds[bundleFill.relayer] = refundObj.refunds[bundleFill.relayer].add(refundAmount);
114
+ if (refundObj.refunds[fill.relayer]) {
115
+ refundObj.refunds[fill.relayer] = refundObj.refunds[fill.relayer].add(refundAmount);
130
116
  } else {
131
- refundObj.refunds[bundleFill.relayer] = refundAmount;
117
+ refundObj.refunds[fill.relayer] = refundAmount;
132
118
  }
133
119
  }
134
120
  }
@@ -296,7 +282,7 @@ export class BundleDataClient {
296
282
  // so as not to affect this approximate refund count.
297
283
  const arweaveData = await this.loadArweaveData(bundleEvaluationBlockRanges);
298
284
  if (arweaveData === undefined) {
299
- combinedRefunds = await this.getApproximateRefundsForBlockRange(chainIds, bundleEvaluationBlockRanges);
285
+ combinedRefunds = this.getApproximateRefundsForBlockRange(chainIds, bundleEvaluationBlockRanges);
300
286
  } else {
301
287
  const { bundleFillsV3, expiredDepositsToRefundV3 } = arweaveData;
302
288
  combinedRefunds = getRefundsFromBundle(bundleFillsV3, expiredDepositsToRefundV3);
@@ -317,72 +303,50 @@ export class BundleDataClient {
317
303
  }
318
304
 
319
305
  // @dev This helper function should probably be moved to the InventoryClient
320
- async getApproximateRefundsForBlockRange(chainIds: number[], blockRanges: number[][]): Promise<CombinedRefunds> {
306
+ getApproximateRefundsForBlockRange(chainIds: number[], blockRanges: number[][]): CombinedRefunds {
321
307
  const refundsForChain: CombinedRefunds = {};
322
308
  for (const chainId of chainIds) {
323
309
  if (this.spokePoolClients[chainId] === undefined) {
324
310
  continue;
325
311
  }
326
312
  const chainIndex = chainIds.indexOf(chainId);
327
- // @todo This function does not account for pre-fill refunds as it is optimized for speed. The way to detect
328
- // pre-fill refunds is to load all deposits that are unmatched by fills in the spoke pool client's memory
329
- // and then query the FillStatus on-chain, but that might slow this function down too much. For now, we
330
- // will live with this expected inaccuracy as it should be small. The pre-fill would have to precede the deposit
331
- // by more than the caller's event lookback window which is expected to be unlikely.
332
- const fillsToCount = await filterAsync(this.spokePoolClients[chainId].getFills(), async (fill) => {
333
- if (
334
- fill.blockNumber < blockRanges[chainIndex][0] ||
335
- fill.blockNumber > blockRanges[chainIndex][1] ||
336
- isZeroValueFillOrSlowFillRequest(fill)
337
- ) {
338
- return false;
339
- }
313
+ this.spokePoolClients[chainId]
314
+ .getFills()
315
+ .filter((fill) => {
316
+ if (fill.blockNumber < blockRanges[chainIndex][0] || fill.blockNumber > blockRanges[chainIndex][1]) {
317
+ return false;
318
+ }
340
319
 
341
- // If origin spoke pool client isn't defined, we can't validate it.
342
- if (this.spokePoolClients[fill.originChainId] === undefined) {
343
- return false;
344
- }
345
- const matchingDeposit = this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
346
- const hasMatchingDeposit =
347
- matchingDeposit !== undefined &&
348
- this.getRelayHashFromEvent(fill) === this.getRelayHashFromEvent(matchingDeposit);
349
- if (hasMatchingDeposit) {
350
- const validRepayment = await verifyFillRepayment(
351
- fill,
352
- this.spokePoolClients[fill.destinationChainId].spokePool.provider,
353
- matchingDeposit,
354
- // @dev: to get valid repayment chain ID's, get all chain IDs for the bundle block range and remove
355
- // disabled block ranges.
356
- this.clients.configStoreClient
357
- .getChainIdIndicesForBlock(blockRanges[0][1])
358
- .filter((_chainId, i) => !isChainDisabled(blockRanges[i]))
359
- );
360
- if (!isDefined(validRepayment)) {
320
+ // If origin spoke pool client isn't defined, we can't validate it.
321
+ if (this.spokePoolClients[fill.originChainId] === undefined) {
361
322
  return false;
362
323
  }
363
- }
364
- return hasMatchingDeposit;
365
- });
366
- fillsToCount.forEach((fill) => {
367
- const matchingDeposit = this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
368
- assert(isDefined(matchingDeposit), "Deposit not found for fill.");
369
- const { chainToSendRefundTo, repaymentToken } = getRefundInformationFromFill(
370
- fill,
371
- this.clients.hubPoolClient,
372
- blockRanges,
373
- this.chainIdListForBundleEvaluationBlockNumbers,
374
- matchingDeposit!.fromLiteChain // Use ! because we've already asserted that matchingDeposit is defined.
375
- );
376
- // Assume that lp fees are 0 for the sake of speed. In the future we could batch compute
377
- // these or make hardcoded assumptions based on the origin-repayment chain direction. This might result
378
- // in slight over estimations of refunds, but its not clear whether underestimating or overestimating is
379
- // worst from the relayer's perspective.
380
- const { relayer, inputAmount: refundAmount } = fill;
381
- refundsForChain[chainToSendRefundTo] ??= {};
382
- refundsForChain[chainToSendRefundTo][repaymentToken] ??= {};
383
- const existingRefundAmount = refundsForChain[chainToSendRefundTo][repaymentToken][relayer] ?? bnZero;
384
- refundsForChain[chainToSendRefundTo][repaymentToken][relayer] = existingRefundAmount.add(refundAmount);
385
- });
324
+ const matchingDeposit = this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
325
+ const hasMatchingDeposit =
326
+ matchingDeposit !== undefined &&
327
+ this.getRelayHashFromEvent(fill) === this.getRelayHashFromEvent(matchingDeposit);
328
+ return hasMatchingDeposit;
329
+ })
330
+ .forEach((fill) => {
331
+ const matchingDeposit = this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
332
+ assert(isDefined(matchingDeposit), "Deposit not found for fill.");
333
+ const { chainToSendRefundTo, repaymentToken } = getRefundInformationFromFill(
334
+ fill,
335
+ this.clients.hubPoolClient,
336
+ blockRanges,
337
+ this.chainIdListForBundleEvaluationBlockNumbers,
338
+ matchingDeposit!.fromLiteChain // Use ! because we've already asserted that matchingDeposit is defined.
339
+ );
340
+ // Assume that lp fees are 0 for the sake of speed. In the future we could batch compute
341
+ // these or make hardcoded assumptions based on the origin-repayment chain direction. This might result
342
+ // in slight over estimations of refunds, but its not clear whether underestimating or overestimating is
343
+ // worst from the relayer's perspective.
344
+ const { relayer, inputAmount: refundAmount } = fill;
345
+ refundsForChain[chainToSendRefundTo] ??= {};
346
+ refundsForChain[chainToSendRefundTo][repaymentToken] ??= {};
347
+ const existingRefundAmount = refundsForChain[chainToSendRefundTo][repaymentToken][relayer] ?? bnZero;
348
+ refundsForChain[chainToSendRefundTo][repaymentToken][relayer] = existingRefundAmount.add(refundAmount);
349
+ });
386
350
  }
387
351
  return refundsForChain;
388
352
  }
@@ -509,7 +473,7 @@ export class BundleDataClient {
509
473
  // ok for this use case.
510
474
  const arweaveData = await this.loadArweaveData(pendingBundleBlockRanges);
511
475
  if (arweaveData === undefined) {
512
- combinedRefunds.push(await this.getApproximateRefundsForBlockRange(chainIds, pendingBundleBlockRanges));
476
+ combinedRefunds.push(this.getApproximateRefundsForBlockRange(chainIds, pendingBundleBlockRanges));
513
477
  } else {
514
478
  const { bundleFillsV3, expiredDepositsToRefundV3 } = arweaveData;
515
479
  combinedRefunds.push(getRefundsFromBundle(bundleFillsV3, expiredDepositsToRefundV3));
@@ -524,7 +488,7 @@ export class BundleDataClient {
524
488
  // - Only look up fills sent after the pending bundle's end blocks
525
489
  // - Skip LP fee computations and just assume the relayer is being refunded the full deposit.inputAmount
526
490
  const start = performance.now();
527
- combinedRefunds.push(await this.getApproximateRefundsForBlockRange(chainIds, widestBundleBlockRanges));
491
+ combinedRefunds.push(this.getApproximateRefundsForBlockRange(chainIds, widestBundleBlockRanges));
528
492
  this.logger.debug({
529
493
  at: "BundleDataClient#getNextBundleRefunds",
530
494
  message: `Loading approximate refunds for next bundle in ${Math.round(performance.now() - start) / 1000}s.`,
@@ -722,10 +686,6 @@ export class BundleDataClient {
722
686
  );
723
687
  };
724
688
 
725
- const _depositIsExpired = (deposit: DepositWithBlock): boolean => {
726
- return deposit.fillDeadline < bundleBlockTimestamps[deposit.destinationChainId][1];
727
- };
728
-
729
689
  const _getFillStatusForDeposit = (deposit: Deposit, queryBlock: number): Promise<FillStatus> => {
730
690
  return spokePoolClients[deposit.destinationChainId].relayFillStatus(
731
691
  deposit,
@@ -788,22 +748,6 @@ export class BundleDataClient {
788
748
  const bundleDepositHashes: string[] = [];
789
749
  const olderDepositHashes: string[] = [];
790
750
 
791
- // We use the following toggle to aid with the migration to pre-fills. The first bundle proposed using this
792
- // pre-fill logic can double refund pre-fills that have already been filled in the last bundle, because the
793
- // last bundle did not recognize a fill as a pre-fill. Therefore the developer should ensure that the version
794
- // is bumped to the PRE_FILL_MIN_CONFIG_STORE_VERSION version before the first pre-fill bundle is proposed.
795
- // To test the following bundle after this, the developer can set the FORCE_REFUND_PREFILLS environment variable
796
- // to "true" simulate the bundle with pre-fill refunds.
797
- // @todo Remove this logic once we have advanced sufficiently past the pre-fill migration.
798
- const startBlockForMainnet = getBlockRangeForChain(
799
- blockRangesForChains,
800
- this.clients.hubPoolClient.chainId,
801
- this.chainIdListForBundleEvaluationBlockNumbers
802
- )[0];
803
- const versionAtProposalBlock = this.clients.configStoreClient.getConfigStoreVersionForBlock(startBlockForMainnet);
804
- const canRefundPrefills =
805
- versionAtProposalBlock >= PRE_FILL_MIN_CONFIG_STORE_VERSION || process.env.FORCE_REFUND_PREFILLS === "true";
806
-
807
751
  let depositCounter = 0;
808
752
  for (const originChainId of allChainIds) {
809
753
  const originClient = spokePoolClients[originChainId];
@@ -817,13 +761,11 @@ export class BundleDataClient {
817
761
  // Only evaluate deposits that are in this bundle or in previous bundles. This means we cannot issue fill
818
762
  // refunds or slow fills here for deposits that are in future bundles (i.e. "pre-fills"). Instead, we'll
819
763
  // evaluate these pre-fills once the deposit is inside the "current" bundle block range.
820
- if (deposit.blockNumber > originChainBlockRange[1] || isZeroValueDeposit(deposit)) {
764
+ if (isZeroValueDeposit(deposit) || deposit.blockNumber > originChainBlockRange[1]) {
821
765
  return;
822
766
  }
823
767
  depositCounter++;
824
768
  const relayDataHash = this.getRelayHashFromEvent(deposit);
825
-
826
- // Duplicate deposits are treated like normal deposits.
827
769
  if (!v3RelayHashes[relayDataHash]) {
828
770
  v3RelayHashes[relayDataHash] = {
829
771
  deposit: deposit,
@@ -840,8 +782,6 @@ export class BundleDataClient {
840
782
  return;
841
783
  }
842
784
 
843
- // Evaluate all expired deposits after fetching fill statuses,
844
- // since we can't know for certain whether an expired deposit was filled a long time ago.
845
785
  if (deposit.blockNumber >= originChainBlockRange[0]) {
846
786
  bundleDepositHashes.push(relayDataHash);
847
787
  updateBundleDepositsV3(bundleDepositsV3, deposit);
@@ -885,8 +825,9 @@ export class BundleDataClient {
885
825
  (fill) => fill.blockNumber <= destinationChainBlockRange[1] && !isZeroValueFillOrSlowFillRequest(fill)
886
826
  ),
887
827
  async (fill) => {
888
- fillCounter++;
889
828
  const relayDataHash = this.getRelayHashFromEvent(fill);
829
+ fillCounter++;
830
+
890
831
  if (v3RelayHashes[relayDataHash]) {
891
832
  if (!v3RelayHashes[relayDataHash].fill) {
892
833
  assert(
@@ -894,60 +835,34 @@ export class BundleDataClient {
894
835
  "Deposit should exist in relay hash dictionary."
895
836
  );
896
837
  // At this point, the v3RelayHashes entry already existed meaning that there is a matching deposit,
897
- // so this fill can no longer be filled on-chain.
838
+ // so this fill is validated.
898
839
  v3RelayHashes[relayDataHash].fill = fill;
899
840
  if (fill.blockNumber >= destinationChainBlockRange[0]) {
900
- // `fill` will only possibly differ from `_fill` in the `relayer` field, which does not affect the
901
- // relay hash, so it is safe to modify.
902
- const fillToRefund = await verifyFillRepayment(
903
- fill,
904
- destinationClient.spokePool.provider,
905
- v3RelayHashes[relayDataHash].deposit!,
906
- allChainIds
907
- );
908
- if (!isDefined(fillToRefund)) {
909
- bundleInvalidFillsV3.push(fill);
910
- // We don't return here yet because we still need to mark unexecutable slow fill leaves
911
- // or duplicate deposits. However, we won't issue a fast fill refund.
912
- } else {
913
- v3RelayHashes[relayDataHash].fill = fillToRefund;
914
- validatedBundleV3Fills.push({
915
- ...fillToRefund,
916
- quoteTimestamp: v3RelayHashes[relayDataHash].deposit!.quoteTimestamp, // ! due to assert above
917
- });
918
- }
919
-
841
+ validatedBundleV3Fills.push({
842
+ ...fill,
843
+ quoteTimestamp: v3RelayHashes[relayDataHash].deposit!.quoteTimestamp, // ! due to assert above
844
+ });
920
845
  // If fill replaced a slow fill request, then mark it as one that might have created an
921
846
  // unexecutable slow fill. We can't know for sure until we check the slow fill request
922
847
  // events.
923
848
  // slow fill requests for deposits from or to lite chains are considered invalid
924
849
  if (
925
850
  fill.relayExecutionInfo.fillType === FillType.ReplacedSlowFill &&
926
- _canCreateSlowFillLeaf(v3RelayHashes[relayDataHash].deposit!)
851
+ !v3RelayHashes[relayDataHash].deposit!.fromLiteChain &&
852
+ !v3RelayHashes[relayDataHash].deposit!.toLiteChain
927
853
  ) {
928
854
  fastFillsReplacingSlowFills.push(relayDataHash);
929
855
  }
930
- // Now that know this deposit has been filled on-chain, identify any duplicate deposits sent for this fill and refund
931
- // them, because they would not be refunded otherwise. These deposits can no longer expire and get
932
- // refunded as an expired deposit, and they won't trigger a pre-fill refund because the fill is
933
- // in this bundle. Pre-fill refunds only happen when deposits are sent in this bundle and the
934
- // fill is from a prior bundle.
935
- const duplicateDeposits = originClient.getDuplicateDeposits(v3RelayHashes[relayDataHash].deposit!);
936
- duplicateDeposits.forEach((duplicateDeposit) => {
937
- updateExpiredDepositsV3(expiredDepositsToRefundV3, duplicateDeposit);
938
- });
939
856
  }
940
- } else {
941
- throw new Error("Duplicate fill detected.");
942
857
  }
943
858
  return;
944
859
  }
945
860
 
946
861
  // At this point, there is no relay hash dictionary entry for this fill, so we need to
947
- // instantiate the entry. We won't modify the fill.relayer until we match it with a deposit.
862
+ // instantiate the entry.
948
863
  v3RelayHashes[relayDataHash] = {
949
864
  deposit: undefined,
950
- fill,
865
+ fill: fill,
951
866
  slowFillRequest: undefined,
952
867
  };
953
868
 
@@ -975,42 +890,24 @@ export class BundleDataClient {
975
890
  bundleInvalidFillsV3.push(fill);
976
891
  } else {
977
892
  const matchedDeposit = historicalDeposit.deposit;
893
+ // @dev Since queryHistoricalDepositForFill validates the fill by checking individual
894
+ // object property values against the deposit's, we
895
+ // sanity check it here by comparing the full relay hashes. If there's an error here then the
896
+ // historical deposit query is not working as expected.
897
+ assert(this.getRelayHashFromEvent(matchedDeposit) === relayDataHash, "Relay hashes should match.");
898
+ validatedBundleV3Fills.push({
899
+ ...fill,
900
+ quoteTimestamp: matchedDeposit.quoteTimestamp,
901
+ });
978
902
  v3RelayHashes[relayDataHash].deposit = matchedDeposit;
979
-
980
- const fillToRefund = await verifyFillRepayment(
981
- fill,
982
- destinationClient.spokePool.provider,
983
- matchedDeposit,
984
- allChainIds
985
- );
986
- if (!isDefined(fillToRefund)) {
987
- bundleInvalidFillsV3.push(fill);
988
- // Don't return yet as we still need to mark down any unexecutable slow fill leaves
989
- // in case this fast fill replaced a slow fill request.
990
- } else {
991
- // @dev Since queryHistoricalDepositForFill validates the fill by checking individual
992
- // object property values against the deposit's, we
993
- // sanity check it here by comparing the full relay hashes. If there's an error here then the
994
- // historical deposit query is not working as expected.
995
- assert(this.getRelayHashFromEvent(matchedDeposit) === relayDataHash, "Relay hashes should match.");
996
- validatedBundleV3Fills.push({
997
- ...fill,
998
- quoteTimestamp: matchedDeposit.quoteTimestamp,
999
- });
1000
- v3RelayHashes[relayDataHash].fill = fillToRefund;
1001
- }
1002
-
1003
903
  // slow fill requests for deposits from or to lite chains are considered invalid
1004
904
  if (
1005
905
  fill.relayExecutionInfo.fillType === FillType.ReplacedSlowFill &&
1006
- _canCreateSlowFillLeaf(matchedDeposit)
906
+ !matchedDeposit.fromLiteChain &&
907
+ !matchedDeposit.toLiteChain
1007
908
  ) {
1008
909
  fastFillsReplacingSlowFills.push(relayDataHash);
1009
910
  }
1010
-
1011
- // No need to check for duplicate deposits here since we would have seen them in memory if they
1012
- // had a non-infinite fill deadline, and duplicate deposits with infinite deadlines are impossible
1013
- // to send.
1014
911
  }
1015
912
  }
1016
913
  }
@@ -1035,9 +932,7 @@ export class BundleDataClient {
1035
932
  v3RelayHashes[relayDataHash].slowFillRequest = slowFillRequest;
1036
933
  if (v3RelayHashes[relayDataHash].fill) {
1037
934
  // If there is a fill matching the relay hash, then this slow fill request can't be used
1038
- // to create a slow fill for a filled deposit. This takes advantage of the fact that
1039
- // slow fill requests must precede fills, so if there is a matching fill for this request's
1040
- // relay data, then this slow fill will be unexecutable.
935
+ // to create a slow fill for a filled deposit.
1041
936
  return;
1042
937
  }
1043
938
  assert(
@@ -1046,22 +941,22 @@ export class BundleDataClient {
1046
941
  );
1047
942
  // The ! is safe here because we've already checked that the deposit exists in the relay hash dictionary.
1048
943
  const matchedDeposit = v3RelayHashes[relayDataHash].deposit!;
944
+ if (!_canCreateSlowFillLeaf(matchedDeposit)) {
945
+ return;
946
+ }
1049
947
 
1050
948
  // If there is no fill matching the relay hash, then this might be a valid slow fill request
1051
949
  // that we should produce a slow fill leaf for. Check if the slow fill request is in the
1052
950
  // destination chain block range.
1053
951
  if (
1054
952
  slowFillRequest.blockNumber >= destinationChainBlockRange[0] &&
1055
- _canCreateSlowFillLeaf(matchedDeposit) &&
1056
953
  // Deposit must not have expired in this bundle.
1057
- !_depositIsExpired(matchedDeposit)
954
+ slowFillRequest.fillDeadline >= bundleBlockTimestamps[destinationChainId][1]
1058
955
  ) {
1059
956
  // At this point, the v3RelayHashes entry already existed meaning that there is a matching deposit,
1060
957
  // so this slow fill request relay data is correct.
1061
958
  validatedBundleSlowFills.push(matchedDeposit);
1062
959
  }
1063
- } else {
1064
- throw new Error("Duplicate slow fill request detected.");
1065
960
  }
1066
961
  return;
1067
962
  }
@@ -1108,7 +1003,7 @@ export class BundleDataClient {
1108
1003
  if (
1109
1004
  !_canCreateSlowFillLeaf(matchedDeposit) ||
1110
1005
  // Deposit must not have expired in this bundle.
1111
- _depositIsExpired(matchedDeposit)
1006
+ slowFillRequest.fillDeadline < bundleBlockTimestamps[destinationChainId][1]
1112
1007
  ) {
1113
1008
  return;
1114
1009
  }
@@ -1124,19 +1019,21 @@ export class BundleDataClient {
1124
1019
  // - Or, has the deposit expired in this bundle? If so, then we need to issue an expiry refund.
1125
1020
  // - And finally, has the deposit been slow filled? If so, then we need to issue a slow fill leaf
1126
1021
  // for this "pre-slow-fill-request" if this request took place in a previous bundle.
1022
+ const originBlockRange = getBlockRangeForChain(blockRangesForChains, originChainId, chainIds);
1127
1023
 
1128
- // @todo Only start refunding pre-fills and slow fill requests after a config store version is activated. We
1129
- // should remove this check once we've advanced far beyond the version bump block.
1130
1024
  await mapAsync(
1131
1025
  bundleDepositHashes.filter((depositHash) => {
1132
1026
  const { deposit } = v3RelayHashes[depositHash];
1133
1027
  return (
1134
- deposit && deposit.originChainId === originChainId && deposit.destinationChainId === destinationChainId
1028
+ deposit &&
1029
+ deposit.originChainId === originChainId &&
1030
+ deposit.destinationChainId === destinationChainId &&
1031
+ deposit.blockNumber >= originBlockRange[0] &&
1032
+ deposit.blockNumber <= originBlockRange[1] &&
1033
+ !isZeroValueDeposit(deposit)
1135
1034
  );
1136
1035
  }),
1137
1036
  async (depositHash) => {
1138
- // We don't need to call verifyFillRepayment() here to replace the fill.relayer because this value should already
1139
- // be overwritten because the deposit and fill both exist.
1140
1037
  const { deposit, fill, slowFillRequest } = v3RelayHashes[depositHash];
1141
1038
  if (!deposit) throw new Error("Deposit should exist in relay hash dictionary.");
1142
1039
 
@@ -1149,7 +1046,7 @@ export class BundleDataClient {
1149
1046
  // If fill exists in memory, then the only case in which we need to create a refund is if the
1150
1047
  // the fill occurred in a previous bundle. There are no expiry refunds for filled deposits.
1151
1048
  if (fill) {
1152
- if (canRefundPrefills && fill.blockNumber < destinationChainBlockRange[0] && !isSlowFill(fill)) {
1049
+ if (!isSlowFill(fill) && fill.blockNumber < destinationChainBlockRange[0]) {
1153
1050
  // If fill is in the current bundle then we can assume there is already a refund for it, so only
1154
1051
  // include this pre fill if the fill is in an older bundle. If fill is after this current bundle, then
1155
1052
  // we won't consider it, following the previous treatment of fills after the bundle block range.
@@ -1157,11 +1054,6 @@ export class BundleDataClient {
1157
1054
  ...fill,
1158
1055
  quoteTimestamp: deposit.quoteTimestamp,
1159
1056
  });
1160
-
1161
- // We don't refund duplicate deposits for pre-fill refunds because we are refunding the pre-fill instead
1162
- // using the duplicate deposited funds. We make an assumption that duplicate deposits for pre-fills
1163
- // are highly unlikely because deposits for pre-fills are designed to be sent by the pre-filler,
1164
- // so the depositor's approval should protect them from the pre-filler sending multiple deposits.
1165
1057
  }
1166
1058
  return;
1167
1059
  }
@@ -1172,12 +1064,11 @@ export class BundleDataClient {
1172
1064
  // we need to create a slow fill leaf for the deposit. The latter should only happen if the slow fill request
1173
1065
  // took place in a prior bundle otherwise we would have already created a slow fill leaf for it.
1174
1066
  if (slowFillRequest) {
1175
- if (_depositIsExpired(deposit)) {
1067
+ if (deposit.fillDeadline < bundleBlockTimestamps[destinationChainId][1]) {
1176
1068
  updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
1177
1069
  } else if (
1178
- canRefundPrefills &&
1179
- slowFillRequest.blockNumber < destinationChainBlockRange[0] &&
1180
- _canCreateSlowFillLeaf(deposit)
1070
+ _canCreateSlowFillLeaf(deposit) &&
1071
+ slowFillRequest.blockNumber < destinationChainBlockRange[0]
1181
1072
  ) {
1182
1073
  validatedBundleSlowFills.push(deposit);
1183
1074
  }
@@ -1195,18 +1086,15 @@ export class BundleDataClient {
1195
1086
  if (fillStatus === FillStatus.Filled) {
1196
1087
  // We need to find the fill event to issue a refund to the right relayer and repayment chain,
1197
1088
  // or msg.sender if relayer address is invalid for the repayment chain.
1198
- const prefill = await this.findMatchingFillEvent(deposit, destinationClient);
1199
- assert(isDefined(prefill), `findFillEvent# Cannot find prefill: ${depositHash}`);
1200
- assert(this.getRelayHashFromEvent(prefill!) === depositHash, "Relay hashes should match.");
1201
- const verifiedFill = await verifyFillRepayment(
1202
- prefill!,
1203
- destinationClient.spokePool.provider,
1089
+ const prefill = (await findFillEvent(
1090
+ destinationClient.spokePool,
1204
1091
  deposit,
1205
- allChainIds
1206
- );
1207
- if (canRefundPrefills && isDefined(verifiedFill) && !isSlowFill(verifiedFill)) {
1092
+ destinationClient.deploymentBlock,
1093
+ destinationClient.latestBlockSearched
1094
+ )) as unknown as FillWithBlock;
1095
+ if (!isSlowFill(prefill)) {
1208
1096
  validatedBundleV3Fills.push({
1209
- ...verifiedFill!,
1097
+ ...prefill,
1210
1098
  quoteTimestamp: deposit.quoteTimestamp,
1211
1099
  });
1212
1100
  }
@@ -1215,14 +1103,14 @@ export class BundleDataClient {
1215
1103
  // We don't check that fillDeadline >= bundleBlockTimestamps[destinationChainId][0] because
1216
1104
  // that would eliminate any deposits in this bundle with a very low fillDeadline like equal to 0
1217
1105
  // for example. Those should be included in this bundle of refunded deposits.
1218
- else if (_depositIsExpired(deposit)) {
1106
+ else if (deposit.fillDeadline < bundleBlockTimestamps[destinationChainId][1]) {
1219
1107
  updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
1220
1108
  }
1221
1109
  // If slow fill requested, then issue a slow fill leaf for the deposit.
1222
1110
  else if (fillStatus === FillStatus.RequestedSlowFill) {
1223
1111
  // Input and Output tokens must be equivalent on the deposit for this to be slow filled.
1224
1112
  // Slow fill requests for deposits from or to lite chains are considered invalid
1225
- if (canRefundPrefills && _canCreateSlowFillLeaf(deposit)) {
1113
+ if (_canCreateSlowFillLeaf(deposit)) {
1226
1114
  // If deposit newly expired, then we can't create a slow fill leaf for it but we can
1227
1115
  // create a deposit refund for it.
1228
1116
  validatedBundleSlowFills.push(deposit);
@@ -1246,15 +1134,24 @@ export class BundleDataClient {
1246
1134
  }
1247
1135
  // We should never push fast fills involving lite chains here because slow fill requests for them are invalid:
1248
1136
  assert(
1249
- _canCreateSlowFillLeaf(deposit),
1250
- "fastFillsReplacingSlowFills should contain only deposits that can be slow filled"
1137
+ !deposit.fromLiteChain && !deposit.toLiteChain,
1138
+ "fastFillsReplacingSlowFills should not contain lite chain deposits"
1251
1139
  );
1252
1140
  const destinationBlockRange = getBlockRangeForChain(blockRangesForChains, destinationChainId, chainIds);
1253
1141
  if (
1142
+ // If the slow fill request that was replaced by this fill was in an older bundle, then we don't
1143
+ // need to check if the slow fill request was valid since we can assume all bundles in the past
1144
+ // were validated. However, we might as well double check.
1145
+ this.clients.hubPoolClient.areTokensEquivalent(
1146
+ deposit.inputToken,
1147
+ deposit.originChainId,
1148
+ deposit.outputToken,
1149
+ deposit.destinationChainId,
1150
+ deposit.quoteBlockNumber
1151
+ ) &&
1254
1152
  // If there is a slow fill request in this bundle that matches the relay hash, then there was no slow fill
1255
1153
  // created that would be considered excess.
1256
- !slowFillRequest ||
1257
- slowFillRequest.blockNumber < destinationBlockRange[0]
1154
+ (!slowFillRequest || slowFillRequest.blockNumber < destinationBlockRange[0])
1258
1155
  ) {
1259
1156
  validatedBundleUnexecutableSlowFills.push(deposit);
1260
1157
  }
@@ -1283,7 +1180,7 @@ export class BundleDataClient {
1283
1180
  // If there is a valid fill that we saw matching this deposit, then it does not need a refund.
1284
1181
  !fill &&
1285
1182
  isDefined(deposit) && // Needed for TSC - we check this above.
1286
- _depositIsExpired(deposit) &&
1183
+ deposit.fillDeadline < bundleBlockTimestamps[destinationChainId][1] &&
1287
1184
  deposit.fillDeadline >= bundleBlockTimestamps[destinationChainId][0] &&
1288
1185
  spokePoolClients[destinationChainId] !== undefined
1289
1186
  ) {
@@ -1299,7 +1196,8 @@ export class BundleDataClient {
1299
1196
  // If fill status is RequestedSlowFill, then we might need to mark down an unexecutable
1300
1197
  // slow fill that we're going to replace with an expired deposit refund.
1301
1198
  // If deposit cannot be slow filled, then exit early.
1302
- if (fillStatus !== FillStatus.RequestedSlowFill || !_canCreateSlowFillLeaf(deposit)) {
1199
+ // slow fill requests for deposits from or to lite chains are considered invalid
1200
+ if (fillStatus !== FillStatus.RequestedSlowFill || deposit.fromLiteChain || deposit.toLiteChain) {
1303
1201
  return;
1304
1202
  }
1305
1203
  // Now, check if there was a slow fill created for this deposit in a previous bundle which would now be
@@ -1308,9 +1206,21 @@ export class BundleDataClient {
1308
1206
 
1309
1207
  // If there is a slow fill request in this bundle, then the expired deposit refund will supercede
1310
1208
  // the slow fill request. If there is no slow fill request seen or its older than this bundle, then we can
1311
- // assume a slow fill leaf was created for it because of the previous _canCreateSlowFillLeaf check.
1312
- // The slow fill request was also sent before the fill deadline expired since we checked that above.
1313
- if (!slowFillRequest || slowFillRequest.blockNumber < destinationBlockRange[0]) {
1209
+ // assume a slow fill leaf was created for it because its tokens are equivalent. The slow fill request was
1210
+ // also sent before the fill deadline expired since we checked that above.
1211
+ if (
1212
+ // Since this deposit was requested for a slow fill in an older bundle at this point, we don't
1213
+ // technically need to check if the slow fill request was valid since we can assume all bundles in the past
1214
+ // were validated. However, we might as well double check.
1215
+ this.clients.hubPoolClient.areTokensEquivalent(
1216
+ deposit.inputToken,
1217
+ deposit.originChainId,
1218
+ deposit.outputToken,
1219
+ deposit.destinationChainId,
1220
+ deposit.quoteBlockNumber
1221
+ ) &&
1222
+ (!slowFillRequest || slowFillRequest.blockNumber < destinationBlockRange[0])
1223
+ ) {
1314
1224
  validatedBundleUnexecutableSlowFills.push(deposit);
1315
1225
  }
1316
1226
  }
@@ -1375,7 +1285,7 @@ export class BundleDataClient {
1375
1285
  chainIds,
1376
1286
  associatedDeposit!.fromLiteChain
1377
1287
  );
1378
- updateBundleFillsV3(bundleFillsV3, fill, realizedLpFeePct, chainToSendRefundTo, repaymentToken, fill.relayer);
1288
+ updateBundleFillsV3(bundleFillsV3, fill, realizedLpFeePct, chainToSendRefundTo, repaymentToken);
1379
1289
  });
1380
1290
  v3SlowFillLpFees.forEach(({ realizedLpFeePct: lpFeePct }, idx) => {
1381
1291
  const deposit = validatedBundleSlowFills[idx];
@@ -1423,24 +1333,8 @@ export class BundleDataClient {
1423
1333
  // keccak256 hash of the relay data, which can be used as input into the on-chain `fillStatuses()` function in the
1424
1334
  // spoke pool contract. However, this internal function is used to uniquely identify a bridging event
1425
1335
  // for speed since its easier to build a string from the event data than to hash it.
1426
- protected getRelayHashFromEvent(event: V3DepositWithBlock | V3FillWithBlock | SlowFillRequestWithBlock): string {
1427
- return `${event.depositor}-${event.recipient}-${event.exclusiveRelayer}-${event.inputToken}-${event.outputToken}-${
1428
- event.inputAmount
1429
- }-${event.outputAmount}-${event.originChainId}-${event.depositId.toString()}-${event.fillDeadline}-${
1430
- event.exclusivityDeadline
1431
- }-${event.message}-${event.destinationChainId}`;
1432
- }
1433
-
1434
- protected async findMatchingFillEvent(
1435
- deposit: DepositWithBlock,
1436
- spokePoolClient: SpokePoolClient
1437
- ): Promise<FillWithBlock | undefined> {
1438
- return await findFillEvent(
1439
- spokePoolClient.spokePool,
1440
- deposit,
1441
- spokePoolClient.deploymentBlock,
1442
- spokePoolClient.latestBlockSearched
1443
- );
1336
+ private getRelayHashFromEvent(event: V3DepositWithBlock | V3FillWithBlock | SlowFillRequestWithBlock): string {
1337
+ return `${event.depositor}-${event.recipient}-${event.exclusiveRelayer}-${event.inputToken}-${event.outputToken}-${event.inputAmount}-${event.outputAmount}-${event.originChainId}-${event.depositId}-${event.fillDeadline}-${event.exclusivityDeadline}-${event.message}-${event.destinationChainId}`;
1444
1338
  }
1445
1339
 
1446
1340
  async getBundleBlockTimestamps(
@@ -1468,26 +1362,13 @@ export class BundleDataClient {
1468
1362
  // will usually be called in production with block ranges that were validated by
1469
1363
  // DataworkerUtils.blockRangesAreInvalidForSpokeClients.
1470
1364
  const startBlockForChain = Math.min(_startBlockForChain, spokePoolClient.latestBlockSearched);
1471
- // @dev Add 1 to the bundle end block. The thinking here is that there can be a gap between
1472
- // block timestamps in subsequent blocks. The bundle data client assumes that fill deadlines expire
1473
- // in exactly one bundle, therefore we must make sure that the bundle block timestamp for one bundle's
1474
- // end block is exactly equal to the bundle block timestamp for the next bundle's start block. This way
1475
- // there are no gaps in block timestamps between bundles.
1476
- const endBlockForChain = Math.min(_endBlockForChain + 1, spokePoolClient.latestBlockSearched);
1477
- const [startTime, _endTime] = [
1365
+ const endBlockForChain = Math.min(_endBlockForChain, spokePoolClient.latestBlockSearched);
1366
+ const [startTime, endTime] = [
1478
1367
  await spokePoolClient.getTimestampForBlock(startBlockForChain),
1479
1368
  await spokePoolClient.getTimestampForBlock(endBlockForChain),
1480
1369
  ];
1481
- // @dev similar to reasoning above to ensure no gaps between bundle block range timestamps and also
1482
- // no overlap, subtract 1 from the end time.
1483
- const endBlockDelta = endBlockForChain > startBlockForChain ? 1 : 0;
1484
- const endTime = Math.max(0, _endTime - endBlockDelta);
1485
-
1486
1370
  // Sanity checks:
1487
- assert(
1488
- endTime >= startTime,
1489
- `End time for block ${endBlockForChain} should be greater than start time for block ${startBlockForChain}: ${endTime} >= ${startTime}.`
1490
- );
1371
+ assert(endTime >= startTime, "End time should be greater than start time.");
1491
1372
  assert(
1492
1373
  startBlockForChain === 0 || startTime > 0,
1493
1374
  "Start timestamp must be greater than 0 if the start block is greater than 0."