@across-protocol/sdk 3.1.17 → 3.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/dist/cjs/index.d.ts +1 -0
  2. package/dist/cjs/index.js +2 -1
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/cjs/providers/cachedProvider.d.ts +17 -0
  5. package/dist/cjs/providers/cachedProvider.js +149 -0
  6. package/dist/cjs/providers/cachedProvider.js.map +1 -0
  7. package/dist/cjs/providers/constants.d.ts +4 -0
  8. package/dist/cjs/providers/constants.js +8 -0
  9. package/dist/cjs/providers/constants.js.map +1 -0
  10. package/dist/cjs/providers/index.d.ts +5 -0
  11. package/dist/cjs/providers/index.js +9 -0
  12. package/dist/cjs/providers/index.js.map +1 -0
  13. package/dist/cjs/providers/rateLimitedProvider.d.ts +10 -0
  14. package/dist/cjs/providers/rateLimitedProvider.js +88 -0
  15. package/dist/cjs/providers/rateLimitedProvider.js.map +1 -0
  16. package/dist/cjs/providers/retryProvider.d.ts +16 -0
  17. package/dist/cjs/providers/retryProvider.js +224 -0
  18. package/dist/cjs/providers/retryProvider.js.map +1 -0
  19. package/dist/cjs/providers/utils.d.ts +21 -0
  20. package/dist/cjs/providers/utils.js +63 -0
  21. package/dist/cjs/providers/utils.js.map +1 -0
  22. package/dist/cjs/relayFeeCalculator/chain-queries/factory.d.ts +1 -1
  23. package/dist/cjs/relayFeeCalculator/chain-queries/factory.js +3 -0
  24. package/dist/cjs/relayFeeCalculator/chain-queries/factory.js.map +1 -1
  25. package/dist/cjs/relayFeeCalculator/chain-queries/polygon.d.ts +1 -1
  26. package/dist/cjs/relayFeeCalculator/chain-queries/polygon.js +3 -0
  27. package/dist/cjs/relayFeeCalculator/chain-queries/polygon.js.map +1 -1
  28. package/dist/cjs/utils/NetworkUtils.d.ts +1 -0
  29. package/dist/cjs/utils/NetworkUtils.js +10 -1
  30. package/dist/cjs/utils/NetworkUtils.js.map +1 -1
  31. package/dist/cjs/utils/ObjectUtils.js.map +1 -1
  32. package/dist/esm/index.d.ts +1 -0
  33. package/dist/esm/index.js +2 -0
  34. package/dist/esm/index.js.map +1 -1
  35. package/dist/esm/providers/cachedProvider.d.ts +17 -0
  36. package/dist/esm/providers/cachedProvider.js +167 -0
  37. package/dist/esm/providers/cachedProvider.js.map +1 -0
  38. package/dist/esm/providers/constants.d.ts +7 -0
  39. package/dist/esm/providers/constants.js +10 -0
  40. package/dist/esm/providers/constants.js.map +1 -0
  41. package/dist/esm/providers/index.d.ts +5 -0
  42. package/dist/esm/providers/index.js +6 -0
  43. package/dist/esm/providers/index.js.map +1 -0
  44. package/dist/esm/providers/rateLimitedProvider.d.ts +10 -0
  45. package/dist/esm/providers/rateLimitedProvider.js +101 -0
  46. package/dist/esm/providers/rateLimitedProvider.js.map +1 -0
  47. package/dist/esm/providers/retryProvider.d.ts +16 -0
  48. package/dist/esm/providers/retryProvider.js +249 -0
  49. package/dist/esm/providers/retryProvider.js.map +1 -0
  50. package/dist/esm/providers/utils.d.ts +39 -0
  51. package/dist/esm/providers/utils.js +89 -0
  52. package/dist/esm/providers/utils.js.map +1 -0
  53. package/dist/esm/relayFeeCalculator/chain-queries/factory.d.ts +1 -1
  54. package/dist/esm/relayFeeCalculator/chain-queries/factory.js +3 -1
  55. package/dist/esm/relayFeeCalculator/chain-queries/factory.js.map +1 -1
  56. package/dist/esm/relayFeeCalculator/chain-queries/polygon.d.ts +1 -1
  57. package/dist/esm/relayFeeCalculator/chain-queries/polygon.js +3 -0
  58. package/dist/esm/relayFeeCalculator/chain-queries/polygon.js.map +1 -1
  59. package/dist/esm/utils/NetworkUtils.d.ts +6 -0
  60. package/dist/esm/utils/NetworkUtils.js +13 -0
  61. package/dist/esm/utils/NetworkUtils.js.map +1 -1
  62. package/dist/esm/utils/ObjectUtils.js +1 -1
  63. package/dist/esm/utils/ObjectUtils.js.map +1 -1
  64. package/dist/types/index.d.ts +1 -0
  65. package/dist/types/index.d.ts.map +1 -1
  66. package/dist/types/providers/cachedProvider.d.ts +18 -0
  67. package/dist/types/providers/cachedProvider.d.ts.map +1 -0
  68. package/dist/types/providers/constants.d.ts +8 -0
  69. package/dist/types/providers/constants.d.ts.map +1 -0
  70. package/dist/types/providers/index.d.ts +6 -0
  71. package/dist/types/providers/index.d.ts.map +1 -0
  72. package/dist/types/providers/rateLimitedProvider.d.ts +11 -0
  73. package/dist/types/providers/rateLimitedProvider.d.ts.map +1 -0
  74. package/dist/types/providers/retryProvider.d.ts +17 -0
  75. package/dist/types/providers/retryProvider.d.ts.map +1 -0
  76. package/dist/types/providers/utils.d.ts +40 -0
  77. package/dist/types/providers/utils.d.ts.map +1 -0
  78. package/dist/types/relayFeeCalculator/chain-queries/factory.d.ts +1 -1
  79. package/dist/types/relayFeeCalculator/chain-queries/factory.d.ts.map +1 -1
  80. package/dist/types/relayFeeCalculator/chain-queries/polygon.d.ts +1 -1
  81. package/dist/types/relayFeeCalculator/chain-queries/polygon.d.ts.map +1 -1
  82. package/dist/types/utils/NetworkUtils.d.ts +6 -0
  83. package/dist/types/utils/NetworkUtils.d.ts.map +1 -1
  84. package/dist/types/utils/ObjectUtils.d.ts.map +1 -1
  85. package/package.json +6 -2
  86. package/src/index.ts +1 -0
  87. package/src/providers/cachedProvider.ts +155 -0
  88. package/src/providers/constants.ts +11 -0
  89. package/src/providers/index.ts +5 -0
  90. package/src/providers/rateLimitedProvider.ts +94 -0
  91. package/src/providers/retryProvider.ts +262 -0
  92. package/src/providers/utils.ts +119 -0
  93. package/src/relayFeeCalculator/chain-queries/factory.ts +4 -1
  94. package/src/relayFeeCalculator/chain-queries/polygon.ts +3 -0
  95. package/src/utils/NetworkUtils.ts +13 -0
  96. package/src/utils/ObjectUtils.ts +1 -0
@@ -0,0 +1,262 @@
1
+ import { ethers, logger } from "ethers";
2
+ import { CachingMechanismInterface } from "../interfaces";
3
+ import { delay, isDefined, isPromiseFulfilled, isPromiseRejected } from "../utils";
4
+ import { getOriginFromURL } from "../utils/NetworkUtils";
5
+ import { CacheProvider } from "./cachedProvider";
6
+ import { compareRpcResults, createSendErrorWithMessage, formatProviderError } from "./utils";
7
+ import { AugmentedLogger } from "@uma/logger";
8
+ import { PROVIDER_CACHE_TTL } from "./constants";
9
+
10
+ export class RetryProvider extends ethers.providers.StaticJsonRpcProvider {
11
+ readonly providers: ethers.providers.StaticJsonRpcProvider[];
12
+ constructor(
13
+ params: ConstructorParameters<typeof ethers.providers.StaticJsonRpcProvider>[],
14
+ chainId: number,
15
+ readonly nodeQuorumThreshold: number,
16
+ readonly retries: number,
17
+ readonly delay: number,
18
+ readonly maxConcurrency: number,
19
+ providerCacheNamespace: string,
20
+ pctRpcCallsLogged: number,
21
+ redisClient?: CachingMechanismInterface,
22
+ standardTtlBlockDistance?: number,
23
+ noTtlBlockDistance?: number,
24
+ providerCacheTtl = PROVIDER_CACHE_TTL,
25
+ logger?: AugmentedLogger
26
+ ) {
27
+ // Initialize the super just with the chainId, which stops it from trying to immediately send out a .send before
28
+ // this derived class is initialized.
29
+ super(undefined, chainId);
30
+ this.providers = params.map(
31
+ (inputs) =>
32
+ new CacheProvider(
33
+ providerCacheNamespace,
34
+ redisClient,
35
+ standardTtlBlockDistance,
36
+ noTtlBlockDistance,
37
+ providerCacheTtl,
38
+ maxConcurrency,
39
+ pctRpcCallsLogged,
40
+ logger,
41
+ ...inputs
42
+ )
43
+ );
44
+ if (this.nodeQuorumThreshold < 1 || !Number.isInteger(this.nodeQuorumThreshold)) {
45
+ throw new Error(
46
+ `nodeQuorum,Threshold cannot be < 1 and must be an integer. Currently set to ${this.nodeQuorumThreshold}`
47
+ );
48
+ }
49
+ if (this.retries < 0 || !Number.isInteger(this.retries)) {
50
+ throw new Error(`retries cannot be < 0 and must be an integer. Currently set to ${this.retries}`);
51
+ }
52
+ if (this.delay < 0) {
53
+ throw new Error(`delay cannot be < 0. Currently set to ${this.delay}`);
54
+ }
55
+ if (this.nodeQuorumThreshold > this.providers.length) {
56
+ throw new Error(
57
+ `nodeQuorumThreshold (${this.nodeQuorumThreshold}) must be <= the number of providers (${this.providers.length})`
58
+ );
59
+ }
60
+ }
61
+
62
+ override async send(method: string, params: Array<unknown>): Promise<unknown> {
63
+ const quorumThreshold = this._getQuorum(method, params);
64
+ const requiredProviders = this.providers.slice(0, quorumThreshold);
65
+ const fallbackProviders = this.providers.slice(quorumThreshold);
66
+ const errors: [ethers.providers.StaticJsonRpcProvider, string][] = [];
67
+
68
+ // This function is used to try to send with a provider and if it fails pop an element off the fallback list to try
69
+ // with that one. Once the fallback provider list is empty, the method throws. Because the fallback providers are
70
+ // removed, we ensure that no provider is used more than once because we care about quorum, making sure all
71
+ // considered responses come from unique providers.
72
+ const tryWithFallback = (
73
+ provider: ethers.providers.StaticJsonRpcProvider
74
+ ): Promise<[ethers.providers.StaticJsonRpcProvider, unknown]> => {
75
+ return this._trySend(provider, method, params)
76
+ .then((result): [ethers.providers.StaticJsonRpcProvider, unknown] => [provider, result])
77
+ .catch((err) => {
78
+ // Append the provider and error to the error array.
79
+ errors.push([provider, err?.stack || err?.toString()]);
80
+
81
+ // If there are no new fallback providers to use, terminate the recursion by throwing an error.
82
+ // Otherwise, we can try to call another provider.
83
+ if (fallbackProviders.length === 0) {
84
+ throw err;
85
+ }
86
+
87
+ // This line does two things:
88
+ // 1. Removes a fallback provider from the array so it cannot be used as a fallback for another required
89
+ // provider.
90
+ // 2. Recursively calls this method with that provider so it goes through the same try logic as the previous one.
91
+ return tryWithFallback(fallbackProviders.shift()!);
92
+ });
93
+ };
94
+
95
+ const results = await Promise.allSettled(requiredProviders.map(tryWithFallback));
96
+
97
+ if (!results.every(isPromiseFulfilled)) {
98
+ // Format the error so that it's very clear which providers failed and succeeded.
99
+ const errorTexts = errors.map(([provider, errorText]) => formatProviderError(provider, errorText));
100
+ const successfulProviderUrls = results.filter(isPromiseFulfilled).map((result) => result.value[0].connection.url);
101
+ throw createSendErrorWithMessage(
102
+ `Not enough providers succeeded. Errors:\n${errorTexts.join("\n")}\n` +
103
+ `Successful Providers:\n${successfulProviderUrls.join("\n")}`,
104
+ results.find(isPromiseRejected)?.reason
105
+ );
106
+ }
107
+
108
+ const values = results.map((result) => result.value);
109
+
110
+ // Start at element 1 and begin comparing.
111
+ // If _all_ values are equal, we have hit quorum, so return.
112
+ if (values.slice(1).every(([, output]) => compareRpcResults(method, values[0][1], output))) {
113
+ return values[0][1];
114
+ }
115
+
116
+ const throwQuorumError = () => {
117
+ const errorTexts = errors.map(([provider, errorText]) => formatProviderError(provider, errorText));
118
+ const successfulProviderUrls = values.map(([provider]) => provider.connection.url);
119
+ throw new Error(
120
+ "Not enough providers agreed to meet quorum.\n" +
121
+ "Providers that errored:\n" +
122
+ `${errorTexts.join("\n")}\n` +
123
+ "Providers that succeeded, but some failed to match:\n" +
124
+ successfulProviderUrls.join("\n")
125
+ );
126
+ };
127
+
128
+ // Exit early if there are no fallback providers left.
129
+ if (fallbackProviders.length === 0) {
130
+ throwQuorumError();
131
+ }
132
+
133
+ // Try each fallback provider in parallel.
134
+ const fallbackResults = await Promise.allSettled(
135
+ fallbackProviders.map((provider) =>
136
+ this._trySend(provider, method, params)
137
+ .then((result): [ethers.providers.StaticJsonRpcProvider, unknown] => [provider, result])
138
+ .catch((err) => {
139
+ errors.push([provider, err?.stack || err?.toString()]);
140
+ throw new Error("No fallbacks during quorum search");
141
+ })
142
+ )
143
+ );
144
+
145
+ // This filters only the fallbacks that succeeded.
146
+ const fallbackValues = fallbackResults.filter(isPromiseFulfilled).map((promise) => promise.value);
147
+
148
+ // Group the results by the count of that result.
149
+ const counts = [...values, ...fallbackValues].reduce(
150
+ (acc, curr) => {
151
+ const [, result] = curr;
152
+
153
+ // Find the first result that matches the return value.
154
+ const existingMatch = acc.find(([existingResult]) => compareRpcResults(method, existingResult, result));
155
+
156
+ // Increment the count if a match is found, else add a new element to the match array with a count of 1.
157
+ if (existingMatch) {
158
+ existingMatch[1]++;
159
+ } else {
160
+ acc.push([result, 1]);
161
+ }
162
+
163
+ // Return the same acc object because it was modified in place.
164
+ return acc;
165
+ },
166
+ [[undefined, 0]] as [unknown, number][] // Initialize with [undefined, 0] as the first element so something is always returned.
167
+ );
168
+
169
+ // Sort so the result with the highest count is first.
170
+ counts.sort(([, a], [, b]) => b - a);
171
+
172
+ // Extract the result by grabbing the first element.
173
+ const [quorumResult, count] = counts[0];
174
+
175
+ // If this count is less than we need for quorum, throw the quorum error.
176
+ if (count < quorumThreshold) {
177
+ throwQuorumError();
178
+ }
179
+
180
+ // If we've achieved quorum, then we should still log the providers that mismatched with the quorum result.
181
+ const mismatchedProviders = Object.fromEntries(
182
+ [...values, ...fallbackValues]
183
+ .filter(([, result]) => !compareRpcResults(method, result, quorumResult))
184
+ .map(([provider, result]) => [provider.connection.url, result])
185
+ );
186
+ const quorumProviders = [...values, ...fallbackValues]
187
+ .filter(([, result]) => compareRpcResults(method, result, quorumResult))
188
+ .map(([provider]) => provider.connection.url);
189
+ if (Object.keys(mismatchedProviders).length > 0 || errors.length > 0) {
190
+ logger.warn({
191
+ at: "ProviderUtils",
192
+ message: "Some providers mismatched with the quorum result or failed 🚸",
193
+ notificationPath: "across-warn",
194
+ method,
195
+ params,
196
+ quorumProviders,
197
+ mismatchedProviders,
198
+ erroringProviders: errors.map(([provider, errorText]) => formatProviderError(provider, errorText)),
199
+ });
200
+ }
201
+
202
+ return quorumResult;
203
+ }
204
+
205
+ _validateResponse(method: string, _: Array<unknown>, response: unknown): boolean {
206
+ // Basic validation logic to start.
207
+ // Note: eth_getTransactionReceipt is ignored here because null responses are expected in the case that ethers is
208
+ // polling for the transaction receipt and receiving null until it does.
209
+ return isDefined(response) || method === "eth_getTransactionReceipt";
210
+ }
211
+
212
+ async _sendAndValidate(
213
+ provider: ethers.providers.StaticJsonRpcProvider,
214
+ method: string,
215
+ params: Array<unknown>
216
+ ): Promise<unknown> {
217
+ const response = await provider.send(method, params);
218
+ if (!this._validateResponse(method, params, response)) {
219
+ // Not a warning to avoid spam since this could trigger a lot.
220
+ logger.debug({
221
+ at: "ProviderUtils",
222
+ message: "Provider returned invalid response",
223
+ provider: getOriginFromURL(provider.connection.url),
224
+ method,
225
+ params,
226
+ response,
227
+ });
228
+ throw new Error("Response failed validation");
229
+ }
230
+ return response;
231
+ }
232
+
233
+ _trySend(provider: ethers.providers.StaticJsonRpcProvider, method: string, params: Array<unknown>): Promise<unknown> {
234
+ let promise = this._sendAndValidate(provider, method, params);
235
+ for (let i = 0; i < this.retries; i++) {
236
+ promise = promise.catch(() => delay(this.delay).then(() => this._sendAndValidate(provider, method, params)));
237
+ }
238
+ return promise;
239
+ }
240
+
241
+ _getQuorum(method: string, params: Array<unknown>): number {
242
+ // Only use quorum if this is a historical query that doesn't depend on the current block number.
243
+
244
+ // All logs queries should use quorum.
245
+ if (method === "eth_getLogs") {
246
+ return this.nodeQuorumThreshold;
247
+ }
248
+
249
+ // getBlockByNumber should only use the quorum if it's not asking for the latest block.
250
+ if (method === "eth_getBlockByNumber" && params[0] !== "latest") {
251
+ return this.nodeQuorumThreshold;
252
+ }
253
+
254
+ // eth_call should only use quorum for queries at a specific past block.
255
+ if (method === "eth_call" && params[1] !== "latest") {
256
+ return this.nodeQuorumThreshold;
257
+ }
258
+
259
+ // All other calls should use quorum 1 to avoid errors due to sync differences.
260
+ return 1;
261
+ }
262
+ }
@@ -0,0 +1,119 @@
1
+ // The async/queue library has a task-based interface for building a concurrent queue.
2
+
3
+ import { providers } from "ethers";
4
+ import { isDefined } from "../utils";
5
+ import { isEqual } from "lodash";
6
+
7
+ /**
8
+ * Deletes keys from an object and returns new copy of object without ignored keys
9
+ * @param ignoredKeys
10
+ * @param obj
11
+ * @returns Objects with ignored keys removed
12
+ */
13
+ function deleteIgnoredKeys(ignoredKeys: string[], obj: Record<string, unknown>) {
14
+ if (!isDefined(obj)) {
15
+ return;
16
+ }
17
+ const newObj = { ...obj };
18
+ for (const key of ignoredKeys) {
19
+ delete newObj[key];
20
+ }
21
+ return newObj;
22
+ }
23
+
24
+ export function compareResultsAndFilterIgnoredKeys(
25
+ ignoredKeys: string[],
26
+ _objA: Record<string, unknown>,
27
+ _objB: Record<string, unknown>
28
+ ): boolean {
29
+ // Remove ignored keys from copied objects.
30
+ const filteredA = deleteIgnoredKeys(ignoredKeys, _objA);
31
+ const filteredB = deleteIgnoredKeys(ignoredKeys, _objB);
32
+
33
+ // Compare objects without the ignored keys.
34
+ return isEqual(filteredA, filteredB);
35
+ }
36
+
37
+ export function compareArrayResultsWithIgnoredKeys(ignoredKeys: string[], objA: unknown[], objB: unknown[]): boolean {
38
+ // Remove ignored keys from each element of copied arrays.
39
+ const filteredA = objA?.map((obj) => deleteIgnoredKeys(ignoredKeys, obj as Record<string, unknown>));
40
+ const filteredB = objB?.map((obj) => deleteIgnoredKeys(ignoredKeys, obj as Record<string, unknown>));
41
+
42
+ // Compare objects without the ignored keys.
43
+ return isDefined(filteredA) && isDefined(filteredB) && isEqual(filteredA, filteredB);
44
+ }
45
+
46
+ /**
47
+ * This is the type we pass to define a request "task".
48
+ */
49
+ export interface RateLimitTask {
50
+ // These are the arguments to be passed to super.send().
51
+ sendArgs: [string, Array<unknown>];
52
+
53
+ // These are the promise callbacks that will cause the initial send call made by the user to either return a result
54
+ // or fail.
55
+ resolve: (result: unknown) => void;
56
+ reject: (err: unknown) => void;
57
+ }
58
+
59
+ /**
60
+ * A helper function to format an error message for a provider.
61
+ * @param provider The provider that failed.
62
+ * @param rawErrorText The raw error text.
63
+ * @returns The formatted error message.
64
+ */
65
+ export function formatProviderError(provider: providers.StaticJsonRpcProvider, rawErrorText: string) {
66
+ return `Provider ${provider.connection.url} failed with error: ${rawErrorText}`;
67
+ }
68
+
69
+ export function createSendErrorWithMessage(message: string, sendError: Record<string, unknown>) {
70
+ const error = new Error(message);
71
+ return { ...sendError, ...error };
72
+ }
73
+
74
+ /**
75
+ * Compares two RPC results, filtering out fields that are known to differ between providers.
76
+ * Note: this function references `IGNORED_ERROR_CODES` which is a record of error codes that correspond to fields
77
+ * that should be ignored when comparing RPC results.
78
+ * @param method The method that was called - conditionally filters out fields based on the method.
79
+ * @param rpcResultA The first RPC result.
80
+ * @param rpcResultB The second RPC result.
81
+ * @returns True if the results are equal, false otherwise.
82
+ */
83
+ export function compareRpcResults(method: string, rpcResultA: unknown, rpcResultB: unknown): boolean {
84
+ if (method === "eth_getBlockByNumber") {
85
+ // We've seen RPC's disagree on the miner field, for example when Polygon nodes updated software that
86
+ // led alchemy and quicknode to disagree on the miner field's value.
87
+ return compareResultsAndFilterIgnoredKeys(
88
+ [
89
+ "miner", // polygon (sometimes)
90
+ "l1BatchNumber", // zkSync
91
+ "l1BatchTimestamp", // zkSync
92
+ "size", // Alchemy/Arbitrum (temporary)
93
+ "totalDifficulty", // Quicknode/Alchemy (sometimes)
94
+ ],
95
+ rpcResultA as Record<string, unknown>,
96
+ rpcResultB as Record<string, unknown>
97
+ );
98
+ } else if (method === "eth_getLogs") {
99
+ // We've seen some RPC's like QuickNode add in transactionLogIndex which isn't in the
100
+ // JSON RPC spec: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getfilterchanges
101
+ // Additional reference: https://github.com/ethers-io/ethers.js/issues/1721
102
+ // 2023-08-31 Added blockHash because of upstream zkSync provider disagreements. Consider removing later.
103
+ // 2024-05-07 Added l1BatchNumber and logType due to Alchemy. Consider removing later.
104
+ // 2024-07-11 Added blockTimestamp after zkSync rolled out a new node release.
105
+ return compareArrayResultsWithIgnoredKeys(
106
+ ["blockTimestamp", "transactionLogIndex", "l1BatchNumber", "logType"],
107
+ rpcResultA as unknown[],
108
+ rpcResultB as unknown[]
109
+ );
110
+ } else {
111
+ return isEqual(rpcResultA, rpcResultB);
112
+ }
113
+ }
114
+
115
+ export enum CacheType {
116
+ NONE, // Do not cache
117
+ WITH_TTL, // Cache with TTL
118
+ NO_TTL, // Cache with infinite TTL
119
+ }
@@ -1,9 +1,10 @@
1
+ import assert from "assert";
1
2
  import { CHAIN_IDs, TOKEN_SYMBOLS_MAP } from "@across-protocol/constants";
2
3
  import { getDeployedAddress } from "@across-protocol/contracts";
3
4
  import { asL2Provider } from "@eth-optimism/sdk";
4
5
  import { providers } from "ethers";
5
6
  import { DEFAULT_SIMULATED_RELAYER_ADDRESS } from "../../constants";
6
- import { chainIsMatic, chainIsOPStack } from "../../utils";
7
+ import { chainIsMatic, chainIsOPStack, isDefined } from "../../utils";
7
8
  import { QueryBase } from "./baseQuery";
8
9
  import { PolygonQueries } from "./polygon";
9
10
  import { DEFAULT_LOGGER, Logger } from "../relayFeeCalculator";
@@ -28,6 +29,8 @@ export class QueryBase__factory {
28
29
  gasMarkup = 0,
29
30
  coingeckoBaseCurrency = "eth"
30
31
  ): QueryBase {
32
+ assert(isDefined(spokePoolAddress));
33
+
31
34
  // Currently the only chain that has a custom query class is Polygon
32
35
  if (chainIsMatic(chainId)) {
33
36
  return new PolygonQueries(
@@ -1,8 +1,10 @@
1
+ import assert from "assert";
1
2
  import { getDeployedAddress } from "../../utils/DeploymentUtils";
2
3
  import { DEFAULT_LOGGER, Logger } from "../relayFeeCalculator";
3
4
  import { providers } from "ethers";
4
5
  import { CHAIN_IDs, DEFAULT_SIMULATED_RELAYER_ADDRESS, TOKEN_SYMBOLS_MAP } from "../../constants";
5
6
  import { Coingecko } from "../../coingecko/Coingecko";
7
+ import { isDefined } from "../../utils";
6
8
  import { QueryBase } from "./baseQuery";
7
9
 
8
10
  export class PolygonQueries extends QueryBase {
@@ -15,6 +17,7 @@ export class PolygonQueries extends QueryBase {
15
17
  logger: Logger = DEFAULT_LOGGER,
16
18
  gasMarkup = 0
17
19
  ) {
20
+ assert(isDefined(spokePoolAddress));
18
21
  super(
19
22
  provider,
20
23
  symbolMapping,
@@ -115,3 +115,16 @@ export function chainIsCCTPEnabled(chainId: number): boolean {
115
115
  export function chainRequiresL1ToL2Finalization(chainId: number): boolean {
116
116
  return chainIsCCTPEnabled(chainId) || chainIsLinea(chainId);
117
117
  }
118
+
119
+ /**
120
+ * Returns the origin of a URL.
121
+ * @param url A URL.
122
+ * @returns The origin of the URL, or "UNKNOWN" if the URL is invalid.
123
+ */
124
+ export function getOriginFromURL(url: string): string {
125
+ try {
126
+ return new URL(url).origin;
127
+ } catch (e) {
128
+ return "UNKNOWN";
129
+ }
130
+ }
@@ -2,6 +2,7 @@
2
2
  /* eslint-disable @typescript-eslint/no-explicit-any */
3
3
  // Append value along the keyPath to object. For example assign(deposits, ['1337', '31337'], [{depositId:1}]) will create
4
4
  // deposits = {1337:{31337:[{depositId:1}]}}. Note that if the path into the object exists then this will append. This
5
+
5
6
  // function respects the destination type; if it is an object then deep merge and if an array effectively will push.
6
7
  export function assign(obj: any, keyPath: any[], value: any): void {
7
8
  const lastKeyIndex = keyPath.length - 1;