@across-protocol/sdk 4.1.9 → 4.1.10-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 +2 -6
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.js +94 -114
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.js.map +1 -1
- package/dist/cjs/clients/HubPoolClient.d.ts +1 -0
- package/dist/cjs/clients/HubPoolClient.js +10 -3
- package/dist/cjs/clients/HubPoolClient.js.map +1 -1
- package/dist/cjs/clients/SpokePoolClient.d.ts +3 -1
- package/dist/cjs/clients/SpokePoolClient.js +61 -38
- package/dist/cjs/clients/SpokePoolClient.js.map +1 -1
- package/dist/cjs/interfaces/HubPool.d.ts +4 -1
- package/dist/cjs/relayFeeCalculator/chain-queries/baseQuery.js +2 -3
- package/dist/cjs/relayFeeCalculator/chain-queries/baseQuery.js.map +1 -1
- package/dist/cjs/utils/CachingUtils.js +3 -3
- package/dist/cjs/utils/CachingUtils.js.map +1 -1
- package/dist/cjs/utils/EventUtils.d.ts +1 -0
- package/dist/cjs/utils/EventUtils.js +5 -1
- package/dist/cjs/utils/EventUtils.js.map +1 -1
- package/dist/cjs/utils/LogUtils.d.ts +1 -0
- package/dist/cjs/utils/LogUtils.js +7 -1
- package/dist/cjs/utils/LogUtils.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/BundleDataClient.d.ts +2 -6
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js +89 -110
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js.map +1 -1
- package/dist/esm/clients/HubPoolClient.d.ts +1 -0
- package/dist/esm/clients/HubPoolClient.js +10 -3
- package/dist/esm/clients/HubPoolClient.js.map +1 -1
- package/dist/esm/clients/SpokePoolClient.d.ts +5 -3
- package/dist/esm/clients/SpokePoolClient.js +71 -44
- package/dist/esm/clients/SpokePoolClient.js.map +1 -1
- package/dist/esm/interfaces/HubPool.d.ts +4 -1
- package/dist/esm/relayFeeCalculator/chain-queries/baseQuery.js +1 -2
- package/dist/esm/relayFeeCalculator/chain-queries/baseQuery.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/EventUtils.d.ts +1 -0
- package/dist/esm/utils/EventUtils.js +3 -0
- package/dist/esm/utils/EventUtils.js.map +1 -1
- package/dist/esm/utils/LogUtils.d.ts +7 -0
- package/dist/esm/utils/LogUtils.js +11 -0
- package/dist/esm/utils/LogUtils.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/types/clients/BundleDataClient/BundleDataClient.d.ts +2 -6
- package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts.map +1 -1
- package/dist/types/clients/HubPoolClient.d.ts +1 -0
- package/dist/types/clients/HubPoolClient.d.ts.map +1 -1
- package/dist/types/clients/SpokePoolClient.d.ts +5 -3
- package/dist/types/clients/SpokePoolClient.d.ts.map +1 -1
- package/dist/types/interfaces/HubPool.d.ts +4 -1
- package/dist/types/interfaces/HubPool.d.ts.map +1 -1
- package/dist/types/relayFeeCalculator/chain-queries/baseQuery.d.ts.map +1 -1
- package/dist/types/utils/EventUtils.d.ts +1 -0
- package/dist/types/utils/EventUtils.d.ts.map +1 -1
- package/dist/types/utils/LogUtils.d.ts +7 -0
- package/dist/types/utils/LogUtils.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/package.json +1 -1
- package/src/clients/BundleDataClient/BundleDataClient.ts +127 -153
- package/src/clients/HubPoolClient.ts +17 -11
- package/src/clients/SpokePoolClient.ts +44 -10
- package/src/interfaces/HubPool.ts +4 -1
- package/src/relayFeeCalculator/chain-queries/baseQuery.ts +1 -1
- package/src/utils/CachingUtils.ts +1 -1
- package/src/utils/EventUtils.ts +4 -0
- package/src/utils/LogUtils.ts +12 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import assert from "assert";
|
|
2
1
|
import _ from "lodash";
|
|
3
2
|
import {
|
|
4
3
|
ProposedRootBundle,
|
|
@@ -19,12 +18,13 @@ import {
|
|
|
19
18
|
Deposit,
|
|
20
19
|
DepositWithBlock,
|
|
21
20
|
} from "../../interfaces";
|
|
22
|
-
import {
|
|
21
|
+
import { SpokePoolClient } from "..";
|
|
23
22
|
import {
|
|
24
23
|
BigNumber,
|
|
25
24
|
bnZero,
|
|
26
25
|
queryHistoricalDepositForFill,
|
|
27
26
|
assign,
|
|
27
|
+
assert,
|
|
28
28
|
fixedPointAdjustment,
|
|
29
29
|
isDefined,
|
|
30
30
|
toBN,
|
|
@@ -41,10 +41,10 @@ import {
|
|
|
41
41
|
isZeroValueFillOrSlowFillRequest,
|
|
42
42
|
chainIsEvm,
|
|
43
43
|
isValidEvmAddress,
|
|
44
|
+
duplicateEvent,
|
|
44
45
|
} from "../../utils";
|
|
45
46
|
import winston from "winston";
|
|
46
47
|
import {
|
|
47
|
-
_buildPoolRebalanceRoot,
|
|
48
48
|
BundleData,
|
|
49
49
|
BundleDataSS,
|
|
50
50
|
getEndBlockBuffers,
|
|
@@ -52,7 +52,6 @@ import {
|
|
|
52
52
|
getRefundsFromBundle,
|
|
53
53
|
getWidestPossibleExpectedBlockRange,
|
|
54
54
|
isChainDisabled,
|
|
55
|
-
PoolRebalanceRoot,
|
|
56
55
|
prettyPrintV3SpokePoolEvents,
|
|
57
56
|
V3DepositWithBlock,
|
|
58
57
|
V3FillWithBlock,
|
|
@@ -240,13 +239,7 @@ export class BundleDataClient {
|
|
|
240
239
|
);
|
|
241
240
|
}
|
|
242
241
|
|
|
243
|
-
private async
|
|
244
|
-
blockRangesForChains: number[][]
|
|
245
|
-
): Promise<LoadDataReturnValue | undefined> {
|
|
246
|
-
if (!isDefined(this.clients?.arweaveClient)) {
|
|
247
|
-
return undefined;
|
|
248
|
-
}
|
|
249
|
-
const start = performance.now();
|
|
242
|
+
private async getBundleDataFromArweave(blockRangesForChains: number[][]) {
|
|
250
243
|
const persistedData = await this.clients.arweaveClient.getByTopic(
|
|
251
244
|
this.getArweaveBundleDataClientKey(blockRangesForChains),
|
|
252
245
|
BundleDataSS
|
|
@@ -256,6 +249,20 @@ export class BundleDataClient {
|
|
|
256
249
|
if (!isDefined(persistedData) || persistedData.length < 1) {
|
|
257
250
|
return undefined;
|
|
258
251
|
}
|
|
252
|
+
return persistedData;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private async loadPersistedDataFromArweave(
|
|
256
|
+
blockRangesForChains: number[][]
|
|
257
|
+
): Promise<LoadDataReturnValue | undefined> {
|
|
258
|
+
if (!isDefined(this.clients?.arweaveClient)) {
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
const start = performance.now();
|
|
262
|
+
const persistedData = await this.getBundleDataFromArweave(blockRangesForChains);
|
|
263
|
+
if (!isDefined(persistedData)) {
|
|
264
|
+
return undefined;
|
|
265
|
+
}
|
|
259
266
|
|
|
260
267
|
// A converter function to account for the fact that our SuperStruct schema does not support numeric
|
|
261
268
|
// keys in records. Fundamentally, this is a limitation of superstruct itself.
|
|
@@ -392,7 +399,7 @@ export class BundleDataClient {
|
|
|
392
399
|
const fill = await verifyFillRepayment(
|
|
393
400
|
_fill,
|
|
394
401
|
this.spokePoolClients[_fill.destinationChainId].spokePool.provider,
|
|
395
|
-
matchingDeposit
|
|
402
|
+
matchingDeposit!,
|
|
396
403
|
this.clients.hubPoolClient
|
|
397
404
|
);
|
|
398
405
|
if (!isDefined(fill)) {
|
|
@@ -403,7 +410,7 @@ export class BundleDataClient {
|
|
|
403
410
|
this.clients.hubPoolClient,
|
|
404
411
|
blockRanges,
|
|
405
412
|
this.chainIdListForBundleEvaluationBlockNumbers,
|
|
406
|
-
matchingDeposit.
|
|
413
|
+
matchingDeposit!.fromLiteChain // Use ! because we've already asserted that matchingDeposit is defined.
|
|
407
414
|
);
|
|
408
415
|
// Assume that lp fees are 0 for the sake of speed. In the future we could batch compute
|
|
409
416
|
// these or make hardcoded assumptions based on the origin-repayment chain direction. This might result
|
|
@@ -431,52 +438,6 @@ export class BundleDataClient {
|
|
|
431
438
|
}, toBN(0));
|
|
432
439
|
}
|
|
433
440
|
|
|
434
|
-
private async getLatestProposedBundleData(): Promise<{ bundleData: LoadDataReturnValue; blockRanges: number[][] }> {
|
|
435
|
-
const hubPoolClient = this.clients.hubPoolClient;
|
|
436
|
-
// Determine which bundle we should fetch from arweave, either the pending bundle or the latest
|
|
437
|
-
// executed one. Both should have arweave data but if for some reason the arweave data is missing,
|
|
438
|
-
// this function will have to compute the bundle data from scratch which will be slow. We have to fallback
|
|
439
|
-
// to computing the bundle from scratch since this function needs to return the full bundle data so that
|
|
440
|
-
// it can be used to get the running balance proposed using its data.
|
|
441
|
-
const bundleBlockRanges = getImpliedBundleBlockRanges(
|
|
442
|
-
hubPoolClient,
|
|
443
|
-
this.clients.configStoreClient,
|
|
444
|
-
hubPoolClient.hasPendingProposal()
|
|
445
|
-
? hubPoolClient.getLatestProposedRootBundle()
|
|
446
|
-
: hubPoolClient.getLatestFullyExecutedRootBundle(hubPoolClient.latestBlockSearched)! // ! because we know there is a bundle
|
|
447
|
-
);
|
|
448
|
-
return {
|
|
449
|
-
blockRanges: bundleBlockRanges,
|
|
450
|
-
bundleData: await this.loadData(
|
|
451
|
-
bundleBlockRanges,
|
|
452
|
-
this.spokePoolClients,
|
|
453
|
-
true // this bundle data should have been published to arweave
|
|
454
|
-
),
|
|
455
|
-
};
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
async getLatestPoolRebalanceRoot(): Promise<{ root: PoolRebalanceRoot; blockRanges: number[][] }> {
|
|
459
|
-
const { bundleData, blockRanges } = await this.getLatestProposedBundleData();
|
|
460
|
-
const hubPoolClient = this.clients.hubPoolClient;
|
|
461
|
-
const root = _buildPoolRebalanceRoot(
|
|
462
|
-
hubPoolClient.latestBlockSearched,
|
|
463
|
-
blockRanges[0][1],
|
|
464
|
-
bundleData.bundleDepositsV3,
|
|
465
|
-
bundleData.bundleFillsV3,
|
|
466
|
-
bundleData.bundleSlowFillsV3,
|
|
467
|
-
bundleData.unexecutableSlowFills,
|
|
468
|
-
bundleData.expiredDepositsToRefundV3,
|
|
469
|
-
{
|
|
470
|
-
hubPoolClient,
|
|
471
|
-
configStoreClient: hubPoolClient.configStoreClient as AcrossConfigStoreClient,
|
|
472
|
-
}
|
|
473
|
-
);
|
|
474
|
-
return {
|
|
475
|
-
root,
|
|
476
|
-
blockRanges,
|
|
477
|
-
};
|
|
478
|
-
}
|
|
479
|
-
|
|
480
441
|
// @dev This function should probably be moved to the InventoryClient since it bypasses loadData completely now.
|
|
481
442
|
// Return refunds from the next valid bundle. This will contain any refunds that have been sent but are not included
|
|
482
443
|
// in a valid bundle with all of its leaves executed. This contains refunds from:
|
|
@@ -850,23 +811,27 @@ export class BundleDataClient {
|
|
|
850
811
|
slowFillRequest: undefined,
|
|
851
812
|
};
|
|
852
813
|
} else {
|
|
853
|
-
|
|
854
|
-
assert(isDefined(deposits) && deposits.length > 0, "Deposit should exist in relay hash dictionary.");
|
|
855
|
-
deposits.push(deposit);
|
|
814
|
+
v3RelayHashes[relayDataHash].deposits!.push(deposit);
|
|
856
815
|
}
|
|
857
816
|
|
|
858
817
|
// Account for duplicate deposits by concatenating the relayDataHash with the count of the number of times
|
|
859
818
|
// we have seen it so far.
|
|
860
|
-
const
|
|
861
|
-
assert(isDefined(deposits) && deposits.length > 0, "Deposit should exist in relay hash dictionary.");
|
|
862
|
-
const newBundleDepositHash = `${relayDataHash}@${deposits.length - 1}`;
|
|
819
|
+
const newBundleDepositHash = `${relayDataHash}@${v3RelayHashes[relayDataHash].deposits!.length - 1}`;
|
|
863
820
|
const decodedBundleDepositHash = decodeBundleDepositHash(newBundleDepositHash);
|
|
864
821
|
assert(
|
|
865
822
|
decodedBundleDepositHash.relayDataHash === relayDataHash &&
|
|
866
|
-
decodedBundleDepositHash.index === deposits
|
|
823
|
+
decodedBundleDepositHash.index === v3RelayHashes[relayDataHash].deposits!.length - 1,
|
|
867
824
|
"Not using correct bundle deposit hash key"
|
|
868
825
|
);
|
|
869
826
|
if (deposit.blockNumber >= originChainBlockRange[0]) {
|
|
827
|
+
if (bundleDepositsV3?.[originChainId]?.[deposit.inputToken]?.find((d) => duplicateEvent(deposit, d))) {
|
|
828
|
+
this.logger.debug({
|
|
829
|
+
at: "BundleDataClient#loadData",
|
|
830
|
+
message: "Duplicate deposit detected",
|
|
831
|
+
deposit,
|
|
832
|
+
});
|
|
833
|
+
throw new Error("Duplicate deposit detected");
|
|
834
|
+
}
|
|
870
835
|
bundleDepositHashes.push(newBundleDepositHash);
|
|
871
836
|
updateBundleDepositsV3(bundleDepositsV3, deposit);
|
|
872
837
|
} else if (deposit.blockNumber < originChainBlockRange[0]) {
|
|
@@ -924,62 +889,69 @@ export class BundleDataClient {
|
|
|
924
889
|
fillCounter++;
|
|
925
890
|
const relayDataHash = getRelayEventKey(fill);
|
|
926
891
|
if (v3RelayHashes[relayDataHash]) {
|
|
927
|
-
if (
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
fill,
|
|
934
|
-
destinationClient.spokePool.provider,
|
|
935
|
-
deposits[0],
|
|
936
|
-
this.clients.hubPoolClient
|
|
937
|
-
);
|
|
938
|
-
if (!isDefined(fillToRefund)) {
|
|
939
|
-
bundleUnrepayableFillsV3.push(fill);
|
|
940
|
-
// We don't return here yet because we still need to mark unexecutable slow fill leaves
|
|
941
|
-
// or duplicate deposits. However, we won't issue a fast fill refund.
|
|
942
|
-
} else {
|
|
943
|
-
v3RelayHashes[relayDataHash].fill = fillToRefund;
|
|
944
|
-
validatedBundleV3Fills.push({
|
|
945
|
-
...fillToRefund,
|
|
946
|
-
quoteTimestamp: deposits[0].quoteTimestamp,
|
|
947
|
-
});
|
|
948
|
-
|
|
949
|
-
// Now that we know this deposit has been filled on-chain, identify any duplicate deposits
|
|
950
|
-
// sent for this fill and refund them to the filler, because this value would not be paid out
|
|
951
|
-
// otherwise. These deposits can no longer expire and get refunded as an expired deposit,
|
|
952
|
-
// and they won't trigger a pre-fill refund because the fill is in this bundle.
|
|
953
|
-
// Pre-fill refunds only happen when deposits are sent in this bundle and the
|
|
954
|
-
// fill is from a prior bundle. Paying out the filler keeps the behavior consistent for how
|
|
955
|
-
// we deal with duplicate deposits regardless if the deposit is matched with a pre-fill or
|
|
956
|
-
// a current bundle fill.
|
|
957
|
-
const duplicateDeposits = deposits.slice(1);
|
|
958
|
-
duplicateDeposits.forEach((duplicateDeposit) => {
|
|
959
|
-
if (isSlowFill(fill)) {
|
|
960
|
-
updateExpiredDepositsV3(expiredDepositsToRefundV3, duplicateDeposit);
|
|
961
|
-
} else {
|
|
962
|
-
validatedBundleV3Fills.push({
|
|
963
|
-
...fillToRefund,
|
|
964
|
-
quoteTimestamp: duplicateDeposit.quoteTimestamp,
|
|
965
|
-
});
|
|
966
|
-
}
|
|
967
|
-
});
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
// If fill replaced a slow fill request, then mark it as one that might have created an
|
|
971
|
-
// unexecutable slow fill. We can't know for sure until we check the slow fill request
|
|
972
|
-
// events.
|
|
973
|
-
if (
|
|
974
|
-
fill.relayExecutionInfo.fillType === FillType.ReplacedSlowFill &&
|
|
975
|
-
_canCreateSlowFillLeaf(deposits[0])
|
|
976
|
-
) {
|
|
977
|
-
fastFillsReplacingSlowFills.push(relayDataHash);
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
} else {
|
|
892
|
+
if (v3RelayHashes[relayDataHash].fill) {
|
|
893
|
+
this.logger.debug({
|
|
894
|
+
at: "BundleDataClient#loadData",
|
|
895
|
+
message: "Duplicate fill detected",
|
|
896
|
+
fill,
|
|
897
|
+
});
|
|
981
898
|
throw new Error("Duplicate fill detected");
|
|
982
899
|
}
|
|
900
|
+
assert(
|
|
901
|
+
isDefined(v3RelayHashes[relayDataHash].deposits) && v3RelayHashes[relayDataHash].deposits!.length > 0,
|
|
902
|
+
"Deposit should exist in relay hash dictionary."
|
|
903
|
+
);
|
|
904
|
+
v3RelayHashes[relayDataHash].fill = fill;
|
|
905
|
+
if (fill.blockNumber >= destinationChainBlockRange[0]) {
|
|
906
|
+
const fillToRefund = await verifyFillRepayment(
|
|
907
|
+
fill,
|
|
908
|
+
destinationClient.spokePool.provider,
|
|
909
|
+
v3RelayHashes[relayDataHash].deposits![0],
|
|
910
|
+
this.clients.hubPoolClient
|
|
911
|
+
);
|
|
912
|
+
if (!isDefined(fillToRefund)) {
|
|
913
|
+
bundleUnrepayableFillsV3.push(fill);
|
|
914
|
+
// We don't return here yet because we still need to mark unexecutable slow fill leaves
|
|
915
|
+
// or duplicate deposits. However, we won't issue a fast fill refund.
|
|
916
|
+
} else {
|
|
917
|
+
v3RelayHashes[relayDataHash].fill = fillToRefund;
|
|
918
|
+
validatedBundleV3Fills.push({
|
|
919
|
+
...fillToRefund,
|
|
920
|
+
quoteTimestamp: v3RelayHashes[relayDataHash].deposits![0].quoteTimestamp,
|
|
921
|
+
});
|
|
922
|
+
|
|
923
|
+
// Now that we know this deposit has been filled on-chain, identify any duplicate deposits
|
|
924
|
+
// sent for this fill and refund them to the filler, because this value would not be paid out
|
|
925
|
+
// otherwise. These deposits can no longer expire and get refunded as an expired deposit,
|
|
926
|
+
// and they won't trigger a pre-fill refund because the fill is in this bundle.
|
|
927
|
+
// Pre-fill refunds only happen when deposits are sent in this bundle and the
|
|
928
|
+
// fill is from a prior bundle. Paying out the filler keeps the behavior consistent for how
|
|
929
|
+
// we deal with duplicate deposits regardless if the deposit is matched with a pre-fill or
|
|
930
|
+
// a current bundle fill.
|
|
931
|
+
const duplicateDeposits = v3RelayHashes[relayDataHash].deposits!.slice(1);
|
|
932
|
+
duplicateDeposits.forEach((duplicateDeposit) => {
|
|
933
|
+
if (isSlowFill(fill)) {
|
|
934
|
+
updateExpiredDepositsV3(expiredDepositsToRefundV3, duplicateDeposit);
|
|
935
|
+
} else {
|
|
936
|
+
validatedBundleV3Fills.push({
|
|
937
|
+
...fillToRefund,
|
|
938
|
+
quoteTimestamp: duplicateDeposit.quoteTimestamp,
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// If fill replaced a slow fill request, then mark it as one that might have created an
|
|
945
|
+
// unexecutable slow fill. We can't know for sure until we check the slow fill request
|
|
946
|
+
// events.
|
|
947
|
+
if (
|
|
948
|
+
fill.relayExecutionInfo.fillType === FillType.ReplacedSlowFill &&
|
|
949
|
+
_canCreateSlowFillLeaf(v3RelayHashes[relayDataHash].deposits![0])
|
|
950
|
+
) {
|
|
951
|
+
fastFillsReplacingSlowFills.push(relayDataHash);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
|
|
983
955
|
return;
|
|
984
956
|
}
|
|
985
957
|
|
|
@@ -1079,28 +1051,34 @@ export class BundleDataClient {
|
|
|
1079
1051
|
const relayDataHash = getRelayEventKey(slowFillRequest);
|
|
1080
1052
|
|
|
1081
1053
|
if (v3RelayHashes[relayDataHash]) {
|
|
1082
|
-
if (
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
// for a fill older than this slow fill request.
|
|
1089
|
-
return;
|
|
1090
|
-
}
|
|
1091
|
-
assert(isDefined(deposits) && deposits.length > 0, "Deposit should exist in relay hash dictionary.");
|
|
1092
|
-
const matchedDeposit = deposits[0];
|
|
1093
|
-
|
|
1094
|
-
if (
|
|
1095
|
-
slowFillRequest.blockNumber >= destinationChainBlockRange[0] &&
|
|
1096
|
-
_canCreateSlowFillLeaf(matchedDeposit) &&
|
|
1097
|
-
!_depositIsExpired(matchedDeposit)
|
|
1098
|
-
) {
|
|
1099
|
-
validatedBundleSlowFills.push(matchedDeposit);
|
|
1100
|
-
}
|
|
1101
|
-
} else {
|
|
1054
|
+
if (v3RelayHashes[relayDataHash].slowFillRequest) {
|
|
1055
|
+
this.logger.debug({
|
|
1056
|
+
at: "BundleDataClient#loadData",
|
|
1057
|
+
message: "Duplicate slow fill request detected",
|
|
1058
|
+
slowFillRequest,
|
|
1059
|
+
});
|
|
1102
1060
|
throw new Error("Duplicate slow fill request detected.");
|
|
1103
1061
|
}
|
|
1062
|
+
v3RelayHashes[relayDataHash].slowFillRequest = slowFillRequest;
|
|
1063
|
+
if (v3RelayHashes[relayDataHash].fill) {
|
|
1064
|
+
// Exiting here assumes that slow fill requests must precede fills, so if there was a fill
|
|
1065
|
+
// following this slow fill request, then we would have already seen it. We don't need to check
|
|
1066
|
+
// for a fill older than this slow fill request.
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
assert(
|
|
1070
|
+
isDefined(v3RelayHashes[relayDataHash].deposits) && v3RelayHashes[relayDataHash].deposits!.length > 0,
|
|
1071
|
+
"Deposit should exist in relay hash dictionary."
|
|
1072
|
+
);
|
|
1073
|
+
const matchedDeposit = v3RelayHashes[relayDataHash].deposits![0];
|
|
1074
|
+
|
|
1075
|
+
if (
|
|
1076
|
+
slowFillRequest.blockNumber >= destinationChainBlockRange[0] &&
|
|
1077
|
+
_canCreateSlowFillLeaf(matchedDeposit) &&
|
|
1078
|
+
!_depositIsExpired(matchedDeposit)
|
|
1079
|
+
) {
|
|
1080
|
+
validatedBundleSlowFills.push(matchedDeposit);
|
|
1081
|
+
}
|
|
1104
1082
|
return;
|
|
1105
1083
|
}
|
|
1106
1084
|
|
|
@@ -1195,7 +1173,7 @@ export class BundleDataClient {
|
|
|
1195
1173
|
const fillToRefund = await verifyFillRepayment(
|
|
1196
1174
|
fill,
|
|
1197
1175
|
destinationClient.spokePool.provider,
|
|
1198
|
-
deposits[0],
|
|
1176
|
+
v3RelayHashes[relayDataHash].deposits![0],
|
|
1199
1177
|
this.clients.hubPoolClient
|
|
1200
1178
|
);
|
|
1201
1179
|
if (!isDefined(fillToRefund)) {
|
|
@@ -1243,18 +1221,18 @@ export class BundleDataClient {
|
|
|
1243
1221
|
// then we wouldn't be in this branch of the code.
|
|
1244
1222
|
const prefill = await this.findMatchingFillEvent(deposit, destinationClient);
|
|
1245
1223
|
assert(isDefined(prefill), `findFillEvent# Cannot find prefill: ${relayDataHash}`);
|
|
1246
|
-
assert(getRelayEventKey(prefill) === relayDataHash, "Relay hashes should match.");
|
|
1224
|
+
assert(getRelayEventKey(prefill!) === relayDataHash, "Relay hashes should match.");
|
|
1247
1225
|
const verifiedFill = await verifyFillRepayment(
|
|
1248
|
-
prefill
|
|
1226
|
+
prefill!,
|
|
1249
1227
|
destinationClient.spokePool.provider,
|
|
1250
1228
|
deposit,
|
|
1251
1229
|
this.clients.hubPoolClient
|
|
1252
1230
|
);
|
|
1253
1231
|
if (!isDefined(verifiedFill)) {
|
|
1254
|
-
bundleUnrepayableFillsV3.push(prefill);
|
|
1232
|
+
bundleUnrepayableFillsV3.push(prefill!);
|
|
1255
1233
|
} else if (!isSlowFill(verifiedFill)) {
|
|
1256
1234
|
validatedBundleV3Fills.push({
|
|
1257
|
-
...verifiedFill
|
|
1235
|
+
...verifiedFill!,
|
|
1258
1236
|
quoteTimestamp: deposit.quoteTimestamp,
|
|
1259
1237
|
});
|
|
1260
1238
|
} else {
|
|
@@ -1377,16 +1355,14 @@ export class BundleDataClient {
|
|
|
1377
1355
|
validatedBundleV3Fills.length > 0
|
|
1378
1356
|
? this.clients.hubPoolClient.batchComputeRealizedLpFeePct(
|
|
1379
1357
|
validatedBundleV3Fills.map((fill) => {
|
|
1380
|
-
const
|
|
1381
|
-
assert(isDefined(deposits) && deposits.length > 0, "Deposit should exist in relay hash dictionary.");
|
|
1382
|
-
const matchedDeposit = deposits[0];
|
|
1358
|
+
const matchedDeposit = v3RelayHashes[getRelayEventKey(fill)].deposits![0];
|
|
1383
1359
|
assert(isDefined(matchedDeposit), "Deposit should exist in relay hash dictionary.");
|
|
1384
1360
|
const { chainToSendRefundTo: paymentChainId } = getRefundInformationFromFill(
|
|
1385
1361
|
fill,
|
|
1386
1362
|
this.clients.hubPoolClient,
|
|
1387
1363
|
blockRangesForChains,
|
|
1388
1364
|
chainIds,
|
|
1389
|
-
matchedDeposit
|
|
1365
|
+
matchedDeposit!.fromLiteChain
|
|
1390
1366
|
);
|
|
1391
1367
|
return {
|
|
1392
1368
|
...fill,
|
|
@@ -1423,16 +1399,14 @@ export class BundleDataClient {
|
|
|
1423
1399
|
});
|
|
1424
1400
|
v3FillLpFees.forEach(({ realizedLpFeePct }, idx) => {
|
|
1425
1401
|
const fill = validatedBundleV3Fills[idx];
|
|
1426
|
-
const
|
|
1427
|
-
assert(isDefined(deposits) && deposits.length > 0, "Deposit should exist in relay hash dictionary.");
|
|
1428
|
-
const associatedDeposit = deposits[0];
|
|
1402
|
+
const associatedDeposit = v3RelayHashes[getRelayEventKey(fill)].deposits![0];
|
|
1429
1403
|
assert(isDefined(associatedDeposit), "Deposit should exist in relay hash dictionary.");
|
|
1430
1404
|
const { chainToSendRefundTo, repaymentToken } = getRefundInformationFromFill(
|
|
1431
1405
|
fill,
|
|
1432
1406
|
this.clients.hubPoolClient,
|
|
1433
1407
|
blockRangesForChains,
|
|
1434
1408
|
chainIds,
|
|
1435
|
-
associatedDeposit
|
|
1409
|
+
associatedDeposit!.fromLiteChain
|
|
1436
1410
|
);
|
|
1437
1411
|
updateBundleFillsV3(bundleFillsV3, fill, realizedLpFeePct, chainToSendRefundTo, repaymentToken, fill.relayer);
|
|
1438
1412
|
});
|
|
@@ -776,31 +776,35 @@ export class HubPoolClient extends BaseAbstractClient {
|
|
|
776
776
|
return endBlock > 0 ? endBlock + 1 : 0;
|
|
777
777
|
}
|
|
778
778
|
|
|
779
|
-
|
|
779
|
+
getLatestExecutedRootBundleContainingL1Token(block: number, chain: number, l1Token: string): ExecutedRootBundle {
|
|
780
780
|
// Search ExecutedRootBundles in descending block order to find the most recent event before the target block.
|
|
781
|
-
|
|
782
|
-
(
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
781
|
+
return sortEventsDescending(this.executedRootBundles).find((executedLeaf: ExecutedRootBundle) => {
|
|
782
|
+
return (
|
|
783
|
+
executedLeaf.blockNumber <= block &&
|
|
784
|
+
executedLeaf.chainId === chain &&
|
|
785
|
+
executedLeaf.l1Tokens.map((l1Token) => l1Token.toLowerCase()).includes(l1Token.toLowerCase())
|
|
786
|
+
);
|
|
787
|
+
}) as ExecutedRootBundle;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
getRunningBalanceBeforeBlockForChain(block: number, chain: number, l1Token: string): TokenRunningBalance {
|
|
791
|
+
const executedRootBundle = this.getLatestExecutedRootBundleContainingL1Token(block, chain, l1Token);
|
|
790
792
|
|
|
791
793
|
return this.getRunningBalanceForToken(l1Token, executedRootBundle);
|
|
792
794
|
}
|
|
793
795
|
|
|
794
796
|
public getRunningBalanceForToken(l1Token: string, executedRootBundle: ExecutedRootBundle): TokenRunningBalance {
|
|
795
797
|
let runningBalance = toBN(0);
|
|
798
|
+
let incentiveBalance = toBN(0);
|
|
796
799
|
if (executedRootBundle) {
|
|
797
800
|
const indexOfL1Token = executedRootBundle.l1Tokens
|
|
798
801
|
.map((l1Token) => l1Token.toLowerCase())
|
|
799
802
|
.indexOf(l1Token.toLowerCase());
|
|
800
803
|
runningBalance = executedRootBundle.runningBalances[indexOfL1Token];
|
|
804
|
+
incentiveBalance = executedRootBundle.incentiveBalances[indexOfL1Token];
|
|
801
805
|
}
|
|
802
806
|
|
|
803
|
-
return { runningBalance };
|
|
807
|
+
return { runningBalance, incentiveBalance };
|
|
804
808
|
}
|
|
805
809
|
|
|
806
810
|
async _update(eventNames: HubPoolEvent[]): Promise<HubPoolUpdate> {
|
|
@@ -1005,6 +1009,8 @@ export class HubPoolClient extends BaseAbstractClient {
|
|
|
1005
1009
|
);
|
|
1006
1010
|
}
|
|
1007
1011
|
executedRootBundle.runningBalances = runningBalances.slice(0, nTokens);
|
|
1012
|
+
executedRootBundle.incentiveBalances =
|
|
1013
|
+
runningBalances.length > nTokens ? runningBalances.slice(nTokens) : runningBalances.map(() => toBN(0));
|
|
1008
1014
|
this.executedRootBundles.push(executedRootBundle);
|
|
1009
1015
|
}
|
|
1010
1016
|
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import assert from "assert";
|
|
2
1
|
import { Contract, EventFilter } from "ethers";
|
|
3
2
|
import winston from "winston";
|
|
4
3
|
import {
|
|
5
4
|
AnyObject,
|
|
6
5
|
BigNumber,
|
|
7
6
|
bnZero,
|
|
8
|
-
bnUint32Max,
|
|
9
7
|
DefaultLogLevels,
|
|
10
8
|
EventSearchConfig,
|
|
11
9
|
MAX_BIG_INT,
|
|
@@ -23,6 +21,7 @@ import {
|
|
|
23
21
|
toAddress,
|
|
24
22
|
} from "../utils";
|
|
25
23
|
import {
|
|
24
|
+
duplicateEvent,
|
|
26
25
|
paginatedEventQuery,
|
|
27
26
|
sortEventsAscendingInPlace,
|
|
28
27
|
spreadEvent,
|
|
@@ -55,6 +54,7 @@ import { getRepaymentChainId, forceDestinationRepayment } from "./BundleDataClie
|
|
|
55
54
|
type SpokePoolUpdateSuccess = {
|
|
56
55
|
success: true;
|
|
57
56
|
currentTime: number;
|
|
57
|
+
oldestTime: number;
|
|
58
58
|
firstDepositId: BigNumber;
|
|
59
59
|
latestDepositId: BigNumber;
|
|
60
60
|
events: Log[][];
|
|
@@ -72,6 +72,7 @@ export type SpokePoolUpdate = SpokePoolUpdateSuccess | SpokePoolUpdateFailure;
|
|
|
72
72
|
*/
|
|
73
73
|
export class SpokePoolClient extends BaseAbstractClient {
|
|
74
74
|
protected currentTime = 0;
|
|
75
|
+
protected oldestTime = 0;
|
|
75
76
|
protected depositHashes: { [depositHash: string]: DepositWithBlock } = {};
|
|
76
77
|
protected duplicateDepositHashes: { [depositHash: string]: DepositWithBlock[] } = {};
|
|
77
78
|
protected depositHashesToFills: { [depositHash: string]: FillWithBlock[] } = {};
|
|
@@ -542,11 +543,12 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
542
543
|
|
|
543
544
|
const timerStart = Date.now();
|
|
544
545
|
const multicallFunctions = ["getCurrentTime", "numberOfDeposits"];
|
|
545
|
-
const [multicallOutput, ...events] = await Promise.all([
|
|
546
|
+
const [multicallOutput, oldestTime, ...events] = await Promise.all([
|
|
546
547
|
spokePool.callStatic.multicall(
|
|
547
548
|
multicallFunctions.map((f) => spokePool.interface.encodeFunctionData(f)),
|
|
548
549
|
{ blockTag: searchConfig.toBlock }
|
|
549
550
|
),
|
|
551
|
+
this.spokePool.getCurrentTime({ blockTag: Math.max(searchConfig.fromBlock, this.deploymentBlock) }),
|
|
550
552
|
...eventSearchConfigs.map((config) => paginatedEventQuery(this.spokePool, config.filter, config.searchConfig)),
|
|
551
553
|
]);
|
|
552
554
|
this.log("debug", `Time to query new events from RPC for ${this.chainId}: ${Date.now() - timerStart} ms`);
|
|
@@ -569,6 +571,7 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
569
571
|
return {
|
|
570
572
|
success: true,
|
|
571
573
|
currentTime: currentTime.toNumber(), // uint32
|
|
574
|
+
oldestTime: oldestTime.toNumber(),
|
|
572
575
|
firstDepositId,
|
|
573
576
|
latestDepositId: _latestDepositId.gt(bnZero) ? _latestDepositId : bnZero,
|
|
574
577
|
searchEndBlock: searchConfig.toBlock,
|
|
@@ -585,6 +588,7 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
585
588
|
* @see _update
|
|
586
589
|
*/
|
|
587
590
|
public async update(eventsToQuery = this.queryableEventNames): Promise<void> {
|
|
591
|
+
const duplicateEvents: Log[] = [];
|
|
588
592
|
if (this.hubPoolClient !== null && !this.hubPoolClient.isUpdated) {
|
|
589
593
|
throw new Error("HubPoolClient not updated");
|
|
590
594
|
}
|
|
@@ -593,7 +597,7 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
593
597
|
if (!update.success) {
|
|
594
598
|
return;
|
|
595
599
|
}
|
|
596
|
-
const { events: queryResults, currentTime, searchEndBlock } = update;
|
|
600
|
+
const { events: queryResults, currentTime, oldestTime, searchEndBlock } = update;
|
|
597
601
|
|
|
598
602
|
if (eventsToQuery.includes("TokensBridged")) {
|
|
599
603
|
// Temporarily query old spoke pool events as well to ease migration:
|
|
@@ -654,6 +658,16 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
654
658
|
}
|
|
655
659
|
|
|
656
660
|
if (this.depositHashes[getRelayEventKey(deposit)] !== undefined) {
|
|
661
|
+
// Sanity check that this event is not a duplicate, even though the relay data hash is a duplicate.
|
|
662
|
+
const allDeposits = this._getDuplicateDeposits(deposit).concat(this.depositHashes[getRelayEventKey(deposit)]);
|
|
663
|
+
if (
|
|
664
|
+
allDeposits.some((e) => {
|
|
665
|
+
return duplicateEvent(deposit, e);
|
|
666
|
+
})
|
|
667
|
+
) {
|
|
668
|
+
duplicateEvents.push(event);
|
|
669
|
+
continue;
|
|
670
|
+
}
|
|
657
671
|
assign(this.duplicateDepositHashes, [getRelayEventKey(deposit)], [deposit]);
|
|
658
672
|
continue;
|
|
659
673
|
}
|
|
@@ -716,6 +730,13 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
716
730
|
}
|
|
717
731
|
|
|
718
732
|
const depositHash = getRelayEventKey({ ...slowFillRequest, destinationChainId: this.chainId });
|
|
733
|
+
|
|
734
|
+
// Sanity check that this event is not a duplicate.
|
|
735
|
+
if (this.slowFillRequests[depositHash] !== undefined) {
|
|
736
|
+
duplicateEvents.push(event);
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
|
|
719
740
|
this.slowFillRequests[depositHash] ??= slowFillRequest;
|
|
720
741
|
}
|
|
721
742
|
};
|
|
@@ -749,6 +770,13 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
749
770
|
fill.relayExecutionInfo.updatedMessageHash = getMessageHash(event.args.relayExecutionInfo.updatedMessage);
|
|
750
771
|
}
|
|
751
772
|
|
|
773
|
+
// Sanity check that this event is not a duplicate.
|
|
774
|
+
const duplicateFill = this.fills[fill.originChainId]?.find((f) => duplicateEvent(fill, f));
|
|
775
|
+
if (duplicateFill) {
|
|
776
|
+
duplicateEvents.push(event);
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
|
|
752
780
|
assign(this.fills, [fill.originChainId], [fill]);
|
|
753
781
|
assign(this.depositHashesToFills, [this.getDepositHash(fill)], [fill]);
|
|
754
782
|
}
|
|
@@ -793,8 +821,16 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
793
821
|
}
|
|
794
822
|
}
|
|
795
823
|
|
|
824
|
+
if (duplicateEvents.length > 0) {
|
|
825
|
+
this.log("debug", "Duplicate events listed", {
|
|
826
|
+
duplicateEvents,
|
|
827
|
+
});
|
|
828
|
+
this.log("error", "Duplicate events detected, check debug logs");
|
|
829
|
+
}
|
|
830
|
+
|
|
796
831
|
// Next iteration should start off from where this one ended.
|
|
797
832
|
this.currentTime = currentTime;
|
|
833
|
+
if (this.oldestTime === 0) this.oldestTime = oldestTime; // Set oldest time only after the first update.
|
|
798
834
|
this.firstDepositIdForSpokePool = update.firstDepositId;
|
|
799
835
|
this.latestBlockSearched = searchEndBlock;
|
|
800
836
|
this.lastDepositIdForSpokePool = update.latestDepositId;
|
|
@@ -884,13 +920,11 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
884
920
|
}
|
|
885
921
|
|
|
886
922
|
/**
|
|
887
|
-
* Retrieves the time
|
|
888
|
-
* @returns The time
|
|
923
|
+
* Retrieves the oldest time searched on the SpokePool contract.
|
|
924
|
+
* @returns The oldest time searched, which will be 0 if there has been no update() yet.
|
|
889
925
|
*/
|
|
890
|
-
public
|
|
891
|
-
|
|
892
|
-
assert(BigNumber.isBigNumber(currentTime) && currentTime.lt(bnUint32Max));
|
|
893
|
-
return currentTime.toNumber();
|
|
926
|
+
public getOldestTime(): number {
|
|
927
|
+
return this.oldestTime;
|
|
894
928
|
}
|
|
895
929
|
|
|
896
930
|
async findDeposit(depositId: BigNumber, destinationChainId: number): Promise<DepositWithBlock> {
|
|
@@ -54,6 +54,7 @@ export interface ExecutedRootBundle extends SortableEvent {
|
|
|
54
54
|
bundleLpFees: BigNumber[];
|
|
55
55
|
netSendAmounts: BigNumber[];
|
|
56
56
|
runningBalances: BigNumber[];
|
|
57
|
+
incentiveBalances: BigNumber[];
|
|
57
58
|
leafId: number;
|
|
58
59
|
l1Tokens: string[];
|
|
59
60
|
proof: string[];
|
|
@@ -61,15 +62,17 @@ export interface ExecutedRootBundle extends SortableEvent {
|
|
|
61
62
|
|
|
62
63
|
export type ExecutedRootBundleStringified = Omit<
|
|
63
64
|
ExecutedRootBundle,
|
|
64
|
-
"bundleLpFees" | "netSendAmounts" | "runningBalances"
|
|
65
|
+
"bundleLpFees" | "netSendAmounts" | "runningBalances" | "incentiveBalances"
|
|
65
66
|
> & {
|
|
66
67
|
bundleLpFees: string[];
|
|
67
68
|
netSendAmounts: string[];
|
|
68
69
|
runningBalances: string[];
|
|
70
|
+
incentiveBalances: string[];
|
|
69
71
|
};
|
|
70
72
|
|
|
71
73
|
export type TokenRunningBalance = {
|
|
72
74
|
runningBalance: BigNumber;
|
|
75
|
+
incentiveBalance: BigNumber;
|
|
73
76
|
};
|
|
74
77
|
|
|
75
78
|
export interface RelayerRefundLeafWithGroup extends RelayerRefundLeaf {
|
|
@@ -13,10 +13,10 @@ import {
|
|
|
13
13
|
BigNumber,
|
|
14
14
|
toBNWei,
|
|
15
15
|
bnZero,
|
|
16
|
+
assert,
|
|
16
17
|
chainIsOPStack,
|
|
17
18
|
fixedPointAdjustment,
|
|
18
19
|
} from "../../utils";
|
|
19
|
-
import assert from "assert";
|
|
20
20
|
import { Logger, QueryInterface } from "../relayFeeCalculator";
|
|
21
21
|
import { Transport } from "viem";
|
|
22
22
|
import { getGasPriceEstimate } from "../../gasPriceOracle/oracle";
|