curvance 5.0.0 → 5.1.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/README.md +22 -7
- package/dist/abis/OptimizerReader.json +43 -45
- package/dist/abis/ProtocolReader.json +279 -55
- package/dist/chains/arbitrum.js +3 -3
- package/dist/chains/arbitrum.js.map +1 -1
- package/dist/classes/Api.d.ts +2 -2
- package/dist/classes/Api.d.ts.map +1 -1
- package/dist/classes/Api.js +87 -14
- package/dist/classes/Api.js.map +1 -1
- package/dist/classes/BorrowableCToken.d.ts +9 -1
- package/dist/classes/BorrowableCToken.d.ts.map +1 -1
- package/dist/classes/BorrowableCToken.js +67 -12
- package/dist/classes/BorrowableCToken.js.map +1 -1
- package/dist/classes/CToken.d.ts +75 -18
- package/dist/classes/CToken.d.ts.map +1 -1
- package/dist/classes/CToken.js +638 -269
- package/dist/classes/CToken.js.map +1 -1
- package/dist/classes/Calldata.d.ts +2 -2
- package/dist/classes/Calldata.d.ts.map +1 -1
- package/dist/classes/Calldata.js +6 -2
- package/dist/classes/Calldata.js.map +1 -1
- package/dist/classes/DexAggregators/IDexAgg.d.ts +1 -1
- package/dist/classes/DexAggregators/Kuru.js +1 -1
- package/dist/classes/DexAggregators/Kuru.js.map +1 -1
- package/dist/classes/DexAggregators/KyberSwap.d.ts.map +1 -1
- package/dist/classes/DexAggregators/KyberSwap.js +112 -61
- package/dist/classes/DexAggregators/KyberSwap.js.map +1 -1
- package/dist/classes/DexAggregators/MultiDexAgg.d.ts +2 -2
- package/dist/classes/DexAggregators/MultiDexAgg.d.ts.map +1 -1
- package/dist/classes/DexAggregators/MultiDexAgg.js +30 -18
- package/dist/classes/DexAggregators/MultiDexAgg.js.map +1 -1
- package/dist/classes/DexAggregators/UnsupportedDexAgg.d.ts +19 -0
- package/dist/classes/DexAggregators/UnsupportedDexAgg.d.ts.map +1 -0
- package/dist/classes/DexAggregators/UnsupportedDexAgg.js +29 -0
- package/dist/classes/DexAggregators/UnsupportedDexAgg.js.map +1 -0
- package/dist/classes/DexAggregators/helpers.d.ts +30 -0
- package/dist/classes/DexAggregators/helpers.d.ts.map +1 -0
- package/dist/classes/DexAggregators/helpers.js +57 -0
- package/dist/classes/DexAggregators/helpers.js.map +1 -0
- package/dist/classes/DexAggregators/index.d.ts +2 -0
- package/dist/classes/DexAggregators/index.d.ts.map +1 -1
- package/dist/classes/DexAggregators/index.js +1 -0
- package/dist/classes/DexAggregators/index.js.map +1 -1
- package/dist/classes/ERC20.d.ts +3 -3
- package/dist/classes/ERC20.d.ts.map +1 -1
- package/dist/classes/ERC20.js +26 -10
- package/dist/classes/ERC20.js.map +1 -1
- package/dist/classes/FormatConverter.d.ts.map +1 -1
- package/dist/classes/FormatConverter.js +4 -1
- package/dist/classes/FormatConverter.js.map +1 -1
- package/dist/classes/Market.d.ts +40 -6
- package/dist/classes/Market.d.ts.map +1 -1
- package/dist/classes/Market.js +333 -106
- package/dist/classes/Market.js.map +1 -1
- package/dist/classes/NativeToken.d.ts +2 -2
- package/dist/classes/NativeToken.d.ts.map +1 -1
- package/dist/classes/NativeToken.js +21 -7
- package/dist/classes/NativeToken.js.map +1 -1
- package/dist/classes/OptimizerReader.d.ts +15 -7
- package/dist/classes/OptimizerReader.d.ts.map +1 -1
- package/dist/classes/OptimizerReader.js +108 -30
- package/dist/classes/OptimizerReader.js.map +1 -1
- package/dist/classes/OracleManager.d.ts.map +1 -1
- package/dist/classes/OracleManager.js +11 -4
- package/dist/classes/OracleManager.js.map +1 -1
- package/dist/classes/PositionManager.d.ts.map +1 -1
- package/dist/classes/PositionManager.js +1 -12
- package/dist/classes/PositionManager.js.map +1 -1
- package/dist/classes/ProtocolReader.d.ts +30 -9
- package/dist/classes/ProtocolReader.d.ts.map +1 -1
- package/dist/classes/ProtocolReader.js +158 -138
- package/dist/classes/ProtocolReader.js.map +1 -1
- package/dist/classes/Zapper.d.ts +5 -5
- package/dist/classes/Zapper.d.ts.map +1 -1
- package/dist/classes/Zapper.js +21 -18
- package/dist/classes/Zapper.js.map +1 -1
- package/dist/contracts/monad-mainnet.json +1 -1
- package/dist/feePolicy.d.ts +2 -0
- package/dist/feePolicy.d.ts.map +1 -1
- package/dist/feePolicy.js +19 -0
- package/dist/feePolicy.js.map +1 -1
- package/dist/format/borrow.d.ts.map +1 -1
- package/dist/format/borrow.js +7 -1
- package/dist/format/borrow.js.map +1 -1
- package/dist/format/health.d.ts +3 -2
- package/dist/format/health.d.ts.map +1 -1
- package/dist/format/health.js +10 -9
- package/dist/format/health.js.map +1 -1
- package/dist/format/leverage.js +5 -4
- package/dist/format/leverage.js.map +1 -1
- package/dist/helpers.d.ts +14 -11
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +171 -52
- package/dist/helpers.js.map +1 -1
- package/dist/integrations/merkl.d.ts +2 -1
- package/dist/integrations/merkl.d.ts.map +1 -1
- package/dist/integrations/merkl.js +199 -4
- package/dist/integrations/merkl.js.map +1 -1
- package/dist/integrations/snapshot.d.ts +28 -24
- package/dist/integrations/snapshot.d.ts.map +1 -1
- package/dist/integrations/snapshot.js +106 -42
- package/dist/integrations/snapshot.js.map +1 -1
- package/dist/retry-provider.d.ts +6 -0
- package/dist/retry-provider.d.ts.map +1 -1
- package/dist/retry-provider.js +103 -11
- package/dist/retry-provider.js.map +1 -1
- package/dist/setup.d.ts +8 -6
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +125 -45
- package/dist/setup.js.map +1 -1
- package/dist/validation.d.ts +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +13 -7
- package/dist/validation.js.map +1 -1
- package/package.json +6 -3
package/dist/classes/Market.js
CHANGED
|
@@ -8,11 +8,19 @@ const helpers_1 = require("../helpers");
|
|
|
8
8
|
const CToken_1 = require("./CToken");
|
|
9
9
|
const MarketManagerIsolated_json_1 = __importDefault(require("../abis/MarketManagerIsolated.json"));
|
|
10
10
|
const decimal_js_1 = require("decimal.js");
|
|
11
|
-
const setup_1 = require("../setup");
|
|
12
11
|
const merkl_1 = require("../integrations/merkl");
|
|
13
12
|
const BorrowableCToken_1 = require("./BorrowableCToken");
|
|
14
13
|
const FormatConverter_1 = __importDefault(require("./FormatConverter"));
|
|
15
14
|
const Api_1 = require("./Api");
|
|
15
|
+
const chains_1 = require("../chains");
|
|
16
|
+
function resolveDefaultSetupConfig(context) {
|
|
17
|
+
const config = require("../setup").setup_config;
|
|
18
|
+
if (config == undefined) {
|
|
19
|
+
throw new Error(`Setup config is not configured for ${context}. ` +
|
|
20
|
+
`Pass setup/provider context explicitly or initialize setupChain() first.`);
|
|
21
|
+
}
|
|
22
|
+
return config;
|
|
23
|
+
}
|
|
16
24
|
class Market {
|
|
17
25
|
provider;
|
|
18
26
|
signer;
|
|
@@ -26,6 +34,7 @@ class Market {
|
|
|
26
34
|
cache;
|
|
27
35
|
milestone = null;
|
|
28
36
|
incentives = [];
|
|
37
|
+
_userDataScope;
|
|
29
38
|
constructor(provider, signer, account, static_data, dynamic_data, user_data, deploy_data, oracle_manager, reader, setup) {
|
|
30
39
|
this.provider = provider;
|
|
31
40
|
this.signer = signer;
|
|
@@ -36,14 +45,10 @@ class Market {
|
|
|
36
45
|
this.setup = setup;
|
|
37
46
|
this.contract = (0, helpers_1.contractSetup)(provider, this.address, MarketManagerIsolated_json_1.default);
|
|
38
47
|
this.cache = { static: static_data, dynamic: dynamic_data, user: user_data, deploy: deploy_data };
|
|
39
|
-
|
|
48
|
+
this._userDataScope = "full";
|
|
49
|
+
for (const tokenData of Market.mergeTokenPayloads(static_data, dynamic_data, user_data)) {
|
|
40
50
|
// @NOTE: Merged fields from the 3 types, so you wanna make sure there is no collisions
|
|
41
51
|
// Otherwise we will have some dataloss
|
|
42
|
-
const tokenData = {
|
|
43
|
-
...static_data.tokens[i],
|
|
44
|
-
...dynamic_data.tokens[i],
|
|
45
|
-
...user_data.tokens[i]
|
|
46
|
-
};
|
|
47
52
|
if (tokenData.isBorrowable) {
|
|
48
53
|
const ctoken = new BorrowableCToken_1.BorrowableCToken(provider, tokenData.address, tokenData, this);
|
|
49
54
|
this.tokens.push(ctoken);
|
|
@@ -57,6 +62,44 @@ class Market {
|
|
|
57
62
|
getAccountOrThrow() {
|
|
58
63
|
return (0, helpers_1.requireAccount)(this.account, this.signer);
|
|
59
64
|
}
|
|
65
|
+
assertRefreshAccountCompatible(account, allowSignerMismatch = false) {
|
|
66
|
+
if (allowSignerMismatch) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const signerAddress = this.signer?.address;
|
|
70
|
+
if (signerAddress == null || signerAddress.toLowerCase() === account.toLowerCase()) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
throw new Error(`Cannot refresh signer-backed market ${this.address} for ${account}; ` +
|
|
74
|
+
`the active signer is ${signerAddress}.`);
|
|
75
|
+
}
|
|
76
|
+
bindRefreshedAccount(account, allowSignerMismatch = false) {
|
|
77
|
+
this.assertRefreshAccountCompatible(account, allowSignerMismatch);
|
|
78
|
+
this.account = account;
|
|
79
|
+
}
|
|
80
|
+
requireFullUserTokenData(accessLabel) {
|
|
81
|
+
if (this.userDataScope !== "summary") {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
throw new Error(`Market-level token-derived user data is stale for ${this.address} after a summary-only refresh. ` +
|
|
85
|
+
`Call market.reloadUserData(account) or Market.reloadUserMarkets(...) before ${accessLabel}.`);
|
|
86
|
+
}
|
|
87
|
+
requireValidCachedUserHealth(accessLabel) {
|
|
88
|
+
if (this.cache.user.errorCodeHit || this.cache.user.priceStale) {
|
|
89
|
+
throw new Error(`Cached market user data for ${this.address} is not reliable for ${accessLabel} because ` +
|
|
90
|
+
`the reader reported an oracle error or stale price.`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
cooldownDateFromUnlockTime(unlockTime, cooldownLength = this.cooldownLength) {
|
|
94
|
+
if (unlockTime === cooldownLength) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
const now = BigInt(Math.floor(Date.now() / 1000));
|
|
98
|
+
if (unlockTime <= now) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
return new Date(Number(unlockTime * 1000n));
|
|
102
|
+
}
|
|
60
103
|
/** @returns {string} - The name of the market at deployment. */
|
|
61
104
|
get name() { return this.cache.deploy.name; }
|
|
62
105
|
/** @returns {Plugins} - The address of the market's plugins by deploy name. */
|
|
@@ -66,15 +109,27 @@ class Market {
|
|
|
66
109
|
/** @returns {bigint[]} - A list of oracle identifiers which can be mapped to AdaptorTypes enum */
|
|
67
110
|
get adapters() { return this.cache.static.adapters; }
|
|
68
111
|
/** @returns {Date | null} - Market cooldown, activated by Collateralization or Borrowing. Lasts as long as {this.cooldownLength} which is currently 20mins */
|
|
69
|
-
get cooldown() { return this.
|
|
112
|
+
get cooldown() { return this.cooldownDateFromUnlockTime(this.cache.user.cooldown); }
|
|
113
|
+
/** @returns The scope of the latest whole-market user refresh. */
|
|
114
|
+
get userDataScope() { return this._userDataScope ?? "full"; }
|
|
70
115
|
/** @returns {Decimal} - The user's collateral in Shares. */
|
|
71
|
-
get userCollateral() {
|
|
116
|
+
get userCollateral() {
|
|
117
|
+
this.requireValidCachedUserHealth("reading userCollateral");
|
|
118
|
+
return (0, helpers_1.toDecimal)(this.cache.user.collateral, 18n);
|
|
119
|
+
}
|
|
72
120
|
/** @returns {USD} - The user's debt in USD. */
|
|
73
|
-
get userDebt() {
|
|
121
|
+
get userDebt() {
|
|
122
|
+
this.requireValidCachedUserHealth("reading userDebt");
|
|
123
|
+
return (0, helpers_1.toDecimal)(this.cache.user.debt, 18n);
|
|
124
|
+
}
|
|
74
125
|
/** @returns {USD} - The user's maximum debt in USD. */
|
|
75
|
-
get userMaxDebt() {
|
|
126
|
+
get userMaxDebt() {
|
|
127
|
+
this.requireValidCachedUserHealth("reading userMaxDebt");
|
|
128
|
+
return (0, helpers_1.toDecimal)(this.cache.user.maxDebt, 18n);
|
|
129
|
+
}
|
|
76
130
|
/** @returns {USD} - The user's remaining credit with a .1% buffer in USD */
|
|
77
131
|
get userRemainingCredit() {
|
|
132
|
+
this.requireValidCachedUserHealth("reading userRemainingCredit");
|
|
78
133
|
const remaining = this.cache.user.maxDebt - this.cache.user.debt;
|
|
79
134
|
return (0, helpers_1.toDecimal)(remaining, 18n).mul(.999);
|
|
80
135
|
}
|
|
@@ -83,6 +138,7 @@ class Market {
|
|
|
83
138
|
* @returns {Percentage | null} - The user's position health Percentage or null if infinity
|
|
84
139
|
*/
|
|
85
140
|
get positionHealth() {
|
|
141
|
+
this.requireValidCachedUserHealth("reading positionHealth");
|
|
86
142
|
if (this.cache.user.positionHealth == helpers_1.UINT256_MAX) {
|
|
87
143
|
return null;
|
|
88
144
|
}
|
|
@@ -93,6 +149,7 @@ class Market {
|
|
|
93
149
|
* @returns {USD} - The total user deposits in USD.
|
|
94
150
|
*/
|
|
95
151
|
get userDeposits() {
|
|
152
|
+
this.requireFullUserTokenData("reading userDeposits");
|
|
96
153
|
let total_deposits = (0, decimal_js_1.Decimal)(0);
|
|
97
154
|
for (const token of this.tokens) {
|
|
98
155
|
total_deposits = total_deposits.add(token.getUserAssetBalance(true));
|
|
@@ -104,6 +161,7 @@ class Market {
|
|
|
104
161
|
* @returns {USD} - The user's net position in USD.
|
|
105
162
|
*/
|
|
106
163
|
get userNet() {
|
|
164
|
+
this.requireFullUserTokenData("reading userNet");
|
|
107
165
|
return this.userDeposits.sub(this.userDebt);
|
|
108
166
|
}
|
|
109
167
|
/** @returns Market LTV */
|
|
@@ -162,6 +220,7 @@ class Market {
|
|
|
162
220
|
* @returns What tokens can and cannot be borrowed from
|
|
163
221
|
*/
|
|
164
222
|
getBorrowableCTokens() {
|
|
223
|
+
this.requireFullUserTokenData("reading borrowable token eligibility");
|
|
165
224
|
const result = {
|
|
166
225
|
eligible: [],
|
|
167
226
|
ineligible: []
|
|
@@ -185,6 +244,7 @@ class Market {
|
|
|
185
244
|
* @returns The total user deposits change (ex: 50, which would be $50/day)
|
|
186
245
|
*/
|
|
187
246
|
getUserDepositsChange(rate) {
|
|
247
|
+
this.requireFullUserTokenData(`reading user deposit change for rate ${rate}`);
|
|
188
248
|
let total_change = (0, decimal_js_1.Decimal)(0);
|
|
189
249
|
for (const token of this.tokens) {
|
|
190
250
|
const amount = token.getUserAssetBalance(true);
|
|
@@ -198,6 +258,7 @@ class Market {
|
|
|
198
258
|
* @returns The total user debt change (ex: 50, which would be $50/day)
|
|
199
259
|
*/
|
|
200
260
|
getUserDebtChange(rate) {
|
|
261
|
+
this.requireFullUserTokenData(`reading user debt change for rate ${rate}`);
|
|
201
262
|
let total_change = (0, decimal_js_1.Decimal)(0);
|
|
202
263
|
for (const token of this.tokens) {
|
|
203
264
|
if (!token.isBorrowable) {
|
|
@@ -214,6 +275,7 @@ class Market {
|
|
|
214
275
|
* @returns The total user net change (ex: 50, which would be $50/day)
|
|
215
276
|
*/
|
|
216
277
|
getUserNetChange(rate) {
|
|
278
|
+
this.requireFullUserTokenData(`reading user net change for rate ${rate}`);
|
|
217
279
|
const earn = this.getUserDepositsChange(rate);
|
|
218
280
|
const debt = this.getUserDebtChange(rate);
|
|
219
281
|
return earn.sub(debt);
|
|
@@ -257,24 +319,82 @@ class Market {
|
|
|
257
319
|
return Promise.all(this.tokens.map((token) => token.getSnapshot(account)));
|
|
258
320
|
}
|
|
259
321
|
hasUserActivity() {
|
|
322
|
+
this.requireFullUserTokenData("determining user activity");
|
|
260
323
|
return this.tokens.some((token) => token.cache.userAssetBalance > 0n ||
|
|
261
324
|
token.cache.userShareBalance > 0n ||
|
|
262
325
|
token.cache.userCollateral > 0n ||
|
|
263
326
|
token.cache.userDebt > 0n);
|
|
264
327
|
}
|
|
328
|
+
requireRefreshTokenRows(rows, label) {
|
|
329
|
+
if (rows.length !== this.tokens.length) {
|
|
330
|
+
throw new Error(`Token row count mismatch for market ${this.address} during refresh: ` +
|
|
331
|
+
`expected=${this.tokens.length} ${label}=${rows.length}.`);
|
|
332
|
+
}
|
|
333
|
+
const rowsByAddress = new Map(rows.map((row) => [row.address.toLowerCase(), row]));
|
|
334
|
+
for (const token of this.tokens) {
|
|
335
|
+
if (!rowsByAddress.has(token.address.toLowerCase())) {
|
|
336
|
+
throw new Error(`Missing ${label} token data for ${token.address} in market ${this.address} during refresh.`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return rowsByAddress;
|
|
340
|
+
}
|
|
341
|
+
requireRefreshMarketAddress(row, label) {
|
|
342
|
+
if (row.address.toLowerCase() !== this.address.toLowerCase()) {
|
|
343
|
+
throw new Error(`Mismatched ${label} market data during refresh: expected=${this.address} received=${row.address}.`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
validateStateRows(dynamicData, userData) {
|
|
347
|
+
this.requireRefreshMarketAddress(dynamicData, "dynamic");
|
|
348
|
+
if (userData != undefined) {
|
|
349
|
+
this.requireRefreshMarketAddress(userData, "user");
|
|
350
|
+
}
|
|
351
|
+
this.requireRefreshTokenRows(dynamicData.tokens, "dynamic");
|
|
352
|
+
if (userData != undefined) {
|
|
353
|
+
this.requireRefreshTokenRows(userData.tokens, "user");
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
validateRefreshState(dynamicData, userData) {
|
|
357
|
+
this.validateStateRows(dynamicData, userData);
|
|
358
|
+
}
|
|
359
|
+
static requireMarketRow(rows, market, label) {
|
|
360
|
+
const row = new Map(rows.map((entry) => [entry.address.toLowerCase(), entry])).get(market.address.toLowerCase());
|
|
361
|
+
if (row == undefined) {
|
|
362
|
+
throw new Error(`Could not reload ${label} data for market ${market.address}.`);
|
|
363
|
+
}
|
|
364
|
+
return row;
|
|
365
|
+
}
|
|
265
366
|
applyState(dynamicData, userData) {
|
|
367
|
+
this.validateStateRows(dynamicData, userData);
|
|
368
|
+
const dynamicRowsByAddress = this.requireRefreshTokenRows(dynamicData.tokens, "dynamic");
|
|
369
|
+
const userRowsByAddress = userData != undefined
|
|
370
|
+
? this.requireRefreshTokenRows(userData.tokens, "user")
|
|
371
|
+
: undefined;
|
|
266
372
|
this.cache.dynamic = dynamicData;
|
|
267
373
|
if (userData != undefined) {
|
|
268
374
|
this.cache.user = userData;
|
|
375
|
+
this._userDataScope = "full";
|
|
269
376
|
}
|
|
270
377
|
for (const token of this.tokens) {
|
|
271
|
-
const nextDynamic =
|
|
272
|
-
const nextUser =
|
|
378
|
+
const nextDynamic = dynamicRowsByAddress.get(token.address.toLowerCase());
|
|
379
|
+
const nextUser = userRowsByAddress?.get(token.address.toLowerCase());
|
|
273
380
|
token.cache = {
|
|
274
381
|
...token.cache,
|
|
275
|
-
...
|
|
382
|
+
...nextDynamic,
|
|
276
383
|
...(nextUser ?? {}),
|
|
277
384
|
};
|
|
385
|
+
if (nextUser != undefined) {
|
|
386
|
+
token.markUserCacheFresh?.();
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
applyUserSummary(userData) {
|
|
391
|
+
this.cache.user = {
|
|
392
|
+
...this.cache.user,
|
|
393
|
+
...userData,
|
|
394
|
+
};
|
|
395
|
+
this._userDataScope = "summary";
|
|
396
|
+
for (const token of this.tokens) {
|
|
397
|
+
token.invalidateUserCache?.();
|
|
278
398
|
}
|
|
279
399
|
}
|
|
280
400
|
async reloadMarketData() {
|
|
@@ -285,48 +405,149 @@ class Market {
|
|
|
285
405
|
}
|
|
286
406
|
this.applyState(dynamic);
|
|
287
407
|
}
|
|
288
|
-
async reloadUserData(account) {
|
|
408
|
+
async reloadUserData(account, options = {}) {
|
|
409
|
+
const allowSignerMismatch = options.allowSignerMismatch ?? false;
|
|
410
|
+
this.assertRefreshAccountCompatible(account, allowSignerMismatch);
|
|
289
411
|
const { dynamicMarkets, userMarkets } = await this.reader.getMarketStates([this.address], account);
|
|
290
|
-
const dynamic = dynamicMarkets
|
|
291
|
-
const user = userMarkets
|
|
292
|
-
if (dynamic == undefined || user == undefined) {
|
|
293
|
-
throw new Error(`Could not reload market state for ${this.address}.`);
|
|
294
|
-
}
|
|
412
|
+
const dynamic = Market.requireMarketRow(dynamicMarkets, this, "dynamic");
|
|
413
|
+
const user = Market.requireMarketRow(userMarkets, this, "user");
|
|
295
414
|
this.applyState(dynamic, user);
|
|
415
|
+
this.bindRefreshedAccount(account, allowSignerMismatch);
|
|
416
|
+
}
|
|
417
|
+
async reloadUserSummary(account) {
|
|
418
|
+
this.assertRefreshAccountCompatible(account);
|
|
419
|
+
const userMarkets = await this.reader.getMarketSummaries([this.address], account);
|
|
420
|
+
const user = Market.requireMarketRow(userMarkets, this, "user summary");
|
|
421
|
+
this.applyUserSummary(user);
|
|
422
|
+
this.bindRefreshedAccount(account);
|
|
296
423
|
}
|
|
297
424
|
static getActiveUserMarkets(markets) {
|
|
298
425
|
return markets.filter((market) => market.hasUserActivity());
|
|
299
426
|
}
|
|
300
|
-
static
|
|
301
|
-
if (markets.length === 0) {
|
|
302
|
-
return [];
|
|
303
|
-
}
|
|
427
|
+
static groupByReaderDeployment(markets) {
|
|
304
428
|
const groups = new Map();
|
|
305
429
|
for (const market of markets) {
|
|
306
|
-
const
|
|
430
|
+
const groupKey = market.reader.batchKey ?? market.reader;
|
|
431
|
+
const existing = groups.get(groupKey);
|
|
307
432
|
if (existing) {
|
|
308
|
-
existing.push(market);
|
|
433
|
+
existing.markets.push(market);
|
|
309
434
|
}
|
|
310
435
|
else {
|
|
311
|
-
groups.set(
|
|
436
|
+
groups.set(groupKey, {
|
|
437
|
+
reader: market.reader,
|
|
438
|
+
markets: [market],
|
|
439
|
+
});
|
|
312
440
|
}
|
|
313
441
|
}
|
|
314
|
-
|
|
442
|
+
return groups.values();
|
|
443
|
+
}
|
|
444
|
+
static async reloadUserMarkets(markets, account) {
|
|
445
|
+
if (markets.length === 0) {
|
|
446
|
+
return [];
|
|
447
|
+
}
|
|
448
|
+
for (const market of markets) {
|
|
449
|
+
market.assertRefreshAccountCompatible(account);
|
|
450
|
+
}
|
|
451
|
+
const plans = [];
|
|
452
|
+
for (const { reader, markets: groupedMarkets } of this.groupByReaderDeployment(markets)) {
|
|
315
453
|
const addresses = groupedMarkets.map((market) => market.address);
|
|
316
454
|
const { dynamicMarkets, userMarkets } = await reader.getMarketStates(addresses, account);
|
|
317
|
-
const dynamicByAddress = new Map(dynamicMarkets.map((market) => [market.address, market]));
|
|
318
|
-
const userByAddress = new Map(userMarkets.map((market) => [market.address, market]));
|
|
455
|
+
const dynamicByAddress = new Map(dynamicMarkets.map((market) => [market.address.toLowerCase(), market]));
|
|
456
|
+
const userByAddress = new Map(userMarkets.map((market) => [market.address.toLowerCase(), market]));
|
|
319
457
|
for (const market of groupedMarkets) {
|
|
320
|
-
const dynamic = dynamicByAddress.get(market.address);
|
|
321
|
-
const user = userByAddress.get(market.address);
|
|
458
|
+
const dynamic = dynamicByAddress.get(market.address.toLowerCase());
|
|
459
|
+
const user = userByAddress.get(market.address.toLowerCase());
|
|
322
460
|
if (dynamic == undefined || user == undefined) {
|
|
323
461
|
throw new Error(`Could not reload market state for ${market.address}.`);
|
|
324
462
|
}
|
|
325
|
-
market.
|
|
463
|
+
market.validateStateRows(dynamic, user);
|
|
464
|
+
plans.push({ market, dynamic, user });
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
for (const { market, dynamic, user } of plans) {
|
|
468
|
+
market.applyState(dynamic, user);
|
|
469
|
+
market.bindRefreshedAccount(account);
|
|
470
|
+
}
|
|
471
|
+
return markets;
|
|
472
|
+
}
|
|
473
|
+
static async reloadUserMarketSummaries(markets, account) {
|
|
474
|
+
if (markets.length === 0) {
|
|
475
|
+
return [];
|
|
476
|
+
}
|
|
477
|
+
for (const market of markets) {
|
|
478
|
+
market.assertRefreshAccountCompatible(account);
|
|
479
|
+
}
|
|
480
|
+
const plans = [];
|
|
481
|
+
for (const { reader, markets: groupedMarkets } of this.groupByReaderDeployment(markets)) {
|
|
482
|
+
const addresses = groupedMarkets.map((market) => market.address);
|
|
483
|
+
const userMarkets = await reader.getMarketSummaries(addresses, account);
|
|
484
|
+
const userByAddress = new Map(userMarkets.map((market) => [market.address.toLowerCase(), market]));
|
|
485
|
+
for (const market of groupedMarkets) {
|
|
486
|
+
const user = userByAddress.get(market.address.toLowerCase());
|
|
487
|
+
if (user == undefined) {
|
|
488
|
+
throw new Error(`Could not reload market user summary for ${market.address}.`);
|
|
489
|
+
}
|
|
490
|
+
market.requireRefreshMarketAddress(user, "user summary");
|
|
491
|
+
plans.push({ market, user });
|
|
326
492
|
}
|
|
327
493
|
}
|
|
494
|
+
for (const { market, user } of plans) {
|
|
495
|
+
market.applyUserSummary(user);
|
|
496
|
+
market.bindRefreshedAccount(account);
|
|
497
|
+
}
|
|
328
498
|
return markets;
|
|
329
499
|
}
|
|
500
|
+
static buildDeployDataIndex(setup) {
|
|
501
|
+
const index = new Map();
|
|
502
|
+
const deployments = setup.contracts.markets;
|
|
503
|
+
for (const [name, data] of Object.entries(deployments)) {
|
|
504
|
+
if (typeof data !== 'object' || data == null || typeof data.address !== 'string') {
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
const key = data.address.toLowerCase();
|
|
508
|
+
if (!index.has(key)) {
|
|
509
|
+
index.set(key, {
|
|
510
|
+
name,
|
|
511
|
+
plugins: 'plugins' in data ? data.plugins : {},
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
return index;
|
|
516
|
+
}
|
|
517
|
+
static buildYieldIndex(yields) {
|
|
518
|
+
const index = new Map();
|
|
519
|
+
for (const yieldEntry of yields) {
|
|
520
|
+
const key = yieldEntry.symbol.toUpperCase();
|
|
521
|
+
if (!index.has(key)) {
|
|
522
|
+
index.set(key, yieldEntry);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return index;
|
|
526
|
+
}
|
|
527
|
+
static buildMarketPayloadIndex(entries) {
|
|
528
|
+
return new Map(entries.map((entry) => [entry.address.toLowerCase(), entry]));
|
|
529
|
+
}
|
|
530
|
+
static requireTokenRow(rowsByAddress, tokenAddress, marketAddress, label) {
|
|
531
|
+
const row = rowsByAddress.get(tokenAddress.toLowerCase());
|
|
532
|
+
if (row == undefined) {
|
|
533
|
+
throw new Error(`Missing ${label} token data for ${tokenAddress} in market ${marketAddress} during Market boot.`);
|
|
534
|
+
}
|
|
535
|
+
return row;
|
|
536
|
+
}
|
|
537
|
+
static mergeTokenPayloads(staticData, dynamicData, userData) {
|
|
538
|
+
if (staticData.tokens.length !== dynamicData.tokens.length ||
|
|
539
|
+
staticData.tokens.length !== userData.tokens.length) {
|
|
540
|
+
throw new Error(`Token row count mismatch for market ${staticData.address} during Market boot: ` +
|
|
541
|
+
`static=${staticData.tokens.length} dynamic=${dynamicData.tokens.length} user=${userData.tokens.length}.`);
|
|
542
|
+
}
|
|
543
|
+
const dynamicByAddress = this.buildMarketPayloadIndex(dynamicData.tokens);
|
|
544
|
+
const userByAddress = this.buildMarketPayloadIndex(userData.tokens);
|
|
545
|
+
return staticData.tokens.map((staticToken) => ({
|
|
546
|
+
...staticToken,
|
|
547
|
+
...this.requireTokenRow(dynamicByAddress, staticToken.address, staticData.address, "dynamic"),
|
|
548
|
+
...this.requireTokenRow(userByAddress, staticToken.address, staticData.address, "user"),
|
|
549
|
+
}));
|
|
550
|
+
}
|
|
330
551
|
/**
|
|
331
552
|
* Preview the impact of the user descision for their deposit/borrow/leverage
|
|
332
553
|
* @param user - Wallet address
|
|
@@ -339,7 +560,14 @@ class Market {
|
|
|
339
560
|
async previewAssetImpact(user, collateral_ctoken, debt_ctoken, deposit_amount, borrow_amount, rate_change) {
|
|
340
561
|
const amount_in = (0, helpers_1.toBigInt)(deposit_amount, collateral_ctoken.asset.decimals);
|
|
341
562
|
const amount_out = (0, helpers_1.toBigInt)(borrow_amount, debt_ctoken.asset.decimals);
|
|
342
|
-
|
|
563
|
+
let { supply, borrow } = await this.reader.previewAssetImpact(user, collateral_ctoken.address, debt_ctoken.address, amount_in, amount_out);
|
|
564
|
+
if (amount_out > 0n) {
|
|
565
|
+
const irm = await debt_ctoken.dynamicIRM();
|
|
566
|
+
const currentAssetsHeld = await debt_ctoken.contract.assetsHeld();
|
|
567
|
+
const currentDebt = await debt_ctoken.contract.marketOutstandingDebt();
|
|
568
|
+
const projectedAssetsHeld = currentAssetsHeld > amount_out ? currentAssetsHeld - amount_out : 0n;
|
|
569
|
+
borrow = await irm.borrowRate(projectedAssetsHeld, currentDebt + amount_out);
|
|
570
|
+
}
|
|
343
571
|
const supply_apy = (0, decimal_js_1.Decimal)(supply * (0, helpers_1.getRateSeconds)('year')).div(helpers_1.WAD);
|
|
344
572
|
const borrow_apy = (0, decimal_js_1.Decimal)(borrow * (0, helpers_1.getRateSeconds)('year')).div(helpers_1.WAD);
|
|
345
573
|
const supply_percent = (0, decimal_js_1.Decimal)(supply * (0, helpers_1.getRateSeconds)(rate_change)).div(helpers_1.WAD);
|
|
@@ -375,27 +603,26 @@ class Market {
|
|
|
375
603
|
if (newLeverage.equals(1)) {
|
|
376
604
|
return null;
|
|
377
605
|
}
|
|
378
|
-
const
|
|
379
|
-
const
|
|
606
|
+
const preview = deposit_ctoken.previewLeverageDown(newLeverage, currentLeverage, borrow_ctoken);
|
|
607
|
+
const repayCollateralAssets = preview.collateralAssetReduction;
|
|
608
|
+
let { collateralAssetReduction } = preview;
|
|
609
|
+
if (preview.feeBps > 0n) {
|
|
610
|
+
collateralAssetReduction = collateralAssetReduction * 10000n / (10000n - preview.feeBps);
|
|
611
|
+
}
|
|
612
|
+
const repayUsd = deposit_ctoken.convertTokensToUsd(repayCollateralAssets, true);
|
|
380
613
|
const repayTokens = borrow_ctoken.convertUsdToTokens(repayUsd, true);
|
|
381
614
|
return this.previewPositionHealth(deposit_ctoken, borrow_ctoken, false, FormatConverter_1.default.bigIntToDecimal(collateralAssetReduction, deposit_ctoken.asset.decimals), true, repayTokens);
|
|
382
615
|
}
|
|
383
|
-
async previewPositionHealthLeverageUp(deposit_ctoken, borrow_ctoken, newLeverage, depositAssets) {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
// must be included or the preview will undercount collateral (showing ~0% health).
|
|
394
|
-
const depositInTokens = depositAssets
|
|
395
|
-
? FormatConverter_1.default.bigIntToDecimal(depositAssets, deposit_ctoken.asset.decimals)
|
|
396
|
-
: (0, decimal_js_1.Decimal)(0);
|
|
397
|
-
const collateralIncrease = collateralFromBorrow.add(depositInTokens);
|
|
398
|
-
return this.previewPositionHealth(deposit_ctoken, borrow_ctoken, true, collateralIncrease, false, borrowAmount);
|
|
616
|
+
async previewPositionHealthLeverageUp(deposit_ctoken, borrow_ctoken, newLeverage, depositAssets, positionManagerType) {
|
|
617
|
+
if ((depositAssets ?? 0n) > 0n) {
|
|
618
|
+
return this.previewPositionHealthDepositAndLeverage(deposit_ctoken, borrow_ctoken, newLeverage, depositAssets, positionManagerType);
|
|
619
|
+
}
|
|
620
|
+
const preview = deposit_ctoken.previewLeverageUp(newLeverage, borrow_ctoken, undefined, positionManagerType);
|
|
621
|
+
return this.previewPositionHealth(deposit_ctoken, borrow_ctoken, true, preview.collateralIncreaseInAssets, false, preview.debtIncreaseInAssets);
|
|
622
|
+
}
|
|
623
|
+
async previewPositionHealthDepositAndLeverage(deposit_ctoken, borrow_ctoken, newLeverage, depositAssets, positionManagerType) {
|
|
624
|
+
const preview = deposit_ctoken.previewDepositAndLeverage(newLeverage, borrow_ctoken, depositAssets, positionManagerType);
|
|
625
|
+
return this.previewPositionHealth(deposit_ctoken, borrow_ctoken, true, preview.collateralIncreaseInAssets, false, preview.debtIncreaseInAssets);
|
|
399
626
|
}
|
|
400
627
|
/**
|
|
401
628
|
* A dynamic position health previewer for any action
|
|
@@ -438,12 +665,13 @@ class Market {
|
|
|
438
665
|
*/
|
|
439
666
|
async previewPositionHealthRedeem(ctoken, amount) {
|
|
440
667
|
const user = this.getAccountOrThrow();
|
|
441
|
-
const
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
668
|
+
const redeemShares = ctoken.convertTokenInputToShares(amount);
|
|
669
|
+
const redeemAssets = FormatConverter_1.default.decimalToBigInt(amount, ctoken.asset.decimals);
|
|
670
|
+
const existing_collateral = ctoken.getUserCollateralShares();
|
|
671
|
+
if (redeemShares > existing_collateral) {
|
|
672
|
+
throw new Error(`Insufficient collateral: Existing (${existing_collateral}) < Redeem amount (${redeemShares})`);
|
|
445
673
|
}
|
|
446
|
-
const data = await this.reader.getPositionHealth(this.address, user, ctoken.address, helpers_1.EMPTY_ADDRESS, false,
|
|
674
|
+
const data = await this.reader.getPositionHealth(this.address, user, ctoken.address, helpers_1.EMPTY_ADDRESS, false, redeemAssets, false, 0n, 0n);
|
|
447
675
|
if (data.errorCodeHit) {
|
|
448
676
|
throw new Error(`Error code hit when calculating position health preview. This usually means price is stale so we couldn't get a valid health value.`);
|
|
449
677
|
}
|
|
@@ -480,7 +708,10 @@ class Market {
|
|
|
480
708
|
*/
|
|
481
709
|
async previewPositionHealthRepay(token, amount) {
|
|
482
710
|
const user = this.getAccountOrThrow();
|
|
483
|
-
const
|
|
711
|
+
const repayAssets = amount.eq(0)
|
|
712
|
+
? helpers_1.UINT256_MAX
|
|
713
|
+
: FormatConverter_1.default.decimalToBigInt(amount, token.decimals);
|
|
714
|
+
const data = await this.reader.getPositionHealth(this.address, user, helpers_1.EMPTY_ADDRESS, token.address, false, 0n, true, repayAssets, 0n);
|
|
484
715
|
if (data.errorCodeHit) {
|
|
485
716
|
throw new Error(`Error code hit when calculating position health preview. This usually means price is stale so we couldn't get a valid health value.`);
|
|
486
717
|
}
|
|
@@ -494,8 +725,8 @@ class Market {
|
|
|
494
725
|
* @param borrowAssets - Amount of assets being borrowed
|
|
495
726
|
* @returns An object containing the hypothetical liquidity values
|
|
496
727
|
*/
|
|
497
|
-
async hypotheticalLiquidityOf(account, cTokenModified = helpers_1.EMPTY_ADDRESS, redemptionShares = 0n, borrowAssets = 0n) {
|
|
498
|
-
return this.
|
|
728
|
+
async hypotheticalLiquidityOf(account, cTokenModified = helpers_1.EMPTY_ADDRESS, redemptionShares = 0n, borrowAssets = 0n, bufferTime = 0n) {
|
|
729
|
+
return this.reader.hypotheticalLiquidityOf(this.address, account, cTokenModified, redemptionShares, borrowAssets, bufferTime);
|
|
499
730
|
}
|
|
500
731
|
/**
|
|
501
732
|
* Fetch the expiration date of a user's cooldown period
|
|
@@ -507,7 +738,7 @@ class Market {
|
|
|
507
738
|
const cooldownTimestamp = await this.contract.accountAssets(account);
|
|
508
739
|
const cooldownLength = fetch || this.cooldownLength == 0n ? await this.contract.MIN_HOLD_PERIOD() : this.cooldownLength;
|
|
509
740
|
const unlockTime = cooldownTimestamp + cooldownLength;
|
|
510
|
-
return unlockTime
|
|
741
|
+
return this.cooldownDateFromUnlockTime(unlockTime, cooldownLength);
|
|
511
742
|
}
|
|
512
743
|
/**
|
|
513
744
|
* Fetch multiple market cooldown expirations
|
|
@@ -526,7 +757,7 @@ class Market {
|
|
|
526
757
|
const market = markets[i];
|
|
527
758
|
const cooldownTimestamp = cooldownTimestamps[i];
|
|
528
759
|
const cooldownLength = market.cooldownLength;
|
|
529
|
-
cooldowns[market.address] = cooldownTimestamp
|
|
760
|
+
cooldowns[market.address] = market.cooldownDateFromUnlockTime(cooldownTimestamp, cooldownLength);
|
|
530
761
|
}
|
|
531
762
|
return cooldowns;
|
|
532
763
|
}
|
|
@@ -537,68 +768,64 @@ class Market {
|
|
|
537
768
|
* @param provider - The RPC provider
|
|
538
769
|
* @returns An array of Market instances setup with protocol reader data
|
|
539
770
|
*/
|
|
540
|
-
static async getAll(reader, oracle_manager, provider
|
|
541
|
-
const
|
|
542
|
-
const
|
|
771
|
+
static async getAll(reader, oracle_manager, provider, signer, account, milestones = {}, incentives = {}, setup) {
|
|
772
|
+
const resolvedSetup = setup ?? resolveDefaultSetupConfig("Market.getAll");
|
|
773
|
+
const resolvedProvider = provider ?? resolvedSetup.readProvider;
|
|
774
|
+
const resolvedSigner = signer === undefined ? resolvedSetup.signer : signer;
|
|
775
|
+
const resolvedAccount = account === undefined ? resolvedSetup.account : account;
|
|
776
|
+
const chainId = chains_1.chain_config[resolvedSetup.chain]?.chainId;
|
|
777
|
+
const all_data = await reader.getAllMarketData(resolvedAccount);
|
|
543
778
|
// Filter out USDC — DeFiLlama incorrectly returns YZM vault yield labeled as USDC
|
|
544
779
|
const [yields, merklLendOpps, merklBorrowOpps] = await Promise.all([
|
|
545
|
-
Api_1.Api.fetchNativeYields(
|
|
546
|
-
(0, merkl_1.fetchMerklOpportunities)({ action: 'LEND' }).catch(() => []),
|
|
547
|
-
(0, merkl_1.fetchMerklOpportunities)({ action: 'BORROW' }).catch(() => []),
|
|
780
|
+
Api_1.Api.fetchNativeYields(resolvedSetup).then(y => y.filter(y => y.symbol.toUpperCase() !== 'USDC')),
|
|
781
|
+
(0, merkl_1.fetchMerklOpportunities)({ action: 'LEND', chainId }).catch(() => []),
|
|
782
|
+
(0, merkl_1.fetchMerklOpportunities)({ action: 'BORROW', chainId }).catch(() => []),
|
|
548
783
|
]);
|
|
784
|
+
const deployIndex = this.buildDeployDataIndex(resolvedSetup);
|
|
785
|
+
const milestonesByAddress = new Map(Object.entries(milestones).map(([market, milestone]) => [market.toLowerCase(), milestone]));
|
|
786
|
+
const incentivesByAddress = new Map(Object.entries(incentives).map(([market, marketIncentives]) => [market.toLowerCase(), marketIncentives]));
|
|
787
|
+
const lendOppApyByToken = (0, helpers_1.aggregateMerklAprByToken)(merklLendOpps, "deposit");
|
|
788
|
+
const borrowOppApyByToken = (0, helpers_1.aggregateMerklAprByToken)(merklBorrowOpps, "borrow");
|
|
789
|
+
const yieldIndex = this.buildYieldIndex(yields);
|
|
790
|
+
const dynamicByAddress = this.buildMarketPayloadIndex(all_data.dynamicMarket);
|
|
791
|
+
const userByAddress = this.buildMarketPayloadIndex(all_data.userData.markets);
|
|
549
792
|
let markets = [];
|
|
550
|
-
for (
|
|
551
|
-
const staticData = all_data.staticMarket[i];
|
|
552
|
-
const dynamicData = all_data.dynamicMarket[i];
|
|
553
|
-
const userData = all_data.userData.markets[i];
|
|
793
|
+
for (const staticData of all_data.staticMarket) {
|
|
554
794
|
const market_address = staticData.address;
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
if (typeof data != 'object') {
|
|
559
|
-
continue;
|
|
560
|
-
}
|
|
561
|
-
if (market_address == data.address) {
|
|
562
|
-
deploy_data = {
|
|
563
|
-
name: obj_key,
|
|
564
|
-
plugins: 'plugins' in data ? data.plugins : {}
|
|
565
|
-
};
|
|
566
|
-
break;
|
|
567
|
-
}
|
|
568
|
-
}
|
|
795
|
+
const deploy_data = deployIndex.get(market_address.toLowerCase());
|
|
796
|
+
const dynamicData = dynamicByAddress.get(market_address.toLowerCase());
|
|
797
|
+
const userData = userByAddress.get(market_address.toLowerCase());
|
|
569
798
|
if (deploy_data == undefined) {
|
|
570
799
|
console.warn(`Could not find deploy data for market: ${market_address}, skipping...`);
|
|
571
800
|
continue;
|
|
572
801
|
}
|
|
573
|
-
if (staticData == undefined) {
|
|
574
|
-
console.warn(`Could not find static market data for index: ${i}`);
|
|
575
|
-
continue;
|
|
576
|
-
}
|
|
577
802
|
if (dynamicData == undefined) {
|
|
578
|
-
|
|
579
|
-
continue;
|
|
803
|
+
throw new Error(`Missing dynamic market data for ${market_address} during Market.getAll boot.`);
|
|
580
804
|
}
|
|
581
805
|
if (userData == undefined) {
|
|
582
|
-
|
|
583
|
-
continue;
|
|
806
|
+
throw new Error(`Missing user market data for ${market_address} during Market.getAll boot.`);
|
|
584
807
|
}
|
|
585
|
-
const market = new Market(
|
|
586
|
-
|
|
587
|
-
|
|
808
|
+
const market = new Market(resolvedProvider, resolvedSigner, resolvedAccount, staticData, dynamicData, userData, deploy_data, oracle_manager, reader, resolvedSetup);
|
|
809
|
+
const rewardKey = market.address.toLowerCase();
|
|
810
|
+
const milestone = milestonesByAddress.get(rewardKey);
|
|
811
|
+
if (milestone != undefined) {
|
|
812
|
+
market.milestone = milestone;
|
|
588
813
|
}
|
|
589
|
-
|
|
590
|
-
|
|
814
|
+
const marketIncentives = incentivesByAddress.get(rewardKey);
|
|
815
|
+
if (marketIncentives != undefined) {
|
|
816
|
+
market.incentives = marketIncentives;
|
|
591
817
|
}
|
|
592
818
|
for (const token of market.tokens) {
|
|
593
|
-
const
|
|
594
|
-
|
|
595
|
-
|
|
819
|
+
const tokenKey = token.address.toLowerCase();
|
|
820
|
+
const lendApy = lendOppApyByToken.get(tokenKey);
|
|
821
|
+
if (lendApy != undefined) {
|
|
822
|
+
token.incentiveSupplyApy = lendApy;
|
|
596
823
|
}
|
|
597
|
-
const
|
|
598
|
-
if (
|
|
599
|
-
token.incentiveBorrowApy =
|
|
824
|
+
const borrowApy = borrowOppApyByToken.get(tokenKey);
|
|
825
|
+
if (borrowApy != undefined) {
|
|
826
|
+
token.incentiveBorrowApy = borrowApy;
|
|
600
827
|
}
|
|
601
|
-
const api_yield =
|
|
828
|
+
const api_yield = yieldIndex.get(token.asset.symbol.toUpperCase());
|
|
602
829
|
if (api_yield != undefined) {
|
|
603
830
|
token.nativeApy = new decimal_js_1.Decimal(api_yield.apy / 100);
|
|
604
831
|
}
|