@across-protocol/sdk 4.1.25 → 4.1.27
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/addressAggregator/adapters/index.d.ts +2 -1
- package/dist/cjs/addressAggregator/adapters/index.js +3 -2
- package/dist/cjs/addressAggregator/adapters/index.js.map +1 -1
- package/dist/cjs/addressAggregator/adapters/risklabs.d.ts +10 -0
- package/dist/cjs/addressAggregator/adapters/risklabs.js +38 -0
- package/dist/cjs/addressAggregator/adapters/risklabs.js.map +1 -0
- package/dist/cjs/addressAggregator/index.js +1 -1
- package/dist/cjs/addressAggregator/index.js.map +1 -1
- package/dist/cjs/clients/SpokePoolClient.d.ts +2 -8
- package/dist/cjs/clients/SpokePoolClient.js +25 -44
- package/dist/cjs/clients/SpokePoolClient.js.map +1 -1
- package/dist/cjs/clients/mocks/MockSpokePoolClient.js +0 -5
- package/dist/cjs/clients/mocks/MockSpokePoolClient.js.map +1 -1
- package/dist/cjs/providers/solana/cachedRpcFactory.js +23 -8
- package/dist/cjs/providers/solana/cachedRpcFactory.js.map +1 -1
- package/dist/cjs/utils/DepositUtils.js +16 -27
- package/dist/cjs/utils/DepositUtils.js.map +1 -1
- package/dist/cjs/utils/Multicall.d.ts +1 -0
- package/dist/cjs/utils/Multicall.js +55 -1
- package/dist/cjs/utils/Multicall.js.map +1 -1
- package/dist/cjs/utils/SpokeUtils.js +22 -22
- package/dist/cjs/utils/SpokeUtils.js.map +1 -1
- package/dist/esm/addressAggregator/adapters/index.d.ts +2 -1
- package/dist/esm/addressAggregator/adapters/index.js +4 -2
- package/dist/esm/addressAggregator/adapters/index.js.map +1 -1
- package/dist/esm/addressAggregator/adapters/risklabs.d.ts +10 -0
- package/dist/esm/addressAggregator/adapters/risklabs.js +35 -0
- package/dist/esm/addressAggregator/adapters/risklabs.js.map +1 -0
- package/dist/esm/addressAggregator/index.js +1 -1
- package/dist/esm/addressAggregator/index.js.map +1 -1
- package/dist/esm/clients/SpokePoolClient.d.ts +8 -8
- package/dist/esm/clients/SpokePoolClient.js +32 -45
- package/dist/esm/clients/SpokePoolClient.js.map +1 -1
- package/dist/esm/clients/mocks/MockSpokePoolClient.js +1 -7
- package/dist/esm/clients/mocks/MockSpokePoolClient.js.map +1 -1
- package/dist/esm/providers/solana/cachedRpcFactory.js +24 -8
- package/dist/esm/providers/solana/cachedRpcFactory.js.map +1 -1
- package/dist/esm/utils/DepositUtils.js +16 -27
- package/dist/esm/utils/DepositUtils.js.map +1 -1
- package/dist/esm/utils/Multicall.d.ts +10 -0
- package/dist/esm/utils/Multicall.js +64 -1
- package/dist/esm/utils/Multicall.js.map +1 -1
- package/dist/esm/utils/SpokeUtils.js +22 -22
- package/dist/esm/utils/SpokeUtils.js.map +1 -1
- package/dist/types/addressAggregator/adapters/index.d.ts +2 -1
- package/dist/types/addressAggregator/adapters/index.d.ts.map +1 -1
- package/dist/types/addressAggregator/adapters/risklabs.d.ts +11 -0
- package/dist/types/addressAggregator/adapters/risklabs.d.ts.map +1 -0
- package/dist/types/clients/SpokePoolClient.d.ts +8 -8
- package/dist/types/clients/SpokePoolClient.d.ts.map +1 -1
- package/dist/types/clients/mocks/MockSpokePoolClient.d.ts.map +1 -1
- package/dist/types/providers/solana/cachedRpcFactory.d.ts.map +1 -1
- package/dist/types/utils/DepositUtils.d.ts.map +1 -1
- package/dist/types/utils/Multicall.d.ts +10 -0
- package/dist/types/utils/Multicall.d.ts.map +1 -1
- package/dist/types/utils/SpokeUtils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/addressAggregator/adapters/index.ts +2 -1
- package/src/addressAggregator/adapters/risklabs.ts +27 -0
- package/src/addressAggregator/index.ts +1 -1
- package/src/clients/SpokePoolClient.ts +29 -45
- package/src/clients/mocks/MockSpokePoolClient.ts +0 -10
- package/src/providers/solana/cachedRpcFactory.ts +28 -14
- package/src/utils/DepositUtils.ts +18 -29
- package/src/utils/Multicall.ts +38 -1
- package/src/utils/SpokeUtils.ts +8 -14
|
@@ -7,16 +7,16 @@ import {
|
|
|
7
7
|
bnZero,
|
|
8
8
|
bnUint32Max,
|
|
9
9
|
DefaultLogLevels,
|
|
10
|
+
DepositSearchResult,
|
|
10
11
|
EventSearchConfig,
|
|
11
12
|
MAX_BIG_INT,
|
|
12
13
|
MakeOptional,
|
|
13
14
|
assign,
|
|
14
15
|
getRelayEventKey,
|
|
16
|
+
InvalidFill,
|
|
15
17
|
isDefined,
|
|
16
18
|
toBN,
|
|
17
|
-
bnOne,
|
|
18
19
|
getMessageHash,
|
|
19
|
-
isUnsafeDepositId,
|
|
20
20
|
isSlowFill,
|
|
21
21
|
isValidEvmAddress,
|
|
22
22
|
isZeroAddress,
|
|
@@ -56,8 +56,6 @@ import { getRepaymentChainId, forceDestinationRepayment } from "./BundleDataClie
|
|
|
56
56
|
type SpokePoolUpdateSuccess = {
|
|
57
57
|
success: true;
|
|
58
58
|
currentTime: number;
|
|
59
|
-
firstDepositId: BigNumber;
|
|
60
|
-
latestDepositId: BigNumber;
|
|
61
59
|
events: Log[][];
|
|
62
60
|
searchEndBlock: number;
|
|
63
61
|
};
|
|
@@ -85,10 +83,6 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
85
83
|
protected queryableEventNames: string[] = [];
|
|
86
84
|
protected configStoreClient: AcrossConfigStoreClient | undefined;
|
|
87
85
|
protected invalidFills: Set<string> = new Set();
|
|
88
|
-
public earliestDepositIdQueried = MAX_BIG_INT;
|
|
89
|
-
public latestDepositIdQueried = bnZero;
|
|
90
|
-
public firstDepositIdForSpokePool = MAX_BIG_INT;
|
|
91
|
-
public lastDepositIdForSpokePool = MAX_BIG_INT;
|
|
92
86
|
public fills: { [OriginChainId: number]: FillWithBlock[] } = {};
|
|
93
87
|
|
|
94
88
|
/**
|
|
@@ -518,16 +512,6 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
518
512
|
* @returns A Promise that resolves to a SpokePoolUpdate object.
|
|
519
513
|
*/
|
|
520
514
|
protected async _update(eventsToQuery: string[]): Promise<SpokePoolUpdate> {
|
|
521
|
-
// Find the earliest known depositId. This assumes no deposits were placed in the deployment block.
|
|
522
|
-
let firstDepositId = this.firstDepositIdForSpokePool;
|
|
523
|
-
if (firstDepositId.eq(MAX_BIG_INT)) {
|
|
524
|
-
firstDepositId = await this.spokePool.numberOfDeposits({ blockTag: this.deploymentBlock });
|
|
525
|
-
firstDepositId = BigNumber.from(firstDepositId); // Cast input to a big number.
|
|
526
|
-
if (!BigNumber.isBigNumber(firstDepositId) || firstDepositId.lt(bnZero)) {
|
|
527
|
-
throw new Error(`SpokePoolClient::update: Invalid first deposit id (${firstDepositId})`);
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
|
|
531
515
|
const searchConfig = await this.updateSearchConfig(this.spokePool.provider);
|
|
532
516
|
if (isUpdateFailureReason(searchConfig)) {
|
|
533
517
|
const reason = searchConfig;
|
|
@@ -562,7 +546,7 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
562
546
|
});
|
|
563
547
|
|
|
564
548
|
const timerStart = Date.now();
|
|
565
|
-
const multicallFunctions = ["getCurrentTime"
|
|
549
|
+
const multicallFunctions = ["getCurrentTime"];
|
|
566
550
|
const [multicallOutput, ...events] = await Promise.all([
|
|
567
551
|
spokePool.callStatic.multicall(
|
|
568
552
|
multicallFunctions.map((f) => spokePool.interface.encodeFunctionData(f)),
|
|
@@ -572,10 +556,9 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
572
556
|
]);
|
|
573
557
|
this.log("debug", `Time to query new events from RPC for ${this.chainId}: ${Date.now() - timerStart} ms`);
|
|
574
558
|
|
|
575
|
-
const [currentTime
|
|
559
|
+
const [currentTime] = multicallFunctions.map(
|
|
576
560
|
(fn, idx) => spokePool.interface.decodeFunctionResult(fn, multicallOutput[idx])[0]
|
|
577
561
|
);
|
|
578
|
-
const _latestDepositId = BigNumber.from(_numberOfDeposits).sub(bnOne);
|
|
579
562
|
|
|
580
563
|
if (!BigNumber.isBigNumber(currentTime) || currentTime.lt(this.currentTime)) {
|
|
581
564
|
const errMsg = BigNumber.isBigNumber(currentTime)
|
|
@@ -590,8 +573,6 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
590
573
|
return {
|
|
591
574
|
success: true,
|
|
592
575
|
currentTime: currentTime.toNumber(), // uint32
|
|
593
|
-
firstDepositId,
|
|
594
|
-
latestDepositId: _latestDepositId.gt(bnZero) ? _latestDepositId : bnZero,
|
|
595
576
|
searchEndBlock: searchConfig.toBlock,
|
|
596
577
|
events,
|
|
597
578
|
};
|
|
@@ -673,13 +654,6 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
673
654
|
continue;
|
|
674
655
|
}
|
|
675
656
|
assign(this.depositHashes, [getRelayEventKey(deposit)], deposit);
|
|
676
|
-
|
|
677
|
-
if (deposit.depositId.lt(this.earliestDepositIdQueried) && !isUnsafeDepositId(deposit.depositId)) {
|
|
678
|
-
this.earliestDepositIdQueried = deposit.depositId;
|
|
679
|
-
}
|
|
680
|
-
if (deposit.depositId.gt(this.latestDepositIdQueried) && !isUnsafeDepositId(deposit.depositId)) {
|
|
681
|
-
this.latestDepositIdQueried = deposit.depositId;
|
|
682
|
-
}
|
|
683
657
|
}
|
|
684
658
|
};
|
|
685
659
|
|
|
@@ -831,9 +805,7 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
831
805
|
|
|
832
806
|
// Next iteration should start off from where this one ended.
|
|
833
807
|
this.currentTime = currentTime;
|
|
834
|
-
this.firstDepositIdForSpokePool = update.firstDepositId;
|
|
835
808
|
this.latestBlockSearched = searchEndBlock;
|
|
836
|
-
this.lastDepositIdForSpokePool = update.latestDepositId;
|
|
837
809
|
this.firstBlockToSearch = searchEndBlock + 1;
|
|
838
810
|
this.eventSearchConfig.toBlock = undefined; // Caller can re-set on subsequent updates if necessary
|
|
839
811
|
this.isUpdated = true;
|
|
@@ -929,14 +901,27 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
929
901
|
return currentTime.toNumber();
|
|
930
902
|
}
|
|
931
903
|
|
|
932
|
-
|
|
904
|
+
/**
|
|
905
|
+
* For a given origin chain depositId, resolve the corresponding Deposit.
|
|
906
|
+
* Note: This method can only be used for depositIds within the non-deterministic range (0 < depositId < 2^32 - 1).
|
|
907
|
+
* @param depositId Deposit ID of the deposit to resolve.
|
|
908
|
+
* @returns A DepositSearchResult instance.
|
|
909
|
+
*/
|
|
910
|
+
async findDeposit(depositId: BigNumber): Promise<DepositSearchResult> {
|
|
911
|
+
let deposit = this.getDeposit(depositId);
|
|
912
|
+
if (deposit) {
|
|
913
|
+
return { found: true, deposit };
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// No deposit found; revert to searching for it.
|
|
933
917
|
const upperBound = this.latestBlockSearched || undefined; // Don't permit block 0 as the high block.
|
|
934
918
|
const fromBlock = await findDepositBlock(this.spokePool, depositId, this.deploymentBlock, upperBound);
|
|
919
|
+
const chain = getNetworkName(this.chainId);
|
|
935
920
|
if (!fromBlock) {
|
|
936
|
-
const
|
|
937
|
-
|
|
938
|
-
`
|
|
939
|
-
|
|
921
|
+
const reason =
|
|
922
|
+
`Unable to find ${chain} depositId ${depositId}` +
|
|
923
|
+
` within blocks [${this.deploymentBlock}, ${upperBound ?? "latest"}].`;
|
|
924
|
+
return { found: false, code: InvalidFill.DepositIdNotFound, reason };
|
|
940
925
|
}
|
|
941
926
|
|
|
942
927
|
const toBlock = fromBlock;
|
|
@@ -961,15 +946,14 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
961
946
|
|
|
962
947
|
const event = query.find(({ args }) => args["depositId"].eq(depositId));
|
|
963
948
|
if (event === undefined) {
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
);
|
|
949
|
+
return {
|
|
950
|
+
found: false,
|
|
951
|
+
code: InvalidFill.DepositIdNotFound,
|
|
952
|
+
reason: `${chain} depositId ${depositId} not found at block ${fromBlock}.`,
|
|
953
|
+
};
|
|
970
954
|
}
|
|
971
955
|
|
|
972
|
-
|
|
956
|
+
deposit = {
|
|
973
957
|
...spreadEventWithBlockNumber(event),
|
|
974
958
|
originChainId: this.chainId,
|
|
975
959
|
quoteBlockNumber: await this.getBlockNumber(Number(event.args["quoteTimestamp"])),
|
|
@@ -990,7 +974,7 @@ export class SpokePoolClient extends BaseAbstractClient {
|
|
|
990
974
|
elapsedMs: tStop - tStart,
|
|
991
975
|
});
|
|
992
976
|
|
|
993
|
-
return deposit;
|
|
977
|
+
return { found: true, deposit };
|
|
994
978
|
}
|
|
995
979
|
|
|
996
980
|
/**
|
|
@@ -24,7 +24,6 @@ import {
|
|
|
24
24
|
randomAddress,
|
|
25
25
|
BigNumber,
|
|
26
26
|
bnZero,
|
|
27
|
-
bnMax,
|
|
28
27
|
bnOne,
|
|
29
28
|
toAddress,
|
|
30
29
|
toBytes32,
|
|
@@ -107,19 +106,10 @@ export class MockSpokePoolClient extends SpokePoolClient {
|
|
|
107
106
|
}
|
|
108
107
|
});
|
|
109
108
|
|
|
110
|
-
// Update latestDepositIdQueried.
|
|
111
|
-
const idx = eventsToQuery.indexOf("V3FundsDeposited");
|
|
112
|
-
const latestDepositId = (events[idx] ?? []).reduce(
|
|
113
|
-
(depositId, event) => bnMax(depositId, event.args["depositId"] ?? bnZero),
|
|
114
|
-
this.latestDepositIdQueried
|
|
115
|
-
);
|
|
116
|
-
|
|
117
109
|
return Promise.resolve({
|
|
118
110
|
success: true,
|
|
119
111
|
firstDepositId: bnZero,
|
|
120
|
-
latestDepositId,
|
|
121
112
|
currentTime,
|
|
122
|
-
oldestTime: 0,
|
|
123
113
|
events,
|
|
124
114
|
searchEndBlock: this.eventSearchConfig.toBlock || latestBlockSearched,
|
|
125
115
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { RpcTransport, GetTransactionApi, RpcFromTransport, SolanaRpcApiFromTransport } from "@solana/kit";
|
|
2
|
+
import { getThrowSolanaErrorResponseTransformer } from "@solana/rpc-transformers";
|
|
2
3
|
import { is, object, optional, string, tuple } from "superstruct";
|
|
3
4
|
import { CachingMechanismInterface } from "../../interfaces";
|
|
4
5
|
import { SolanaClusterRpcFactory } from "./baseRpcFactories";
|
|
@@ -70,25 +71,38 @@ export class CachedSolanaRpcFactory extends SolanaClusterRpcFactory {
|
|
|
70
71
|
// Do not throw if params are not valid, just skip caching and pass through to the underlying transport.
|
|
71
72
|
if (!this.isGetTransactionParams(params)) return this.rateLimitedTransport<TResponse>(...args);
|
|
72
73
|
|
|
73
|
-
// Check the confirmation status first to avoid caching non-finalized transactions.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
// Check the confirmation status first to avoid caching non-finalized transactions. In case of null or errors just
|
|
75
|
+
// skip caching and pass through to the underlying transport.
|
|
76
|
+
try {
|
|
77
|
+
const getSignatureStatusesResponse = await this.rateLimitedRpcClient
|
|
78
|
+
.getSignatureStatuses([params[0]], {
|
|
79
|
+
searchTransactionHistory: true,
|
|
80
|
+
})
|
|
81
|
+
.send();
|
|
82
|
+
if (getSignatureStatusesResponse.value[0]?.confirmationStatus !== "finalized") {
|
|
83
|
+
return this.rateLimitedTransport<TResponse>(...args);
|
|
84
|
+
}
|
|
85
|
+
} catch (error) {
|
|
86
|
+
return this.rateLimitedTransport<TResponse>(...args);
|
|
87
|
+
}
|
|
79
88
|
|
|
80
89
|
const getTransactionResponse = await this.rateLimitedTransport<TResponse>(...args);
|
|
81
90
|
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
JSON.stringify(getTransactionResponse, jsonReplacerWithBigInts),
|
|
88
|
-
Number.POSITIVE_INFINITY
|
|
89
|
-
);
|
|
91
|
+
// Do not cache JSON-RPC error responses, let them pass through for the RPC client to handle.
|
|
92
|
+
try {
|
|
93
|
+
getThrowSolanaErrorResponseTransformer()(getTransactionResponse, { methodName: method, params });
|
|
94
|
+
} catch {
|
|
95
|
+
return getTransactionResponse;
|
|
90
96
|
}
|
|
91
97
|
|
|
98
|
+
// Cache the transaction JSON-RPC response as we checked the transaction is finalized and not an error.
|
|
99
|
+
const redisKey = this.buildRedisKey(method, params);
|
|
100
|
+
await this.redisClient?.set(
|
|
101
|
+
redisKey,
|
|
102
|
+
JSON.stringify(getTransactionResponse, jsonReplacerWithBigInts),
|
|
103
|
+
Number.POSITIVE_INFINITY
|
|
104
|
+
);
|
|
105
|
+
|
|
92
106
|
return getTransactionResponse;
|
|
93
107
|
}
|
|
94
108
|
|
|
@@ -60,40 +60,23 @@ export async function queryHistoricalDepositForFill(
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
const { depositId } = fill;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
({ earliestDepositIdQueried: lowId, latestDepositIdQueried: highId } = spokePoolClient);
|
|
73
|
-
if (depositId.gte(lowId) && depositId.lte(highId)) {
|
|
74
|
-
const originChain = getNetworkName(fill.originChainId);
|
|
75
|
-
const deposit = spokePoolClient.getDeposit(depositId);
|
|
76
|
-
if (isDefined(deposit)) {
|
|
77
|
-
const match = validateFillForDeposit(fill, deposit);
|
|
78
|
-
if (match.valid) {
|
|
79
|
-
return { found: true, deposit };
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return {
|
|
83
|
-
found: false,
|
|
84
|
-
code: InvalidFill.FillMismatch,
|
|
85
|
-
reason: `Fill for ${originChain} deposit ID ${depositId.toString()} is invalid (${match.reason}).`,
|
|
86
|
-
};
|
|
63
|
+
const originChain = getNetworkName(fill.originChainId);
|
|
64
|
+
let deposit = spokePoolClient.getDeposit(depositId);
|
|
65
|
+
if (isDefined(deposit)) {
|
|
66
|
+
const match = validateFillForDeposit(fill, deposit);
|
|
67
|
+
if (match.valid) {
|
|
68
|
+
return { found: true, deposit };
|
|
87
69
|
}
|
|
88
70
|
|
|
89
71
|
return {
|
|
90
72
|
found: false,
|
|
91
|
-
code: InvalidFill.
|
|
92
|
-
reason:
|
|
73
|
+
code: InvalidFill.FillMismatch,
|
|
74
|
+
reason: `Fill for ${originChain} deposit ID ${depositId.toString()} is invalid (${match.reason}).`,
|
|
93
75
|
};
|
|
94
76
|
}
|
|
95
77
|
|
|
96
|
-
|
|
78
|
+
// Deposit not found in SpokePoolClient buffer, search elsewhere.
|
|
79
|
+
let cachedDeposit: Deposit | undefined;
|
|
97
80
|
if (cache) {
|
|
98
81
|
cachedDeposit = await getDepositInCache(getDepositKey(fill), cache);
|
|
99
82
|
// We only want to warn and remove the cached deposit if it
|
|
@@ -116,17 +99,23 @@ export async function queryHistoricalDepositForFill(
|
|
|
116
99
|
if (isDefined(cachedDeposit)) {
|
|
117
100
|
deposit = cachedDeposit as DepositWithBlock;
|
|
118
101
|
} else {
|
|
119
|
-
|
|
102
|
+
const result = await spokePoolClient.findDeposit(fill.depositId);
|
|
103
|
+
if (!result.found) {
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
({ deposit } = result);
|
|
120
108
|
if (cache) {
|
|
121
109
|
await setDepositInCache(deposit, getCurrentTime(), cache, DEFAULT_CACHING_TTL);
|
|
122
110
|
}
|
|
123
111
|
}
|
|
112
|
+
assert(isDefined(deposit), `Unexpectedly failed to locate ${originChain} deposit ${fill.depositId}`);
|
|
124
113
|
|
|
125
114
|
deposit.messageHash ??= getMessageHash(deposit.message);
|
|
126
115
|
|
|
127
116
|
const match = validateFillForDeposit(fill, deposit);
|
|
128
117
|
if (match.valid) {
|
|
129
|
-
return { found: true, deposit };
|
|
118
|
+
return { found: true, deposit: deposit! };
|
|
130
119
|
}
|
|
131
120
|
|
|
132
121
|
return {
|
package/src/utils/Multicall.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import assert from "assert";
|
|
1
2
|
import { Contract, providers, Signer, utils as ethersUtils } from "ethers";
|
|
2
|
-
import { CHAIN_IDs } from "@across-protocol/constants";
|
|
3
|
+
import { CHAIN_IDs, MAINNET_CHAIN_IDs } from "@across-protocol/constants";
|
|
3
4
|
import { chainIsOPStack, hreNetworks } from "./NetworkUtils";
|
|
4
5
|
import { BigNumber } from "./BigNumberUtils";
|
|
5
6
|
import { Multicall3, Multicall3__factory } from "./abi/typechain";
|
|
@@ -16,6 +17,7 @@ export type Call3 = {
|
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
const DETERMINISTIC_MULTICALL_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
|
|
20
|
+
const MULTICALL_DEPLOYER = "0x05f32B3cC3888453ff71B01135B34FF8e41263F2";
|
|
19
21
|
|
|
20
22
|
const NON_DETERMINISTIC_MULTICALL_ADDRESSES = {
|
|
21
23
|
[CHAIN_IDs.ZK_SYNC]: "0xF9cda624FBC7e059355ce98a31693d299FACd963",
|
|
@@ -68,6 +70,10 @@ export async function aggregate(multicall3: Contract, calls: Call3[], blockTag?:
|
|
|
68
70
|
});
|
|
69
71
|
}
|
|
70
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Note that this function behaves unexpectedly on Arbitrum chains, where block.number
|
|
75
|
+
* corresponds to the approximate block number of the underlying chain.
|
|
76
|
+
*/
|
|
71
77
|
export async function blockAndAggregate(
|
|
72
78
|
multicall3: Contract,
|
|
73
79
|
calls: Call3[],
|
|
@@ -94,3 +100,34 @@ export async function blockAndAggregate(
|
|
|
94
100
|
|
|
95
101
|
return { blockNumber: blockNumber.toNumber(), returnData };
|
|
96
102
|
}
|
|
103
|
+
|
|
104
|
+
async function isDeployed(provider: providers.Provider): Promise<boolean> {
|
|
105
|
+
const { chainId } = await provider.getNetwork();
|
|
106
|
+
const expectedMulticallAddress = getMulticallAddress(chainId) ?? DETERMINISTIC_MULTICALL_ADDRESS;
|
|
107
|
+
const code = await provider.getCode(expectedMulticallAddress);
|
|
108
|
+
return code !== "0x";
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Deploy a Multicall3 instance.
|
|
113
|
+
* Guidance: https://github.com/mds1/multicall3?tab=readme-ov-file#new-deployments
|
|
114
|
+
* @param signer An Ethers Signer instance with at least 0.1 ETH.
|
|
115
|
+
*/
|
|
116
|
+
export async function deploy(signer: Signer) {
|
|
117
|
+
assert(signer.provider);
|
|
118
|
+
if (await isDeployed(signer.provider)) {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
const { chainId } = await signer.provider.getNetwork();
|
|
122
|
+
assert(!Object.values(MAINNET_CHAIN_IDs).includes(chainId));
|
|
123
|
+
|
|
124
|
+
await signer.sendTransaction({ to: MULTICALL_DEPLOYER, value: ethersUtils.parseEther("0.1") }); // Pre-fund the deployer address.
|
|
125
|
+
await signer.provider.sendTransaction(MULTICALL_CALLDATA); // Deploy
|
|
126
|
+
|
|
127
|
+
assert(await isDeployed(signer.provider));
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Multicall3 deployment transaction, pre-signed `deployer`.
|
|
132
|
+
const MULTICALL_CALLDATA =
|
|
133
|
+
"0xf90f538085174876e800830f42408080b90f00608060405234801561001057600080fd5b50610ee0806100206000396000f3fe6080604052600436106100f35760003560e01c80634d2301cc1161008a578063a8b0574e11610059578063a8b0574e1461025a578063bce38bd714610275578063c3077fa914610288578063ee82ac5e1461029b57600080fd5b80634d2301cc146101ec57806372425d9d1461022157806382ad56cb1461023457806386d516e81461024757600080fd5b80633408e470116100c65780633408e47014610191578063399542e9146101a45780633e64a696146101c657806342cbb15c146101d957600080fd5b80630f28c97d146100f8578063174dea711461011a578063252dba421461013a57806327e86d6e1461015b575b600080fd5b34801561010457600080fd5b50425b6040519081526020015b60405180910390f35b61012d610128366004610a85565b6102ba565b6040516101119190610bbe565b61014d610148366004610a85565b6104ef565b604051610111929190610bd8565b34801561016757600080fd5b50437fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0140610107565b34801561019d57600080fd5b5046610107565b6101b76101b2366004610c60565b610690565b60405161011193929190610cba565b3480156101d257600080fd5b5048610107565b3480156101e557600080fd5b5043610107565b3480156101f857600080fd5b50610107610207366004610ce2565b73ffffffffffffffffffffffffffffffffffffffff163190565b34801561022d57600080fd5b5044610107565b61012d610242366004610a85565b6106ab565b34801561025357600080fd5b5045610107565b34801561026657600080fd5b50604051418152602001610111565b61012d610283366004610c60565b61085a565b6101b7610296366004610a85565b610a1a565b3480156102a757600080fd5b506101076102b6366004610d18565b4090565b60606000828067ffffffffffffffff8111156102d8576102d8610d31565b60405190808252806020026020018201604052801561031e57816020015b6040805180820190915260008152606060208201528152602001906001900390816102f65790505b5092503660005b8281101561047757600085828151811061034157610341610d60565b6020026020010151905087878381811061035d5761035d610d60565b905060200281019061036f9190610d8f565b6040810135958601959093506103886020850185610ce2565b73ffffffffffffffffffffffffffffffffffffffff16816103ac6060870187610dcd565b6040516103ba929190610e32565b60006040518083038185875af1925050503d80600081146103f7576040519150601f19603f3d011682016040523d82523d6000602084013e6103fc565b606091505b50602080850191909152901515808452908501351761046d577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260846000fd5b5050600101610325565b508234146104e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d756c746963616c6c333a2076616c7565206d69736d6174636800000000000060448201526064015b60405180910390fd5b50505092915050565b436060828067ffffffffffffffff81111561050c5761050c610d31565b60405190808252806020026020018201604052801561053f57816020015b606081526020019060019003908161052a5790505b5091503660005b8281101561068657600087878381811061056257610562610d60565b90506020028101906105749190610e42565b92506105836020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166105a66020850185610dcd565b6040516105b4929190610e32565b6000604051808303816000865af19150503d80600081146105f1576040519150601f19603f3d011682016040523d82523d6000602084013e6105f6565b606091505b5086848151811061060957610609610d60565b602090810291909101015290508061067d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b50600101610546565b5050509250929050565b43804060606106a086868661085a565b905093509350939050565b6060818067ffffffffffffffff8111156106c7576106c7610d31565b60405190808252806020026020018201604052801561070d57816020015b6040805180820190915260008152606060208201528152602001906001900390816106e55790505b5091503660005b828110156104e657600084828151811061073057610730610d60565b6020026020010151905086868381811061074c5761074c610d60565b905060200281019061075e9190610e76565b925061076d6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166107906040850185610dcd565b60405161079e929190610e32565b6000604051808303816000865af19150503d80600081146107db576040519150601f19603f3d011682016040523d82523d6000602084013e6107e0565b606091505b506020808401919091529015158083529084013517610851577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260646000fd5b50600101610714565b6060818067ffffffffffffffff81111561087657610876610d31565b6040519080825280602002602001820160405280156108bc57816020015b6040805180820190915260008152606060208201528152602001906001900390816108945790505b5091503660005b82811015610a105760008482815181106108df576108df610d60565b602002602001015190508686838181106108fb576108fb610d60565b905060200281019061090d9190610e42565b925061091c6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff1661093f6020850185610dcd565b60405161094d929190610e32565b6000604051808303816000865af19150503d806000811461098a576040519150601f19603f3d011682016040523d82523d6000602084013e61098f565b606091505b506020830152151581528715610a07578051610a07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b506001016108c3565b5050509392505050565b6000806060610a2b60018686610690565b919790965090945092505050565b60008083601f840112610a4b57600080fd5b50813567ffffffffffffffff811115610a6357600080fd5b6020830191508360208260051b8501011115610a7e57600080fd5b9250929050565b60008060208385031215610a9857600080fd5b823567ffffffffffffffff811115610aaf57600080fd5b610abb85828601610a39565b90969095509350505050565b6000815180845260005b81811015610aed57602081850181015186830182015201610ad1565b81811115610aff576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600082825180855260208086019550808260051b84010181860160005b84811015610bb1578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001895281518051151584528401516040858501819052610b9d81860183610ac7565b9a86019a9450505090830190600101610b4f565b5090979650505050505050565b602081526000610bd16020830184610b32565b9392505050565b600060408201848352602060408185015281855180845260608601915060608160051b870101935082870160005b82811015610c52577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0888703018452610c40868351610ac7565b95509284019290840190600101610c06565b509398975050505050505050565b600080600060408486031215610c7557600080fd5b83358015158114610c8557600080fd5b9250602084013567ffffffffffffffff811115610ca157600080fd5b610cad86828701610a39565b9497909650939450505050565b838152826020820152606060408201526000610cd96060830184610b32565b95945050505050565b600060208284031215610cf457600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610bd157600080fd5b600060208284031215610d2a57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112610dc357600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610e0257600080fd5b83018035915067ffffffffffffffff821115610e1d57600080fd5b602001915036819003821315610a7e57600080fd5b8183823760009101908152919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112610dc357600080fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1833603018112610dc357600080fdfea2646970667358221220bb2b5c71a328032f97c676ae39a1ec2148d3e5d6f73d95e9b17910152d61f16264736f6c634300080c00331ca0edce47092c0f398cebf3ffc267f05c8e7076e3b89445e0fe50f6332273d4569ba01b0b9d000e19b24c5869b0fc3b22b0d6fa47cd63316875cbbd577d76e6fde086";
|
package/src/utils/SpokeUtils.ts
CHANGED
|
@@ -7,7 +7,6 @@ import { chunk } from "./ArrayUtils";
|
|
|
7
7
|
import { BigNumber, toBN, bnOne, bnZero } from "./BigNumberUtils";
|
|
8
8
|
import { keccak256 } from "./common";
|
|
9
9
|
import { isMessageEmpty } from "./DepositUtils";
|
|
10
|
-
import { blockAndAggregate, getMulticall3 } from "./Multicall";
|
|
11
10
|
import { isDefined } from "./TypeGuards";
|
|
12
11
|
import { getNetworkName } from "./NetworkUtils";
|
|
13
12
|
import { paginatedEventQuery, spreadEventWithBlockNumber } from "./EventUtils";
|
|
@@ -345,22 +344,17 @@ export async function findDepositBlock(
|
|
|
345
344
|
throw new Error(`Cannot binary search for depositId ${depositId}`);
|
|
346
345
|
}
|
|
347
346
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
const { chainId } = await spokePool.provider.getNetwork();
|
|
351
|
-
const multicall3 = getMulticall3(chainId, spokePool.provider);
|
|
352
|
-
assert(multicall3, `No multicall3 defined for chain ${chainId}`);
|
|
347
|
+
highBlock ??= await spokePool.provider.getBlockNumber();
|
|
348
|
+
assert(highBlock > lowBlock, `Block numbers out of range (${lowBlock} >= ${highBlock})`);
|
|
353
349
|
|
|
354
350
|
// Make sure the deposit occurred within the block range supplied by the caller.
|
|
355
|
-
const [
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
351
|
+
const [nDepositsLow, nDepositsHigh] = (
|
|
352
|
+
await Promise.all([
|
|
353
|
+
spokePool.numberOfDeposits({ blockTag: lowBlock }),
|
|
354
|
+
spokePool.numberOfDeposits({ blockTag: highBlock }),
|
|
355
|
+
])
|
|
356
|
+
).map((n) => toBN(n));
|
|
361
357
|
|
|
362
|
-
const nDepositsLow = toBN(_nDepositsLow);
|
|
363
|
-
const nDepositsHigh = toBN(returnData.at(0)!);
|
|
364
358
|
if (nDepositsLow.gt(depositId) || nDepositsHigh.lte(depositId)) {
|
|
365
359
|
return undefined; // Deposit did not occur within the specified block range.
|
|
366
360
|
}
|