@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.
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.d.ts +5 -4
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.js +345 -187
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.d.ts +1 -2
- package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js +1 -2
- package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/FillUtils.d.ts +5 -1
- package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js +47 -1
- package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/SuperstructUtils.d.ts +4 -4
- package/dist/cjs/clients/SpokePoolClient.d.ts +1 -0
- package/dist/cjs/clients/SpokePoolClient.js +10 -1
- package/dist/cjs/clients/SpokePoolClient.js.map +1 -1
- package/dist/cjs/clients/mocks/MockSpokePoolClient.d.ts +2 -1
- package/dist/cjs/clients/mocks/MockSpokePoolClient.js +11 -0
- package/dist/cjs/clients/mocks/MockSpokePoolClient.js.map +1 -1
- package/dist/cjs/constants.d.ts +0 -1
- package/dist/cjs/constants.js +1 -2
- package/dist/cjs/constants.js.map +1 -1
- package/dist/cjs/providers/index.d.ts +1 -0
- package/dist/cjs/providers/index.js +2 -0
- package/dist/cjs/providers/index.js.map +1 -1
- package/dist/cjs/providers/mockProvider.d.ts +19 -0
- package/dist/cjs/providers/mockProvider.js +70 -0
- package/dist/cjs/providers/mockProvider.js.map +1 -0
- package/dist/cjs/utils/AddressUtils.d.ts +1 -0
- package/dist/cjs/utils/AddressUtils.js +6 -1
- package/dist/cjs/utils/AddressUtils.js.map +1 -1
- package/dist/cjs/utils/BlockUtils.js +1 -0
- package/dist/cjs/utils/BlockUtils.js.map +1 -1
- package/dist/cjs/utils/DepositUtils.js +1 -1
- package/dist/cjs/utils/DepositUtils.js.map +1 -1
- package/dist/cjs/utils/EventUtils.js +21 -0
- package/dist/cjs/utils/EventUtils.js.map +1 -1
- package/dist/cjs/utils/NetworkUtils.js +2 -1
- package/dist/cjs/utils/NetworkUtils.js.map +1 -1
- package/dist/cjs/utils/SpokeUtils.d.ts +1 -0
- package/dist/cjs/utils/SpokeUtils.js +15 -8
- package/dist/cjs/utils/SpokeUtils.js.map +1 -1
- package/dist/cjs/utils/common.d.ts +1 -0
- package/dist/cjs/utils/common.js +2 -1
- package/dist/cjs/utils/common.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/BundleDataClient.d.ts +5 -4
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js +448 -281
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.d.ts +1 -2
- package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js +2 -3
- package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/FillUtils.d.ts +5 -1
- package/dist/esm/clients/BundleDataClient/utils/FillUtils.js +54 -1
- package/dist/esm/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/SuperstructUtils.d.ts +4 -4
- package/dist/esm/clients/SpokePoolClient.d.ts +8 -0
- package/dist/esm/clients/SpokePoolClient.js +17 -1
- package/dist/esm/clients/SpokePoolClient.js.map +1 -1
- package/dist/esm/clients/mocks/MockSpokePoolClient.d.ts +2 -1
- package/dist/esm/clients/mocks/MockSpokePoolClient.js +11 -0
- package/dist/esm/clients/mocks/MockSpokePoolClient.js.map +1 -1
- package/dist/esm/constants.d.ts +0 -1
- package/dist/esm/constants.js +0 -1
- package/dist/esm/constants.js.map +1 -1
- package/dist/esm/providers/index.d.ts +1 -0
- package/dist/esm/providers/index.js +2 -0
- package/dist/esm/providers/index.js.map +1 -1
- package/dist/esm/providers/mockProvider.d.ts +23 -0
- package/dist/esm/providers/mockProvider.js +73 -0
- package/dist/esm/providers/mockProvider.js.map +1 -0
- package/dist/esm/utils/AddressUtils.d.ts +1 -0
- package/dist/esm/utils/AddressUtils.js +9 -0
- package/dist/esm/utils/AddressUtils.js.map +1 -1
- package/dist/esm/utils/BlockUtils.js +1 -0
- package/dist/esm/utils/BlockUtils.js.map +1 -1
- package/dist/esm/utils/DepositUtils.js +2 -2
- package/dist/esm/utils/DepositUtils.js.map +1 -1
- package/dist/esm/utils/EventUtils.js +29 -1
- package/dist/esm/utils/EventUtils.js.map +1 -1
- package/dist/esm/utils/NetworkUtils.js +2 -1
- package/dist/esm/utils/NetworkUtils.js.map +1 -1
- package/dist/esm/utils/SpokeUtils.d.ts +1 -0
- package/dist/esm/utils/SpokeUtils.js +13 -7
- package/dist/esm/utils/SpokeUtils.js.map +1 -1
- package/dist/esm/utils/abi/typechain/Multicall3.d.ts +4 -1
- package/dist/esm/utils/abi/typechain/factories/Multicall3__factory.js.map +1 -1
- package/dist/esm/utils/common.d.ts +1 -0
- package/dist/esm/utils/common.js +1 -0
- package/dist/esm/utils/common.js.map +1 -1
- package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts +5 -4
- package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts.map +1 -1
- package/dist/types/clients/BundleDataClient/utils/DataworkerUtils.d.ts +1 -2
- package/dist/types/clients/BundleDataClient/utils/DataworkerUtils.d.ts.map +1 -1
- package/dist/types/clients/BundleDataClient/utils/FillUtils.d.ts +5 -1
- package/dist/types/clients/BundleDataClient/utils/FillUtils.d.ts.map +1 -1
- package/dist/types/clients/BundleDataClient/utils/SuperstructUtils.d.ts +4 -4
- package/dist/types/clients/SpokePoolClient.d.ts +8 -0
- package/dist/types/clients/SpokePoolClient.d.ts.map +1 -1
- package/dist/types/clients/mocks/MockSpokePoolClient.d.ts +2 -1
- package/dist/types/clients/mocks/MockSpokePoolClient.d.ts.map +1 -1
- package/dist/types/constants.d.ts +0 -1
- package/dist/types/constants.d.ts.map +1 -1
- package/dist/types/providers/index.d.ts +1 -0
- package/dist/types/providers/index.d.ts.map +1 -1
- package/dist/types/providers/mockProvider.d.ts +24 -0
- package/dist/types/providers/mockProvider.d.ts.map +1 -0
- package/dist/types/utils/AddressUtils.d.ts +1 -0
- package/dist/types/utils/AddressUtils.d.ts.map +1 -1
- package/dist/types/utils/BlockUtils.d.ts.map +1 -1
- package/dist/types/utils/EventUtils.d.ts.map +1 -1
- package/dist/types/utils/NetworkUtils.d.ts.map +1 -1
- package/dist/types/utils/SpokeUtils.d.ts +1 -0
- package/dist/types/utils/SpokeUtils.d.ts.map +1 -1
- package/dist/types/utils/abi/typechain/Multicall3.d.ts +4 -1
- package/dist/types/utils/abi/typechain/Multicall3.d.ts.map +1 -1
- package/dist/types/utils/abi/typechain/common.d.ts.map +1 -1
- package/dist/types/utils/abi/typechain/factories/Multicall3__factory.d.ts.map +1 -1
- package/dist/types/utils/common.d.ts +1 -0
- package/dist/types/utils/common.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/clients/BundleDataClient/BundleDataClient.ts +406 -249
- package/src/clients/BundleDataClient/utils/DataworkerUtils.ts +0 -8
- package/src/clients/BundleDataClient/utils/FillUtils.ts +66 -2
- package/src/clients/SpokePoolClient.ts +16 -3
- package/src/clients/mocks/MockSpokePoolClient.ts +14 -0
- package/src/constants.ts +0 -2
- package/src/providers/index.ts +1 -0
- package/src/providers/mockProvider.ts +77 -0
- package/src/utils/AddressUtils.ts +10 -0
- package/src/utils/BlockUtils.ts +1 -0
- package/src/utils/DepositUtils.ts +2 -2
- package/src/utils/EventUtils.ts +29 -1
- package/src/utils/NetworkUtils.ts +2 -1
- package/src/utils/SpokeUtils.ts +21 -8
- package/src/utils/common.ts +2 -0
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
getImpliedBundleBlockRanges,
|
|
34
34
|
isSlowFill,
|
|
35
35
|
mapAsync,
|
|
36
|
+
filterAsync,
|
|
36
37
|
bnUint32Max,
|
|
37
38
|
isZeroValueDeposit,
|
|
38
39
|
findFillEvent,
|
|
@@ -49,10 +50,12 @@ import {
|
|
|
49
50
|
getRefundsFromBundle,
|
|
50
51
|
getWidestPossibleExpectedBlockRange,
|
|
51
52
|
isChainDisabled,
|
|
53
|
+
isEvmRepaymentValid,
|
|
52
54
|
PoolRebalanceRoot,
|
|
53
55
|
prettyPrintV3SpokePoolEvents,
|
|
54
56
|
V3DepositWithBlock,
|
|
55
57
|
V3FillWithBlock,
|
|
58
|
+
verifyFillRepayment,
|
|
56
59
|
} from "./utils";
|
|
57
60
|
import { PRE_FILL_MIN_CONFIG_STORE_VERSION } from "../../constants";
|
|
58
61
|
|
|
@@ -87,12 +90,14 @@ function updateBundleFillsV3(
|
|
|
87
90
|
fill: V3FillWithBlock,
|
|
88
91
|
lpFeePct: BigNumber,
|
|
89
92
|
repaymentChainId: number,
|
|
90
|
-
repaymentToken: string
|
|
93
|
+
repaymentToken: string,
|
|
94
|
+
repaymentAddress: string
|
|
91
95
|
): void {
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
+
// We shouldn't pass any unrepayable fills into this function, so we perform an extra safety check.
|
|
97
|
+
assert(
|
|
98
|
+
chainIsEvm(repaymentChainId) && isEvmRepaymentValid(fill, repaymentChainId),
|
|
99
|
+
"validatedBundleV3Fills dictionary should only contain fills with valid repayment information"
|
|
100
|
+
);
|
|
96
101
|
if (!dict?.[repaymentChainId]?.[repaymentToken]) {
|
|
97
102
|
assign(dict, [repaymentChainId, repaymentToken], {
|
|
98
103
|
fills: [],
|
|
@@ -102,19 +107,19 @@ function updateBundleFillsV3(
|
|
|
102
107
|
});
|
|
103
108
|
}
|
|
104
109
|
|
|
105
|
-
const bundleFill: BundleFillV3 = { ...fill, lpFeePct };
|
|
110
|
+
const bundleFill: BundleFillV3 = { ...fill, lpFeePct, relayer: repaymentAddress };
|
|
106
111
|
|
|
107
112
|
// Add all fills, slow and fast, to dictionary.
|
|
108
113
|
assign(dict, [repaymentChainId, repaymentToken, "fills"], [bundleFill]);
|
|
109
114
|
|
|
110
115
|
// All fills update the bundle LP fees.
|
|
111
116
|
const refundObj = dict[repaymentChainId][repaymentToken];
|
|
112
|
-
const realizedLpFee =
|
|
117
|
+
const realizedLpFee = bundleFill.inputAmount.mul(bundleFill.lpFeePct).div(fixedPointAdjustment);
|
|
113
118
|
refundObj.realizedLpFees = refundObj.realizedLpFees ? refundObj.realizedLpFees.add(realizedLpFee) : realizedLpFee;
|
|
114
119
|
|
|
115
120
|
// Only fast fills get refunded.
|
|
116
|
-
if (!isSlowFill(
|
|
117
|
-
const refundAmount =
|
|
121
|
+
if (!isSlowFill(bundleFill)) {
|
|
122
|
+
const refundAmount = bundleFill.inputAmount.mul(fixedPointAdjustment.sub(lpFeePct)).div(fixedPointAdjustment);
|
|
118
123
|
refundObj.totalRefundAmount = refundObj.totalRefundAmount
|
|
119
124
|
? refundObj.totalRefundAmount.add(refundAmount)
|
|
120
125
|
: refundAmount;
|
|
@@ -122,10 +127,10 @@ function updateBundleFillsV3(
|
|
|
122
127
|
// Instantiate dictionary if it doesn't exist.
|
|
123
128
|
refundObj.refunds ??= {};
|
|
124
129
|
|
|
125
|
-
if (refundObj.refunds[
|
|
126
|
-
refundObj.refunds[
|
|
130
|
+
if (refundObj.refunds[bundleFill.relayer]) {
|
|
131
|
+
refundObj.refunds[bundleFill.relayer] = refundObj.refunds[bundleFill.relayer].add(refundAmount);
|
|
127
132
|
} else {
|
|
128
|
-
refundObj.refunds[
|
|
133
|
+
refundObj.refunds[bundleFill.relayer] = refundAmount;
|
|
129
134
|
}
|
|
130
135
|
}
|
|
131
136
|
}
|
|
@@ -142,6 +147,9 @@ function updateBundleExcessSlowFills(
|
|
|
142
147
|
}
|
|
143
148
|
|
|
144
149
|
function updateBundleSlowFills(dict: BundleSlowFills, deposit: V3DepositWithBlock & { lpFeePct: BigNumber }): void {
|
|
150
|
+
if (chainIsEvm(deposit.destinationChainId) && !isValidEvmAddress(deposit.recipient)) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
145
153
|
const { destinationChainId, outputToken } = deposit;
|
|
146
154
|
if (!dict?.[destinationChainId]?.[outputToken]) {
|
|
147
155
|
assign(dict, [destinationChainId, outputToken], []);
|
|
@@ -245,7 +253,6 @@ export class BundleDataClient {
|
|
|
245
253
|
bundleData: prettyPrintV3SpokePoolEvents(
|
|
246
254
|
bundleData.bundleDepositsV3,
|
|
247
255
|
bundleData.bundleFillsV3,
|
|
248
|
-
[], // Invalid fills are not persisted to Arweave.
|
|
249
256
|
bundleData.bundleSlowFillsV3,
|
|
250
257
|
bundleData.expiredDepositsToRefundV3,
|
|
251
258
|
bundleData.unexecutableSlowFills
|
|
@@ -293,7 +300,7 @@ export class BundleDataClient {
|
|
|
293
300
|
// so as not to affect this approximate refund count.
|
|
294
301
|
const arweaveData = await this.loadArweaveData(bundleEvaluationBlockRanges);
|
|
295
302
|
if (arweaveData === undefined) {
|
|
296
|
-
combinedRefunds = this.getApproximateRefundsForBlockRange(chainIds, bundleEvaluationBlockRanges);
|
|
303
|
+
combinedRefunds = await this.getApproximateRefundsForBlockRange(chainIds, bundleEvaluationBlockRanges);
|
|
297
304
|
} else {
|
|
298
305
|
const { bundleFillsV3, expiredDepositsToRefundV3 } = arweaveData;
|
|
299
306
|
combinedRefunds = getRefundsFromBundle(bundleFillsV3, expiredDepositsToRefundV3);
|
|
@@ -314,56 +321,72 @@ export class BundleDataClient {
|
|
|
314
321
|
}
|
|
315
322
|
|
|
316
323
|
// @dev This helper function should probably be moved to the InventoryClient
|
|
317
|
-
getApproximateRefundsForBlockRange(chainIds: number[], blockRanges: number[][]): CombinedRefunds {
|
|
324
|
+
async getApproximateRefundsForBlockRange(chainIds: number[], blockRanges: number[][]): Promise<CombinedRefunds> {
|
|
318
325
|
const refundsForChain: CombinedRefunds = {};
|
|
319
326
|
for (const chainId of chainIds) {
|
|
320
327
|
if (this.spokePoolClients[chainId] === undefined) {
|
|
321
328
|
continue;
|
|
322
329
|
}
|
|
323
330
|
const chainIndex = chainIds.indexOf(chainId);
|
|
324
|
-
|
|
325
|
-
// @todo This function does not account for pre-fill refunds as it is optimized for speed. The way to detect
|
|
331
|
+
// @dev This function does not account for pre-fill refunds as it is optimized for speed. The way to detect
|
|
326
332
|
// pre-fill refunds is to load all deposits that are unmatched by fills in the spoke pool client's memory
|
|
327
333
|
// and then query the FillStatus on-chain, but that might slow this function down too much. For now, we
|
|
328
334
|
// will live with this expected inaccuracy as it should be small. The pre-fill would have to precede the deposit
|
|
329
335
|
// by more than the caller's event lookback window which is expected to be unlikely.
|
|
330
|
-
this.spokePoolClients[chainId]
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
+
const fillsToCount = await filterAsync(this.spokePoolClients[chainId].getFills(), async (fill) => {
|
|
337
|
+
if (
|
|
338
|
+
fill.blockNumber < blockRanges[chainIndex][0] ||
|
|
339
|
+
fill.blockNumber > blockRanges[chainIndex][1] ||
|
|
340
|
+
isZeroValueFillOrSlowFillRequest(fill)
|
|
341
|
+
) {
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
336
344
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
.forEach((fill) => {
|
|
348
|
-
const matchingDeposit = this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
|
|
349
|
-
assert(isDefined(matchingDeposit), "Deposit not found for fill.");
|
|
350
|
-
const { chainToSendRefundTo, repaymentToken } = getRefundInformationFromFill(
|
|
345
|
+
// If origin spoke pool client isn't defined, we can't validate it.
|
|
346
|
+
if (this.spokePoolClients[fill.originChainId] === undefined) {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
const matchingDeposit = this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
|
|
350
|
+
const hasMatchingDeposit =
|
|
351
|
+
matchingDeposit !== undefined &&
|
|
352
|
+
this.getRelayHashFromEvent(fill) === this.getRelayHashFromEvent(matchingDeposit);
|
|
353
|
+
if (hasMatchingDeposit) {
|
|
354
|
+
const validRepayment = await verifyFillRepayment(
|
|
351
355
|
fill,
|
|
352
|
-
this.
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
+
this.spokePoolClients[fill.destinationChainId].spokePool.provider,
|
|
357
|
+
matchingDeposit,
|
|
358
|
+
// @dev: to get valid repayment chain ID's, get all chain IDs for the bundle block range and remove
|
|
359
|
+
// disabled block ranges.
|
|
360
|
+
this.clients.configStoreClient
|
|
361
|
+
.getChainIdIndicesForBlock(blockRanges[0][1])
|
|
362
|
+
.filter((_chainId, i) => !isChainDisabled(blockRanges[i]))
|
|
356
363
|
);
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
}
|
|
364
|
+
if (!isDefined(validRepayment)) {
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return hasMatchingDeposit;
|
|
369
|
+
});
|
|
370
|
+
fillsToCount.forEach((fill) => {
|
|
371
|
+
const matchingDeposit = this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
|
|
372
|
+
assert(isDefined(matchingDeposit), "Deposit not found for fill.");
|
|
373
|
+
const { chainToSendRefundTo, repaymentToken } = getRefundInformationFromFill(
|
|
374
|
+
fill,
|
|
375
|
+
this.clients.hubPoolClient,
|
|
376
|
+
blockRanges,
|
|
377
|
+
this.chainIdListForBundleEvaluationBlockNumbers,
|
|
378
|
+
matchingDeposit!.fromLiteChain // Use ! because we've already asserted that matchingDeposit is defined.
|
|
379
|
+
);
|
|
380
|
+
// Assume that lp fees are 0 for the sake of speed. In the future we could batch compute
|
|
381
|
+
// these or make hardcoded assumptions based on the origin-repayment chain direction. This might result
|
|
382
|
+
// in slight over estimations of refunds, but its not clear whether underestimating or overestimating is
|
|
383
|
+
// worst from the relayer's perspective.
|
|
384
|
+
const { relayer, inputAmount: refundAmount } = fill;
|
|
385
|
+
refundsForChain[chainToSendRefundTo] ??= {};
|
|
386
|
+
refundsForChain[chainToSendRefundTo][repaymentToken] ??= {};
|
|
387
|
+
const existingRefundAmount = refundsForChain[chainToSendRefundTo][repaymentToken][relayer] ?? bnZero;
|
|
388
|
+
refundsForChain[chainToSendRefundTo][repaymentToken][relayer] = existingRefundAmount.add(refundAmount);
|
|
389
|
+
});
|
|
367
390
|
}
|
|
368
391
|
return refundsForChain;
|
|
369
392
|
}
|
|
@@ -407,7 +430,7 @@ export class BundleDataClient {
|
|
|
407
430
|
async getLatestPoolRebalanceRoot(): Promise<{ root: PoolRebalanceRoot; blockRanges: number[][] }> {
|
|
408
431
|
const { bundleData, blockRanges } = await this.getLatestProposedBundleData();
|
|
409
432
|
const hubPoolClient = this.clients.hubPoolClient;
|
|
410
|
-
const root =
|
|
433
|
+
const root = _buildPoolRebalanceRoot(
|
|
411
434
|
hubPoolClient.latestBlockSearched,
|
|
412
435
|
blockRanges[0][1],
|
|
413
436
|
bundleData.bundleDepositsV3,
|
|
@@ -490,7 +513,7 @@ export class BundleDataClient {
|
|
|
490
513
|
// ok for this use case.
|
|
491
514
|
const arweaveData = await this.loadArweaveData(pendingBundleBlockRanges);
|
|
492
515
|
if (arweaveData === undefined) {
|
|
493
|
-
combinedRefunds.push(this.getApproximateRefundsForBlockRange(chainIds, pendingBundleBlockRanges));
|
|
516
|
+
combinedRefunds.push(await this.getApproximateRefundsForBlockRange(chainIds, pendingBundleBlockRanges));
|
|
494
517
|
} else {
|
|
495
518
|
const { bundleFillsV3, expiredDepositsToRefundV3 } = arweaveData;
|
|
496
519
|
combinedRefunds.push(getRefundsFromBundle(bundleFillsV3, expiredDepositsToRefundV3));
|
|
@@ -505,7 +528,7 @@ export class BundleDataClient {
|
|
|
505
528
|
// - Only look up fills sent after the pending bundle's end blocks
|
|
506
529
|
// - Skip LP fee computations and just assume the relayer is being refunded the full deposit.inputAmount
|
|
507
530
|
const start = performance.now();
|
|
508
|
-
combinedRefunds.push(this.getApproximateRefundsForBlockRange(chainIds, widestBundleBlockRanges));
|
|
531
|
+
combinedRefunds.push(await this.getApproximateRefundsForBlockRange(chainIds, widestBundleBlockRanges));
|
|
509
532
|
this.logger.debug({
|
|
510
533
|
at: "BundleDataClient#getNextBundleRefunds",
|
|
511
534
|
message: `Loading approximate refunds for next bundle in ${Math.round(performance.now() - start) / 1000}s.`,
|
|
@@ -672,6 +695,8 @@ export class BundleDataClient {
|
|
|
672
695
|
const bundleDepositsV3: BundleDepositsV3 = {}; // Deposits in bundle block range.
|
|
673
696
|
const bundleFillsV3: BundleFillsV3 = {}; // Fills to refund in bundle block range.
|
|
674
697
|
const bundleInvalidFillsV3: V3FillWithBlock[] = []; // Fills that are not valid in this bundle.
|
|
698
|
+
const bundleUnrepayableFillsV3: V3FillWithBlock[] = []; // Fills that are not repayable in this bundle.
|
|
699
|
+
const bundleInvalidSlowFillRequests: SlowFillRequestWithBlock[] = []; // Slow fill requests that are not valid in this bundle.
|
|
675
700
|
const bundleSlowFillsV3: BundleSlowFills = {}; // Deposits that we need to send slow fills
|
|
676
701
|
// for in this bundle.
|
|
677
702
|
const expiredDepositsToRefundV3: ExpiredDepositsToRefundV3 = {};
|
|
@@ -758,7 +783,7 @@ export class BundleDataClient {
|
|
|
758
783
|
// Note: Since there are no partial fills in v3, there should only be one fill per relay hash.
|
|
759
784
|
// Moreover, the SpokePool blocks multiple slow fill requests, so
|
|
760
785
|
// there should also only be one slow fill request per relay hash.
|
|
761
|
-
|
|
786
|
+
deposits?: V3DepositWithBlock[];
|
|
762
787
|
fill?: V3FillWithBlock;
|
|
763
788
|
slowFillRequest?: SlowFillRequestWithBlock;
|
|
764
789
|
};
|
|
@@ -769,6 +794,11 @@ export class BundleDataClient {
|
|
|
769
794
|
const bundleDepositHashes: string[] = [];
|
|
770
795
|
const olderDepositHashes: string[] = [];
|
|
771
796
|
|
|
797
|
+
const decodeBundleDepositHash = (depositHash: string): { relayDataHash: string; index: number } => {
|
|
798
|
+
const [relayDataHash, i] = depositHash.split("@");
|
|
799
|
+
return { relayDataHash, index: Number(i) };
|
|
800
|
+
};
|
|
801
|
+
|
|
772
802
|
// We use the following toggle to aid with the migration to pre-fills. The first bundle proposed using this
|
|
773
803
|
// pre-fill logic can double refund pre-fills that have already been filled in the last bundle, because the
|
|
774
804
|
// last bundle did not recognize a fill as a pre-fill. Therefore the developer should ensure that the version
|
|
@@ -785,7 +815,8 @@ export class BundleDataClient {
|
|
|
785
815
|
const canRefundPrefills =
|
|
786
816
|
versionAtProposalBlock >= PRE_FILL_MIN_CONFIG_STORE_VERSION || process.env.FORCE_REFUND_PREFILLS === "true";
|
|
787
817
|
|
|
788
|
-
|
|
818
|
+
// Prerequisite step: Load all deposit events from the current or older bundles into the v3RelayHashes dictionary
|
|
819
|
+
// for convenient matching with fills.
|
|
789
820
|
for (const originChainId of allChainIds) {
|
|
790
821
|
const originClient = spokePoolClients[originChainId];
|
|
791
822
|
const originChainBlockRange = getBlockRangeForChain(blockRangesForChains, originChainId, chainIds);
|
|
@@ -795,50 +826,59 @@ export class BundleDataClient {
|
|
|
795
826
|
continue;
|
|
796
827
|
}
|
|
797
828
|
originClient.getDepositsForDestinationChainWithDuplicates(destinationChainId).forEach((deposit) => {
|
|
798
|
-
// Only evaluate deposits that are in this bundle or in previous bundles. This means we cannot issue fill
|
|
799
|
-
// refunds or slow fills here for deposits that are in future bundles (i.e. "pre-fills"). Instead, we'll
|
|
800
|
-
// evaluate these pre-fills once the deposit is inside the "current" bundle block range.
|
|
801
829
|
if (deposit.blockNumber > originChainBlockRange[1] || isZeroValueDeposit(deposit)) {
|
|
802
830
|
return;
|
|
803
831
|
}
|
|
804
|
-
depositCounter++;
|
|
805
832
|
const relayDataHash = this.getRelayHashFromEvent(deposit);
|
|
806
833
|
|
|
807
|
-
// Duplicate deposits are treated like normal deposits.
|
|
808
834
|
if (!v3RelayHashes[relayDataHash]) {
|
|
809
835
|
v3RelayHashes[relayDataHash] = {
|
|
810
|
-
|
|
836
|
+
deposits: [deposit],
|
|
811
837
|
fill: undefined,
|
|
812
838
|
slowFillRequest: undefined,
|
|
813
839
|
};
|
|
840
|
+
} else {
|
|
841
|
+
v3RelayHashes[relayDataHash].deposits!.push(deposit);
|
|
814
842
|
}
|
|
815
843
|
|
|
816
|
-
//
|
|
817
|
-
//
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
// since we can't know for certain whether an expired deposit was filled a long time ago.
|
|
844
|
+
// Account for duplicate deposits by concatenating the relayDataHash with the count of the number of times
|
|
845
|
+
// we have seen it so far.
|
|
846
|
+
const newBundleDepositHash = `${relayDataHash}@${v3RelayHashes[relayDataHash].deposits!.length - 1}`;
|
|
847
|
+
const decodedBundleDepositHash = decodeBundleDepositHash(newBundleDepositHash);
|
|
848
|
+
assert(
|
|
849
|
+
decodedBundleDepositHash.relayDataHash === relayDataHash &&
|
|
850
|
+
decodedBundleDepositHash.index === v3RelayHashes[relayDataHash].deposits!.length - 1,
|
|
851
|
+
"Not using correct bundle deposit hash key"
|
|
852
|
+
);
|
|
826
853
|
if (deposit.blockNumber >= originChainBlockRange[0]) {
|
|
827
|
-
bundleDepositHashes.push(
|
|
854
|
+
bundleDepositHashes.push(newBundleDepositHash);
|
|
828
855
|
updateBundleDepositsV3(bundleDepositsV3, deposit);
|
|
829
856
|
} else if (deposit.blockNumber < originChainBlockRange[0]) {
|
|
830
|
-
olderDepositHashes.push(
|
|
857
|
+
olderDepositHashes.push(newBundleDepositHash);
|
|
831
858
|
}
|
|
832
859
|
});
|
|
833
860
|
}
|
|
834
861
|
}
|
|
835
862
|
this.logger.debug({
|
|
836
863
|
at: "BundleDataClient#loadData",
|
|
837
|
-
message: `Processed ${
|
|
864
|
+
message: `Processed ${bundleDepositHashes.length + olderDepositHashes.length} deposits in ${
|
|
865
|
+
performance.now() - start
|
|
866
|
+
}ms.`,
|
|
838
867
|
});
|
|
839
868
|
start = performance.now();
|
|
840
869
|
|
|
841
|
-
// Process fills
|
|
870
|
+
// Process fills and maintain the following the invariants:
|
|
871
|
+
// - Every single fill whose type is not SlowFill in the bundle block range whose relay data matches
|
|
872
|
+
// with a deposit in the same or an older range produces a refund to the filler,
|
|
873
|
+
// unless the specified filler address cannot be repaid on the repayment chain.
|
|
874
|
+
// - Fills can match with duplicate deposits, so for every matched fill whose type is not SlowFill
|
|
875
|
+
// in the bundle block range, produce a refund to the filler for each matched deposit.
|
|
876
|
+
// - For every SlowFill in the block range that matches with multiple deposits, produce a refund to the depositor
|
|
877
|
+
// for every deposit except except the first.
|
|
878
|
+
|
|
879
|
+
// Assumptions about fills:
|
|
880
|
+
// - Duplicate fills for the same relay data hash are impossible to send.
|
|
881
|
+
// - Fills can only be sent before the deposit's fillDeadline.
|
|
842
882
|
const validatedBundleV3Fills: (V3FillWithBlock & { quoteTimestamp: number })[] = [];
|
|
843
883
|
const validatedBundleSlowFills: V3DepositWithBlock[] = [];
|
|
844
884
|
const validatedBundleUnexecutableSlowFills: V3DepositWithBlock[] = [];
|
|
@@ -852,9 +892,8 @@ export class BundleDataClient {
|
|
|
852
892
|
|
|
853
893
|
const destinationClient = spokePoolClients[destinationChainId];
|
|
854
894
|
const destinationChainBlockRange = getBlockRangeForChain(blockRangesForChains, destinationChainId, chainIds);
|
|
895
|
+
const originChainBlockRange = getBlockRangeForChain(blockRangesForChains, originChainId, chainIds);
|
|
855
896
|
|
|
856
|
-
// Keep track of fast fills that replaced slow fills, which we'll use to create "unexecutable" slow fills
|
|
857
|
-
// if the slow fill request was sent in a prior bundle.
|
|
858
897
|
const fastFillsReplacingSlowFills: string[] = [];
|
|
859
898
|
await forEachAsync(
|
|
860
899
|
destinationClient
|
|
@@ -866,47 +905,79 @@ export class BundleDataClient {
|
|
|
866
905
|
(fill) => fill.blockNumber <= destinationChainBlockRange[1] && !isZeroValueFillOrSlowFillRequest(fill)
|
|
867
906
|
),
|
|
868
907
|
async (fill) => {
|
|
869
|
-
const relayDataHash = this.getRelayHashFromEvent(fill);
|
|
870
908
|
fillCounter++;
|
|
871
|
-
|
|
909
|
+
const relayDataHash = this.getRelayHashFromEvent(fill);
|
|
872
910
|
if (v3RelayHashes[relayDataHash]) {
|
|
873
911
|
if (!v3RelayHashes[relayDataHash].fill) {
|
|
874
912
|
assert(
|
|
875
|
-
isDefined(v3RelayHashes[relayDataHash].
|
|
913
|
+
isDefined(v3RelayHashes[relayDataHash].deposits) && v3RelayHashes[relayDataHash].deposits!.length > 0,
|
|
876
914
|
"Deposit should exist in relay hash dictionary."
|
|
877
915
|
);
|
|
878
|
-
// At this point, the v3RelayHashes entry already existed meaning that there is a matching deposit,
|
|
879
|
-
// so this fill is validated.
|
|
880
916
|
v3RelayHashes[relayDataHash].fill = fill;
|
|
881
917
|
if (fill.blockNumber >= destinationChainBlockRange[0]) {
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
918
|
+
const fillToRefund = await verifyFillRepayment(
|
|
919
|
+
fill,
|
|
920
|
+
destinationClient.spokePool.provider,
|
|
921
|
+
v3RelayHashes[relayDataHash].deposits![0],
|
|
922
|
+
allChainIds
|
|
923
|
+
);
|
|
924
|
+
if (!isDefined(fillToRefund)) {
|
|
925
|
+
bundleUnrepayableFillsV3.push(fill);
|
|
926
|
+
// We don't return here yet because we still need to mark unexecutable slow fill leaves
|
|
927
|
+
// or duplicate deposits. However, we won't issue a fast fill refund.
|
|
928
|
+
} else {
|
|
929
|
+
v3RelayHashes[relayDataHash].fill = fillToRefund;
|
|
930
|
+
validatedBundleV3Fills.push({
|
|
931
|
+
...fillToRefund,
|
|
932
|
+
quoteTimestamp: v3RelayHashes[relayDataHash].deposits![0].quoteTimestamp,
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
// Now that we know this deposit has been filled on-chain, identify any duplicate deposits
|
|
936
|
+
// sent for this fill and refund them to the filler, because this value would not be paid out
|
|
937
|
+
// otherwise. These deposits can no longer expire and get refunded as an expired deposit,
|
|
938
|
+
// and they won't trigger a pre-fill refund because the fill is in this bundle.
|
|
939
|
+
// Pre-fill refunds only happen when deposits are sent in this bundle and the
|
|
940
|
+
// fill is from a prior bundle. Paying out the filler keeps the behavior consistent for how
|
|
941
|
+
// we deal with duplicate deposits regardless if the deposit is matched with a pre-fill or
|
|
942
|
+
// a current bundle fill.
|
|
943
|
+
const duplicateDeposits = v3RelayHashes[relayDataHash].deposits!.slice(1);
|
|
944
|
+
duplicateDeposits.forEach((duplicateDeposit) => {
|
|
945
|
+
if (isSlowFill(fill)) {
|
|
946
|
+
updateExpiredDepositsV3(expiredDepositsToRefundV3, duplicateDeposit);
|
|
947
|
+
} else {
|
|
948
|
+
validatedBundleV3Fills.push({
|
|
949
|
+
...fillToRefund,
|
|
950
|
+
quoteTimestamp: duplicateDeposit.quoteTimestamp,
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
|
|
886
956
|
// If fill replaced a slow fill request, then mark it as one that might have created an
|
|
887
957
|
// unexecutable slow fill. We can't know for sure until we check the slow fill request
|
|
888
958
|
// events.
|
|
889
|
-
// slow fill requests for deposits from or to lite chains are considered invalid
|
|
890
959
|
if (
|
|
891
960
|
fill.relayExecutionInfo.fillType === FillType.ReplacedSlowFill &&
|
|
892
|
-
_canCreateSlowFillLeaf(v3RelayHashes[relayDataHash].
|
|
961
|
+
_canCreateSlowFillLeaf(v3RelayHashes[relayDataHash].deposits![0])
|
|
893
962
|
) {
|
|
894
963
|
fastFillsReplacingSlowFills.push(relayDataHash);
|
|
895
964
|
}
|
|
896
965
|
}
|
|
966
|
+
} else {
|
|
967
|
+
throw new Error("Duplicate fill detected");
|
|
897
968
|
}
|
|
898
969
|
return;
|
|
899
970
|
}
|
|
900
971
|
|
|
901
972
|
// At this point, there is no relay hash dictionary entry for this fill, so we need to
|
|
902
|
-
// instantiate the entry.
|
|
973
|
+
// instantiate the entry. We won't modify the fill.relayer until we match it with a deposit.
|
|
903
974
|
v3RelayHashes[relayDataHash] = {
|
|
904
|
-
|
|
905
|
-
fill
|
|
975
|
+
deposits: undefined,
|
|
976
|
+
fill,
|
|
906
977
|
slowFillRequest: undefined,
|
|
907
978
|
};
|
|
908
979
|
|
|
909
|
-
// TODO: We
|
|
980
|
+
// TODO: We can remove the following historical query once we deprecate the deposit()
|
|
910
981
|
// function since there won't be any old, unexpired deposits anymore assuming the spoke pool client
|
|
911
982
|
// lookbacks have been validated, which they should be before we run this function.
|
|
912
983
|
|
|
@@ -922,18 +993,6 @@ export class BundleDataClient {
|
|
|
922
993
|
bundleInvalidFillsV3.push(fill);
|
|
923
994
|
return;
|
|
924
995
|
}
|
|
925
|
-
// If the fill's repayment address is not a valid EVM address and the repayment chain is an EVM chain, the fill is invalid.
|
|
926
|
-
if (chainIsEvm(fill.repaymentChainId) && !isValidEvmAddress(fill.relayer)) {
|
|
927
|
-
const fillTransaction = await originClient.spokePool.provider.getTransaction(fill.transactionHash);
|
|
928
|
-
const originRelayer = fillTransaction.from;
|
|
929
|
-
// Repayment chain is still an EVM chain, but the msg.sender is a bytes32 address, so the fill is invalid.
|
|
930
|
-
if (!isValidEvmAddress(originRelayer)) {
|
|
931
|
-
bundleInvalidFillsV3.push(fill);
|
|
932
|
-
return;
|
|
933
|
-
}
|
|
934
|
-
// Otherwise, assume the relayer to be repaid is the msg.sender.
|
|
935
|
-
fill.relayer = originRelayer;
|
|
936
|
-
}
|
|
937
996
|
// If deposit is using the deterministic relay hash feature, then the following binary search-based
|
|
938
997
|
// algorithm will not work. However, it is impossible to emit an infinite fill deadline using
|
|
939
998
|
// the unsafeDepositV3 function so there is no need to catch the special case.
|
|
@@ -942,17 +1001,40 @@ export class BundleDataClient {
|
|
|
942
1001
|
bundleInvalidFillsV3.push(fill);
|
|
943
1002
|
} else {
|
|
944
1003
|
const matchedDeposit = historicalDeposit.deposit;
|
|
945
|
-
//
|
|
946
|
-
//
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
1004
|
+
// If deposit is in a following bundle, then this fill will have to be refunded once that deposit
|
|
1005
|
+
// is in the current bundle.
|
|
1006
|
+
if (matchedDeposit.blockNumber > originChainBlockRange[1]) {
|
|
1007
|
+
bundleInvalidFillsV3.push(fill);
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
v3RelayHashes[relayDataHash].deposits = [matchedDeposit];
|
|
1011
|
+
|
|
1012
|
+
const fillToRefund = await verifyFillRepayment(
|
|
1013
|
+
fill,
|
|
1014
|
+
destinationClient.spokePool.provider,
|
|
1015
|
+
matchedDeposit,
|
|
1016
|
+
allChainIds
|
|
1017
|
+
);
|
|
1018
|
+
if (!isDefined(fillToRefund)) {
|
|
1019
|
+
bundleUnrepayableFillsV3.push(fill);
|
|
1020
|
+
// Don't return yet as we still need to mark down any unexecutable slow fill leaves
|
|
1021
|
+
// in case this fast fill replaced a slow fill request.
|
|
1022
|
+
} else {
|
|
1023
|
+
// @dev Since queryHistoricalDepositForFill validates the fill by checking individual
|
|
1024
|
+
// object property values against the deposit's, we
|
|
1025
|
+
// sanity check it here by comparing the full relay hashes. If there's an error here then the
|
|
1026
|
+
// historical deposit query is not working as expected.
|
|
1027
|
+
assert(this.getRelayHashFromEvent(matchedDeposit) === relayDataHash, "Relay hashes should match.");
|
|
1028
|
+
validatedBundleV3Fills.push({
|
|
1029
|
+
...fillToRefund,
|
|
1030
|
+
quoteTimestamp: matchedDeposit.quoteTimestamp,
|
|
1031
|
+
});
|
|
1032
|
+
v3RelayHashes[relayDataHash].fill = fillToRefund;
|
|
1033
|
+
|
|
1034
|
+
// No need to check for duplicate deposits here since duplicate deposits with
|
|
1035
|
+
// infinite deadlines are impossible to send via unsafeDeposit().
|
|
1036
|
+
}
|
|
1037
|
+
|
|
956
1038
|
if (
|
|
957
1039
|
fill.relayExecutionInfo.fillType === FillType.ReplacedSlowFill &&
|
|
958
1040
|
_canCreateSlowFillLeaf(matchedDeposit)
|
|
@@ -964,8 +1046,14 @@ export class BundleDataClient {
|
|
|
964
1046
|
}
|
|
965
1047
|
);
|
|
966
1048
|
|
|
967
|
-
// Process slow fill requests
|
|
968
|
-
// for deposits that
|
|
1049
|
+
// Process slow fill requests and produce slow fill leaves while maintaining the following the invariants:
|
|
1050
|
+
// - Slow fill leaves cannot be produced for deposits that have expired in this bundle.
|
|
1051
|
+
// - Slow fill leaves cannot be produced for deposits that have been filled.
|
|
1052
|
+
|
|
1053
|
+
// Assumptions about fills:
|
|
1054
|
+
// - Duplicate slow fill requests for the same relay data hash are impossible to send.
|
|
1055
|
+
// - Slow fill requests can only be sent before the deposit's fillDeadline.
|
|
1056
|
+
// - Slow fill requests for a deposit that has been filled.
|
|
969
1057
|
await forEachAsync(
|
|
970
1058
|
destinationClient
|
|
971
1059
|
.getSlowFillRequestsForOriginChain(originChainId)
|
|
@@ -978,46 +1066,40 @@ export class BundleDataClient {
|
|
|
978
1066
|
|
|
979
1067
|
if (v3RelayHashes[relayDataHash]) {
|
|
980
1068
|
if (!v3RelayHashes[relayDataHash].slowFillRequest) {
|
|
981
|
-
// At this point, the v3RelayHashes entry already existed meaning that there is either a matching
|
|
982
|
-
// fill or deposit.
|
|
983
1069
|
v3RelayHashes[relayDataHash].slowFillRequest = slowFillRequest;
|
|
984
1070
|
if (v3RelayHashes[relayDataHash].fill) {
|
|
985
|
-
//
|
|
986
|
-
//
|
|
1071
|
+
// Exiting here assumes that slow fill requests must precede fills, so if there was a fill
|
|
1072
|
+
// following this slow fill request, then we would have already seen it. We don't need to check
|
|
1073
|
+
// for a fill older than this slow fill request.
|
|
987
1074
|
return;
|
|
988
1075
|
}
|
|
989
1076
|
assert(
|
|
990
|
-
isDefined(v3RelayHashes[relayDataHash].
|
|
1077
|
+
isDefined(v3RelayHashes[relayDataHash].deposits) && v3RelayHashes[relayDataHash].deposits!.length > 0,
|
|
991
1078
|
"Deposit should exist in relay hash dictionary."
|
|
992
1079
|
);
|
|
993
|
-
|
|
994
|
-
const matchedDeposit = v3RelayHashes[relayDataHash].deposit!;
|
|
1080
|
+
const matchedDeposit = v3RelayHashes[relayDataHash].deposits![0];
|
|
995
1081
|
|
|
996
|
-
// If there is no fill matching the relay hash, then this might be a valid slow fill request
|
|
997
|
-
// that we should produce a slow fill leaf for. Check if the slow fill request is in the
|
|
998
|
-
// destination chain block range.
|
|
999
1082
|
if (
|
|
1000
1083
|
slowFillRequest.blockNumber >= destinationChainBlockRange[0] &&
|
|
1001
1084
|
_canCreateSlowFillLeaf(matchedDeposit) &&
|
|
1002
|
-
// Deposit must not have expired in this bundle.
|
|
1003
1085
|
!_depositIsExpired(matchedDeposit)
|
|
1004
1086
|
) {
|
|
1005
|
-
// At this point, the v3RelayHashes entry already existed meaning that there is a matching deposit,
|
|
1006
|
-
// so this slow fill request relay data is correct.
|
|
1007
1087
|
validatedBundleSlowFills.push(matchedDeposit);
|
|
1008
1088
|
}
|
|
1089
|
+
} else {
|
|
1090
|
+
throw new Error("Duplicate slow fill request detected.");
|
|
1009
1091
|
}
|
|
1010
1092
|
return;
|
|
1011
1093
|
}
|
|
1012
1094
|
|
|
1013
1095
|
// Instantiate dictionary if there is neither a deposit nor fill matching it.
|
|
1014
1096
|
v3RelayHashes[relayDataHash] = {
|
|
1015
|
-
|
|
1097
|
+
deposits: undefined,
|
|
1016
1098
|
fill: undefined,
|
|
1017
1099
|
slowFillRequest: slowFillRequest,
|
|
1018
1100
|
};
|
|
1019
1101
|
|
|
1020
|
-
// TODO: We
|
|
1102
|
+
// TODO: We can remove the following historical query once we deprecate the deposit()
|
|
1021
1103
|
// function since there won't be any old, unexpired deposits anymore assuming the spoke pool client
|
|
1022
1104
|
// lookbacks have been validated, which they should be before we run this function.
|
|
1023
1105
|
|
|
@@ -1029,16 +1111,23 @@ export class BundleDataClient {
|
|
|
1029
1111
|
// want to perform a binary search lookup for it because the deposit ID is "unsafe" and cannot be
|
|
1030
1112
|
// found using such a method) because infinite fill deadlines cannot be produced from the unsafeDepositV3()
|
|
1031
1113
|
// function.
|
|
1032
|
-
if (
|
|
1033
|
-
slowFillRequest.
|
|
1034
|
-
|
|
1035
|
-
|
|
1114
|
+
if (slowFillRequest.blockNumber >= destinationChainBlockRange[0]) {
|
|
1115
|
+
if (!INFINITE_FILL_DEADLINE.eq(slowFillRequest.fillDeadline)) {
|
|
1116
|
+
bundleInvalidSlowFillRequests.push(slowFillRequest);
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1036
1119
|
const historicalDeposit = await queryHistoricalDepositForFill(originClient, slowFillRequest);
|
|
1037
1120
|
if (!historicalDeposit.found) {
|
|
1038
|
-
|
|
1121
|
+
bundleInvalidSlowFillRequests.push(slowFillRequest);
|
|
1039
1122
|
return;
|
|
1040
1123
|
}
|
|
1041
1124
|
const matchedDeposit: V3DepositWithBlock = historicalDeposit.deposit;
|
|
1125
|
+
// If deposit is in a following bundle, then this slow fill request will have to be created
|
|
1126
|
+
// once that deposit is in the current bundle.
|
|
1127
|
+
if (matchedDeposit.blockNumber > originChainBlockRange[1]) {
|
|
1128
|
+
bundleInvalidSlowFillRequests.push(slowFillRequest);
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1042
1131
|
// @dev Since queryHistoricalDepositForFill validates the slow fill request by checking individual
|
|
1043
1132
|
// object property values against the deposit's, we
|
|
1044
1133
|
// sanity check it here by comparing the full relay hashes. If there's an error here then the
|
|
@@ -1047,13 +1136,9 @@ export class BundleDataClient {
|
|
|
1047
1136
|
this.getRelayHashFromEvent(matchedDeposit) === relayDataHash,
|
|
1048
1137
|
"Deposit relay hashes should match."
|
|
1049
1138
|
);
|
|
1050
|
-
v3RelayHashes[relayDataHash].
|
|
1139
|
+
v3RelayHashes[relayDataHash].deposits = [matchedDeposit];
|
|
1051
1140
|
|
|
1052
|
-
if (
|
|
1053
|
-
!_canCreateSlowFillLeaf(matchedDeposit) ||
|
|
1054
|
-
// Deposit must not have expired in this bundle.
|
|
1055
|
-
_depositIsExpired(matchedDeposit)
|
|
1056
|
-
) {
|
|
1141
|
+
if (!_canCreateSlowFillLeaf(matchedDeposit) || _depositIsExpired(matchedDeposit)) {
|
|
1057
1142
|
return;
|
|
1058
1143
|
}
|
|
1059
1144
|
validatedBundleSlowFills.push(matchedDeposit);
|
|
@@ -1061,126 +1146,143 @@ export class BundleDataClient {
|
|
|
1061
1146
|
}
|
|
1062
1147
|
);
|
|
1063
1148
|
|
|
1064
|
-
//
|
|
1065
|
-
//
|
|
1066
|
-
//
|
|
1067
|
-
//
|
|
1068
|
-
//
|
|
1069
|
-
// -
|
|
1070
|
-
//
|
|
1071
|
-
|
|
1072
|
-
//
|
|
1073
|
-
//
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1149
|
+
// Process deposits and maintain the following invariants:
|
|
1150
|
+
// - Deposits matching fills that are not type SlowFill from previous bundle block ranges should produce
|
|
1151
|
+
// refunds for those fills.
|
|
1152
|
+
// - Deposits matching fills that are type SlowFill from previous bundle block ranges should be refunded to the
|
|
1153
|
+
// depositor.
|
|
1154
|
+
// - All deposits expiring in this bundle, even those sent in prior bundle block ranges, should be refunded
|
|
1155
|
+
// to the depositor.
|
|
1156
|
+
// - An expired deposit cannot be refunded if the deposit was filled.
|
|
1157
|
+
// - If a deposit from a prior bundle expired in this bundle, had a slow fill request created for it
|
|
1158
|
+
// in a prior bundle, and has not been filled yet, then an unexecutable slow fill leaf has been created
|
|
1159
|
+
// and needs to be refunded to the HubPool.
|
|
1160
|
+
// - Deposits matching slow fill requests from previous bundle block ranges should produce slow fills
|
|
1161
|
+
// if the deposit has not been filled.
|
|
1162
|
+
|
|
1163
|
+
// Assumptions:
|
|
1164
|
+
// - If the deposit has a matching fill or slow fill request in the bundle then we have already stored
|
|
1165
|
+
// it in the relay hashes dictionary.
|
|
1166
|
+
// - We've created refunds for all fills in this bundle matching a deposit.
|
|
1167
|
+
// - We've created slow fill leaves for all slow fill requests in this bundle matching an unfilled deposit.
|
|
1168
|
+
// - Deposits for the same relay data hash can be sent an arbitrary amount of times.
|
|
1169
|
+
// - Deposits can be sent an arbitrary amount of time after a fill has been sent for the matching relay data.
|
|
1170
|
+
await mapAsync(bundleDepositHashes, async (depositHash) => {
|
|
1171
|
+
const { relayDataHash, index } = decodeBundleDepositHash(depositHash);
|
|
1172
|
+
const { deposits, fill, slowFillRequest } = v3RelayHashes[relayDataHash];
|
|
1173
|
+
if (!deposits || deposits.length === 0) {
|
|
1174
|
+
throw new Error("Deposits should exist in relay hash dictionary.");
|
|
1175
|
+
}
|
|
1176
|
+
const deposit = deposits[index];
|
|
1177
|
+
if (!deposit) throw new Error("Deposit should exist in relay hash dictionary.");
|
|
1178
|
+
if (deposit.originChainId !== originChainId || deposit.destinationChainId !== destinationChainId) {
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
// If fill is in the current bundle then we can assume there is already a refund for it, so only
|
|
1183
|
+
// include this pre fill if the fill is in an older bundle.
|
|
1184
|
+
if (fill) {
|
|
1185
|
+
if (canRefundPrefills && fill.blockNumber < destinationChainBlockRange[0]) {
|
|
1186
|
+
const fillToRefund = await verifyFillRepayment(
|
|
1187
|
+
fill,
|
|
1188
|
+
destinationClient.spokePool.provider,
|
|
1189
|
+
v3RelayHashes[relayDataHash].deposits![0],
|
|
1190
|
+
allChainIds
|
|
1191
|
+
);
|
|
1192
|
+
if (!isDefined(fillToRefund)) {
|
|
1193
|
+
bundleUnrepayableFillsV3.push(fill);
|
|
1194
|
+
} else if (!isSlowFill(fill)) {
|
|
1195
|
+
v3RelayHashes[relayDataHash].fill = fillToRefund;
|
|
1098
1196
|
validatedBundleV3Fills.push({
|
|
1099
|
-
...
|
|
1197
|
+
...fillToRefund,
|
|
1100
1198
|
quoteTimestamp: deposit.quoteTimestamp,
|
|
1101
1199
|
});
|
|
1200
|
+
} else {
|
|
1201
|
+
updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
|
|
1102
1202
|
}
|
|
1103
|
-
return;
|
|
1104
1203
|
}
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1105
1206
|
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
)
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
return;
|
|
1207
|
+
// If a slow fill request exists in memory, then we know the deposit has not been filled because fills
|
|
1208
|
+
// must follow slow fill requests and we would have seen the fill already if it existed.,
|
|
1209
|
+
// We can conclude that either the deposit has expired or we need to create a slow fill leaf for the
|
|
1210
|
+
// deposit because it has not been filled. Slow fill leaves were already created for requests sent
|
|
1211
|
+
// in the current bundle so only create new slow fill leaves for prior bundle deposits.
|
|
1212
|
+
if (slowFillRequest) {
|
|
1213
|
+
if (_depositIsExpired(deposit)) {
|
|
1214
|
+
updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
|
|
1215
|
+
} else if (
|
|
1216
|
+
canRefundPrefills &&
|
|
1217
|
+
slowFillRequest.blockNumber < destinationChainBlockRange[0] &&
|
|
1218
|
+
_canCreateSlowFillLeaf(deposit) &&
|
|
1219
|
+
validatedBundleSlowFills.every((d) => this.getRelayHashFromEvent(d) !== relayDataHash)
|
|
1220
|
+
) {
|
|
1221
|
+
validatedBundleSlowFills.push(deposit);
|
|
1122
1222
|
}
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
1123
1225
|
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
//
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1226
|
+
// So at this point in the code, there is no fill or slow fill request in memory for this deposit.
|
|
1227
|
+
// We need to check its fill status on-chain to figure out whether to issue a refund or a slow fill leaf.
|
|
1228
|
+
// We can assume at this point that all fills or slow fill requests, if found, were in previous bundles
|
|
1229
|
+
// because the spoke pool client lookback would have returned this entire bundle of events and stored
|
|
1230
|
+
// them into the relay hash dictionary.
|
|
1231
|
+
const fillStatus = await _getFillStatusForDeposit(deposit, destinationChainBlockRange[1]);
|
|
1232
|
+
if (fillStatus === FillStatus.Filled) {
|
|
1233
|
+
// We don't need to verify the fill block is before the bundle end block on the destination chain because
|
|
1234
|
+
// we queried the fillStatus at the end block. Therefore, if the fill took place after the end block,
|
|
1235
|
+
// then we wouldn't be in this branch of the code.
|
|
1236
|
+
const prefill = await this.findMatchingFillEvent(deposit, destinationClient);
|
|
1237
|
+
assert(isDefined(prefill), `findFillEvent# Cannot find prefill: ${relayDataHash}`);
|
|
1238
|
+
assert(this.getRelayHashFromEvent(prefill!) === relayDataHash, "Relay hashes should match.");
|
|
1239
|
+
if (canRefundPrefills) {
|
|
1240
|
+
const verifiedFill = await verifyFillRepayment(
|
|
1241
|
+
prefill!,
|
|
1242
|
+
destinationClient.spokePool.provider,
|
|
1137
1243
|
deposit,
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
))
|
|
1141
|
-
|
|
1244
|
+
allChainIds
|
|
1245
|
+
);
|
|
1246
|
+
if (!isDefined(verifiedFill)) {
|
|
1247
|
+
bundleUnrepayableFillsV3.push(prefill!);
|
|
1248
|
+
} else if (!isSlowFill(verifiedFill)) {
|
|
1142
1249
|
validatedBundleV3Fills.push({
|
|
1143
|
-
...
|
|
1250
|
+
...verifiedFill!,
|
|
1144
1251
|
quoteTimestamp: deposit.quoteTimestamp,
|
|
1145
1252
|
});
|
|
1253
|
+
} else {
|
|
1254
|
+
updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
|
|
1146
1255
|
}
|
|
1147
1256
|
}
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
// Input and Output tokens must be equivalent on the deposit for this to be slow filled.
|
|
1158
|
-
// Slow fill requests for deposits from or to lite chains are considered invalid
|
|
1159
|
-
if (canRefundPrefills && _canCreateSlowFillLeaf(deposit)) {
|
|
1160
|
-
// If deposit newly expired, then we can't create a slow fill leaf for it but we can
|
|
1161
|
-
// create a deposit refund for it.
|
|
1162
|
-
validatedBundleSlowFills.push(deposit);
|
|
1163
|
-
}
|
|
1257
|
+
} else if (_depositIsExpired(deposit)) {
|
|
1258
|
+
updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
|
|
1259
|
+
} else if (
|
|
1260
|
+
fillStatus === FillStatus.RequestedSlowFill &&
|
|
1261
|
+
// Don't create duplicate slow fill requests for the same deposit.
|
|
1262
|
+
validatedBundleSlowFills.every((d) => this.getRelayHashFromEvent(d) !== relayDataHash)
|
|
1263
|
+
) {
|
|
1264
|
+
if (canRefundPrefills && _canCreateSlowFillLeaf(deposit)) {
|
|
1265
|
+
validatedBundleSlowFills.push(deposit);
|
|
1164
1266
|
}
|
|
1165
1267
|
}
|
|
1166
|
-
);
|
|
1268
|
+
});
|
|
1167
1269
|
|
|
1168
1270
|
// For all fills that came after a slow fill request, we can now check if the slow fill request
|
|
1169
1271
|
// was a valid one and whether it was created in a previous bundle. If so, then it created a slow fill
|
|
1170
1272
|
// leaf that is now unexecutable.
|
|
1171
1273
|
fastFillsReplacingSlowFills.forEach((relayDataHash) => {
|
|
1172
|
-
const {
|
|
1274
|
+
const { deposits, slowFillRequest, fill } = v3RelayHashes[relayDataHash];
|
|
1173
1275
|
assert(
|
|
1174
1276
|
fill?.relayExecutionInfo.fillType === FillType.ReplacedSlowFill,
|
|
1175
1277
|
"Fill type should be ReplacedSlowFill."
|
|
1176
1278
|
);
|
|
1177
1279
|
// Needed for TSC - are implicitely checking that deposit exists by making it to this point.
|
|
1178
|
-
if (!
|
|
1280
|
+
if (!deposits || deposits.length < 1) {
|
|
1179
1281
|
throw new Error("Deposit should exist in relay hash dictionary.");
|
|
1180
1282
|
}
|
|
1181
1283
|
// We should never push fast fills involving lite chains here because slow fill requests for them are invalid:
|
|
1182
1284
|
assert(
|
|
1183
|
-
_canCreateSlowFillLeaf(
|
|
1285
|
+
_canCreateSlowFillLeaf(deposits[0]),
|
|
1184
1286
|
"fastFillsReplacingSlowFills should contain only deposits that can be slow filled"
|
|
1185
1287
|
);
|
|
1186
1288
|
const destinationBlockRange = getBlockRangeForChain(blockRangesForChains, destinationChainId, chainIds);
|
|
@@ -1190,7 +1292,7 @@ export class BundleDataClient {
|
|
|
1190
1292
|
!slowFillRequest ||
|
|
1191
1293
|
slowFillRequest.blockNumber < destinationBlockRange[0]
|
|
1192
1294
|
) {
|
|
1193
|
-
validatedBundleUnexecutableSlowFills.push(
|
|
1295
|
+
validatedBundleUnexecutableSlowFills.push(deposits[0]);
|
|
1194
1296
|
}
|
|
1195
1297
|
});
|
|
1196
1298
|
}
|
|
@@ -1204,15 +1306,19 @@ export class BundleDataClient {
|
|
|
1204
1306
|
// For all deposits older than this bundle, we need to check if they expired in this bundle and if they did,
|
|
1205
1307
|
// whether there was a slow fill created for it in a previous bundle that is now unexecutable and replaced
|
|
1206
1308
|
// by a new expired deposit refund.
|
|
1207
|
-
await forEachAsync(olderDepositHashes, async (
|
|
1208
|
-
const {
|
|
1209
|
-
|
|
1210
|
-
|
|
1309
|
+
await forEachAsync(olderDepositHashes, async (depositHash) => {
|
|
1310
|
+
const { relayDataHash, index } = decodeBundleDepositHash(depositHash);
|
|
1311
|
+
const { deposits, slowFillRequest, fill } = v3RelayHashes[relayDataHash];
|
|
1312
|
+
if (!deposits || deposits.length < 1) {
|
|
1313
|
+
throw new Error("Deposit should exist in relay hash dictionary.");
|
|
1314
|
+
}
|
|
1315
|
+
const deposit = deposits[index];
|
|
1316
|
+
const { destinationChainId } = deposit;
|
|
1211
1317
|
const destinationBlockRange = getBlockRangeForChain(blockRangesForChains, destinationChainId, chainIds);
|
|
1212
1318
|
|
|
1213
1319
|
// Only look for deposits that were mined before this bundle and that are newly expired.
|
|
1214
1320
|
// If the fill deadline is lower than the bundle start block on the destination chain, then
|
|
1215
|
-
// we should assume it was
|
|
1321
|
+
// we should assume it was refunded in a previous bundle.
|
|
1216
1322
|
if (
|
|
1217
1323
|
// If there is a valid fill that we saw matching this deposit, then it does not need a refund.
|
|
1218
1324
|
!fill &&
|
|
@@ -1256,7 +1362,7 @@ export class BundleDataClient {
|
|
|
1256
1362
|
validatedBundleV3Fills.length > 0
|
|
1257
1363
|
? this.clients.hubPoolClient.batchComputeRealizedLpFeePct(
|
|
1258
1364
|
validatedBundleV3Fills.map((fill) => {
|
|
1259
|
-
const matchedDeposit = v3RelayHashes[this.getRelayHashFromEvent(fill)].
|
|
1365
|
+
const matchedDeposit = v3RelayHashes[this.getRelayHashFromEvent(fill)].deposits![0];
|
|
1260
1366
|
assert(isDefined(matchedDeposit), "Deposit should exist in relay hash dictionary.");
|
|
1261
1367
|
const { chainToSendRefundTo: paymentChainId } = getRefundInformationFromFill(
|
|
1262
1368
|
fill,
|
|
@@ -1300,7 +1406,7 @@ export class BundleDataClient {
|
|
|
1300
1406
|
});
|
|
1301
1407
|
v3FillLpFees.forEach(({ realizedLpFeePct }, idx) => {
|
|
1302
1408
|
const fill = validatedBundleV3Fills[idx];
|
|
1303
|
-
const associatedDeposit = v3RelayHashes[this.getRelayHashFromEvent(fill)].
|
|
1409
|
+
const associatedDeposit = v3RelayHashes[this.getRelayHashFromEvent(fill)].deposits![0];
|
|
1304
1410
|
assert(isDefined(associatedDeposit), "Deposit should exist in relay hash dictionary.");
|
|
1305
1411
|
const { chainToSendRefundTo, repaymentToken } = getRefundInformationFromFill(
|
|
1306
1412
|
fill,
|
|
@@ -1309,10 +1415,19 @@ export class BundleDataClient {
|
|
|
1309
1415
|
chainIds,
|
|
1310
1416
|
associatedDeposit!.fromLiteChain
|
|
1311
1417
|
);
|
|
1312
|
-
updateBundleFillsV3(bundleFillsV3, fill, realizedLpFeePct, chainToSendRefundTo, repaymentToken);
|
|
1418
|
+
updateBundleFillsV3(bundleFillsV3, fill, realizedLpFeePct, chainToSendRefundTo, repaymentToken, fill.relayer);
|
|
1313
1419
|
});
|
|
1314
1420
|
v3SlowFillLpFees.forEach(({ realizedLpFeePct: lpFeePct }, idx) => {
|
|
1315
1421
|
const deposit = validatedBundleSlowFills[idx];
|
|
1422
|
+
// We should not create slow fill leaves for duplicate deposit hashes and we should only create a slow
|
|
1423
|
+
// fill leaf for the first deposit (the quote timestamp of the deposit determines the LP fee, so its
|
|
1424
|
+
// important we pick out the correct deposit). Deposits are pushed into validatedBundleSlowFills in ascending
|
|
1425
|
+
// order so the following slice will only match the first deposit.
|
|
1426
|
+
const relayDataHash = this.getRelayHashFromEvent(deposit);
|
|
1427
|
+
if (validatedBundleSlowFills.slice(0, idx).some((d) => this.getRelayHashFromEvent(d) === relayDataHash)) {
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
assert(!_depositIsExpired(deposit), "Cannot create slow fill leaf for expired deposit.");
|
|
1316
1431
|
updateBundleSlowFills(bundleSlowFillsV3, { ...deposit, lpFeePct });
|
|
1317
1432
|
});
|
|
1318
1433
|
v3UnexecutableSlowFillLpFees.forEach(({ realizedLpFeePct: lpFeePct }, idx) => {
|
|
@@ -1323,7 +1438,6 @@ export class BundleDataClient {
|
|
|
1323
1438
|
const v3SpokeEventsReadable = prettyPrintV3SpokePoolEvents(
|
|
1324
1439
|
bundleDepositsV3,
|
|
1325
1440
|
bundleFillsV3,
|
|
1326
|
-
bundleInvalidFillsV3,
|
|
1327
1441
|
bundleSlowFillsV3,
|
|
1328
1442
|
expiredDepositsToRefundV3,
|
|
1329
1443
|
unexecutableSlowFills
|
|
@@ -1332,12 +1446,30 @@ export class BundleDataClient {
|
|
|
1332
1446
|
if (bundleInvalidFillsV3.length > 0) {
|
|
1333
1447
|
this.logger.debug({
|
|
1334
1448
|
at: "BundleDataClient#loadData",
|
|
1335
|
-
message: "Finished loading V3 spoke pool data and found some invalid
|
|
1449
|
+
message: "Finished loading V3 spoke pool data and found some invalid fills in range",
|
|
1336
1450
|
blockRangesForChains,
|
|
1337
1451
|
bundleInvalidFillsV3,
|
|
1338
1452
|
});
|
|
1339
1453
|
}
|
|
1340
1454
|
|
|
1455
|
+
if (bundleUnrepayableFillsV3.length > 0) {
|
|
1456
|
+
this.logger.debug({
|
|
1457
|
+
at: "BundleDataClient#loadData",
|
|
1458
|
+
message: "Finished loading V3 spoke pool data and found some unrepayable fills in range",
|
|
1459
|
+
blockRangesForChains,
|
|
1460
|
+
bundleUnrepayableFillsV3,
|
|
1461
|
+
});
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
if (bundleInvalidSlowFillRequests.length > 0) {
|
|
1465
|
+
this.logger.debug({
|
|
1466
|
+
at: "BundleDataClient#loadData",
|
|
1467
|
+
message: "Finished loading V3 spoke pool data and found some invalid slow fill requests in range",
|
|
1468
|
+
blockRangesForChains,
|
|
1469
|
+
bundleInvalidSlowFillRequests,
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1341
1473
|
this.logger.debug({
|
|
1342
1474
|
at: "BundleDataClient#loadDataFromScratch",
|
|
1343
1475
|
message: `Computed bundle data in ${Math.round(performance.now() - start) / 1000}s.`,
|
|
@@ -1357,7 +1489,7 @@ export class BundleDataClient {
|
|
|
1357
1489
|
// keccak256 hash of the relay data, which can be used as input into the on-chain `fillStatuses()` function in the
|
|
1358
1490
|
// spoke pool contract. However, this internal function is used to uniquely identify a bridging event
|
|
1359
1491
|
// for speed since its easier to build a string from the event data than to hash it.
|
|
1360
|
-
|
|
1492
|
+
protected getRelayHashFromEvent(event: V3DepositWithBlock | V3FillWithBlock | SlowFillRequestWithBlock): string {
|
|
1361
1493
|
return `${event.depositor}-${event.recipient}-${event.exclusiveRelayer}-${event.inputToken}-${event.outputToken}-${
|
|
1362
1494
|
event.inputAmount
|
|
1363
1495
|
}-${event.outputAmount}-${event.originChainId}-${event.depositId.toString()}-${event.fillDeadline}-${
|
|
@@ -1365,6 +1497,18 @@ export class BundleDataClient {
|
|
|
1365
1497
|
}-${event.message}-${event.destinationChainId}`;
|
|
1366
1498
|
}
|
|
1367
1499
|
|
|
1500
|
+
protected async findMatchingFillEvent(
|
|
1501
|
+
deposit: DepositWithBlock,
|
|
1502
|
+
spokePoolClient: SpokePoolClient
|
|
1503
|
+
): Promise<FillWithBlock | undefined> {
|
|
1504
|
+
return await findFillEvent(
|
|
1505
|
+
spokePoolClient.spokePool,
|
|
1506
|
+
deposit,
|
|
1507
|
+
spokePoolClient.deploymentBlock,
|
|
1508
|
+
spokePoolClient.latestBlockSearched
|
|
1509
|
+
);
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1368
1512
|
async getBundleBlockTimestamps(
|
|
1369
1513
|
chainIds: number[],
|
|
1370
1514
|
blockRangesForChains: number[][],
|
|
@@ -1390,13 +1534,26 @@ export class BundleDataClient {
|
|
|
1390
1534
|
// will usually be called in production with block ranges that were validated by
|
|
1391
1535
|
// DataworkerUtils.blockRangesAreInvalidForSpokeClients.
|
|
1392
1536
|
const startBlockForChain = Math.min(_startBlockForChain, spokePoolClient.latestBlockSearched);
|
|
1393
|
-
|
|
1394
|
-
|
|
1537
|
+
// @dev Add 1 to the bundle end block. The thinking here is that there can be a gap between
|
|
1538
|
+
// block timestamps in subsequent blocks. The bundle data client assumes that fill deadlines expire
|
|
1539
|
+
// in exactly one bundle, therefore we must make sure that the bundle block timestamp for one bundle's
|
|
1540
|
+
// end block is exactly equal to the bundle block timestamp for the next bundle's start block. This way
|
|
1541
|
+
// there are no gaps in block timestamps between bundles.
|
|
1542
|
+
const endBlockForChain = Math.min(_endBlockForChain + 1, spokePoolClient.latestBlockSearched);
|
|
1543
|
+
const [startTime, _endTime] = [
|
|
1395
1544
|
await spokePoolClient.getTimestampForBlock(startBlockForChain),
|
|
1396
1545
|
await spokePoolClient.getTimestampForBlock(endBlockForChain),
|
|
1397
1546
|
];
|
|
1547
|
+
// @dev similar to reasoning above to ensure no gaps between bundle block range timestamps and also
|
|
1548
|
+
// no overlap, subtract 1 from the end time.
|
|
1549
|
+
const endBlockDelta = endBlockForChain > startBlockForChain ? 1 : 0;
|
|
1550
|
+
const endTime = Math.max(0, _endTime - endBlockDelta);
|
|
1551
|
+
|
|
1398
1552
|
// Sanity checks:
|
|
1399
|
-
assert(
|
|
1553
|
+
assert(
|
|
1554
|
+
endTime >= startTime,
|
|
1555
|
+
`End time for block ${endBlockForChain} should be greater than start time for block ${startBlockForChain}: ${endTime} >= ${startTime}.`
|
|
1556
|
+
);
|
|
1400
1557
|
assert(
|
|
1401
1558
|
startBlockForChain === 0 || startTime > 0,
|
|
1402
1559
|
"Start timestamp must be greater than 0 if the start block is greater than 0."
|