@across-protocol/sdk 4.1.43 → 4.1.45-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/addressAggregator/adapters/abstract.d.ts +15 -0
- package/dist/cjs/addressAggregator/adapters/abstract.js +83 -0
- package/dist/cjs/addressAggregator/adapters/abstract.js.map +1 -0
- package/dist/cjs/addressAggregator/adapters/bybit.d.ts +5 -9
- package/dist/cjs/addressAggregator/adapters/bybit.js +11 -12
- package/dist/cjs/addressAggregator/adapters/bybit.js.map +1 -1
- package/dist/cjs/addressAggregator/adapters/env.d.ts +5 -7
- package/dist/cjs/addressAggregator/adapters/env.js +12 -11
- package/dist/cjs/addressAggregator/adapters/env.js.map +1 -1
- package/dist/cjs/addressAggregator/adapters/file.d.ts +5 -7
- package/dist/cjs/addressAggregator/adapters/file.js +13 -12
- package/dist/cjs/addressAggregator/adapters/file.js.map +1 -1
- package/dist/cjs/addressAggregator/adapters/risklabs.d.ts +5 -9
- package/dist/cjs/addressAggregator/adapters/risklabs.js +18 -14
- package/dist/cjs/addressAggregator/adapters/risklabs.js.map +1 -1
- package/dist/cjs/addressAggregator/index.js +2 -2
- package/dist/cjs/addressAggregator/index.js.map +1 -1
- package/dist/cjs/addressAggregator/types.d.ts +9 -1
- package/dist/cjs/addressAggregator/types.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.js +13 -6
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js +9 -0
- package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/FillUtils.d.ts +7 -6
- package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js +46 -35
- package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
- package/dist/cjs/clients/HubPoolClient.d.ts +2 -0
- package/dist/cjs/clients/HubPoolClient.js +27 -10
- package/dist/cjs/clients/HubPoolClient.js.map +1 -1
- package/dist/cjs/clients/SpokePoolClient/SpokePoolClient.d.ts +1 -0
- package/dist/cjs/clients/SpokePoolClient/SpokePoolClient.js +16 -3
- package/dist/cjs/clients/SpokePoolClient/SpokePoolClient.js.map +1 -1
- package/dist/cjs/clients/mocks/MockHubPoolClient.d.ts +2 -0
- package/dist/cjs/clients/mocks/MockHubPoolClient.js +18 -0
- package/dist/cjs/clients/mocks/MockHubPoolClient.js.map +1 -1
- package/dist/cjs/constants.d.ts +1 -0
- package/dist/cjs/constants.js +13 -1
- package/dist/cjs/constants.js.map +1 -1
- package/dist/cjs/svm/eventsClient.js +5 -12
- package/dist/cjs/svm/eventsClient.js.map +1 -1
- package/dist/cjs/svm/index.d.ts +2 -0
- package/dist/cjs/svm/index.js +3 -0
- package/dist/cjs/svm/index.js.map +1 -1
- package/dist/cjs/svm/utils/events.d.ts +6 -1
- package/dist/cjs/svm/utils/events.js +13 -4
- package/dist/cjs/svm/utils/events.js.map +1 -1
- package/dist/cjs/svm/utils/index.d.ts +1 -0
- package/dist/cjs/svm/utils/index.js +5 -0
- package/dist/cjs/svm/utils/index.js.map +1 -0
- 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/TokenUtils.d.ts +1 -0
- package/dist/cjs/utils/TokenUtils.js +5 -1
- package/dist/cjs/utils/TokenUtils.js.map +1 -1
- package/dist/cjs/utils/common.d.ts +2 -0
- package/dist/cjs/utils/common.js +3 -1
- package/dist/cjs/utils/common.js.map +1 -1
- package/dist/esm/addressAggregator/adapters/abstract.d.ts +15 -0
- package/dist/esm/addressAggregator/adapters/abstract.js +80 -0
- package/dist/esm/addressAggregator/adapters/abstract.js.map +1 -0
- package/dist/esm/addressAggregator/adapters/bybit.d.ts +5 -9
- package/dist/esm/addressAggregator/adapters/bybit.js +12 -13
- package/dist/esm/addressAggregator/adapters/bybit.js.map +1 -1
- package/dist/esm/addressAggregator/adapters/env.d.ts +5 -7
- package/dist/esm/addressAggregator/adapters/env.js +12 -11
- package/dist/esm/addressAggregator/adapters/env.js.map +1 -1
- package/dist/esm/addressAggregator/adapters/file.d.ts +5 -7
- package/dist/esm/addressAggregator/adapters/file.js +14 -13
- package/dist/esm/addressAggregator/adapters/file.js.map +1 -1
- package/dist/esm/addressAggregator/adapters/risklabs.d.ts +5 -9
- package/dist/esm/addressAggregator/adapters/risklabs.js +19 -15
- package/dist/esm/addressAggregator/adapters/risklabs.js.map +1 -1
- package/dist/esm/addressAggregator/index.js +2 -2
- package/dist/esm/addressAggregator/index.js.map +1 -1
- package/dist/esm/addressAggregator/types.d.ts +9 -1
- package/dist/esm/addressAggregator/types.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js +14 -7
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js +15 -0
- package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/FillUtils.d.ts +28 -6
- package/dist/esm/clients/BundleDataClient/utils/FillUtils.js +82 -49
- package/dist/esm/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
- package/dist/esm/clients/HubPoolClient.d.ts +2 -0
- package/dist/esm/clients/HubPoolClient.js +31 -13
- package/dist/esm/clients/HubPoolClient.js.map +1 -1
- package/dist/esm/clients/SpokePoolClient/SpokePoolClient.d.ts +1 -0
- package/dist/esm/clients/SpokePoolClient/SpokePoolClient.js +24 -11
- package/dist/esm/clients/SpokePoolClient/SpokePoolClient.js.map +1 -1
- package/dist/esm/clients/mocks/MockHubPoolClient.d.ts +2 -0
- package/dist/esm/clients/mocks/MockHubPoolClient.js +18 -0
- package/dist/esm/clients/mocks/MockHubPoolClient.js.map +1 -1
- package/dist/esm/constants.d.ts +1 -0
- package/dist/esm/constants.js +12 -0
- package/dist/esm/constants.js.map +1 -1
- package/dist/esm/svm/eventsClient.js +6 -13
- package/dist/esm/svm/eventsClient.js.map +1 -1
- package/dist/esm/svm/index.d.ts +2 -0
- package/dist/esm/svm/index.js +3 -0
- package/dist/esm/svm/index.js.map +1 -1
- package/dist/esm/svm/utils/events.d.ts +9 -1
- package/dist/esm/svm/utils/events.js +15 -3
- package/dist/esm/svm/utils/events.js.map +1 -1
- package/dist/esm/svm/utils/index.d.ts +1 -0
- package/dist/esm/svm/utils/index.js +2 -0
- package/dist/esm/svm/utils/index.js.map +1 -0
- 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/TokenUtils.d.ts +1 -0
- package/dist/esm/utils/TokenUtils.js +3 -0
- package/dist/esm/utils/TokenUtils.js.map +1 -1
- package/dist/esm/utils/common.d.ts +2 -0
- package/dist/esm/utils/common.js +2 -0
- package/dist/esm/utils/common.js.map +1 -1
- package/dist/types/addressAggregator/adapters/abstract.d.ts +16 -0
- package/dist/types/addressAggregator/adapters/abstract.d.ts.map +1 -0
- package/dist/types/addressAggregator/adapters/bybit.d.ts +5 -9
- package/dist/types/addressAggregator/adapters/bybit.d.ts.map +1 -1
- package/dist/types/addressAggregator/adapters/env.d.ts +5 -7
- package/dist/types/addressAggregator/adapters/env.d.ts.map +1 -1
- package/dist/types/addressAggregator/adapters/file.d.ts +5 -7
- package/dist/types/addressAggregator/adapters/file.d.ts.map +1 -1
- package/dist/types/addressAggregator/adapters/risklabs.d.ts +5 -9
- package/dist/types/addressAggregator/adapters/risklabs.d.ts.map +1 -1
- package/dist/types/addressAggregator/types.d.ts +9 -1
- package/dist/types/addressAggregator/types.d.ts.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 +28 -6
- package/dist/types/clients/BundleDataClient/utils/FillUtils.d.ts.map +1 -1
- package/dist/types/clients/HubPoolClient.d.ts +2 -0
- package/dist/types/clients/HubPoolClient.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/MockHubPoolClient.d.ts +2 -0
- package/dist/types/clients/mocks/MockHubPoolClient.d.ts.map +1 -1
- package/dist/types/constants.d.ts +1 -0
- package/dist/types/constants.d.ts.map +1 -1
- package/dist/types/svm/eventsClient.d.ts.map +1 -1
- package/dist/types/svm/index.d.ts +2 -0
- package/dist/types/svm/index.d.ts.map +1 -1
- package/dist/types/svm/utils/events.d.ts +9 -1
- package/dist/types/svm/utils/events.d.ts.map +1 -1
- package/dist/types/svm/utils/index.d.ts +2 -0
- package/dist/types/svm/utils/index.d.ts.map +1 -0
- package/dist/types/utils/DepositUtils.d.ts +1 -0
- package/dist/types/utils/DepositUtils.d.ts.map +1 -1
- package/dist/types/utils/TokenUtils.d.ts +1 -0
- package/dist/types/utils/TokenUtils.d.ts.map +1 -1
- package/dist/types/utils/common.d.ts +2 -0
- package/dist/types/utils/common.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/addressAggregator/adapters/abstract.ts +74 -0
- package/src/addressAggregator/adapters/bybit.ts +10 -11
- package/src/addressAggregator/adapters/env.ts +10 -10
- package/src/addressAggregator/adapters/file.ts +11 -12
- package/src/addressAggregator/adapters/risklabs.ts +14 -13
- package/src/addressAggregator/index.ts +2 -2
- package/src/addressAggregator/types.ts +10 -1
- package/src/clients/BundleDataClient/BundleDataClient.ts +28 -18
- package/src/clients/BundleDataClient/utils/DataworkerUtils.ts +29 -0
- package/src/clients/BundleDataClient/utils/FillUtils.ts +128 -71
- package/src/clients/HubPoolClient.ts +38 -14
- package/src/clients/SpokePoolClient/SpokePoolClient.ts +40 -14
- package/src/clients/mocks/MockHubPoolClient.ts +16 -0
- package/src/constants.ts +13 -0
- package/src/svm/eventsClient.ts +6 -12
- package/src/svm/index.ts +2 -0
- package/src/svm/utils/events.ts +16 -4
- package/src/svm/utils/index.ts +1 -0
- package/src/utils/DepositUtils.ts +6 -1
- package/src/utils/TokenUtils.ts +6 -0
- package/src/utils/common.ts +2 -0
- package/dist/cjs/addressAggregator/adapters/util.d.ts +0 -4
- package/dist/cjs/addressAggregator/adapters/util.js +0 -66
- package/dist/cjs/addressAggregator/adapters/util.js.map +0 -1
- package/dist/esm/addressAggregator/adapters/util.d.ts +0 -4
- package/dist/esm/addressAggregator/adapters/util.js +0 -60
- package/dist/esm/addressAggregator/adapters/util.js.map +0 -1
- package/dist/types/addressAggregator/adapters/util.d.ts +0 -5
- package/dist/types/addressAggregator/adapters/util.d.ts.map +0 -1
- package/src/addressAggregator/adapters/util.ts +0 -47
|
@@ -1,124 +1,119 @@
|
|
|
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 & { quoteBlockNumber: number; 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
|
-
|
|
9
|
-
hubPoolClient: HubPoolClient
|
|
10
|
-
blockRangesForChains: number[][],
|
|
11
|
-
chainIdListForBundleEvaluationBlockNumbers: number[],
|
|
12
|
-
fromLiteChain: boolean
|
|
22
|
+
relayData: FillRepaymentInformation,
|
|
23
|
+
hubPoolClient: HubPoolClient
|
|
13
24
|
): {
|
|
14
25
|
chainToSendRefundTo: number;
|
|
15
26
|
repaymentToken: string;
|
|
16
27
|
} {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (fromLiteChain) {
|
|
24
|
-
chainToSendRefundTo = fill.originChainId;
|
|
28
|
+
const chainToSendRefundTo = _getRepaymentChainId(relayData, hubPoolClient);
|
|
29
|
+
if (chainToSendRefundTo === relayData.originChainId) {
|
|
30
|
+
return {
|
|
31
|
+
chainToSendRefundTo,
|
|
32
|
+
repaymentToken: relayData.inputToken,
|
|
33
|
+
};
|
|
25
34
|
}
|
|
26
35
|
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
hubPoolClient.chainId,
|
|
31
|
-
chainIdListForBundleEvaluationBlockNumbers
|
|
32
|
-
)[1];
|
|
33
|
-
|
|
36
|
+
// Now figure out the equivalent L2 token for the repayment token. If the inputToken doesn't have a
|
|
37
|
+
// PoolRebalanceRoute, then the repayment chain would have been the originChainId after the getRepaymentChainId()
|
|
38
|
+
// call and we would have returned already, so the following call should always succeed.
|
|
34
39
|
const l1TokenCounterpart = hubPoolClient.getL1TokenForL2TokenAtBlock(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
relayData.inputToken,
|
|
41
|
+
relayData.originChainId,
|
|
42
|
+
relayData.quoteBlockNumber
|
|
38
43
|
);
|
|
39
44
|
|
|
40
45
|
const repaymentToken = hubPoolClient.getL2TokenForL1TokenAtBlock(
|
|
41
46
|
l1TokenCounterpart,
|
|
42
47
|
chainToSendRefundTo,
|
|
43
|
-
|
|
48
|
+
relayData.quoteBlockNumber
|
|
44
49
|
);
|
|
45
50
|
return {
|
|
46
51
|
chainToSendRefundTo,
|
|
47
52
|
repaymentToken,
|
|
48
53
|
};
|
|
49
54
|
}
|
|
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.
|
|
55
|
+
/**
|
|
56
|
+
* @notice Verifies that the fill can be repaid. If the repayment address is not
|
|
57
|
+
* valid for the requested repayment chain, then this function will attempt to change the fill's repayment chain
|
|
58
|
+
* to the destination chain and its repayment address to the msg.sender and if this is possible,
|
|
59
|
+
* return the fill. Otherwise, return undefined.
|
|
60
|
+
* @param _fill Fill with a requested repayment chain and address
|
|
61
|
+
* @return Fill with the applied repayment chain (depends on the validity of the requested repayment address)
|
|
62
|
+
* and applied repayment address, or undefined if the applied repayment address is not valid for the
|
|
63
|
+
* applied repayment chain.
|
|
64
|
+
*/
|
|
82
65
|
export async function verifyFillRepayment(
|
|
83
66
|
_fill: FillWithBlock,
|
|
84
67
|
destinationChainProvider: providers.Provider,
|
|
85
68
|
matchedDeposit: DepositWithBlock,
|
|
86
69
|
hubPoolClient: HubPoolClient
|
|
87
70
|
): Promise<FillWithBlock | undefined> {
|
|
88
|
-
const fill =
|
|
71
|
+
const fill = {
|
|
72
|
+
..._.cloneDeep(_fill),
|
|
73
|
+
fromLiteChain: matchedDeposit.fromLiteChain,
|
|
74
|
+
quoteBlockNumber: matchedDeposit.quoteBlockNumber,
|
|
75
|
+
};
|
|
89
76
|
|
|
90
77
|
// Slow fills don't result in repayments so they're always valid.
|
|
91
78
|
if (isSlowFill(fill)) {
|
|
92
79
|
return fill;
|
|
93
80
|
}
|
|
94
81
|
|
|
95
|
-
let repaymentChainId =
|
|
82
|
+
let repaymentChainId = _getRepaymentChainId(fill, hubPoolClient);
|
|
96
83
|
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
if (
|
|
100
|
-
repaymentChainId = fill.destinationChainId;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (!isValidEvmAddress(fill.relayer)) {
|
|
84
|
+
// Repayments will always go to the fill.relayer address so check if its a valid EVM address. If its not, attempt
|
|
85
|
+
// to change it to the msg.sender of the FilledRelay.
|
|
86
|
+
if (_repaymentAddressNeedsToBeOverwritten(fill)) {
|
|
104
87
|
// TODO: Handle case where fill was sent on non-EVM chain, in which case the following call would fail
|
|
105
88
|
// or return something unexpected. We'd want to return undefined here.
|
|
89
|
+
|
|
90
|
+
// @todo: If chainIsEvm:
|
|
106
91
|
const fillTransaction = await destinationChainProvider.getTransaction(fill.transactionHash);
|
|
107
92
|
const destinationRelayer = fillTransaction?.from;
|
|
108
93
|
// Repayment chain is still an EVM chain, but the msg.sender is a bytes32 address, so the fill is invalid.
|
|
109
94
|
if (!isDefined(destinationRelayer) || !isValidEvmAddress(destinationRelayer)) {
|
|
110
95
|
return undefined;
|
|
111
96
|
}
|
|
112
|
-
|
|
97
|
+
// If we can switch the repayment chain to the destination chain, then do so. We should only switch if the
|
|
98
|
+
// destination chain has a valid repayment token that is equivalent to the deposited input token. This would
|
|
99
|
+
// also be the same mapping as the repayment token on the repayment chain.
|
|
100
|
+
if (
|
|
101
|
+
!matchedDeposit.fromLiteChain &&
|
|
102
|
+
hubPoolClient.areTokensEquivalent(fill.inputToken, fill.originChainId, fill.outputToken, fill.destinationChainId)
|
|
103
|
+
) {
|
|
113
104
|
repaymentChainId = fill.destinationChainId;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
105
|
+
}
|
|
106
|
+
// If we can't switch the chain, then we need to verify that the msg.sender is a valid address on the repayment chain.
|
|
107
|
+
// Because we already checked that the `destinationRelayer` was a valid EVM address above, we only need to check
|
|
108
|
+
// that the repayment chain is an EVM chain.
|
|
109
|
+
else {
|
|
117
110
|
if (!chainIsEvm(repaymentChainId)) {
|
|
118
111
|
return undefined;
|
|
119
112
|
}
|
|
120
113
|
}
|
|
121
114
|
fill.relayer = destinationRelayer;
|
|
115
|
+
|
|
116
|
+
// @todo: If chainIsSvm:
|
|
122
117
|
}
|
|
123
118
|
|
|
124
119
|
// Repayment address is now valid and repayment chain is either origin chain for lite chain or the destination
|
|
@@ -126,3 +121,65 @@ export async function verifyFillRepayment(
|
|
|
126
121
|
fill.repaymentChainId = repaymentChainId;
|
|
127
122
|
return fill;
|
|
128
123
|
}
|
|
124
|
+
|
|
125
|
+
function _getRepaymentChainId(relayData: FillRepaymentInformation, hubPoolClient: HubPoolClient): number {
|
|
126
|
+
if (relayData.fromLiteChain) {
|
|
127
|
+
assert(!isSlowFill(relayData), "getRepaymentChainId: fromLiteChain and slow fill are mutually exclusive");
|
|
128
|
+
return relayData.originChainId;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Handle slow relay where FilledRelay.repaymentChainId = 0, as hardcoded in the SpokePool contract.
|
|
132
|
+
// Slow relays always pay recipient on destination chain.
|
|
133
|
+
if (isSlowFill(relayData)) {
|
|
134
|
+
return relayData.destinationChainId;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Repayment chain is valid if the input token and repayment chain are mapped to the same PoolRebalanceRoute.
|
|
138
|
+
const repaymentTokenIsValid = _repaymentChainTokenIsValid(relayData, hubPoolClient);
|
|
139
|
+
if (repaymentTokenIsValid) {
|
|
140
|
+
return relayData.repaymentChainId;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// If repayment chain is not valid, default to origin chain since we always know the input token can be refunded.
|
|
144
|
+
return relayData.originChainId;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function _repaymentChainTokenIsValid(relayData: FillRepaymentInformation, hubPoolClient: HubPoolClient): boolean {
|
|
148
|
+
if (
|
|
149
|
+
!hubPoolClient.l2TokenHasPoolRebalanceRoute(
|
|
150
|
+
relayData.inputToken,
|
|
151
|
+
relayData.originChainId,
|
|
152
|
+
relayData.quoteBlockNumber
|
|
153
|
+
)
|
|
154
|
+
) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
const l1TokenCounterpart = hubPoolClient.getL1TokenForL2TokenAtBlock(
|
|
158
|
+
relayData.inputToken,
|
|
159
|
+
relayData.originChainId,
|
|
160
|
+
relayData.quoteBlockNumber
|
|
161
|
+
);
|
|
162
|
+
if (
|
|
163
|
+
!hubPoolClient.l2TokenEnabledForL1TokenAtBlock(
|
|
164
|
+
l1TokenCounterpart,
|
|
165
|
+
relayData.repaymentChainId,
|
|
166
|
+
relayData.quoteBlockNumber
|
|
167
|
+
)
|
|
168
|
+
) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function _repaymentAddressNeedsToBeOverwritten(fill: Fill): boolean {
|
|
175
|
+
// Slow fills don't result in repayments so they're always valid.
|
|
176
|
+
if (isSlowFill(fill)) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// @todo add Solana logic here:
|
|
181
|
+
// - i.e. If chainIsSvm && !isValidSvmAddress(fill.relayer) then return false
|
|
182
|
+
// If chainIsEvm && !isValidEvmAddress(fill.relayer) then return false
|
|
183
|
+
// If chainIsEvm && isValidEvmAddress(fill.relayer) then return true
|
|
184
|
+
return !isValidEvmAddress(fill.relayer);
|
|
185
|
+
}
|
|
@@ -265,6 +265,28 @@ export class HubPoolClient extends BaseAbstractClient {
|
|
|
265
265
|
return this.l1TokensToDestinationTokens?.[l1Token]?.[destinationChainId] != undefined;
|
|
266
266
|
}
|
|
267
267
|
|
|
268
|
+
l2TokenEnabledForL1TokenAtBlock(l1Token: string, destinationChainId: number, hubBlockNumber: number): boolean {
|
|
269
|
+
// Find the last mapping published before the target block.
|
|
270
|
+
const l2Token: DestinationTokenWithBlock | undefined = sortEventsDescending(
|
|
271
|
+
this.l1TokensToDestinationTokensWithBlock?.[l1Token]?.[destinationChainId] ?? []
|
|
272
|
+
).find((mapping: DestinationTokenWithBlock) => mapping.blockNumber <= hubBlockNumber);
|
|
273
|
+
return l2Token !== undefined;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
l2TokenHasPoolRebalanceRoute(l2Token: string, l2ChainId: number, hubPoolBlock = this.latestBlockSearched): boolean {
|
|
277
|
+
return Object.values(this.l1TokensToDestinationTokensWithBlock).some((destinationTokenMapping) => {
|
|
278
|
+
return Object.entries(destinationTokenMapping).some(([_l2ChainId, setPoolRebalanceRouteEvents]) => {
|
|
279
|
+
return setPoolRebalanceRouteEvents.some((e) => {
|
|
280
|
+
return (
|
|
281
|
+
e.blockNumber <= hubPoolBlock &&
|
|
282
|
+
compareAddressesSimple(e.l2Token, l2Token) &&
|
|
283
|
+
Number(_l2ChainId) === l2ChainId
|
|
284
|
+
);
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
268
290
|
/**
|
|
269
291
|
* @dev If tokenAddress + chain do not exist in TOKEN_SYMBOLS_MAP then this will throw.
|
|
270
292
|
* @param tokenAddress Token address on `chain`
|
|
@@ -529,21 +551,23 @@ export class HubPoolClient extends BaseAbstractClient {
|
|
|
529
551
|
chainIdB: number,
|
|
530
552
|
hubPoolBlock = this.latestBlockSearched
|
|
531
553
|
): boolean {
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
return tokenA === _tokenA && tokenB === _tokenB;
|
|
544
|
-
} catch {
|
|
545
|
-
return false; // One or both input tokens were not recognised.
|
|
554
|
+
if (
|
|
555
|
+
!this.l2TokenHasPoolRebalanceRoute(tokenA, chainIdA, hubPoolBlock) ||
|
|
556
|
+
!this.l2TokenHasPoolRebalanceRoute(tokenB, chainIdB, hubPoolBlock)
|
|
557
|
+
) {
|
|
558
|
+
return false;
|
|
559
|
+
}
|
|
560
|
+
// Resolve both SpokePool tokens back to their respective HubPool tokens and verify that they match.
|
|
561
|
+
const l1TokenA = this.getL1TokenForL2TokenAtBlock(tokenA, chainIdA, hubPoolBlock);
|
|
562
|
+
const l1TokenB = this.getL1TokenForL2TokenAtBlock(tokenB, chainIdB, hubPoolBlock);
|
|
563
|
+
if (l1TokenA !== l1TokenB) {
|
|
564
|
+
return false;
|
|
546
565
|
}
|
|
566
|
+
|
|
567
|
+
// Resolve both HubPool tokens back to a current SpokePool token and verify that they match.
|
|
568
|
+
const _tokenA = this.getL2TokenForL1TokenAtBlock(l1TokenA, chainIdA, hubPoolBlock);
|
|
569
|
+
const _tokenB = this.getL2TokenForL1TokenAtBlock(l1TokenB, chainIdB, hubPoolBlock);
|
|
570
|
+
return tokenA === _tokenA && tokenB === _tokenB;
|
|
547
571
|
}
|
|
548
572
|
|
|
549
573
|
getSpokeActivationBlockForChain(chainId: number): number {
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
isZeroAddress,
|
|
18
18
|
toAddress,
|
|
19
19
|
validateFillForDeposit,
|
|
20
|
+
chainIsEvm,
|
|
20
21
|
} from "../../utils";
|
|
21
22
|
import {
|
|
22
23
|
duplicateEvent,
|
|
@@ -41,7 +42,7 @@ import {
|
|
|
41
42
|
} from "../../interfaces";
|
|
42
43
|
import { BaseAbstractClient, UpdateFailureReason } from "../BaseAbstractClient";
|
|
43
44
|
import { AcrossConfigStoreClient } from "../AcrossConfigStoreClient";
|
|
44
|
-
import {
|
|
45
|
+
import { getRefundInformationFromFill } from "../BundleDataClient";
|
|
45
46
|
import { HubPoolClient } from "../HubPoolClient";
|
|
46
47
|
|
|
47
48
|
export type SpokePoolUpdateSuccess = {
|
|
@@ -365,22 +366,25 @@ export abstract class SpokePoolClient extends BaseAbstractClient {
|
|
|
365
366
|
fill: FillWithBlock
|
|
366
367
|
) => {
|
|
367
368
|
if (validateFillForDeposit(fill, deposit).valid) {
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
369
|
+
const fillRepaymentData = {
|
|
370
|
+
...fill,
|
|
371
|
+
fromLiteChain: deposit.fromLiteChain,
|
|
372
|
+
quoteBlockNumber: this.hubPoolClient!.latestBlockSearched,
|
|
373
|
+
};
|
|
374
|
+
const { chainToSendRefundTo: repaymentChainId } = getRefundInformationFromFill(
|
|
375
|
+
fillRepaymentData,
|
|
376
|
+
this.hubPoolClient!
|
|
377
|
+
);
|
|
378
|
+
// In order to keep this function sync, we can't call verifyFillRepayment so we'll log any fills where
|
|
379
|
+
// the filler-specified repayment chain and repayment address is not a valid repayment upon
|
|
380
|
+
// first glance. In other words, the repayment address is not a valid EVM address or the repayment chain
|
|
381
|
+
// is not a valid EVM chain. In the case where the repayment address is not a valid EVM address, the dataworker
|
|
382
|
+
// might be able to overwrite the repayment address to the msg.sender on the fill txn, but to keep this
|
|
383
|
+
// functioon synchronous, we can't make that decision now. So this function might log some false positives.
|
|
375
384
|
if (
|
|
376
385
|
this.hubPoolClient &&
|
|
377
386
|
!isSlowFill(fill) &&
|
|
378
|
-
(!isValidEvmAddress(fill.relayer)
|
|
379
|
-
forceDestinationRepayment(
|
|
380
|
-
repaymentChainId,
|
|
381
|
-
{ ...deposit, quoteBlockNumber: this.hubPoolClient!.latestBlockSearched },
|
|
382
|
-
this.hubPoolClient
|
|
383
|
-
))
|
|
387
|
+
(!chainIsEvm(repaymentChainId) || !isValidEvmAddress(fill.relayer))
|
|
384
388
|
) {
|
|
385
389
|
groupedFills.unrepayableFills.push(fill);
|
|
386
390
|
}
|
|
@@ -450,6 +454,25 @@ export abstract class SpokePoolClient extends BaseAbstractClient {
|
|
|
450
454
|
return `${event.depositId.toString()}-${event.originChainId}`;
|
|
451
455
|
}
|
|
452
456
|
|
|
457
|
+
protected canResolveZeroAddressOutputToken(deposit: DepositWithBlock): boolean {
|
|
458
|
+
if (
|
|
459
|
+
!this.hubPoolClient?.l2TokenHasPoolRebalanceRoute(
|
|
460
|
+
deposit.inputToken,
|
|
461
|
+
deposit.originChainId,
|
|
462
|
+
deposit.quoteBlockNumber
|
|
463
|
+
)
|
|
464
|
+
) {
|
|
465
|
+
return false;
|
|
466
|
+
} else {
|
|
467
|
+
const l1Token = this.hubPoolClient?.getL1TokenForDeposit(deposit);
|
|
468
|
+
return this.hubPoolClient.l2TokenEnabledForL1TokenAtBlock(
|
|
469
|
+
l1Token,
|
|
470
|
+
deposit.destinationChainId,
|
|
471
|
+
deposit.quoteBlockNumber
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
453
476
|
/**
|
|
454
477
|
* A wrapper over the `_update` method that handles errors and logs. This method additionally calls into the
|
|
455
478
|
* HubPoolClient to update the state of this client with data from the HubPool contract.
|
|
@@ -709,6 +732,9 @@ export abstract class SpokePoolClient extends BaseAbstractClient {
|
|
|
709
732
|
* @returns The destination token.
|
|
710
733
|
*/
|
|
711
734
|
protected getDestinationTokenForDeposit(deposit: DepositWithBlock): string {
|
|
735
|
+
if (!this.canResolveZeroAddressOutputToken(deposit)) {
|
|
736
|
+
return ZERO_ADDRESS;
|
|
737
|
+
}
|
|
712
738
|
// If there is no rate model client return address(0).
|
|
713
739
|
return this.hubPoolClient?.getL2TokenForDeposit(deposit) ?? ZERO_ADDRESS;
|
|
714
740
|
}
|
|
@@ -100,6 +100,22 @@ export class MockHubPoolClient extends HubPoolClient {
|
|
|
100
100
|
this.spokePoolTokens[l1Token][chainId] = l2Token;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
l2TokenEnabledForL1TokenAtBlock(l1Token: string, destinationChainId: number, hubBlockNumber: number): boolean {
|
|
104
|
+
if (this.spokePoolTokens[l1Token]?.[destinationChainId]) {
|
|
105
|
+
return true;
|
|
106
|
+
} else {
|
|
107
|
+
return super.l2TokenEnabledForL1TokenAtBlock(l1Token, destinationChainId, hubBlockNumber);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
l2TokenHasPoolRebalanceRoute(l2Token: string, chainId: number, hubPoolBlock: number): boolean {
|
|
111
|
+
const l1Token = Object.keys(this.spokePoolTokens).find(
|
|
112
|
+
(l1Token) => this.spokePoolTokens[l1Token]?.[chainId] === l2Token
|
|
113
|
+
);
|
|
114
|
+
if (!l1Token) {
|
|
115
|
+
return super.l2TokenHasPoolRebalanceRoute(l2Token, chainId, hubPoolBlock);
|
|
116
|
+
} else return true;
|
|
117
|
+
}
|
|
118
|
+
|
|
103
119
|
deleteTokenMapping(l1Token: string, chainId: number) {
|
|
104
120
|
delete this.spokePoolTokens[l1Token]?.[chainId];
|
|
105
121
|
}
|
package/src/constants.ts
CHANGED
|
@@ -66,6 +66,19 @@ export const BRIDGED_USDC_SYMBOLS = [
|
|
|
66
66
|
TOKEN_SYMBOLS_MAP.USDzC.symbol,
|
|
67
67
|
];
|
|
68
68
|
|
|
69
|
+
export const STABLE_COIN_SYMBOLS = [
|
|
70
|
+
...BRIDGED_USDC_SYMBOLS,
|
|
71
|
+
TOKEN_SYMBOLS_MAP.USDB.symbol,
|
|
72
|
+
TOKEN_SYMBOLS_MAP.USDC.symbol,
|
|
73
|
+
TOKEN_SYMBOLS_MAP.USDT.symbol,
|
|
74
|
+
TOKEN_SYMBOLS_MAP.DAI.symbol,
|
|
75
|
+
TOKEN_SYMBOLS_MAP["TATARA-USDC"].symbol,
|
|
76
|
+
TOKEN_SYMBOLS_MAP["TATARA-USDT"].symbol,
|
|
77
|
+
TOKEN_SYMBOLS_MAP["TATARA-USDS"].symbol,
|
|
78
|
+
TOKEN_SYMBOLS_MAP.GHO.symbol,
|
|
79
|
+
TOKEN_SYMBOLS_MAP.WGHO.symbol,
|
|
80
|
+
];
|
|
81
|
+
|
|
69
82
|
export const CUSTOM_GAS_TOKENS = {
|
|
70
83
|
[CHAIN_IDs.POLYGON]: "MATIC",
|
|
71
84
|
[CHAIN_IDs.POLYGON_AMOY]: "MATIC",
|
package/src/svm/eventsClient.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { getDeployedAddress, SvmSpokeIdl } from "@across-protocol/contracts";
|
|
2
2
|
import { getSolanaChainId } from "@across-protocol/contracts/dist/src/svm/web3-v1";
|
|
3
|
-
import { BorshEventCoder, utils } from "@coral-xyz/anchor";
|
|
4
3
|
import web3, {
|
|
5
4
|
Address,
|
|
6
5
|
Commitment,
|
|
@@ -9,8 +8,9 @@ import web3, {
|
|
|
9
8
|
RpcTransport,
|
|
10
9
|
Signature,
|
|
11
10
|
} from "@solana/kit";
|
|
11
|
+
import { bs58 } from "../utils";
|
|
12
12
|
import { EventData, EventName, EventWithData } from "./types";
|
|
13
|
-
import {
|
|
13
|
+
import { decodeEvent } from "./utils/events";
|
|
14
14
|
import { isDevnet } from "./utils/helpers";
|
|
15
15
|
|
|
16
16
|
// Utility type to extract the return type for the JSON encoding overload. We only care about the overload where the
|
|
@@ -185,17 +185,11 @@ export class SvmSpokeEventsClient {
|
|
|
185
185
|
this.svmSpokeAddress === ixProgramId &&
|
|
186
186
|
this.svmSpokeEventAuthority === singleIxAccount
|
|
187
187
|
) {
|
|
188
|
-
const ixData =
|
|
188
|
+
const ixData = bs58.decode(ix.data);
|
|
189
189
|
// Skip the first 8 bytes (assumed header) and encode the rest.
|
|
190
|
-
const eventData =
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
const name = getEventName(event.name);
|
|
194
|
-
events.push({
|
|
195
|
-
program: this.svmSpokeAddress,
|
|
196
|
-
data: parseEventData(event?.data),
|
|
197
|
-
name,
|
|
198
|
-
});
|
|
190
|
+
const eventData = Buffer.from(ixData.slice(8)).toString("base64");
|
|
191
|
+
const { name, data } = decodeEvent(SvmSpokeIdl, eventData);
|
|
192
|
+
events.push({ program: this.svmSpokeAddress, name, data });
|
|
199
193
|
}
|
|
200
194
|
}
|
|
201
195
|
}
|
package/src/svm/index.ts
CHANGED
package/src/svm/utils/events.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { BN } from "@coral-xyz/anchor";
|
|
2
|
-
import
|
|
3
|
-
import { EventName, SVMEventNames } from "../types";
|
|
1
|
+
import { BN, BorshEventCoder, Idl } from "@coral-xyz/anchor";
|
|
2
|
+
import { address } from "@solana/kit";
|
|
3
|
+
import { EventName, EventData, SVMEventNames } from "../types";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Parses event data from a transaction.
|
|
@@ -15,7 +15,7 @@ export function parseEventData(eventData: any): any {
|
|
|
15
15
|
|
|
16
16
|
if (typeof eventData === "object") {
|
|
17
17
|
if (eventData.constructor.name === "PublicKey") {
|
|
18
|
-
return
|
|
18
|
+
return address(eventData.toString());
|
|
19
19
|
}
|
|
20
20
|
if (BN.isBN(eventData)) {
|
|
21
21
|
return BigInt(eventData.toString());
|
|
@@ -30,6 +30,18 @@ export function parseEventData(eventData: any): any {
|
|
|
30
30
|
return eventData;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Decodes a raw event according to a supplied IDL.
|
|
35
|
+
*/
|
|
36
|
+
export function decodeEvent(idl: Idl, rawEvent: string): { data: EventData; name: EventName } {
|
|
37
|
+
const event = new BorshEventCoder(idl).decode(rawEvent);
|
|
38
|
+
if (!event) throw new Error(`Malformed rawEvent for IDL ${idl.address}: ${rawEvent}`);
|
|
39
|
+
return {
|
|
40
|
+
name: getEventName(event.name),
|
|
41
|
+
data: parseEventData(event.data),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
33
45
|
/**
|
|
34
46
|
* Converts a snake_case string to camelCase.
|
|
35
47
|
*/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./events";
|
|
@@ -2,7 +2,7 @@ import assert from "assert";
|
|
|
2
2
|
import { SpokePoolClient } from "../clients";
|
|
3
3
|
import { DEFAULT_CACHING_TTL, EMPTY_MESSAGE, UNDEFINED_MESSAGE_HASH, ZERO_BYTES } from "../constants";
|
|
4
4
|
import { CachingMechanismInterface, Deposit, DepositWithBlock, Fill, RelayData, SlowFillRequest } from "../interfaces";
|
|
5
|
-
import { getMessageHash, isUnsafeDepositId } from "./SpokeUtils";
|
|
5
|
+
import { getMessageHash, isUnsafeDepositId, isZeroAddress } from "./SpokeUtils";
|
|
6
6
|
import { getNetworkName } from "./NetworkUtils";
|
|
7
7
|
import { bnZero } from "./BigNumberUtils";
|
|
8
8
|
import { getDepositInCache, getDepositKey, setDepositInCache } from "./CachingUtils";
|
|
@@ -208,6 +208,11 @@ export function isZeroValueDeposit(deposit: Pick<Deposit, "inputAmount" | "messa
|
|
|
208
208
|
return deposit.inputAmount.eq(0) && isMessageEmpty(deposit.message);
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
+
export function invalidOutputToken(deposit: Pick<DepositWithBlock, "outputToken">): boolean {
|
|
212
|
+
// If the output token is zero address, then it is invalid.
|
|
213
|
+
return isZeroAddress(deposit.outputToken);
|
|
214
|
+
}
|
|
215
|
+
|
|
211
216
|
export function isZeroValueFillOrSlowFillRequest(
|
|
212
217
|
e: Pick<Fill | SlowFillRequest, "inputAmount" | "messageHash">
|
|
213
218
|
): boolean {
|
package/src/utils/TokenUtils.ts
CHANGED
|
@@ -109,6 +109,12 @@ export function isBridgedUsdc(tokenSymbol: string): boolean {
|
|
|
109
109
|
);
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
export function isStablecoin(tokenSymbol: string): boolean {
|
|
113
|
+
return constants.STABLE_COIN_SYMBOLS.some(
|
|
114
|
+
(stablecoinSymbol) => stablecoinSymbol.toLowerCase() === tokenSymbol.toLowerCase()
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
112
118
|
export function getTokenInfo(l2TokenAddress: string, chainId: number): L1Token {
|
|
113
119
|
// @dev This might give false positives if tokens on different networks have the same address. I'm not sure how
|
|
114
120
|
// to get around this...
|
package/src/utils/common.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Decimal from "decimal.js";
|
|
2
|
+
import bs58 from "bs58";
|
|
2
3
|
import { ethers } from "ethers";
|
|
3
4
|
import { BigNumber, BigNumberish, BN, formatUnits, parseUnits, toBN } from "./BigNumberUtils";
|
|
4
5
|
import { ConvertDecimals } from "./FormattingUtils";
|
|
@@ -8,6 +9,7 @@ export const AddressZero = ethers.constants.AddressZero;
|
|
|
8
9
|
export const MAX_BIG_INT = BigNumber.from(Number.MAX_SAFE_INTEGER.toString());
|
|
9
10
|
|
|
10
11
|
export const { keccak256 } = ethers.utils;
|
|
12
|
+
export { bs58 };
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* toBNWei.
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { Logger } from "../../utils";
|
|
2
|
-
export { Logger } from "../../utils";
|
|
3
|
-
export declare function logError(name: string, error: unknown, logger?: Logger): Promise<string[]>;
|
|
4
|
-
export declare function fetch(name: string, url: string, timeout?: number, retries?: number): Promise<unknown>;
|