@across-protocol/sdk 3.4.19 → 4.0.0-beta.0
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 +1 -1
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.js +215 -253
- 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/BundleDataClient/utils/SuperstructUtils.d.ts +12 -14
- package/dist/cjs/clients/BundleDataClient/utils/SuperstructUtils.js +3 -3
- package/dist/cjs/clients/BundleDataClient/utils/SuperstructUtils.js.map +1 -1
- package/dist/cjs/clients/SpokePoolClient.d.ts +16 -12
- package/dist/cjs/clients/SpokePoolClient.js +25 -20
- package/dist/cjs/clients/SpokePoolClient.js.map +1 -1
- package/dist/cjs/clients/mocks/MockSpokePoolClient.d.ts +5 -7
- package/dist/cjs/clients/mocks/MockSpokePoolClient.js +6 -18
- package/dist/cjs/clients/mocks/MockSpokePoolClient.js.map +1 -1
- package/dist/cjs/constants.d.ts +2 -2
- package/dist/cjs/constants.js +3 -3
- package/dist/cjs/constants.js.map +1 -1
- package/dist/cjs/interfaces/SpokePool.d.ts +2 -2
- 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 -2
- package/dist/cjs/utils/AddressUtils.js +1 -19
- package/dist/cjs/utils/AddressUtils.js.map +1 -1
- package/dist/cjs/utils/BigNumberUtils.d.ts +0 -1
- package/dist/cjs/utils/BigNumberUtils.js +1 -5
- package/dist/cjs/utils/BigNumberUtils.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 +5 -14
- package/dist/cjs/utils/DepositUtils.js.map +1 -1
- package/dist/cjs/utils/EventUtils.js +0 -20
- package/dist/cjs/utils/EventUtils.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.d.ts +5 -7
- package/dist/cjs/utils/SpokeUtils.js +59 -32
- package/dist/cjs/utils/SpokeUtils.js.map +1 -1
- package/dist/cjs/utils/ValidatorUtils.js +1 -1
- package/dist/cjs/utils/ValidatorUtils.js.map +1 -1
- package/dist/cjs/utils/common.d.ts +0 -1
- package/dist/cjs/utils/common.js +1 -2
- package/dist/cjs/utils/common.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/BundleDataClient.d.ts +1 -1
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js +276 -323
- 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/BundleDataClient/utils/SuperstructUtils.d.ts +12 -14
- package/dist/esm/clients/BundleDataClient/utils/SuperstructUtils.js +4 -4
- package/dist/esm/clients/BundleDataClient/utils/SuperstructUtils.js.map +1 -1
- package/dist/esm/clients/SpokePoolClient.d.ts +24 -13
- package/dist/esm/clients/SpokePoolClient.js +34 -22
- package/dist/esm/clients/SpokePoolClient.js.map +1 -1
- package/dist/esm/clients/mocks/MockSpokePoolClient.d.ts +5 -7
- package/dist/esm/clients/mocks/MockSpokePoolClient.js +7 -19
- package/dist/esm/clients/mocks/MockSpokePoolClient.js.map +1 -1
- package/dist/esm/constants.d.ts +2 -2
- package/dist/esm/constants.js +2 -2
- package/dist/esm/constants.js.map +1 -1
- package/dist/esm/interfaces/SpokePool.d.ts +2 -2
- 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 -2
- package/dist/esm/utils/AddressUtils.js +0 -25
- package/dist/esm/utils/AddressUtils.js.map +1 -1
- package/dist/esm/utils/BigNumberUtils.d.ts +0 -8
- package/dist/esm/utils/BigNumberUtils.js +0 -10
- package/dist/esm/utils/BigNumberUtils.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/EventUtils.js +0 -30
- package/dist/esm/utils/EventUtils.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.d.ts +5 -7
- package/dist/esm/utils/SpokeUtils.js +61 -34
- package/dist/esm/utils/SpokeUtils.js.map +1 -1
- package/dist/esm/utils/ValidatorUtils.js +1 -1
- package/dist/esm/utils/ValidatorUtils.js.map +1 -1
- package/dist/esm/utils/abi/typechain/Multicall3.d.ts +1 -4
- package/dist/esm/utils/abi/typechain/factories/Multicall3__factory.js.map +1 -1
- package/dist/esm/utils/common.d.ts +0 -1
- package/dist/esm/utils/common.js +0 -1
- package/dist/esm/utils/common.js.map +1 -1
- package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts +1 -1
- 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/BundleDataClient/utils/SuperstructUtils.d.ts +12 -14
- package/dist/types/clients/BundleDataClient/utils/SuperstructUtils.d.ts.map +1 -1
- package/dist/types/clients/SpokePoolClient.d.ts +24 -13
- package/dist/types/clients/SpokePoolClient.d.ts.map +1 -1
- package/dist/types/clients/mocks/MockSpokePoolClient.d.ts +5 -7
- package/dist/types/clients/mocks/MockSpokePoolClient.d.ts.map +1 -1
- package/dist/types/constants.d.ts +2 -2
- package/dist/types/constants.d.ts.map +1 -1
- package/dist/types/interfaces/SpokePool.d.ts +2 -2
- package/dist/types/interfaces/SpokePool.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 -2
- package/dist/types/utils/AddressUtils.d.ts.map +1 -1
- package/dist/types/utils/BigNumberUtils.d.ts +0 -8
- package/dist/types/utils/BigNumberUtils.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/EventUtils.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 +5 -7
- package/dist/types/utils/SpokeUtils.d.ts.map +1 -1
- package/dist/types/utils/abi/typechain/Multicall3.d.ts +1 -4
- 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 +0 -1
- package/dist/types/utils/common.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/clients/BundleDataClient/BundleDataClient.ts +238 -309
- package/src/clients/BundleDataClient/utils/DataworkerUtils.ts +8 -0
- package/src/clients/BundleDataClient/utils/FillUtils.ts +2 -47
- package/src/clients/BundleDataClient/utils/SuperstructUtils.ts +2 -4
- package/src/clients/SpokePoolClient.ts +44 -31
- package/src/clients/mocks/MockSpokePoolClient.ts +10 -25
- package/src/constants.ts +2 -2
- package/src/interfaces/SpokePool.ts +2 -2
- package/src/providers/index.ts +0 -1
- package/src/utils/AddressUtils.ts +0 -26
- package/src/utils/BigNumberUtils.ts +0 -11
- package/src/utils/CachingUtils.ts +1 -1
- package/src/utils/DepositUtils.ts +5 -14
- package/src/utils/EventUtils.ts +0 -31
- package/src/utils/NetworkUtils.ts +0 -11
- package/src/utils/SpokeUtils.ts +60 -52
- package/src/utils/ValidatorUtils.ts +1 -1
- package/src/utils/common.ts +0 -2
- 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
|
@@ -14,6 +14,9 @@ import {
|
|
|
14
14
|
ExpiredDepositsToRefundV3,
|
|
15
15
|
Clients,
|
|
16
16
|
CombinedRefunds,
|
|
17
|
+
FillWithBlock,
|
|
18
|
+
Deposit,
|
|
19
|
+
DepositWithBlock,
|
|
17
20
|
} from "../../interfaces";
|
|
18
21
|
import { AcrossConfigStoreClient, SpokePoolClient } from "..";
|
|
19
22
|
import {
|
|
@@ -30,11 +33,9 @@ import {
|
|
|
30
33
|
getImpliedBundleBlockRanges,
|
|
31
34
|
isSlowFill,
|
|
32
35
|
mapAsync,
|
|
33
|
-
filterAsync,
|
|
34
36
|
bnUint32Max,
|
|
35
37
|
isZeroValueDeposit,
|
|
36
|
-
|
|
37
|
-
isValidEvmAddress,
|
|
38
|
+
findFillEvent,
|
|
38
39
|
} from "../../utils";
|
|
39
40
|
import winston from "winston";
|
|
40
41
|
import {
|
|
@@ -49,7 +50,6 @@ import {
|
|
|
49
50
|
prettyPrintV3SpokePoolEvents,
|
|
50
51
|
V3DepositWithBlock,
|
|
51
52
|
V3FillWithBlock,
|
|
52
|
-
verifyFillRepayment,
|
|
53
53
|
} from "./utils";
|
|
54
54
|
|
|
55
55
|
// max(uint256) - 1
|
|
@@ -59,10 +59,6 @@ type DataCache = Record<string, Promise<LoadDataReturnValue>>;
|
|
|
59
59
|
|
|
60
60
|
// V3 dictionary helper functions
|
|
61
61
|
function updateExpiredDepositsV3(dict: ExpiredDepositsToRefundV3, deposit: V3DepositWithBlock): void {
|
|
62
|
-
// A deposit refund for a deposit is invalid if the depositor has a bytes32 address input for an EVM chain. It is valid otherwise.
|
|
63
|
-
if (chainIsEvm(deposit.originChainId) && !isValidEvmAddress(deposit.depositor)) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
62
|
const { originChainId, inputToken } = deposit;
|
|
67
63
|
if (!dict?.[originChainId]?.[inputToken]) {
|
|
68
64
|
assign(dict, [originChainId, inputToken], []);
|
|
@@ -83,13 +79,8 @@ function updateBundleFillsV3(
|
|
|
83
79
|
fill: V3FillWithBlock,
|
|
84
80
|
lpFeePct: BigNumber,
|
|
85
81
|
repaymentChainId: number,
|
|
86
|
-
repaymentToken: string
|
|
87
|
-
repaymentAddress: string
|
|
82
|
+
repaymentToken: string
|
|
88
83
|
): void {
|
|
89
|
-
// It is impossible to refund a deposit if the repayment chain is EVM and the relayer is a non-evm address.
|
|
90
|
-
if (chainIsEvm(repaymentChainId) && !isValidEvmAddress(repaymentAddress)) {
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
84
|
if (!dict?.[repaymentChainId]?.[repaymentToken]) {
|
|
94
85
|
assign(dict, [repaymentChainId, repaymentToken], {
|
|
95
86
|
fills: [],
|
|
@@ -99,19 +90,19 @@ function updateBundleFillsV3(
|
|
|
99
90
|
});
|
|
100
91
|
}
|
|
101
92
|
|
|
102
|
-
const bundleFill: BundleFillV3 = { ...fill, lpFeePct
|
|
93
|
+
const bundleFill: BundleFillV3 = { ...fill, lpFeePct };
|
|
103
94
|
|
|
104
95
|
// Add all fills, slow and fast, to dictionary.
|
|
105
96
|
assign(dict, [repaymentChainId, repaymentToken, "fills"], [bundleFill]);
|
|
106
97
|
|
|
107
98
|
// All fills update the bundle LP fees.
|
|
108
99
|
const refundObj = dict[repaymentChainId][repaymentToken];
|
|
109
|
-
const realizedLpFee =
|
|
100
|
+
const realizedLpFee = fill.inputAmount.mul(bundleFill.lpFeePct).div(fixedPointAdjustment);
|
|
110
101
|
refundObj.realizedLpFees = refundObj.realizedLpFees ? refundObj.realizedLpFees.add(realizedLpFee) : realizedLpFee;
|
|
111
102
|
|
|
112
103
|
// Only fast fills get refunded.
|
|
113
|
-
if (!isSlowFill(
|
|
114
|
-
const refundAmount =
|
|
104
|
+
if (!isSlowFill(fill)) {
|
|
105
|
+
const refundAmount = fill.inputAmount.mul(fixedPointAdjustment.sub(lpFeePct)).div(fixedPointAdjustment);
|
|
115
106
|
refundObj.totalRefundAmount = refundObj.totalRefundAmount
|
|
116
107
|
? refundObj.totalRefundAmount.add(refundAmount)
|
|
117
108
|
: refundAmount;
|
|
@@ -119,10 +110,10 @@ function updateBundleFillsV3(
|
|
|
119
110
|
// Instantiate dictionary if it doesn't exist.
|
|
120
111
|
refundObj.refunds ??= {};
|
|
121
112
|
|
|
122
|
-
if (refundObj.refunds[
|
|
123
|
-
refundObj.refunds[
|
|
113
|
+
if (refundObj.refunds[fill.relayer]) {
|
|
114
|
+
refundObj.refunds[fill.relayer] = refundObj.refunds[fill.relayer].add(refundAmount);
|
|
124
115
|
} else {
|
|
125
|
-
refundObj.refunds[
|
|
116
|
+
refundObj.refunds[fill.relayer] = refundAmount;
|
|
126
117
|
}
|
|
127
118
|
}
|
|
128
119
|
}
|
|
@@ -242,6 +233,7 @@ export class BundleDataClient {
|
|
|
242
233
|
bundleData: prettyPrintV3SpokePoolEvents(
|
|
243
234
|
bundleData.bundleDepositsV3,
|
|
244
235
|
bundleData.bundleFillsV3,
|
|
236
|
+
[], // Invalid fills are not persisted to Arweave.
|
|
245
237
|
bundleData.bundleSlowFillsV3,
|
|
246
238
|
bundleData.expiredDepositsToRefundV3,
|
|
247
239
|
bundleData.unexecutableSlowFills
|
|
@@ -289,7 +281,7 @@ export class BundleDataClient {
|
|
|
289
281
|
// so as not to affect this approximate refund count.
|
|
290
282
|
const arweaveData = await this.loadArweaveData(bundleEvaluationBlockRanges);
|
|
291
283
|
if (arweaveData === undefined) {
|
|
292
|
-
combinedRefunds =
|
|
284
|
+
combinedRefunds = this.getApproximateRefundsForBlockRange(chainIds, bundleEvaluationBlockRanges);
|
|
293
285
|
} else {
|
|
294
286
|
const { bundleFillsV3, expiredDepositsToRefundV3 } = arweaveData;
|
|
295
287
|
combinedRefunds = getRefundsFromBundle(bundleFillsV3, expiredDepositsToRefundV3);
|
|
@@ -310,63 +302,50 @@ export class BundleDataClient {
|
|
|
310
302
|
}
|
|
311
303
|
|
|
312
304
|
// @dev This helper function should probably be moved to the InventoryClient
|
|
313
|
-
|
|
305
|
+
getApproximateRefundsForBlockRange(chainIds: number[], blockRanges: number[][]): CombinedRefunds {
|
|
314
306
|
const refundsForChain: CombinedRefunds = {};
|
|
315
307
|
for (const chainId of chainIds) {
|
|
316
308
|
if (this.spokePoolClients[chainId] === undefined) {
|
|
317
309
|
continue;
|
|
318
310
|
}
|
|
319
311
|
const chainIndex = chainIds.indexOf(chainId);
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
312
|
+
this.spokePoolClients[chainId]
|
|
313
|
+
.getFills()
|
|
314
|
+
.filter((fill) => {
|
|
315
|
+
if (fill.blockNumber < blockRanges[chainIndex][0] || fill.blockNumber > blockRanges[chainIndex][1]) {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
324
318
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
return false;
|
|
328
|
-
}
|
|
329
|
-
const matchingDeposit = this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
|
|
330
|
-
const hasMatchingDeposit =
|
|
331
|
-
matchingDeposit !== undefined &&
|
|
332
|
-
this.getRelayHashFromEvent(fill) === this.getRelayHashFromEvent(matchingDeposit);
|
|
333
|
-
if (hasMatchingDeposit) {
|
|
334
|
-
const validRepayment = await verifyFillRepayment(
|
|
335
|
-
fill,
|
|
336
|
-
this.spokePoolClients[fill.destinationChainId].spokePool.provider,
|
|
337
|
-
matchingDeposit,
|
|
338
|
-
// @dev: to get valid repayment chain ID's, get all chain IDs for the bundle block range and remove
|
|
339
|
-
// disabled block ranges.
|
|
340
|
-
this.clients.configStoreClient
|
|
341
|
-
.getChainIdIndicesForBlock(blockRanges[0][1])
|
|
342
|
-
.filter((_chainId, i) => !isChainDisabled(blockRanges[i]))
|
|
343
|
-
);
|
|
344
|
-
if (!isDefined(validRepayment)) {
|
|
319
|
+
// If origin spoke pool client isn't defined, we can't validate it.
|
|
320
|
+
if (this.spokePoolClients[fill.originChainId] === undefined) {
|
|
345
321
|
return false;
|
|
346
322
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
fill
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
323
|
+
const matchingDeposit = this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
|
|
324
|
+
const hasMatchingDeposit =
|
|
325
|
+
matchingDeposit !== undefined &&
|
|
326
|
+
this.getRelayHashFromEvent(fill) === this.getRelayHashFromEvent(matchingDeposit);
|
|
327
|
+
return hasMatchingDeposit;
|
|
328
|
+
})
|
|
329
|
+
.forEach((fill) => {
|
|
330
|
+
const matchingDeposit = this.spokePoolClients[fill.originChainId].getDeposit(fill.depositId);
|
|
331
|
+
assert(isDefined(matchingDeposit), "Deposit not found for fill.");
|
|
332
|
+
const { chainToSendRefundTo, repaymentToken } = getRefundInformationFromFill(
|
|
333
|
+
fill,
|
|
334
|
+
this.clients.hubPoolClient,
|
|
335
|
+
blockRanges,
|
|
336
|
+
this.chainIdListForBundleEvaluationBlockNumbers,
|
|
337
|
+
matchingDeposit!.fromLiteChain // Use ! because we've already asserted that matchingDeposit is defined.
|
|
338
|
+
);
|
|
339
|
+
// Assume that lp fees are 0 for the sake of speed. In the future we could batch compute
|
|
340
|
+
// these or make hardcoded assumptions based on the origin-repayment chain direction. This might result
|
|
341
|
+
// in slight over estimations of refunds, but its not clear whether underestimating or overestimating is
|
|
342
|
+
// worst from the relayer's perspective.
|
|
343
|
+
const { relayer, inputAmount: refundAmount } = fill;
|
|
344
|
+
refundsForChain[chainToSendRefundTo] ??= {};
|
|
345
|
+
refundsForChain[chainToSendRefundTo][repaymentToken] ??= {};
|
|
346
|
+
const existingRefundAmount = refundsForChain[chainToSendRefundTo][repaymentToken][relayer] ?? bnZero;
|
|
347
|
+
refundsForChain[chainToSendRefundTo][repaymentToken][relayer] = existingRefundAmount.add(refundAmount);
|
|
348
|
+
});
|
|
370
349
|
}
|
|
371
350
|
return refundsForChain;
|
|
372
351
|
}
|
|
@@ -410,7 +389,7 @@ export class BundleDataClient {
|
|
|
410
389
|
async getLatestPoolRebalanceRoot(): Promise<{ root: PoolRebalanceRoot; blockRanges: number[][] }> {
|
|
411
390
|
const { bundleData, blockRanges } = await this.getLatestProposedBundleData();
|
|
412
391
|
const hubPoolClient = this.clients.hubPoolClient;
|
|
413
|
-
const root = _buildPoolRebalanceRoot(
|
|
392
|
+
const root = await _buildPoolRebalanceRoot(
|
|
414
393
|
hubPoolClient.latestBlockSearched,
|
|
415
394
|
blockRanges[0][1],
|
|
416
395
|
bundleData.bundleDepositsV3,
|
|
@@ -493,7 +472,7 @@ export class BundleDataClient {
|
|
|
493
472
|
// ok for this use case.
|
|
494
473
|
const arweaveData = await this.loadArweaveData(pendingBundleBlockRanges);
|
|
495
474
|
if (arweaveData === undefined) {
|
|
496
|
-
combinedRefunds.push(
|
|
475
|
+
combinedRefunds.push(this.getApproximateRefundsForBlockRange(chainIds, pendingBundleBlockRanges));
|
|
497
476
|
} else {
|
|
498
477
|
const { bundleFillsV3, expiredDepositsToRefundV3 } = arweaveData;
|
|
499
478
|
combinedRefunds.push(getRefundsFromBundle(bundleFillsV3, expiredDepositsToRefundV3));
|
|
@@ -508,7 +487,7 @@ export class BundleDataClient {
|
|
|
508
487
|
// - Only look up fills sent after the pending bundle's end blocks
|
|
509
488
|
// - Skip LP fee computations and just assume the relayer is being refunded the full deposit.inputAmount
|
|
510
489
|
const start = performance.now();
|
|
511
|
-
combinedRefunds.push(
|
|
490
|
+
combinedRefunds.push(this.getApproximateRefundsForBlockRange(chainIds, widestBundleBlockRanges));
|
|
512
491
|
this.logger.debug({
|
|
513
492
|
at: "BundleDataClient#getNextBundleRefunds",
|
|
514
493
|
message: `Loading approximate refunds for next bundle in ${Math.round(performance.now() - start) / 1000}s.`,
|
|
@@ -675,7 +654,6 @@ export class BundleDataClient {
|
|
|
675
654
|
const bundleDepositsV3: BundleDepositsV3 = {}; // Deposits in bundle block range.
|
|
676
655
|
const bundleFillsV3: BundleFillsV3 = {}; // Fills to refund in bundle block range.
|
|
677
656
|
const bundleInvalidFillsV3: V3FillWithBlock[] = []; // Fills that are not valid in this bundle.
|
|
678
|
-
const bundleUnrepayableFillsV3: V3FillWithBlock[] = []; // Fills that are not repayable in this bundle.
|
|
679
657
|
const bundleSlowFillsV3: BundleSlowFills = {}; // Deposits that we need to send slow fills
|
|
680
658
|
// for in this bundle.
|
|
681
659
|
const expiredDepositsToRefundV3: ExpiredDepositsToRefundV3 = {};
|
|
@@ -691,6 +669,35 @@ export class BundleDataClient {
|
|
|
691
669
|
return isChainDisabled(blockRangeForChain);
|
|
692
670
|
};
|
|
693
671
|
|
|
672
|
+
const _canCreateSlowFillLeaf = (deposit: DepositWithBlock): boolean => {
|
|
673
|
+
return (
|
|
674
|
+
// Cannot slow fill when input and output tokens are not equivalent.
|
|
675
|
+
this.clients.hubPoolClient.areTokensEquivalent(
|
|
676
|
+
deposit.inputToken,
|
|
677
|
+
deposit.originChainId,
|
|
678
|
+
deposit.outputToken,
|
|
679
|
+
deposit.destinationChainId,
|
|
680
|
+
deposit.quoteBlockNumber
|
|
681
|
+
) &&
|
|
682
|
+
// Cannot slow fill from or to a lite chain.
|
|
683
|
+
!deposit.fromLiteChain &&
|
|
684
|
+
!deposit.toLiteChain
|
|
685
|
+
);
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
const _getFillStatusForDeposit = (deposit: Deposit, queryBlock: number): Promise<FillStatus> => {
|
|
689
|
+
return spokePoolClients[deposit.destinationChainId].relayFillStatus(
|
|
690
|
+
deposit,
|
|
691
|
+
// We can assume that in production
|
|
692
|
+
// the block to query is not one that the spoke pool client
|
|
693
|
+
// hasn't queried. This is because this function will usually be called
|
|
694
|
+
// in production with block ranges that were validated by
|
|
695
|
+
// DataworkerUtils.blockRangesAreInvalidForSpokeClients.
|
|
696
|
+
Math.min(queryBlock, spokePoolClients[deposit.destinationChainId].latestBlockSearched),
|
|
697
|
+
deposit.destinationChainId
|
|
698
|
+
);
|
|
699
|
+
};
|
|
700
|
+
|
|
694
701
|
// Infer chain ID's to load from number of block ranges passed in.
|
|
695
702
|
const allChainIds = blockRangesForChains
|
|
696
703
|
.map((_blockRange, index) => chainIds[index])
|
|
@@ -721,67 +728,13 @@ export class BundleDataClient {
|
|
|
721
728
|
bundleBlockTimestamps = _cachedBundleTimestamps;
|
|
722
729
|
}
|
|
723
730
|
|
|
724
|
-
/** *****************************
|
|
725
|
-
*
|
|
726
|
-
* Handle V3 events
|
|
727
|
-
*
|
|
728
|
-
* *****************************/
|
|
729
|
-
|
|
730
|
-
// The methodology here is roughly as follows
|
|
731
|
-
// - Query all deposits from SpokePoolClients
|
|
732
|
-
// - If deposit is in origin chain block range, add it to bundleDepositsV3
|
|
733
|
-
// - If deposit is expired or from an older bundle, stash it away as a deposit that may require an expired
|
|
734
|
-
// deposit refund.
|
|
735
|
-
// - Query fills from SpokePoolClients
|
|
736
|
-
// - If fill is in destination chain block range, then validate fill
|
|
737
|
-
// - Fill is valid if its RelayData hash is identical to a deposit's relay data hash that we've already seen.
|
|
738
|
-
// If we haven't seen a deposit with a matching hash, then we need to query for an older deposit earlier than
|
|
739
|
-
// the SpokePoolClient's lookback window via queryHistoricalDepositForFill().
|
|
740
|
-
// - If fill is valid, then add it to bundleFillsV3. If it's a slow fill execution, we won't
|
|
741
|
-
// add a relayer refund for it, but all fills accumulate realized LP fees.
|
|
742
|
-
// - If fill replaced a slow fill request, then stash it away as one that potentially created an
|
|
743
|
-
// unexecutable slow fill.
|
|
744
|
-
// - Query slow fills from SpokePoolClients
|
|
745
|
-
// - If slow fill is in destination chain block range, then validate slow fill
|
|
746
|
-
// - Slow fill is valid if its RelayData hash is identical to a deposit's relay data hash that we've already seen,
|
|
747
|
-
// and it does not match with a Fill that we've seen, and its input and output tokens are equivalent,
|
|
748
|
-
// and the deposit that is being slow filled has not expired.
|
|
749
|
-
// - Note that if we haven't can't match the slow fill with a deposit, then we need to query for an older
|
|
750
|
-
// deposit earlier than the SpokePoolClient's lookback window via queryHistoricalDepositForFill().
|
|
751
|
-
// - input and output tokens are considered equivalent if they map to the same L1 token via a PoolRebalanceRoute
|
|
752
|
-
// at the deposit.quoteBlockNumber.
|
|
753
|
-
// - To validate fills that replaced slow fills, we should check that there is no slow fill request in the
|
|
754
|
-
// current destination chain bundle block range with a matching relay hash. Additionally, the
|
|
755
|
-
// fast fill replacing a slow fill must have filled a slow-fill eligible deposit meaning that
|
|
756
|
-
// its input and output tokens are equivalent. We don't need to check that the slow fill was created
|
|
757
|
-
// before the deposit expired by definition because the deposit was fast-filled, meaning that it did not
|
|
758
|
-
// expire.
|
|
759
|
-
// - To validate deposits in the current bundle block range that expired newly in this destination
|
|
760
|
-
// chain's current bundle block range, we only have to check that the deposit was not filled in the current
|
|
761
|
-
// destination chain block range.
|
|
762
|
-
// - To validate deposits from a prior bundle that expired newly, we need to make sure that the deposit
|
|
763
|
-
// was not filled. If we can't find a fill, then we should check its FillStatus on-chain via eth_call.
|
|
764
|
-
// This will return either Unfilled, RequestedSlowFill, or Filled. If the deposit is Filled, then
|
|
765
|
-
// then the fill happened a long time ago and we should do nothing. If the deposit is Unfilled, then
|
|
766
|
-
// we should refund it as an expired deposit. If the deposit is RequestedSlowFill then we need to validate
|
|
767
|
-
// that the deposit is eligible for a slow fill (its input and output tokens are equivalent) and that
|
|
768
|
-
// the slow fill request was not sent in the current destination chain's bundle block range.
|
|
769
|
-
|
|
770
|
-
// Using the above rules, we will create a list of:
|
|
771
|
-
// - deposits in the current bundle
|
|
772
|
-
// - fast fills to refund in the current bundle
|
|
773
|
-
// - fills creating bundle LP fees in the current bundle
|
|
774
|
-
// - slow fills to create for the current bundle
|
|
775
|
-
// - deposits that expired in the current bundle
|
|
776
|
-
|
|
777
731
|
// Use this dictionary to conveniently unite all events with the same relay data hash which will make
|
|
778
732
|
// secondary lookups faster. The goal is to lazily fill up this dictionary with all events in the SpokePool
|
|
779
733
|
// client's in-memory event cache.
|
|
780
734
|
const v3RelayHashes: {
|
|
781
735
|
[relayHash: string]: {
|
|
782
736
|
// Note: Since there are no partial fills in v3, there should only be one fill per relay hash.
|
|
783
|
-
//
|
|
784
|
-
// same spoke pool. Moreover, the SpokePool blocks multiple slow fill requests, so
|
|
737
|
+
// Moreover, the SpokePool blocks multiple slow fill requests, so
|
|
785
738
|
// there should also only be one slow fill request per relay hash.
|
|
786
739
|
deposit?: V3DepositWithBlock;
|
|
787
740
|
fill?: V3FillWithBlock;
|
|
@@ -789,11 +742,10 @@ export class BundleDataClient {
|
|
|
789
742
|
};
|
|
790
743
|
} = {};
|
|
791
744
|
|
|
792
|
-
// Process all deposits first and
|
|
793
|
-
//
|
|
794
|
-
const
|
|
795
|
-
|
|
796
|
-
const olderDepositHashes: Set<string> = new Set<string>();
|
|
745
|
+
// Process all deposits first and populate the v3RelayHashes dictionary. Sort deposits into whether they are
|
|
746
|
+
// in this bundle block range or in a previous bundle block range.
|
|
747
|
+
const bundleDepositHashes: string[] = [];
|
|
748
|
+
const olderDepositHashes: string[] = [];
|
|
797
749
|
|
|
798
750
|
let depositCounter = 0;
|
|
799
751
|
for (const originChainId of allChainIds) {
|
|
@@ -804,24 +756,22 @@ export class BundleDataClient {
|
|
|
804
756
|
if (originChainId === destinationChainId) {
|
|
805
757
|
continue;
|
|
806
758
|
}
|
|
807
|
-
originClient.
|
|
808
|
-
|
|
759
|
+
originClient.getDepositsForDestinationChainWithDuplicates(destinationChainId).forEach((deposit) => {
|
|
760
|
+
// Only evaluate deposits that are in this bundle or in previous bundles. This means we cannot issue fill
|
|
761
|
+
// refunds or slow fills here for deposits that are in future bundles (i.e. "pre-fills"). Instead, we'll
|
|
762
|
+
// evaluate these pre-fills once the deposit is inside the "current" bundle block range.
|
|
763
|
+
if (isZeroValueDeposit(deposit) || deposit.blockNumber > originChainBlockRange[1]) {
|
|
809
764
|
return;
|
|
810
765
|
}
|
|
811
766
|
depositCounter++;
|
|
812
767
|
const relayDataHash = this.getRelayHashFromEvent(deposit);
|
|
813
|
-
if (v3RelayHashes[relayDataHash]) {
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
768
|
+
if (!v3RelayHashes[relayDataHash]) {
|
|
769
|
+
v3RelayHashes[relayDataHash] = {
|
|
770
|
+
deposit: deposit,
|
|
771
|
+
fill: undefined,
|
|
772
|
+
slowFillRequest: undefined,
|
|
773
|
+
};
|
|
817
774
|
}
|
|
818
|
-
// Even if deposit is not in bundle block range, store all deposits we can see in memory in this
|
|
819
|
-
// convenient dictionary.
|
|
820
|
-
v3RelayHashes[relayDataHash] = {
|
|
821
|
-
deposit: deposit,
|
|
822
|
-
fill: undefined,
|
|
823
|
-
slowFillRequest: undefined,
|
|
824
|
-
};
|
|
825
775
|
|
|
826
776
|
// Once we've saved the deposit hash into v3RelayHashes, then we can exit early here if the inputAmount
|
|
827
777
|
// is 0 because there can be no expired amount to refund and no unexecutable slow fill amount to return
|
|
@@ -831,24 +781,11 @@ export class BundleDataClient {
|
|
|
831
781
|
return;
|
|
832
782
|
}
|
|
833
783
|
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
// Note: if the `depositor` field in the expired deposit is an invalid address, e.g. a bytes32 address on an EVM
|
|
837
|
-
// chain, then the deposit refund is invalid and ignored.
|
|
838
|
-
// If deposit is not in the bundle block range, then save it as an older deposit that
|
|
839
|
-
// may have expired.
|
|
840
|
-
if (deposit.blockNumber >= originChainBlockRange[0] && deposit.blockNumber <= originChainBlockRange[1]) {
|
|
841
|
-
// Deposit is a V3 deposit in this origin chain's bundle block range and is not a duplicate.
|
|
784
|
+
if (deposit.blockNumber >= originChainBlockRange[0]) {
|
|
785
|
+
bundleDepositHashes.push(relayDataHash);
|
|
842
786
|
updateBundleDepositsV3(bundleDepositsV3, deposit);
|
|
843
|
-
// We don't check that fillDeadline >= bundleBlockTimestamps[destinationChainId][0] because
|
|
844
|
-
// that would eliminate any deposits in this bundle with a very low fillDeadline like equal to 0
|
|
845
|
-
// for example. Those should be impossible to create but technically should be included in this
|
|
846
|
-
// bundle of refunded deposits.
|
|
847
|
-
if (deposit.fillDeadline < bundleBlockTimestamps[destinationChainId][1]) {
|
|
848
|
-
expiredBundleDepositHashes.add(relayDataHash);
|
|
849
|
-
}
|
|
850
787
|
} else if (deposit.blockNumber < originChainBlockRange[0]) {
|
|
851
|
-
olderDepositHashes.
|
|
788
|
+
olderDepositHashes.push(relayDataHash);
|
|
852
789
|
}
|
|
853
790
|
});
|
|
854
791
|
}
|
|
@@ -885,8 +822,9 @@ export class BundleDataClient {
|
|
|
885
822
|
// request for the deposit, we'd want to see the fill took place.
|
|
886
823
|
.filter((fill) => fill.blockNumber <= destinationChainBlockRange[1] && !isZeroValueDeposit(fill)),
|
|
887
824
|
async (fill) => {
|
|
888
|
-
fillCounter++;
|
|
889
825
|
const relayDataHash = this.getRelayHashFromEvent(fill);
|
|
826
|
+
fillCounter++;
|
|
827
|
+
|
|
890
828
|
if (v3RelayHashes[relayDataHash]) {
|
|
891
829
|
if (!v3RelayHashes[relayDataHash].fill) {
|
|
892
830
|
assert(
|
|
@@ -897,24 +835,10 @@ export class BundleDataClient {
|
|
|
897
835
|
// so this fill is validated.
|
|
898
836
|
v3RelayHashes[relayDataHash].fill = fill;
|
|
899
837
|
if (fill.blockNumber >= destinationChainBlockRange[0]) {
|
|
900
|
-
|
|
901
|
-
fill,
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
allChainIds
|
|
905
|
-
);
|
|
906
|
-
if (!isDefined(fillToRefund)) {
|
|
907
|
-
bundleUnrepayableFillsV3.push(fill);
|
|
908
|
-
// We don't return here yet because we still need to mark unexecutable slow fill leaves
|
|
909
|
-
// or duplicate deposits. However, we won't issue a fast fill refund.
|
|
910
|
-
} else {
|
|
911
|
-
v3RelayHashes[relayDataHash].fill = fillToRefund;
|
|
912
|
-
validatedBundleV3Fills.push({
|
|
913
|
-
...fillToRefund,
|
|
914
|
-
quoteTimestamp: v3RelayHashes[relayDataHash].deposit!.quoteTimestamp, // ! due to assert above
|
|
915
|
-
});
|
|
916
|
-
}
|
|
917
|
-
|
|
838
|
+
validatedBundleV3Fills.push({
|
|
839
|
+
...fill,
|
|
840
|
+
quoteTimestamp: v3RelayHashes[relayDataHash].deposit!.quoteTimestamp, // ! due to assert above
|
|
841
|
+
});
|
|
918
842
|
// If fill replaced a slow fill request, then mark it as one that might have created an
|
|
919
843
|
// unexecutable slow fill. We can't know for sure until we check the slow fill request
|
|
920
844
|
// events.
|
|
@@ -927,17 +851,15 @@ export class BundleDataClient {
|
|
|
927
851
|
fastFillsReplacingSlowFills.push(relayDataHash);
|
|
928
852
|
}
|
|
929
853
|
}
|
|
930
|
-
} else {
|
|
931
|
-
throw new Error("Duplicate fill detected");
|
|
932
854
|
}
|
|
933
855
|
return;
|
|
934
856
|
}
|
|
935
857
|
|
|
936
858
|
// At this point, there is no relay hash dictionary entry for this fill, so we need to
|
|
937
|
-
// instantiate the entry.
|
|
859
|
+
// instantiate the entry.
|
|
938
860
|
v3RelayHashes[relayDataHash] = {
|
|
939
861
|
deposit: undefined,
|
|
940
|
-
fill,
|
|
862
|
+
fill: fill,
|
|
941
863
|
slowFillRequest: undefined,
|
|
942
864
|
};
|
|
943
865
|
|
|
@@ -965,31 +887,16 @@ export class BundleDataClient {
|
|
|
965
887
|
bundleInvalidFillsV3.push(fill);
|
|
966
888
|
} else {
|
|
967
889
|
const matchedDeposit = historicalDeposit.deposit;
|
|
890
|
+
// @dev Since queryHistoricalDepositForFill validates the fill by checking individual
|
|
891
|
+
// object property values against the deposit's, we
|
|
892
|
+
// sanity check it here by comparing the full relay hashes. If there's an error here then the
|
|
893
|
+
// historical deposit query is not working as expected.
|
|
894
|
+
assert(this.getRelayHashFromEvent(matchedDeposit) === relayDataHash, "Relay hashes should match.");
|
|
895
|
+
validatedBundleV3Fills.push({
|
|
896
|
+
...fill,
|
|
897
|
+
quoteTimestamp: matchedDeposit.quoteTimestamp,
|
|
898
|
+
});
|
|
968
899
|
v3RelayHashes[relayDataHash].deposit = matchedDeposit;
|
|
969
|
-
|
|
970
|
-
const fillToRefund = await verifyFillRepayment(
|
|
971
|
-
fill,
|
|
972
|
-
destinationClient.spokePool.provider,
|
|
973
|
-
matchedDeposit,
|
|
974
|
-
allChainIds
|
|
975
|
-
);
|
|
976
|
-
if (!isDefined(fillToRefund)) {
|
|
977
|
-
bundleUnrepayableFillsV3.push(fill);
|
|
978
|
-
// Don't return yet as we still need to mark down any unexecutable slow fill leaves
|
|
979
|
-
// in case this fast fill replaced a slow fill request.
|
|
980
|
-
} else {
|
|
981
|
-
// @dev Since queryHistoricalDepositForFill validates the fill by checking individual
|
|
982
|
-
// object property values against the deposit's, we
|
|
983
|
-
// sanity check it here by comparing the full relay hashes. If there's an error here then the
|
|
984
|
-
// historical deposit query is not working as expected.
|
|
985
|
-
assert(this.getRelayHashFromEvent(matchedDeposit) === relayDataHash, "Relay hashes should match.");
|
|
986
|
-
validatedBundleV3Fills.push({
|
|
987
|
-
...fillToRefund,
|
|
988
|
-
quoteTimestamp: matchedDeposit.quoteTimestamp,
|
|
989
|
-
});
|
|
990
|
-
v3RelayHashes[relayDataHash].fill = fillToRefund;
|
|
991
|
-
}
|
|
992
|
-
|
|
993
900
|
// slow fill requests for deposits from or to lite chains are considered invalid
|
|
994
901
|
if (
|
|
995
902
|
fill.relayExecutionInfo.fillType === FillType.ReplacedSlowFill &&
|
|
@@ -1003,6 +910,8 @@ export class BundleDataClient {
|
|
|
1003
910
|
}
|
|
1004
911
|
);
|
|
1005
912
|
|
|
913
|
+
// Process slow fill requests. One invariant we need to maintain is that we cannot create slow fill requests
|
|
914
|
+
// for deposits that would expire in this bundle.
|
|
1006
915
|
await forEachAsync(
|
|
1007
916
|
destinationClient
|
|
1008
917
|
.getSlowFillRequestsForOriginChain(originChainId)
|
|
@@ -1017,10 +926,7 @@ export class BundleDataClient {
|
|
|
1017
926
|
v3RelayHashes[relayDataHash].slowFillRequest = slowFillRequest;
|
|
1018
927
|
if (v3RelayHashes[relayDataHash].fill) {
|
|
1019
928
|
// If there is a fill matching the relay hash, then this slow fill request can't be used
|
|
1020
|
-
// to create a slow fill for a filled deposit.
|
|
1021
|
-
// slow fill requests must precede fills, so if there is a matching fill for this request's
|
|
1022
|
-
// relay data, then this slow fill will be unexecutable. We therefore must have seen the fill
|
|
1023
|
-
// already if it exists
|
|
929
|
+
// to create a slow fill for a filled deposit.
|
|
1024
930
|
return;
|
|
1025
931
|
}
|
|
1026
932
|
assert(
|
|
@@ -1029,31 +935,13 @@ export class BundleDataClient {
|
|
|
1029
935
|
);
|
|
1030
936
|
// The ! is safe here because we've already checked that the deposit exists in the relay hash dictionary.
|
|
1031
937
|
const matchedDeposit = v3RelayHashes[relayDataHash].deposit!;
|
|
1032
|
-
|
|
1033
|
-
// Input and Output tokens must be equivalent on the deposit for this to be slow filled.
|
|
1034
|
-
if (
|
|
1035
|
-
!this.clients.hubPoolClient.areTokensEquivalent(
|
|
1036
|
-
matchedDeposit.inputToken,
|
|
1037
|
-
matchedDeposit.originChainId,
|
|
1038
|
-
matchedDeposit.outputToken,
|
|
1039
|
-
matchedDeposit.destinationChainId,
|
|
1040
|
-
matchedDeposit.quoteBlockNumber
|
|
1041
|
-
)
|
|
1042
|
-
) {
|
|
1043
|
-
return;
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
// slow fill requests for deposits from or to lite chains are considered invalid
|
|
1047
|
-
if (
|
|
1048
|
-
v3RelayHashes[relayDataHash].deposit?.fromLiteChain ||
|
|
1049
|
-
v3RelayHashes[relayDataHash].deposit?.toLiteChain
|
|
1050
|
-
) {
|
|
938
|
+
if (!_canCreateSlowFillLeaf(matchedDeposit)) {
|
|
1051
939
|
return;
|
|
1052
940
|
}
|
|
1053
941
|
|
|
1054
942
|
// If there is no fill matching the relay hash, then this might be a valid slow fill request
|
|
1055
943
|
// that we should produce a slow fill leaf for. Check if the slow fill request is in the
|
|
1056
|
-
// destination chain block range
|
|
944
|
+
// destination chain block range.
|
|
1057
945
|
if (
|
|
1058
946
|
slowFillRequest.blockNumber >= destinationChainBlockRange[0] &&
|
|
1059
947
|
// Deposit must not have expired in this bundle.
|
|
@@ -1063,8 +951,6 @@ export class BundleDataClient {
|
|
|
1063
951
|
// so this slow fill request relay data is correct.
|
|
1064
952
|
validatedBundleSlowFills.push(matchedDeposit);
|
|
1065
953
|
}
|
|
1066
|
-
} else {
|
|
1067
|
-
throw new Error("Duplicate slow fill request detected.");
|
|
1068
954
|
}
|
|
1069
955
|
return;
|
|
1070
956
|
}
|
|
@@ -1106,30 +992,13 @@ export class BundleDataClient {
|
|
|
1106
992
|
this.getRelayHashFromEvent(matchedDeposit) === relayDataHash,
|
|
1107
993
|
"Deposit relay hashes should match."
|
|
1108
994
|
);
|
|
1109
|
-
|
|
1110
|
-
// slow fill requests for deposits from or to lite chains are considered invalid
|
|
1111
|
-
if (matchedDeposit.fromLiteChain || matchedDeposit.toLiteChain) {
|
|
1112
|
-
return;
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
995
|
v3RelayHashes[relayDataHash].deposit = matchedDeposit;
|
|
1116
996
|
|
|
1117
|
-
// Note: we don't need to query for a historical fill at this point because a fill
|
|
1118
|
-
// cannot precede a slow fill request and if the fill came after the slow fill request,
|
|
1119
|
-
// we would have seen it already because we would have processed it in the loop above.
|
|
1120
997
|
if (
|
|
1121
|
-
|
|
1122
|
-
!this.clients.hubPoolClient.areTokensEquivalent(
|
|
1123
|
-
matchedDeposit.inputToken,
|
|
1124
|
-
matchedDeposit.originChainId,
|
|
1125
|
-
matchedDeposit.outputToken,
|
|
1126
|
-
matchedDeposit.destinationChainId,
|
|
1127
|
-
matchedDeposit.quoteBlockNumber
|
|
1128
|
-
) ||
|
|
998
|
+
!_canCreateSlowFillLeaf(matchedDeposit) ||
|
|
1129
999
|
// Deposit must not have expired in this bundle.
|
|
1130
1000
|
slowFillRequest.fillDeadline < bundleBlockTimestamps[destinationChainId][1]
|
|
1131
1001
|
) {
|
|
1132
|
-
// TODO: Invalid slow fill request. Maybe worth logging.
|
|
1133
1002
|
return;
|
|
1134
1003
|
}
|
|
1135
1004
|
validatedBundleSlowFills.push(matchedDeposit);
|
|
@@ -1137,6 +1006,113 @@ export class BundleDataClient {
|
|
|
1137
1006
|
}
|
|
1138
1007
|
);
|
|
1139
1008
|
|
|
1009
|
+
// Deposits can be submitted an arbitrary amount of time after matching fills and slow fill requests.
|
|
1010
|
+
// Therefore, let's go through each deposit in this bundle again and check a few things in order:
|
|
1011
|
+
// - Has the deposit been filled ? If so, then we need to issue a relayer refund for
|
|
1012
|
+
// this "pre-fill" if the fill took place in a previous bundle.
|
|
1013
|
+
// - Or, has the deposit expired in this bundle? If so, then we need to issue an expiry refund.
|
|
1014
|
+
// - And finally, has the deposit been slow filled? If so, then we need to issue a slow fill leaf
|
|
1015
|
+
// for this "pre-slow-fill-request" if this request took place in a previous bundle.
|
|
1016
|
+
const originBlockRange = getBlockRangeForChain(blockRangesForChains, originChainId, chainIds);
|
|
1017
|
+
|
|
1018
|
+
await mapAsync(
|
|
1019
|
+
bundleDepositHashes.filter((depositHash) => {
|
|
1020
|
+
const { deposit } = v3RelayHashes[depositHash];
|
|
1021
|
+
return (
|
|
1022
|
+
deposit &&
|
|
1023
|
+
deposit.originChainId === originChainId &&
|
|
1024
|
+
deposit.destinationChainId === destinationChainId &&
|
|
1025
|
+
deposit.blockNumber >= originBlockRange[0] &&
|
|
1026
|
+
deposit.blockNumber <= originBlockRange[1] &&
|
|
1027
|
+
!isZeroValueDeposit(deposit)
|
|
1028
|
+
);
|
|
1029
|
+
}),
|
|
1030
|
+
async (depositHash) => {
|
|
1031
|
+
const { deposit, fill, slowFillRequest } = v3RelayHashes[depositHash];
|
|
1032
|
+
if (!deposit) throw new Error("Deposit should exist in relay hash dictionary.");
|
|
1033
|
+
|
|
1034
|
+
// We are willing to refund a pre-fill multiple times for each duplicate deposit.
|
|
1035
|
+
// This is because a duplicate deposit for a pre-fill cannot get
|
|
1036
|
+
// refunded to the depositor anymore because its fill status on-chain has changed to Filled. Therefore
|
|
1037
|
+
// any duplicate deposits result in a net loss of funds for the depositor and effectively pay out
|
|
1038
|
+
// the pre-filler.
|
|
1039
|
+
|
|
1040
|
+
// If fill exists in memory, then the only case in which we need to create a refund is if the
|
|
1041
|
+
// the fill occurred in a previous bundle. There are no expiry refunds for filled deposits.
|
|
1042
|
+
if (fill) {
|
|
1043
|
+
if (!isSlowFill(fill) && fill.blockNumber < destinationChainBlockRange[0]) {
|
|
1044
|
+
// If fill is in the current bundle then we can assume there is already a refund for it, so only
|
|
1045
|
+
// include this pre fill if the fill is in an older bundle. If fill is after this current bundle, then
|
|
1046
|
+
// we won't consider it, following the previous treatment of fills after the bundle block range.
|
|
1047
|
+
validatedBundleV3Fills.push({
|
|
1048
|
+
...fill,
|
|
1049
|
+
quoteTimestamp: deposit.quoteTimestamp,
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
// If a slow fill request exists in memory, then we know the deposit has not been filled because fills
|
|
1056
|
+
// must follow slow fill requests and we would have seen the fill already if it existed. Therefore,
|
|
1057
|
+
// we can conclude that either the deposit has expired and we need to create a deposit expiry refund, or
|
|
1058
|
+
// we need to create a slow fill leaf for the deposit. The latter should only happen if the slow fill request
|
|
1059
|
+
// took place in a prior bundle otherwise we would have already created a slow fill leaf for it.
|
|
1060
|
+
if (slowFillRequest) {
|
|
1061
|
+
if (deposit.fillDeadline < bundleBlockTimestamps[destinationChainId][1]) {
|
|
1062
|
+
updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
|
|
1063
|
+
} else if (
|
|
1064
|
+
_canCreateSlowFillLeaf(deposit) &&
|
|
1065
|
+
slowFillRequest.blockNumber < destinationChainBlockRange[0]
|
|
1066
|
+
) {
|
|
1067
|
+
validatedBundleSlowFills.push(deposit);
|
|
1068
|
+
}
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
// So at this point in the code, there is no fill or slow fill request in memory for this deposit.
|
|
1073
|
+
// We need to check its fill status on-chain to figure out whether to issue a refund or a slow fill leaf.
|
|
1074
|
+
// We can assume at this point that all fills or slow fill requests, if found, were in previous bundles
|
|
1075
|
+
// because the spoke pool client lookback would have returned this entire bundle of events and stored
|
|
1076
|
+
// them into the relay hash dictionary.
|
|
1077
|
+
const fillStatus = await _getFillStatusForDeposit(deposit, destinationChainBlockRange[1]);
|
|
1078
|
+
|
|
1079
|
+
// If deposit was filled, then we need to issue a refund for it.
|
|
1080
|
+
if (fillStatus === FillStatus.Filled) {
|
|
1081
|
+
// We need to find the fill event to issue a refund to the right relayer and repayment chain,
|
|
1082
|
+
// or msg.sender if relayer address is invalid for the repayment chain.
|
|
1083
|
+
const prefill = (await findFillEvent(
|
|
1084
|
+
destinationClient.spokePool,
|
|
1085
|
+
deposit,
|
|
1086
|
+
destinationClient.deploymentBlock,
|
|
1087
|
+
destinationClient.latestBlockSearched
|
|
1088
|
+
)) as unknown as FillWithBlock;
|
|
1089
|
+
if (!isSlowFill(prefill)) {
|
|
1090
|
+
validatedBundleV3Fills.push({
|
|
1091
|
+
...prefill,
|
|
1092
|
+
quoteTimestamp: deposit.quoteTimestamp,
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
// If deposit is not filled and its newly expired, we can create a deposit refund for it.
|
|
1097
|
+
// We don't check that fillDeadline >= bundleBlockTimestamps[destinationChainId][0] because
|
|
1098
|
+
// that would eliminate any deposits in this bundle with a very low fillDeadline like equal to 0
|
|
1099
|
+
// for example. Those should be included in this bundle of refunded deposits.
|
|
1100
|
+
else if (deposit.fillDeadline < bundleBlockTimestamps[destinationChainId][1]) {
|
|
1101
|
+
updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
|
|
1102
|
+
}
|
|
1103
|
+
// If slow fill requested, then issue a slow fill leaf for the deposit.
|
|
1104
|
+
else if (fillStatus === FillStatus.RequestedSlowFill) {
|
|
1105
|
+
// Input and Output tokens must be equivalent on the deposit for this to be slow filled.
|
|
1106
|
+
// Slow fill requests for deposits from or to lite chains are considered invalid
|
|
1107
|
+
if (_canCreateSlowFillLeaf(deposit)) {
|
|
1108
|
+
// If deposit newly expired, then we can't create a slow fill leaf for it but we can
|
|
1109
|
+
// create a deposit refund for it.
|
|
1110
|
+
validatedBundleSlowFills.push(deposit);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
);
|
|
1115
|
+
|
|
1140
1116
|
// For all fills that came after a slow fill request, we can now check if the slow fill request
|
|
1141
1117
|
// was a valid one and whether it was created in a previous bundle. If so, then it created a slow fill
|
|
1142
1118
|
// leaf that is now unexecutable.
|
|
@@ -1182,23 +1158,10 @@ export class BundleDataClient {
|
|
|
1182
1158
|
});
|
|
1183
1159
|
start = performance.now();
|
|
1184
1160
|
|
|
1185
|
-
// Go through expired deposits in this bundle and now prune those that we have seen a fill for to construct
|
|
1186
|
-
// the list of expired deposits we need to refund in this bundle.
|
|
1187
|
-
expiredBundleDepositHashes.forEach((relayDataHash) => {
|
|
1188
|
-
const { deposit, fill } = v3RelayHashes[relayDataHash];
|
|
1189
|
-
assert(isDefined(deposit), "Deposit should exist in relay hash dictionary.");
|
|
1190
|
-
if (
|
|
1191
|
-
!fill &&
|
|
1192
|
-
isDefined(deposit) // Needed for TSC - we check this above.
|
|
1193
|
-
) {
|
|
1194
|
-
updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
|
|
1195
|
-
}
|
|
1196
|
-
});
|
|
1197
|
-
|
|
1198
1161
|
// For all deposits older than this bundle, we need to check if they expired in this bundle and if they did,
|
|
1199
1162
|
// whether there was a slow fill created for it in a previous bundle that is now unexecutable and replaced
|
|
1200
1163
|
// by a new expired deposit refund.
|
|
1201
|
-
await forEachAsync(
|
|
1164
|
+
await forEachAsync(olderDepositHashes, async (relayDataHash) => {
|
|
1202
1165
|
const { deposit, slowFillRequest, fill } = v3RelayHashes[relayDataHash];
|
|
1203
1166
|
assert(isDefined(deposit), "Deposit should exist in relay hash dictionary.");
|
|
1204
1167
|
const { destinationChainId } = deposit!;
|
|
@@ -1217,16 +1180,7 @@ export class BundleDataClient {
|
|
|
1217
1180
|
) {
|
|
1218
1181
|
// If we haven't seen a fill matching this deposit, then we need to rule out that it was filled a long time ago
|
|
1219
1182
|
// by checkings its on-chain fill status.
|
|
1220
|
-
const fillStatus = await
|
|
1221
|
-
deposit,
|
|
1222
|
-
// We can assume that in production
|
|
1223
|
-
// the block ranges passed into this function would never contain blocks where the spoke pool client
|
|
1224
|
-
// hasn't queried. This is because this function will usually be called
|
|
1225
|
-
// in production with block ranges that were validated by
|
|
1226
|
-
// DataworkerUtils.blockRangesAreInvalidForSpokeClients
|
|
1227
|
-
Math.min(destinationBlockRange[1], spokePoolClients[destinationChainId].latestBlockSearched),
|
|
1228
|
-
destinationChainId
|
|
1229
|
-
);
|
|
1183
|
+
const fillStatus = await _getFillStatusForDeposit(deposit, destinationBlockRange[1]);
|
|
1230
1184
|
|
|
1231
1185
|
// If there is no matching fill and the deposit expired in this bundle and the fill status on-chain is not
|
|
1232
1186
|
// Filled, then we can to refund it as an expired deposit.
|
|
@@ -1325,7 +1279,7 @@ export class BundleDataClient {
|
|
|
1325
1279
|
chainIds,
|
|
1326
1280
|
associatedDeposit!.fromLiteChain
|
|
1327
1281
|
);
|
|
1328
|
-
updateBundleFillsV3(bundleFillsV3, fill, realizedLpFeePct, chainToSendRefundTo, repaymentToken
|
|
1282
|
+
updateBundleFillsV3(bundleFillsV3, fill, realizedLpFeePct, chainToSendRefundTo, repaymentToken);
|
|
1329
1283
|
});
|
|
1330
1284
|
v3SlowFillLpFees.forEach(({ realizedLpFeePct: lpFeePct }, idx) => {
|
|
1331
1285
|
const deposit = validatedBundleSlowFills[idx];
|
|
@@ -1339,6 +1293,7 @@ export class BundleDataClient {
|
|
|
1339
1293
|
const v3SpokeEventsReadable = prettyPrintV3SpokePoolEvents(
|
|
1340
1294
|
bundleDepositsV3,
|
|
1341
1295
|
bundleFillsV3,
|
|
1296
|
+
bundleInvalidFillsV3,
|
|
1342
1297
|
bundleSlowFillsV3,
|
|
1343
1298
|
expiredDepositsToRefundV3,
|
|
1344
1299
|
unexecutableSlowFills
|
|
@@ -1353,15 +1308,6 @@ export class BundleDataClient {
|
|
|
1353
1308
|
});
|
|
1354
1309
|
}
|
|
1355
1310
|
|
|
1356
|
-
if (bundleUnrepayableFillsV3.length > 0) {
|
|
1357
|
-
this.logger.debug({
|
|
1358
|
-
at: "BundleDataClient#loadData",
|
|
1359
|
-
message: "Finished loading V3 spoke pool data and found some unrepayable V3 fills in range",
|
|
1360
|
-
blockRangesForChains,
|
|
1361
|
-
bundleUnrepayableFillsV3,
|
|
1362
|
-
});
|
|
1363
|
-
}
|
|
1364
|
-
|
|
1365
1311
|
this.logger.debug({
|
|
1366
1312
|
at: "BundleDataClient#loadDataFromScratch",
|
|
1367
1313
|
message: `Computed bundle data in ${Math.round(performance.now() - start) / 1000}s.`,
|
|
@@ -1382,11 +1328,7 @@ export class BundleDataClient {
|
|
|
1382
1328
|
// spoke pool contract. However, this internal function is used to uniquely identify a bridging event
|
|
1383
1329
|
// for speed since its easier to build a string from the event data than to hash it.
|
|
1384
1330
|
private getRelayHashFromEvent(event: V3DepositWithBlock | V3FillWithBlock | SlowFillRequestWithBlock): string {
|
|
1385
|
-
return `${event.depositor}-${event.recipient}-${event.exclusiveRelayer}-${event.inputToken}-${event.outputToken}-${
|
|
1386
|
-
event.inputAmount
|
|
1387
|
-
}-${event.outputAmount}-${event.originChainId}-${event.depositId.toString()}-${event.fillDeadline}-${
|
|
1388
|
-
event.exclusivityDeadline
|
|
1389
|
-
}-${event.message}-${event.destinationChainId}`;
|
|
1331
|
+
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}`;
|
|
1390
1332
|
}
|
|
1391
1333
|
|
|
1392
1334
|
async getBundleBlockTimestamps(
|
|
@@ -1414,26 +1356,13 @@ export class BundleDataClient {
|
|
|
1414
1356
|
// will usually be called in production with block ranges that were validated by
|
|
1415
1357
|
// DataworkerUtils.blockRangesAreInvalidForSpokeClients.
|
|
1416
1358
|
const startBlockForChain = Math.min(_startBlockForChain, spokePoolClient.latestBlockSearched);
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
// in exactly one bundle, therefore we must make sure that the bundle block timestamp for one bundle's
|
|
1420
|
-
// end block is exactly equal to the bundle block timestamp for the next bundle's start block. This way
|
|
1421
|
-
// there are no gaps in block timestamps between bundles.
|
|
1422
|
-
const endBlockForChain = Math.min(_endBlockForChain + 1, spokePoolClient.latestBlockSearched);
|
|
1423
|
-
const [startTime, _endTime] = [
|
|
1359
|
+
const endBlockForChain = Math.min(_endBlockForChain, spokePoolClient.latestBlockSearched);
|
|
1360
|
+
const [startTime, endTime] = [
|
|
1424
1361
|
await spokePoolClient.getTimestampForBlock(startBlockForChain),
|
|
1425
1362
|
await spokePoolClient.getTimestampForBlock(endBlockForChain),
|
|
1426
1363
|
];
|
|
1427
|
-
// @dev similar to reasoning above to ensure no gaps between bundle block range timestamps and also
|
|
1428
|
-
// no overlap, subtract 1 from the end time.
|
|
1429
|
-
const endBlockDelta = endBlockForChain > startBlockForChain ? 1 : 0;
|
|
1430
|
-
const endTime = Math.max(0, _endTime - endBlockDelta);
|
|
1431
|
-
|
|
1432
1364
|
// Sanity checks:
|
|
1433
|
-
assert(
|
|
1434
|
-
endTime >= startTime,
|
|
1435
|
-
`End time for block ${endBlockForChain} should be greater than start time for block ${startBlockForChain}: ${endTime} >= ${startTime}.`
|
|
1436
|
-
);
|
|
1365
|
+
assert(endTime >= startTime, "End time should be greater than start time.");
|
|
1437
1366
|
assert(
|
|
1438
1367
|
startBlockForChain === 0 || startTime > 0,
|
|
1439
1368
|
"Start timestamp must be greater than 0 if the start block is greater than 0."
|