@across-protocol/sdk 4.0.0-beta.3 → 4.0.0-beta.30

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 (132) hide show
  1. package/dist/cjs/clients/BundleDataClient/BundleDataClient.d.ts +5 -4
  2. package/dist/cjs/clients/BundleDataClient/BundleDataClient.js +340 -174
  3. package/dist/cjs/clients/BundleDataClient/BundleDataClient.js.map +1 -1
  4. package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.d.ts +1 -2
  5. package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js +1 -2
  6. package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
  7. package/dist/cjs/clients/BundleDataClient/utils/FillUtils.d.ts +5 -1
  8. package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js +47 -1
  9. package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
  10. package/dist/cjs/clients/BundleDataClient/utils/SuperstructUtils.d.ts +4 -4
  11. package/dist/cjs/clients/SpokePoolClient.d.ts +1 -0
  12. package/dist/cjs/clients/SpokePoolClient.js +13 -4
  13. package/dist/cjs/clients/SpokePoolClient.js.map +1 -1
  14. package/dist/cjs/clients/mocks/MockSpokePoolClient.d.ts +2 -1
  15. package/dist/cjs/clients/mocks/MockSpokePoolClient.js +11 -0
  16. package/dist/cjs/clients/mocks/MockSpokePoolClient.js.map +1 -1
  17. package/dist/cjs/constants.d.ts +1 -1
  18. package/dist/cjs/constants.js +2 -2
  19. package/dist/cjs/constants.js.map +1 -1
  20. package/dist/cjs/providers/index.d.ts +1 -0
  21. package/dist/cjs/providers/index.js +2 -0
  22. package/dist/cjs/providers/index.js.map +1 -1
  23. package/dist/cjs/providers/mockProvider.d.ts +19 -0
  24. package/dist/cjs/providers/mockProvider.js +70 -0
  25. package/dist/cjs/providers/mockProvider.js.map +1 -0
  26. package/dist/cjs/utils/AddressUtils.d.ts +2 -0
  27. package/dist/cjs/utils/AddressUtils.js +19 -1
  28. package/dist/cjs/utils/AddressUtils.js.map +1 -1
  29. package/dist/cjs/utils/CachingUtils.js +1 -1
  30. package/dist/cjs/utils/CachingUtils.js.map +1 -1
  31. package/dist/cjs/utils/DepositUtils.d.ts +2 -1
  32. package/dist/cjs/utils/DepositUtils.js +13 -4
  33. package/dist/cjs/utils/DepositUtils.js.map +1 -1
  34. package/dist/cjs/utils/EventUtils.js +21 -0
  35. package/dist/cjs/utils/EventUtils.js.map +1 -1
  36. package/dist/cjs/utils/NetworkUtils.d.ts +1 -0
  37. package/dist/cjs/utils/NetworkUtils.js +6 -1
  38. package/dist/cjs/utils/NetworkUtils.js.map +1 -1
  39. package/dist/cjs/utils/SpokeUtils.d.ts +1 -0
  40. package/dist/cjs/utils/SpokeUtils.js +18 -11
  41. package/dist/cjs/utils/SpokeUtils.js.map +1 -1
  42. package/dist/cjs/utils/common.d.ts +1 -0
  43. package/dist/cjs/utils/common.js +2 -1
  44. package/dist/cjs/utils/common.js.map +1 -1
  45. package/dist/esm/clients/BundleDataClient/BundleDataClient.d.ts +5 -4
  46. package/dist/esm/clients/BundleDataClient/BundleDataClient.js +410 -208
  47. package/dist/esm/clients/BundleDataClient/BundleDataClient.js.map +1 -1
  48. package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.d.ts +1 -2
  49. package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js +2 -3
  50. package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
  51. package/dist/esm/clients/BundleDataClient/utils/FillUtils.d.ts +5 -1
  52. package/dist/esm/clients/BundleDataClient/utils/FillUtils.js +54 -1
  53. package/dist/esm/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
  54. package/dist/esm/clients/BundleDataClient/utils/SuperstructUtils.d.ts +4 -4
  55. package/dist/esm/clients/SpokePoolClient.d.ts +8 -0
  56. package/dist/esm/clients/SpokePoolClient.js +20 -4
  57. package/dist/esm/clients/SpokePoolClient.js.map +1 -1
  58. package/dist/esm/clients/mocks/MockSpokePoolClient.d.ts +2 -1
  59. package/dist/esm/clients/mocks/MockSpokePoolClient.js +11 -0
  60. package/dist/esm/clients/mocks/MockSpokePoolClient.js.map +1 -1
  61. package/dist/esm/constants.d.ts +1 -1
  62. package/dist/esm/constants.js +2 -2
  63. package/dist/esm/constants.js.map +1 -1
  64. package/dist/esm/providers/index.d.ts +1 -0
  65. package/dist/esm/providers/index.js +2 -0
  66. package/dist/esm/providers/index.js.map +1 -1
  67. package/dist/esm/providers/mockProvider.d.ts +23 -0
  68. package/dist/esm/providers/mockProvider.js +73 -0
  69. package/dist/esm/providers/mockProvider.js.map +1 -0
  70. package/dist/esm/utils/AddressUtils.d.ts +2 -0
  71. package/dist/esm/utils/AddressUtils.js +25 -0
  72. package/dist/esm/utils/AddressUtils.js.map +1 -1
  73. package/dist/esm/utils/CachingUtils.js +1 -1
  74. package/dist/esm/utils/CachingUtils.js.map +1 -1
  75. package/dist/esm/utils/DepositUtils.d.ts +2 -1
  76. package/dist/esm/utils/DepositUtils.js +14 -5
  77. package/dist/esm/utils/DepositUtils.js.map +1 -1
  78. package/dist/esm/utils/EventUtils.js +29 -1
  79. package/dist/esm/utils/EventUtils.js.map +1 -1
  80. package/dist/esm/utils/NetworkUtils.d.ts +6 -0
  81. package/dist/esm/utils/NetworkUtils.js +10 -0
  82. package/dist/esm/utils/NetworkUtils.js.map +1 -1
  83. package/dist/esm/utils/SpokeUtils.d.ts +1 -0
  84. package/dist/esm/utils/SpokeUtils.js +17 -11
  85. package/dist/esm/utils/SpokeUtils.js.map +1 -1
  86. package/dist/esm/utils/common.d.ts +1 -0
  87. package/dist/esm/utils/common.js +1 -0
  88. package/dist/esm/utils/common.js.map +1 -1
  89. package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts +5 -4
  90. package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts.map +1 -1
  91. package/dist/types/clients/BundleDataClient/utils/DataworkerUtils.d.ts +1 -2
  92. package/dist/types/clients/BundleDataClient/utils/DataworkerUtils.d.ts.map +1 -1
  93. package/dist/types/clients/BundleDataClient/utils/FillUtils.d.ts +5 -1
  94. package/dist/types/clients/BundleDataClient/utils/FillUtils.d.ts.map +1 -1
  95. package/dist/types/clients/BundleDataClient/utils/SuperstructUtils.d.ts +4 -4
  96. package/dist/types/clients/SpokePoolClient.d.ts +8 -0
  97. package/dist/types/clients/SpokePoolClient.d.ts.map +1 -1
  98. package/dist/types/clients/mocks/MockSpokePoolClient.d.ts +2 -1
  99. package/dist/types/clients/mocks/MockSpokePoolClient.d.ts.map +1 -1
  100. package/dist/types/constants.d.ts +1 -1
  101. package/dist/types/constants.d.ts.map +1 -1
  102. package/dist/types/providers/index.d.ts +1 -0
  103. package/dist/types/providers/index.d.ts.map +1 -1
  104. package/dist/types/providers/mockProvider.d.ts +24 -0
  105. package/dist/types/providers/mockProvider.d.ts.map +1 -0
  106. package/dist/types/utils/AddressUtils.d.ts +2 -0
  107. package/dist/types/utils/AddressUtils.d.ts.map +1 -1
  108. package/dist/types/utils/DepositUtils.d.ts +2 -1
  109. package/dist/types/utils/DepositUtils.d.ts.map +1 -1
  110. package/dist/types/utils/EventUtils.d.ts.map +1 -1
  111. package/dist/types/utils/NetworkUtils.d.ts +6 -0
  112. package/dist/types/utils/NetworkUtils.d.ts.map +1 -1
  113. package/dist/types/utils/SpokeUtils.d.ts +1 -0
  114. package/dist/types/utils/SpokeUtils.d.ts.map +1 -1
  115. package/dist/types/utils/common.d.ts +1 -0
  116. package/dist/types/utils/common.d.ts.map +1 -1
  117. package/package.json +1 -1
  118. package/src/clients/BundleDataClient/BundleDataClient.ts +413 -184
  119. package/src/clients/BundleDataClient/utils/DataworkerUtils.ts +0 -8
  120. package/src/clients/BundleDataClient/utils/FillUtils.ts +66 -2
  121. package/src/clients/SpokePoolClient.ts +19 -6
  122. package/src/clients/mocks/MockSpokePoolClient.ts +14 -0
  123. package/src/constants.ts +3 -3
  124. package/src/providers/index.ts +1 -0
  125. package/src/providers/mockProvider.ts +77 -0
  126. package/src/utils/AddressUtils.ts +26 -0
  127. package/src/utils/CachingUtils.ts +1 -1
  128. package/src/utils/DepositUtils.ts +14 -5
  129. package/src/utils/EventUtils.ts +29 -1
  130. package/src/utils/NetworkUtils.ts +11 -0
  131. package/src/utils/SpokeUtils.ts +27 -13
  132. package/src/utils/common.ts +2 -0
@@ -1,13 +1,18 @@
1
1
  import { __assign, __awaiter, __generator } from "tslib";
2
2
  import _ from "lodash";
3
3
  import { FillType, FillStatus, } from "../../interfaces";
4
- import { BigNumber, bnZero, queryHistoricalDepositForFill, assign, assert, fixedPointAdjustment, isDefined, toBN, forEachAsync, getBlockRangeForChain, getImpliedBundleBlockRanges, isSlowFill, mapAsync, bnUint32Max, isZeroValueDeposit, findFillEvent, isZeroValueFillOrSlowFillRequest, } from "../../utils";
5
- import { _buildPoolRebalanceRoot, BundleDataSS, getEndBlockBuffers, getRefundInformationFromFill, getRefundsFromBundle, getWidestPossibleExpectedBlockRange, isChainDisabled, prettyPrintV3SpokePoolEvents, } from "./utils";
4
+ import { BigNumber, bnZero, queryHistoricalDepositForFill, assign, assert, fixedPointAdjustment, isDefined, toBN, forEachAsync, getBlockRangeForChain, getImpliedBundleBlockRanges, isSlowFill, mapAsync, filterAsync, bnUint32Max, isZeroValueDeposit, findFillEvent, isZeroValueFillOrSlowFillRequest, chainIsEvm, isValidEvmAddress, } from "../../utils";
5
+ import { _buildPoolRebalanceRoot, BundleDataSS, getEndBlockBuffers, getRefundInformationFromFill, getRefundsFromBundle, getWidestPossibleExpectedBlockRange, isChainDisabled, isEvmRepaymentValid, prettyPrintV3SpokePoolEvents, verifyFillRepayment, } from "./utils";
6
+ import { PRE_FILL_MIN_CONFIG_STORE_VERSION } from "../../constants";
6
7
  // max(uint256) - 1
7
8
  export var INFINITE_FILL_DEADLINE = bnUint32Max;
8
9
  // V3 dictionary helper functions
9
10
  function updateExpiredDepositsV3(dict, deposit) {
10
11
  var _a;
12
+ // A deposit refund for a deposit is invalid if the depositor has a bytes32 address input for an EVM chain. It is valid otherwise.
13
+ if (chainIsEvm(deposit.originChainId) && !isValidEvmAddress(deposit.depositor)) {
14
+ return;
15
+ }
11
16
  var originChainId = deposit.originChainId, inputToken = deposit.inputToken;
12
17
  if (!((_a = dict === null || dict === void 0 ? void 0 : dict[originChainId]) === null || _a === void 0 ? void 0 : _a[inputToken])) {
13
18
  assign(dict, [originChainId, inputToken], []);
@@ -22,8 +27,10 @@ function updateBundleDepositsV3(dict, deposit) {
22
27
  }
23
28
  dict[originChainId][inputToken].push(deposit);
24
29
  }
25
- function updateBundleFillsV3(dict, fill, lpFeePct, repaymentChainId, repaymentToken) {
30
+ function updateBundleFillsV3(dict, fill, lpFeePct, repaymentChainId, repaymentToken, repaymentAddress) {
26
31
  var _a, _b;
32
+ // We shouldn't pass any unrepayable fills into this function, so we perform an extra safety check.
33
+ assert(chainIsEvm(repaymentChainId) && isEvmRepaymentValid(fill, repaymentChainId), "validatedBundleV3Fills dictionary should only contain fills with valid repayment information");
27
34
  if (!((_a = dict === null || dict === void 0 ? void 0 : dict[repaymentChainId]) === null || _a === void 0 ? void 0 : _a[repaymentToken])) {
28
35
  assign(dict, [repaymentChainId, repaymentToken], {
29
36
  fills: [],
@@ -32,26 +39,26 @@ function updateBundleFillsV3(dict, fill, lpFeePct, repaymentChainId, repaymentTo
32
39
  refunds: {},
33
40
  });
34
41
  }
35
- var bundleFill = __assign(__assign({}, fill), { lpFeePct: lpFeePct });
42
+ var bundleFill = __assign(__assign({}, fill), { lpFeePct: lpFeePct, relayer: repaymentAddress });
36
43
  // Add all fills, slow and fast, to dictionary.
37
44
  assign(dict, [repaymentChainId, repaymentToken, "fills"], [bundleFill]);
38
45
  // All fills update the bundle LP fees.
39
46
  var refundObj = dict[repaymentChainId][repaymentToken];
40
- var realizedLpFee = fill.inputAmount.mul(bundleFill.lpFeePct).div(fixedPointAdjustment);
47
+ var realizedLpFee = bundleFill.inputAmount.mul(bundleFill.lpFeePct).div(fixedPointAdjustment);
41
48
  refundObj.realizedLpFees = refundObj.realizedLpFees ? refundObj.realizedLpFees.add(realizedLpFee) : realizedLpFee;
42
49
  // Only fast fills get refunded.
43
- if (!isSlowFill(fill)) {
44
- var refundAmount = fill.inputAmount.mul(fixedPointAdjustment.sub(lpFeePct)).div(fixedPointAdjustment);
50
+ if (!isSlowFill(bundleFill)) {
51
+ var refundAmount = bundleFill.inputAmount.mul(fixedPointAdjustment.sub(lpFeePct)).div(fixedPointAdjustment);
45
52
  refundObj.totalRefundAmount = refundObj.totalRefundAmount
46
53
  ? refundObj.totalRefundAmount.add(refundAmount)
47
54
  : refundAmount;
48
55
  // Instantiate dictionary if it doesn't exist.
49
56
  (_b = refundObj.refunds) !== null && _b !== void 0 ? _b : (refundObj.refunds = {});
50
- if (refundObj.refunds[fill.relayer]) {
51
- refundObj.refunds[fill.relayer] = refundObj.refunds[fill.relayer].add(refundAmount);
57
+ if (refundObj.refunds[bundleFill.relayer]) {
58
+ refundObj.refunds[bundleFill.relayer] = refundObj.refunds[bundleFill.relayer].add(refundAmount);
52
59
  }
53
60
  else {
54
- refundObj.refunds[fill.relayer] = refundAmount;
61
+ refundObj.refunds[bundleFill.relayer] = refundAmount;
55
62
  }
56
63
  }
57
64
  }
@@ -65,6 +72,9 @@ function updateBundleExcessSlowFills(dict, deposit) {
65
72
  }
66
73
  function updateBundleSlowFills(dict, deposit) {
67
74
  var _a;
75
+ if (chainIsEvm(deposit.destinationChainId) && !isValidEvmAddress(deposit.recipient)) {
76
+ return;
77
+ }
68
78
  var destinationChainId = deposit.destinationChainId, outputToken = deposit.outputToken;
69
79
  if (!((_a = dict === null || dict === void 0 ? void 0 : dict[destinationChainId]) === null || _a === void 0 ? void 0 : _a[outputToken])) {
70
80
  assign(dict, [destinationChainId, outputToken], []);
@@ -162,8 +172,7 @@ var BundleDataClient = /** @class */ (function () {
162
172
  at: "BundleDataClient#loadPersistedDataFromArweave",
163
173
  message: "Loaded persisted data from Arweave in ".concat(Math.round(performance.now() - start) / 1000, "s."),
164
174
  blockRanges: JSON.stringify(blockRangesForChains),
165
- bundleData: prettyPrintV3SpokePoolEvents(bundleData.bundleDepositsV3, bundleData.bundleFillsV3, [], // Invalid fills are not persisted to Arweave.
166
- bundleData.bundleSlowFillsV3, bundleData.expiredDepositsToRefundV3, bundleData.unexecutableSlowFills),
175
+ bundleData: prettyPrintV3SpokePoolEvents(bundleData.bundleDepositsV3, bundleData.bundleFillsV3, bundleData.bundleSlowFillsV3, bundleData.expiredDepositsToRefundV3, bundleData.unexecutableSlowFills),
167
176
  });
168
177
  return [2 /*return*/, bundleData];
169
178
  }
@@ -209,77 +218,124 @@ var BundleDataClient = /** @class */ (function () {
209
218
  return [4 /*yield*/, this.loadArweaveData(bundleEvaluationBlockRanges)];
210
219
  case 1:
211
220
  arweaveData = _a.sent();
212
- if (arweaveData === undefined) {
213
- combinedRefunds = this.getApproximateRefundsForBlockRange(chainIds, bundleEvaluationBlockRanges);
214
- }
215
- else {
216
- bundleFillsV3 = arweaveData.bundleFillsV3, expiredDepositsToRefundV3 = arweaveData.expiredDepositsToRefundV3;
217
- combinedRefunds = getRefundsFromBundle(bundleFillsV3, expiredDepositsToRefundV3);
218
- // If we don't have a spoke pool client for a chain, then we won't be able to deduct refunds correctly for this
219
- // chain. For most of the pending bundle's liveness period, these past refunds are already executed so this is
220
- // a reasonable assumption. This empty refund chain also matches what the alternative
221
- // `getApproximateRefundsForBlockRange` would return.
222
- Object.keys(combinedRefunds).forEach(function (chainId) {
223
- if (_this.spokePoolClients[Number(chainId)] === undefined) {
224
- delete combinedRefunds[Number(chainId)];
225
- }
226
- });
227
- }
228
- // The latest proposed bundle's refund leaves might have already been partially or entirely executed.
229
- // We have to deduct the executed amounts from the total refund amounts.
230
- return [2 /*return*/, this.deductExecutedRefunds(combinedRefunds, bundle)];
221
+ if (!(arweaveData === undefined)) return [3 /*break*/, 3];
222
+ return [4 /*yield*/, this.getApproximateRefundsForBlockRange(chainIds, bundleEvaluationBlockRanges)];
223
+ case 2:
224
+ combinedRefunds = _a.sent();
225
+ return [3 /*break*/, 4];
226
+ case 3:
227
+ bundleFillsV3 = arweaveData.bundleFillsV3, expiredDepositsToRefundV3 = arweaveData.expiredDepositsToRefundV3;
228
+ combinedRefunds = getRefundsFromBundle(bundleFillsV3, expiredDepositsToRefundV3);
229
+ // If we don't have a spoke pool client for a chain, then we won't be able to deduct refunds correctly for this
230
+ // chain. For most of the pending bundle's liveness period, these past refunds are already executed so this is
231
+ // a reasonable assumption. This empty refund chain also matches what the alternative
232
+ // `getApproximateRefundsForBlockRange` would return.
233
+ Object.keys(combinedRefunds).forEach(function (chainId) {
234
+ if (_this.spokePoolClients[Number(chainId)] === undefined) {
235
+ delete combinedRefunds[Number(chainId)];
236
+ }
237
+ });
238
+ _a.label = 4;
239
+ case 4:
240
+ // The latest proposed bundle's refund leaves might have already been partially or entirely executed.
241
+ // We have to deduct the executed amounts from the total refund amounts.
242
+ return [2 /*return*/, this.deductExecutedRefunds(combinedRefunds, bundle)];
231
243
  }
232
244
  });
233
245
  });
234
246
  };
235
247
  // @dev This helper function should probably be moved to the InventoryClient
236
248
  BundleDataClient.prototype.getApproximateRefundsForBlockRange = function (chainIds, blockRanges) {
237
- var _this = this;
238
- var refundsForChain = {};
239
- var _loop_1 = function (chainId) {
240
- if (this_1.spokePoolClients[chainId] === undefined) {
241
- return "continue";
242
- }
243
- var chainIndex = chainIds.indexOf(chainId);
244
- this_1.spokePoolClients[chainId]
245
- .getFills()
246
- .filter(function (fill) {
247
- if (fill.blockNumber < blockRanges[chainIndex][0] || fill.blockNumber > blockRanges[chainIndex][1]) {
248
- return false;
249
- }
250
- // If origin spoke pool client isn't defined, we can't validate it.
251
- if (_this.spokePoolClients[fill.originChainId] === undefined) {
252
- return false;
249
+ return __awaiter(this, void 0, void 0, function () {
250
+ var refundsForChain, _loop_1, this_1, _i, chainIds_1, chainId;
251
+ var _this = this;
252
+ return __generator(this, function (_a) {
253
+ switch (_a.label) {
254
+ case 0:
255
+ refundsForChain = {};
256
+ _loop_1 = function (chainId) {
257
+ var chainIndex, fillsToCount;
258
+ return __generator(this, function (_b) {
259
+ switch (_b.label) {
260
+ case 0:
261
+ if (this_1.spokePoolClients[chainId] === undefined) {
262
+ return [2 /*return*/, "continue"];
263
+ }
264
+ chainIndex = chainIds.indexOf(chainId);
265
+ return [4 /*yield*/, filterAsync(this_1.spokePoolClients[chainId].getFills(), function (fill) { return __awaiter(_this, void 0, void 0, function () {
266
+ var matchingDeposit, hasMatchingDeposit, validRepayment;
267
+ return __generator(this, function (_a) {
268
+ switch (_a.label) {
269
+ case 0:
270
+ if (fill.blockNumber < blockRanges[chainIndex][0] ||
271
+ fill.blockNumber > blockRanges[chainIndex][1] ||
272
+ isZeroValueFillOrSlowFillRequest(fill)) {
273
+ return [2 /*return*/, false];
274
+ }
275
+ // If origin spoke pool client isn't defined, we can't validate it.
276
+ if (this.spokePoolClients[fill.originChainId] === undefined) {
277
+ return [2 /*return*/, false];
278
+ }
279
+ matchingDeposit = this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
280
+ hasMatchingDeposit = matchingDeposit !== undefined &&
281
+ this.getRelayHashFromEvent(fill) === this.getRelayHashFromEvent(matchingDeposit);
282
+ if (!hasMatchingDeposit) return [3 /*break*/, 2];
283
+ return [4 /*yield*/, verifyFillRepayment(fill, this.spokePoolClients[fill.destinationChainId].spokePool.provider, matchingDeposit,
284
+ // @dev: to get valid repayment chain ID's, get all chain IDs for the bundle block range and remove
285
+ // disabled block ranges.
286
+ this.clients.configStoreClient
287
+ .getChainIdIndicesForBlock(blockRanges[0][1])
288
+ .filter(function (_chainId, i) { return !isChainDisabled(blockRanges[i]); }))];
289
+ case 1:
290
+ validRepayment = _a.sent();
291
+ if (!isDefined(validRepayment)) {
292
+ return [2 /*return*/, false];
293
+ }
294
+ _a.label = 2;
295
+ case 2: return [2 /*return*/, hasMatchingDeposit];
296
+ }
297
+ });
298
+ }); })];
299
+ case 1:
300
+ fillsToCount = _b.sent();
301
+ fillsToCount.forEach(function (fill) {
302
+ var _a, _b, _c;
303
+ var _d;
304
+ var matchingDeposit = _this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
305
+ assert(isDefined(matchingDeposit), "Deposit not found for fill.");
306
+ var _e = getRefundInformationFromFill(fill, _this.clients.hubPoolClient, blockRanges, _this.chainIdListForBundleEvaluationBlockNumbers, matchingDeposit.fromLiteChain // Use ! because we've already asserted that matchingDeposit is defined.
307
+ ), chainToSendRefundTo = _e.chainToSendRefundTo, repaymentToken = _e.repaymentToken;
308
+ // Assume that lp fees are 0 for the sake of speed. In the future we could batch compute
309
+ // these or make hardcoded assumptions based on the origin-repayment chain direction. This might result
310
+ // in slight over estimations of refunds, but its not clear whether underestimating or overestimating is
311
+ // worst from the relayer's perspective.
312
+ var relayer = fill.relayer, refundAmount = fill.inputAmount;
313
+ (_a = refundsForChain[chainToSendRefundTo]) !== null && _a !== void 0 ? _a : (refundsForChain[chainToSendRefundTo] = {});
314
+ (_b = (_d = refundsForChain[chainToSendRefundTo])[repaymentToken]) !== null && _b !== void 0 ? _b : (_d[repaymentToken] = {});
315
+ var existingRefundAmount = (_c = refundsForChain[chainToSendRefundTo][repaymentToken][relayer]) !== null && _c !== void 0 ? _c : bnZero;
316
+ refundsForChain[chainToSendRefundTo][repaymentToken][relayer] = existingRefundAmount.add(refundAmount);
317
+ });
318
+ return [2 /*return*/];
319
+ }
320
+ });
321
+ };
322
+ this_1 = this;
323
+ _i = 0, chainIds_1 = chainIds;
324
+ _a.label = 1;
325
+ case 1:
326
+ if (!(_i < chainIds_1.length)) return [3 /*break*/, 4];
327
+ chainId = chainIds_1[_i];
328
+ return [5 /*yield**/, _loop_1(chainId)];
329
+ case 2:
330
+ _a.sent();
331
+ _a.label = 3;
332
+ case 3:
333
+ _i++;
334
+ return [3 /*break*/, 1];
335
+ case 4: return [2 /*return*/, refundsForChain];
253
336
  }
254
- var matchingDeposit = _this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
255
- var hasMatchingDeposit = matchingDeposit !== undefined &&
256
- _this.getRelayHashFromEvent(fill) === _this.getRelayHashFromEvent(matchingDeposit);
257
- return hasMatchingDeposit;
258
- })
259
- .forEach(function (fill) {
260
- var _a, _b, _c;
261
- var _d;
262
- var matchingDeposit = _this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
263
- assert(isDefined(matchingDeposit), "Deposit not found for fill.");
264
- var _e = getRefundInformationFromFill(fill, _this.clients.hubPoolClient, blockRanges, _this.chainIdListForBundleEvaluationBlockNumbers, matchingDeposit.fromLiteChain // Use ! because we've already asserted that matchingDeposit is defined.
265
- ), chainToSendRefundTo = _e.chainToSendRefundTo, repaymentToken = _e.repaymentToken;
266
- // Assume that lp fees are 0 for the sake of speed. In the future we could batch compute
267
- // these or make hardcoded assumptions based on the origin-repayment chain direction. This might result
268
- // in slight over estimations of refunds, but its not clear whether underestimating or overestimating is
269
- // worst from the relayer's perspective.
270
- var relayer = fill.relayer, refundAmount = fill.inputAmount;
271
- (_a = refundsForChain[chainToSendRefundTo]) !== null && _a !== void 0 ? _a : (refundsForChain[chainToSendRefundTo] = {});
272
- (_b = (_d = refundsForChain[chainToSendRefundTo])[repaymentToken]) !== null && _b !== void 0 ? _b : (_d[repaymentToken] = {});
273
- var existingRefundAmount = (_c = refundsForChain[chainToSendRefundTo][repaymentToken][relayer]) !== null && _c !== void 0 ? _c : bnZero;
274
- refundsForChain[chainToSendRefundTo][repaymentToken][relayer] = existingRefundAmount.add(refundAmount);
275
337
  });
276
- };
277
- var this_1 = this;
278
- for (var _i = 0, chainIds_1 = chainIds; _i < chainIds_1.length; _i++) {
279
- var chainId = chainIds_1[_i];
280
- _loop_1(chainId);
281
- }
282
- return refundsForChain;
338
+ });
283
339
  };
284
340
  BundleDataClient.prototype.getUpcomingDepositAmount = function (chainId, l2Token, latestBlockToSearch) {
285
341
  if (this.spokePoolClients[chainId] === undefined) {
@@ -324,12 +380,10 @@ var BundleDataClient = /** @class */ (function () {
324
380
  case 1:
325
381
  _a = _b.sent(), bundleData = _a.bundleData, blockRanges = _a.blockRanges;
326
382
  hubPoolClient = this.clients.hubPoolClient;
327
- return [4 /*yield*/, _buildPoolRebalanceRoot(hubPoolClient.latestBlockSearched, blockRanges[0][1], bundleData.bundleDepositsV3, bundleData.bundleFillsV3, bundleData.bundleSlowFillsV3, bundleData.unexecutableSlowFills, bundleData.expiredDepositsToRefundV3, {
328
- hubPoolClient: hubPoolClient,
329
- configStoreClient: hubPoolClient.configStoreClient,
330
- })];
331
- case 2:
332
- root = _b.sent();
383
+ root = _buildPoolRebalanceRoot(hubPoolClient.latestBlockSearched, blockRanges[0][1], bundleData.bundleDepositsV3, bundleData.bundleFillsV3, bundleData.bundleSlowFillsV3, bundleData.unexecutableSlowFills, bundleData.expiredDepositsToRefundV3, {
384
+ hubPoolClient: hubPoolClient,
385
+ configStoreClient: hubPoolClient.configStoreClient,
386
+ });
333
387
  return [2 /*return*/, {
334
388
  root: root,
335
389
  blockRanges: blockRanges,
@@ -371,33 +425,40 @@ var BundleDataClient = /** @class */ (function () {
371
425
  return [initialBlockRange[1] + 1, blockRange[1]];
372
426
  });
373
427
  }
374
- var hubPoolClient, nextBundleMainnetStartBlock, chainIds, combinedRefunds, widestBundleBlockRanges, pendingBundleBlockRanges, arweaveData, bundleFillsV3, expiredDepositsToRefundV3, start;
375
- return __generator(this, function (_a) {
376
- switch (_a.label) {
428
+ var hubPoolClient, nextBundleMainnetStartBlock, chainIds, combinedRefunds, widestBundleBlockRanges, pendingBundleBlockRanges, arweaveData, _a, _b, bundleFillsV3, expiredDepositsToRefundV3, start, _c, _d;
429
+ return __generator(this, function (_e) {
430
+ switch (_e.label) {
377
431
  case 0:
378
432
  hubPoolClient = this.clients.hubPoolClient;
379
433
  nextBundleMainnetStartBlock = hubPoolClient.getNextBundleStartBlockNumber(this.chainIdListForBundleEvaluationBlockNumbers, hubPoolClient.latestBlockSearched, hubPoolClient.chainId);
380
434
  chainIds = this.clients.configStoreClient.getChainIdIndicesForBlock(nextBundleMainnetStartBlock);
381
435
  combinedRefunds = [];
382
436
  widestBundleBlockRanges = getWidestPossibleExpectedBlockRange(chainIds, this.spokePoolClients, getEndBlockBuffers(chainIds, this.blockRangeEndBlockBuffer), this.clients, this.clients.hubPoolClient.latestBlockSearched, this.clients.configStoreClient.getEnabledChains(this.clients.hubPoolClient.latestBlockSearched));
383
- if (!hubPoolClient.hasPendingProposal()) return [3 /*break*/, 2];
437
+ if (!hubPoolClient.hasPendingProposal()) return [3 /*break*/, 5];
384
438
  pendingBundleBlockRanges = getImpliedBundleBlockRanges(hubPoolClient, this.clients.configStoreClient, hubPoolClient.getLatestProposedRootBundle());
385
439
  return [4 /*yield*/, this.loadArweaveData(pendingBundleBlockRanges)];
386
440
  case 1:
387
- arweaveData = _a.sent();
388
- if (arweaveData === undefined) {
389
- combinedRefunds.push(this.getApproximateRefundsForBlockRange(chainIds, pendingBundleBlockRanges));
390
- }
391
- else {
392
- bundleFillsV3 = arweaveData.bundleFillsV3, expiredDepositsToRefundV3 = arweaveData.expiredDepositsToRefundV3;
393
- combinedRefunds.push(getRefundsFromBundle(bundleFillsV3, expiredDepositsToRefundV3));
394
- }
441
+ arweaveData = _e.sent();
442
+ if (!(arweaveData === undefined)) return [3 /*break*/, 3];
443
+ _b = (_a = combinedRefunds).push;
444
+ return [4 /*yield*/, this.getApproximateRefundsForBlockRange(chainIds, pendingBundleBlockRanges)];
445
+ case 2:
446
+ _b.apply(_a, [_e.sent()]);
447
+ return [3 /*break*/, 4];
448
+ case 3:
449
+ bundleFillsV3 = arweaveData.bundleFillsV3, expiredDepositsToRefundV3 = arweaveData.expiredDepositsToRefundV3;
450
+ combinedRefunds.push(getRefundsFromBundle(bundleFillsV3, expiredDepositsToRefundV3));
451
+ _e.label = 4;
452
+ case 4:
395
453
  // Shorten the widestBundleBlockRanges now to not double count the pending bundle blocks.
396
454
  widestBundleBlockRanges = getBlockRangeDelta(pendingBundleBlockRanges);
397
- _a.label = 2;
398
- case 2:
455
+ _e.label = 5;
456
+ case 5:
399
457
  start = performance.now();
400
- combinedRefunds.push(this.getApproximateRefundsForBlockRange(chainIds, widestBundleBlockRanges));
458
+ _d = (_c = combinedRefunds).push;
459
+ return [4 /*yield*/, this.getApproximateRefundsForBlockRange(chainIds, widestBundleBlockRanges)];
460
+ case 6:
461
+ _d.apply(_c, [_e.sent()]);
401
462
  this.logger.debug({
402
463
  at: "BundleDataClient#getNextBundleRefunds",
403
464
  message: "Loading approximate refunds for next bundle in ".concat(Math.round(performance.now() - start) / 1000, "s."),
@@ -540,7 +601,7 @@ var BundleDataClient = /** @class */ (function () {
540
601
  };
541
602
  BundleDataClient.prototype.loadDataFromScratch = function (blockRangesForChains, spokePoolClients) {
542
603
  return __awaiter(this, void 0, void 0, function () {
543
- var start, key, chainIds, bundleDepositsV3, bundleFillsV3, bundleInvalidFillsV3, bundleSlowFillsV3, expiredDepositsToRefundV3, unexecutableSlowFills, _isChainDisabled, _canCreateSlowFillLeaf, _depositIsExpired, _getFillStatusForDeposit, allChainIds, _cachedBundleTimestamps, bundleBlockTimestamps, v3RelayHashes, bundleDepositHashes, olderDepositHashes, depositCounter, _loop_2, _i, allChainIds_1, originChainId, validatedBundleV3Fills, validatedBundleSlowFills, validatedBundleUnexecutableSlowFills, fillCounter, _loop_3, _a, allChainIds_2, originChainId, promises, _b, v3FillLpFees, v3SlowFillLpFees, v3UnexecutableSlowFillLpFees, v3SpokeEventsReadable;
604
+ var start, key, chainIds, bundleDepositsV3, bundleFillsV3, bundleInvalidFillsV3, bundleUnrepayableFillsV3, bundleSlowFillsV3, expiredDepositsToRefundV3, unexecutableSlowFills, _isChainDisabled, _canCreateSlowFillLeaf, _depositIsExpired, _getFillStatusForDeposit, allChainIds, _cachedBundleTimestamps, bundleBlockTimestamps, v3RelayHashes, bundleDepositHashes, olderDepositHashes, decodeBundleDepositHash, startBlockForMainnet, versionAtProposalBlock, canRefundPrefills, depositCounter, _loop_2, _i, allChainIds_1, originChainId, validatedBundleV3Fills, validatedBundleSlowFills, validatedBundleUnexecutableSlowFills, fillCounter, _loop_3, _a, allChainIds_2, originChainId, promises, _b, v3FillLpFees, v3SlowFillLpFees, v3UnexecutableSlowFillLpFees, v3SpokeEventsReadable;
544
605
  var _this = this;
545
606
  return __generator(this, function (_c) {
546
607
  switch (_c.label) {
@@ -560,6 +621,7 @@ var BundleDataClient = /** @class */ (function () {
560
621
  bundleDepositsV3 = {};
561
622
  bundleFillsV3 = {};
562
623
  bundleInvalidFillsV3 = [];
624
+ bundleUnrepayableFillsV3 = [];
563
625
  bundleSlowFillsV3 = {};
564
626
  expiredDepositsToRefundV3 = {};
565
627
  unexecutableSlowFills = {};
@@ -617,6 +679,13 @@ var BundleDataClient = /** @class */ (function () {
617
679
  v3RelayHashes = {};
618
680
  bundleDepositHashes = [];
619
681
  olderDepositHashes = [];
682
+ decodeBundleDepositHash = function (depositHash) {
683
+ var _a = depositHash.split("@"), relayDataHash = _a[0], i = _a[1];
684
+ return { relayDataHash: relayDataHash, index: Number(i) };
685
+ };
686
+ startBlockForMainnet = getBlockRangeForChain(blockRangesForChains, this.clients.hubPoolClient.chainId, this.chainIdListForBundleEvaluationBlockNumbers)[0];
687
+ versionAtProposalBlock = this.clients.configStoreClient.getConfigStoreVersionForBlock(startBlockForMainnet);
688
+ canRefundPrefills = versionAtProposalBlock >= PRE_FILL_MIN_CONFIG_STORE_VERSION || process.env.FORCE_REFUND_PREFILLS === "true";
620
689
  depositCounter = 0;
621
690
  _loop_2 = function (originChainId) {
622
691
  var originClient = spokePoolClients[originChainId];
@@ -637,11 +706,14 @@ var BundleDataClient = /** @class */ (function () {
637
706
  var relayDataHash = _this.getRelayHashFromEvent(deposit);
638
707
  if (!v3RelayHashes[relayDataHash]) {
639
708
  v3RelayHashes[relayDataHash] = {
640
- deposit: deposit,
709
+ deposits: [deposit],
641
710
  fill: undefined,
642
711
  slowFillRequest: undefined,
643
712
  };
644
713
  }
714
+ else {
715
+ v3RelayHashes[relayDataHash].deposits.push(deposit);
716
+ }
645
717
  // Once we've saved the deposit hash into v3RelayHashes, then we can exit early here if the inputAmount
646
718
  // is 0 because there can be no expired amount to refund and no unexecutable slow fill amount to return
647
719
  // if this deposit did expire. Input amount can only be zero at this point if the message is non-empty,
@@ -649,12 +721,18 @@ var BundleDataClient = /** @class */ (function () {
649
721
  if (deposit.inputAmount.eq(0)) {
650
722
  return;
651
723
  }
724
+ // Evaluate all expired deposits after fetching fill statuses,
725
+ // since we can't know for certain whether an expired deposit was filled a long time ago.
726
+ var newBundleDepositHash = "".concat(relayDataHash, "@").concat(v3RelayHashes[relayDataHash].deposits.length - 1);
727
+ var decodedBundleDepositHash = decodeBundleDepositHash(newBundleDepositHash);
728
+ assert(decodedBundleDepositHash.relayDataHash === relayDataHash &&
729
+ decodedBundleDepositHash.index === v3RelayHashes[relayDataHash].deposits.length - 1, "Not using correct bundle deposit hash key");
652
730
  if (deposit.blockNumber >= originChainBlockRange[0]) {
653
- bundleDepositHashes.push(relayDataHash);
731
+ bundleDepositHashes.push(newBundleDepositHash);
654
732
  updateBundleDepositsV3(bundleDepositsV3, deposit);
655
733
  }
656
734
  else if (deposit.blockNumber < originChainBlockRange[0]) {
657
- olderDepositHashes.push(relayDataHash);
735
+ olderDepositHashes.push(newBundleDepositHash);
658
736
  }
659
737
  });
660
738
  }
@@ -679,7 +757,7 @@ var BundleDataClient = /** @class */ (function () {
679
757
  case 0:
680
758
  originClient = spokePoolClients[originChainId];
681
759
  _loop_4 = function (destinationChainId) {
682
- var destinationClient, destinationChainBlockRange, fastFillsReplacingSlowFills, originBlockRange;
760
+ var destinationClient, destinationChainBlockRange, originChainBlockRange, fastFillsReplacingSlowFills;
683
761
  return __generator(this, function (_g) {
684
762
  switch (_g.label) {
685
763
  case 0:
@@ -688,6 +766,7 @@ var BundleDataClient = /** @class */ (function () {
688
766
  }
689
767
  destinationClient = spokePoolClients[destinationChainId];
690
768
  destinationChainBlockRange = getBlockRangeForChain(blockRangesForChains, destinationChainId, chainIds);
769
+ originChainBlockRange = getBlockRangeForChain(blockRangesForChains, originChainId, chainIds);
691
770
  fastFillsReplacingSlowFills = [];
692
771
  return [4 /*yield*/, forEachAsync(destinationClient
693
772
  .getFillsForOriginChain(originChainId)
@@ -695,40 +774,64 @@ var BundleDataClient = /** @class */ (function () {
695
774
  // tokens to the filler. We can't remove non-empty message deposit here in case there is a slow fill
696
775
  // request for the deposit, we'd want to see the fill took place.
697
776
  .filter(function (fill) { return fill.blockNumber <= destinationChainBlockRange[1] && !isZeroValueFillOrSlowFillRequest(fill); }), function (fill) { return __awaiter(_this, void 0, void 0, function () {
698
- var relayDataHash, historicalDeposit, matchedDeposit;
777
+ var relayDataHash, fillToRefund_1, duplicateDeposits, historicalDeposit, matchedDeposit, fillToRefund;
699
778
  return __generator(this, function (_a) {
700
779
  switch (_a.label) {
701
780
  case 0:
702
- relayDataHash = this.getRelayHashFromEvent(fill);
703
781
  fillCounter++;
704
- if (v3RelayHashes[relayDataHash]) {
705
- if (!v3RelayHashes[relayDataHash].fill) {
706
- assert(isDefined(v3RelayHashes[relayDataHash].deposit), "Deposit should exist in relay hash dictionary.");
707
- // At this point, the v3RelayHashes entry already existed meaning that there is a matching deposit,
708
- // so this fill is validated.
709
- v3RelayHashes[relayDataHash].fill = fill;
710
- if (fill.blockNumber >= destinationChainBlockRange[0]) {
711
- validatedBundleV3Fills.push(__assign(__assign({}, fill), { quoteTimestamp: v3RelayHashes[relayDataHash].deposit.quoteTimestamp }));
712
- // If fill replaced a slow fill request, then mark it as one that might have created an
713
- // unexecutable slow fill. We can't know for sure until we check the slow fill request
714
- // events.
715
- // slow fill requests for deposits from or to lite chains are considered invalid
716
- if (fill.relayExecutionInfo.fillType === FillType.ReplacedSlowFill &&
717
- _canCreateSlowFillLeaf(v3RelayHashes[relayDataHash].deposit)) {
718
- fastFillsReplacingSlowFills.push(relayDataHash);
719
- }
782
+ relayDataHash = this.getRelayHashFromEvent(fill);
783
+ if (!v3RelayHashes[relayDataHash]) return [3 /*break*/, 5];
784
+ if (!!v3RelayHashes[relayDataHash].fill) return [3 /*break*/, 3];
785
+ assert(isDefined(v3RelayHashes[relayDataHash].deposits) && v3RelayHashes[relayDataHash].deposits.length > 0, "Deposit should exist in relay hash dictionary.");
786
+ // At this point, the v3RelayHashes entry already existed meaning that there is a matching deposit,
787
+ // so this fill can no longer be filled on-chain.
788
+ v3RelayHashes[relayDataHash].fill = fill;
789
+ if (!(fill.blockNumber >= destinationChainBlockRange[0])) return [3 /*break*/, 2];
790
+ return [4 /*yield*/, verifyFillRepayment(fill, destinationClient.spokePool.provider, v3RelayHashes[relayDataHash].deposits[0], allChainIds)];
791
+ case 1:
792
+ fillToRefund_1 = _a.sent();
793
+ if (!isDefined(fillToRefund_1)) {
794
+ // We won't repay the fill but the depositor has received funds so we don't need to make a
795
+ // payment.
796
+ bundleUnrepayableFillsV3.push(fill);
797
+ // We don't return here yet because we still need to mark unexecutable slow fill leaves
798
+ // or duplicate deposits. However, we won't issue a fast fill refund.
799
+ }
800
+ else {
801
+ v3RelayHashes[relayDataHash].fill = fillToRefund_1;
802
+ validatedBundleV3Fills.push(__assign(__assign({}, fillToRefund_1), { quoteTimestamp: v3RelayHashes[relayDataHash].deposits[0].quoteTimestamp }));
803
+ duplicateDeposits = v3RelayHashes[relayDataHash].deposits.slice(1);
804
+ duplicateDeposits.forEach(function (duplicateDeposit) {
805
+ // If fill is a slow fill, refund deposit to depositor, otherwise refund to filler.
806
+ if (isSlowFill(fill)) {
807
+ updateExpiredDepositsV3(expiredDepositsToRefundV3, duplicateDeposit);
720
808
  }
721
- }
722
- return [2 /*return*/];
809
+ else {
810
+ validatedBundleV3Fills.push(__assign(__assign({}, fillToRefund_1), { quoteTimestamp: duplicateDeposit.quoteTimestamp }));
811
+ }
812
+ });
723
813
  }
814
+ // If fill replaced a slow fill request, then mark it as one that might have created an
815
+ // unexecutable slow fill. We can't know for sure until we check the slow fill request
816
+ // events.
817
+ // slow fill requests for deposits from or to lite chains are considered invalid
818
+ if (fill.relayExecutionInfo.fillType === FillType.ReplacedSlowFill &&
819
+ _canCreateSlowFillLeaf(v3RelayHashes[relayDataHash].deposits[0])) {
820
+ fastFillsReplacingSlowFills.push(relayDataHash);
821
+ }
822
+ _a.label = 2;
823
+ case 2: return [3 /*break*/, 4];
824
+ case 3: throw new Error("Duplicate fill detected");
825
+ case 4: return [2 /*return*/];
826
+ case 5:
724
827
  // At this point, there is no relay hash dictionary entry for this fill, so we need to
725
- // instantiate the entry.
828
+ // instantiate the entry. We won't modify the fill.relayer until we match it with a deposit.
726
829
  v3RelayHashes[relayDataHash] = {
727
- deposit: undefined,
830
+ deposits: undefined,
728
831
  fill: fill,
729
832
  slowFillRequest: undefined,
730
833
  };
731
- if (!(fill.blockNumber >= destinationChainBlockRange[0])) return [3 /*break*/, 2];
834
+ if (!(fill.blockNumber >= destinationChainBlockRange[0])) return [3 /*break*/, 9];
732
835
  // Fill has a non-infinite expiry, and we can assume our spoke pool clients have old enough deposits
733
836
  // to conclude that this fill is invalid if we haven't found a matching deposit in memory, so
734
837
  // skip the historical query.
@@ -737,28 +840,46 @@ var BundleDataClient = /** @class */ (function () {
737
840
  return [2 /*return*/];
738
841
  }
739
842
  return [4 /*yield*/, queryHistoricalDepositForFill(originClient, fill)];
740
- case 1:
843
+ case 6:
741
844
  historicalDeposit = _a.sent();
742
- if (!historicalDeposit.found) {
845
+ if (!!historicalDeposit.found) return [3 /*break*/, 7];
846
+ bundleInvalidFillsV3.push(fill);
847
+ return [3 /*break*/, 9];
848
+ case 7:
849
+ matchedDeposit = historicalDeposit.deposit;
850
+ // If deposit is in a following bundle, then this fill will have to be refunded once that deposit
851
+ // is in the current bundle.
852
+ if (matchedDeposit.blockNumber > originChainBlockRange[1]) {
743
853
  bundleInvalidFillsV3.push(fill);
854
+ return [2 /*return*/];
855
+ }
856
+ v3RelayHashes[relayDataHash].deposits = [matchedDeposit];
857
+ return [4 /*yield*/, verifyFillRepayment(fill, destinationClient.spokePool.provider, matchedDeposit, allChainIds)];
858
+ case 8:
859
+ fillToRefund = _a.sent();
860
+ if (!isDefined(fillToRefund)) {
861
+ bundleUnrepayableFillsV3.push(fill);
862
+ // Don't return yet as we still need to mark down any unexecutable slow fill leaves
863
+ // in case this fast fill replaced a slow fill request.
744
864
  }
745
865
  else {
746
- matchedDeposit = historicalDeposit.deposit;
747
866
  // @dev Since queryHistoricalDepositForFill validates the fill by checking individual
748
867
  // object property values against the deposit's, we
749
868
  // sanity check it here by comparing the full relay hashes. If there's an error here then the
750
869
  // historical deposit query is not working as expected.
751
870
  assert(this.getRelayHashFromEvent(matchedDeposit) === relayDataHash, "Relay hashes should match.");
752
- validatedBundleV3Fills.push(__assign(__assign({}, fill), { quoteTimestamp: matchedDeposit.quoteTimestamp }));
753
- v3RelayHashes[relayDataHash].deposit = matchedDeposit;
754
- // slow fill requests for deposits from or to lite chains are considered invalid
755
- if (fill.relayExecutionInfo.fillType === FillType.ReplacedSlowFill &&
756
- _canCreateSlowFillLeaf(matchedDeposit)) {
757
- fastFillsReplacingSlowFills.push(relayDataHash);
758
- }
871
+ validatedBundleV3Fills.push(__assign(__assign({}, fillToRefund), { quoteTimestamp: matchedDeposit.quoteTimestamp }));
872
+ v3RelayHashes[relayDataHash].fill = fillToRefund;
873
+ // No need to check for duplicate deposits here since duplicate deposits with
874
+ // infinite deadlines are impossible to send via unsafeDeposit().
759
875
  }
760
- _a.label = 2;
761
- case 2: return [2 /*return*/];
876
+ // slow fill requests for deposits from or to lite chains are considered invalid
877
+ if (fill.relayExecutionInfo.fillType === FillType.ReplacedSlowFill &&
878
+ _canCreateSlowFillLeaf(matchedDeposit)) {
879
+ fastFillsReplacingSlowFills.push(relayDataHash);
880
+ }
881
+ _a.label = 9;
882
+ case 9: return [2 /*return*/];
762
883
  }
763
884
  });
764
885
  }); })];
@@ -783,11 +904,13 @@ var BundleDataClient = /** @class */ (function () {
783
904
  v3RelayHashes[relayDataHash].slowFillRequest = slowFillRequest;
784
905
  if (v3RelayHashes[relayDataHash].fill) {
785
906
  // If there is a fill matching the relay hash, then this slow fill request can't be used
786
- // to create a slow fill for a filled deposit.
907
+ // to create a slow fill for a filled deposit. This takes advantage of the fact that
908
+ // slow fill requests must precede fills, so if there is a matching fill for this request's
909
+ // relay data, then this slow fill will be unexecutable.
787
910
  return [2 /*return*/];
788
911
  }
789
- assert(isDefined(v3RelayHashes[relayDataHash].deposit), "Deposit should exist in relay hash dictionary.");
790
- matchedDeposit = v3RelayHashes[relayDataHash].deposit;
912
+ assert(isDefined(v3RelayHashes[relayDataHash].deposits) && v3RelayHashes[relayDataHash].deposits.length > 0, "Deposit should exist in relay hash dictionary.");
913
+ matchedDeposit = v3RelayHashes[relayDataHash].deposits[0];
791
914
  // If there is no fill matching the relay hash, then this might be a valid slow fill request
792
915
  // that we should produce a slow fill leaf for. Check if the slow fill request is in the
793
916
  // destination chain block range.
@@ -800,16 +923,19 @@ var BundleDataClient = /** @class */ (function () {
800
923
  validatedBundleSlowFills.push(matchedDeposit);
801
924
  }
802
925
  }
926
+ else {
927
+ throw new Error("Duplicate slow fill request detected.");
928
+ }
803
929
  return [2 /*return*/];
804
930
  }
805
931
  // Instantiate dictionary if there is neither a deposit nor fill matching it.
806
932
  v3RelayHashes[relayDataHash] = {
807
- deposit: undefined,
933
+ deposits: undefined,
808
934
  fill: undefined,
809
935
  slowFillRequest: slowFillRequest,
810
936
  };
811
- if (!(slowFillRequest.blockNumber >= destinationChainBlockRange[0] &&
812
- INFINITE_FILL_DEADLINE.eq(slowFillRequest.fillDeadline))) return [3 /*break*/, 2];
937
+ if (!(INFINITE_FILL_DEADLINE.eq(slowFillRequest.fillDeadline) &&
938
+ slowFillRequest.blockNumber >= destinationChainBlockRange[0])) return [3 /*break*/, 2];
813
939
  return [4 /*yield*/, queryHistoricalDepositForFill(originClient, slowFillRequest)];
814
940
  case 1:
815
941
  historicalDeposit = _a.sent();
@@ -818,12 +944,17 @@ var BundleDataClient = /** @class */ (function () {
818
944
  return [2 /*return*/];
819
945
  }
820
946
  matchedDeposit = historicalDeposit.deposit;
947
+ // If deposit is in a following bundle, then this slow fill request will have to be created
948
+ // once that deposit is in the current bundle.
949
+ if (matchedDeposit.blockNumber > originChainBlockRange[1]) {
950
+ return [2 /*return*/];
951
+ }
821
952
  // @dev Since queryHistoricalDepositForFill validates the slow fill request by checking individual
822
953
  // object property values against the deposit's, we
823
954
  // sanity check it here by comparing the full relay hashes. If there's an error here then the
824
955
  // historical deposit query is not working as expected.
825
956
  assert(this.getRelayHashFromEvent(matchedDeposit) === relayDataHash, "Deposit relay hashes should match.");
826
- v3RelayHashes[relayDataHash].deposit = matchedDeposit;
957
+ v3RelayHashes[relayDataHash].deposits = [matchedDeposit];
827
958
  if (!_canCreateSlowFillLeaf(matchedDeposit) ||
828
959
  // Deposit must not have expired in this bundle.
829
960
  _depositIsExpired(matchedDeposit)) {
@@ -839,39 +970,53 @@ var BundleDataClient = /** @class */ (function () {
839
970
  // Process slow fill requests. One invariant we need to maintain is that we cannot create slow fill requests
840
971
  // for deposits that would expire in this bundle.
841
972
  _g.sent();
842
- originBlockRange = getBlockRangeForChain(blockRangesForChains, originChainId, chainIds);
843
- return [4 /*yield*/, mapAsync(bundleDepositHashes.filter(function (depositHash) {
844
- var deposit = v3RelayHashes[depositHash].deposit;
845
- return (deposit &&
846
- deposit.originChainId === originChainId &&
847
- deposit.destinationChainId === destinationChainId &&
848
- deposit.blockNumber >= originBlockRange[0] &&
849
- deposit.blockNumber <= originBlockRange[1] &&
850
- !isZeroValueDeposit(deposit));
851
- }), function (depositHash) { return __awaiter(_this, void 0, void 0, function () {
852
- var _a, deposit, fill, slowFillRequest, fillStatus, prefill;
853
- return __generator(this, function (_b) {
854
- switch (_b.label) {
973
+ // Deposits can be submitted an arbitrary amount of time after matching fills and slow fill requests.
974
+ // Therefore, let's go through each deposit in this bundle again and check a few things in order:
975
+ // - Has the deposit been filled ? If so, then we need to issue a relayer refund for
976
+ // this "pre-fill" if the fill took place in a previous bundle.
977
+ // - Or, has the deposit expired in this bundle? If so, then we need to issue an expiry refund.
978
+ // - And finally, has the deposit been slow filled? If so, then we need to issue a slow fill leaf
979
+ // for this "pre-slow-fill-request" if this request took place in a previous bundle.
980
+ return [4 /*yield*/, mapAsync(bundleDepositHashes, function (depositHash) { return __awaiter(_this, void 0, void 0, function () {
981
+ var _a, relayDataHash, index, _b, deposits, fill, slowFillRequest, deposit, fillToRefund, fillStatus, prefill, verifiedFill;
982
+ var _this = this;
983
+ return __generator(this, function (_c) {
984
+ switch (_c.label) {
855
985
  case 0:
856
- _a = v3RelayHashes[depositHash], deposit = _a.deposit, fill = _a.fill, slowFillRequest = _a.slowFillRequest;
986
+ _a = decodeBundleDepositHash(depositHash), relayDataHash = _a.relayDataHash, index = _a.index;
987
+ _b = v3RelayHashes[relayDataHash], deposits = _b.deposits, fill = _b.fill, slowFillRequest = _b.slowFillRequest;
988
+ if (!deposits || deposits.length === 0) {
989
+ throw new Error("Deposits should exist in relay hash dictionary.");
990
+ }
991
+ deposit = deposits[index];
857
992
  if (!deposit)
858
993
  throw new Error("Deposit should exist in relay hash dictionary.");
859
- // We are willing to refund a pre-fill multiple times for each duplicate deposit.
860
- // This is because a duplicate deposit for a pre-fill cannot get
861
- // refunded to the depositor anymore because its fill status on-chain has changed to Filled. Therefore
862
- // any duplicate deposits result in a net loss of funds for the depositor and effectively pay out
863
- // the pre-filler.
864
- // If fill exists in memory, then the only case in which we need to create a refund is if the
865
- // the fill occurred in a previous bundle. There are no expiry refunds for filled deposits.
866
- if (fill) {
867
- if (fill.blockNumber < destinationChainBlockRange[0] && !isSlowFill(fill)) {
868
- // If fill is in the current bundle then we can assume there is already a refund for it, so only
869
- // include this pre fill if the fill is in an older bundle. If fill is after this current bundle, then
870
- // we won't consider it, following the previous treatment of fills after the bundle block range.
871
- validatedBundleV3Fills.push(__assign(__assign({}, fill), { quoteTimestamp: deposit.quoteTimestamp }));
872
- }
994
+ if (deposit.originChainId !== originChainId || deposit.destinationChainId !== destinationChainId) {
873
995
  return [2 /*return*/];
874
996
  }
997
+ if (!fill) return [3 /*break*/, 4];
998
+ if (!(canRefundPrefills && fill.blockNumber < destinationChainBlockRange[0])) return [3 /*break*/, 3];
999
+ if (!!isSlowFill(fill)) return [3 /*break*/, 2];
1000
+ return [4 /*yield*/, verifyFillRepayment(fill, destinationClient.spokePool.provider, v3RelayHashes[relayDataHash].deposits[0], allChainIds)];
1001
+ case 1:
1002
+ fillToRefund = _c.sent();
1003
+ if (!isDefined(fillToRefund)) {
1004
+ // We won't repay the fill but the depositor has received funds so we don't need to make a
1005
+ // payment.
1006
+ bundleUnrepayableFillsV3.push(fill);
1007
+ }
1008
+ else {
1009
+ v3RelayHashes[relayDataHash].fill = fillToRefund;
1010
+ validatedBundleV3Fills.push(__assign(__assign({}, fillToRefund), { quoteTimestamp: deposit.quoteTimestamp }));
1011
+ }
1012
+ return [3 /*break*/, 3];
1013
+ case 2:
1014
+ // Slow fills cannot result in refunds to a relayer to refund the deposit. Slow fills also
1015
+ // were created after the deposit was sent, so we can assume this deposit is a duplicate.
1016
+ updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
1017
+ _c.label = 3;
1018
+ case 3: return [2 /*return*/];
1019
+ case 4:
875
1020
  // If a slow fill request exists in memory, then we know the deposit has not been filled because fills
876
1021
  // must follow slow fill requests and we would have seen the fill already if it existed. Therefore,
877
1022
  // we can conclude that either the deposit has expired and we need to create a deposit expiry refund, or
@@ -881,63 +1026,88 @@ var BundleDataClient = /** @class */ (function () {
881
1026
  if (_depositIsExpired(deposit)) {
882
1027
  updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
883
1028
  }
884
- else if (slowFillRequest.blockNumber < destinationChainBlockRange[0] &&
885
- _canCreateSlowFillLeaf(deposit)) {
1029
+ else if (canRefundPrefills &&
1030
+ slowFillRequest.blockNumber < destinationChainBlockRange[0] &&
1031
+ _canCreateSlowFillLeaf(deposit) &&
1032
+ validatedBundleSlowFills.every(function (d) { return _this.getRelayHashFromEvent(d) !== relayDataHash; })) {
886
1033
  validatedBundleSlowFills.push(deposit);
887
1034
  }
888
1035
  return [2 /*return*/];
889
1036
  }
890
1037
  return [4 /*yield*/, _getFillStatusForDeposit(deposit, destinationChainBlockRange[1])];
891
- case 1:
892
- fillStatus = _b.sent();
893
- if (!(fillStatus === FillStatus.Filled)) return [3 /*break*/, 3];
894
- return [4 /*yield*/, findFillEvent(destinationClient.spokePool, deposit, destinationClient.deploymentBlock, destinationClient.latestBlockSearched)];
895
- case 2:
896
- prefill = (_b.sent());
897
- if (!isSlowFill(prefill)) {
898
- validatedBundleV3Fills.push(__assign(__assign({}, prefill), { quoteTimestamp: deposit.quoteTimestamp }));
1038
+ case 5:
1039
+ fillStatus = _c.sent();
1040
+ if (!(fillStatus === FillStatus.Filled)) return [3 /*break*/, 9];
1041
+ return [4 /*yield*/, this.findMatchingFillEvent(deposit, destinationClient)];
1042
+ case 6:
1043
+ prefill = _c.sent();
1044
+ assert(isDefined(prefill), "findFillEvent# Cannot find prefill: ".concat(relayDataHash));
1045
+ assert(this.getRelayHashFromEvent(prefill) === relayDataHash, "Relay hashes should match.");
1046
+ if (!canRefundPrefills) return [3 /*break*/, 8];
1047
+ return [4 /*yield*/, verifyFillRepayment(prefill, destinationClient.spokePool.provider, deposit, allChainIds)];
1048
+ case 7:
1049
+ verifiedFill = _c.sent();
1050
+ if (!isDefined(verifiedFill)) {
1051
+ bundleUnrepayableFillsV3.push(prefill);
1052
+ }
1053
+ else if (!isSlowFill(verifiedFill)) {
1054
+ validatedBundleV3Fills.push(__assign(__assign({}, verifiedFill), { quoteTimestamp: deposit.quoteTimestamp }));
899
1055
  }
900
- return [3 /*break*/, 4];
901
- case 3:
1056
+ else {
1057
+ // Slow fills cannot result in refunds to a relayer to refund the deposit. Slow fills also
1058
+ // were created after the deposit was sent, so we can assume this deposit is a duplicate.
1059
+ updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
1060
+ }
1061
+ _c.label = 8;
1062
+ case 8: return [3 /*break*/, 10];
1063
+ case 9:
902
1064
  if (_depositIsExpired(deposit)) {
903
1065
  updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
904
1066
  }
905
1067
  // If slow fill requested, then issue a slow fill leaf for the deposit.
906
- else if (fillStatus === FillStatus.RequestedSlowFill) {
1068
+ else if (fillStatus === FillStatus.RequestedSlowFill &&
1069
+ validatedBundleSlowFills.every(function (d) { return _this.getRelayHashFromEvent(d) !== relayDataHash; })) {
907
1070
  // Input and Output tokens must be equivalent on the deposit for this to be slow filled.
908
1071
  // Slow fill requests for deposits from or to lite chains are considered invalid
909
- if (_canCreateSlowFillLeaf(deposit)) {
1072
+ if (canRefundPrefills && _canCreateSlowFillLeaf(deposit)) {
910
1073
  // If deposit newly expired, then we can't create a slow fill leaf for it but we can
911
1074
  // create a deposit refund for it.
912
1075
  validatedBundleSlowFills.push(deposit);
913
1076
  }
914
1077
  }
915
- _b.label = 4;
916
- case 4: return [2 /*return*/];
1078
+ _c.label = 10;
1079
+ case 10: return [2 /*return*/];
917
1080
  }
918
1081
  });
919
1082
  }); })];
920
1083
  case 3:
1084
+ // Deposits can be submitted an arbitrary amount of time after matching fills and slow fill requests.
1085
+ // Therefore, let's go through each deposit in this bundle again and check a few things in order:
1086
+ // - Has the deposit been filled ? If so, then we need to issue a relayer refund for
1087
+ // this "pre-fill" if the fill took place in a previous bundle.
1088
+ // - Or, has the deposit expired in this bundle? If so, then we need to issue an expiry refund.
1089
+ // - And finally, has the deposit been slow filled? If so, then we need to issue a slow fill leaf
1090
+ // for this "pre-slow-fill-request" if this request took place in a previous bundle.
921
1091
  _g.sent();
922
1092
  // For all fills that came after a slow fill request, we can now check if the slow fill request
923
1093
  // was a valid one and whether it was created in a previous bundle. If so, then it created a slow fill
924
1094
  // leaf that is now unexecutable.
925
1095
  fastFillsReplacingSlowFills.forEach(function (relayDataHash) {
926
- var _a = v3RelayHashes[relayDataHash], deposit = _a.deposit, slowFillRequest = _a.slowFillRequest, fill = _a.fill;
1096
+ var _a = v3RelayHashes[relayDataHash], deposits = _a.deposits, slowFillRequest = _a.slowFillRequest, fill = _a.fill;
927
1097
  assert((fill === null || fill === void 0 ? void 0 : fill.relayExecutionInfo.fillType) === FillType.ReplacedSlowFill, "Fill type should be ReplacedSlowFill.");
928
1098
  // Needed for TSC - are implicitely checking that deposit exists by making it to this point.
929
- if (!deposit) {
1099
+ if (!deposits || deposits.length < 1) {
930
1100
  throw new Error("Deposit should exist in relay hash dictionary.");
931
1101
  }
932
1102
  // We should never push fast fills involving lite chains here because slow fill requests for them are invalid:
933
- assert(_canCreateSlowFillLeaf(deposit), "fastFillsReplacingSlowFills should contain only deposits that can be slow filled");
1103
+ assert(_canCreateSlowFillLeaf(deposits[0]), "fastFillsReplacingSlowFills should contain only deposits that can be slow filled");
934
1104
  var destinationBlockRange = getBlockRangeForChain(blockRangesForChains, destinationChainId, chainIds);
935
1105
  if (
936
1106
  // If there is a slow fill request in this bundle that matches the relay hash, then there was no slow fill
937
1107
  // created that would be considered excess.
938
1108
  !slowFillRequest ||
939
1109
  slowFillRequest.blockNumber < destinationBlockRange[0]) {
940
- validatedBundleUnexecutableSlowFills.push(deposit);
1110
+ validatedBundleUnexecutableSlowFills.push(deposits[0]);
941
1111
  }
942
1112
  });
943
1113
  return [2 /*return*/];
@@ -981,13 +1151,17 @@ var BundleDataClient = /** @class */ (function () {
981
1151
  // For all deposits older than this bundle, we need to check if they expired in this bundle and if they did,
982
1152
  // whether there was a slow fill created for it in a previous bundle that is now unexecutable and replaced
983
1153
  // by a new expired deposit refund.
984
- return [4 /*yield*/, forEachAsync(olderDepositHashes, function (relayDataHash) { return __awaiter(_this, void 0, void 0, function () {
985
- var _a, deposit, slowFillRequest, fill, destinationChainId, destinationBlockRange, fillStatus;
986
- return __generator(this, function (_b) {
987
- switch (_b.label) {
1154
+ return [4 /*yield*/, forEachAsync(olderDepositHashes, function (depositHash) { return __awaiter(_this, void 0, void 0, function () {
1155
+ var _a, relayDataHash, index, _b, deposits, slowFillRequest, fill, deposit, destinationChainId, destinationBlockRange, fillStatus;
1156
+ return __generator(this, function (_c) {
1157
+ switch (_c.label) {
988
1158
  case 0:
989
- _a = v3RelayHashes[relayDataHash], deposit = _a.deposit, slowFillRequest = _a.slowFillRequest, fill = _a.fill;
990
- assert(isDefined(deposit), "Deposit should exist in relay hash dictionary.");
1159
+ _a = decodeBundleDepositHash(depositHash), relayDataHash = _a.relayDataHash, index = _a.index;
1160
+ _b = v3RelayHashes[relayDataHash], deposits = _b.deposits, slowFillRequest = _b.slowFillRequest, fill = _b.fill;
1161
+ if (!deposits || deposits.length < 1) {
1162
+ throw new Error("Deposit should exist in relay hash dictionary.");
1163
+ }
1164
+ deposit = deposits[index];
991
1165
  destinationChainId = deposit.destinationChainId;
992
1166
  destinationBlockRange = getBlockRangeForChain(blockRangesForChains, destinationChainId, chainIds);
993
1167
  if (!
@@ -1001,7 +1175,7 @@ var BundleDataClient = /** @class */ (function () {
1001
1175
  return [3 /*break*/, 2];
1002
1176
  return [4 /*yield*/, _getFillStatusForDeposit(deposit, destinationBlockRange[1])];
1003
1177
  case 1:
1004
- fillStatus = _b.sent();
1178
+ fillStatus = _c.sent();
1005
1179
  // If there is no matching fill and the deposit expired in this bundle and the fill status on-chain is not
1006
1180
  // Filled, then we can to refund it as an expired deposit.
1007
1181
  if (fillStatus !== FillStatus.Filled) {
@@ -1023,7 +1197,7 @@ var BundleDataClient = /** @class */ (function () {
1023
1197
  if (!slowFillRequest || slowFillRequest.blockNumber < destinationBlockRange[0]) {
1024
1198
  validatedBundleUnexecutableSlowFills.push(deposit);
1025
1199
  }
1026
- _b.label = 2;
1200
+ _c.label = 2;
1027
1201
  case 2: return [2 /*return*/];
1028
1202
  }
1029
1203
  });
@@ -1038,7 +1212,7 @@ var BundleDataClient = /** @class */ (function () {
1038
1212
  promises = [
1039
1213
  validatedBundleV3Fills.length > 0
1040
1214
  ? this.clients.hubPoolClient.batchComputeRealizedLpFeePct(validatedBundleV3Fills.map(function (fill) {
1041
- var matchedDeposit = v3RelayHashes[_this.getRelayHashFromEvent(fill)].deposit;
1215
+ var matchedDeposit = v3RelayHashes[_this.getRelayHashFromEvent(fill)].deposits[0];
1042
1216
  assert(isDefined(matchedDeposit), "Deposit should exist in relay hash dictionary.");
1043
1217
  var paymentChainId = getRefundInformationFromFill(fill, _this.clients.hubPoolClient, blockRangesForChains, chainIds, matchedDeposit.fromLiteChain).chainToSendRefundTo;
1044
1218
  return __assign(__assign({}, fill), { paymentChainId: paymentChainId });
@@ -1065,14 +1239,22 @@ var BundleDataClient = /** @class */ (function () {
1065
1239
  v3FillLpFees.forEach(function (_a, idx) {
1066
1240
  var realizedLpFeePct = _a.realizedLpFeePct;
1067
1241
  var fill = validatedBundleV3Fills[idx];
1068
- var associatedDeposit = v3RelayHashes[_this.getRelayHashFromEvent(fill)].deposit;
1242
+ var associatedDeposit = v3RelayHashes[_this.getRelayHashFromEvent(fill)].deposits[0];
1069
1243
  assert(isDefined(associatedDeposit), "Deposit should exist in relay hash dictionary.");
1070
1244
  var _b = getRefundInformationFromFill(fill, _this.clients.hubPoolClient, blockRangesForChains, chainIds, associatedDeposit.fromLiteChain), chainToSendRefundTo = _b.chainToSendRefundTo, repaymentToken = _b.repaymentToken;
1071
- updateBundleFillsV3(bundleFillsV3, fill, realizedLpFeePct, chainToSendRefundTo, repaymentToken);
1245
+ updateBundleFillsV3(bundleFillsV3, fill, realizedLpFeePct, chainToSendRefundTo, repaymentToken, fill.relayer);
1072
1246
  });
1073
1247
  v3SlowFillLpFees.forEach(function (_a, idx) {
1074
1248
  var lpFeePct = _a.realizedLpFeePct;
1075
1249
  var deposit = validatedBundleSlowFills[idx];
1250
+ // We should not create slow fill leaves for duplicate deposit hashes and we should only create a slow
1251
+ // fill leaf for the first deposit (the quote timestamp of the deposit determines the LP fee, so its
1252
+ // important we pick out the correct deposit). Deposits are pushed into validatedBundleSlowFills in ascending
1253
+ // order so the following slice will only match the first deposit.
1254
+ var relayDataHash = _this.getRelayHashFromEvent(deposit);
1255
+ if (validatedBundleSlowFills.slice(0, idx).some(function (d) { return _this.getRelayHashFromEvent(d) === relayDataHash; })) {
1256
+ return;
1257
+ }
1076
1258
  updateBundleSlowFills(bundleSlowFillsV3, __assign(__assign({}, deposit), { lpFeePct: lpFeePct }));
1077
1259
  });
1078
1260
  v3UnexecutableSlowFillLpFees.forEach(function (_a, idx) {
@@ -1080,7 +1262,7 @@ var BundleDataClient = /** @class */ (function () {
1080
1262
  var deposit = validatedBundleUnexecutableSlowFills[idx];
1081
1263
  updateBundleExcessSlowFills(unexecutableSlowFills, __assign(__assign({}, deposit), { lpFeePct: lpFeePct }));
1082
1264
  });
1083
- v3SpokeEventsReadable = prettyPrintV3SpokePoolEvents(bundleDepositsV3, bundleFillsV3, bundleInvalidFillsV3, bundleSlowFillsV3, expiredDepositsToRefundV3, unexecutableSlowFills);
1265
+ v3SpokeEventsReadable = prettyPrintV3SpokePoolEvents(bundleDepositsV3, bundleFillsV3, bundleSlowFillsV3, expiredDepositsToRefundV3, unexecutableSlowFills);
1084
1266
  if (bundleInvalidFillsV3.length > 0) {
1085
1267
  this.logger.debug({
1086
1268
  at: "BundleDataClient#loadData",
@@ -1089,6 +1271,14 @@ var BundleDataClient = /** @class */ (function () {
1089
1271
  bundleInvalidFillsV3: bundleInvalidFillsV3,
1090
1272
  });
1091
1273
  }
1274
+ if (bundleUnrepayableFillsV3.length > 0) {
1275
+ this.logger.debug({
1276
+ at: "BundleDataClient#loadData",
1277
+ message: "Finished loading V3 spoke pool data and found some unrepayable V3 fills in range",
1278
+ blockRangesForChains: blockRangesForChains,
1279
+ bundleUnrepayableFillsV3: bundleUnrepayableFillsV3,
1280
+ });
1281
+ }
1092
1282
  this.logger.debug({
1093
1283
  at: "BundleDataClient#loadDataFromScratch",
1094
1284
  message: "Computed bundle data in ".concat(Math.round(performance.now() - start) / 1000, "s."),
@@ -1111,7 +1301,17 @@ var BundleDataClient = /** @class */ (function () {
1111
1301
  // spoke pool contract. However, this internal function is used to uniquely identify a bridging event
1112
1302
  // for speed since its easier to build a string from the event data than to hash it.
1113
1303
  BundleDataClient.prototype.getRelayHashFromEvent = function (event) {
1114
- return "".concat(event.depositor, "-").concat(event.recipient, "-").concat(event.exclusiveRelayer, "-").concat(event.inputToken, "-").concat(event.outputToken, "-").concat(event.inputAmount, "-").concat(event.outputAmount, "-").concat(event.originChainId, "-").concat(event.depositId, "-").concat(event.fillDeadline, "-").concat(event.exclusivityDeadline, "-").concat(event.message, "-").concat(event.destinationChainId);
1304
+ return "".concat(event.depositor, "-").concat(event.recipient, "-").concat(event.exclusiveRelayer, "-").concat(event.inputToken, "-").concat(event.outputToken, "-").concat(event.inputAmount, "-").concat(event.outputAmount, "-").concat(event.originChainId, "-").concat(event.depositId.toString(), "-").concat(event.fillDeadline, "-").concat(event.exclusivityDeadline, "-").concat(event.message, "-").concat(event.destinationChainId);
1305
+ };
1306
+ BundleDataClient.prototype.findMatchingFillEvent = function (deposit, spokePoolClient) {
1307
+ return __awaiter(this, void 0, void 0, function () {
1308
+ return __generator(this, function (_a) {
1309
+ switch (_a.label) {
1310
+ case 0: return [4 /*yield*/, findFillEvent(spokePoolClient.spokePool, deposit, spokePoolClient.deploymentBlock, spokePoolClient.latestBlockSearched)];
1311
+ case 1: return [2 /*return*/, _a.sent()];
1312
+ }
1313
+ });
1314
+ });
1115
1315
  };
1116
1316
  BundleDataClient.prototype.getBundleBlockTimestamps = function (chainIds, blockRangesForChains, spokePoolClients) {
1117
1317
  return __awaiter(this, void 0, void 0, function () {
@@ -1122,7 +1322,7 @@ var BundleDataClient = /** @class */ (function () {
1122
1322
  case 0:
1123
1323
  _b = (_a = Object).fromEntries;
1124
1324
  return [4 /*yield*/, mapAsync(chainIds, function (chainId, index) { return __awaiter(_this, void 0, void 0, function () {
1125
- var blockRangeForChain, _startBlockForChain, _endBlockForChain, spokePoolClient, startBlockForChain, endBlockForChain, _a, startTime, endTime, _b;
1325
+ var blockRangeForChain, _startBlockForChain, _endBlockForChain, spokePoolClient, startBlockForChain, endBlockForChain, _a, startTime, _endTime, _b, endBlockDelta, endTime;
1126
1326
  return __generator(this, function (_c) {
1127
1327
  switch (_c.label) {
1128
1328
  case 0:
@@ -1137,7 +1337,7 @@ var BundleDataClient = /** @class */ (function () {
1137
1337
  return [2 /*return*/];
1138
1338
  }
1139
1339
  startBlockForChain = Math.min(_startBlockForChain, spokePoolClient.latestBlockSearched);
1140
- endBlockForChain = Math.min(_endBlockForChain, spokePoolClient.latestBlockSearched);
1340
+ endBlockForChain = Math.min(_endBlockForChain + 1, spokePoolClient.latestBlockSearched);
1141
1341
  return [4 /*yield*/, spokePoolClient.getTimestampForBlock(startBlockForChain)];
1142
1342
  case 1:
1143
1343
  _b = [
@@ -1147,9 +1347,11 @@ var BundleDataClient = /** @class */ (function () {
1147
1347
  case 2:
1148
1348
  _a = _b.concat([
1149
1349
  _c.sent()
1150
- ]), startTime = _a[0], endTime = _a[1];
1350
+ ]), startTime = _a[0], _endTime = _a[1];
1351
+ endBlockDelta = endBlockForChain > startBlockForChain ? 1 : 0;
1352
+ endTime = Math.max(0, _endTime - endBlockDelta);
1151
1353
  // Sanity checks:
1152
- assert(endTime >= startTime, "End time should be greater than start time.");
1354
+ assert(endTime >= startTime, "End time for block ".concat(endBlockForChain, " should be greater than start time for block ").concat(startBlockForChain, ": ").concat(endTime, " >= ").concat(startTime, "."));
1153
1355
  assert(startBlockForChain === 0 || startTime > 0, "Start timestamp must be greater than 0 if the start block is greater than 0.");
1154
1356
  return [2 /*return*/, [chainId, [startTime, endTime]]];
1155
1357
  }