@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.
Files changed (66) hide show
  1. package/dist/cjs/addressAggregator/adapters/index.d.ts +2 -1
  2. package/dist/cjs/addressAggregator/adapters/index.js +3 -2
  3. package/dist/cjs/addressAggregator/adapters/index.js.map +1 -1
  4. package/dist/cjs/addressAggregator/adapters/risklabs.d.ts +10 -0
  5. package/dist/cjs/addressAggregator/adapters/risklabs.js +38 -0
  6. package/dist/cjs/addressAggregator/adapters/risklabs.js.map +1 -0
  7. package/dist/cjs/addressAggregator/index.js +1 -1
  8. package/dist/cjs/addressAggregator/index.js.map +1 -1
  9. package/dist/cjs/clients/SpokePoolClient.d.ts +2 -8
  10. package/dist/cjs/clients/SpokePoolClient.js +25 -44
  11. package/dist/cjs/clients/SpokePoolClient.js.map +1 -1
  12. package/dist/cjs/clients/mocks/MockSpokePoolClient.js +0 -5
  13. package/dist/cjs/clients/mocks/MockSpokePoolClient.js.map +1 -1
  14. package/dist/cjs/providers/solana/cachedRpcFactory.js +23 -8
  15. package/dist/cjs/providers/solana/cachedRpcFactory.js.map +1 -1
  16. package/dist/cjs/utils/DepositUtils.js +16 -27
  17. package/dist/cjs/utils/DepositUtils.js.map +1 -1
  18. package/dist/cjs/utils/Multicall.d.ts +1 -0
  19. package/dist/cjs/utils/Multicall.js +55 -1
  20. package/dist/cjs/utils/Multicall.js.map +1 -1
  21. package/dist/cjs/utils/SpokeUtils.js +22 -22
  22. package/dist/cjs/utils/SpokeUtils.js.map +1 -1
  23. package/dist/esm/addressAggregator/adapters/index.d.ts +2 -1
  24. package/dist/esm/addressAggregator/adapters/index.js +4 -2
  25. package/dist/esm/addressAggregator/adapters/index.js.map +1 -1
  26. package/dist/esm/addressAggregator/adapters/risklabs.d.ts +10 -0
  27. package/dist/esm/addressAggregator/adapters/risklabs.js +35 -0
  28. package/dist/esm/addressAggregator/adapters/risklabs.js.map +1 -0
  29. package/dist/esm/addressAggregator/index.js +1 -1
  30. package/dist/esm/addressAggregator/index.js.map +1 -1
  31. package/dist/esm/clients/SpokePoolClient.d.ts +8 -8
  32. package/dist/esm/clients/SpokePoolClient.js +32 -45
  33. package/dist/esm/clients/SpokePoolClient.js.map +1 -1
  34. package/dist/esm/clients/mocks/MockSpokePoolClient.js +1 -7
  35. package/dist/esm/clients/mocks/MockSpokePoolClient.js.map +1 -1
  36. package/dist/esm/providers/solana/cachedRpcFactory.js +24 -8
  37. package/dist/esm/providers/solana/cachedRpcFactory.js.map +1 -1
  38. package/dist/esm/utils/DepositUtils.js +16 -27
  39. package/dist/esm/utils/DepositUtils.js.map +1 -1
  40. package/dist/esm/utils/Multicall.d.ts +10 -0
  41. package/dist/esm/utils/Multicall.js +64 -1
  42. package/dist/esm/utils/Multicall.js.map +1 -1
  43. package/dist/esm/utils/SpokeUtils.js +22 -22
  44. package/dist/esm/utils/SpokeUtils.js.map +1 -1
  45. package/dist/types/addressAggregator/adapters/index.d.ts +2 -1
  46. package/dist/types/addressAggregator/adapters/index.d.ts.map +1 -1
  47. package/dist/types/addressAggregator/adapters/risklabs.d.ts +11 -0
  48. package/dist/types/addressAggregator/adapters/risklabs.d.ts.map +1 -0
  49. package/dist/types/clients/SpokePoolClient.d.ts +8 -8
  50. package/dist/types/clients/SpokePoolClient.d.ts.map +1 -1
  51. package/dist/types/clients/mocks/MockSpokePoolClient.d.ts.map +1 -1
  52. package/dist/types/providers/solana/cachedRpcFactory.d.ts.map +1 -1
  53. package/dist/types/utils/DepositUtils.d.ts.map +1 -1
  54. package/dist/types/utils/Multicall.d.ts +10 -0
  55. package/dist/types/utils/Multicall.d.ts.map +1 -1
  56. package/dist/types/utils/SpokeUtils.d.ts.map +1 -1
  57. package/package.json +1 -1
  58. package/src/addressAggregator/adapters/index.ts +2 -1
  59. package/src/addressAggregator/adapters/risklabs.ts +27 -0
  60. package/src/addressAggregator/index.ts +1 -1
  61. package/src/clients/SpokePoolClient.ts +29 -45
  62. package/src/clients/mocks/MockSpokePoolClient.ts +0 -10
  63. package/src/providers/solana/cachedRpcFactory.ts +28 -14
  64. package/src/utils/DepositUtils.ts +18 -29
  65. package/src/utils/Multicall.ts +38 -1
  66. 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", "numberOfDeposits"];
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, _numberOfDeposits] = multicallFunctions.map(
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
- async findDeposit(depositId: BigNumber, destinationChainId: number): Promise<DepositWithBlock> {
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 chain = getNetworkName(this.chainId);
937
- throw new Error(
938
- `Unable to find ${chain} depositId ${depositId} within blocks [${this.deploymentBlock}, ${upperBound}]`
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
- const srcChain = getNetworkName(this.chainId);
965
- const dstChain = getNetworkName(destinationChainId);
966
- throw new Error(
967
- `Could not find deposit ${depositId.toString()} for ${dstChain} fill` +
968
- ` between ${srcChain} blocks [${fromBlock}, ${toBlock}]`
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
- const deposit = {
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
- const getSignatureStatusesResponse = await this.rateLimitedRpcClient
75
- .getSignatureStatuses([params[0]], {
76
- searchTransactionHistory: true,
77
- })
78
- .send();
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
- // Cache the transaction only if it is finalized.
83
- if (getSignatureStatusesResponse.value[0]?.confirmationStatus === "finalized") {
84
- const redisKey = this.buildRedisKey(method, params);
85
- await this.redisClient?.set(
86
- redisKey,
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
- let { firstDepositIdForSpokePool: lowId, lastDepositIdForSpokePool: highId } = spokePoolClient;
64
- if (depositId.lt(lowId) || depositId.gt(highId)) {
65
- return {
66
- found: false,
67
- code: InvalidFill.DepositIdInvalid,
68
- reason: `Deposit ID ${depositId.toString()} is outside of SpokePool bounds [${lowId},${highId}].`,
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.DepositIdNotFound,
92
- reason: `${originChain} deposit ID ${depositId.toString()} not found in SpokePoolClient event buffer.`,
73
+ code: InvalidFill.FillMismatch,
74
+ reason: `Fill for ${originChain} deposit ID ${depositId.toString()} is invalid (${match.reason}).`,
93
75
  };
94
76
  }
95
77
 
96
- let deposit: DepositWithBlock, cachedDeposit: Deposit | undefined;
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
- deposit = await spokePoolClient.findDeposit(fill.depositId, fill.destinationChainId);
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 {
@@ -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";
@@ -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
- // @todo this call can be optimised away by using multicall3.blockAndAggregate(..., { blockTag: highBlockNumber }).
349
- // This is left for future because the change is a bit complex, and multicall3 isn't supported in test.
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 [_nDepositsLow, { blockNumber: _highBlock, returnData }] = await Promise.all([
356
- spokePool.numberOfDeposits({ blockTag: lowBlock }),
357
- blockAndAggregate(multicall3, [{ contract: spokePool, method: "numberOfDeposits" }], highBlock),
358
- ]);
359
- highBlock = _highBlock;
360
- assert(highBlock > lowBlock, `Block numbers out of range (${lowBlock} >= ${highBlock})`);
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
  }