@across-protocol/sdk 4.0.0-beta.28 → 4.0.0-beta.3
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 +4 -5
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.js +164 -326
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.d.ts +2 -1
- package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js +2 -1
- package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/FillUtils.d.ts +1 -3
- package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js +1 -33
- package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
- package/dist/cjs/clients/SpokePoolClient.d.ts +0 -1
- package/dist/cjs/clients/SpokePoolClient.js +4 -13
- package/dist/cjs/clients/SpokePoolClient.js.map +1 -1
- package/dist/cjs/constants.d.ts +1 -1
- package/dist/cjs/constants.js +2 -2
- package/dist/cjs/constants.js.map +1 -1
- package/dist/cjs/providers/index.d.ts +0 -1
- package/dist/cjs/providers/index.js +0 -2
- package/dist/cjs/providers/index.js.map +1 -1
- package/dist/cjs/utils/AddressUtils.d.ts +0 -1
- package/dist/cjs/utils/AddressUtils.js +1 -14
- package/dist/cjs/utils/AddressUtils.js.map +1 -1
- package/dist/cjs/utils/CachingUtils.js +1 -1
- package/dist/cjs/utils/CachingUtils.js.map +1 -1
- package/dist/cjs/utils/DepositUtils.d.ts +1 -2
- package/dist/cjs/utils/DepositUtils.js +4 -13
- package/dist/cjs/utils/DepositUtils.js.map +1 -1
- package/dist/cjs/utils/NetworkUtils.d.ts +0 -1
- package/dist/cjs/utils/NetworkUtils.js +1 -6
- package/dist/cjs/utils/NetworkUtils.js.map +1 -1
- package/dist/cjs/utils/SpokeUtils.js +3 -3
- package/dist/cjs/utils/SpokeUtils.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/BundleDataClient.d.ts +4 -5
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js +188 -394
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.d.ts +2 -1
- package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js +3 -2
- package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/FillUtils.d.ts +1 -3
- package/dist/esm/clients/BundleDataClient/utils/FillUtils.js +1 -42
- package/dist/esm/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
- package/dist/esm/clients/SpokePoolClient.d.ts +0 -8
- package/dist/esm/clients/SpokePoolClient.js +4 -20
- package/dist/esm/clients/SpokePoolClient.js.map +1 -1
- package/dist/esm/constants.d.ts +1 -1
- package/dist/esm/constants.js +2 -2
- package/dist/esm/constants.js.map +1 -1
- package/dist/esm/providers/index.d.ts +0 -1
- package/dist/esm/providers/index.js +0 -2
- package/dist/esm/providers/index.js.map +1 -1
- package/dist/esm/utils/AddressUtils.d.ts +0 -1
- package/dist/esm/utils/AddressUtils.js +0 -16
- package/dist/esm/utils/AddressUtils.js.map +1 -1
- package/dist/esm/utils/CachingUtils.js +1 -1
- package/dist/esm/utils/CachingUtils.js.map +1 -1
- package/dist/esm/utils/DepositUtils.d.ts +1 -2
- package/dist/esm/utils/DepositUtils.js +5 -14
- package/dist/esm/utils/DepositUtils.js.map +1 -1
- package/dist/esm/utils/NetworkUtils.d.ts +0 -6
- package/dist/esm/utils/NetworkUtils.js +0 -10
- package/dist/esm/utils/NetworkUtils.js.map +1 -1
- package/dist/esm/utils/SpokeUtils.js +4 -4
- package/dist/esm/utils/SpokeUtils.js.map +1 -1
- package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts +4 -5
- package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts.map +1 -1
- package/dist/types/clients/BundleDataClient/utils/DataworkerUtils.d.ts +2 -1
- package/dist/types/clients/BundleDataClient/utils/DataworkerUtils.d.ts.map +1 -1
- package/dist/types/clients/BundleDataClient/utils/FillUtils.d.ts +1 -3
- package/dist/types/clients/BundleDataClient/utils/FillUtils.d.ts.map +1 -1
- package/dist/types/clients/SpokePoolClient.d.ts +0 -8
- package/dist/types/clients/SpokePoolClient.d.ts.map +1 -1
- package/dist/types/constants.d.ts +1 -1
- package/dist/types/constants.d.ts.map +1 -1
- package/dist/types/providers/index.d.ts +0 -1
- package/dist/types/providers/index.d.ts.map +1 -1
- package/dist/types/utils/AddressUtils.d.ts +0 -1
- package/dist/types/utils/AddressUtils.d.ts.map +1 -1
- package/dist/types/utils/DepositUtils.d.ts +1 -2
- package/dist/types/utils/DepositUtils.d.ts.map +1 -1
- package/dist/types/utils/NetworkUtils.d.ts +0 -6
- package/dist/types/utils/NetworkUtils.d.ts.map +1 -1
- package/dist/types/utils/SpokeUtils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/clients/BundleDataClient/BundleDataClient.ts +179 -395
- package/src/clients/BundleDataClient/utils/DataworkerUtils.ts +8 -0
- package/src/clients/BundleDataClient/utils/FillUtils.ts +2 -47
- package/src/clients/SpokePoolClient.ts +6 -19
- package/src/constants.ts +3 -3
- package/src/providers/index.ts +0 -1
- package/src/utils/AddressUtils.ts +0 -16
- package/src/utils/CachingUtils.ts +1 -1
- package/src/utils/DepositUtils.ts +5 -14
- package/src/utils/NetworkUtils.ts +0 -11
- package/src/utils/SpokeUtils.ts +6 -6
- package/dist/cjs/providers/mockProvider.d.ts +0 -19
- package/dist/cjs/providers/mockProvider.js +0 -70
- package/dist/cjs/providers/mockProvider.js.map +0 -1
- package/dist/esm/providers/mockProvider.d.ts +0 -23
- package/dist/esm/providers/mockProvider.js +0 -73
- package/dist/esm/providers/mockProvider.js.map +0 -1
- package/dist/types/providers/mockProvider.d.ts +0 -24
- package/dist/types/providers/mockProvider.d.ts.map +0 -1
- package/src/providers/mockProvider.ts +0 -77
|
@@ -33,13 +33,10 @@ import {
|
|
|
33
33
|
getImpliedBundleBlockRanges,
|
|
34
34
|
isSlowFill,
|
|
35
35
|
mapAsync,
|
|
36
|
-
filterAsync,
|
|
37
36
|
bnUint32Max,
|
|
38
37
|
isZeroValueDeposit,
|
|
39
38
|
findFillEvent,
|
|
40
39
|
isZeroValueFillOrSlowFillRequest,
|
|
41
|
-
chainIsEvm,
|
|
42
|
-
isValidEvmAddress,
|
|
43
40
|
} from "../../utils";
|
|
44
41
|
import winston from "winston";
|
|
45
42
|
import {
|
|
@@ -54,9 +51,7 @@ import {
|
|
|
54
51
|
prettyPrintV3SpokePoolEvents,
|
|
55
52
|
V3DepositWithBlock,
|
|
56
53
|
V3FillWithBlock,
|
|
57
|
-
verifyFillRepayment,
|
|
58
54
|
} from "./utils";
|
|
59
|
-
import { PRE_FILL_MIN_CONFIG_STORE_VERSION } from "../../constants";
|
|
60
55
|
|
|
61
56
|
// max(uint256) - 1
|
|
62
57
|
export const INFINITE_FILL_DEADLINE = bnUint32Max;
|
|
@@ -65,10 +60,6 @@ type DataCache = Record<string, Promise<LoadDataReturnValue>>;
|
|
|
65
60
|
|
|
66
61
|
// V3 dictionary helper functions
|
|
67
62
|
function updateExpiredDepositsV3(dict: ExpiredDepositsToRefundV3, deposit: V3DepositWithBlock): void {
|
|
68
|
-
// A deposit refund for a deposit is invalid if the depositor has a bytes32 address input for an EVM chain. It is valid otherwise.
|
|
69
|
-
if (chainIsEvm(deposit.originChainId) && !isValidEvmAddress(deposit.depositor)) {
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
63
|
const { originChainId, inputToken } = deposit;
|
|
73
64
|
if (!dict?.[originChainId]?.[inputToken]) {
|
|
74
65
|
assign(dict, [originChainId, inputToken], []);
|
|
@@ -89,13 +80,8 @@ function updateBundleFillsV3(
|
|
|
89
80
|
fill: V3FillWithBlock,
|
|
90
81
|
lpFeePct: BigNumber,
|
|
91
82
|
repaymentChainId: number,
|
|
92
|
-
repaymentToken: string
|
|
93
|
-
repaymentAddress: string
|
|
83
|
+
repaymentToken: string
|
|
94
84
|
): void {
|
|
95
|
-
// It is impossible to refund a deposit if the repayment chain is EVM and the relayer is a non-evm address.
|
|
96
|
-
if (chainIsEvm(repaymentChainId) && !isValidEvmAddress(repaymentAddress)) {
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
85
|
if (!dict?.[repaymentChainId]?.[repaymentToken]) {
|
|
100
86
|
assign(dict, [repaymentChainId, repaymentToken], {
|
|
101
87
|
fills: [],
|
|
@@ -105,19 +91,19 @@ function updateBundleFillsV3(
|
|
|
105
91
|
});
|
|
106
92
|
}
|
|
107
93
|
|
|
108
|
-
const bundleFill: BundleFillV3 = { ...fill, lpFeePct
|
|
94
|
+
const bundleFill: BundleFillV3 = { ...fill, lpFeePct };
|
|
109
95
|
|
|
110
96
|
// Add all fills, slow and fast, to dictionary.
|
|
111
97
|
assign(dict, [repaymentChainId, repaymentToken, "fills"], [bundleFill]);
|
|
112
98
|
|
|
113
99
|
// All fills update the bundle LP fees.
|
|
114
100
|
const refundObj = dict[repaymentChainId][repaymentToken];
|
|
115
|
-
const realizedLpFee =
|
|
101
|
+
const realizedLpFee = fill.inputAmount.mul(bundleFill.lpFeePct).div(fixedPointAdjustment);
|
|
116
102
|
refundObj.realizedLpFees = refundObj.realizedLpFees ? refundObj.realizedLpFees.add(realizedLpFee) : realizedLpFee;
|
|
117
103
|
|
|
118
104
|
// Only fast fills get refunded.
|
|
119
|
-
if (!isSlowFill(
|
|
120
|
-
const refundAmount =
|
|
105
|
+
if (!isSlowFill(fill)) {
|
|
106
|
+
const refundAmount = fill.inputAmount.mul(fixedPointAdjustment.sub(lpFeePct)).div(fixedPointAdjustment);
|
|
121
107
|
refundObj.totalRefundAmount = refundObj.totalRefundAmount
|
|
122
108
|
? refundObj.totalRefundAmount.add(refundAmount)
|
|
123
109
|
: refundAmount;
|
|
@@ -125,10 +111,10 @@ function updateBundleFillsV3(
|
|
|
125
111
|
// Instantiate dictionary if it doesn't exist.
|
|
126
112
|
refundObj.refunds ??= {};
|
|
127
113
|
|
|
128
|
-
if (refundObj.refunds[
|
|
129
|
-
refundObj.refunds[
|
|
114
|
+
if (refundObj.refunds[fill.relayer]) {
|
|
115
|
+
refundObj.refunds[fill.relayer] = refundObj.refunds[fill.relayer].add(refundAmount);
|
|
130
116
|
} else {
|
|
131
|
-
refundObj.refunds[
|
|
117
|
+
refundObj.refunds[fill.relayer] = refundAmount;
|
|
132
118
|
}
|
|
133
119
|
}
|
|
134
120
|
}
|
|
@@ -145,9 +131,6 @@ function updateBundleExcessSlowFills(
|
|
|
145
131
|
}
|
|
146
132
|
|
|
147
133
|
function updateBundleSlowFills(dict: BundleSlowFills, deposit: V3DepositWithBlock & { lpFeePct: BigNumber }): void {
|
|
148
|
-
if (chainIsEvm(deposit.destinationChainId) && !isValidEvmAddress(deposit.recipient)) {
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
134
|
const { destinationChainId, outputToken } = deposit;
|
|
152
135
|
if (!dict?.[destinationChainId]?.[outputToken]) {
|
|
153
136
|
assign(dict, [destinationChainId, outputToken], []);
|
|
@@ -251,6 +234,7 @@ export class BundleDataClient {
|
|
|
251
234
|
bundleData: prettyPrintV3SpokePoolEvents(
|
|
252
235
|
bundleData.bundleDepositsV3,
|
|
253
236
|
bundleData.bundleFillsV3,
|
|
237
|
+
[], // Invalid fills are not persisted to Arweave.
|
|
254
238
|
bundleData.bundleSlowFillsV3,
|
|
255
239
|
bundleData.expiredDepositsToRefundV3,
|
|
256
240
|
bundleData.unexecutableSlowFills
|
|
@@ -298,7 +282,7 @@ export class BundleDataClient {
|
|
|
298
282
|
// so as not to affect this approximate refund count.
|
|
299
283
|
const arweaveData = await this.loadArweaveData(bundleEvaluationBlockRanges);
|
|
300
284
|
if (arweaveData === undefined) {
|
|
301
|
-
combinedRefunds =
|
|
285
|
+
combinedRefunds = this.getApproximateRefundsForBlockRange(chainIds, bundleEvaluationBlockRanges);
|
|
302
286
|
} else {
|
|
303
287
|
const { bundleFillsV3, expiredDepositsToRefundV3 } = arweaveData;
|
|
304
288
|
combinedRefunds = getRefundsFromBundle(bundleFillsV3, expiredDepositsToRefundV3);
|
|
@@ -319,72 +303,50 @@ export class BundleDataClient {
|
|
|
319
303
|
}
|
|
320
304
|
|
|
321
305
|
// @dev This helper function should probably be moved to the InventoryClient
|
|
322
|
-
|
|
306
|
+
getApproximateRefundsForBlockRange(chainIds: number[], blockRanges: number[][]): CombinedRefunds {
|
|
323
307
|
const refundsForChain: CombinedRefunds = {};
|
|
324
308
|
for (const chainId of chainIds) {
|
|
325
309
|
if (this.spokePoolClients[chainId] === undefined) {
|
|
326
310
|
continue;
|
|
327
311
|
}
|
|
328
312
|
const chainIndex = chainIds.indexOf(chainId);
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
if (
|
|
336
|
-
fill.blockNumber < blockRanges[chainIndex][0] ||
|
|
337
|
-
fill.blockNumber > blockRanges[chainIndex][1] ||
|
|
338
|
-
isZeroValueFillOrSlowFillRequest(fill)
|
|
339
|
-
) {
|
|
340
|
-
return false;
|
|
341
|
-
}
|
|
313
|
+
this.spokePoolClients[chainId]
|
|
314
|
+
.getFills()
|
|
315
|
+
.filter((fill) => {
|
|
316
|
+
if (fill.blockNumber < blockRanges[chainIndex][0] || fill.blockNumber > blockRanges[chainIndex][1]) {
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
342
319
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
return false;
|
|
346
|
-
}
|
|
347
|
-
const matchingDeposit = this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
|
|
348
|
-
const hasMatchingDeposit =
|
|
349
|
-
matchingDeposit !== undefined &&
|
|
350
|
-
this.getRelayHashFromEvent(fill) === this.getRelayHashFromEvent(matchingDeposit);
|
|
351
|
-
if (hasMatchingDeposit) {
|
|
352
|
-
const validRepayment = await verifyFillRepayment(
|
|
353
|
-
fill,
|
|
354
|
-
this.spokePoolClients[fill.destinationChainId].spokePool.provider,
|
|
355
|
-
matchingDeposit,
|
|
356
|
-
// @dev: to get valid repayment chain ID's, get all chain IDs for the bundle block range and remove
|
|
357
|
-
// disabled block ranges.
|
|
358
|
-
this.clients.configStoreClient
|
|
359
|
-
.getChainIdIndicesForBlock(blockRanges[0][1])
|
|
360
|
-
.filter((_chainId, i) => !isChainDisabled(blockRanges[i]))
|
|
361
|
-
);
|
|
362
|
-
if (!isDefined(validRepayment)) {
|
|
320
|
+
// If origin spoke pool client isn't defined, we can't validate it.
|
|
321
|
+
if (this.spokePoolClients[fill.originChainId] === undefined) {
|
|
363
322
|
return false;
|
|
364
323
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
fill
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
324
|
+
const matchingDeposit = this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
|
|
325
|
+
const hasMatchingDeposit =
|
|
326
|
+
matchingDeposit !== undefined &&
|
|
327
|
+
this.getRelayHashFromEvent(fill) === this.getRelayHashFromEvent(matchingDeposit);
|
|
328
|
+
return hasMatchingDeposit;
|
|
329
|
+
})
|
|
330
|
+
.forEach((fill) => {
|
|
331
|
+
const matchingDeposit = this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
|
|
332
|
+
assert(isDefined(matchingDeposit), "Deposit not found for fill.");
|
|
333
|
+
const { chainToSendRefundTo, repaymentToken } = getRefundInformationFromFill(
|
|
334
|
+
fill,
|
|
335
|
+
this.clients.hubPoolClient,
|
|
336
|
+
blockRanges,
|
|
337
|
+
this.chainIdListForBundleEvaluationBlockNumbers,
|
|
338
|
+
matchingDeposit!.fromLiteChain // Use ! because we've already asserted that matchingDeposit is defined.
|
|
339
|
+
);
|
|
340
|
+
// Assume that lp fees are 0 for the sake of speed. In the future we could batch compute
|
|
341
|
+
// these or make hardcoded assumptions based on the origin-repayment chain direction. This might result
|
|
342
|
+
// in slight over estimations of refunds, but its not clear whether underestimating or overestimating is
|
|
343
|
+
// worst from the relayer's perspective.
|
|
344
|
+
const { relayer, inputAmount: refundAmount } = fill;
|
|
345
|
+
refundsForChain[chainToSendRefundTo] ??= {};
|
|
346
|
+
refundsForChain[chainToSendRefundTo][repaymentToken] ??= {};
|
|
347
|
+
const existingRefundAmount = refundsForChain[chainToSendRefundTo][repaymentToken][relayer] ?? bnZero;
|
|
348
|
+
refundsForChain[chainToSendRefundTo][repaymentToken][relayer] = existingRefundAmount.add(refundAmount);
|
|
349
|
+
});
|
|
388
350
|
}
|
|
389
351
|
return refundsForChain;
|
|
390
352
|
}
|
|
@@ -511,7 +473,7 @@ export class BundleDataClient {
|
|
|
511
473
|
// ok for this use case.
|
|
512
474
|
const arweaveData = await this.loadArweaveData(pendingBundleBlockRanges);
|
|
513
475
|
if (arweaveData === undefined) {
|
|
514
|
-
combinedRefunds.push(
|
|
476
|
+
combinedRefunds.push(this.getApproximateRefundsForBlockRange(chainIds, pendingBundleBlockRanges));
|
|
515
477
|
} else {
|
|
516
478
|
const { bundleFillsV3, expiredDepositsToRefundV3 } = arweaveData;
|
|
517
479
|
combinedRefunds.push(getRefundsFromBundle(bundleFillsV3, expiredDepositsToRefundV3));
|
|
@@ -526,7 +488,7 @@ export class BundleDataClient {
|
|
|
526
488
|
// - Only look up fills sent after the pending bundle's end blocks
|
|
527
489
|
// - Skip LP fee computations and just assume the relayer is being refunded the full deposit.inputAmount
|
|
528
490
|
const start = performance.now();
|
|
529
|
-
combinedRefunds.push(
|
|
491
|
+
combinedRefunds.push(this.getApproximateRefundsForBlockRange(chainIds, widestBundleBlockRanges));
|
|
530
492
|
this.logger.debug({
|
|
531
493
|
at: "BundleDataClient#getNextBundleRefunds",
|
|
532
494
|
message: `Loading approximate refunds for next bundle in ${Math.round(performance.now() - start) / 1000}s.`,
|
|
@@ -693,7 +655,6 @@ export class BundleDataClient {
|
|
|
693
655
|
const bundleDepositsV3: BundleDepositsV3 = {}; // Deposits in bundle block range.
|
|
694
656
|
const bundleFillsV3: BundleFillsV3 = {}; // Fills to refund in bundle block range.
|
|
695
657
|
const bundleInvalidFillsV3: V3FillWithBlock[] = []; // Fills that are not valid in this bundle.
|
|
696
|
-
const bundleUnrepayableFillsV3: V3FillWithBlock[] = []; // Fills that are not repayable in this bundle.
|
|
697
658
|
const bundleSlowFillsV3: BundleSlowFills = {}; // Deposits that we need to send slow fills
|
|
698
659
|
// for in this bundle.
|
|
699
660
|
const expiredDepositsToRefundV3: ExpiredDepositsToRefundV3 = {};
|
|
@@ -780,7 +741,7 @@ export class BundleDataClient {
|
|
|
780
741
|
// Note: Since there are no partial fills in v3, there should only be one fill per relay hash.
|
|
781
742
|
// Moreover, the SpokePool blocks multiple slow fill requests, so
|
|
782
743
|
// there should also only be one slow fill request per relay hash.
|
|
783
|
-
|
|
744
|
+
deposit?: V3DepositWithBlock;
|
|
784
745
|
fill?: V3FillWithBlock;
|
|
785
746
|
slowFillRequest?: SlowFillRequestWithBlock;
|
|
786
747
|
};
|
|
@@ -791,29 +752,6 @@ export class BundleDataClient {
|
|
|
791
752
|
const bundleDepositHashes: string[] = [];
|
|
792
753
|
const olderDepositHashes: string[] = [];
|
|
793
754
|
|
|
794
|
-
const decodeBundleDepositHash = (depositHash: string): { relayDataHash: string; index: number } => {
|
|
795
|
-
const [relayDataHash, i] = depositHash.split("@");
|
|
796
|
-
return { relayDataHash, index: Number(i) };
|
|
797
|
-
};
|
|
798
|
-
|
|
799
|
-
// We use the following toggle to aid with the migration to pre-fills. The first bundle proposed using this
|
|
800
|
-
// pre-fill logic can double refund pre-fills that have already been filled in the last bundle, because the
|
|
801
|
-
// last bundle did not recognize a fill as a pre-fill. Therefore the developer should ensure that the version
|
|
802
|
-
// is bumped to the PRE_FILL_MIN_CONFIG_STORE_VERSION version before the first pre-fill bundle is proposed.
|
|
803
|
-
// To test the following bundle after this, the developer can set the FORCE_REFUND_PREFILLS environment variable
|
|
804
|
-
// to "true" simulate the bundle with pre-fill refunds.
|
|
805
|
-
// @todo Remove this logic once we have advanced sufficiently past the pre-fill migration.
|
|
806
|
-
const startBlockForMainnet = getBlockRangeForChain(
|
|
807
|
-
blockRangesForChains,
|
|
808
|
-
this.clients.hubPoolClient.chainId,
|
|
809
|
-
this.chainIdListForBundleEvaluationBlockNumbers
|
|
810
|
-
)[0];
|
|
811
|
-
const versionAtProposalBlock = this.clients.configStoreClient.getConfigStoreVersionForBlock(startBlockForMainnet);
|
|
812
|
-
const canRefundPrefills =
|
|
813
|
-
versionAtProposalBlock >= PRE_FILL_MIN_CONFIG_STORE_VERSION || process.env.FORCE_REFUND_PREFILLS === "true";
|
|
814
|
-
|
|
815
|
-
// Prerequisite step: Load all deposit events from the current or older bundles into the v3RelayHashes dictionary
|
|
816
|
-
// for convenient matching with fills.
|
|
817
755
|
let depositCounter = 0;
|
|
818
756
|
for (const originChainId of allChainIds) {
|
|
819
757
|
const originClient = spokePoolClients[originChainId];
|
|
@@ -832,15 +770,12 @@ export class BundleDataClient {
|
|
|
832
770
|
}
|
|
833
771
|
depositCounter++;
|
|
834
772
|
const relayDataHash = this.getRelayHashFromEvent(deposit);
|
|
835
|
-
|
|
836
773
|
if (!v3RelayHashes[relayDataHash]) {
|
|
837
774
|
v3RelayHashes[relayDataHash] = {
|
|
838
|
-
|
|
775
|
+
deposit: deposit,
|
|
839
776
|
fill: undefined,
|
|
840
777
|
slowFillRequest: undefined,
|
|
841
778
|
};
|
|
842
|
-
} else {
|
|
843
|
-
v3RelayHashes[relayDataHash].deposits!.push(deposit);
|
|
844
779
|
}
|
|
845
780
|
|
|
846
781
|
// Once we've saved the deposit hash into v3RelayHashes, then we can exit early here if the inputAmount
|
|
@@ -851,20 +786,11 @@ export class BundleDataClient {
|
|
|
851
786
|
return;
|
|
852
787
|
}
|
|
853
788
|
|
|
854
|
-
// Evaluate all expired deposits after fetching fill statuses,
|
|
855
|
-
// since we can't know for certain whether an expired deposit was filled a long time ago.
|
|
856
|
-
const newBundleDepositHash = `${relayDataHash}@${v3RelayHashes[relayDataHash].deposits!.length - 1}`;
|
|
857
|
-
const decodedBundleDepositHash = decodeBundleDepositHash(newBundleDepositHash);
|
|
858
|
-
assert(
|
|
859
|
-
decodedBundleDepositHash.relayDataHash === relayDataHash &&
|
|
860
|
-
decodedBundleDepositHash.index === v3RelayHashes[relayDataHash].deposits!.length - 1,
|
|
861
|
-
"Not using correct bundle deposit hash key"
|
|
862
|
-
);
|
|
863
789
|
if (deposit.blockNumber >= originChainBlockRange[0]) {
|
|
864
|
-
bundleDepositHashes.push(
|
|
790
|
+
bundleDepositHashes.push(relayDataHash);
|
|
865
791
|
updateBundleDepositsV3(bundleDepositsV3, deposit);
|
|
866
792
|
} else if (deposit.blockNumber < originChainBlockRange[0]) {
|
|
867
|
-
olderDepositHashes.push(
|
|
793
|
+
olderDepositHashes.push(relayDataHash);
|
|
868
794
|
}
|
|
869
795
|
});
|
|
870
796
|
}
|
|
@@ -889,7 +815,6 @@ export class BundleDataClient {
|
|
|
889
815
|
|
|
890
816
|
const destinationClient = spokePoolClients[destinationChainId];
|
|
891
817
|
const destinationChainBlockRange = getBlockRangeForChain(blockRangesForChains, destinationChainId, chainIds);
|
|
892
|
-
const originChainBlockRange = getBlockRangeForChain(blockRangesForChains, originChainId, chainIds);
|
|
893
818
|
|
|
894
819
|
// Keep track of fast fills that replaced slow fills, which we'll use to create "unexecutable" slow fills
|
|
895
820
|
// if the slow fill request was sent in a prior bundle.
|
|
@@ -904,81 +829,43 @@ export class BundleDataClient {
|
|
|
904
829
|
(fill) => fill.blockNumber <= destinationChainBlockRange[1] && !isZeroValueFillOrSlowFillRequest(fill)
|
|
905
830
|
),
|
|
906
831
|
async (fill) => {
|
|
907
|
-
fillCounter++;
|
|
908
832
|
const relayDataHash = this.getRelayHashFromEvent(fill);
|
|
833
|
+
fillCounter++;
|
|
834
|
+
|
|
909
835
|
if (v3RelayHashes[relayDataHash]) {
|
|
910
836
|
if (!v3RelayHashes[relayDataHash].fill) {
|
|
911
837
|
assert(
|
|
912
|
-
isDefined(v3RelayHashes[relayDataHash].
|
|
838
|
+
isDefined(v3RelayHashes[relayDataHash].deposit),
|
|
913
839
|
"Deposit should exist in relay hash dictionary."
|
|
914
840
|
);
|
|
915
841
|
// At this point, the v3RelayHashes entry already existed meaning that there is a matching deposit,
|
|
916
|
-
// so this fill
|
|
842
|
+
// so this fill is validated.
|
|
917
843
|
v3RelayHashes[relayDataHash].fill = fill;
|
|
918
844
|
if (fill.blockNumber >= destinationChainBlockRange[0]) {
|
|
919
|
-
|
|
920
|
-
fill,
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
allChainIds
|
|
924
|
-
);
|
|
925
|
-
if (!isDefined(fillToRefund)) {
|
|
926
|
-
// We won't repay the fill but the depositor has received funds so we don't need to make a
|
|
927
|
-
// payment.
|
|
928
|
-
bundleUnrepayableFillsV3.push(fill);
|
|
929
|
-
// We don't return here yet because we still need to mark unexecutable slow fill leaves
|
|
930
|
-
// or duplicate deposits. However, we won't issue a fast fill refund.
|
|
931
|
-
} else {
|
|
932
|
-
v3RelayHashes[relayDataHash].fill = fillToRefund;
|
|
933
|
-
validatedBundleV3Fills.push({
|
|
934
|
-
...fillToRefund,
|
|
935
|
-
quoteTimestamp: v3RelayHashes[relayDataHash].deposits![0].quoteTimestamp, // ! due to assert above
|
|
936
|
-
});
|
|
937
|
-
|
|
938
|
-
// Now that we know this deposit has been filled on-chain, identify any duplicate deposits
|
|
939
|
-
// sent for this fill and refund them to the filler, because this value would not be paid out
|
|
940
|
-
// otherwise. These deposits can no longer expire and get refunded as an expired deposit,
|
|
941
|
-
// and they won't trigger a pre-fill refund because the fill is in this bundle.
|
|
942
|
-
// Pre-fill refunds only happen when deposits are sent in this bundle and the
|
|
943
|
-
// fill is from a prior bundle. Paying out the filler keeps the behavior consistent for how
|
|
944
|
-
// we deal with duplicate deposits regardless if the deposit is matched with a pre-fill or
|
|
945
|
-
// a current bundle fill.
|
|
946
|
-
const duplicateDeposits = v3RelayHashes[relayDataHash].deposits!.slice(1);
|
|
947
|
-
duplicateDeposits.forEach((duplicateDeposit) => {
|
|
948
|
-
// If fill is a slow fill, refund deposit to depositor, otherwise refund to filler.
|
|
949
|
-
if (isSlowFill(fill)) {
|
|
950
|
-
updateExpiredDepositsV3(expiredDepositsToRefundV3, duplicateDeposit);
|
|
951
|
-
} else {
|
|
952
|
-
validatedBundleV3Fills.push({
|
|
953
|
-
...fillToRefund,
|
|
954
|
-
quoteTimestamp: duplicateDeposit.quoteTimestamp,
|
|
955
|
-
});
|
|
956
|
-
}
|
|
957
|
-
});
|
|
958
|
-
}
|
|
959
|
-
|
|
845
|
+
validatedBundleV3Fills.push({
|
|
846
|
+
...fill,
|
|
847
|
+
quoteTimestamp: v3RelayHashes[relayDataHash].deposit!.quoteTimestamp, // ! due to assert above
|
|
848
|
+
});
|
|
960
849
|
// If fill replaced a slow fill request, then mark it as one that might have created an
|
|
961
850
|
// unexecutable slow fill. We can't know for sure until we check the slow fill request
|
|
962
851
|
// events.
|
|
963
852
|
// slow fill requests for deposits from or to lite chains are considered invalid
|
|
964
853
|
if (
|
|
965
854
|
fill.relayExecutionInfo.fillType === FillType.ReplacedSlowFill &&
|
|
966
|
-
_canCreateSlowFillLeaf(v3RelayHashes[relayDataHash].
|
|
855
|
+
_canCreateSlowFillLeaf(v3RelayHashes[relayDataHash].deposit!)
|
|
967
856
|
) {
|
|
968
857
|
fastFillsReplacingSlowFills.push(relayDataHash);
|
|
969
858
|
}
|
|
970
859
|
}
|
|
971
|
-
} else {
|
|
972
|
-
throw new Error("Duplicate fill detected");
|
|
973
860
|
}
|
|
974
861
|
return;
|
|
975
862
|
}
|
|
976
863
|
|
|
977
864
|
// At this point, there is no relay hash dictionary entry for this fill, so we need to
|
|
978
|
-
// instantiate the entry.
|
|
865
|
+
// instantiate the entry.
|
|
979
866
|
v3RelayHashes[relayDataHash] = {
|
|
980
|
-
|
|
981
|
-
fill,
|
|
867
|
+
deposit: undefined,
|
|
868
|
+
fill: fill,
|
|
982
869
|
slowFillRequest: undefined,
|
|
983
870
|
};
|
|
984
871
|
|
|
@@ -1006,40 +893,16 @@ export class BundleDataClient {
|
|
|
1006
893
|
bundleInvalidFillsV3.push(fill);
|
|
1007
894
|
} else {
|
|
1008
895
|
const matchedDeposit = historicalDeposit.deposit;
|
|
1009
|
-
//
|
|
1010
|
-
//
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
destinationClient.spokePool.provider,
|
|
1020
|
-
matchedDeposit,
|
|
1021
|
-
allChainIds
|
|
1022
|
-
);
|
|
1023
|
-
if (!isDefined(fillToRefund)) {
|
|
1024
|
-
bundleUnrepayableFillsV3.push(fill);
|
|
1025
|
-
// Don't return yet as we still need to mark down any unexecutable slow fill leaves
|
|
1026
|
-
// in case this fast fill replaced a slow fill request.
|
|
1027
|
-
} else {
|
|
1028
|
-
// @dev Since queryHistoricalDepositForFill validates the fill by checking individual
|
|
1029
|
-
// object property values against the deposit's, we
|
|
1030
|
-
// sanity check it here by comparing the full relay hashes. If there's an error here then the
|
|
1031
|
-
// historical deposit query is not working as expected.
|
|
1032
|
-
assert(this.getRelayHashFromEvent(matchedDeposit) === relayDataHash, "Relay hashes should match.");
|
|
1033
|
-
validatedBundleV3Fills.push({
|
|
1034
|
-
...fillToRefund,
|
|
1035
|
-
quoteTimestamp: matchedDeposit.quoteTimestamp,
|
|
1036
|
-
});
|
|
1037
|
-
v3RelayHashes[relayDataHash].fill = fillToRefund;
|
|
1038
|
-
|
|
1039
|
-
// No need to check for duplicate deposits here since duplicate deposits with
|
|
1040
|
-
// infinite deadlines are impossible to send via unsafeDeposit().
|
|
1041
|
-
}
|
|
1042
|
-
|
|
896
|
+
// @dev Since queryHistoricalDepositForFill validates the fill by checking individual
|
|
897
|
+
// object property values against the deposit's, we
|
|
898
|
+
// sanity check it here by comparing the full relay hashes. If there's an error here then the
|
|
899
|
+
// historical deposit query is not working as expected.
|
|
900
|
+
assert(this.getRelayHashFromEvent(matchedDeposit) === relayDataHash, "Relay hashes should match.");
|
|
901
|
+
validatedBundleV3Fills.push({
|
|
902
|
+
...fill,
|
|
903
|
+
quoteTimestamp: matchedDeposit.quoteTimestamp,
|
|
904
|
+
});
|
|
905
|
+
v3RelayHashes[relayDataHash].deposit = matchedDeposit;
|
|
1043
906
|
// slow fill requests for deposits from or to lite chains are considered invalid
|
|
1044
907
|
if (
|
|
1045
908
|
fill.relayExecutionInfo.fillType === FillType.ReplacedSlowFill &&
|
|
@@ -1071,17 +934,15 @@ export class BundleDataClient {
|
|
|
1071
934
|
v3RelayHashes[relayDataHash].slowFillRequest = slowFillRequest;
|
|
1072
935
|
if (v3RelayHashes[relayDataHash].fill) {
|
|
1073
936
|
// If there is a fill matching the relay hash, then this slow fill request can't be used
|
|
1074
|
-
// to create a slow fill for a filled deposit.
|
|
1075
|
-
// slow fill requests must precede fills, so if there is a matching fill for this request's
|
|
1076
|
-
// relay data, then this slow fill will be unexecutable.
|
|
937
|
+
// to create a slow fill for a filled deposit.
|
|
1077
938
|
return;
|
|
1078
939
|
}
|
|
1079
940
|
assert(
|
|
1080
|
-
isDefined(v3RelayHashes[relayDataHash].
|
|
941
|
+
isDefined(v3RelayHashes[relayDataHash].deposit),
|
|
1081
942
|
"Deposit should exist in relay hash dictionary."
|
|
1082
943
|
);
|
|
1083
944
|
// The ! is safe here because we've already checked that the deposit exists in the relay hash dictionary.
|
|
1084
|
-
const matchedDeposit = v3RelayHashes[relayDataHash].
|
|
945
|
+
const matchedDeposit = v3RelayHashes[relayDataHash].deposit!;
|
|
1085
946
|
|
|
1086
947
|
// If there is no fill matching the relay hash, then this might be a valid slow fill request
|
|
1087
948
|
// that we should produce a slow fill leaf for. Check if the slow fill request is in the
|
|
@@ -1096,15 +957,13 @@ export class BundleDataClient {
|
|
|
1096
957
|
// so this slow fill request relay data is correct.
|
|
1097
958
|
validatedBundleSlowFills.push(matchedDeposit);
|
|
1098
959
|
}
|
|
1099
|
-
} else {
|
|
1100
|
-
throw new Error("Duplicate slow fill request detected.");
|
|
1101
960
|
}
|
|
1102
961
|
return;
|
|
1103
962
|
}
|
|
1104
963
|
|
|
1105
964
|
// Instantiate dictionary if there is neither a deposit nor fill matching it.
|
|
1106
965
|
v3RelayHashes[relayDataHash] = {
|
|
1107
|
-
|
|
966
|
+
deposit: undefined,
|
|
1108
967
|
fill: undefined,
|
|
1109
968
|
slowFillRequest: slowFillRequest,
|
|
1110
969
|
};
|
|
@@ -1122,8 +981,8 @@ export class BundleDataClient {
|
|
|
1122
981
|
// found using such a method) because infinite fill deadlines cannot be produced from the unsafeDepositV3()
|
|
1123
982
|
// function.
|
|
1124
983
|
if (
|
|
1125
|
-
|
|
1126
|
-
slowFillRequest.
|
|
984
|
+
slowFillRequest.blockNumber >= destinationChainBlockRange[0] &&
|
|
985
|
+
INFINITE_FILL_DEADLINE.eq(slowFillRequest.fillDeadline)
|
|
1127
986
|
) {
|
|
1128
987
|
const historicalDeposit = await queryHistoricalDepositForFill(originClient, slowFillRequest);
|
|
1129
988
|
if (!historicalDeposit.found) {
|
|
@@ -1131,11 +990,6 @@ export class BundleDataClient {
|
|
|
1131
990
|
return;
|
|
1132
991
|
}
|
|
1133
992
|
const matchedDeposit: V3DepositWithBlock = historicalDeposit.deposit;
|
|
1134
|
-
// If deposit is in a following bundle, then this slow fill request will have to be created
|
|
1135
|
-
// once that deposit is in the current bundle.
|
|
1136
|
-
if (matchedDeposit.blockNumber > originChainBlockRange[1]) {
|
|
1137
|
-
return;
|
|
1138
|
-
}
|
|
1139
993
|
// @dev Since queryHistoricalDepositForFill validates the slow fill request by checking individual
|
|
1140
994
|
// object property values against the deposit's, we
|
|
1141
995
|
// sanity check it here by comparing the full relay hashes. If there's an error here then the
|
|
@@ -1144,7 +998,7 @@ export class BundleDataClient {
|
|
|
1144
998
|
this.getRelayHashFromEvent(matchedDeposit) === relayDataHash,
|
|
1145
999
|
"Deposit relay hashes should match."
|
|
1146
1000
|
);
|
|
1147
|
-
v3RelayHashes[relayDataHash].
|
|
1001
|
+
v3RelayHashes[relayDataHash].deposit = matchedDeposit;
|
|
1148
1002
|
|
|
1149
1003
|
if (
|
|
1150
1004
|
!_canCreateSlowFillLeaf(matchedDeposit) ||
|
|
@@ -1165,143 +1019,122 @@ export class BundleDataClient {
|
|
|
1165
1019
|
// - Or, has the deposit expired in this bundle? If so, then we need to issue an expiry refund.
|
|
1166
1020
|
// - And finally, has the deposit been slow filled? If so, then we need to issue a slow fill leaf
|
|
1167
1021
|
// for this "pre-slow-fill-request" if this request took place in a previous bundle.
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
if (!isSlowFill(fill)) {
|
|
1022
|
+
const originBlockRange = getBlockRangeForChain(blockRangesForChains, originChainId, chainIds);
|
|
1023
|
+
|
|
1024
|
+
await mapAsync(
|
|
1025
|
+
bundleDepositHashes.filter((depositHash) => {
|
|
1026
|
+
const { deposit } = v3RelayHashes[depositHash];
|
|
1027
|
+
return (
|
|
1028
|
+
deposit &&
|
|
1029
|
+
deposit.originChainId === originChainId &&
|
|
1030
|
+
deposit.destinationChainId === destinationChainId &&
|
|
1031
|
+
deposit.blockNumber >= originBlockRange[0] &&
|
|
1032
|
+
deposit.blockNumber <= originBlockRange[1] &&
|
|
1033
|
+
!isZeroValueDeposit(deposit)
|
|
1034
|
+
);
|
|
1035
|
+
}),
|
|
1036
|
+
async (depositHash) => {
|
|
1037
|
+
const { deposit, fill, slowFillRequest } = v3RelayHashes[depositHash];
|
|
1038
|
+
if (!deposit) throw new Error("Deposit should exist in relay hash dictionary.");
|
|
1039
|
+
|
|
1040
|
+
// We are willing to refund a pre-fill multiple times for each duplicate deposit.
|
|
1041
|
+
// This is because a duplicate deposit for a pre-fill cannot get
|
|
1042
|
+
// refunded to the depositor anymore because its fill status on-chain has changed to Filled. Therefore
|
|
1043
|
+
// any duplicate deposits result in a net loss of funds for the depositor and effectively pay out
|
|
1044
|
+
// the pre-filler.
|
|
1045
|
+
|
|
1046
|
+
// If fill exists in memory, then the only case in which we need to create a refund is if the
|
|
1047
|
+
// the fill occurred in a previous bundle. There are no expiry refunds for filled deposits.
|
|
1048
|
+
if (fill) {
|
|
1049
|
+
if (fill.blockNumber < destinationChainBlockRange[0] && !isSlowFill(fill)) {
|
|
1050
|
+
// If fill is in the current bundle then we can assume there is already a refund for it, so only
|
|
1051
|
+
// include this pre fill if the fill is in an older bundle. If fill is after this current bundle, then
|
|
1052
|
+
// we won't consider it, following the previous treatment of fills after the bundle block range.
|
|
1196
1053
|
validatedBundleV3Fills.push({
|
|
1197
1054
|
...fill,
|
|
1198
1055
|
quoteTimestamp: deposit.quoteTimestamp,
|
|
1199
1056
|
});
|
|
1200
|
-
} else {
|
|
1201
|
-
// Slow fills cannot result in refunds to a relayer to refund the deposit. Slow fills also
|
|
1202
|
-
// were created after the deposit was sent, so we can assume this deposit is a duplicate.
|
|
1203
|
-
updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
|
|
1204
1057
|
}
|
|
1058
|
+
return;
|
|
1205
1059
|
}
|
|
1206
|
-
return;
|
|
1207
|
-
}
|
|
1208
1060
|
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1061
|
+
// If a slow fill request exists in memory, then we know the deposit has not been filled because fills
|
|
1062
|
+
// must follow slow fill requests and we would have seen the fill already if it existed. Therefore,
|
|
1063
|
+
// we can conclude that either the deposit has expired and we need to create a deposit expiry refund, or
|
|
1064
|
+
// we need to create a slow fill leaf for the deposit. The latter should only happen if the slow fill request
|
|
1065
|
+
// took place in a prior bundle otherwise we would have already created a slow fill leaf for it.
|
|
1066
|
+
if (slowFillRequest) {
|
|
1067
|
+
if (_depositIsExpired(deposit)) {
|
|
1068
|
+
updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
|
|
1069
|
+
} else if (
|
|
1070
|
+
slowFillRequest.blockNumber < destinationChainBlockRange[0] &&
|
|
1071
|
+
_canCreateSlowFillLeaf(deposit)
|
|
1072
|
+
) {
|
|
1073
|
+
validatedBundleSlowFills.push(deposit);
|
|
1074
|
+
}
|
|
1075
|
+
return;
|
|
1224
1076
|
}
|
|
1225
|
-
return;
|
|
1226
|
-
}
|
|
1227
1077
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
// we queried the fillStatus at the end block. Therefore, if the fill took place after the end block,
|
|
1242
|
-
// then we wouldn't be in this branch of the code.
|
|
1243
|
-
const prefill = await this.findMatchingFillEvent(deposit, destinationClient);
|
|
1244
|
-
assert(isDefined(prefill), `findFillEvent# Cannot find prefill: ${relayDataHash}`);
|
|
1245
|
-
assert(this.getRelayHashFromEvent(prefill!) === relayDataHash, "Relay hashes should match.");
|
|
1246
|
-
if (canRefundPrefills) {
|
|
1247
|
-
const verifiedFill = await verifyFillRepayment(
|
|
1248
|
-
prefill!,
|
|
1249
|
-
destinationClient.spokePool.provider,
|
|
1078
|
+
// So at this point in the code, there is no fill or slow fill request in memory for this deposit.
|
|
1079
|
+
// We need to check its fill status on-chain to figure out whether to issue a refund or a slow fill leaf.
|
|
1080
|
+
// We can assume at this point that all fills or slow fill requests, if found, were in previous bundles
|
|
1081
|
+
// because the spoke pool client lookback would have returned this entire bundle of events and stored
|
|
1082
|
+
// them into the relay hash dictionary.
|
|
1083
|
+
const fillStatus = await _getFillStatusForDeposit(deposit, destinationChainBlockRange[1]);
|
|
1084
|
+
|
|
1085
|
+
// If deposit was filled, then we need to issue a refund for it.
|
|
1086
|
+
if (fillStatus === FillStatus.Filled) {
|
|
1087
|
+
// We need to find the fill event to issue a refund to the right relayer and repayment chain,
|
|
1088
|
+
// or msg.sender if relayer address is invalid for the repayment chain.
|
|
1089
|
+
const prefill = (await findFillEvent(
|
|
1090
|
+
destinationClient.spokePool,
|
|
1250
1091
|
deposit,
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
} else if (!isSlowFill(verifiedFill)) {
|
|
1092
|
+
destinationClient.deploymentBlock,
|
|
1093
|
+
destinationClient.latestBlockSearched
|
|
1094
|
+
)) as unknown as FillWithBlock;
|
|
1095
|
+
if (!isSlowFill(prefill)) {
|
|
1256
1096
|
validatedBundleV3Fills.push({
|
|
1257
|
-
...
|
|
1097
|
+
...prefill,
|
|
1258
1098
|
quoteTimestamp: deposit.quoteTimestamp,
|
|
1259
1099
|
});
|
|
1260
|
-
} else {
|
|
1261
|
-
// Slow fills cannot result in refunds to a relayer to refund the deposit. Slow fills also
|
|
1262
|
-
// were created after the deposit was sent, so we can assume this deposit is a duplicate.
|
|
1263
|
-
updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
|
|
1264
1100
|
}
|
|
1265
1101
|
}
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
// If deposit newly expired, then we can't create a slow fill leaf for it but we can
|
|
1283
|
-
// create a deposit refund for it.
|
|
1284
|
-
validatedBundleSlowFills.push(deposit);
|
|
1102
|
+
// If deposit is not filled and its newly expired, we can create a deposit refund for it.
|
|
1103
|
+
// We don't check that fillDeadline >= bundleBlockTimestamps[destinationChainId][0] because
|
|
1104
|
+
// that would eliminate any deposits in this bundle with a very low fillDeadline like equal to 0
|
|
1105
|
+
// for example. Those should be included in this bundle of refunded deposits.
|
|
1106
|
+
else if (_depositIsExpired(deposit)) {
|
|
1107
|
+
updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
|
|
1108
|
+
}
|
|
1109
|
+
// If slow fill requested, then issue a slow fill leaf for the deposit.
|
|
1110
|
+
else if (fillStatus === FillStatus.RequestedSlowFill) {
|
|
1111
|
+
// Input and Output tokens must be equivalent on the deposit for this to be slow filled.
|
|
1112
|
+
// Slow fill requests for deposits from or to lite chains are considered invalid
|
|
1113
|
+
if (_canCreateSlowFillLeaf(deposit)) {
|
|
1114
|
+
// If deposit newly expired, then we can't create a slow fill leaf for it but we can
|
|
1115
|
+
// create a deposit refund for it.
|
|
1116
|
+
validatedBundleSlowFills.push(deposit);
|
|
1117
|
+
}
|
|
1285
1118
|
}
|
|
1286
1119
|
}
|
|
1287
|
-
|
|
1120
|
+
);
|
|
1288
1121
|
|
|
1289
1122
|
// For all fills that came after a slow fill request, we can now check if the slow fill request
|
|
1290
1123
|
// was a valid one and whether it was created in a previous bundle. If so, then it created a slow fill
|
|
1291
1124
|
// leaf that is now unexecutable.
|
|
1292
1125
|
fastFillsReplacingSlowFills.forEach((relayDataHash) => {
|
|
1293
|
-
const {
|
|
1126
|
+
const { deposit, slowFillRequest, fill } = v3RelayHashes[relayDataHash];
|
|
1294
1127
|
assert(
|
|
1295
1128
|
fill?.relayExecutionInfo.fillType === FillType.ReplacedSlowFill,
|
|
1296
1129
|
"Fill type should be ReplacedSlowFill."
|
|
1297
1130
|
);
|
|
1298
1131
|
// Needed for TSC - are implicitely checking that deposit exists by making it to this point.
|
|
1299
|
-
if (!
|
|
1132
|
+
if (!deposit) {
|
|
1300
1133
|
throw new Error("Deposit should exist in relay hash dictionary.");
|
|
1301
1134
|
}
|
|
1302
1135
|
// We should never push fast fills involving lite chains here because slow fill requests for them are invalid:
|
|
1303
1136
|
assert(
|
|
1304
|
-
_canCreateSlowFillLeaf(
|
|
1137
|
+
_canCreateSlowFillLeaf(deposit),
|
|
1305
1138
|
"fastFillsReplacingSlowFills should contain only deposits that can be slow filled"
|
|
1306
1139
|
);
|
|
1307
1140
|
const destinationBlockRange = getBlockRangeForChain(blockRangesForChains, destinationChainId, chainIds);
|
|
@@ -1311,7 +1144,7 @@ export class BundleDataClient {
|
|
|
1311
1144
|
!slowFillRequest ||
|
|
1312
1145
|
slowFillRequest.blockNumber < destinationBlockRange[0]
|
|
1313
1146
|
) {
|
|
1314
|
-
validatedBundleUnexecutableSlowFills.push(
|
|
1147
|
+
validatedBundleUnexecutableSlowFills.push(deposit);
|
|
1315
1148
|
}
|
|
1316
1149
|
});
|
|
1317
1150
|
}
|
|
@@ -1325,14 +1158,10 @@ export class BundleDataClient {
|
|
|
1325
1158
|
// For all deposits older than this bundle, we need to check if they expired in this bundle and if they did,
|
|
1326
1159
|
// whether there was a slow fill created for it in a previous bundle that is now unexecutable and replaced
|
|
1327
1160
|
// by a new expired deposit refund.
|
|
1328
|
-
await forEachAsync(olderDepositHashes, async (
|
|
1329
|
-
const {
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
throw new Error("Deposit should exist in relay hash dictionary.");
|
|
1333
|
-
}
|
|
1334
|
-
const deposit = deposits[index];
|
|
1335
|
-
const { destinationChainId } = deposit;
|
|
1161
|
+
await forEachAsync(olderDepositHashes, async (relayDataHash) => {
|
|
1162
|
+
const { deposit, slowFillRequest, fill } = v3RelayHashes[relayDataHash];
|
|
1163
|
+
assert(isDefined(deposit), "Deposit should exist in relay hash dictionary.");
|
|
1164
|
+
const { destinationChainId } = deposit!;
|
|
1336
1165
|
const destinationBlockRange = getBlockRangeForChain(blockRangesForChains, destinationChainId, chainIds);
|
|
1337
1166
|
|
|
1338
1167
|
// Only look for deposits that were mined before this bundle and that are newly expired.
|
|
@@ -1381,7 +1210,7 @@ export class BundleDataClient {
|
|
|
1381
1210
|
validatedBundleV3Fills.length > 0
|
|
1382
1211
|
? this.clients.hubPoolClient.batchComputeRealizedLpFeePct(
|
|
1383
1212
|
validatedBundleV3Fills.map((fill) => {
|
|
1384
|
-
const matchedDeposit = v3RelayHashes[this.getRelayHashFromEvent(fill)].
|
|
1213
|
+
const matchedDeposit = v3RelayHashes[this.getRelayHashFromEvent(fill)].deposit;
|
|
1385
1214
|
assert(isDefined(matchedDeposit), "Deposit should exist in relay hash dictionary.");
|
|
1386
1215
|
const { chainToSendRefundTo: paymentChainId } = getRefundInformationFromFill(
|
|
1387
1216
|
fill,
|
|
@@ -1425,7 +1254,7 @@ export class BundleDataClient {
|
|
|
1425
1254
|
});
|
|
1426
1255
|
v3FillLpFees.forEach(({ realizedLpFeePct }, idx) => {
|
|
1427
1256
|
const fill = validatedBundleV3Fills[idx];
|
|
1428
|
-
const associatedDeposit = v3RelayHashes[this.getRelayHashFromEvent(fill)].
|
|
1257
|
+
const associatedDeposit = v3RelayHashes[this.getRelayHashFromEvent(fill)].deposit;
|
|
1429
1258
|
assert(isDefined(associatedDeposit), "Deposit should exist in relay hash dictionary.");
|
|
1430
1259
|
const { chainToSendRefundTo, repaymentToken } = getRefundInformationFromFill(
|
|
1431
1260
|
fill,
|
|
@@ -1434,18 +1263,10 @@ export class BundleDataClient {
|
|
|
1434
1263
|
chainIds,
|
|
1435
1264
|
associatedDeposit!.fromLiteChain
|
|
1436
1265
|
);
|
|
1437
|
-
updateBundleFillsV3(bundleFillsV3, fill, realizedLpFeePct, chainToSendRefundTo, repaymentToken
|
|
1266
|
+
updateBundleFillsV3(bundleFillsV3, fill, realizedLpFeePct, chainToSendRefundTo, repaymentToken);
|
|
1438
1267
|
});
|
|
1439
1268
|
v3SlowFillLpFees.forEach(({ realizedLpFeePct: lpFeePct }, idx) => {
|
|
1440
1269
|
const deposit = validatedBundleSlowFills[idx];
|
|
1441
|
-
// We should not create slow fill leaves for duplicate deposit hashes and we should only create a slow
|
|
1442
|
-
// fill leaf for the first deposit (the quote timestamp of the deposit determines the LP fee, so its
|
|
1443
|
-
// important we pick out the correct deposit). Deposits are pushed into validatedBundleSlowFills in ascending
|
|
1444
|
-
// order so the following slice will only match the first deposit.
|
|
1445
|
-
const relayDataHash = this.getRelayHashFromEvent(deposit);
|
|
1446
|
-
if (validatedBundleSlowFills.slice(0, idx).some((d) => this.getRelayHashFromEvent(d) === relayDataHash)) {
|
|
1447
|
-
return;
|
|
1448
|
-
}
|
|
1449
1270
|
updateBundleSlowFills(bundleSlowFillsV3, { ...deposit, lpFeePct });
|
|
1450
1271
|
});
|
|
1451
1272
|
v3UnexecutableSlowFillLpFees.forEach(({ realizedLpFeePct: lpFeePct }, idx) => {
|
|
@@ -1456,6 +1277,7 @@ export class BundleDataClient {
|
|
|
1456
1277
|
const v3SpokeEventsReadable = prettyPrintV3SpokePoolEvents(
|
|
1457
1278
|
bundleDepositsV3,
|
|
1458
1279
|
bundleFillsV3,
|
|
1280
|
+
bundleInvalidFillsV3,
|
|
1459
1281
|
bundleSlowFillsV3,
|
|
1460
1282
|
expiredDepositsToRefundV3,
|
|
1461
1283
|
unexecutableSlowFills
|
|
@@ -1470,15 +1292,6 @@ export class BundleDataClient {
|
|
|
1470
1292
|
});
|
|
1471
1293
|
}
|
|
1472
1294
|
|
|
1473
|
-
if (bundleUnrepayableFillsV3.length > 0) {
|
|
1474
|
-
this.logger.debug({
|
|
1475
|
-
at: "BundleDataClient#loadData",
|
|
1476
|
-
message: "Finished loading V3 spoke pool data and found some unrepayable V3 fills in range",
|
|
1477
|
-
blockRangesForChains,
|
|
1478
|
-
bundleUnrepayableFillsV3,
|
|
1479
|
-
});
|
|
1480
|
-
}
|
|
1481
|
-
|
|
1482
1295
|
this.logger.debug({
|
|
1483
1296
|
at: "BundleDataClient#loadDataFromScratch",
|
|
1484
1297
|
message: `Computed bundle data in ${Math.round(performance.now() - start) / 1000}s.`,
|
|
@@ -1498,24 +1311,8 @@ export class BundleDataClient {
|
|
|
1498
1311
|
// keccak256 hash of the relay data, which can be used as input into the on-chain `fillStatuses()` function in the
|
|
1499
1312
|
// spoke pool contract. However, this internal function is used to uniquely identify a bridging event
|
|
1500
1313
|
// for speed since its easier to build a string from the event data than to hash it.
|
|
1501
|
-
|
|
1502
|
-
return `${event.depositor}-${event.recipient}-${event.exclusiveRelayer}-${event.inputToken}-${event.outputToken}-${
|
|
1503
|
-
event.inputAmount
|
|
1504
|
-
}-${event.outputAmount}-${event.originChainId}-${event.depositId.toString()}-${event.fillDeadline}-${
|
|
1505
|
-
event.exclusivityDeadline
|
|
1506
|
-
}-${event.message}-${event.destinationChainId}`;
|
|
1507
|
-
}
|
|
1508
|
-
|
|
1509
|
-
protected async findMatchingFillEvent(
|
|
1510
|
-
deposit: DepositWithBlock,
|
|
1511
|
-
spokePoolClient: SpokePoolClient
|
|
1512
|
-
): Promise<FillWithBlock | undefined> {
|
|
1513
|
-
return await findFillEvent(
|
|
1514
|
-
spokePoolClient.spokePool,
|
|
1515
|
-
deposit,
|
|
1516
|
-
spokePoolClient.deploymentBlock,
|
|
1517
|
-
spokePoolClient.latestBlockSearched
|
|
1518
|
-
);
|
|
1314
|
+
private getRelayHashFromEvent(event: V3DepositWithBlock | V3FillWithBlock | SlowFillRequestWithBlock): string {
|
|
1315
|
+
return `${event.depositor}-${event.recipient}-${event.exclusiveRelayer}-${event.inputToken}-${event.outputToken}-${event.inputAmount}-${event.outputAmount}-${event.originChainId}-${event.depositId}-${event.fillDeadline}-${event.exclusivityDeadline}-${event.message}-${event.destinationChainId}`;
|
|
1519
1316
|
}
|
|
1520
1317
|
|
|
1521
1318
|
async getBundleBlockTimestamps(
|
|
@@ -1543,26 +1340,13 @@ export class BundleDataClient {
|
|
|
1543
1340
|
// will usually be called in production with block ranges that were validated by
|
|
1544
1341
|
// DataworkerUtils.blockRangesAreInvalidForSpokeClients.
|
|
1545
1342
|
const startBlockForChain = Math.min(_startBlockForChain, spokePoolClient.latestBlockSearched);
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
// in exactly one bundle, therefore we must make sure that the bundle block timestamp for one bundle's
|
|
1549
|
-
// end block is exactly equal to the bundle block timestamp for the next bundle's start block. This way
|
|
1550
|
-
// there are no gaps in block timestamps between bundles.
|
|
1551
|
-
const endBlockForChain = Math.min(_endBlockForChain + 1, spokePoolClient.latestBlockSearched);
|
|
1552
|
-
const [startTime, _endTime] = [
|
|
1343
|
+
const endBlockForChain = Math.min(_endBlockForChain, spokePoolClient.latestBlockSearched);
|
|
1344
|
+
const [startTime, endTime] = [
|
|
1553
1345
|
await spokePoolClient.getTimestampForBlock(startBlockForChain),
|
|
1554
1346
|
await spokePoolClient.getTimestampForBlock(endBlockForChain),
|
|
1555
1347
|
];
|
|
1556
|
-
// @dev similar to reasoning above to ensure no gaps between bundle block range timestamps and also
|
|
1557
|
-
// no overlap, subtract 1 from the end time.
|
|
1558
|
-
const endBlockDelta = endBlockForChain > startBlockForChain ? 1 : 0;
|
|
1559
|
-
const endTime = Math.max(0, _endTime - endBlockDelta);
|
|
1560
|
-
|
|
1561
1348
|
// Sanity checks:
|
|
1562
|
-
assert(
|
|
1563
|
-
endTime >= startTime,
|
|
1564
|
-
`End time for block ${endBlockForChain} should be greater than start time for block ${startBlockForChain}: ${endTime} >= ${startTime}.`
|
|
1565
|
-
);
|
|
1349
|
+
assert(endTime >= startTime, "End time should be greater than start time.");
|
|
1566
1350
|
assert(
|
|
1567
1351
|
startBlockForChain === 0 || startTime > 0,
|
|
1568
1352
|
"Start timestamp must be greater than 0 if the start block is greater than 0."
|