@across-protocol/sdk 4.0.0-beta.9 → 4.0.1

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 +345 -187
  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 +10 -1
  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 +0 -1
  18. package/dist/cjs/constants.js +1 -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 +1 -0
  27. package/dist/cjs/utils/AddressUtils.js +6 -1
  28. package/dist/cjs/utils/AddressUtils.js.map +1 -1
  29. package/dist/cjs/utils/BlockUtils.js +1 -0
  30. package/dist/cjs/utils/BlockUtils.js.map +1 -1
  31. package/dist/cjs/utils/DepositUtils.js +1 -1
  32. package/dist/cjs/utils/DepositUtils.js.map +1 -1
  33. package/dist/cjs/utils/EventUtils.js +21 -0
  34. package/dist/cjs/utils/EventUtils.js.map +1 -1
  35. package/dist/cjs/utils/NetworkUtils.js +2 -1
  36. package/dist/cjs/utils/NetworkUtils.js.map +1 -1
  37. package/dist/cjs/utils/SpokeUtils.d.ts +1 -0
  38. package/dist/cjs/utils/SpokeUtils.js +15 -8
  39. package/dist/cjs/utils/SpokeUtils.js.map +1 -1
  40. package/dist/cjs/utils/common.d.ts +1 -0
  41. package/dist/cjs/utils/common.js +2 -1
  42. package/dist/cjs/utils/common.js.map +1 -1
  43. package/dist/esm/clients/BundleDataClient/BundleDataClient.d.ts +5 -4
  44. package/dist/esm/clients/BundleDataClient/BundleDataClient.js +448 -281
  45. package/dist/esm/clients/BundleDataClient/BundleDataClient.js.map +1 -1
  46. package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.d.ts +1 -2
  47. package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js +2 -3
  48. package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
  49. package/dist/esm/clients/BundleDataClient/utils/FillUtils.d.ts +5 -1
  50. package/dist/esm/clients/BundleDataClient/utils/FillUtils.js +54 -1
  51. package/dist/esm/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
  52. package/dist/esm/clients/BundleDataClient/utils/SuperstructUtils.d.ts +4 -4
  53. package/dist/esm/clients/SpokePoolClient.d.ts +8 -0
  54. package/dist/esm/clients/SpokePoolClient.js +17 -1
  55. package/dist/esm/clients/SpokePoolClient.js.map +1 -1
  56. package/dist/esm/clients/mocks/MockSpokePoolClient.d.ts +2 -1
  57. package/dist/esm/clients/mocks/MockSpokePoolClient.js +11 -0
  58. package/dist/esm/clients/mocks/MockSpokePoolClient.js.map +1 -1
  59. package/dist/esm/constants.d.ts +0 -1
  60. package/dist/esm/constants.js +0 -1
  61. package/dist/esm/constants.js.map +1 -1
  62. package/dist/esm/providers/index.d.ts +1 -0
  63. package/dist/esm/providers/index.js +2 -0
  64. package/dist/esm/providers/index.js.map +1 -1
  65. package/dist/esm/providers/mockProvider.d.ts +23 -0
  66. package/dist/esm/providers/mockProvider.js +73 -0
  67. package/dist/esm/providers/mockProvider.js.map +1 -0
  68. package/dist/esm/utils/AddressUtils.d.ts +1 -0
  69. package/dist/esm/utils/AddressUtils.js +9 -0
  70. package/dist/esm/utils/AddressUtils.js.map +1 -1
  71. package/dist/esm/utils/BlockUtils.js +1 -0
  72. package/dist/esm/utils/BlockUtils.js.map +1 -1
  73. package/dist/esm/utils/DepositUtils.js +2 -2
  74. package/dist/esm/utils/DepositUtils.js.map +1 -1
  75. package/dist/esm/utils/EventUtils.js +29 -1
  76. package/dist/esm/utils/EventUtils.js.map +1 -1
  77. package/dist/esm/utils/NetworkUtils.js +2 -1
  78. package/dist/esm/utils/NetworkUtils.js.map +1 -1
  79. package/dist/esm/utils/SpokeUtils.d.ts +1 -0
  80. package/dist/esm/utils/SpokeUtils.js +13 -7
  81. package/dist/esm/utils/SpokeUtils.js.map +1 -1
  82. package/dist/esm/utils/abi/typechain/Multicall3.d.ts +4 -1
  83. package/dist/esm/utils/abi/typechain/factories/Multicall3__factory.js.map +1 -1
  84. package/dist/esm/utils/common.d.ts +1 -0
  85. package/dist/esm/utils/common.js +1 -0
  86. package/dist/esm/utils/common.js.map +1 -1
  87. package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts +5 -4
  88. package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts.map +1 -1
  89. package/dist/types/clients/BundleDataClient/utils/DataworkerUtils.d.ts +1 -2
  90. package/dist/types/clients/BundleDataClient/utils/DataworkerUtils.d.ts.map +1 -1
  91. package/dist/types/clients/BundleDataClient/utils/FillUtils.d.ts +5 -1
  92. package/dist/types/clients/BundleDataClient/utils/FillUtils.d.ts.map +1 -1
  93. package/dist/types/clients/BundleDataClient/utils/SuperstructUtils.d.ts +4 -4
  94. package/dist/types/clients/SpokePoolClient.d.ts +8 -0
  95. package/dist/types/clients/SpokePoolClient.d.ts.map +1 -1
  96. package/dist/types/clients/mocks/MockSpokePoolClient.d.ts +2 -1
  97. package/dist/types/clients/mocks/MockSpokePoolClient.d.ts.map +1 -1
  98. package/dist/types/constants.d.ts +0 -1
  99. package/dist/types/constants.d.ts.map +1 -1
  100. package/dist/types/providers/index.d.ts +1 -0
  101. package/dist/types/providers/index.d.ts.map +1 -1
  102. package/dist/types/providers/mockProvider.d.ts +24 -0
  103. package/dist/types/providers/mockProvider.d.ts.map +1 -0
  104. package/dist/types/utils/AddressUtils.d.ts +1 -0
  105. package/dist/types/utils/AddressUtils.d.ts.map +1 -1
  106. package/dist/types/utils/BlockUtils.d.ts.map +1 -1
  107. package/dist/types/utils/EventUtils.d.ts.map +1 -1
  108. package/dist/types/utils/NetworkUtils.d.ts.map +1 -1
  109. package/dist/types/utils/SpokeUtils.d.ts +1 -0
  110. package/dist/types/utils/SpokeUtils.d.ts.map +1 -1
  111. package/dist/types/utils/abi/typechain/Multicall3.d.ts +4 -1
  112. package/dist/types/utils/abi/typechain/Multicall3.d.ts.map +1 -1
  113. package/dist/types/utils/abi/typechain/common.d.ts.map +1 -1
  114. package/dist/types/utils/abi/typechain/factories/Multicall3__factory.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 +3 -3
  118. package/src/clients/BundleDataClient/BundleDataClient.ts +406 -249
  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 +16 -3
  122. package/src/clients/mocks/MockSpokePoolClient.ts +14 -0
  123. package/src/constants.ts +0 -2
  124. package/src/providers/index.ts +1 -0
  125. package/src/providers/mockProvider.ts +77 -0
  126. package/src/utils/AddressUtils.ts +10 -0
  127. package/src/utils/BlockUtils.ts +1 -0
  128. package/src/utils/DepositUtils.ts +2 -2
  129. package/src/utils/EventUtils.ts +29 -1
  130. package/src/utils/NetworkUtils.ts +2 -1
  131. package/src/utils/SpokeUtils.ts +21 -8
  132. package/src/utils/common.ts +2 -0
@@ -1,8 +1,8 @@
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, chainIsEvm, isValidEvmAddress, } 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
6
  import { PRE_FILL_MIN_CONFIG_STORE_VERSION } from "../../constants";
7
7
  // max(uint256) - 1
8
8
  export var INFINITE_FILL_DEADLINE = bnUint32Max;
@@ -27,12 +27,10 @@ function updateBundleDepositsV3(dict, deposit) {
27
27
  }
28
28
  dict[originChainId][inputToken].push(deposit);
29
29
  }
30
- function updateBundleFillsV3(dict, fill, lpFeePct, repaymentChainId, repaymentToken) {
30
+ function updateBundleFillsV3(dict, fill, lpFeePct, repaymentChainId, repaymentToken, repaymentAddress) {
31
31
  var _a, _b;
32
- // It is impossible to refund a deposit if the repayment chain is EVM and the relayer is a non-evm address.
33
- if (chainIsEvm(fill.repaymentChainId) && !isValidEvmAddress(fill.relayer)) {
34
- return;
35
- }
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");
36
34
  if (!((_a = dict === null || dict === void 0 ? void 0 : dict[repaymentChainId]) === null || _a === void 0 ? void 0 : _a[repaymentToken])) {
37
35
  assign(dict, [repaymentChainId, repaymentToken], {
38
36
  fills: [],
@@ -41,26 +39,26 @@ function updateBundleFillsV3(dict, fill, lpFeePct, repaymentChainId, repaymentTo
41
39
  refunds: {},
42
40
  });
43
41
  }
44
- var bundleFill = __assign(__assign({}, fill), { lpFeePct: lpFeePct });
42
+ var bundleFill = __assign(__assign({}, fill), { lpFeePct: lpFeePct, relayer: repaymentAddress });
45
43
  // Add all fills, slow and fast, to dictionary.
46
44
  assign(dict, [repaymentChainId, repaymentToken, "fills"], [bundleFill]);
47
45
  // All fills update the bundle LP fees.
48
46
  var refundObj = dict[repaymentChainId][repaymentToken];
49
- var realizedLpFee = fill.inputAmount.mul(bundleFill.lpFeePct).div(fixedPointAdjustment);
47
+ var realizedLpFee = bundleFill.inputAmount.mul(bundleFill.lpFeePct).div(fixedPointAdjustment);
50
48
  refundObj.realizedLpFees = refundObj.realizedLpFees ? refundObj.realizedLpFees.add(realizedLpFee) : realizedLpFee;
51
49
  // Only fast fills get refunded.
52
- if (!isSlowFill(fill)) {
53
- 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);
54
52
  refundObj.totalRefundAmount = refundObj.totalRefundAmount
55
53
  ? refundObj.totalRefundAmount.add(refundAmount)
56
54
  : refundAmount;
57
55
  // Instantiate dictionary if it doesn't exist.
58
56
  (_b = refundObj.refunds) !== null && _b !== void 0 ? _b : (refundObj.refunds = {});
59
- if (refundObj.refunds[fill.relayer]) {
60
- 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);
61
59
  }
62
60
  else {
63
- refundObj.refunds[fill.relayer] = refundAmount;
61
+ refundObj.refunds[bundleFill.relayer] = refundAmount;
64
62
  }
65
63
  }
66
64
  }
@@ -74,6 +72,9 @@ function updateBundleExcessSlowFills(dict, deposit) {
74
72
  }
75
73
  function updateBundleSlowFills(dict, deposit) {
76
74
  var _a;
75
+ if (chainIsEvm(deposit.destinationChainId) && !isValidEvmAddress(deposit.recipient)) {
76
+ return;
77
+ }
77
78
  var destinationChainId = deposit.destinationChainId, outputToken = deposit.outputToken;
78
79
  if (!((_a = dict === null || dict === void 0 ? void 0 : dict[destinationChainId]) === null || _a === void 0 ? void 0 : _a[outputToken])) {
79
80
  assign(dict, [destinationChainId, outputToken], []);
@@ -171,8 +172,7 @@ var BundleDataClient = /** @class */ (function () {
171
172
  at: "BundleDataClient#loadPersistedDataFromArweave",
172
173
  message: "Loaded persisted data from Arweave in ".concat(Math.round(performance.now() - start) / 1000, "s."),
173
174
  blockRanges: JSON.stringify(blockRangesForChains),
174
- bundleData: prettyPrintV3SpokePoolEvents(bundleData.bundleDepositsV3, bundleData.bundleFillsV3, [], // Invalid fills are not persisted to Arweave.
175
- bundleData.bundleSlowFillsV3, bundleData.expiredDepositsToRefundV3, bundleData.unexecutableSlowFills),
175
+ bundleData: prettyPrintV3SpokePoolEvents(bundleData.bundleDepositsV3, bundleData.bundleFillsV3, bundleData.bundleSlowFillsV3, bundleData.expiredDepositsToRefundV3, bundleData.unexecutableSlowFills),
176
176
  });
177
177
  return [2 /*return*/, bundleData];
178
178
  }
@@ -218,82 +218,124 @@ var BundleDataClient = /** @class */ (function () {
218
218
  return [4 /*yield*/, this.loadArweaveData(bundleEvaluationBlockRanges)];
219
219
  case 1:
220
220
  arweaveData = _a.sent();
221
- if (arweaveData === undefined) {
222
- combinedRefunds = this.getApproximateRefundsForBlockRange(chainIds, bundleEvaluationBlockRanges);
223
- }
224
- else {
225
- bundleFillsV3 = arweaveData.bundleFillsV3, expiredDepositsToRefundV3 = arweaveData.expiredDepositsToRefundV3;
226
- combinedRefunds = getRefundsFromBundle(bundleFillsV3, expiredDepositsToRefundV3);
227
- // If we don't have a spoke pool client for a chain, then we won't be able to deduct refunds correctly for this
228
- // chain. For most of the pending bundle's liveness period, these past refunds are already executed so this is
229
- // a reasonable assumption. This empty refund chain also matches what the alternative
230
- // `getApproximateRefundsForBlockRange` would return.
231
- Object.keys(combinedRefunds).forEach(function (chainId) {
232
- if (_this.spokePoolClients[Number(chainId)] === undefined) {
233
- delete combinedRefunds[Number(chainId)];
234
- }
235
- });
236
- }
237
- // The latest proposed bundle's refund leaves might have already been partially or entirely executed.
238
- // We have to deduct the executed amounts from the total refund amounts.
239
- 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)];
240
243
  }
241
244
  });
242
245
  });
243
246
  };
244
247
  // @dev This helper function should probably be moved to the InventoryClient
245
248
  BundleDataClient.prototype.getApproximateRefundsForBlockRange = function (chainIds, blockRanges) {
246
- var _this = this;
247
- var refundsForChain = {};
248
- var _loop_1 = function (chainId) {
249
- if (this_1.spokePoolClients[chainId] === undefined) {
250
- return "continue";
251
- }
252
- var chainIndex = chainIds.indexOf(chainId);
253
- // @todo This function does not account for pre-fill refunds as it is optimized for speed. The way to detect
254
- // pre-fill refunds is to load all deposits that are unmatched by fills in the spoke pool client's memory
255
- // and then query the FillStatus on-chain, but that might slow this function down too much. For now, we
256
- // will live with this expected inaccuracy as it should be small. The pre-fill would have to precede the deposit
257
- // by more than the caller's event lookback window which is expected to be unlikely.
258
- this_1.spokePoolClients[chainId]
259
- .getFills()
260
- .filter(function (fill) {
261
- if (fill.blockNumber < blockRanges[chainIndex][0] || fill.blockNumber > blockRanges[chainIndex][1]) {
262
- return false;
263
- }
264
- // If origin spoke pool client isn't defined, we can't validate it.
265
- if (_this.spokePoolClients[fill.originChainId] === undefined) {
266
- 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];
267
336
  }
268
- var matchingDeposit = _this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
269
- var hasMatchingDeposit = matchingDeposit !== undefined &&
270
- _this.getRelayHashFromEvent(fill) === _this.getRelayHashFromEvent(matchingDeposit);
271
- return hasMatchingDeposit;
272
- })
273
- .forEach(function (fill) {
274
- var _a, _b, _c;
275
- var _d;
276
- var matchingDeposit = _this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
277
- assert(isDefined(matchingDeposit), "Deposit not found for fill.");
278
- var _e = getRefundInformationFromFill(fill, _this.clients.hubPoolClient, blockRanges, _this.chainIdListForBundleEvaluationBlockNumbers, matchingDeposit.fromLiteChain // Use ! because we've already asserted that matchingDeposit is defined.
279
- ), chainToSendRefundTo = _e.chainToSendRefundTo, repaymentToken = _e.repaymentToken;
280
- // Assume that lp fees are 0 for the sake of speed. In the future we could batch compute
281
- // these or make hardcoded assumptions based on the origin-repayment chain direction. This might result
282
- // in slight over estimations of refunds, but its not clear whether underestimating or overestimating is
283
- // worst from the relayer's perspective.
284
- var relayer = fill.relayer, refundAmount = fill.inputAmount;
285
- (_a = refundsForChain[chainToSendRefundTo]) !== null && _a !== void 0 ? _a : (refundsForChain[chainToSendRefundTo] = {});
286
- (_b = (_d = refundsForChain[chainToSendRefundTo])[repaymentToken]) !== null && _b !== void 0 ? _b : (_d[repaymentToken] = {});
287
- var existingRefundAmount = (_c = refundsForChain[chainToSendRefundTo][repaymentToken][relayer]) !== null && _c !== void 0 ? _c : bnZero;
288
- refundsForChain[chainToSendRefundTo][repaymentToken][relayer] = existingRefundAmount.add(refundAmount);
289
337
  });
290
- };
291
- var this_1 = this;
292
- for (var _i = 0, chainIds_1 = chainIds; _i < chainIds_1.length; _i++) {
293
- var chainId = chainIds_1[_i];
294
- _loop_1(chainId);
295
- }
296
- return refundsForChain;
338
+ });
297
339
  };
298
340
  BundleDataClient.prototype.getUpcomingDepositAmount = function (chainId, l2Token, latestBlockToSearch) {
299
341
  if (this.spokePoolClients[chainId] === undefined) {
@@ -338,12 +380,10 @@ var BundleDataClient = /** @class */ (function () {
338
380
  case 1:
339
381
  _a = _b.sent(), bundleData = _a.bundleData, blockRanges = _a.blockRanges;
340
382
  hubPoolClient = this.clients.hubPoolClient;
341
- return [4 /*yield*/, _buildPoolRebalanceRoot(hubPoolClient.latestBlockSearched, blockRanges[0][1], bundleData.bundleDepositsV3, bundleData.bundleFillsV3, bundleData.bundleSlowFillsV3, bundleData.unexecutableSlowFills, bundleData.expiredDepositsToRefundV3, {
342
- hubPoolClient: hubPoolClient,
343
- configStoreClient: hubPoolClient.configStoreClient,
344
- })];
345
- case 2:
346
- 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
+ });
347
387
  return [2 /*return*/, {
348
388
  root: root,
349
389
  blockRanges: blockRanges,
@@ -385,33 +425,40 @@ var BundleDataClient = /** @class */ (function () {
385
425
  return [initialBlockRange[1] + 1, blockRange[1]];
386
426
  });
387
427
  }
388
- var hubPoolClient, nextBundleMainnetStartBlock, chainIds, combinedRefunds, widestBundleBlockRanges, pendingBundleBlockRanges, arweaveData, bundleFillsV3, expiredDepositsToRefundV3, start;
389
- return __generator(this, function (_a) {
390
- 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) {
391
431
  case 0:
392
432
  hubPoolClient = this.clients.hubPoolClient;
393
433
  nextBundleMainnetStartBlock = hubPoolClient.getNextBundleStartBlockNumber(this.chainIdListForBundleEvaluationBlockNumbers, hubPoolClient.latestBlockSearched, hubPoolClient.chainId);
394
434
  chainIds = this.clients.configStoreClient.getChainIdIndicesForBlock(nextBundleMainnetStartBlock);
395
435
  combinedRefunds = [];
396
436
  widestBundleBlockRanges = getWidestPossibleExpectedBlockRange(chainIds, this.spokePoolClients, getEndBlockBuffers(chainIds, this.blockRangeEndBlockBuffer), this.clients, this.clients.hubPoolClient.latestBlockSearched, this.clients.configStoreClient.getEnabledChains(this.clients.hubPoolClient.latestBlockSearched));
397
- if (!hubPoolClient.hasPendingProposal()) return [3 /*break*/, 2];
437
+ if (!hubPoolClient.hasPendingProposal()) return [3 /*break*/, 5];
398
438
  pendingBundleBlockRanges = getImpliedBundleBlockRanges(hubPoolClient, this.clients.configStoreClient, hubPoolClient.getLatestProposedRootBundle());
399
439
  return [4 /*yield*/, this.loadArweaveData(pendingBundleBlockRanges)];
400
440
  case 1:
401
- arweaveData = _a.sent();
402
- if (arweaveData === undefined) {
403
- combinedRefunds.push(this.getApproximateRefundsForBlockRange(chainIds, pendingBundleBlockRanges));
404
- }
405
- else {
406
- bundleFillsV3 = arweaveData.bundleFillsV3, expiredDepositsToRefundV3 = arweaveData.expiredDepositsToRefundV3;
407
- combinedRefunds.push(getRefundsFromBundle(bundleFillsV3, expiredDepositsToRefundV3));
408
- }
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:
409
453
  // Shorten the widestBundleBlockRanges now to not double count the pending bundle blocks.
410
454
  widestBundleBlockRanges = getBlockRangeDelta(pendingBundleBlockRanges);
411
- _a.label = 2;
412
- case 2:
455
+ _e.label = 5;
456
+ case 5:
413
457
  start = performance.now();
414
- 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()]);
415
462
  this.logger.debug({
416
463
  at: "BundleDataClient#getNextBundleRefunds",
417
464
  message: "Loading approximate refunds for next bundle in ".concat(Math.round(performance.now() - start) / 1000, "s."),
@@ -554,7 +601,7 @@ var BundleDataClient = /** @class */ (function () {
554
601
  };
555
602
  BundleDataClient.prototype.loadDataFromScratch = function (blockRangesForChains, spokePoolClients) {
556
603
  return __awaiter(this, void 0, void 0, function () {
557
- var start, key, chainIds, bundleDepositsV3, bundleFillsV3, bundleInvalidFillsV3, bundleSlowFillsV3, expiredDepositsToRefundV3, unexecutableSlowFills, _isChainDisabled, _canCreateSlowFillLeaf, _depositIsExpired, _getFillStatusForDeposit, allChainIds, _cachedBundleTimestamps, bundleBlockTimestamps, v3RelayHashes, bundleDepositHashes, olderDepositHashes, 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;
604
+ var start, key, chainIds, bundleDepositsV3, bundleFillsV3, bundleInvalidFillsV3, bundleUnrepayableFillsV3, bundleInvalidSlowFillRequests, bundleSlowFillsV3, expiredDepositsToRefundV3, unexecutableSlowFills, _isChainDisabled, _canCreateSlowFillLeaf, _depositIsExpired, _getFillStatusForDeposit, allChainIds, _cachedBundleTimestamps, bundleBlockTimestamps, v3RelayHashes, bundleDepositHashes, olderDepositHashes, decodeBundleDepositHash, startBlockForMainnet, versionAtProposalBlock, canRefundPrefills, _loop_2, _i, allChainIds_1, originChainId, validatedBundleV3Fills, validatedBundleSlowFills, validatedBundleUnexecutableSlowFills, fillCounter, _loop_3, _a, allChainIds_2, originChainId, promises, _b, v3FillLpFees, v3SlowFillLpFees, v3UnexecutableSlowFillLpFees, v3SpokeEventsReadable;
558
605
  var _this = this;
559
606
  return __generator(this, function (_c) {
560
607
  switch (_c.label) {
@@ -574,6 +621,8 @@ var BundleDataClient = /** @class */ (function () {
574
621
  bundleDepositsV3 = {};
575
622
  bundleFillsV3 = {};
576
623
  bundleInvalidFillsV3 = [];
624
+ bundleUnrepayableFillsV3 = [];
625
+ bundleInvalidSlowFillRequests = [];
577
626
  bundleSlowFillsV3 = {};
578
627
  expiredDepositsToRefundV3 = {};
579
628
  unexecutableSlowFills = {};
@@ -631,10 +680,13 @@ var BundleDataClient = /** @class */ (function () {
631
680
  v3RelayHashes = {};
632
681
  bundleDepositHashes = [];
633
682
  olderDepositHashes = [];
683
+ decodeBundleDepositHash = function (depositHash) {
684
+ var _a = depositHash.split("@"), relayDataHash = _a[0], i = _a[1];
685
+ return { relayDataHash: relayDataHash, index: Number(i) };
686
+ };
634
687
  startBlockForMainnet = getBlockRangeForChain(blockRangesForChains, this.clients.hubPoolClient.chainId, this.chainIdListForBundleEvaluationBlockNumbers)[0];
635
688
  versionAtProposalBlock = this.clients.configStoreClient.getConfigStoreVersionForBlock(startBlockForMainnet);
636
689
  canRefundPrefills = versionAtProposalBlock >= PRE_FILL_MIN_CONFIG_STORE_VERSION || process.env.FORCE_REFUND_PREFILLS === "true";
637
- depositCounter = 0;
638
690
  _loop_2 = function (originChainId) {
639
691
  var originClient = spokePoolClients[originChainId];
640
692
  var originChainBlockRange = getBlockRangeForChain(blockRangesForChains, originChainId, chainIds);
@@ -644,48 +696,45 @@ var BundleDataClient = /** @class */ (function () {
644
696
  continue;
645
697
  }
646
698
  originClient.getDepositsForDestinationChainWithDuplicates(destinationChainId).forEach(function (deposit) {
647
- // Only evaluate deposits that are in this bundle or in previous bundles. This means we cannot issue fill
648
- // refunds or slow fills here for deposits that are in future bundles (i.e. "pre-fills"). Instead, we'll
649
- // evaluate these pre-fills once the deposit is inside the "current" bundle block range.
650
699
  if (deposit.blockNumber > originChainBlockRange[1] || isZeroValueDeposit(deposit)) {
651
700
  return;
652
701
  }
653
- depositCounter++;
654
702
  var relayDataHash = _this.getRelayHashFromEvent(deposit);
655
- // Duplicate deposits are treated like normal deposits.
656
703
  if (!v3RelayHashes[relayDataHash]) {
657
704
  v3RelayHashes[relayDataHash] = {
658
- deposit: deposit,
705
+ deposits: [deposit],
659
706
  fill: undefined,
660
707
  slowFillRequest: undefined,
661
708
  };
662
709
  }
663
- // Once we've saved the deposit hash into v3RelayHashes, then we can exit early here if the inputAmount
664
- // is 0 because there can be no expired amount to refund and no unexecutable slow fill amount to return
665
- // if this deposit did expire. Input amount can only be zero at this point if the message is non-empty,
666
- // but the message doesn't matter for expired deposits and unexecutable slow fills.
667
- if (deposit.inputAmount.eq(0)) {
668
- return;
710
+ else {
711
+ v3RelayHashes[relayDataHash].deposits.push(deposit);
669
712
  }
670
- // Evaluate all expired deposits after fetching fill statuses,
671
- // since we can't know for certain whether an expired deposit was filled a long time ago.
713
+ // Account for duplicate deposits by concatenating the relayDataHash with the count of the number of times
714
+ // we have seen it so far.
715
+ var newBundleDepositHash = "".concat(relayDataHash, "@").concat(v3RelayHashes[relayDataHash].deposits.length - 1);
716
+ var decodedBundleDepositHash = decodeBundleDepositHash(newBundleDepositHash);
717
+ assert(decodedBundleDepositHash.relayDataHash === relayDataHash &&
718
+ decodedBundleDepositHash.index === v3RelayHashes[relayDataHash].deposits.length - 1, "Not using correct bundle deposit hash key");
672
719
  if (deposit.blockNumber >= originChainBlockRange[0]) {
673
- bundleDepositHashes.push(relayDataHash);
720
+ bundleDepositHashes.push(newBundleDepositHash);
674
721
  updateBundleDepositsV3(bundleDepositsV3, deposit);
675
722
  }
676
723
  else if (deposit.blockNumber < originChainBlockRange[0]) {
677
- olderDepositHashes.push(relayDataHash);
724
+ olderDepositHashes.push(newBundleDepositHash);
678
725
  }
679
726
  });
680
727
  }
681
728
  };
729
+ // Prerequisite step: Load all deposit events from the current or older bundles into the v3RelayHashes dictionary
730
+ // for convenient matching with fills.
682
731
  for (_i = 0, allChainIds_1 = allChainIds; _i < allChainIds_1.length; _i++) {
683
732
  originChainId = allChainIds_1[_i];
684
733
  _loop_2(originChainId);
685
734
  }
686
735
  this.logger.debug({
687
736
  at: "BundleDataClient#loadData",
688
- message: "Processed ".concat(depositCounter, " deposits in ").concat(performance.now() - start, "ms."),
737
+ message: "Processed ".concat(bundleDepositHashes.length + olderDepositHashes.length, " deposits in ").concat(performance.now() - start, "ms."),
689
738
  });
690
739
  start = performance.now();
691
740
  validatedBundleV3Fills = [];
@@ -699,7 +748,7 @@ var BundleDataClient = /** @class */ (function () {
699
748
  case 0:
700
749
  originClient = spokePoolClients[originChainId];
701
750
  _loop_4 = function (destinationChainId) {
702
- var destinationClient, destinationChainBlockRange, fastFillsReplacingSlowFills;
751
+ var destinationClient, destinationChainBlockRange, originChainBlockRange, fastFillsReplacingSlowFills;
703
752
  return __generator(this, function (_g) {
704
753
  switch (_g.label) {
705
754
  case 0:
@@ -708,6 +757,7 @@ var BundleDataClient = /** @class */ (function () {
708
757
  }
709
758
  destinationClient = spokePoolClients[destinationChainId];
710
759
  destinationChainBlockRange = getBlockRangeForChain(blockRangesForChains, destinationChainId, chainIds);
760
+ originChainBlockRange = getBlockRangeForChain(blockRangesForChains, originChainId, chainIds);
711
761
  fastFillsReplacingSlowFills = [];
712
762
  return [4 /*yield*/, forEachAsync(destinationClient
713
763
  .getFillsForOriginChain(originChainId)
@@ -715,40 +765,58 @@ var BundleDataClient = /** @class */ (function () {
715
765
  // tokens to the filler. We can't remove non-empty message deposit here in case there is a slow fill
716
766
  // request for the deposit, we'd want to see the fill took place.
717
767
  .filter(function (fill) { return fill.blockNumber <= destinationChainBlockRange[1] && !isZeroValueFillOrSlowFillRequest(fill); }), function (fill) { return __awaiter(_this, void 0, void 0, function () {
718
- var relayDataHash, fillTransaction, originRelayer, historicalDeposit, matchedDeposit;
768
+ var relayDataHash, fillToRefund_1, duplicateDeposits, historicalDeposit, matchedDeposit, fillToRefund;
719
769
  return __generator(this, function (_a) {
720
770
  switch (_a.label) {
721
771
  case 0:
722
- relayDataHash = this.getRelayHashFromEvent(fill);
723
772
  fillCounter++;
724
- if (v3RelayHashes[relayDataHash]) {
725
- if (!v3RelayHashes[relayDataHash].fill) {
726
- assert(isDefined(v3RelayHashes[relayDataHash].deposit), "Deposit should exist in relay hash dictionary.");
727
- // At this point, the v3RelayHashes entry already existed meaning that there is a matching deposit,
728
- // so this fill is validated.
729
- v3RelayHashes[relayDataHash].fill = fill;
730
- if (fill.blockNumber >= destinationChainBlockRange[0]) {
731
- validatedBundleV3Fills.push(__assign(__assign({}, fill), { quoteTimestamp: v3RelayHashes[relayDataHash].deposit.quoteTimestamp }));
732
- // If fill replaced a slow fill request, then mark it as one that might have created an
733
- // unexecutable slow fill. We can't know for sure until we check the slow fill request
734
- // events.
735
- // slow fill requests for deposits from or to lite chains are considered invalid
736
- if (fill.relayExecutionInfo.fillType === FillType.ReplacedSlowFill &&
737
- _canCreateSlowFillLeaf(v3RelayHashes[relayDataHash].deposit)) {
738
- fastFillsReplacingSlowFills.push(relayDataHash);
739
- }
773
+ relayDataHash = this.getRelayHashFromEvent(fill);
774
+ if (!v3RelayHashes[relayDataHash]) return [3 /*break*/, 5];
775
+ if (!!v3RelayHashes[relayDataHash].fill) return [3 /*break*/, 3];
776
+ assert(isDefined(v3RelayHashes[relayDataHash].deposits) && v3RelayHashes[relayDataHash].deposits.length > 0, "Deposit should exist in relay hash dictionary.");
777
+ v3RelayHashes[relayDataHash].fill = fill;
778
+ if (!(fill.blockNumber >= destinationChainBlockRange[0])) return [3 /*break*/, 2];
779
+ return [4 /*yield*/, verifyFillRepayment(fill, destinationClient.spokePool.provider, v3RelayHashes[relayDataHash].deposits[0], allChainIds)];
780
+ case 1:
781
+ fillToRefund_1 = _a.sent();
782
+ if (!isDefined(fillToRefund_1)) {
783
+ bundleUnrepayableFillsV3.push(fill);
784
+ // We don't return here yet because we still need to mark unexecutable slow fill leaves
785
+ // or duplicate deposits. However, we won't issue a fast fill refund.
786
+ }
787
+ else {
788
+ v3RelayHashes[relayDataHash].fill = fillToRefund_1;
789
+ validatedBundleV3Fills.push(__assign(__assign({}, fillToRefund_1), { quoteTimestamp: v3RelayHashes[relayDataHash].deposits[0].quoteTimestamp }));
790
+ duplicateDeposits = v3RelayHashes[relayDataHash].deposits.slice(1);
791
+ duplicateDeposits.forEach(function (duplicateDeposit) {
792
+ if (isSlowFill(fill)) {
793
+ updateExpiredDepositsV3(expiredDepositsToRefundV3, duplicateDeposit);
740
794
  }
741
- }
742
- return [2 /*return*/];
795
+ else {
796
+ validatedBundleV3Fills.push(__assign(__assign({}, fillToRefund_1), { quoteTimestamp: duplicateDeposit.quoteTimestamp }));
797
+ }
798
+ });
743
799
  }
800
+ // If fill replaced a slow fill request, then mark it as one that might have created an
801
+ // unexecutable slow fill. We can't know for sure until we check the slow fill request
802
+ // events.
803
+ if (fill.relayExecutionInfo.fillType === FillType.ReplacedSlowFill &&
804
+ _canCreateSlowFillLeaf(v3RelayHashes[relayDataHash].deposits[0])) {
805
+ fastFillsReplacingSlowFills.push(relayDataHash);
806
+ }
807
+ _a.label = 2;
808
+ case 2: return [3 /*break*/, 4];
809
+ case 3: throw new Error("Duplicate fill detected");
810
+ case 4: return [2 /*return*/];
811
+ case 5:
744
812
  // At this point, there is no relay hash dictionary entry for this fill, so we need to
745
- // instantiate the entry.
813
+ // instantiate the entry. We won't modify the fill.relayer until we match it with a deposit.
746
814
  v3RelayHashes[relayDataHash] = {
747
- deposit: undefined,
815
+ deposits: undefined,
748
816
  fill: fill,
749
817
  slowFillRequest: undefined,
750
818
  };
751
- if (!(fill.blockNumber >= destinationChainBlockRange[0])) return [3 /*break*/, 4];
819
+ if (!(fill.blockNumber >= destinationChainBlockRange[0])) return [3 /*break*/, 9];
752
820
  // Fill has a non-infinite expiry, and we can assume our spoke pool clients have old enough deposits
753
821
  // to conclude that this fill is invalid if we haven't found a matching deposit in memory, so
754
822
  // skip the historical query.
@@ -756,49 +824,58 @@ var BundleDataClient = /** @class */ (function () {
756
824
  bundleInvalidFillsV3.push(fill);
757
825
  return [2 /*return*/];
758
826
  }
759
- if (!(chainIsEvm(fill.repaymentChainId) && !isValidEvmAddress(fill.relayer))) return [3 /*break*/, 2];
760
- return [4 /*yield*/, originClient.spokePool.provider.getTransaction(fill.transactionHash)];
761
- case 1:
762
- fillTransaction = _a.sent();
763
- originRelayer = fillTransaction.from;
764
- // Repayment chain is still an EVM chain, but the msg.sender is a bytes32 address, so the fill is invalid.
765
- if (!isValidEvmAddress(originRelayer)) {
827
+ return [4 /*yield*/, queryHistoricalDepositForFill(originClient, fill)];
828
+ case 6:
829
+ historicalDeposit = _a.sent();
830
+ if (!!historicalDeposit.found) return [3 /*break*/, 7];
831
+ bundleInvalidFillsV3.push(fill);
832
+ return [3 /*break*/, 9];
833
+ case 7:
834
+ matchedDeposit = historicalDeposit.deposit;
835
+ // If deposit is in a following bundle, then this fill will have to be refunded once that deposit
836
+ // is in the current bundle.
837
+ if (matchedDeposit.blockNumber > originChainBlockRange[1]) {
766
838
  bundleInvalidFillsV3.push(fill);
767
839
  return [2 /*return*/];
768
840
  }
769
- // Otherwise, assume the relayer to be repaid is the msg.sender.
770
- fill.relayer = originRelayer;
771
- _a.label = 2;
772
- case 2: return [4 /*yield*/, queryHistoricalDepositForFill(originClient, fill)];
773
- case 3:
774
- historicalDeposit = _a.sent();
775
- if (!historicalDeposit.found) {
776
- bundleInvalidFillsV3.push(fill);
841
+ v3RelayHashes[relayDataHash].deposits = [matchedDeposit];
842
+ return [4 /*yield*/, verifyFillRepayment(fill, destinationClient.spokePool.provider, matchedDeposit, allChainIds)];
843
+ case 8:
844
+ fillToRefund = _a.sent();
845
+ if (!isDefined(fillToRefund)) {
846
+ bundleUnrepayableFillsV3.push(fill);
847
+ // Don't return yet as we still need to mark down any unexecutable slow fill leaves
848
+ // in case this fast fill replaced a slow fill request.
777
849
  }
778
850
  else {
779
- matchedDeposit = historicalDeposit.deposit;
780
851
  // @dev Since queryHistoricalDepositForFill validates the fill by checking individual
781
852
  // object property values against the deposit's, we
782
853
  // sanity check it here by comparing the full relay hashes. If there's an error here then the
783
854
  // historical deposit query is not working as expected.
784
855
  assert(this.getRelayHashFromEvent(matchedDeposit) === relayDataHash, "Relay hashes should match.");
785
- validatedBundleV3Fills.push(__assign(__assign({}, fill), { quoteTimestamp: matchedDeposit.quoteTimestamp }));
786
- v3RelayHashes[relayDataHash].deposit = matchedDeposit;
787
- // slow fill requests for deposits from or to lite chains are considered invalid
788
- if (fill.relayExecutionInfo.fillType === FillType.ReplacedSlowFill &&
789
- _canCreateSlowFillLeaf(matchedDeposit)) {
790
- fastFillsReplacingSlowFills.push(relayDataHash);
791
- }
856
+ validatedBundleV3Fills.push(__assign(__assign({}, fillToRefund), { quoteTimestamp: matchedDeposit.quoteTimestamp }));
857
+ v3RelayHashes[relayDataHash].fill = fillToRefund;
858
+ // No need to check for duplicate deposits here since duplicate deposits with
859
+ // infinite deadlines are impossible to send via unsafeDeposit().
792
860
  }
793
- _a.label = 4;
794
- case 4: return [2 /*return*/];
861
+ if (fill.relayExecutionInfo.fillType === FillType.ReplacedSlowFill &&
862
+ _canCreateSlowFillLeaf(matchedDeposit)) {
863
+ fastFillsReplacingSlowFills.push(relayDataHash);
864
+ }
865
+ _a.label = 9;
866
+ case 9: return [2 /*return*/];
795
867
  }
796
868
  });
797
869
  }); })];
798
870
  case 1:
799
871
  _g.sent();
800
- // Process slow fill requests. One invariant we need to maintain is that we cannot create slow fill requests
801
- // for deposits that would expire in this bundle.
872
+ // Process slow fill requests and produce slow fill leaves while maintaining the following the invariants:
873
+ // - Slow fill leaves cannot be produced for deposits that have expired in this bundle.
874
+ // - Slow fill leaves cannot be produced for deposits that have been filled.
875
+ // Assumptions about fills:
876
+ // - Duplicate slow fill requests for the same relay data hash are impossible to send.
877
+ // - Slow fill requests can only be sent before the deposit's fillDeadline.
878
+ // - Slow fill requests for a deposit that has been filled.
802
879
  return [4 /*yield*/, forEachAsync(destinationClient
803
880
  .getSlowFillRequestsForOriginChain(originChainId)
804
881
  .filter(function (request) {
@@ -811,55 +888,58 @@ var BundleDataClient = /** @class */ (function () {
811
888
  relayDataHash = this.getRelayHashFromEvent(slowFillRequest);
812
889
  if (v3RelayHashes[relayDataHash]) {
813
890
  if (!v3RelayHashes[relayDataHash].slowFillRequest) {
814
- // At this point, the v3RelayHashes entry already existed meaning that there is either a matching
815
- // fill or deposit.
816
891
  v3RelayHashes[relayDataHash].slowFillRequest = slowFillRequest;
817
892
  if (v3RelayHashes[relayDataHash].fill) {
818
- // If there is a fill matching the relay hash, then this slow fill request can't be used
819
- // to create a slow fill for a filled deposit.
893
+ // Exiting here assumes that slow fill requests must precede fills, so if there was a fill
894
+ // following this slow fill request, then we would have already seen it. We don't need to check
895
+ // for a fill older than this slow fill request.
820
896
  return [2 /*return*/];
821
897
  }
822
- assert(isDefined(v3RelayHashes[relayDataHash].deposit), "Deposit should exist in relay hash dictionary.");
823
- matchedDeposit = v3RelayHashes[relayDataHash].deposit;
824
- // If there is no fill matching the relay hash, then this might be a valid slow fill request
825
- // that we should produce a slow fill leaf for. Check if the slow fill request is in the
826
- // destination chain block range.
898
+ assert(isDefined(v3RelayHashes[relayDataHash].deposits) && v3RelayHashes[relayDataHash].deposits.length > 0, "Deposit should exist in relay hash dictionary.");
899
+ matchedDeposit = v3RelayHashes[relayDataHash].deposits[0];
827
900
  if (slowFillRequest.blockNumber >= destinationChainBlockRange[0] &&
828
901
  _canCreateSlowFillLeaf(matchedDeposit) &&
829
- // Deposit must not have expired in this bundle.
830
902
  !_depositIsExpired(matchedDeposit)) {
831
- // At this point, the v3RelayHashes entry already existed meaning that there is a matching deposit,
832
- // so this slow fill request relay data is correct.
833
903
  validatedBundleSlowFills.push(matchedDeposit);
834
904
  }
835
905
  }
906
+ else {
907
+ throw new Error("Duplicate slow fill request detected.");
908
+ }
836
909
  return [2 /*return*/];
837
910
  }
838
911
  // Instantiate dictionary if there is neither a deposit nor fill matching it.
839
912
  v3RelayHashes[relayDataHash] = {
840
- deposit: undefined,
913
+ deposits: undefined,
841
914
  fill: undefined,
842
915
  slowFillRequest: slowFillRequest,
843
916
  };
844
- if (!(slowFillRequest.blockNumber >= destinationChainBlockRange[0] &&
845
- INFINITE_FILL_DEADLINE.eq(slowFillRequest.fillDeadline))) return [3 /*break*/, 2];
917
+ if (!(slowFillRequest.blockNumber >= destinationChainBlockRange[0])) return [3 /*break*/, 2];
918
+ if (!INFINITE_FILL_DEADLINE.eq(slowFillRequest.fillDeadline)) {
919
+ bundleInvalidSlowFillRequests.push(slowFillRequest);
920
+ return [2 /*return*/];
921
+ }
846
922
  return [4 /*yield*/, queryHistoricalDepositForFill(originClient, slowFillRequest)];
847
923
  case 1:
848
924
  historicalDeposit = _a.sent();
849
925
  if (!historicalDeposit.found) {
850
- // TODO: Invalid slow fill request. Maybe worth logging.
926
+ bundleInvalidSlowFillRequests.push(slowFillRequest);
851
927
  return [2 /*return*/];
852
928
  }
853
929
  matchedDeposit = historicalDeposit.deposit;
930
+ // If deposit is in a following bundle, then this slow fill request will have to be created
931
+ // once that deposit is in the current bundle.
932
+ if (matchedDeposit.blockNumber > originChainBlockRange[1]) {
933
+ bundleInvalidSlowFillRequests.push(slowFillRequest);
934
+ return [2 /*return*/];
935
+ }
854
936
  // @dev Since queryHistoricalDepositForFill validates the slow fill request by checking individual
855
937
  // object property values against the deposit's, we
856
938
  // sanity check it here by comparing the full relay hashes. If there's an error here then the
857
939
  // historical deposit query is not working as expected.
858
940
  assert(this.getRelayHashFromEvent(matchedDeposit) === relayDataHash, "Deposit relay hashes should match.");
859
- v3RelayHashes[relayDataHash].deposit = matchedDeposit;
860
- if (!_canCreateSlowFillLeaf(matchedDeposit) ||
861
- // Deposit must not have expired in this bundle.
862
- _depositIsExpired(matchedDeposit)) {
941
+ v3RelayHashes[relayDataHash].deposits = [matchedDeposit];
942
+ if (!_canCreateSlowFillLeaf(matchedDeposit) || _depositIsExpired(matchedDeposit)) {
863
943
  return [2 /*return*/];
864
944
  }
865
945
  validatedBundleSlowFills.push(matchedDeposit);
@@ -869,121 +949,167 @@ var BundleDataClient = /** @class */ (function () {
869
949
  });
870
950
  }); })];
871
951
  case 2:
872
- // Process slow fill requests. One invariant we need to maintain is that we cannot create slow fill requests
873
- // for deposits that would expire in this bundle.
952
+ // Process slow fill requests and produce slow fill leaves while maintaining the following the invariants:
953
+ // - Slow fill leaves cannot be produced for deposits that have expired in this bundle.
954
+ // - Slow fill leaves cannot be produced for deposits that have been filled.
955
+ // Assumptions about fills:
956
+ // - Duplicate slow fill requests for the same relay data hash are impossible to send.
957
+ // - Slow fill requests can only be sent before the deposit's fillDeadline.
958
+ // - Slow fill requests for a deposit that has been filled.
874
959
  _g.sent();
875
- // Deposits can be submitted an arbitrary amount of time after matching fills and slow fill requests.
876
- // Therefore, let's go through each deposit in this bundle again and check a few things in order:
877
- // - Has the deposit been filled ? If so, then we need to issue a relayer refund for
878
- // this "pre-fill" if the fill took place in a previous bundle.
879
- // - Or, has the deposit expired in this bundle? If so, then we need to issue an expiry refund.
880
- // - And finally, has the deposit been slow filled? If so, then we need to issue a slow fill leaf
881
- // for this "pre-slow-fill-request" if this request took place in a previous bundle.
882
- // @todo Only start refunding pre-fills and slow fill requests after a config store version is activated. We
883
- // should remove this check once we've advanced far beyond the version bump block.
884
- return [4 /*yield*/, mapAsync(bundleDepositHashes.filter(function (depositHash) {
885
- var deposit = v3RelayHashes[depositHash].deposit;
886
- return (deposit && deposit.originChainId === originChainId && deposit.destinationChainId === destinationChainId);
887
- }), function (depositHash) { return __awaiter(_this, void 0, void 0, function () {
888
- var _a, deposit, fill, slowFillRequest, fillStatus, prefill;
889
- return __generator(this, function (_b) {
890
- switch (_b.label) {
960
+ // Process deposits and maintain the following invariants:
961
+ // - Deposits matching fills that are not type SlowFill from previous bundle block ranges should produce
962
+ // refunds for those fills.
963
+ // - Deposits matching fills that are type SlowFill from previous bundle block ranges should be refunded to the
964
+ // depositor.
965
+ // - All deposits expiring in this bundle, even those sent in prior bundle block ranges, should be refunded
966
+ // to the depositor.
967
+ // - An expired deposit cannot be refunded if the deposit was filled.
968
+ // - If a deposit from a prior bundle expired in this bundle, had a slow fill request created for it
969
+ // in a prior bundle, and has not been filled yet, then an unexecutable slow fill leaf has been created
970
+ // and needs to be refunded to the HubPool.
971
+ // - Deposits matching slow fill requests from previous bundle block ranges should produce slow fills
972
+ // if the deposit has not been filled.
973
+ // Assumptions:
974
+ // - If the deposit has a matching fill or slow fill request in the bundle then we have already stored
975
+ // it in the relay hashes dictionary.
976
+ // - We've created refunds for all fills in this bundle matching a deposit.
977
+ // - We've created slow fill leaves for all slow fill requests in this bundle matching an unfilled deposit.
978
+ // - Deposits for the same relay data hash can be sent an arbitrary amount of times.
979
+ // - Deposits can be sent an arbitrary amount of time after a fill has been sent for the matching relay data.
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) {
891
985
  case 0:
892
- _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];
893
992
  if (!deposit)
894
993
  throw new Error("Deposit should exist in relay hash dictionary.");
895
- // We are willing to refund a pre-fill multiple times for each duplicate deposit.
896
- // This is because a duplicate deposit for a pre-fill cannot get
897
- // refunded to the depositor anymore because its fill status on-chain has changed to Filled. Therefore
898
- // any duplicate deposits result in a net loss of funds for the depositor and effectively pay out
899
- // the pre-filler.
900
- // If fill exists in memory, then the only case in which we need to create a refund is if the
901
- // the fill occurred in a previous bundle. There are no expiry refunds for filled deposits.
902
- if (fill) {
903
- if (canRefundPrefills && fill.blockNumber < destinationChainBlockRange[0] && !isSlowFill(fill)) {
904
- // If fill is in the current bundle then we can assume there is already a refund for it, so only
905
- // include this pre fill if the fill is in an older bundle. If fill is after this current bundle, then
906
- // we won't consider it, following the previous treatment of fills after the bundle block range.
907
- validatedBundleV3Fills.push(__assign(__assign({}, fill), { quoteTimestamp: deposit.quoteTimestamp }));
908
- }
994
+ if (deposit.originChainId !== originChainId || deposit.destinationChainId !== destinationChainId) {
909
995
  return [2 /*return*/];
910
996
  }
997
+ if (!fill) return [3 /*break*/, 3];
998
+ if (!(canRefundPrefills && fill.blockNumber < destinationChainBlockRange[0])) return [3 /*break*/, 2];
999
+ return [4 /*yield*/, verifyFillRepayment(fill, destinationClient.spokePool.provider, v3RelayHashes[relayDataHash].deposits[0], allChainIds)];
1000
+ case 1:
1001
+ fillToRefund = _c.sent();
1002
+ if (!isDefined(fillToRefund)) {
1003
+ bundleUnrepayableFillsV3.push(fill);
1004
+ }
1005
+ else if (!isSlowFill(fill)) {
1006
+ v3RelayHashes[relayDataHash].fill = fillToRefund;
1007
+ validatedBundleV3Fills.push(__assign(__assign({}, fillToRefund), { quoteTimestamp: deposit.quoteTimestamp }));
1008
+ }
1009
+ else {
1010
+ updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
1011
+ }
1012
+ _c.label = 2;
1013
+ case 2: return [2 /*return*/];
1014
+ case 3:
911
1015
  // If a slow fill request exists in memory, then we know the deposit has not been filled because fills
912
- // must follow slow fill requests and we would have seen the fill already if it existed. Therefore,
913
- // we can conclude that either the deposit has expired and we need to create a deposit expiry refund, or
914
- // we need to create a slow fill leaf for the deposit. The latter should only happen if the slow fill request
915
- // took place in a prior bundle otherwise we would have already created a slow fill leaf for it.
1016
+ // must follow slow fill requests and we would have seen the fill already if it existed.,
1017
+ // We can conclude that either the deposit has expired or we need to create a slow fill leaf for the
1018
+ // deposit because it has not been filled. Slow fill leaves were already created for requests sent
1019
+ // in the current bundle so only create new slow fill leaves for prior bundle deposits.
916
1020
  if (slowFillRequest) {
917
1021
  if (_depositIsExpired(deposit)) {
918
1022
  updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
919
1023
  }
920
1024
  else if (canRefundPrefills &&
921
1025
  slowFillRequest.blockNumber < destinationChainBlockRange[0] &&
922
- _canCreateSlowFillLeaf(deposit)) {
1026
+ _canCreateSlowFillLeaf(deposit) &&
1027
+ validatedBundleSlowFills.every(function (d) { return _this.getRelayHashFromEvent(d) !== relayDataHash; })) {
923
1028
  validatedBundleSlowFills.push(deposit);
924
1029
  }
925
1030
  return [2 /*return*/];
926
1031
  }
927
1032
  return [4 /*yield*/, _getFillStatusForDeposit(deposit, destinationChainBlockRange[1])];
928
- case 1:
929
- fillStatus = _b.sent();
930
- if (!(fillStatus === FillStatus.Filled)) return [3 /*break*/, 3];
931
- return [4 /*yield*/, findFillEvent(destinationClient.spokePool, deposit, destinationClient.deploymentBlock, destinationClient.latestBlockSearched)];
932
- case 2:
933
- prefill = (_b.sent());
934
- if (canRefundPrefills && !isSlowFill(prefill)) {
935
- validatedBundleV3Fills.push(__assign(__assign({}, prefill), { quoteTimestamp: deposit.quoteTimestamp }));
1033
+ case 4:
1034
+ fillStatus = _c.sent();
1035
+ if (!(fillStatus === FillStatus.Filled)) return [3 /*break*/, 8];
1036
+ return [4 /*yield*/, this.findMatchingFillEvent(deposit, destinationClient)];
1037
+ case 5:
1038
+ prefill = _c.sent();
1039
+ assert(isDefined(prefill), "findFillEvent# Cannot find prefill: ".concat(relayDataHash));
1040
+ assert(this.getRelayHashFromEvent(prefill) === relayDataHash, "Relay hashes should match.");
1041
+ if (!canRefundPrefills) return [3 /*break*/, 7];
1042
+ return [4 /*yield*/, verifyFillRepayment(prefill, destinationClient.spokePool.provider, deposit, allChainIds)];
1043
+ case 6:
1044
+ verifiedFill = _c.sent();
1045
+ if (!isDefined(verifiedFill)) {
1046
+ bundleUnrepayableFillsV3.push(prefill);
936
1047
  }
937
- return [3 /*break*/, 4];
938
- case 3:
1048
+ else if (!isSlowFill(verifiedFill)) {
1049
+ validatedBundleV3Fills.push(__assign(__assign({}, verifiedFill), { quoteTimestamp: deposit.quoteTimestamp }));
1050
+ }
1051
+ else {
1052
+ updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
1053
+ }
1054
+ _c.label = 7;
1055
+ case 7: return [3 /*break*/, 9];
1056
+ case 8:
939
1057
  if (_depositIsExpired(deposit)) {
940
1058
  updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
941
1059
  }
942
- // If slow fill requested, then issue a slow fill leaf for the deposit.
943
- else if (fillStatus === FillStatus.RequestedSlowFill) {
944
- // Input and Output tokens must be equivalent on the deposit for this to be slow filled.
945
- // Slow fill requests for deposits from or to lite chains are considered invalid
1060
+ else if (fillStatus === FillStatus.RequestedSlowFill &&
1061
+ // Don't create duplicate slow fill requests for the same deposit.
1062
+ validatedBundleSlowFills.every(function (d) { return _this.getRelayHashFromEvent(d) !== relayDataHash; })) {
946
1063
  if (canRefundPrefills && _canCreateSlowFillLeaf(deposit)) {
947
- // If deposit newly expired, then we can't create a slow fill leaf for it but we can
948
- // create a deposit refund for it.
949
1064
  validatedBundleSlowFills.push(deposit);
950
1065
  }
951
1066
  }
952
- _b.label = 4;
953
- case 4: return [2 /*return*/];
1067
+ _c.label = 9;
1068
+ case 9: return [2 /*return*/];
954
1069
  }
955
1070
  });
956
1071
  }); })];
957
1072
  case 3:
958
- // Deposits can be submitted an arbitrary amount of time after matching fills and slow fill requests.
959
- // Therefore, let's go through each deposit in this bundle again and check a few things in order:
960
- // - Has the deposit been filled ? If so, then we need to issue a relayer refund for
961
- // this "pre-fill" if the fill took place in a previous bundle.
962
- // - Or, has the deposit expired in this bundle? If so, then we need to issue an expiry refund.
963
- // - And finally, has the deposit been slow filled? If so, then we need to issue a slow fill leaf
964
- // for this "pre-slow-fill-request" if this request took place in a previous bundle.
965
- // @todo Only start refunding pre-fills and slow fill requests after a config store version is activated. We
966
- // should remove this check once we've advanced far beyond the version bump block.
1073
+ // Process deposits and maintain the following invariants:
1074
+ // - Deposits matching fills that are not type SlowFill from previous bundle block ranges should produce
1075
+ // refunds for those fills.
1076
+ // - Deposits matching fills that are type SlowFill from previous bundle block ranges should be refunded to the
1077
+ // depositor.
1078
+ // - All deposits expiring in this bundle, even those sent in prior bundle block ranges, should be refunded
1079
+ // to the depositor.
1080
+ // - An expired deposit cannot be refunded if the deposit was filled.
1081
+ // - If a deposit from a prior bundle expired in this bundle, had a slow fill request created for it
1082
+ // in a prior bundle, and has not been filled yet, then an unexecutable slow fill leaf has been created
1083
+ // and needs to be refunded to the HubPool.
1084
+ // - Deposits matching slow fill requests from previous bundle block ranges should produce slow fills
1085
+ // if the deposit has not been filled.
1086
+ // Assumptions:
1087
+ // - If the deposit has a matching fill or slow fill request in the bundle then we have already stored
1088
+ // it in the relay hashes dictionary.
1089
+ // - We've created refunds for all fills in this bundle matching a deposit.
1090
+ // - We've created slow fill leaves for all slow fill requests in this bundle matching an unfilled deposit.
1091
+ // - Deposits for the same relay data hash can be sent an arbitrary amount of times.
1092
+ // - Deposits can be sent an arbitrary amount of time after a fill has been sent for the matching relay data.
967
1093
  _g.sent();
968
1094
  // For all fills that came after a slow fill request, we can now check if the slow fill request
969
1095
  // was a valid one and whether it was created in a previous bundle. If so, then it created a slow fill
970
1096
  // leaf that is now unexecutable.
971
1097
  fastFillsReplacingSlowFills.forEach(function (relayDataHash) {
972
- var _a = v3RelayHashes[relayDataHash], deposit = _a.deposit, slowFillRequest = _a.slowFillRequest, fill = _a.fill;
1098
+ var _a = v3RelayHashes[relayDataHash], deposits = _a.deposits, slowFillRequest = _a.slowFillRequest, fill = _a.fill;
973
1099
  assert((fill === null || fill === void 0 ? void 0 : fill.relayExecutionInfo.fillType) === FillType.ReplacedSlowFill, "Fill type should be ReplacedSlowFill.");
974
1100
  // Needed for TSC - are implicitely checking that deposit exists by making it to this point.
975
- if (!deposit) {
1101
+ if (!deposits || deposits.length < 1) {
976
1102
  throw new Error("Deposit should exist in relay hash dictionary.");
977
1103
  }
978
1104
  // We should never push fast fills involving lite chains here because slow fill requests for them are invalid:
979
- assert(_canCreateSlowFillLeaf(deposit), "fastFillsReplacingSlowFills should contain only deposits that can be slow filled");
1105
+ assert(_canCreateSlowFillLeaf(deposits[0]), "fastFillsReplacingSlowFills should contain only deposits that can be slow filled");
980
1106
  var destinationBlockRange = getBlockRangeForChain(blockRangesForChains, destinationChainId, chainIds);
981
1107
  if (
982
1108
  // If there is a slow fill request in this bundle that matches the relay hash, then there was no slow fill
983
1109
  // created that would be considered excess.
984
1110
  !slowFillRequest ||
985
1111
  slowFillRequest.blockNumber < destinationBlockRange[0]) {
986
- validatedBundleUnexecutableSlowFills.push(deposit);
1112
+ validatedBundleUnexecutableSlowFills.push(deposits[0]);
987
1113
  }
988
1114
  });
989
1115
  return [2 /*return*/];
@@ -1027,13 +1153,17 @@ var BundleDataClient = /** @class */ (function () {
1027
1153
  // For all deposits older than this bundle, we need to check if they expired in this bundle and if they did,
1028
1154
  // whether there was a slow fill created for it in a previous bundle that is now unexecutable and replaced
1029
1155
  // by a new expired deposit refund.
1030
- return [4 /*yield*/, forEachAsync(olderDepositHashes, function (relayDataHash) { return __awaiter(_this, void 0, void 0, function () {
1031
- var _a, deposit, slowFillRequest, fill, destinationChainId, destinationBlockRange, fillStatus;
1032
- return __generator(this, function (_b) {
1033
- switch (_b.label) {
1156
+ return [4 /*yield*/, forEachAsync(olderDepositHashes, function (depositHash) { return __awaiter(_this, void 0, void 0, function () {
1157
+ var _a, relayDataHash, index, _b, deposits, slowFillRequest, fill, deposit, destinationChainId, destinationBlockRange, fillStatus;
1158
+ return __generator(this, function (_c) {
1159
+ switch (_c.label) {
1034
1160
  case 0:
1035
- _a = v3RelayHashes[relayDataHash], deposit = _a.deposit, slowFillRequest = _a.slowFillRequest, fill = _a.fill;
1036
- assert(isDefined(deposit), "Deposit should exist in relay hash dictionary.");
1161
+ _a = decodeBundleDepositHash(depositHash), relayDataHash = _a.relayDataHash, index = _a.index;
1162
+ _b = v3RelayHashes[relayDataHash], deposits = _b.deposits, slowFillRequest = _b.slowFillRequest, fill = _b.fill;
1163
+ if (!deposits || deposits.length < 1) {
1164
+ throw new Error("Deposit should exist in relay hash dictionary.");
1165
+ }
1166
+ deposit = deposits[index];
1037
1167
  destinationChainId = deposit.destinationChainId;
1038
1168
  destinationBlockRange = getBlockRangeForChain(blockRangesForChains, destinationChainId, chainIds);
1039
1169
  if (!
@@ -1047,7 +1177,7 @@ var BundleDataClient = /** @class */ (function () {
1047
1177
  return [3 /*break*/, 2];
1048
1178
  return [4 /*yield*/, _getFillStatusForDeposit(deposit, destinationBlockRange[1])];
1049
1179
  case 1:
1050
- fillStatus = _b.sent();
1180
+ fillStatus = _c.sent();
1051
1181
  // If there is no matching fill and the deposit expired in this bundle and the fill status on-chain is not
1052
1182
  // Filled, then we can to refund it as an expired deposit.
1053
1183
  if (fillStatus !== FillStatus.Filled) {
@@ -1069,7 +1199,7 @@ var BundleDataClient = /** @class */ (function () {
1069
1199
  if (!slowFillRequest || slowFillRequest.blockNumber < destinationBlockRange[0]) {
1070
1200
  validatedBundleUnexecutableSlowFills.push(deposit);
1071
1201
  }
1072
- _b.label = 2;
1202
+ _c.label = 2;
1073
1203
  case 2: return [2 /*return*/];
1074
1204
  }
1075
1205
  });
@@ -1084,7 +1214,7 @@ var BundleDataClient = /** @class */ (function () {
1084
1214
  promises = [
1085
1215
  validatedBundleV3Fills.length > 0
1086
1216
  ? this.clients.hubPoolClient.batchComputeRealizedLpFeePct(validatedBundleV3Fills.map(function (fill) {
1087
- var matchedDeposit = v3RelayHashes[_this.getRelayHashFromEvent(fill)].deposit;
1217
+ var matchedDeposit = v3RelayHashes[_this.getRelayHashFromEvent(fill)].deposits[0];
1088
1218
  assert(isDefined(matchedDeposit), "Deposit should exist in relay hash dictionary.");
1089
1219
  var paymentChainId = getRefundInformationFromFill(fill, _this.clients.hubPoolClient, blockRangesForChains, chainIds, matchedDeposit.fromLiteChain).chainToSendRefundTo;
1090
1220
  return __assign(__assign({}, fill), { paymentChainId: paymentChainId });
@@ -1111,14 +1241,23 @@ var BundleDataClient = /** @class */ (function () {
1111
1241
  v3FillLpFees.forEach(function (_a, idx) {
1112
1242
  var realizedLpFeePct = _a.realizedLpFeePct;
1113
1243
  var fill = validatedBundleV3Fills[idx];
1114
- var associatedDeposit = v3RelayHashes[_this.getRelayHashFromEvent(fill)].deposit;
1244
+ var associatedDeposit = v3RelayHashes[_this.getRelayHashFromEvent(fill)].deposits[0];
1115
1245
  assert(isDefined(associatedDeposit), "Deposit should exist in relay hash dictionary.");
1116
1246
  var _b = getRefundInformationFromFill(fill, _this.clients.hubPoolClient, blockRangesForChains, chainIds, associatedDeposit.fromLiteChain), chainToSendRefundTo = _b.chainToSendRefundTo, repaymentToken = _b.repaymentToken;
1117
- updateBundleFillsV3(bundleFillsV3, fill, realizedLpFeePct, chainToSendRefundTo, repaymentToken);
1247
+ updateBundleFillsV3(bundleFillsV3, fill, realizedLpFeePct, chainToSendRefundTo, repaymentToken, fill.relayer);
1118
1248
  });
1119
1249
  v3SlowFillLpFees.forEach(function (_a, idx) {
1120
1250
  var lpFeePct = _a.realizedLpFeePct;
1121
1251
  var deposit = validatedBundleSlowFills[idx];
1252
+ // We should not create slow fill leaves for duplicate deposit hashes and we should only create a slow
1253
+ // fill leaf for the first deposit (the quote timestamp of the deposit determines the LP fee, so its
1254
+ // important we pick out the correct deposit). Deposits are pushed into validatedBundleSlowFills in ascending
1255
+ // order so the following slice will only match the first deposit.
1256
+ var relayDataHash = _this.getRelayHashFromEvent(deposit);
1257
+ if (validatedBundleSlowFills.slice(0, idx).some(function (d) { return _this.getRelayHashFromEvent(d) === relayDataHash; })) {
1258
+ return;
1259
+ }
1260
+ assert(!_depositIsExpired(deposit), "Cannot create slow fill leaf for expired deposit.");
1122
1261
  updateBundleSlowFills(bundleSlowFillsV3, __assign(__assign({}, deposit), { lpFeePct: lpFeePct }));
1123
1262
  });
1124
1263
  v3UnexecutableSlowFillLpFees.forEach(function (_a, idx) {
@@ -1126,15 +1265,31 @@ var BundleDataClient = /** @class */ (function () {
1126
1265
  var deposit = validatedBundleUnexecutableSlowFills[idx];
1127
1266
  updateBundleExcessSlowFills(unexecutableSlowFills, __assign(__assign({}, deposit), { lpFeePct: lpFeePct }));
1128
1267
  });
1129
- v3SpokeEventsReadable = prettyPrintV3SpokePoolEvents(bundleDepositsV3, bundleFillsV3, bundleInvalidFillsV3, bundleSlowFillsV3, expiredDepositsToRefundV3, unexecutableSlowFills);
1268
+ v3SpokeEventsReadable = prettyPrintV3SpokePoolEvents(bundleDepositsV3, bundleFillsV3, bundleSlowFillsV3, expiredDepositsToRefundV3, unexecutableSlowFills);
1130
1269
  if (bundleInvalidFillsV3.length > 0) {
1131
1270
  this.logger.debug({
1132
1271
  at: "BundleDataClient#loadData",
1133
- message: "Finished loading V3 spoke pool data and found some invalid V3 fills in range",
1272
+ message: "Finished loading V3 spoke pool data and found some invalid fills in range",
1134
1273
  blockRangesForChains: blockRangesForChains,
1135
1274
  bundleInvalidFillsV3: bundleInvalidFillsV3,
1136
1275
  });
1137
1276
  }
1277
+ if (bundleUnrepayableFillsV3.length > 0) {
1278
+ this.logger.debug({
1279
+ at: "BundleDataClient#loadData",
1280
+ message: "Finished loading V3 spoke pool data and found some unrepayable fills in range",
1281
+ blockRangesForChains: blockRangesForChains,
1282
+ bundleUnrepayableFillsV3: bundleUnrepayableFillsV3,
1283
+ });
1284
+ }
1285
+ if (bundleInvalidSlowFillRequests.length > 0) {
1286
+ this.logger.debug({
1287
+ at: "BundleDataClient#loadData",
1288
+ message: "Finished loading V3 spoke pool data and found some invalid slow fill requests in range",
1289
+ blockRangesForChains: blockRangesForChains,
1290
+ bundleInvalidSlowFillRequests: bundleInvalidSlowFillRequests,
1291
+ });
1292
+ }
1138
1293
  this.logger.debug({
1139
1294
  at: "BundleDataClient#loadDataFromScratch",
1140
1295
  message: "Computed bundle data in ".concat(Math.round(performance.now() - start) / 1000, "s."),
@@ -1159,6 +1314,16 @@ var BundleDataClient = /** @class */ (function () {
1159
1314
  BundleDataClient.prototype.getRelayHashFromEvent = function (event) {
1160
1315
  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);
1161
1316
  };
1317
+ BundleDataClient.prototype.findMatchingFillEvent = function (deposit, spokePoolClient) {
1318
+ return __awaiter(this, void 0, void 0, function () {
1319
+ return __generator(this, function (_a) {
1320
+ switch (_a.label) {
1321
+ case 0: return [4 /*yield*/, findFillEvent(spokePoolClient.spokePool, deposit, spokePoolClient.deploymentBlock, spokePoolClient.latestBlockSearched)];
1322
+ case 1: return [2 /*return*/, _a.sent()];
1323
+ }
1324
+ });
1325
+ });
1326
+ };
1162
1327
  BundleDataClient.prototype.getBundleBlockTimestamps = function (chainIds, blockRangesForChains, spokePoolClients) {
1163
1328
  return __awaiter(this, void 0, void 0, function () {
1164
1329
  var _a, _b;
@@ -1168,7 +1333,7 @@ var BundleDataClient = /** @class */ (function () {
1168
1333
  case 0:
1169
1334
  _b = (_a = Object).fromEntries;
1170
1335
  return [4 /*yield*/, mapAsync(chainIds, function (chainId, index) { return __awaiter(_this, void 0, void 0, function () {
1171
- var blockRangeForChain, _startBlockForChain, _endBlockForChain, spokePoolClient, startBlockForChain, endBlockForChain, _a, startTime, endTime, _b;
1336
+ var blockRangeForChain, _startBlockForChain, _endBlockForChain, spokePoolClient, startBlockForChain, endBlockForChain, _a, startTime, _endTime, _b, endBlockDelta, endTime;
1172
1337
  return __generator(this, function (_c) {
1173
1338
  switch (_c.label) {
1174
1339
  case 0:
@@ -1183,7 +1348,7 @@ var BundleDataClient = /** @class */ (function () {
1183
1348
  return [2 /*return*/];
1184
1349
  }
1185
1350
  startBlockForChain = Math.min(_startBlockForChain, spokePoolClient.latestBlockSearched);
1186
- endBlockForChain = Math.min(_endBlockForChain, spokePoolClient.latestBlockSearched);
1351
+ endBlockForChain = Math.min(_endBlockForChain + 1, spokePoolClient.latestBlockSearched);
1187
1352
  return [4 /*yield*/, spokePoolClient.getTimestampForBlock(startBlockForChain)];
1188
1353
  case 1:
1189
1354
  _b = [
@@ -1193,9 +1358,11 @@ var BundleDataClient = /** @class */ (function () {
1193
1358
  case 2:
1194
1359
  _a = _b.concat([
1195
1360
  _c.sent()
1196
- ]), startTime = _a[0], endTime = _a[1];
1361
+ ]), startTime = _a[0], _endTime = _a[1];
1362
+ endBlockDelta = endBlockForChain > startBlockForChain ? 1 : 0;
1363
+ endTime = Math.max(0, _endTime - endBlockDelta);
1197
1364
  // Sanity checks:
1198
- assert(endTime >= startTime, "End time should be greater than start time.");
1365
+ assert(endTime >= startTime, "End time for block ".concat(endBlockForChain, " should be greater than start time for block ").concat(startBlockForChain, ": ").concat(endTime, " >= ").concat(startTime, "."));
1199
1366
  assert(startBlockForChain === 0 || startTime > 0, "Start timestamp must be greater than 0 if the start block is greater than 0.");
1200
1367
  return [2 /*return*/, [chainId, [startTime, endTime]]];
1201
1368
  }