@across-protocol/sdk 4.3.50 → 4.3.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.d.ts +22 -1
- package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js +79 -10
- package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/SuperstructUtils.d.ts +2 -2
- package/dist/cjs/coingecko/Coingecko.d.ts +7 -0
- package/dist/cjs/coingecko/Coingecko.js +71 -0
- package/dist/cjs/coingecko/Coingecko.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.d.ts +22 -1
- package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js +132 -12
- package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/SuperstructUtils.d.ts +2 -2
- package/dist/esm/coingecko/Coingecko.d.ts +7 -0
- package/dist/esm/coingecko/Coingecko.js +71 -0
- package/dist/esm/coingecko/Coingecko.js.map +1 -1
- package/dist/types/clients/BundleDataClient/utils/DataworkerUtils.d.ts +22 -1
- package/dist/types/clients/BundleDataClient/utils/DataworkerUtils.d.ts.map +1 -1
- package/dist/types/clients/BundleDataClient/utils/SuperstructUtils.d.ts +2 -2
- package/dist/types/coingecko/Coingecko.d.ts +7 -0
- package/dist/types/coingecko/Coingecko.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/clients/BundleDataClient/utils/DataworkerUtils.ts +249 -22
- package/src/coingecko/Coingecko.ts +69 -0
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
PoolRebalanceLeaf,
|
|
11
11
|
Refund,
|
|
12
12
|
RunningBalances,
|
|
13
|
+
SpokePoolClientsByChain,
|
|
13
14
|
} from "../../../interfaces";
|
|
14
15
|
import {
|
|
15
16
|
bnZero,
|
|
@@ -18,6 +19,9 @@ import {
|
|
|
18
19
|
count2DDictionaryValues,
|
|
19
20
|
count3DDictionaryValues,
|
|
20
21
|
toAddressType,
|
|
22
|
+
getImpliedBundleBlockRanges,
|
|
23
|
+
isDefined,
|
|
24
|
+
EvmAddress,
|
|
21
25
|
} from "../../../utils";
|
|
22
26
|
import {
|
|
23
27
|
addLastRunningBalance,
|
|
@@ -28,6 +32,7 @@ import {
|
|
|
28
32
|
} from "./PoolRebalanceUtils";
|
|
29
33
|
import { AcrossConfigStoreClient } from "../../AcrossConfigStoreClient";
|
|
30
34
|
import { HubPoolClient } from "../../HubPoolClient";
|
|
35
|
+
import { BundleDataClient } from "../../BundleDataClient";
|
|
31
36
|
import { buildPoolRebalanceLeafTree } from "./MerkleTreeUtils";
|
|
32
37
|
|
|
33
38
|
// and expired deposits.
|
|
@@ -114,7 +119,22 @@ export function getEndBlockBuffers(
|
|
|
114
119
|
return chainIdListForBundleEvaluationBlockNumbers.map((chainId: number) => blockRangeEndBlockBuffer[chainId] ?? 0);
|
|
115
120
|
}
|
|
116
121
|
|
|
117
|
-
|
|
122
|
+
/*
|
|
123
|
+
* @notice Constructs a new pool rebalance root given the input bundle data.
|
|
124
|
+
* @dev It is assumed that the input bundle data corresponds to the block ranges of the input mainnetBundleEndBlock.
|
|
125
|
+
* If the mainnetBundleEndBlock does not correspond to any historical or pending root bundle, then the output pool rebalance
|
|
126
|
+
* root will be constructed under the assumption that the pending root bundle is valid and passes liveness.
|
|
127
|
+
* @param latestMainnetBlock The latest mainnet block number.
|
|
128
|
+
* @param mainnetBundleEndBlock The end block number of the block range corresponding to the bundle data.
|
|
129
|
+
* @param bundleV3Deposits Deposit bundle data for the implied block range given by the mainnetBundleEndBlock.
|
|
130
|
+
* @param bundleFillsV3 Fill bundle data.
|
|
131
|
+
* @param bundleSlowFillsV3 Slow fill bundle data.
|
|
132
|
+
* @param unexecutableSlowFills Expired slow fill bundle data.
|
|
133
|
+
* @param expiredDepositsToRefundV3 Expired deposit bundle data.
|
|
134
|
+
* @param clients Clients required to construct a new pool rebalance root.
|
|
135
|
+
* @maxL1TokenCountOverride Optional parameter to cap the number of tokens in a single pool rebalance leaf.
|
|
136
|
+
*/
|
|
137
|
+
export async function _buildPoolRebalanceRoot(
|
|
118
138
|
latestMainnetBlock: number,
|
|
119
139
|
mainnetBundleEndBlock: number,
|
|
120
140
|
bundleV3Deposits: BundleDepositsV3,
|
|
@@ -122,9 +142,235 @@ export function _buildPoolRebalanceRoot(
|
|
|
122
142
|
bundleSlowFillsV3: BundleSlowFills,
|
|
123
143
|
unexecutableSlowFills: BundleExcessSlowFills,
|
|
124
144
|
expiredDepositsToRefundV3: ExpiredDepositsToRefundV3,
|
|
125
|
-
clients: {
|
|
145
|
+
clients: {
|
|
146
|
+
hubPoolClient: HubPoolClient;
|
|
147
|
+
configStoreClient: AcrossConfigStoreClient;
|
|
148
|
+
bundleDataClient: BundleDataClient;
|
|
149
|
+
spokePoolClients: SpokePoolClientsByChain;
|
|
150
|
+
},
|
|
151
|
+
maxL1TokenCountOverride?: number
|
|
152
|
+
): Promise<PoolRebalanceRoot> {
|
|
153
|
+
// If there is a pending proposal and the mainnet bundle end block is greater than the pending proposal's mainnet end block, then this pool rebalance root is being built during the liveness
|
|
154
|
+
// of a different root bundle, so running balance calculations will be slightly different.
|
|
155
|
+
if (
|
|
156
|
+
clients.hubPoolClient.hasPendingProposal() &&
|
|
157
|
+
clients.hubPoolClient.getPendingRootBundle()!.bundleEvaluationBlockNumbers[0] < mainnetBundleEndBlock
|
|
158
|
+
) {
|
|
159
|
+
return await _buildOptimisticPoolRebalanceRoot(
|
|
160
|
+
latestMainnetBlock,
|
|
161
|
+
mainnetBundleEndBlock,
|
|
162
|
+
bundleV3Deposits,
|
|
163
|
+
bundleFillsV3,
|
|
164
|
+
bundleSlowFillsV3,
|
|
165
|
+
unexecutableSlowFills,
|
|
166
|
+
expiredDepositsToRefundV3,
|
|
167
|
+
clients,
|
|
168
|
+
maxL1TokenCountOverride
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
// Otherwise, we can synchronously reconstruct a historical pool rebalance root from the input data.
|
|
172
|
+
return _buildHistoricalPoolRebalanceRoot(
|
|
173
|
+
latestMainnetBlock,
|
|
174
|
+
mainnetBundleEndBlock,
|
|
175
|
+
bundleV3Deposits,
|
|
176
|
+
bundleFillsV3,
|
|
177
|
+
bundleSlowFillsV3,
|
|
178
|
+
unexecutableSlowFills,
|
|
179
|
+
expiredDepositsToRefundV3,
|
|
180
|
+
clients,
|
|
181
|
+
maxL1TokenCountOverride
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/*
|
|
186
|
+
* @notice Constructs a new pool rebalance root given historical bundle data.
|
|
187
|
+
* @param latestMainnetBlock The latest mainnet block number.
|
|
188
|
+
* @param mainnetBundleEndBlock The end block number of the block range corresponding to the bundle data.
|
|
189
|
+
* @param bundleV3Deposits Deposit bundle data for the implied block range given by the mainnetBundleEndBlock.
|
|
190
|
+
* @param bundleFillsV3 Fill bundle data.
|
|
191
|
+
* @param bundleSlowFillsV3 Slow fill bundle data.
|
|
192
|
+
* @param unexecutableSlowFills Expired slow fill bundle data.
|
|
193
|
+
* @param expiredDepositsToRefundV3 Expired deposit bundle data.
|
|
194
|
+
* @param clients Clients required to construct a new pool rebalance root.
|
|
195
|
+
* @maxL1TokenCountOverride Optional parameter to cap the number of tokens in a single pool rebalance leaf.
|
|
196
|
+
*/
|
|
197
|
+
export function _buildHistoricalPoolRebalanceRoot(
|
|
198
|
+
latestMainnetBlock: number,
|
|
199
|
+
mainnetBundleEndBlock: number,
|
|
200
|
+
bundleV3Deposits: BundleDepositsV3,
|
|
201
|
+
bundleFillsV3: BundleFillsV3,
|
|
202
|
+
bundleSlowFillsV3: BundleSlowFills,
|
|
203
|
+
unexecutableSlowFills: BundleExcessSlowFills,
|
|
204
|
+
expiredDepositsToRefundV3: ExpiredDepositsToRefundV3,
|
|
205
|
+
clients: {
|
|
206
|
+
hubPoolClient: HubPoolClient;
|
|
207
|
+
configStoreClient: AcrossConfigStoreClient;
|
|
208
|
+
},
|
|
126
209
|
maxL1TokenCountOverride?: number
|
|
127
210
|
): PoolRebalanceRoot {
|
|
211
|
+
const { runningBalances, realizedLpFees, chainWithRefundsOnly } = _getMarginalRunningBalances(
|
|
212
|
+
mainnetBundleEndBlock,
|
|
213
|
+
bundleV3Deposits,
|
|
214
|
+
bundleFillsV3,
|
|
215
|
+
bundleSlowFillsV3,
|
|
216
|
+
unexecutableSlowFills,
|
|
217
|
+
expiredDepositsToRefundV3,
|
|
218
|
+
clients
|
|
219
|
+
);
|
|
220
|
+
addLastRunningBalance(latestMainnetBlock, runningBalances, clients.hubPoolClient);
|
|
221
|
+
const leaves: PoolRebalanceLeaf[] = constructPoolRebalanceLeaves(
|
|
222
|
+
mainnetBundleEndBlock,
|
|
223
|
+
runningBalances,
|
|
224
|
+
realizedLpFees,
|
|
225
|
+
Array.from(chainWithRefundsOnly).filter((chainId) => !Object.keys(runningBalances).includes(chainId.toString())),
|
|
226
|
+
clients.configStoreClient,
|
|
227
|
+
maxL1TokenCountOverride
|
|
228
|
+
);
|
|
229
|
+
return {
|
|
230
|
+
runningBalances,
|
|
231
|
+
realizedLpFees,
|
|
232
|
+
leaves,
|
|
233
|
+
tree: buildPoolRebalanceLeafTree(leaves),
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/*
|
|
238
|
+
* @notice Constructs a new pool rebalance root given the input bundle data. This function assumes there is a pending root bundle which will eventually clear liveness.
|
|
239
|
+
* @param latestMainnetBlock The latest mainnet block number.
|
|
240
|
+
* @param mainnetBundleEndBlock The end block number of the block range corresponding to the bundle data.
|
|
241
|
+
* @param bundleV3Deposits Deposit bundle data for the implied block range given by the mainnetBundleEndBlock.
|
|
242
|
+
* @param bundleFillsV3 Fill bundle data.
|
|
243
|
+
* @param bundleSlowFillsV3 Slow fill bundle data.
|
|
244
|
+
* @param unexecutableSlowFills Expired slow fill bundle data.
|
|
245
|
+
* @param expiredDepositsToRefundV3 Expired deposit bundle data.
|
|
246
|
+
* @param clients Clients required to construct a new pool rebalance root.
|
|
247
|
+
* @maxL1TokenCountOverride Optional parameter to cap the number of tokens in a single pool rebalance leaf.
|
|
248
|
+
*/
|
|
249
|
+
export async function _buildOptimisticPoolRebalanceRoot(
|
|
250
|
+
latestMainnetBlock: number,
|
|
251
|
+
mainnetBundleEndBlock: number,
|
|
252
|
+
bundleV3Deposits: BundleDepositsV3,
|
|
253
|
+
bundleFillsV3: BundleFillsV3,
|
|
254
|
+
bundleSlowFillsV3: BundleSlowFills,
|
|
255
|
+
unexecutableSlowFills: BundleExcessSlowFills,
|
|
256
|
+
expiredDepositsToRefundV3: ExpiredDepositsToRefundV3,
|
|
257
|
+
clients: {
|
|
258
|
+
hubPoolClient: HubPoolClient;
|
|
259
|
+
configStoreClient: AcrossConfigStoreClient;
|
|
260
|
+
bundleDataClient: BundleDataClient;
|
|
261
|
+
spokePoolClients: SpokePoolClientsByChain;
|
|
262
|
+
},
|
|
263
|
+
maxL1TokenCountOverride?: number
|
|
264
|
+
): Promise<PoolRebalanceRoot> {
|
|
265
|
+
const { runningBalances, realizedLpFees, chainWithRefundsOnly } = _getMarginalRunningBalances(
|
|
266
|
+
mainnetBundleEndBlock,
|
|
267
|
+
bundleV3Deposits,
|
|
268
|
+
bundleFillsV3,
|
|
269
|
+
bundleSlowFillsV3,
|
|
270
|
+
unexecutableSlowFills,
|
|
271
|
+
expiredDepositsToRefundV3,
|
|
272
|
+
clients
|
|
273
|
+
);
|
|
274
|
+
// Get the pool rebalance root for the pending bundle so that we may account for its calculated running balances.
|
|
275
|
+
// @dev It is safe to index the hub pool client's proposed root bundles here since there is guaranteed to be a pending proposal in this code block.
|
|
276
|
+
const mostRecentProposedRootBundle = clients.hubPoolClient.getLatestProposedRootBundle();
|
|
277
|
+
const blockRangesForChains = getImpliedBundleBlockRanges(
|
|
278
|
+
clients.hubPoolClient,
|
|
279
|
+
clients.configStoreClient,
|
|
280
|
+
mostRecentProposedRootBundle
|
|
281
|
+
);
|
|
282
|
+
// We are loading data from a pending root bundle which should be well into liveness, so we want to use arweave if possible.
|
|
283
|
+
const pendingRootBundleData = await clients.bundleDataClient.loadData(
|
|
284
|
+
blockRangesForChains,
|
|
285
|
+
clients.spokePoolClients,
|
|
286
|
+
true
|
|
287
|
+
);
|
|
288
|
+
// Build the pool rebalance root for the pending root bundle.
|
|
289
|
+
const { leaves, tree } = _buildHistoricalPoolRebalanceRoot(
|
|
290
|
+
latestMainnetBlock,
|
|
291
|
+
blockRangesForChains[0][1],
|
|
292
|
+
pendingRootBundleData.bundleDepositsV3,
|
|
293
|
+
pendingRootBundleData.bundleFillsV3,
|
|
294
|
+
pendingRootBundleData.bundleSlowFillsV3,
|
|
295
|
+
pendingRootBundleData.unexecutableSlowFills,
|
|
296
|
+
pendingRootBundleData.expiredDepositsToRefundV3,
|
|
297
|
+
clients,
|
|
298
|
+
maxL1TokenCountOverride
|
|
299
|
+
);
|
|
300
|
+
// Assert that the rebuilt pool rebalance root matches the pending root bundle's value. If it does not, then we likely misconstructed the pending root bundle and should throw.
|
|
301
|
+
assert(tree.getHexRoot() === mostRecentProposedRootBundle.poolRebalanceRoot);
|
|
302
|
+
|
|
303
|
+
// Only add marginal pending running balances if there is already an entry in `runningBalances`. If there is no entry in `runningBalances`, then
|
|
304
|
+
// The running balance for this entry was unchanged since the last root bundle.
|
|
305
|
+
Object.keys(runningBalances).forEach((_repaymentChainId) => {
|
|
306
|
+
Object.keys(runningBalances[Number(_repaymentChainId)]).forEach((_l1TokenAddress) => {
|
|
307
|
+
const repaymentChainId = Number(_repaymentChainId);
|
|
308
|
+
const l1TokenAddress = EvmAddress.from(_l1TokenAddress);
|
|
309
|
+
const pendingPoolRebalanceLeaf = leaves.find(
|
|
310
|
+
(leaf) => leaf.chainId === repaymentChainId && leaf.l1Tokens.some((l1Token) => l1Token.eq(l1TokenAddress))
|
|
311
|
+
);
|
|
312
|
+
// If the pending pool rebalance root has running balances defined, then add it to `runningBalances`.
|
|
313
|
+
if (isDefined(pendingPoolRebalanceLeaf)) {
|
|
314
|
+
const pendingLeafTokenIdx = pendingPoolRebalanceLeaf.l1Tokens.findIndex((l1Token) =>
|
|
315
|
+
l1Token.eq(l1TokenAddress)
|
|
316
|
+
);
|
|
317
|
+
assert(pendingLeafTokenIdx !== -1);
|
|
318
|
+
const pendingRunningBalanceAmount = pendingPoolRebalanceLeaf.runningBalances[pendingLeafTokenIdx];
|
|
319
|
+
if (!pendingRunningBalanceAmount.eq(bnZero)) {
|
|
320
|
+
updateRunningBalance(runningBalances, repaymentChainId, _l1TokenAddress, pendingRunningBalanceAmount);
|
|
321
|
+
}
|
|
322
|
+
} else {
|
|
323
|
+
// Otherwise, add the last running balance for this token.
|
|
324
|
+
const { runningBalance: lastExecutedBundleRunningBalance } =
|
|
325
|
+
clients.hubPoolClient.getRunningBalanceBeforeBlockForChain(
|
|
326
|
+
latestMainnetBlock,
|
|
327
|
+
repaymentChainId,
|
|
328
|
+
l1TokenAddress
|
|
329
|
+
);
|
|
330
|
+
if (!lastExecutedBundleRunningBalance.eq(bnZero)) {
|
|
331
|
+
updateRunningBalance(runningBalances, repaymentChainId, _l1TokenAddress, lastExecutedBundleRunningBalance);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
const poolRebalanceLeaves: PoolRebalanceLeaf[] = constructPoolRebalanceLeaves(
|
|
337
|
+
mainnetBundleEndBlock,
|
|
338
|
+
runningBalances,
|
|
339
|
+
realizedLpFees,
|
|
340
|
+
Array.from(chainWithRefundsOnly).filter((chainId) => !Object.keys(runningBalances).includes(chainId.toString())),
|
|
341
|
+
clients.configStoreClient,
|
|
342
|
+
maxL1TokenCountOverride
|
|
343
|
+
);
|
|
344
|
+
return {
|
|
345
|
+
runningBalances,
|
|
346
|
+
realizedLpFees,
|
|
347
|
+
leaves: poolRebalanceLeaves,
|
|
348
|
+
tree: buildPoolRebalanceLeafTree(poolRebalanceLeaves),
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/*
|
|
353
|
+
* @notice Gets the running balance amounts derived from the input bundle data.
|
|
354
|
+
* @param mainnetBundleEndBlock The end block number of the block range corresponding to the bundle data.
|
|
355
|
+
* @param bundleV3Deposits Deposit bundle data for the implied block range given by the mainnetBundleEndBlock.
|
|
356
|
+
* @param bundleFillsV3 Fill bundle data.
|
|
357
|
+
* @param bundleSlowFillsV3 Slow fill bundle data.
|
|
358
|
+
* @param unexecutableSlowFills Expired slow fill bundle data.
|
|
359
|
+
* @param expiredDepositsToRefundV3 Expired deposit bundle data.
|
|
360
|
+
* @param clients Clients required to construct a new pool rebalance root.
|
|
361
|
+
*/
|
|
362
|
+
export function _getMarginalRunningBalances(
|
|
363
|
+
mainnetBundleEndBlock: number,
|
|
364
|
+
bundleV3Deposits: BundleDepositsV3,
|
|
365
|
+
bundleFillsV3: BundleFillsV3,
|
|
366
|
+
bundleSlowFillsV3: BundleSlowFills,
|
|
367
|
+
unexecutableSlowFills: BundleExcessSlowFills,
|
|
368
|
+
expiredDepositsToRefundV3: ExpiredDepositsToRefundV3,
|
|
369
|
+
clients: {
|
|
370
|
+
hubPoolClient: HubPoolClient;
|
|
371
|
+
configStoreClient: AcrossConfigStoreClient;
|
|
372
|
+
}
|
|
373
|
+
): { chainWithRefundsOnly: Set<number>; realizedLpFees: RunningBalances; runningBalances: RunningBalances } {
|
|
128
374
|
// Running balances are the amount of tokens that we need to send to each SpokePool to pay for all instant and
|
|
129
375
|
// slow relay refunds. They are decreased by the amount of funds already held by the SpokePool. Balances are keyed
|
|
130
376
|
// by the SpokePool's network and L1 token equivalent of the L2 token to refund.
|
|
@@ -296,24 +542,5 @@ export function _buildPoolRebalanceRoot(
|
|
|
296
542
|
});
|
|
297
543
|
});
|
|
298
544
|
});
|
|
299
|
-
|
|
300
|
-
// Add to the running balance value from the last valid root bundle proposal for {chainId, l1Token}
|
|
301
|
-
// combination if found.
|
|
302
|
-
addLastRunningBalance(latestMainnetBlock, runningBalances, clients.hubPoolClient);
|
|
303
|
-
|
|
304
|
-
const leaves: PoolRebalanceLeaf[] = constructPoolRebalanceLeaves(
|
|
305
|
-
mainnetBundleEndBlock,
|
|
306
|
-
runningBalances,
|
|
307
|
-
realizedLpFees,
|
|
308
|
-
Array.from(chainWithRefundsOnly).filter((chainId) => !Object.keys(runningBalances).includes(chainId.toString())),
|
|
309
|
-
clients.configStoreClient,
|
|
310
|
-
maxL1TokenCountOverride
|
|
311
|
-
);
|
|
312
|
-
|
|
313
|
-
return {
|
|
314
|
-
runningBalances,
|
|
315
|
-
realizedLpFees,
|
|
316
|
-
leaves,
|
|
317
|
-
tree: buildPoolRebalanceLeafTree(leaves),
|
|
318
|
-
};
|
|
545
|
+
return { runningBalances, chainWithRefundsOnly, realizedLpFees };
|
|
319
546
|
}
|
|
@@ -29,6 +29,12 @@ type PriceCache = {
|
|
|
29
29
|
};
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
type PriceCacheBySymbol = {
|
|
33
|
+
[symbol: string]: {
|
|
34
|
+
[currency: string]: Omit<CoinGeckoPrice, "address">;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
|
|
32
38
|
type CGTokenPrice = {
|
|
33
39
|
[currency: string]: number;
|
|
34
40
|
last_updated_at: number;
|
|
@@ -61,6 +67,7 @@ export type HistoricPriceChartData = {
|
|
|
61
67
|
export class Coingecko {
|
|
62
68
|
private static instance: Coingecko | undefined;
|
|
63
69
|
private prices: PriceCache;
|
|
70
|
+
private pricesBySymbol: PriceCacheBySymbol;
|
|
64
71
|
private _maxPriceAge = 300; // seconds
|
|
65
72
|
|
|
66
73
|
// Retry configuration.
|
|
@@ -103,6 +110,7 @@ export class Coingecko {
|
|
|
103
110
|
private readonly customPlatformIdMap?: Record<number, string>
|
|
104
111
|
) {
|
|
105
112
|
this.prices = {};
|
|
113
|
+
this.pricesBySymbol = {};
|
|
106
114
|
}
|
|
107
115
|
|
|
108
116
|
protected async getPlatformId(chainId: number): Promise<string> {
|
|
@@ -281,6 +289,29 @@ export class Coingecko {
|
|
|
281
289
|
return [tokenPrice.timestamp.toString(), tokenPrice.price];
|
|
282
290
|
}
|
|
283
291
|
|
|
292
|
+
async getCurrentPriceBySymbol(symbol: string, currency = "usd"): Promise<[string, number]> {
|
|
293
|
+
let tokenPrice = this.getCachedSymbolPrice(symbol, currency);
|
|
294
|
+
if (tokenPrice === undefined) {
|
|
295
|
+
const result = await this.call<Record<string, CGTokenPrice>>(
|
|
296
|
+
`simple/price?symbols=${symbol}&vs_currencies=${currency}&include_last_updated_at=true`
|
|
297
|
+
);
|
|
298
|
+
const cgPrice = result?.[symbol.toLowerCase()];
|
|
299
|
+
if (cgPrice === undefined || !cgPrice?.[currency]) {
|
|
300
|
+
const errMsg = `Failed to retrieve ${symbol}/${currency} price via Coingecko API`;
|
|
301
|
+
this.logger.debug({
|
|
302
|
+
at: "Coingecko#getCurrentPriceBySymbol",
|
|
303
|
+
message: errMsg,
|
|
304
|
+
});
|
|
305
|
+
throw new Error(errMsg);
|
|
306
|
+
} else {
|
|
307
|
+
this.updatePriceCacheBySymbol(cgPrice, symbol, currency);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
tokenPrice = this.getCachedSymbolPrice(symbol, currency);
|
|
311
|
+
assert(tokenPrice !== undefined);
|
|
312
|
+
return [tokenPrice.timestamp.toString(), tokenPrice.price];
|
|
313
|
+
}
|
|
314
|
+
|
|
284
315
|
// Return an array of spot prices for an array of collateral addresses in one async call. Note we might in future
|
|
285
316
|
// This was adapted from packages/merkle-distributor/kpi-options-helpers/calculate-uma-tvl.ts
|
|
286
317
|
async getContractPrices(
|
|
@@ -381,6 +412,11 @@ export class Coingecko {
|
|
|
381
412
|
return this.prices[platform_id][currency];
|
|
382
413
|
}
|
|
383
414
|
|
|
415
|
+
protected getPriceCacheBySymbol(symbol: string): { [currency: string]: Omit<CoinGeckoPrice, "address"> } {
|
|
416
|
+
if (this.pricesBySymbol[symbol] === undefined) this.pricesBySymbol[symbol] = {};
|
|
417
|
+
return this.pricesBySymbol[symbol];
|
|
418
|
+
}
|
|
419
|
+
|
|
384
420
|
protected getCachedAddressPrice(
|
|
385
421
|
contractAddress: string,
|
|
386
422
|
currency: string,
|
|
@@ -409,6 +445,16 @@ export class Coingecko {
|
|
|
409
445
|
}
|
|
410
446
|
}
|
|
411
447
|
|
|
448
|
+
protected getCachedSymbolPrice(symbol: string, currency: string): Omit<CoinGeckoPrice, "address"> | undefined {
|
|
449
|
+
const priceCache = this.getPriceCacheBySymbol(symbol);
|
|
450
|
+
const now: number = msToS(Date.now());
|
|
451
|
+
const tokenPrice: Omit<CoinGeckoPrice, "address"> | undefined = priceCache[currency];
|
|
452
|
+
if (tokenPrice === undefined || tokenPrice.timestamp + this.maxPriceAge <= now) {
|
|
453
|
+
return undefined;
|
|
454
|
+
}
|
|
455
|
+
return tokenPrice;
|
|
456
|
+
}
|
|
457
|
+
|
|
412
458
|
protected updatePriceCache(cgPrice: CGTokenPrice, contractAddress: string, currency: string, platform_id: string) {
|
|
413
459
|
const priceCache = this.getPriceCache(currency, platform_id);
|
|
414
460
|
if (priceCache[contractAddress] === undefined) {
|
|
@@ -433,6 +479,29 @@ export class Coingecko {
|
|
|
433
479
|
}
|
|
434
480
|
}
|
|
435
481
|
|
|
482
|
+
protected updatePriceCacheBySymbol(cgPrice: CGTokenPrice, symbol: string, currency: string) {
|
|
483
|
+
const priceCache = this.getPriceCacheBySymbol(symbol);
|
|
484
|
+
if (priceCache[currency] === undefined) {
|
|
485
|
+
priceCache[currency] = { price: 0, timestamp: 0 };
|
|
486
|
+
}
|
|
487
|
+
if (cgPrice.last_updated_at > priceCache[currency].timestamp) {
|
|
488
|
+
priceCache[currency] = {
|
|
489
|
+
price: cgPrice[currency],
|
|
490
|
+
timestamp: cgPrice.last_updated_at,
|
|
491
|
+
};
|
|
492
|
+
this.logger.debug({
|
|
493
|
+
at: "Coingecko#updatePriceCacheBySymbol",
|
|
494
|
+
message: `Updated ${symbol}/${currency} token price cache.`,
|
|
495
|
+
});
|
|
496
|
+
} else {
|
|
497
|
+
this.logger.debug({
|
|
498
|
+
at: "Coingecko#updatePriceCacheBySymbol",
|
|
499
|
+
message: `No new price available for symbol ${symbol}.`,
|
|
500
|
+
token: cgPrice,
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
436
505
|
private async _callBasic(path: string, timeout?: number) {
|
|
437
506
|
const url = `${this.host}/${path}`;
|
|
438
507
|
|