@across-protocol/sdk 4.3.57 → 4.3.59-alpha.1

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 (81) hide show
  1. package/dist/cjs/clients/BundleDataClient/BundleDataClient.js +39 -39
  2. package/dist/cjs/clients/BundleDataClient/BundleDataClient.js.map +1 -1
  3. package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js +6 -4
  4. package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
  5. package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js +5 -0
  6. package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
  7. package/dist/cjs/clients/BundleDataClient/utils/PoolRebalanceUtils.d.ts +1 -1
  8. package/dist/cjs/clients/BundleDataClient/utils/PoolRebalanceUtils.js +4 -3
  9. package/dist/cjs/clients/BundleDataClient/utils/PoolRebalanceUtils.js.map +1 -1
  10. package/dist/cjs/clients/HubPoolClient.d.ts +3 -9
  11. package/dist/cjs/clients/HubPoolClient.js +44 -68
  12. package/dist/cjs/clients/HubPoolClient.js.map +1 -1
  13. package/dist/cjs/clients/SpokePoolClient/EVMSpokePoolClient.d.ts +1 -0
  14. package/dist/cjs/clients/SpokePoolClient/EVMSpokePoolClient.js +48 -30
  15. package/dist/cjs/clients/SpokePoolClient/EVMSpokePoolClient.js.map +1 -1
  16. package/dist/cjs/clients/SpokePoolClient/SVMSpokePoolClient.js +7 -2
  17. package/dist/cjs/clients/SpokePoolClient/SVMSpokePoolClient.js.map +1 -1
  18. package/dist/cjs/clients/SpokePoolClient/SpokePoolClient.js +4 -3
  19. package/dist/cjs/clients/SpokePoolClient/SpokePoolClient.js.map +1 -1
  20. package/dist/cjs/clients/mocks/MockHubPoolClient.d.ts +2 -2
  21. package/dist/cjs/clients/mocks/MockHubPoolClient.js +1 -1
  22. package/dist/cjs/clients/mocks/MockHubPoolClient.js.map +1 -1
  23. package/dist/cjs/interfaces/SpokePool.d.ts +5 -0
  24. package/dist/cjs/utils/SpokeUtils.d.ts +5 -1
  25. package/dist/cjs/utils/SpokeUtils.js +67 -0
  26. package/dist/cjs/utils/SpokeUtils.js.map +1 -1
  27. package/dist/esm/clients/BundleDataClient/BundleDataClient.js +40 -40
  28. package/dist/esm/clients/BundleDataClient/BundleDataClient.js.map +1 -1
  29. package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js +6 -4
  30. package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
  31. package/dist/esm/clients/BundleDataClient/utils/FillUtils.js +6 -1
  32. package/dist/esm/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
  33. package/dist/esm/clients/BundleDataClient/utils/PoolRebalanceUtils.d.ts +1 -1
  34. package/dist/esm/clients/BundleDataClient/utils/PoolRebalanceUtils.js +3 -2
  35. package/dist/esm/clients/BundleDataClient/utils/PoolRebalanceUtils.js.map +1 -1
  36. package/dist/esm/clients/HubPoolClient.d.ts +3 -9
  37. package/dist/esm/clients/HubPoolClient.js +48 -74
  38. package/dist/esm/clients/HubPoolClient.js.map +1 -1
  39. package/dist/esm/clients/SpokePoolClient/EVMSpokePoolClient.d.ts +1 -0
  40. package/dist/esm/clients/SpokePoolClient/EVMSpokePoolClient.js +48 -30
  41. package/dist/esm/clients/SpokePoolClient/EVMSpokePoolClient.js.map +1 -1
  42. package/dist/esm/clients/SpokePoolClient/SVMSpokePoolClient.js +7 -2
  43. package/dist/esm/clients/SpokePoolClient/SVMSpokePoolClient.js.map +1 -1
  44. package/dist/esm/clients/SpokePoolClient/SpokePoolClient.js +4 -3
  45. package/dist/esm/clients/SpokePoolClient/SpokePoolClient.js.map +1 -1
  46. package/dist/esm/clients/mocks/MockHubPoolClient.d.ts +2 -2
  47. package/dist/esm/clients/mocks/MockHubPoolClient.js +1 -1
  48. package/dist/esm/clients/mocks/MockHubPoolClient.js.map +1 -1
  49. package/dist/esm/interfaces/SpokePool.d.ts +5 -0
  50. package/dist/esm/utils/SpokeUtils.d.ts +5 -1
  51. package/dist/esm/utils/SpokeUtils.js +72 -3
  52. package/dist/esm/utils/SpokeUtils.js.map +1 -1
  53. package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts.map +1 -1
  54. package/dist/types/clients/BundleDataClient/utils/DataworkerUtils.d.ts.map +1 -1
  55. package/dist/types/clients/BundleDataClient/utils/FillUtils.d.ts.map +1 -1
  56. package/dist/types/clients/BundleDataClient/utils/PoolRebalanceUtils.d.ts +1 -1
  57. package/dist/types/clients/BundleDataClient/utils/PoolRebalanceUtils.d.ts.map +1 -1
  58. package/dist/types/clients/HubPoolClient.d.ts +3 -9
  59. package/dist/types/clients/HubPoolClient.d.ts.map +1 -1
  60. package/dist/types/clients/SpokePoolClient/EVMSpokePoolClient.d.ts +1 -0
  61. package/dist/types/clients/SpokePoolClient/EVMSpokePoolClient.d.ts.map +1 -1
  62. package/dist/types/clients/SpokePoolClient/SVMSpokePoolClient.d.ts.map +1 -1
  63. package/dist/types/clients/SpokePoolClient/SpokePoolClient.d.ts.map +1 -1
  64. package/dist/types/clients/mocks/MockHubPoolClient.d.ts +2 -2
  65. package/dist/types/clients/mocks/MockHubPoolClient.d.ts.map +1 -1
  66. package/dist/types/interfaces/SpokePool.d.ts +5 -0
  67. package/dist/types/interfaces/SpokePool.d.ts.map +1 -1
  68. package/dist/types/utils/SpokeUtils.d.ts +5 -1
  69. package/dist/types/utils/SpokeUtils.d.ts.map +1 -1
  70. package/package.json +2 -2
  71. package/src/clients/BundleDataClient/BundleDataClient.ts +12 -10
  72. package/src/clients/BundleDataClient/utils/DataworkerUtils.ts +13 -20
  73. package/src/clients/BundleDataClient/utils/FillUtils.ts +7 -1
  74. package/src/clients/BundleDataClient/utils/PoolRebalanceUtils.ts +8 -2
  75. package/src/clients/HubPoolClient.ts +49 -80
  76. package/src/clients/SpokePoolClient/EVMSpokePoolClient.ts +54 -39
  77. package/src/clients/SpokePoolClient/SVMSpokePoolClient.ts +5 -0
  78. package/src/clients/SpokePoolClient/SpokePoolClient.ts +15 -11
  79. package/src/clients/mocks/MockHubPoolClient.ts +3 -3
  80. package/src/interfaces/SpokePool.ts +6 -0
  81. package/src/utils/SpokeUtils.ts +63 -2
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@across-protocol/sdk",
3
3
  "author": "UMA Team",
4
- "version": "4.3.57",
4
+ "version": "4.3.59-alpha.1",
5
5
  "license": "AGPL-3.0",
6
6
  "homepage": "https://docs.across.to/reference/sdk",
7
7
  "files": [
@@ -34,7 +34,7 @@
34
34
  "bump-version:minor": "yarn version --minor --no-git-tag-version --no-commit-hooks && git commit -m 'chore: bump version' ./package.json --no-verify",
35
35
  "bump-version:patch": "yarn version --patch --no-git-tag-version --no-commit-hooks && git commit -m 'chore: bump version' ./package.json --no-verify",
36
36
  "typechain": "typechain --target ethers-v5 --out-dir src/utils/abi/typechain 'src/utils/abi/contracts/*.json' && eslint --fix src/utils/abi/typechain && yarn prettier --write \"src/utils/abi/typechain/**/*.ts\"",
37
- "yalc:watch": "nodemon --watch src --ext ts,tsx,json,js,jsx --exec 'yalc push'"
37
+ "yalc:watch": "nodemon --watch src --ext ts,tsx,json,js,jsx --ignore src/utils/abi/ --exec 'yalc publish --push --changed'"
38
38
  },
39
39
  "lint-staged": {
40
40
  "*.ts": "yarn lint"
@@ -54,7 +54,7 @@ import {
54
54
  getRefundInformationFromFill,
55
55
  getRefundsFromBundle,
56
56
  getWidestPossibleExpectedBlockRange,
57
- isChainDisabled,
57
+ isChainDisabledAtBlock,
58
58
  prettyPrintV3SpokePoolEvents,
59
59
  V3DepositWithBlock,
60
60
  V3FillWithBlock,
@@ -761,8 +761,8 @@ export class BundleDataClient {
761
761
  throw new Error("HubPoolClient not updated");
762
762
  }
763
763
 
764
- const chainIds = this.clients.configStoreClient.getChainIdIndicesForBlock(blockRangesForChains[0][0]);
765
- const bundleEndBlockForMainnet = blockRangesForChains[0][1];
764
+ const [bundleStartBlockForMainnet, bundleEndBlockForMainnet] = blockRangesForChains[0];
765
+ const chainIds = this.clients.configStoreClient.getChainIdIndicesForBlock(bundleStartBlockForMainnet);
766
766
 
767
767
  if (blockRangesForChains.length > chainIds.length) {
768
768
  throw new Error(
@@ -786,11 +786,6 @@ export class BundleDataClient {
786
786
  // (2) the fill deadline has passed. We'll need to decrement running balances for these deposits on the
787
787
  // destination chain where the slow fill would have been executed.
788
788
 
789
- const _isChainDisabled = (chainId: number): boolean => {
790
- const blockRangeForChain = getBlockRangeForChain(blockRangesForChains, chainId, chainIds);
791
- return isChainDisabled(blockRangeForChain);
792
- };
793
-
794
789
  const _canCreateSlowFillLeaf = (deposit: DepositWithBlock): boolean => {
795
790
  return (
796
791
  // Cannot slow fill when input and output tokens are not equivalent.
@@ -826,7 +821,11 @@ export class BundleDataClient {
826
821
  // Infer chain ID's to load from number of block ranges passed in.
827
822
  const allChainIds = blockRangesForChains
828
823
  .map((_blockRange, index) => chainIds[index])
829
- .filter((chainId) => !_isChainDisabled(chainId) && spokePoolClients[chainId] !== undefined);
824
+ .filter(
825
+ (chainId) =>
826
+ !isChainDisabledAtBlock(chainId, bundleStartBlockForMainnet, this.clients.configStoreClient) &&
827
+ spokePoolClients[chainId] !== undefined
828
+ );
830
829
  allChainIds.forEach((chainId) => {
831
830
  const spokePoolClient = spokePoolClients[chainId];
832
831
  if (!spokePoolClient.isUpdated) {
@@ -1676,7 +1675,10 @@ export class BundleDataClient {
1676
1675
  (
1677
1676
  await mapAsync(chainIds, async (chainId, index) => {
1678
1677
  const blockRangeForChain = blockRangesForChains[index];
1679
- if (!isDefined(blockRangeForChain) || isChainDisabled(blockRangeForChain)) {
1678
+ if (
1679
+ !isDefined(blockRangeForChain) ||
1680
+ isChainDisabledAtBlock(chainId, blockRangesForChains[0][0], this.clients.configStoreClient)
1681
+ ) {
1680
1682
  return;
1681
1683
  }
1682
1684
  const [_startBlockForChain, _endBlockForChain] = blockRangeForChain;
@@ -399,21 +399,15 @@ export function _getMarginalRunningBalances(
399
399
  ([l2TokenAddress, { realizedLpFees: totalRealizedLpFee, totalRefundAmount }]) => {
400
400
  // If the repayment token and repayment chain ID do not map to a PoolRebalanceRoute graph, then
401
401
  // there are no relevant L1 running balances.
402
- if (
403
- !clients.hubPoolClient.l2TokenHasPoolRebalanceRoute(
404
- toAddressType(l2TokenAddress, repaymentChainId),
405
- repaymentChainId,
406
- mainnetBundleEndBlock
407
- )
408
- ) {
409
- chainWithRefundsOnly.add(repaymentChainId);
410
- return;
411
- }
412
402
  const l1Token = clients.hubPoolClient.getL1TokenForL2TokenAtBlock(
413
403
  toAddressType(l2TokenAddress, repaymentChainId),
414
404
  repaymentChainId,
415
405
  mainnetBundleEndBlock
416
406
  );
407
+ if (!l1Token) {
408
+ chainWithRefundsOnly.add(repaymentChainId);
409
+ return;
410
+ }
417
411
  const l1TokenAddr = l1Token.toNative();
418
412
  assert(l1Token.isEVM(), `Expected an EVM address: ${l1TokenAddr}`);
419
413
 
@@ -439,6 +433,9 @@ export function _getMarginalRunningBalances(
439
433
  destinationChainId,
440
434
  mainnetBundleEndBlock
441
435
  );
436
+
437
+ assert(isDefined(l1TokenCounterpart), "getRefundInformationFromFill: l1TokenCounterpart is undefined");
438
+
442
439
  const lpFee = deposit.lpFeePct.mul(deposit.inputAmount).div(fixedPointAdjustment);
443
440
  updateRunningBalance(
444
441
  runningBalances,
@@ -468,6 +465,8 @@ export function _getMarginalRunningBalances(
468
465
  destinationChainId,
469
466
  mainnetBundleEndBlock
470
467
  );
468
+ assert(isDefined(l1TokenCounterpart), "getRefundInformationFromFill: l1TokenCounterpart is undefined");
469
+
471
470
  const lpFee = deposit.lpFeePct.mul(deposit.inputAmount).div(fixedPointAdjustment);
472
471
  updateRunningBalance(
473
472
  runningBalances,
@@ -523,21 +522,15 @@ export function _getMarginalRunningBalances(
523
522
  deposits.forEach((deposit) => {
524
523
  // If the repayment token and repayment chain ID do not map to a PoolRebalanceRoute graph, then
525
524
  // there are no relevant L1 running balances.
526
- if (
527
- !clients.hubPoolClient.l2TokenHasPoolRebalanceRoute(
528
- deposit.inputToken,
529
- deposit.originChainId,
530
- mainnetBundleEndBlock
531
- )
532
- ) {
533
- chainWithRefundsOnly.add(deposit.originChainId);
534
- return;
535
- }
536
525
  const l1TokenCounterpart = clients.hubPoolClient.getL1TokenForL2TokenAtBlock(
537
526
  toAddressType(inputToken, originChainId),
538
527
  originChainId,
539
528
  mainnetBundleEndBlock
540
529
  );
530
+ if (!l1TokenCounterpart) {
531
+ chainWithRefundsOnly.add(deposit.originChainId);
532
+ return;
533
+ }
541
534
  updateRunningBalance(runningBalances, originChainId, l1TokenCounterpart.toEvmAddress(), deposit.inputAmount);
542
535
  });
543
536
  });
@@ -37,18 +37,21 @@ export function getRefundInformationFromFill(
37
37
 
38
38
  // Now figure out the equivalent L2 token for the repayment token. If the inputToken doesn't have a
39
39
  // PoolRebalanceRoute, then the repayment chain would have been the originChainId after the getRepaymentChainId()
40
- // call and we would have returned already, so the following call should always succeed.
40
+ // call and we would have returned already, so the following call should always return a valid L1 token.
41
41
  const l1TokenCounterpart = hubPoolClient.getL1TokenForL2TokenAtBlock(
42
42
  relayData.inputToken,
43
43
  relayData.originChainId,
44
44
  bundleEndBlockForMainnet
45
45
  );
46
46
 
47
+ assert(isDefined(l1TokenCounterpart), "getRefundInformationFromFill: l1TokenCounterpart is undefined");
48
+
47
49
  const repaymentToken = hubPoolClient.getL2TokenForL1TokenAtBlock(
48
50
  l1TokenCounterpart,
49
51
  chainToSendRefundTo,
50
52
  bundleEndBlockForMainnet
51
53
  );
54
+ assert(isDefined(repaymentToken), "getRefundInformationFromFill: repaymentToken is undefined");
52
55
 
53
56
  return {
54
57
  chainToSendRefundTo,
@@ -183,6 +186,9 @@ function _repaymentChainTokenIsValid(
183
186
  relayData.originChainId,
184
187
  bundleEndBlockForMainnet
185
188
  );
189
+ if (!l1TokenCounterpart) {
190
+ return false;
191
+ }
186
192
  if (
187
193
  !hubPoolClient.l2TokenEnabledForL1TokenAtBlock(
188
194
  l1TokenCounterpart,
@@ -98,8 +98,12 @@ export async function getWidestPossibleExpectedBlockRange(
98
98
  });
99
99
  }
100
100
 
101
- export function isChainDisabled(blockRangeForChain: number[]): boolean {
102
- return blockRangeForChain[0] === blockRangeForChain[1];
101
+ export function isChainDisabledAtBlock(
102
+ chainId: number,
103
+ mainnetBlock: number,
104
+ configStoreClient: AcrossConfigStoreClient
105
+ ): boolean {
106
+ return configStoreClient.getDisabledChainsForBlock(mainnetBlock).includes(chainId);
103
107
  }
104
108
 
105
109
  // Note: this function computes the intended transfer amount before considering the transfer threshold.
@@ -202,6 +206,8 @@ export function updateRunningBalanceForDeposit(
202
206
  deposit.originChainId,
203
207
  mainnetBundleEndBlock
204
208
  );
209
+ assert(isDefined(l1TokenCounterpart), "updateRunningBalanceForDeposit: l1TokenCounterpart is undefined");
210
+
205
211
  updateRunningBalance(runningBalances, deposit.originChainId, l1TokenCounterpart.toEvmAddress(), updateAmount);
206
212
  }
207
213
 
@@ -34,7 +34,6 @@ import {
34
34
  fetchTokenInfo,
35
35
  getCachedBlockForTimestamp,
36
36
  getCurrentTime,
37
- getNetworkName,
38
37
  isDefined,
39
38
  mapAsync,
40
39
  paginatedEventQuery,
@@ -75,17 +74,11 @@ type HubPoolEvent =
75
74
  | "RootBundleExecuted"
76
75
  | "CrossChainContractsSet";
77
76
 
78
- type L1TokensToDestinationTokens = {
79
- [l1Token: string]: { [destinationChainId: number]: Address };
80
- };
81
-
82
77
  export type LpFeeRequest = Pick<Deposit, "originChainId" | "inputToken" | "inputAmount" | "quoteTimestamp"> & {
83
78
  paymentChainId?: number;
84
79
  };
85
80
 
86
81
  export class HubPoolClient extends BaseAbstractClient {
87
- // L1Token -> destinationChainId -> destinationToken
88
- protected l1TokensToDestinationTokens: L1TokensToDestinationTokens = {};
89
82
  protected l1Tokens: L1TokenInfo[] = []; // L1Tokens and their associated info.
90
83
  // @dev `token` here is a 20-byte hex sting
91
84
  protected lpTokens: { [token: string]: LpToken } = {};
@@ -192,24 +185,16 @@ export class HubPoolClient extends BaseAbstractClient {
192
185
  l1Token: EvmAddress,
193
186
  destinationChainId: number,
194
187
  latestHubBlock = Number.MAX_SAFE_INTEGER
195
- ): Address {
188
+ ): Address | undefined {
196
189
  if (!this.l1TokensToDestinationTokensWithBlock?.[l1Token.toNative()]?.[destinationChainId]) {
197
- const chain = getNetworkName(destinationChainId);
198
- const { symbol } = this.l1Tokens.find(({ address }) => address.eq(l1Token)) ?? { symbol: l1Token.toString() };
199
- throw new Error(`Could not find SpokePool mapping for ${symbol} on ${chain} and L1 token ${l1Token}`);
190
+ return undefined;
200
191
  }
201
192
  // Find the last mapping published before the target block.
202
- const l2Token: DestinationTokenWithBlock | undefined = sortEventsDescending(
203
- this.l1TokensToDestinationTokensWithBlock[l1Token.toNative()][destinationChainId]
204
- ).find((mapping: DestinationTokenWithBlock) => mapping.blockNumber <= latestHubBlock);
205
- if (!l2Token) {
206
- const chain = getNetworkName(destinationChainId);
207
- const { symbol } = this.l1Tokens.find(({ address }) => address.eq(l1Token)) ?? { symbol: l1Token.toString() };
208
- throw new Error(
209
- `Could not find SpokePool mapping for ${symbol} on ${chain} at or before HubPool block ${latestHubBlock}!`
210
- );
211
- }
212
- return l2Token.l2Token;
193
+ const l2Token: DestinationTokenWithBlock | undefined = this.l1TokensToDestinationTokensWithBlock[
194
+ l1Token.toNative()
195
+ ][destinationChainId].find((mapping: DestinationTokenWithBlock) => mapping.blockNumber <= latestHubBlock);
196
+
197
+ return !isDefined(l2Token) || l2Token.l2Token.isZeroAddress() ? undefined : l2Token.l2Token;
213
198
  }
214
199
 
215
200
  // Returns the latest L1 token to use for an L2 token as of the input hub block.
@@ -217,32 +202,27 @@ export class HubPoolClient extends BaseAbstractClient {
217
202
  l2Token: Address,
218
203
  destinationChainId: number,
219
204
  latestHubBlock = Number.MAX_SAFE_INTEGER
220
- ): EvmAddress {
221
- const l2Tokens = Object.keys(this.l1TokensToDestinationTokensWithBlock)
222
- .filter((l1Token) => this.l2TokenEnabledForL1Token(EvmAddress.from(l1Token), destinationChainId))
223
- .map((l1Token) => {
224
- // Return all matching L2 token mappings that are equal to or earlier than the target block.
225
- // @dev Since tokens on L2s (like Solana) can have 32 byte addresses, filter on the lower 20 bytes of the token only.
226
- return this.l1TokensToDestinationTokensWithBlock[l1Token][destinationChainId].filter(
227
- (dstTokenWithBlock) =>
228
- dstTokenWithBlock.l2Token.truncateToBytes20() === l2Token.truncateToBytes20() &&
229
- dstTokenWithBlock.blockNumber <= latestHubBlock
230
- );
231
- })
232
- .flat();
233
- if (l2Tokens.length === 0) {
234
- const chain = getNetworkName(destinationChainId);
235
- throw new Error(
236
- `Could not find HubPool mapping for ${l2Token} on ${chain} at or before HubPool block ${latestHubBlock}!`
205
+ ): EvmAddress | undefined {
206
+ const l2Tokens = Object.keys(this.l1TokensToDestinationTokensWithBlock).flatMap((l1Token) => {
207
+ // Get the latest L2 token mapping for the given L1 token.
208
+ // @dev Since tokens on L2s (like Solana) can have 32 byte addresses, filter on the lower 20 bytes of the token only.
209
+ const sortedL2Tokens = sortEventsDescending(
210
+ (this.l1TokensToDestinationTokensWithBlock[l1Token][destinationChainId] ?? []).filter(
211
+ (dstTokenWithBlock) => dstTokenWithBlock.blockNumber <= latestHubBlock
212
+ )
237
213
  );
238
- }
239
- // Find the last mapping published before the target block.
240
- return sortEventsDescending(l2Tokens)[0].l1Token;
214
+ // If the latest L2 token mapping is equal to the target L2 token, return it.
215
+ return sortedL2Tokens.length > 0 && sortedL2Tokens[0].l2Token.truncateToBytes20() === l2Token.truncateToBytes20()
216
+ ? sortedL2Tokens[0]
217
+ : [];
218
+ });
219
+
220
+ return l2Tokens.length === 0 ? undefined : sortEventsDescending(l2Tokens)[0].l1Token;
241
221
  }
242
222
 
243
223
  protected getL1TokenForDeposit(
244
224
  deposit: Pick<DepositWithBlock, "originChainId" | "inputToken" | "quoteBlockNumber">
245
- ): EvmAddress {
225
+ ): EvmAddress | undefined {
246
226
  // L1-->L2 token mappings are set via PoolRebalanceRoutes which occur on mainnet,
247
227
  // so we use the latest token mapping. This way if a very old deposit is filled, the relayer can use the
248
228
  // latest L2 token mapping to find the L1 token counterpart.
@@ -250,7 +230,7 @@ export class HubPoolClient extends BaseAbstractClient {
250
230
  }
251
231
 
252
232
  l2TokenEnabledForL1Token(l1Token: EvmAddress, destinationChainId: number): boolean {
253
- return this.l1TokensToDestinationTokens?.[l1Token.toNative()]?.[destinationChainId] != undefined;
233
+ return this.l2TokenEnabledForL1TokenAtBlock(l1Token, destinationChainId, Number.MAX_SAFE_INTEGER);
254
234
  }
255
235
 
256
236
  l2TokenEnabledForL1TokenAtBlock(l1Token: EvmAddress, destinationChainId: number, hubBlockNumber: number): boolean {
@@ -258,21 +238,12 @@ export class HubPoolClient extends BaseAbstractClient {
258
238
  const l2Token: DestinationTokenWithBlock | undefined = sortEventsDescending(
259
239
  this.l1TokensToDestinationTokensWithBlock?.[l1Token.toNative()]?.[destinationChainId] ?? []
260
240
  ).find((mapping: DestinationTokenWithBlock) => mapping.blockNumber <= hubBlockNumber);
261
- return l2Token !== undefined;
241
+ return isDefined(l2Token) && !l2Token.l2Token.isZeroAddress();
262
242
  }
263
243
 
264
244
  l2TokenHasPoolRebalanceRoute(l2Token: Address, l2ChainId: number, hubPoolBlock = this.latestHeightSearched): boolean {
265
- return Object.values(this.l1TokensToDestinationTokensWithBlock).some((destinationTokenMapping) => {
266
- return Object.entries(destinationTokenMapping).some(([_l2ChainId, setPoolRebalanceRouteEvents]) => {
267
- return setPoolRebalanceRouteEvents.some((e) => {
268
- return (
269
- e.blockNumber <= hubPoolBlock &&
270
- e.l2Token.truncateToBytes20() === l2Token.truncateToBytes20() &&
271
- Number(_l2ChainId) === l2ChainId
272
- );
273
- });
274
- });
275
- });
245
+ const l1Token = this.getL1TokenForL2TokenAtBlock(l2Token, l2ChainId, hubPoolBlock);
246
+ return isDefined(l1Token);
276
247
  }
277
248
 
278
249
  /**
@@ -401,11 +372,11 @@ export class HubPoolClient extends BaseAbstractClient {
401
372
  const hubPoolTokens: { [k: string]: EvmAddress } = {};
402
373
  const getHubPoolToken = (deposit: LpFeeRequest, quoteBlockNumber: number): EvmAddress | undefined => {
403
374
  const tokenKey = `${deposit.originChainId}-${deposit.inputToken}`;
404
- if (this.l2TokenHasPoolRebalanceRoute(deposit.inputToken, deposit.originChainId, quoteBlockNumber)) {
405
- return (hubPoolTokens[tokenKey] ??= this.getL1TokenForDeposit({ ...deposit, quoteBlockNumber }));
375
+ const l1Token = this.getL1TokenForDeposit({ ...deposit, quoteBlockNumber });
376
+ if (!isDefined(l1Token)) {
377
+ return undefined;
406
378
  }
407
-
408
- return undefined;
379
+ return (hubPoolTokens[tokenKey] ??= l1Token);
409
380
  };
410
381
 
411
382
  // Filter hubPoolTokens for duplicates by reverting to their native string
@@ -553,14 +524,14 @@ export class HubPoolClient extends BaseAbstractClient {
553
524
  // Resolve both SpokePool tokens back to their respective HubPool tokens and verify that they match.
554
525
  const l1TokenA = this.getL1TokenForL2TokenAtBlock(tokenA, chainIdA, hubPoolBlock);
555
526
  const l1TokenB = this.getL1TokenForL2TokenAtBlock(tokenB, chainIdB, hubPoolBlock);
556
- if (!l1TokenA.eq(l1TokenB)) {
527
+ if (!isDefined(l1TokenA) || !isDefined(l1TokenB) || !l1TokenA.eq(l1TokenB)) {
557
528
  return false;
558
529
  }
559
530
 
560
531
  // Resolve both HubPool tokens back to a current SpokePool token and verify that they match.
561
532
  const _tokenA = this.getL2TokenForL1TokenAtBlock(l1TokenA, chainIdA, hubPoolBlock);
562
533
  const _tokenB = this.getL2TokenForL1TokenAtBlock(l1TokenB, chainIdB, hubPoolBlock);
563
- return tokenA.eq(_tokenA) && tokenB.eq(_tokenB);
534
+ return isDefined(_tokenA) && isDefined(_tokenB) && tokenA.eq(_tokenA) && tokenB.eq(_tokenB);
564
535
  }
565
536
 
566
537
  getSpokeActivationBlockForChain(chainId: number): number {
@@ -1001,24 +972,22 @@ export class HubPoolClient extends BaseAbstractClient {
1001
972
  destinationToken = svmUsdc;
1002
973
  }
1003
974
 
1004
- // If the destination token is set to the zero address in an event, then this means Across should no longer
1005
- // rebalance to this chain.
1006
- if (!destinationToken.isZeroAddress()) {
1007
- assign(this.l1TokensToDestinationTokens, [args.l1Token, args.destinationChainId], destinationToken);
1008
- assign(
1009
- this.l1TokensToDestinationTokensWithBlock,
1010
- [args.l1Token, args.destinationChainId],
1011
- [
1012
- {
1013
- l1Token: EvmAddress.from(args.l1Token),
1014
- l2Token: destinationToken,
1015
- blockNumber: args.blockNumber,
1016
- txnIndex: args.txnIndex,
1017
- logIndex: args.logIndex,
1018
- txnRef: args.txnRef,
1019
- },
1020
- ]
1021
- );
975
+ const newRoute: DestinationTokenWithBlock = {
976
+ l1Token: EvmAddress.from(args.l1Token),
977
+ l2Token: destinationToken,
978
+ blockNumber: args.blockNumber,
979
+ txnIndex: args.txnIndex,
980
+ logIndex: args.logIndex,
981
+ txnRef: args.txnRef,
982
+ };
983
+ if (this.l1TokensToDestinationTokensWithBlock[args.l1Token]?.[args.destinationChainId]) {
984
+ // Events are most likely coming in descending orders already but just in case we sort them again.
985
+ this.l1TokensToDestinationTokensWithBlock[args.l1Token][args.destinationChainId] = sortEventsDescending([
986
+ ...this.l1TokensToDestinationTokensWithBlock[args.l1Token][args.destinationChainId],
987
+ newRoute,
988
+ ]);
989
+ } else {
990
+ assign(this.l1TokensToDestinationTokensWithBlock, [args.l1Token, args.destinationChainId], [newRoute]);
1022
991
  }
1023
992
  }
1024
993
  }
@@ -7,7 +7,7 @@ import {
7
7
  relayFillStatus,
8
8
  getTimestampForBlock as _getTimestampForBlock,
9
9
  } from "../../arch/evm";
10
- import { DepositWithBlock, FillStatus, RelayData } from "../../interfaces";
10
+ import { DepositWithBlock, FillStatus, Log, RelayData } from "../../interfaces";
11
11
  import {
12
12
  BigNumber,
13
13
  DepositSearchResult,
@@ -155,45 +155,14 @@ export class EVMSpokePoolClient extends SpokePoolClient {
155
155
  }
156
156
 
157
157
  // No deposit found; revert to searching for it.
158
- const upperBound = this.latestHeightSearched || undefined; // Don't permit block 0 as the high block.
159
- const from = await findDepositBlock(this.spokePool, depositId, this.deploymentBlock, upperBound);
160
- const chain = getNetworkName(this.chainId);
161
- if (!from) {
162
- const reason =
163
- `Unable to find ${chain} depositId ${depositId}` +
164
- ` within blocks [${this.deploymentBlock}, ${upperBound ?? "latest"}].`;
165
- return { found: false, code: InvalidFill.DepositIdNotFound, reason };
166
- }
158
+ const result = await this.queryDepositEvents(depositId);
167
159
 
168
- const to = from;
169
- const tStart = Date.now();
170
- // Check both V3FundsDeposited and FundsDeposited events to look for a specified depositId.
171
- const { maxLookBack } = this.eventSearchConfig;
172
- const query = (
173
- await Promise.all([
174
- paginatedEventQuery(
175
- this.spokePool,
176
- this.spokePool.filters.V3FundsDeposited(null, null, null, null, null, depositId),
177
- { from, to, maxLookBack }
178
- ),
179
- paginatedEventQuery(
180
- this.spokePool,
181
- this.spokePool.filters.FundsDeposited(null, null, null, null, null, depositId),
182
- { from, to, maxLookBack }
183
- ),
184
- ])
185
- ).flat();
186
- const tStop = Date.now();
187
-
188
- const event = query.find(({ args }) => args["depositId"].eq(depositId));
189
- if (event === undefined) {
190
- return {
191
- found: false,
192
- code: InvalidFill.DepositIdNotFound,
193
- reason: `${chain} depositId ${depositId} not found at block ${from}.`,
194
- };
160
+ if ("reason" in result) {
161
+ return { found: false, code: InvalidFill.DepositIdNotFound, reason: result.reason };
195
162
  }
196
163
 
164
+ const { event, elapsedMs } = result;
165
+
197
166
  const partialDeposit = unpackDepositEvent(spreadEventWithBlockNumber(event), this.chainId);
198
167
  const quoteBlockNumber = await this.getBlockNumber(partialDeposit.quoteTimestamp);
199
168
  const outputToken = partialDeposit.outputToken.isZeroAddress()
@@ -212,13 +181,59 @@ export class EVMSpokePoolClient extends SpokePoolClient {
212
181
  at: "SpokePoolClient#findDeposit",
213
182
  message: "Located deposit outside of SpokePoolClient's search range",
214
183
  deposit,
215
- elapsedMs: tStop - tStart,
184
+ elapsedMs,
216
185
  });
217
-
218
186
  return { found: true, deposit };
219
187
  }
220
188
 
221
189
  public override getTimestampForBlock(blockNumber: number): Promise<number> {
222
190
  return _getTimestampForBlock(this.spokePool.provider, blockNumber);
223
191
  }
192
+
193
+ private async queryDepositEvents(
194
+ depositId: BigNumber
195
+ ): Promise<{ event: Log; elapsedMs: number } | { reason: string }> {
196
+ const tStart = Date.now();
197
+ const upperBound = this.latestHeightSearched || undefined;
198
+ const from = await findDepositBlock(this.spokePool, depositId, this.deploymentBlock, upperBound);
199
+ const chain = getNetworkName(this.chainId);
200
+
201
+ if (!from) {
202
+ return {
203
+ reason: `Unable to find ${chain} depositId ${depositId} within blocks [${this.deploymentBlock}, ${
204
+ upperBound ?? "latest"
205
+ }].`,
206
+ };
207
+ }
208
+
209
+ const to = from;
210
+
211
+ const { maxLookBack } = this.eventSearchConfig;
212
+ const events = (
213
+ await Promise.all([
214
+ paginatedEventQuery(
215
+ this.spokePool,
216
+ this.spokePool.filters.V3FundsDeposited(null, null, null, null, null, depositId),
217
+ { from, to, maxLookBack }
218
+ ),
219
+ paginatedEventQuery(
220
+ this.spokePool,
221
+ this.spokePool.filters.FundsDeposited(null, null, null, null, null, depositId),
222
+ { from, to, maxLookBack }
223
+ ),
224
+ ])
225
+ )
226
+ .flat()
227
+ .filter(({ args }) => args["depositId"].eq(depositId));
228
+
229
+ const tStop = Date.now();
230
+ const [event] = events;
231
+ if (!event) {
232
+ return {
233
+ reason: `Unable to find ${chain} depositId ${depositId} within blocks [${from}, ${upperBound ?? "latest"}].`,
234
+ };
235
+ }
236
+
237
+ return { event, elapsedMs: tStop - tStart };
238
+ }
224
239
  }
@@ -218,6 +218,11 @@ export class SVMSpokePoolClient extends SpokePoolClient {
218
218
  * Finds a deposit based on its deposit ID on the SVM chain.
219
219
  */
220
220
  public async findDeposit(depositId: BigNumber): Promise<DepositSearchResult> {
221
+ // First check memory for deposits
222
+ const memoryDeposit = this.getDeposit(depositId);
223
+ if (memoryDeposit) {
224
+ return { found: true, deposit: memoryDeposit };
225
+ }
221
226
  const deposit = await findDeposit(this.svmEventsClient, depositId, this.logger);
222
227
  if (!deposit) {
223
228
  return {
@@ -474,18 +474,22 @@ export abstract class SpokePoolClient extends BaseAbstractClient {
474
474
  )
475
475
  ) {
476
476
  return false;
477
- } else {
478
- const l1Token = this.hubPoolClient?.getL1TokenForL2TokenAtBlock(
479
- deposit.inputToken,
480
- deposit.originChainId,
481
- deposit.quoteBlockNumber
482
- );
483
- return this.hubPoolClient.l2TokenEnabledForL1TokenAtBlock(
484
- l1Token,
485
- deposit.destinationChainId,
486
- deposit.quoteBlockNumber
487
- );
488
477
  }
478
+
479
+ const l1Token = this.hubPoolClient?.getL1TokenForL2TokenAtBlock(
480
+ deposit.inputToken,
481
+ deposit.originChainId,
482
+ deposit.quoteBlockNumber
483
+ );
484
+ if (!l1Token) {
485
+ return false;
486
+ }
487
+
488
+ return this.hubPoolClient.l2TokenEnabledForL1TokenAtBlock(
489
+ l1Token,
490
+ deposit.destinationChainId,
491
+ deposit.quoteBlockNumber
492
+ );
489
493
  }
490
494
 
491
495
  /**
@@ -118,9 +118,9 @@ export class MockHubPoolClient extends HubPoolClient {
118
118
  delete this.spokePoolTokens[l1Token]?.[chainId];
119
119
  }
120
120
 
121
- getL1TokenForL2TokenAtBlock(l2Token: Address, chainId: number, blockNumber: number): EvmAddress {
121
+ getL1TokenForL2TokenAtBlock(l2Token: Address, chainId: number, blockNumber: number): EvmAddress | undefined {
122
122
  const l1Token = Object.keys(this.spokePoolTokens).find(
123
- (l1Token) => this.spokePoolTokens[l1Token]?.[chainId].eq(l2Token)
123
+ (l1Token) => this.spokePoolTokens[l1Token]?.[chainId]?.eq(l2Token)
124
124
  );
125
125
  if (isDefined(l1Token)) {
126
126
  return EvmAddress.from(l1Token);
@@ -129,7 +129,7 @@ export class MockHubPoolClient extends HubPoolClient {
129
129
  }
130
130
  }
131
131
 
132
- getL2TokenForL1TokenAtBlock(l1Token: EvmAddress, chainId: number, blockNumber: number): Address {
132
+ getL2TokenForL1TokenAtBlock(l1Token: EvmAddress, chainId: number, blockNumber: number): Address | undefined {
133
133
  const l2Token = this.spokePoolTokens[l1Token.toEvmAddress()]?.[chainId];
134
134
  return l2Token ?? super.getL2TokenForL1TokenAtBlock(l1Token, chainId, blockNumber);
135
135
  }
@@ -175,3 +175,9 @@ export interface SpokePoolClientsByChain {
175
175
  export interface RelayDataWithMessageHash extends RelayData {
176
176
  messageHash?: string;
177
177
  }
178
+
179
+ export interface InvalidFill {
180
+ fill: FillWithBlock;
181
+ reason: string;
182
+ deposit?: DepositWithBlock;
183
+ }