@across-protocol/sdk 4.1.46-beta.0 → 4.1.46-beta.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/AcrossConfigStoreClient/AcrossConfigStoreClient.d.ts +3 -3
- package/dist/cjs/clients/AcrossConfigStoreClient/AcrossConfigStoreClient.js +15 -11
- package/dist/cjs/clients/AcrossConfigStoreClient/AcrossConfigStoreClient.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.d.ts +0 -2
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.js +75 -47
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js +10 -1
- package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/FillUtils.d.ts +7 -7
- package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js +48 -37
- 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 +2 -2
- package/dist/cjs/clients/BundleDataClient/utils/PoolRebalanceUtils.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/SuperstructUtils.d.ts +48 -20
- package/dist/cjs/clients/BundleDataClient/utils/SuperstructUtils.js +8 -6
- package/dist/cjs/clients/BundleDataClient/utils/SuperstructUtils.js.map +1 -1
- package/dist/cjs/clients/HubPoolClient.d.ts +3 -2
- package/dist/cjs/clients/HubPoolClient.js +65 -32
- package/dist/cjs/clients/HubPoolClient.js.map +1 -1
- package/dist/cjs/clients/SpokePoolClient/EVMSpokePoolClient.js +1 -1
- package/dist/cjs/clients/SpokePoolClient/EVMSpokePoolClient.js.map +1 -1
- package/dist/cjs/clients/SpokePoolClient/SpokePoolClient.d.ts +1 -0
- package/dist/cjs/clients/SpokePoolClient/SpokePoolClient.js +20 -6
- package/dist/cjs/clients/SpokePoolClient/SpokePoolClient.js.map +1 -1
- package/dist/cjs/clients/mocks/MockConfigStoreClient.js +2 -2
- package/dist/cjs/clients/mocks/MockConfigStoreClient.js.map +1 -1
- package/dist/cjs/clients/mocks/MockHubPoolClient.d.ts +3 -0
- package/dist/cjs/clients/mocks/MockHubPoolClient.js +35 -0
- package/dist/cjs/clients/mocks/MockHubPoolClient.js.map +1 -1
- package/dist/cjs/clients/mocks/MockSpokePoolClient.js +5 -5
- package/dist/cjs/clients/mocks/MockSpokePoolClient.js.map +1 -1
- package/dist/cjs/interfaces/Common.d.ts +2 -2
- package/dist/cjs/utils/AddressUtils.d.ts +1 -0
- package/dist/cjs/utils/AddressUtils.js +3 -0
- package/dist/cjs/utils/AddressUtils.js.map +1 -1
- package/dist/cjs/utils/DepositUtils.d.ts +1 -0
- package/dist/cjs/utils/DepositUtils.js +5 -1
- package/dist/cjs/utils/DepositUtils.js.map +1 -1
- package/dist/cjs/utils/EventUtils.d.ts +2 -1
- package/dist/cjs/utils/EventUtils.js +21 -12
- package/dist/cjs/utils/EventUtils.js.map +1 -1
- package/dist/esm/clients/AcrossConfigStoreClient/AcrossConfigStoreClient.d.ts +3 -3
- package/dist/esm/clients/AcrossConfigStoreClient/AcrossConfigStoreClient.js +15 -11
- package/dist/esm/clients/AcrossConfigStoreClient/AcrossConfigStoreClient.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/BundleDataClient.d.ts +0 -2
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js +82 -52
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js +16 -1
- package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/FillUtils.d.ts +28 -7
- package/dist/esm/clients/BundleDataClient/utils/FillUtils.js +84 -51
- 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 +2 -2
- package/dist/esm/clients/BundleDataClient/utils/PoolRebalanceUtils.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/SuperstructUtils.d.ts +48 -20
- package/dist/esm/clients/BundleDataClient/utils/SuperstructUtils.js +5 -3
- package/dist/esm/clients/BundleDataClient/utils/SuperstructUtils.js.map +1 -1
- package/dist/esm/clients/HubPoolClient.d.ts +3 -16
- package/dist/esm/clients/HubPoolClient.js +77 -52
- package/dist/esm/clients/HubPoolClient.js.map +1 -1
- package/dist/esm/clients/SpokePoolClient/EVMSpokePoolClient.js +2 -2
- package/dist/esm/clients/SpokePoolClient/EVMSpokePoolClient.js.map +1 -1
- package/dist/esm/clients/SpokePoolClient/SpokePoolClient.d.ts +1 -0
- package/dist/esm/clients/SpokePoolClient/SpokePoolClient.js +29 -15
- package/dist/esm/clients/SpokePoolClient/SpokePoolClient.js.map +1 -1
- package/dist/esm/clients/mocks/MockConfigStoreClient.js +3 -3
- package/dist/esm/clients/mocks/MockConfigStoreClient.js.map +1 -1
- package/dist/esm/clients/mocks/MockHubPoolClient.d.ts +3 -0
- package/dist/esm/clients/mocks/MockHubPoolClient.js +35 -0
- package/dist/esm/clients/mocks/MockHubPoolClient.js.map +1 -1
- package/dist/esm/clients/mocks/MockSpokePoolClient.js +5 -5
- package/dist/esm/clients/mocks/MockSpokePoolClient.js.map +1 -1
- package/dist/esm/interfaces/Common.d.ts +2 -2
- package/dist/esm/utils/AddressUtils.d.ts +1 -0
- package/dist/esm/utils/AddressUtils.js +3 -0
- package/dist/esm/utils/AddressUtils.js.map +1 -1
- package/dist/esm/utils/DepositUtils.d.ts +1 -0
- package/dist/esm/utils/DepositUtils.js +5 -1
- package/dist/esm/utils/DepositUtils.js.map +1 -1
- package/dist/esm/utils/EventUtils.d.ts +2 -1
- package/dist/esm/utils/EventUtils.js +18 -10
- package/dist/esm/utils/EventUtils.js.map +1 -1
- package/dist/types/clients/AcrossConfigStoreClient/AcrossConfigStoreClient.d.ts +3 -3
- package/dist/types/clients/AcrossConfigStoreClient/AcrossConfigStoreClient.d.ts.map +1 -1
- package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts +0 -2
- 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 +28 -7
- 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/BundleDataClient/utils/SuperstructUtils.d.ts +48 -20
- package/dist/types/clients/BundleDataClient/utils/SuperstructUtils.d.ts.map +1 -1
- package/dist/types/clients/HubPoolClient.d.ts +3 -16
- package/dist/types/clients/HubPoolClient.d.ts.map +1 -1
- package/dist/types/clients/SpokePoolClient/EVMSpokePoolClient.d.ts.map +1 -1
- package/dist/types/clients/SpokePoolClient/SpokePoolClient.d.ts +1 -0
- package/dist/types/clients/SpokePoolClient/SpokePoolClient.d.ts.map +1 -1
- package/dist/types/clients/mocks/MockConfigStoreClient.d.ts.map +1 -1
- package/dist/types/clients/mocks/MockHubPoolClient.d.ts +3 -0
- package/dist/types/clients/mocks/MockHubPoolClient.d.ts.map +1 -1
- package/dist/types/interfaces/Common.d.ts +2 -2
- package/dist/types/interfaces/Common.d.ts.map +1 -1
- package/dist/types/utils/AddressUtils.d.ts +1 -0
- package/dist/types/utils/AddressUtils.d.ts.map +1 -1
- package/dist/types/utils/DepositUtils.d.ts +1 -0
- package/dist/types/utils/DepositUtils.d.ts.map +1 -1
- package/dist/types/utils/EventUtils.d.ts +2 -1
- package/dist/types/utils/EventUtils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/clients/AcrossConfigStoreClient/AcrossConfigStoreClient.ts +17 -15
- package/src/clients/BundleDataClient/BundleDataClient.ts +144 -62
- package/src/clients/BundleDataClient/utils/DataworkerUtils.ts +36 -1
- package/src/clients/BundleDataClient/utils/FillUtils.ts +132 -72
- package/src/clients/BundleDataClient/utils/PoolRebalanceUtils.ts +3 -2
- package/src/clients/BundleDataClient/utils/SuperstructUtils.ts +7 -3
- package/src/clients/HubPoolClient.ts +97 -62
- package/src/clients/SpokePoolClient/EVMSpokePoolClient.ts +2 -1
- package/src/clients/SpokePoolClient/SpokePoolClient.ts +55 -17
- package/src/clients/mocks/MockConfigStoreClient.ts +10 -3
- package/src/clients/mocks/MockHubPoolClient.ts +40 -0
- package/src/clients/mocks/MockSpokePoolClient.ts +5 -5
- package/src/interfaces/Common.ts +2 -2
- package/src/utils/AddressUtils.ts +4 -0
- package/src/utils/DepositUtils.ts +6 -1
- package/src/utils/EventUtils.ts +19 -13
|
@@ -146,6 +146,13 @@ export function _buildPoolRebalanceRoot(
|
|
|
146
146
|
const repaymentChainId = Number(_repaymentChainId);
|
|
147
147
|
Object.entries(fillsForChain).forEach(
|
|
148
148
|
([l2TokenAddress, { realizedLpFees: totalRealizedLpFee, totalRefundAmount }]) => {
|
|
149
|
+
// If the repayment token and repayment chain ID do not map to a PoolRebalanceRoute graph, then
|
|
150
|
+
// there are no relevant L1 running balances.
|
|
151
|
+
if (
|
|
152
|
+
!clients.hubPoolClient.l2TokenHasPoolRebalanceRoute(l2TokenAddress, repaymentChainId, mainnetBundleEndBlock)
|
|
153
|
+
) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
149
156
|
const l1TokenCounterpart = clients.hubPoolClient.getL1TokenForL2TokenAtBlock(
|
|
150
157
|
l2TokenAddress,
|
|
151
158
|
repaymentChainId,
|
|
@@ -215,7 +222,24 @@ export function _buildPoolRebalanceRoot(
|
|
|
215
222
|
Object.entries(bundleV3Deposits).forEach(([, depositsForChain]) => {
|
|
216
223
|
Object.entries(depositsForChain).forEach(([, deposits]) => {
|
|
217
224
|
deposits.forEach((deposit) => {
|
|
218
|
-
|
|
225
|
+
// If the repayment token and repayment chain ID do not map to a PoolRebalanceRoute graph, then
|
|
226
|
+
// there are no relevant L1 running balances.
|
|
227
|
+
if (
|
|
228
|
+
!clients.hubPoolClient.l2TokenHasPoolRebalanceRoute(
|
|
229
|
+
deposit.inputToken,
|
|
230
|
+
deposit.originChainId,
|
|
231
|
+
mainnetBundleEndBlock
|
|
232
|
+
)
|
|
233
|
+
) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
updateRunningBalanceForDeposit(
|
|
237
|
+
runningBalances,
|
|
238
|
+
clients.hubPoolClient,
|
|
239
|
+
deposit,
|
|
240
|
+
deposit.inputAmount.mul(-1),
|
|
241
|
+
mainnetBundleEndBlock
|
|
242
|
+
);
|
|
219
243
|
});
|
|
220
244
|
});
|
|
221
245
|
});
|
|
@@ -229,6 +253,17 @@ export function _buildPoolRebalanceRoot(
|
|
|
229
253
|
const originChainId = Number(_originChainId);
|
|
230
254
|
Object.entries(depositsForChain).forEach(([inputToken, deposits]) => {
|
|
231
255
|
deposits.forEach((deposit) => {
|
|
256
|
+
// If the repayment token and repayment chain ID do not map to a PoolRebalanceRoute graph, then
|
|
257
|
+
// there are no relevant L1 running balances.
|
|
258
|
+
if (
|
|
259
|
+
!clients.hubPoolClient.l2TokenHasPoolRebalanceRoute(
|
|
260
|
+
deposit.inputToken,
|
|
261
|
+
deposit.originChainId,
|
|
262
|
+
mainnetBundleEndBlock
|
|
263
|
+
)
|
|
264
|
+
) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
232
267
|
const l1TokenCounterpart = clients.hubPoolClient.getL1TokenForL2TokenAtBlock(
|
|
233
268
|
inputToken,
|
|
234
269
|
originChainId,
|
|
@@ -1,124 +1,118 @@
|
|
|
1
1
|
import _ from "lodash";
|
|
2
|
+
import assert from "assert";
|
|
2
3
|
import { providers } from "ethers";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
4
|
+
import { DepositWithBlock, Fill, FillWithBlock } from "../../../interfaces";
|
|
5
|
+
import { isSlowFill, isValidEvmAddress, isDefined, chainIsEvm } from "../../../utils";
|
|
5
6
|
import { HubPoolClient } from "../../HubPoolClient";
|
|
6
7
|
|
|
8
|
+
/**
|
|
9
|
+
* @notice FillRepaymentInformation is a fill with additional properties required to determine where it can
|
|
10
|
+
* be repaid.
|
|
11
|
+
*/
|
|
12
|
+
type FillRepaymentInformation = Fill & { fromLiteChain: boolean };
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @notice Return repayment chain and repayment token for a fill, but does not verify if the returned
|
|
16
|
+
* repayment information is valid for the desired repayment address.
|
|
17
|
+
* @dev The passed in fill should be verified first via verifyFillRepayment().
|
|
18
|
+
* @param fill The fill to get the repayment information for.
|
|
19
|
+
* @return The chain to send the refund to and the token to use for the refund.
|
|
20
|
+
*/
|
|
7
21
|
export function getRefundInformationFromFill(
|
|
8
|
-
|
|
22
|
+
relayData: FillRepaymentInformation,
|
|
9
23
|
hubPoolClient: HubPoolClient,
|
|
10
|
-
|
|
11
|
-
chainIdListForBundleEvaluationBlockNumbers: number[],
|
|
12
|
-
fromLiteChain: boolean
|
|
24
|
+
bundleEndBlockForMainnet: number
|
|
13
25
|
): {
|
|
14
26
|
chainToSendRefundTo: number;
|
|
15
27
|
repaymentToken: string;
|
|
16
28
|
} {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (fromLiteChain) {
|
|
24
|
-
chainToSendRefundTo = fill.originChainId;
|
|
29
|
+
const chainToSendRefundTo = _getRepaymentChainId(relayData, hubPoolClient, bundleEndBlockForMainnet);
|
|
30
|
+
if (chainToSendRefundTo === relayData.originChainId) {
|
|
31
|
+
return {
|
|
32
|
+
chainToSendRefundTo,
|
|
33
|
+
repaymentToken: relayData.inputToken,
|
|
34
|
+
};
|
|
25
35
|
}
|
|
26
36
|
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
hubPoolClient.chainId,
|
|
31
|
-
chainIdListForBundleEvaluationBlockNumbers
|
|
32
|
-
)[1];
|
|
33
|
-
|
|
37
|
+
// Now figure out the equivalent L2 token for the repayment token. If the inputToken doesn't have a
|
|
38
|
+
// PoolRebalanceRoute, then the repayment chain would have been the originChainId after the getRepaymentChainId()
|
|
39
|
+
// call and we would have returned already, so the following call should always succeed.
|
|
34
40
|
const l1TokenCounterpart = hubPoolClient.getL1TokenForL2TokenAtBlock(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
relayData.inputToken,
|
|
42
|
+
relayData.originChainId,
|
|
43
|
+
bundleEndBlockForMainnet
|
|
38
44
|
);
|
|
39
45
|
|
|
40
46
|
const repaymentToken = hubPoolClient.getL2TokenForL1TokenAtBlock(
|
|
41
47
|
l1TokenCounterpart,
|
|
42
48
|
chainToSendRefundTo,
|
|
43
|
-
|
|
49
|
+
bundleEndBlockForMainnet
|
|
44
50
|
);
|
|
45
51
|
return {
|
|
46
52
|
chainToSendRefundTo,
|
|
47
53
|
repaymentToken,
|
|
48
54
|
};
|
|
49
55
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
): boolean {
|
|
61
|
-
if (!matchedDeposit.fromLiteChain) {
|
|
62
|
-
try {
|
|
63
|
-
const l1TokenCounterpart = hubPoolClient.getL1TokenForL2TokenAtBlock(
|
|
64
|
-
matchedDeposit.inputToken,
|
|
65
|
-
matchedDeposit.originChainId,
|
|
66
|
-
matchedDeposit.quoteBlockNumber
|
|
67
|
-
);
|
|
68
|
-
hubPoolClient.getL2TokenForL1TokenAtBlock(l1TokenCounterpart, repaymentChainId, matchedDeposit.quoteBlockNumber);
|
|
69
|
-
// Repayment token could be found, this is a valid repayment chain.
|
|
70
|
-
return false;
|
|
71
|
-
} catch {
|
|
72
|
-
// Repayment token doesn't exist on repayment chain via PoolRebalanceRoutes, impossible to repay filler there.
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
75
|
-
} else {
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Verify that a fill sent to an EVM chain has a 20 byte address. If the fill does not, then attempt
|
|
81
|
-
// to repay the `msg.sender` of the relay transaction. Otherwise, return undefined.
|
|
56
|
+
/**
|
|
57
|
+
* @notice Verifies that the fill can be repaid. If the repayment address is not
|
|
58
|
+
* valid for the requested repayment chain, then this function will attempt to change the fill's repayment chain
|
|
59
|
+
* to the destination chain and its repayment address to the msg.sender and if this is possible,
|
|
60
|
+
* return the fill. Otherwise, return undefined.
|
|
61
|
+
* @param _fill Fill with a requested repayment chain and address
|
|
62
|
+
* @return Fill with the applied repayment chain (depends on the validity of the requested repayment address)
|
|
63
|
+
* and applied repayment address, or undefined if the applied repayment address is not valid for the
|
|
64
|
+
* applied repayment chain.
|
|
65
|
+
*/
|
|
82
66
|
export async function verifyFillRepayment(
|
|
83
67
|
_fill: FillWithBlock,
|
|
84
68
|
destinationChainProvider: providers.Provider,
|
|
85
69
|
matchedDeposit: DepositWithBlock,
|
|
86
|
-
hubPoolClient: HubPoolClient
|
|
70
|
+
hubPoolClient: HubPoolClient,
|
|
71
|
+
bundleEndBlockForMainnet: number
|
|
87
72
|
): Promise<FillWithBlock | undefined> {
|
|
88
|
-
const fill =
|
|
73
|
+
const fill = {
|
|
74
|
+
..._.cloneDeep(_fill),
|
|
75
|
+
fromLiteChain: matchedDeposit.fromLiteChain,
|
|
76
|
+
};
|
|
89
77
|
|
|
90
78
|
// Slow fills don't result in repayments so they're always valid.
|
|
91
79
|
if (isSlowFill(fill)) {
|
|
92
80
|
return fill;
|
|
93
81
|
}
|
|
94
82
|
|
|
95
|
-
let repaymentChainId =
|
|
96
|
-
|
|
97
|
-
// If repayment chain doesn't have a Pool Rebalance Route for the input token, then change the repayment
|
|
98
|
-
// chain to the destination chain.
|
|
99
|
-
if (forceDestinationRepayment(repaymentChainId, matchedDeposit, hubPoolClient)) {
|
|
100
|
-
repaymentChainId = fill.destinationChainId;
|
|
101
|
-
}
|
|
83
|
+
let repaymentChainId = _getRepaymentChainId(fill, hubPoolClient, bundleEndBlockForMainnet);
|
|
102
84
|
|
|
103
|
-
|
|
85
|
+
// Repayments will always go to the fill.relayer address so check if its a valid EVM address. If its not, attempt
|
|
86
|
+
// to change it to the msg.sender of the FilledRelay.
|
|
87
|
+
if (_repaymentAddressNeedsToBeOverwritten(fill)) {
|
|
104
88
|
// TODO: Handle case where fill was sent on non-EVM chain, in which case the following call would fail
|
|
105
89
|
// or return something unexpected. We'd want to return undefined here.
|
|
106
|
-
const fillTransaction = await destinationChainProvider.getTransaction(fill.
|
|
90
|
+
const fillTransaction = await destinationChainProvider.getTransaction(fill.txnRef);
|
|
107
91
|
const destinationRelayer = fillTransaction?.from;
|
|
108
92
|
// Repayment chain is still an EVM chain, but the msg.sender is a bytes32 address, so the fill is invalid.
|
|
109
93
|
if (!isDefined(destinationRelayer) || !isValidEvmAddress(destinationRelayer)) {
|
|
110
94
|
return undefined;
|
|
111
95
|
}
|
|
112
|
-
|
|
96
|
+
// If we can switch the repayment chain to the destination chain, then do so. We should only switch if the
|
|
97
|
+
// destination chain has a valid repayment token that is equivalent to the deposited input token. This would
|
|
98
|
+
// also be the same mapping as the repayment token on the repayment chain.
|
|
99
|
+
if (
|
|
100
|
+
!matchedDeposit.fromLiteChain &&
|
|
101
|
+
hubPoolClient.areTokensEquivalent(fill.inputToken, fill.originChainId, fill.outputToken, fill.destinationChainId)
|
|
102
|
+
) {
|
|
113
103
|
repaymentChainId = fill.destinationChainId;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
104
|
+
}
|
|
105
|
+
// If we can't switch the chain, then we need to verify that the msg.sender is a valid address on the repayment chain.
|
|
106
|
+
// Because we already checked that the `destinationRelayer` was a valid EVM address above, we only need to check
|
|
107
|
+
// that the repayment chain is an EVM chain.
|
|
108
|
+
else {
|
|
117
109
|
if (!chainIsEvm(repaymentChainId)) {
|
|
118
110
|
return undefined;
|
|
119
111
|
}
|
|
120
112
|
}
|
|
121
113
|
fill.relayer = destinationRelayer;
|
|
114
|
+
|
|
115
|
+
// @todo: If chainIsSvm:
|
|
122
116
|
}
|
|
123
117
|
|
|
124
118
|
// Repayment address is now valid and repayment chain is either origin chain for lite chain or the destination
|
|
@@ -126,3 +120,69 @@ export async function verifyFillRepayment(
|
|
|
126
120
|
fill.repaymentChainId = repaymentChainId;
|
|
127
121
|
return fill;
|
|
128
122
|
}
|
|
123
|
+
|
|
124
|
+
function _getRepaymentChainId(
|
|
125
|
+
relayData: FillRepaymentInformation,
|
|
126
|
+
hubPoolClient: HubPoolClient,
|
|
127
|
+
bundleEndBlockForMainnet: number
|
|
128
|
+
): number {
|
|
129
|
+
if (relayData.fromLiteChain) {
|
|
130
|
+
assert(!isSlowFill(relayData), "getRepaymentChainId: fromLiteChain and slow fill are mutually exclusive");
|
|
131
|
+
return relayData.originChainId;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Handle slow relay where FilledRelay.repaymentChainId = 0, as hardcoded in the SpokePool contract.
|
|
135
|
+
// Slow relays always pay recipient on destination chain.
|
|
136
|
+
if (isSlowFill(relayData)) {
|
|
137
|
+
return relayData.destinationChainId;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Repayment chain is valid if the input token and repayment chain are mapped to the same PoolRebalanceRoute.
|
|
141
|
+
const repaymentTokenIsValid = _repaymentChainTokenIsValid(relayData, hubPoolClient, bundleEndBlockForMainnet);
|
|
142
|
+
if (repaymentTokenIsValid) {
|
|
143
|
+
return relayData.repaymentChainId;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// If repayment chain is not valid, default to origin chain since we always know the input token can be refunded.
|
|
147
|
+
return relayData.originChainId;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function _repaymentChainTokenIsValid(
|
|
151
|
+
relayData: FillRepaymentInformation,
|
|
152
|
+
hubPoolClient: HubPoolClient,
|
|
153
|
+
bundleEndBlockForMainnet: number
|
|
154
|
+
): boolean {
|
|
155
|
+
if (
|
|
156
|
+
!hubPoolClient.l2TokenHasPoolRebalanceRoute(relayData.inputToken, relayData.originChainId, bundleEndBlockForMainnet)
|
|
157
|
+
) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
const l1TokenCounterpart = hubPoolClient.getL1TokenForL2TokenAtBlock(
|
|
161
|
+
relayData.inputToken,
|
|
162
|
+
relayData.originChainId,
|
|
163
|
+
bundleEndBlockForMainnet
|
|
164
|
+
);
|
|
165
|
+
if (
|
|
166
|
+
!hubPoolClient.l2TokenEnabledForL1TokenAtBlock(
|
|
167
|
+
l1TokenCounterpart,
|
|
168
|
+
relayData.repaymentChainId,
|
|
169
|
+
bundleEndBlockForMainnet
|
|
170
|
+
)
|
|
171
|
+
) {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function _repaymentAddressNeedsToBeOverwritten(fill: Fill): boolean {
|
|
178
|
+
// Slow fills don't result in repayments so they're always valid.
|
|
179
|
+
if (isSlowFill(fill)) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// @todo add Solana logic here:
|
|
184
|
+
// - i.e. If chainIsSvm && !isValidSvmAddress(fill.relayer) then return false
|
|
185
|
+
// If chainIsEvm && !isValidEvmAddress(fill.relayer) then return false
|
|
186
|
+
// If chainIsEvm && isValidEvmAddress(fill.relayer) then return true
|
|
187
|
+
return !isValidEvmAddress(fill.relayer);
|
|
188
|
+
}
|
|
@@ -163,12 +163,13 @@ export function updateRunningBalanceForDeposit(
|
|
|
163
163
|
runningBalances: RunningBalances,
|
|
164
164
|
hubPoolClient: HubPoolClient,
|
|
165
165
|
deposit: V3DepositWithBlock,
|
|
166
|
-
updateAmount: BigNumber
|
|
166
|
+
updateAmount: BigNumber,
|
|
167
|
+
mainnetBundleEndBlock: number
|
|
167
168
|
): void {
|
|
168
169
|
const l1TokenCounterpart = hubPoolClient.getL1TokenForL2TokenAtBlock(
|
|
169
170
|
deposit.inputToken,
|
|
170
171
|
deposit.originChainId,
|
|
171
|
-
|
|
172
|
+
mainnetBundleEndBlock
|
|
172
173
|
);
|
|
173
174
|
updateRunningBalance(runningBalances, deposit.originChainId, l1TokenCounterpart, updateAmount);
|
|
174
175
|
}
|
|
@@ -48,11 +48,15 @@ const V3RelayDataSS = {
|
|
|
48
48
|
message: string(),
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
-
const SortableEventSS = {
|
|
51
|
+
export const SortableEventSS = {
|
|
52
52
|
blockNumber: number(),
|
|
53
|
-
transactionIndex: number(),
|
|
54
53
|
logIndex: number(),
|
|
55
|
-
|
|
54
|
+
|
|
55
|
+
txnRef: optional(string()),
|
|
56
|
+
txnIndex: optional(number()),
|
|
57
|
+
|
|
58
|
+
transactionHash: optional(string()),
|
|
59
|
+
transactionIndex: optional(number()),
|
|
56
60
|
};
|
|
57
61
|
|
|
58
62
|
const V3DepositSS = {
|
|
@@ -2,7 +2,7 @@ import assert from "assert";
|
|
|
2
2
|
import { Contract, EventFilter } from "ethers";
|
|
3
3
|
import _ from "lodash";
|
|
4
4
|
import winston from "winston";
|
|
5
|
-
import { DEFAULT_CACHING_SAFE_LAG, DEFAULT_CACHING_TTL, ZERO_ADDRESS } from "../constants";
|
|
5
|
+
import { DEFAULT_CACHING_SAFE_LAG, DEFAULT_CACHING_TTL, TOKEN_SYMBOLS_MAP, ZERO_ADDRESS } from "../constants";
|
|
6
6
|
import {
|
|
7
7
|
CachingMechanismInterface,
|
|
8
8
|
CancelledRootBundle,
|
|
@@ -44,6 +44,9 @@ import {
|
|
|
44
44
|
getTokenInfo,
|
|
45
45
|
getUsdcSymbol,
|
|
46
46
|
compareAddressesSimple,
|
|
47
|
+
chainIsSvm,
|
|
48
|
+
getDeployedAddress,
|
|
49
|
+
SvmAddress,
|
|
47
50
|
} from "../utils";
|
|
48
51
|
import { AcrossConfigStoreClient as ConfigStoreClient } from "./AcrossConfigStoreClient/AcrossConfigStoreClient";
|
|
49
52
|
import { BaseAbstractClient, isUpdateFailureReason, UpdateFailureReason } from "./BaseAbstractClient";
|
|
@@ -230,40 +233,41 @@ export class HubPoolClient extends BaseAbstractClient {
|
|
|
230
233
|
return sortEventsDescending(l2Tokens)[0].l1Token;
|
|
231
234
|
}
|
|
232
235
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
* at the HubPool equivalent block number of the L2 event.
|
|
237
|
-
* @param deposit Deposit event
|
|
238
|
-
* @param returns string L1 token counterpart for Deposit
|
|
239
|
-
*/
|
|
240
|
-
getL1TokenForDeposit(deposit: Pick<DepositWithBlock, "originChainId" | "inputToken" | "quoteBlockNumber">): string {
|
|
236
|
+
protected getL1TokenForDeposit(
|
|
237
|
+
deposit: Pick<DepositWithBlock, "originChainId" | "inputToken" | "quoteBlockNumber">
|
|
238
|
+
): string {
|
|
241
239
|
// L1-->L2 token mappings are set via PoolRebalanceRoutes which occur on mainnet,
|
|
242
240
|
// so we use the latest token mapping. This way if a very old deposit is filled, the relayer can use the
|
|
243
241
|
// latest L2 token mapping to find the L1 token counterpart.
|
|
244
242
|
return this.getL1TokenForL2TokenAtBlock(deposit.inputToken, deposit.originChainId, deposit.quoteBlockNumber);
|
|
245
243
|
}
|
|
246
244
|
|
|
247
|
-
/**
|
|
248
|
-
* Returns the L2 token that should be used as a counterpart to a deposit event. For example, the caller
|
|
249
|
-
* might want to know what the refund token will be on l2ChainId for the deposit event.
|
|
250
|
-
* @param l2ChainId Chain where caller wants to get L2 token counterpart for
|
|
251
|
-
* @param event Deposit event
|
|
252
|
-
* @returns string L2 token counterpart on l2ChainId
|
|
253
|
-
*/
|
|
254
|
-
getL2TokenForDeposit(
|
|
255
|
-
deposit: Pick<DepositWithBlock, "originChainId" | "destinationChainId" | "inputToken" | "quoteBlockNumber">,
|
|
256
|
-
l2ChainId = deposit.destinationChainId
|
|
257
|
-
): string {
|
|
258
|
-
const l1Token = this.getL1TokenForDeposit(deposit);
|
|
259
|
-
// Use the latest hub block number to find the L2 token counterpart.
|
|
260
|
-
return this.getL2TokenForL1TokenAtBlock(l1Token, l2ChainId, deposit.quoteBlockNumber);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
245
|
l2TokenEnabledForL1Token(l1Token: string, destinationChainId: number): boolean {
|
|
264
246
|
return this.l1TokensToDestinationTokens?.[l1Token]?.[destinationChainId] != undefined;
|
|
265
247
|
}
|
|
266
248
|
|
|
249
|
+
l2TokenEnabledForL1TokenAtBlock(l1Token: string, destinationChainId: number, hubBlockNumber: number): boolean {
|
|
250
|
+
// Find the last mapping published before the target block.
|
|
251
|
+
const l2Token: DestinationTokenWithBlock | undefined = sortEventsDescending(
|
|
252
|
+
this.l1TokensToDestinationTokensWithBlock?.[l1Token]?.[destinationChainId] ?? []
|
|
253
|
+
).find((mapping: DestinationTokenWithBlock) => mapping.blockNumber <= hubBlockNumber);
|
|
254
|
+
return l2Token !== undefined;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
l2TokenHasPoolRebalanceRoute(l2Token: string, l2ChainId: number, hubPoolBlock = this.latestBlockSearched): boolean {
|
|
258
|
+
return Object.values(this.l1TokensToDestinationTokensWithBlock).some((destinationTokenMapping) => {
|
|
259
|
+
return Object.entries(destinationTokenMapping).some(([_l2ChainId, setPoolRebalanceRouteEvents]) => {
|
|
260
|
+
return setPoolRebalanceRouteEvents.some((e) => {
|
|
261
|
+
return (
|
|
262
|
+
e.blockNumber <= hubPoolBlock &&
|
|
263
|
+
compareAddressesSimple(e.l2Token, l2Token) &&
|
|
264
|
+
Number(_l2ChainId) === l2ChainId
|
|
265
|
+
);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
267
271
|
/**
|
|
268
272
|
* @dev If tokenAddress + chain do not exist in TOKEN_SYMBOLS_MAP then this will throw.
|
|
269
273
|
* @param tokenAddress Token address on `chain`
|
|
@@ -506,21 +510,23 @@ export class HubPoolClient extends BaseAbstractClient {
|
|
|
506
510
|
chainIdB: number,
|
|
507
511
|
hubPoolBlock = this.latestBlockSearched
|
|
508
512
|
): boolean {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
return false;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
// Resolve both HubPool tokens back to a current SpokePool token and verify that they match.
|
|
518
|
-
const _tokenA = this.getL2TokenForL1TokenAtBlock(l1TokenA, chainIdA, hubPoolBlock);
|
|
519
|
-
const _tokenB = this.getL2TokenForL1TokenAtBlock(l1TokenB, chainIdB, hubPoolBlock);
|
|
520
|
-
return tokenA === _tokenA && tokenB === _tokenB;
|
|
521
|
-
} catch {
|
|
522
|
-
return false; // One or both input tokens were not recognised.
|
|
513
|
+
if (
|
|
514
|
+
!this.l2TokenHasPoolRebalanceRoute(tokenA, chainIdA, hubPoolBlock) ||
|
|
515
|
+
!this.l2TokenHasPoolRebalanceRoute(tokenB, chainIdB, hubPoolBlock)
|
|
516
|
+
) {
|
|
517
|
+
return false;
|
|
523
518
|
}
|
|
519
|
+
// Resolve both SpokePool tokens back to their respective HubPool tokens and verify that they match.
|
|
520
|
+
const l1TokenA = this.getL1TokenForL2TokenAtBlock(tokenA, chainIdA, hubPoolBlock);
|
|
521
|
+
const l1TokenB = this.getL1TokenForL2TokenAtBlock(tokenB, chainIdB, hubPoolBlock);
|
|
522
|
+
if (l1TokenA !== l1TokenB) {
|
|
523
|
+
return false;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Resolve both HubPool tokens back to a current SpokePool token and verify that they match.
|
|
527
|
+
const _tokenA = this.getL2TokenForL1TokenAtBlock(l1TokenA, chainIdA, hubPoolBlock);
|
|
528
|
+
const _tokenB = this.getL2TokenForL1TokenAtBlock(l1TokenB, chainIdB, hubPoolBlock);
|
|
529
|
+
return tokenA === _tokenA && tokenB === _tokenB;
|
|
524
530
|
}
|
|
525
531
|
|
|
526
532
|
getSpokeActivationBlockForChain(chainId: number): number {
|
|
@@ -878,39 +884,73 @@ export class HubPoolClient extends BaseAbstractClient {
|
|
|
878
884
|
if (eventsToQuery.includes("CrossChainContractsSet")) {
|
|
879
885
|
for (const event of events["CrossChainContractsSet"]) {
|
|
880
886
|
const args = spreadEventWithBlockNumber(event) as CrossChainContractsSet;
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
887
|
+
const dataToAdd: CrossChainContractsSet = {
|
|
888
|
+
spokePool: args.spokePool,
|
|
889
|
+
blockNumber: args.blockNumber,
|
|
890
|
+
txnRef: args.txnRef,
|
|
891
|
+
logIndex: args.logIndex,
|
|
892
|
+
txnIndex: args.txnIndex,
|
|
893
|
+
l2ChainId: args.l2ChainId,
|
|
894
|
+
};
|
|
895
|
+
// If the chain is SVM then our `args.spokePool` will be set to the `solanaSpokePool.toAddressUnchecked()` in the
|
|
896
|
+
// hubpool event because our hub deals with `address` types and not byte32. Therefore, we should confirm that the
|
|
897
|
+
// `args.spokePool` is the same as the `solanaSpokePool.toAddressUnchecked()`. We can derive the `solanaSpokePool`
|
|
898
|
+
// address by using the `getDeployedAddress` function.
|
|
899
|
+
if (chainIsSvm(args.l2ChainId)) {
|
|
900
|
+
const solanaSpokePool = getDeployedAddress("SvmSpoke", args.l2ChainId);
|
|
901
|
+
if (!solanaSpokePool) {
|
|
902
|
+
throw new Error(`SVM spoke pool not found for chain ${args.l2ChainId}`);
|
|
903
|
+
}
|
|
904
|
+
const truncatedAddress = SvmAddress.from(solanaSpokePool).toEvmAddress();
|
|
905
|
+
// Verify the event address matches our expected truncated address
|
|
906
|
+
if (args.spokePool.toLowerCase() !== truncatedAddress.toLowerCase()) {
|
|
907
|
+
throw new Error(
|
|
908
|
+
`SVM spoke pool address mismatch for chain ${args.l2ChainId}. ` +
|
|
909
|
+
`Expected ${truncatedAddress}, got ${args.spokePool}`
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
// Store the full Solana address
|
|
913
|
+
dataToAdd.spokePool = SvmAddress.from(solanaSpokePool).toBytes32();
|
|
914
|
+
}
|
|
915
|
+
assign(this.crossChainContracts, [args.l2ChainId], [dataToAdd]);
|
|
893
916
|
}
|
|
894
917
|
}
|
|
895
918
|
|
|
896
919
|
if (eventsToQuery.includes("SetPoolRebalanceRoute")) {
|
|
897
920
|
for (const event of events["SetPoolRebalanceRoute"]) {
|
|
898
921
|
const args = spreadEventWithBlockNumber(event) as SetPoolRebalanceRoot;
|
|
922
|
+
|
|
923
|
+
// If the destination chain is SVM, then we need to convert the destination token to the Solana address.
|
|
924
|
+
// This is because the HubPool contract only holds a truncated address for the USDC token and currently
|
|
925
|
+
// only supports USDC as a destination token for Solana.
|
|
926
|
+
let destinationToken = args.destinationToken;
|
|
927
|
+
if (chainIsSvm(args.destinationChainId)) {
|
|
928
|
+
const usdcTokenSol = TOKEN_SYMBOLS_MAP.USDC.addresses[args.destinationChainId];
|
|
929
|
+
const truncatedAddress = SvmAddress.from(usdcTokenSol).toEvmAddress();
|
|
930
|
+
if (destinationToken.toLowerCase() !== truncatedAddress.toLowerCase()) {
|
|
931
|
+
throw new Error(
|
|
932
|
+
`SVM USDC address mismatch for chain ${args.destinationChainId}. ` +
|
|
933
|
+
`Expected ${truncatedAddress}, got ${destinationToken}`
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
destinationToken = SvmAddress.from(usdcTokenSol).toBytes32();
|
|
937
|
+
}
|
|
938
|
+
|
|
899
939
|
// If the destination token is set to the zero address in an event, then this means Across should no longer
|
|
900
940
|
// rebalance to this chain.
|
|
901
|
-
if (
|
|
902
|
-
assign(this.l1TokensToDestinationTokens, [args.l1Token, args.destinationChainId],
|
|
941
|
+
if (destinationToken !== ZERO_ADDRESS) {
|
|
942
|
+
assign(this.l1TokensToDestinationTokens, [args.l1Token, args.destinationChainId], destinationToken);
|
|
903
943
|
assign(
|
|
904
944
|
this.l1TokensToDestinationTokensWithBlock,
|
|
905
945
|
[args.l1Token, args.destinationChainId],
|
|
906
946
|
[
|
|
907
947
|
{
|
|
908
948
|
l1Token: args.l1Token,
|
|
909
|
-
l2Token:
|
|
949
|
+
l2Token: destinationToken,
|
|
910
950
|
blockNumber: args.blockNumber,
|
|
911
|
-
|
|
951
|
+
txnIndex: args.txnIndex,
|
|
912
952
|
logIndex: args.logIndex,
|
|
913
|
-
|
|
953
|
+
txnRef: args.txnRef,
|
|
914
954
|
},
|
|
915
955
|
]
|
|
916
956
|
);
|
|
@@ -956,12 +996,7 @@ export class HubPoolClient extends BaseAbstractClient {
|
|
|
956
996
|
this.proposedRootBundles.push(
|
|
957
997
|
...events["ProposeRootBundle"]
|
|
958
998
|
.filter((event) => !this.configOverride.ignoredHubProposedBundles.includes(event.blockNumber))
|
|
959
|
-
.map((event) =>
|
|
960
|
-
return {
|
|
961
|
-
...spreadEventWithBlockNumber(event),
|
|
962
|
-
transactionHash: event.transactionHash,
|
|
963
|
-
} as ProposedRootBundle;
|
|
964
|
-
})
|
|
999
|
+
.map((event) => spreadEventWithBlockNumber(event) as ProposedRootBundle)
|
|
965
1000
|
);
|
|
966
1001
|
}
|
|
967
1002
|
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from "../../utils";
|
|
20
20
|
import {
|
|
21
21
|
EventSearchConfig,
|
|
22
|
+
logToSortableEvent,
|
|
22
23
|
paginatedEventQuery,
|
|
23
24
|
sortEventsAscendingInPlace,
|
|
24
25
|
spreadEventWithBlockNumber,
|
|
@@ -127,7 +128,7 @@ export class EVMSpokePoolClient extends SpokePoolClient {
|
|
|
127
128
|
}
|
|
128
129
|
|
|
129
130
|
// Sort all events to ensure they are stored in a consistent order.
|
|
130
|
-
events.forEach((events) => sortEventsAscendingInPlace(events));
|
|
131
|
+
events.forEach((events) => sortEventsAscendingInPlace(events.map(logToSortableEvent)));
|
|
131
132
|
|
|
132
133
|
return {
|
|
133
134
|
success: true,
|