@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.
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.js +39 -39
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js +6 -4
- package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js +5 -0
- package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/PoolRebalanceUtils.d.ts +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/PoolRebalanceUtils.js +4 -3
- package/dist/cjs/clients/BundleDataClient/utils/PoolRebalanceUtils.js.map +1 -1
- package/dist/cjs/clients/HubPoolClient.d.ts +3 -9
- package/dist/cjs/clients/HubPoolClient.js +44 -68
- package/dist/cjs/clients/HubPoolClient.js.map +1 -1
- package/dist/cjs/clients/SpokePoolClient/EVMSpokePoolClient.d.ts +1 -0
- package/dist/cjs/clients/SpokePoolClient/EVMSpokePoolClient.js +48 -30
- package/dist/cjs/clients/SpokePoolClient/EVMSpokePoolClient.js.map +1 -1
- package/dist/cjs/clients/SpokePoolClient/SVMSpokePoolClient.js +7 -2
- package/dist/cjs/clients/SpokePoolClient/SVMSpokePoolClient.js.map +1 -1
- package/dist/cjs/clients/SpokePoolClient/SpokePoolClient.js +4 -3
- package/dist/cjs/clients/SpokePoolClient/SpokePoolClient.js.map +1 -1
- package/dist/cjs/clients/mocks/MockHubPoolClient.d.ts +2 -2
- package/dist/cjs/clients/mocks/MockHubPoolClient.js +1 -1
- package/dist/cjs/clients/mocks/MockHubPoolClient.js.map +1 -1
- package/dist/cjs/interfaces/SpokePool.d.ts +5 -0
- package/dist/cjs/utils/SpokeUtils.d.ts +5 -1
- package/dist/cjs/utils/SpokeUtils.js +67 -0
- package/dist/cjs/utils/SpokeUtils.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js +40 -40
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js +6 -4
- package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/FillUtils.js +6 -1
- package/dist/esm/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/PoolRebalanceUtils.d.ts +1 -1
- package/dist/esm/clients/BundleDataClient/utils/PoolRebalanceUtils.js +3 -2
- package/dist/esm/clients/BundleDataClient/utils/PoolRebalanceUtils.js.map +1 -1
- package/dist/esm/clients/HubPoolClient.d.ts +3 -9
- package/dist/esm/clients/HubPoolClient.js +48 -74
- package/dist/esm/clients/HubPoolClient.js.map +1 -1
- package/dist/esm/clients/SpokePoolClient/EVMSpokePoolClient.d.ts +1 -0
- package/dist/esm/clients/SpokePoolClient/EVMSpokePoolClient.js +48 -30
- package/dist/esm/clients/SpokePoolClient/EVMSpokePoolClient.js.map +1 -1
- package/dist/esm/clients/SpokePoolClient/SVMSpokePoolClient.js +7 -2
- package/dist/esm/clients/SpokePoolClient/SVMSpokePoolClient.js.map +1 -1
- package/dist/esm/clients/SpokePoolClient/SpokePoolClient.js +4 -3
- package/dist/esm/clients/SpokePoolClient/SpokePoolClient.js.map +1 -1
- package/dist/esm/clients/mocks/MockHubPoolClient.d.ts +2 -2
- package/dist/esm/clients/mocks/MockHubPoolClient.js +1 -1
- package/dist/esm/clients/mocks/MockHubPoolClient.js.map +1 -1
- package/dist/esm/interfaces/SpokePool.d.ts +5 -0
- package/dist/esm/utils/SpokeUtils.d.ts +5 -1
- package/dist/esm/utils/SpokeUtils.js +72 -3
- package/dist/esm/utils/SpokeUtils.js.map +1 -1
- package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts.map +1 -1
- package/dist/types/clients/BundleDataClient/utils/DataworkerUtils.d.ts.map +1 -1
- package/dist/types/clients/BundleDataClient/utils/FillUtils.d.ts.map +1 -1
- package/dist/types/clients/BundleDataClient/utils/PoolRebalanceUtils.d.ts +1 -1
- package/dist/types/clients/BundleDataClient/utils/PoolRebalanceUtils.d.ts.map +1 -1
- package/dist/types/clients/HubPoolClient.d.ts +3 -9
- package/dist/types/clients/HubPoolClient.d.ts.map +1 -1
- package/dist/types/clients/SpokePoolClient/EVMSpokePoolClient.d.ts +1 -0
- package/dist/types/clients/SpokePoolClient/EVMSpokePoolClient.d.ts.map +1 -1
- package/dist/types/clients/SpokePoolClient/SVMSpokePoolClient.d.ts.map +1 -1
- package/dist/types/clients/SpokePoolClient/SpokePoolClient.d.ts.map +1 -1
- package/dist/types/clients/mocks/MockHubPoolClient.d.ts +2 -2
- package/dist/types/clients/mocks/MockHubPoolClient.d.ts.map +1 -1
- package/dist/types/interfaces/SpokePool.d.ts +5 -0
- package/dist/types/interfaces/SpokePool.d.ts.map +1 -1
- package/dist/types/utils/SpokeUtils.d.ts +5 -1
- package/dist/types/utils/SpokeUtils.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/clients/BundleDataClient/BundleDataClient.ts +12 -10
- package/src/clients/BundleDataClient/utils/DataworkerUtils.ts +13 -20
- package/src/clients/BundleDataClient/utils/FillUtils.ts +7 -1
- package/src/clients/BundleDataClient/utils/PoolRebalanceUtils.ts +8 -2
- package/src/clients/HubPoolClient.ts +49 -80
- package/src/clients/SpokePoolClient/EVMSpokePoolClient.ts +54 -39
- package/src/clients/SpokePoolClient/SVMSpokePoolClient.ts +5 -0
- package/src/clients/SpokePoolClient/SpokePoolClient.ts +15 -11
- package/src/clients/mocks/MockHubPoolClient.ts +3 -3
- package/src/interfaces/SpokePool.ts +6 -0
- 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.
|
|
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
|
-
|
|
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
|
|
765
|
-
const
|
|
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(
|
|
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 (
|
|
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
|
|
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
|
|
102
|
-
|
|
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
|
-
|
|
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 =
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
240
|
-
|
|
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.
|
|
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
|
|
241
|
+
return isDefined(l2Token) && !l2Token.l2Token.isZeroAddress();
|
|
262
242
|
}
|
|
263
243
|
|
|
264
244
|
l2TokenHasPoolRebalanceRoute(l2Token: Address, l2ChainId: number, hubPoolBlock = this.latestHeightSearched): boolean {
|
|
265
|
-
|
|
266
|
-
|
|
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
|
-
|
|
405
|
-
|
|
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
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
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
|
|
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
|
-
|
|
169
|
-
|
|
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
|
|
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]
|
|
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
|
+
}
|