@across-protocol/sdk 4.1.8 → 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 +72 -87
- 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 +5 -2
- package/dist/cjs/clients/HubPoolClient.js.map +1 -1
- package/dist/cjs/clients/SpokePoolClient.js +50 -20
- package/dist/cjs/clients/SpokePoolClient.js.map +1 -1
- package/dist/cjs/constants.d.ts +0 -1
- package/dist/cjs/constants.js +1 -2
- package/dist/cjs/constants.js.map +1 -1
- package/dist/cjs/utils/BlockUtils.js +1 -1
- package/dist/cjs/utils/BlockUtils.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/NetworkUtils.js +1 -1
- package/dist/cjs/utils/NetworkUtils.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/BundleDataClient.d.ts +2 -6
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js +80 -97
- 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 +5 -2
- package/dist/esm/clients/HubPoolClient.js.map +1 -1
- package/dist/esm/clients/SpokePoolClient.js +57 -23
- package/dist/esm/clients/SpokePoolClient.js.map +1 -1
- package/dist/esm/constants.d.ts +0 -1
- package/dist/esm/constants.js +0 -1
- package/dist/esm/constants.js.map +1 -1
- package/dist/esm/utils/BlockUtils.js +1 -1
- package/dist/esm/utils/BlockUtils.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/NetworkUtils.js +1 -1
- package/dist/esm/utils/NetworkUtils.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.map +1 -1
- package/dist/types/constants.d.ts +0 -1
- package/dist/types/constants.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/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 +2 -2
- package/src/clients/BundleDataClient/BundleDataClient.ts +129 -170
- package/src/clients/HubPoolClient.ts +12 -10
- package/src/clients/SpokePoolClient.ts +33 -0
- package/src/constants.ts +0 -2
- package/src/utils/BlockUtils.ts +1 -1
- package/src/utils/EventUtils.ts +4 -0
- package/src/utils/NetworkUtils.ts +1 -1
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
Deposit,
|
|
19
19
|
DepositWithBlock,
|
|
20
20
|
} from "../../interfaces";
|
|
21
|
-
import {
|
|
21
|
+
import { SpokePoolClient } from "..";
|
|
22
22
|
import {
|
|
23
23
|
BigNumber,
|
|
24
24
|
bnZero,
|
|
@@ -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,13 +52,12 @@ import {
|
|
|
52
52
|
getRefundsFromBundle,
|
|
53
53
|
getWidestPossibleExpectedBlockRange,
|
|
54
54
|
isChainDisabled,
|
|
55
|
-
PoolRebalanceRoot,
|
|
56
55
|
prettyPrintV3SpokePoolEvents,
|
|
57
56
|
V3DepositWithBlock,
|
|
58
57
|
V3FillWithBlock,
|
|
59
58
|
verifyFillRepayment,
|
|
60
59
|
} from "./utils";
|
|
61
|
-
import {
|
|
60
|
+
import { UNDEFINED_MESSAGE_HASH } from "../../constants";
|
|
62
61
|
|
|
63
62
|
// max(uint256) - 1
|
|
64
63
|
export const INFINITE_FILL_DEADLINE = bnUint32Max;
|
|
@@ -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.
|
|
@@ -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:
|
|
@@ -827,22 +788,6 @@ export class BundleDataClient {
|
|
|
827
788
|
return { relayDataHash, index: Number(i) };
|
|
828
789
|
};
|
|
829
790
|
|
|
830
|
-
// We use the following toggle to aid with the migration to pre-fills. The first bundle proposed using this
|
|
831
|
-
// pre-fill logic can double refund pre-fills that have already been filled in the last bundle, because the
|
|
832
|
-
// last bundle did not recognize a fill as a pre-fill. Therefore the developer should ensure that the version
|
|
833
|
-
// is bumped to the PRE_FILL_MIN_CONFIG_STORE_VERSION version before the first pre-fill bundle is proposed.
|
|
834
|
-
// To test the following bundle after this, the developer can set the FORCE_REFUND_PREFILLS environment variable
|
|
835
|
-
// to "true" simulate the bundle with pre-fill refunds.
|
|
836
|
-
// @todo Remove this logic once we have advanced sufficiently past the pre-fill migration.
|
|
837
|
-
const startBlockForMainnet = getBlockRangeForChain(
|
|
838
|
-
blockRangesForChains,
|
|
839
|
-
this.clients.hubPoolClient.chainId,
|
|
840
|
-
this.chainIdListForBundleEvaluationBlockNumbers
|
|
841
|
-
)[0];
|
|
842
|
-
const versionAtProposalBlock = this.clients.configStoreClient.getConfigStoreVersionForBlock(startBlockForMainnet);
|
|
843
|
-
const canRefundPrefills =
|
|
844
|
-
versionAtProposalBlock >= PRE_FILL_MIN_CONFIG_STORE_VERSION || process.env.FORCE_REFUND_PREFILLS === "true";
|
|
845
|
-
|
|
846
791
|
// Prerequisite step: Load all deposit events from the current or older bundles into the v3RelayHashes dictionary
|
|
847
792
|
// for convenient matching with fills.
|
|
848
793
|
for (const originChainId of allChainIds) {
|
|
@@ -879,6 +824,14 @@ export class BundleDataClient {
|
|
|
879
824
|
"Not using correct bundle deposit hash key"
|
|
880
825
|
);
|
|
881
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
|
+
}
|
|
882
835
|
bundleDepositHashes.push(newBundleDepositHash);
|
|
883
836
|
updateBundleDepositsV3(bundleDepositsV3, deposit);
|
|
884
837
|
} else if (deposit.blockNumber < originChainBlockRange[0]) {
|
|
@@ -936,64 +889,69 @@ export class BundleDataClient {
|
|
|
936
889
|
fillCounter++;
|
|
937
890
|
const relayDataHash = getRelayEventKey(fill);
|
|
938
891
|
if (v3RelayHashes[relayDataHash]) {
|
|
939
|
-
if (
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
"
|
|
892
|
+
if (v3RelayHashes[relayDataHash].fill) {
|
|
893
|
+
this.logger.debug({
|
|
894
|
+
at: "BundleDataClient#loadData",
|
|
895
|
+
message: "Duplicate fill detected",
|
|
896
|
+
fill,
|
|
897
|
+
});
|
|
898
|
+
throw new Error("Duplicate fill detected");
|
|
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
|
|
943
911
|
);
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
// If fill replaced a slow fill request, then mark it as one that might have created an
|
|
985
|
-
// unexecutable slow fill. We can't know for sure until we check the slow fill request
|
|
986
|
-
// events.
|
|
987
|
-
if (
|
|
988
|
-
fill.relayExecutionInfo.fillType === FillType.ReplacedSlowFill &&
|
|
989
|
-
_canCreateSlowFillLeaf(v3RelayHashes[relayDataHash].deposits![0])
|
|
990
|
-
) {
|
|
991
|
-
fastFillsReplacingSlowFills.push(relayDataHash);
|
|
992
|
-
}
|
|
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);
|
|
993
952
|
}
|
|
994
|
-
} else {
|
|
995
|
-
throw new Error("Duplicate fill detected");
|
|
996
953
|
}
|
|
954
|
+
|
|
997
955
|
return;
|
|
998
956
|
}
|
|
999
957
|
|
|
@@ -1093,30 +1051,34 @@ export class BundleDataClient {
|
|
|
1093
1051
|
const relayDataHash = getRelayEventKey(slowFillRequest);
|
|
1094
1052
|
|
|
1095
1053
|
if (v3RelayHashes[relayDataHash]) {
|
|
1096
|
-
if (
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
return;
|
|
1103
|
-
}
|
|
1104
|
-
assert(
|
|
1105
|
-
isDefined(v3RelayHashes[relayDataHash].deposits) && v3RelayHashes[relayDataHash].deposits!.length > 0,
|
|
1106
|
-
"Deposit should exist in relay hash dictionary."
|
|
1107
|
-
);
|
|
1108
|
-
const matchedDeposit = v3RelayHashes[relayDataHash].deposits![0];
|
|
1109
|
-
|
|
1110
|
-
if (
|
|
1111
|
-
slowFillRequest.blockNumber >= destinationChainBlockRange[0] &&
|
|
1112
|
-
_canCreateSlowFillLeaf(matchedDeposit) &&
|
|
1113
|
-
!_depositIsExpired(matchedDeposit)
|
|
1114
|
-
) {
|
|
1115
|
-
validatedBundleSlowFills.push(matchedDeposit);
|
|
1116
|
-
}
|
|
1117
|
-
} else {
|
|
1054
|
+
if (v3RelayHashes[relayDataHash].slowFillRequest) {
|
|
1055
|
+
this.logger.debug({
|
|
1056
|
+
at: "BundleDataClient#loadData",
|
|
1057
|
+
message: "Duplicate slow fill request detected",
|
|
1058
|
+
slowFillRequest,
|
|
1059
|
+
});
|
|
1118
1060
|
throw new Error("Duplicate slow fill request detected.");
|
|
1119
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
|
+
}
|
|
1120
1082
|
return;
|
|
1121
1083
|
}
|
|
1122
1084
|
|
|
@@ -1207,7 +1169,7 @@ export class BundleDataClient {
|
|
|
1207
1169
|
// If fill is in the current bundle then we can assume there is already a refund for it, so only
|
|
1208
1170
|
// include this pre fill if the fill is in an older bundle.
|
|
1209
1171
|
if (fill) {
|
|
1210
|
-
if (
|
|
1172
|
+
if (fill.blockNumber < destinationChainBlockRange[0]) {
|
|
1211
1173
|
const fillToRefund = await verifyFillRepayment(
|
|
1212
1174
|
fill,
|
|
1213
1175
|
destinationClient.spokePool.provider,
|
|
@@ -1238,7 +1200,6 @@ export class BundleDataClient {
|
|
|
1238
1200
|
if (_depositIsExpired(deposit)) {
|
|
1239
1201
|
updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
|
|
1240
1202
|
} else if (
|
|
1241
|
-
canRefundPrefills &&
|
|
1242
1203
|
slowFillRequest.blockNumber < destinationChainBlockRange[0] &&
|
|
1243
1204
|
_canCreateSlowFillLeaf(deposit) &&
|
|
1244
1205
|
validatedBundleSlowFills.every((d) => getRelayEventKey(d) !== relayDataHash)
|
|
@@ -1261,23 +1222,21 @@ export class BundleDataClient {
|
|
|
1261
1222
|
const prefill = await this.findMatchingFillEvent(deposit, destinationClient);
|
|
1262
1223
|
assert(isDefined(prefill), `findFillEvent# Cannot find prefill: ${relayDataHash}`);
|
|
1263
1224
|
assert(getRelayEventKey(prefill!) === relayDataHash, "Relay hashes should match.");
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
|
|
1280
|
-
}
|
|
1225
|
+
const verifiedFill = await verifyFillRepayment(
|
|
1226
|
+
prefill!,
|
|
1227
|
+
destinationClient.spokePool.provider,
|
|
1228
|
+
deposit,
|
|
1229
|
+
this.clients.hubPoolClient
|
|
1230
|
+
);
|
|
1231
|
+
if (!isDefined(verifiedFill)) {
|
|
1232
|
+
bundleUnrepayableFillsV3.push(prefill!);
|
|
1233
|
+
} else if (!isSlowFill(verifiedFill)) {
|
|
1234
|
+
validatedBundleV3Fills.push({
|
|
1235
|
+
...verifiedFill!,
|
|
1236
|
+
quoteTimestamp: deposit.quoteTimestamp,
|
|
1237
|
+
});
|
|
1238
|
+
} else {
|
|
1239
|
+
updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
|
|
1281
1240
|
}
|
|
1282
1241
|
} else if (_depositIsExpired(deposit)) {
|
|
1283
1242
|
updateExpiredDepositsV3(expiredDepositsToRefundV3, deposit);
|
|
@@ -1286,7 +1245,7 @@ export class BundleDataClient {
|
|
|
1286
1245
|
// Don't create duplicate slow fill requests for the same deposit.
|
|
1287
1246
|
validatedBundleSlowFills.every((d) => getRelayEventKey(d) !== relayDataHash)
|
|
1288
1247
|
) {
|
|
1289
|
-
if (
|
|
1248
|
+
if (_canCreateSlowFillLeaf(deposit)) {
|
|
1290
1249
|
validatedBundleSlowFills.push(deposit);
|
|
1291
1250
|
}
|
|
1292
1251
|
}
|
|
@@ -776,17 +776,19 @@ 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
|
}
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
toAddress,
|
|
22
22
|
} from "../utils";
|
|
23
23
|
import {
|
|
24
|
+
duplicateEvent,
|
|
24
25
|
paginatedEventQuery,
|
|
25
26
|
sortEventsAscendingInPlace,
|
|
26
27
|
spreadEvent,
|
|
@@ -587,6 +588,7 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
587
588
|
* @see _update
|
|
588
589
|
*/
|
|
589
590
|
public async update(eventsToQuery = this.queryableEventNames): Promise<void> {
|
|
591
|
+
const duplicateEvents: Log[] = [];
|
|
590
592
|
if (this.hubPoolClient !== null && !this.hubPoolClient.isUpdated) {
|
|
591
593
|
throw new Error("HubPoolClient not updated");
|
|
592
594
|
}
|
|
@@ -656,6 +658,16 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
656
658
|
}
|
|
657
659
|
|
|
658
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
|
+
}
|
|
659
671
|
assign(this.duplicateDepositHashes, [getRelayEventKey(deposit)], [deposit]);
|
|
660
672
|
continue;
|
|
661
673
|
}
|
|
@@ -718,6 +730,13 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
718
730
|
}
|
|
719
731
|
|
|
720
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
|
+
|
|
721
740
|
this.slowFillRequests[depositHash] ??= slowFillRequest;
|
|
722
741
|
}
|
|
723
742
|
};
|
|
@@ -751,6 +770,13 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
751
770
|
fill.relayExecutionInfo.updatedMessageHash = getMessageHash(event.args.relayExecutionInfo.updatedMessage);
|
|
752
771
|
}
|
|
753
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
|
+
|
|
754
780
|
assign(this.fills, [fill.originChainId], [fill]);
|
|
755
781
|
assign(this.depositHashesToFills, [this.getDepositHash(fill)], [fill]);
|
|
756
782
|
}
|
|
@@ -795,6 +821,13 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
795
821
|
}
|
|
796
822
|
}
|
|
797
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
|
+
|
|
798
831
|
// Next iteration should start off from where this one ended.
|
|
799
832
|
this.currentTime = currentTime;
|
|
800
833
|
if (this.oldestTime === 0) this.oldestTime = oldestTime; // Set oldest time only after the first update.
|
package/src/constants.ts
CHANGED
|
@@ -29,8 +29,6 @@ export const HUBPOOL_CHAIN_ID = 1;
|
|
|
29
29
|
// List of versions where certain UMIP features were deprecated or activated
|
|
30
30
|
export const TRANSFER_THRESHOLD_MAX_CONFIG_STORE_VERSION = 1;
|
|
31
31
|
|
|
32
|
-
export const PRE_FILL_MIN_CONFIG_STORE_VERSION = 5;
|
|
33
|
-
|
|
34
32
|
// A hardcoded identifier used, by default, to tag all Arweave records.
|
|
35
33
|
export const ARWEAVE_TAG_APP_NAME = "across-protocol";
|
|
36
34
|
|
package/src/utils/BlockUtils.ts
CHANGED
|
@@ -34,11 +34,11 @@ const defaultHighBlockOffset = 10;
|
|
|
34
34
|
const cacheTTL = 60 * 15;
|
|
35
35
|
const now = getCurrentTime(); // Seed the cache with initial values.
|
|
36
36
|
const blockTimes: { [chainId: number]: BlockTimeAverage } = {
|
|
37
|
-
[CHAIN_IDs.DOCTOR_WHO]: { average: 1, timestamp: now, blockRange: 1 },
|
|
38
37
|
[CHAIN_IDs.INK]: { average: 1, timestamp: now, blockRange: 1 },
|
|
39
38
|
[CHAIN_IDs.LINEA]: { average: 3, timestamp: now, blockRange: 1 },
|
|
40
39
|
[CHAIN_IDs.MAINNET]: { average: 12.5, timestamp: now, blockRange: 1 },
|
|
41
40
|
[CHAIN_IDs.OPTIMISM]: { average: 2, timestamp: now, blockRange: 1 },
|
|
41
|
+
[CHAIN_IDs.UNICHAIN]: { average: 1, timestamp: now, blockRange: 1 },
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
/**
|
package/src/utils/EventUtils.ts
CHANGED
|
@@ -266,3 +266,7 @@ export function isEventOlder<T extends SortableEvent>(ex: T, ey: T): boolean {
|
|
|
266
266
|
export function getTransactionHashes(events: SortableEvent[]): string[] {
|
|
267
267
|
return [...Array.from(new Set(events.map((e) => e.transactionHash)))];
|
|
268
268
|
}
|
|
269
|
+
|
|
270
|
+
export function duplicateEvent(a: SortableEvent, b: SortableEvent): boolean {
|
|
271
|
+
return a.transactionHash === b.transactionHash && a.logIndex === b.logIndex;
|
|
272
|
+
}
|
|
@@ -144,9 +144,9 @@ export function chainIsCCTPEnabled(chainId: number): boolean {
|
|
|
144
144
|
// Mainnets
|
|
145
145
|
CHAIN_IDs.ARBITRUM,
|
|
146
146
|
CHAIN_IDs.BASE,
|
|
147
|
-
CHAIN_IDs.DOCTOR_WHO,
|
|
148
147
|
CHAIN_IDs.OPTIMISM,
|
|
149
148
|
CHAIN_IDs.POLYGON,
|
|
149
|
+
CHAIN_IDs.UNICHAIN,
|
|
150
150
|
// Testnets
|
|
151
151
|
CHAIN_IDs.BASE_SEPOLIA,
|
|
152
152
|
CHAIN_IDs.OPTIMISM_SEPOLIA,
|