@1delta/margin-fetcher 0.0.15 → 0.0.16
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/lending/aave-v3-type/publicCallParse.d.ts +4 -4
- package/dist/lending/aave-v3-type/publicCallParse.d.ts.map +1 -1
- package/dist/lending/aave-v3-type/publicCallParse.js +68 -44
- package/dist/lending/fetchLender.d.ts.map +1 -1
- package/dist/lending/user-data/morpho/userCallParse.d.ts.map +1 -1
- package/dist/lending/user-data/morpho/userCallParse.js +60 -0
- package/dist/prices/main-prices/fetchOracleData.d.ts.map +1 -1
- package/dist/prices/main-prices/fetchOracleData.js +8 -3
- package/dist/types/providers.d.ts +1 -1
- package/dist/types/providers.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/lending/aave-v3-type/publicCallParse.ts +312 -110
- package/src/lending/fetchLender.ts +1 -0
- package/src/lending/user-data/morpho/userCallParse.ts +63 -0
- package/src/prices/main-prices/fetchOracleData.ts +11 -7
- package/src/types/providers.ts +4 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { AaveV3GeneralPublicResponse } from
|
|
2
|
-
import { AdditionalYields } from
|
|
3
|
-
import { Lender } from
|
|
4
|
-
import { GenericTokenList } from
|
|
1
|
+
import { AaveV3GeneralPublicResponse } from './types';
|
|
2
|
+
import { AdditionalYields } from '../../types';
|
|
3
|
+
import { Lender } from '@1delta/asset-registry';
|
|
4
|
+
import { GenericTokenList } from '../types';
|
|
5
5
|
export declare const getAaveV3ReservesDataConverter: (lender: Lender, chainId: string, prices: {
|
|
6
6
|
[asset: string]: number;
|
|
7
7
|
}, additionalYields: AdditionalYields, tokenList?: GenericTokenList) => [(data: any[]) => AaveV3GeneralPublicResponse | undefined, number];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publicCallParse.d.ts","sourceRoot":"","sources":["../../../src/lending/aave-v3-type/publicCallParse.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"publicCallParse.d.ts","sourceRoot":"","sources":["../../../src/lending/aave-v3-type/publicCallParse.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,2BAA2B,EAK5B,MAAM,SAAS,CAAA;AAEhB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE9C,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAQ/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAE3C,eAAO,MAAM,8BAA8B,GACzC,QAAQ,MAAM,EACd,SAAS,MAAM,EACf,QAAQ;IAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,EACnC,kBAAkB,gBAAgB,EAClC,YAAW,gBAAqB,KAC/B,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,2BAA2B,GAAG,SAAS,EAAE,MAAM,CA2MnE,CAAA;AA0BD,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,GAAG,EACrB,MAAM,EAAE,GAAG,EACX,SAAS,EAAE,GAAG,GACb,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,GAAG,SAAS,EAAE,MAAM,CAAC,CA0H5C"}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { AaveV3GetreserveConfigDataIndexes, AaveV3GetReservesIndexes, YLDRTypeGetReserveConfigurationData, YLDRTypeGetReserveDataIndexes } from
|
|
2
|
-
import { AAVE_V3_EMODES } from
|
|
3
|
-
import { BPS, formatAaveRawApyToApr, parseRawAmount } from
|
|
4
|
-
import { Lender } from
|
|
5
|
-
import { getAaveAssets, getAssetMeta, LENDER_MODE_NO_MODE, toGenericPriceKey, toOracleKey } from
|
|
1
|
+
import { AaveV3GetreserveConfigDataIndexes, AaveV3GetReservesIndexes, YLDRTypeGetReserveConfigurationData, YLDRTypeGetReserveDataIndexes, } from './types';
|
|
2
|
+
import { AAVE_V3_EMODES } from './publicCallBuild';
|
|
3
|
+
import { BPS, formatAaveRawApyToApr, parseRawAmount } from '../../utils/parsing';
|
|
4
|
+
import { Lender } from '@1delta/asset-registry';
|
|
5
|
+
import { getAaveAssets, getAssetMeta, LENDER_MODE_NO_MODE, toGenericPriceKey, toOracleKey, } from '../../assets';
|
|
6
6
|
export const getAaveV3ReservesDataConverter = (lender, chainId, prices, additionalYields, tokenList = {}) => {
|
|
7
|
-
if (lender.
|
|
7
|
+
if (lender.startsWith('AAVE_V3'))
|
|
8
8
|
return parseAave32(chainId, lender, prices, additionalYields, tokenList);
|
|
9
9
|
switch (lender) {
|
|
10
10
|
/** AAVE V3 style with rewards from state */
|
|
11
|
-
case Lender.YLDR:
|
|
11
|
+
case Lender.YLDR:
|
|
12
|
+
return parseYLDRCall(chainId, lender, additionalYields, prices, tokenList);
|
|
12
13
|
default: {
|
|
13
14
|
const assetsToQuery = getAaveAssets(chainId, lender);
|
|
14
15
|
const expectedNumberOfCalls = assetsToQuery.length * 6 + AAVE_V3_EMODES.length + 1;
|
|
@@ -29,7 +30,7 @@ export const getAaveV3ReservesDataConverter = (lender, chainId, prices, addition
|
|
|
29
30
|
label: rawCfg.label,
|
|
30
31
|
borrowCollateralFactor: rawCfg.ltv / BPS,
|
|
31
32
|
collateralFactor: rawCfg.liquidationThreshold / BPS,
|
|
32
|
-
borrowFactor: 1
|
|
33
|
+
borrowFactor: 1,
|
|
33
34
|
};
|
|
34
35
|
});
|
|
35
36
|
for (let i = 0; i < assetsToQuery.length; i++) {
|
|
@@ -41,13 +42,19 @@ export const getAaveV3ReservesDataConverter = (lender, chainId, prices, addition
|
|
|
41
42
|
const reserveCaps = data[i * 6 + 3];
|
|
42
43
|
const debtCeiling = data[i * 6 + 4];
|
|
43
44
|
const reserveEMode = data[i * 6 + 5];
|
|
44
|
-
const assetMeta = tokenList[asset] ?? {
|
|
45
|
+
const assetMeta = tokenList[asset] ?? {
|
|
46
|
+
...getAssetMeta(chainId, asset),
|
|
47
|
+
address: asset,
|
|
48
|
+
};
|
|
45
49
|
const decimals = assetMeta?.decimals ?? 18;
|
|
46
50
|
const totalDeposits = parseRawAmount(reserveData?.[AaveV3GetReservesIndexes.totalAToken]?.toString(), decimals);
|
|
47
51
|
const totalDebtStable = parseRawAmount(reserveData?.[AaveV3GetReservesIndexes.totalStableDebt]?.toString(), decimals);
|
|
48
52
|
const totalDebt = parseRawAmount(reserveData?.[AaveV3GetReservesIndexes.totalVariableDebt]?.toString(), decimals);
|
|
49
|
-
const liquidity = Number(totalDeposits) -
|
|
50
|
-
|
|
53
|
+
const liquidity = Number(totalDeposits) -
|
|
54
|
+
Number(totalDebt) -
|
|
55
|
+
Number(totalDebtStable);
|
|
56
|
+
const price = prices[toOracleKey(assetMeta?.assetGroup) ??
|
|
57
|
+
toGenericPriceKey(asset, chainId)];
|
|
51
58
|
resultReserves[asset] = {
|
|
52
59
|
poolId: asset,
|
|
53
60
|
underlying: asset,
|
|
@@ -75,16 +82,20 @@ export const getAaveV3ReservesDataConverter = (lender, chainId, prices, addition
|
|
|
75
82
|
...resultReserves[asset],
|
|
76
83
|
decimals: Number(configData?.[AaveV3GetreserveConfigDataIndexes.decimals]),
|
|
77
84
|
config: {
|
|
78
|
-
...populateEModes(Number(configData?.[AaveV3GetreserveConfigDataIndexes.ltv]) /
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
85
|
+
...populateEModes(Number(configData?.[AaveV3GetreserveConfigDataIndexes.ltv]) /
|
|
86
|
+
BPS, Number(configData?.[AaveV3GetreserveConfigDataIndexes.liquidationThreshold]) / BPS),
|
|
87
|
+
...(eModeCategory !== 0
|
|
88
|
+
? {
|
|
89
|
+
[eModeCategory]: {
|
|
90
|
+
category: eModeCategory,
|
|
91
|
+
label: eModeConfigs[eModeCategory]?.label ?? 'Default',
|
|
92
|
+
borrowCollateralFactor: eModeConfigs[eModeCategory]?.borrowCollateralFactor ??
|
|
93
|
+
0,
|
|
94
|
+
collateralFactor: eModeConfigs[eModeCategory]?.collateralFactor ?? 0,
|
|
95
|
+
borrowFactor: 1,
|
|
96
|
+
},
|
|
86
97
|
}
|
|
87
|
-
|
|
98
|
+
: {}),
|
|
88
99
|
},
|
|
89
100
|
// flags
|
|
90
101
|
collateralActive: configData?.[AaveV3GetreserveConfigDataIndexes.usageAsCollateralEnabled],
|
|
@@ -97,13 +108,13 @@ export const getAaveV3ReservesDataConverter = (lender, chainId, prices, addition
|
|
|
97
108
|
// caps
|
|
98
109
|
borrowCap: Number(reserveCaps[0]?.toString()),
|
|
99
110
|
supplyCap: Number(reserveCaps[1]?.toString()),
|
|
100
|
-
debtCeiling: parseRawAmount(debtCeiling[i]?.toString(), Number(decimalsCeiling?.toString()))
|
|
111
|
+
debtCeiling: parseRawAmount(debtCeiling[i]?.toString(), Number(decimalsCeiling?.toString())),
|
|
101
112
|
};
|
|
102
113
|
}
|
|
103
114
|
return {
|
|
104
115
|
data: resultReserves,
|
|
105
116
|
chainId,
|
|
106
|
-
eModes: eModeConfigs
|
|
117
|
+
eModes: eModeConfigs,
|
|
107
118
|
};
|
|
108
119
|
},
|
|
109
120
|
expectedNumberOfCalls,
|
|
@@ -113,12 +124,12 @@ export const getAaveV3ReservesDataConverter = (lender, chainId, prices, addition
|
|
|
113
124
|
};
|
|
114
125
|
const populateEModes = (borrowCollateralFactor, collateralFactor) => {
|
|
115
126
|
let data = {};
|
|
116
|
-
AAVE_V3_EMODES.forEach(e => {
|
|
127
|
+
AAVE_V3_EMODES.forEach((e) => {
|
|
117
128
|
data[e] = {
|
|
118
129
|
category: e,
|
|
119
130
|
borrowCollateralFactor,
|
|
120
131
|
collateralFactor,
|
|
121
|
-
borrowFactor: 1
|
|
132
|
+
borrowFactor: 1,
|
|
122
133
|
};
|
|
123
134
|
});
|
|
124
135
|
return data;
|
|
@@ -140,12 +151,16 @@ export function parseYLDRCall(chainId, lender, additionalYields, prices, tokenLi
|
|
|
140
151
|
const asset = assetsToQuery[i];
|
|
141
152
|
const reserveData = data[i * 2];
|
|
142
153
|
const configData = data[i * 2 + 1];
|
|
143
|
-
const assetMeta = tokenList[asset] ?? {
|
|
154
|
+
const assetMeta = tokenList[asset] ?? {
|
|
155
|
+
...getAssetMeta(chainId, asset),
|
|
156
|
+
address: asset,
|
|
157
|
+
};
|
|
144
158
|
const decimals = assetMeta?.decimals ?? 18;
|
|
145
159
|
const totalVariableDebt = parseRawAmount(reserveData?.[YLDRTypeGetReserveDataIndexes.totalVariableDebt]?.toString(), decimals);
|
|
146
160
|
const totalAToken = parseRawAmount(reserveData?.[YLDRTypeGetReserveDataIndexes.totalYToken]?.toString(), decimals);
|
|
147
161
|
const liquidity = Number(totalAToken) - Number(totalVariableDebt);
|
|
148
|
-
const oracleKey = toOracleKey(assetMeta?.assetGroup) ??
|
|
162
|
+
const oracleKey = toOracleKey(assetMeta?.assetGroup) ??
|
|
163
|
+
toGenericPriceKey(asset, chainId);
|
|
149
164
|
const price = prices[oracleKey] ?? 1;
|
|
150
165
|
const totalDepositsUSD = Number(totalAToken) * price;
|
|
151
166
|
const totalDebtUSD = Number(totalVariableDebt) * price;
|
|
@@ -177,8 +192,8 @@ export function parseYLDRCall(chainId, lender, additionalYields, prices, tokenLi
|
|
|
177
192
|
// collateral factors
|
|
178
193
|
borrowCollateralFactor: Number(configData?.[YLDRTypeGetReserveConfigurationData.ltv]?.toString()) / BPS,
|
|
179
194
|
collateralFactor: Number(configData?.[YLDRTypeGetReserveConfigurationData.liquidationThreshold]?.toString()) / BPS,
|
|
180
|
-
borrowFactor: 1
|
|
181
|
-
}
|
|
195
|
+
borrowFactor: 1,
|
|
196
|
+
},
|
|
182
197
|
},
|
|
183
198
|
liquidationBonus: Number(configData?.[YLDRTypeGetReserveConfigurationData.liquidationBonus]?.toString()) / BPS,
|
|
184
199
|
// flags
|
|
@@ -192,7 +207,7 @@ export function parseYLDRCall(chainId, lender, additionalYields, prices, tokenLi
|
|
|
192
207
|
return {
|
|
193
208
|
data: resultReserves,
|
|
194
209
|
chainId,
|
|
195
|
-
eModes: {}
|
|
210
|
+
eModes: {},
|
|
196
211
|
};
|
|
197
212
|
},
|
|
198
213
|
expectedNumberOfCalls,
|
|
@@ -221,13 +236,13 @@ function parseAave32(chainId, lender, prices, additionalYields, tokenList) {
|
|
|
221
236
|
label: rawCfg.label,
|
|
222
237
|
borrowCollateralFactor: rawCfg.ltv / BPS,
|
|
223
238
|
collateralFactor: rawCfg.liquidationThreshold / BPS,
|
|
224
|
-
borrowFactor: 1
|
|
239
|
+
borrowFactor: 1,
|
|
225
240
|
};
|
|
226
241
|
debtBitmap[mode] = emodeDataResult[3 * index + 1];
|
|
227
242
|
collateralBitmap[mode] = emodeDataResult[3 * index + 2];
|
|
228
243
|
});
|
|
229
244
|
// we get the reserves list to obtain the index per reserve
|
|
230
|
-
const lowerReservesList = reservesList
|
|
245
|
+
const lowerReservesList = reservesList?.map((a) => a.toLowerCase());
|
|
231
246
|
for (let i = 0; i < assetsToQuery.length; i++) {
|
|
232
247
|
const asset = assetsToQuery[i];
|
|
233
248
|
const reserveData = data[i * 5];
|
|
@@ -236,13 +251,17 @@ function parseAave32(chainId, lender, prices, additionalYields, tokenList) {
|
|
|
236
251
|
// const siloedBorrowing = data[i * 5 + 2]
|
|
237
252
|
const reserveCaps = data[i * 5 + 3];
|
|
238
253
|
const debtCeiling = data[i * 5 + 4];
|
|
239
|
-
const assetMeta = tokenList[asset] ?? {
|
|
254
|
+
const assetMeta = tokenList[asset] ?? {
|
|
255
|
+
...getAssetMeta(chainId, asset),
|
|
256
|
+
address: asset,
|
|
257
|
+
};
|
|
240
258
|
const decimals = assetMeta?.decimals ?? 18;
|
|
241
259
|
const totalDeposits = parseRawAmount(reserveData?.[AaveV3GetReservesIndexes.totalAToken]?.toString(), decimals);
|
|
242
260
|
const totalDebtStable = parseRawAmount(reserveData?.[AaveV3GetReservesIndexes.totalStableDebt]?.toString(), decimals);
|
|
243
261
|
const totalDebt = parseRawAmount(reserveData?.[AaveV3GetReservesIndexes.totalVariableDebt]?.toString(), decimals);
|
|
244
262
|
const liquidity = Number(totalDeposits) - Number(totalDebt) - Number(totalDebtStable);
|
|
245
|
-
const price = prices[toOracleKey(assetMeta?.assetGroup)] ??
|
|
263
|
+
const price = prices[toOracleKey(assetMeta?.assetGroup)] ??
|
|
264
|
+
toGenericPriceKey(asset, chainId);
|
|
246
265
|
resultReserves[asset] = {
|
|
247
266
|
poolId: asset,
|
|
248
267
|
underlying: asset,
|
|
@@ -265,22 +284,27 @@ function parseAave32(chainId, lender, prices, additionalYields, tokenList) {
|
|
|
265
284
|
// rewards
|
|
266
285
|
rewards: {},
|
|
267
286
|
};
|
|
268
|
-
const activeEmodes = AAVE_V3_EMODES.map(mode => isReserveEnabledOnBitmap(debtBitmap[mode], lowerReservesList.indexOf(asset)) ||
|
|
287
|
+
const activeEmodes = AAVE_V3_EMODES.map((mode) => isReserveEnabledOnBitmap(debtBitmap[mode], lowerReservesList.indexOf(asset)) ||
|
|
288
|
+
isReserveEnabledOnBitmap(collateralBitmap[mode], lowerReservesList.indexOf(asset))
|
|
289
|
+
? mode
|
|
290
|
+
: -1).filter((m) => m !== undefined && m > 0);
|
|
269
291
|
const eModeCategory = activeEmodes.length === 0 ? 0 : activeEmodes[0]; // Number(emodeResult[index].toString())
|
|
270
292
|
resultReserves[asset] = {
|
|
271
293
|
...resultReserves[asset],
|
|
272
294
|
decimals: Number(configData?.[AaveV3GetreserveConfigDataIndexes.decimals]),
|
|
273
295
|
config: {
|
|
274
296
|
...populateEModes(Number(configData?.[AaveV3GetreserveConfigDataIndexes.ltv]) / BPS, Number(configData?.[AaveV3GetreserveConfigDataIndexes.liquidationThreshold]) / BPS),
|
|
275
|
-
...(eModeCategory !== 0
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
297
|
+
...(eModeCategory !== 0
|
|
298
|
+
? {
|
|
299
|
+
[eModeCategory]: {
|
|
300
|
+
category: eModeCategory,
|
|
301
|
+
label: eModeConfigs[eModeCategory]?.label ?? 'Default',
|
|
302
|
+
borrowCollateralFactor: eModeConfigs[eModeCategory]?.borrowCollateralFactor ?? 0,
|
|
303
|
+
collateralFactor: eModeConfigs[eModeCategory]?.collateralFactor ?? 0,
|
|
304
|
+
borrowFactor: 1,
|
|
305
|
+
},
|
|
282
306
|
}
|
|
283
|
-
|
|
307
|
+
: {}),
|
|
284
308
|
},
|
|
285
309
|
// flags
|
|
286
310
|
collateralActive: configData?.[AaveV3GetreserveConfigDataIndexes.usageAsCollateralEnabled],
|
|
@@ -293,13 +317,13 @@ function parseAave32(chainId, lender, prices, additionalYields, tokenList) {
|
|
|
293
317
|
// caps
|
|
294
318
|
borrowCap: Number(reserveCaps[0]?.toString()),
|
|
295
319
|
supplyCap: Number(reserveCaps[1]?.toString()),
|
|
296
|
-
debtCeiling: parseRawAmount(debtCeiling[i]?.toString(), Number(decimalsCeiling?.toString()))
|
|
320
|
+
debtCeiling: parseRawAmount(debtCeiling[i]?.toString(), Number(decimalsCeiling?.toString())),
|
|
297
321
|
};
|
|
298
322
|
}
|
|
299
323
|
return {
|
|
300
324
|
data: resultReserves,
|
|
301
325
|
chainId,
|
|
302
|
-
eModes: eModeConfigs
|
|
326
|
+
eModes: eModeConfigs,
|
|
303
327
|
};
|
|
304
328
|
},
|
|
305
329
|
expectedNumberOfCalls,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetchLender.d.ts","sourceRoot":"","sources":["../../src/lending/fetchLender.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA;AAWnE,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAG/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AA+E1C,eAAO,MAAM,mBAAmB,GAC9B,SAAS,MAAM,EACf,SAAS,MAAM,EAAE,EACjB,QAAQ;IAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,EACnC,kBAAkB,gBAAgB,EAClC,gBAAgB,sBAAsB,EACtC,YAAW,MAAM,OAAO,CAAC,gBAAgB,CAExC,KACA,OAAO,CAAC;IAAE,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"fetchLender.d.ts","sourceRoot":"","sources":["../../src/lending/fetchLender.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA;AAWnE,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAG/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AA+E1C,eAAO,MAAM,mBAAmB,GAC9B,SAAS,MAAM,EACf,SAAS,MAAM,EAAE,EACjB,QAAQ;IAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,EACnC,kBAAkB,gBAAgB,EAClC,gBAAgB,sBAAsB,EACtC,YAAW,MAAM,OAAO,CAAC,gBAAgB,CAExC,KACA,OAAO,CAAC;IAAE,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAAA;CAAE,CAsDnC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"userCallParse.d.ts","sourceRoot":"","sources":["../../../../src/lending/user-data/morpho/userCallParse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAE/C,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"userCallParse.d.ts","sourceRoot":"","sources":["../../../../src/lending/user-data/morpho/userCallParse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAE/C,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAA;AA+CpD,eAAO,MAAM,kCAAkC,GAC7C,QAAQ,MAAM,EACd,SAAS,MAAM,EACf,SAAS,MAAM,EACf,SAAS,MAAM,EAAE,EACjB,QAAQ;IAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,EACnC,YAAY;IAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,EACvC,YAAY,GAAG,KACd,CACD,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK;IAAE,CAAC,MAAM,EAAE,MAAM,GAAG,yBAAyB,CAAA;CAAE,GAAG,SAAS,EAC5E,MAAM,CA8EP,CAAA"}
|
|
@@ -2,6 +2,45 @@ import { getAssetMeta, toGenericPriceKey, toOracleKey } from '../../../assets';
|
|
|
2
2
|
import { createBaseTypeUserState } from '../utils';
|
|
3
3
|
import { parseRawAmount } from '../../../utils/parsing';
|
|
4
4
|
import { decodePackedDataset } from './decoder';
|
|
5
|
+
const balanceData = {
|
|
6
|
+
'0': {
|
|
7
|
+
borrowDiscountedCollateral: 0,
|
|
8
|
+
collateral: 0,
|
|
9
|
+
deposits: 0,
|
|
10
|
+
debt: 0,
|
|
11
|
+
adjustedDebt: 0,
|
|
12
|
+
nav: 0,
|
|
13
|
+
deposits24h: 0,
|
|
14
|
+
debt24h: 0,
|
|
15
|
+
nav24h: 0,
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
// aggregated apr data
|
|
19
|
+
const aprData = {
|
|
20
|
+
'0': {
|
|
21
|
+
apr: 0,
|
|
22
|
+
borrowApr: 0,
|
|
23
|
+
depositApr: 0,
|
|
24
|
+
rewards: 0,
|
|
25
|
+
rewardApr: 0,
|
|
26
|
+
rewardDepositApr: 0,
|
|
27
|
+
rewardBorrowApr: 0,
|
|
28
|
+
stakingApr: 0,
|
|
29
|
+
stakingDepositApr: 0,
|
|
30
|
+
stakingBorrowApr: 0,
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
const userDataEmpty = (account, chainId) => {
|
|
34
|
+
return {
|
|
35
|
+
account,
|
|
36
|
+
chainId,
|
|
37
|
+
balanceData,
|
|
38
|
+
aprData,
|
|
39
|
+
userConfigs: {
|
|
40
|
+
'0': { selectedMode: 0, id: account },
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
};
|
|
5
44
|
export const getMorphoUserDataConverterWithlens = (lender, chainId, account, markets, prices, pricesHist, lenderData) => {
|
|
6
45
|
const expectedNumberOfCalls = Math.ceil(markets.length / 100);
|
|
7
46
|
return [
|
|
@@ -10,6 +49,7 @@ export const getMorphoUserDataConverterWithlens = (lender, chainId, account, mar
|
|
|
10
49
|
if (data.length !== expectedNumberOfCalls) {
|
|
11
50
|
return undefined;
|
|
12
51
|
}
|
|
52
|
+
let marketsHandled = [];
|
|
13
53
|
// de-chunk the calldata
|
|
14
54
|
const returnDatas = data.flatMap((d) => decodePackedDataset(d));
|
|
15
55
|
let datas = {};
|
|
@@ -18,6 +58,7 @@ export const getMorphoUserDataConverterWithlens = (lender, chainId, account, mar
|
|
|
18
58
|
let totalDeposits24h = 0;
|
|
19
59
|
const balanceData = returnDatas[i];
|
|
20
60
|
const markeId = markets[balanceData.index];
|
|
61
|
+
marketsHandled.push(markeId);
|
|
21
62
|
const market = lenderData[markeId]?.params?.market;
|
|
22
63
|
const { dataForMarket, addedDebt, addedDeposits } = createMorphoEntryFromMarketWithLens(balanceData, chainId, market, prices, pricesHist);
|
|
23
64
|
totalDebt24h += addedDebt;
|
|
@@ -36,6 +77,25 @@ export const getMorphoUserDataConverterWithlens = (lender, chainId, account, mar
|
|
|
36
77
|
id: market.id,
|
|
37
78
|
};
|
|
38
79
|
}
|
|
80
|
+
for (const [marketId, pubData] of Object.entries(lenderData ?? {})) {
|
|
81
|
+
if (marketsHandled.includes(marketId))
|
|
82
|
+
continue;
|
|
83
|
+
const payload = {
|
|
84
|
+
chainId,
|
|
85
|
+
account,
|
|
86
|
+
lendingPositions: { '0': {} },
|
|
87
|
+
rewards: {},
|
|
88
|
+
userEMode: 0,
|
|
89
|
+
};
|
|
90
|
+
const userData = userDataEmpty(account, chainId);
|
|
91
|
+
// @ts-ignore
|
|
92
|
+
datas[marketId] = {
|
|
93
|
+
...payload,
|
|
94
|
+
...userData,
|
|
95
|
+
// @ts-ignore
|
|
96
|
+
id: pubData?.params?.market.id,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
39
99
|
return datas;
|
|
40
100
|
},
|
|
41
101
|
expectedNumberOfCalls,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetchOracleData.d.ts","sourceRoot":"","sources":["../../../src/prices/main-prices/fetchOracleData.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAGlD,eAAO,MAAM,eAAe,GAAI,OAAO,MAAM,EAAE,cAAY,KAAG,
|
|
1
|
+
{"version":3,"file":"fetchOracleData.d.ts","sourceRoot":"","sources":["../../../src/prices/main-prices/fetchOracleData.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAGlD,eAAO,MAAM,eAAe,GAAI,OAAO,MAAM,EAAE,cAAY,KAAG,MAY7D,CAAA;AAOD,UAAU,UAAU;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CACtB;AAID;;GAEG;AACH,eAAO,MAAM,eAAe,GAC1B,UAAU,MAAM,EAAE,EAClB,cAAc,oBAAoB,KACjC,OAAO,CAAC,UAAU,CA8GpB,CAAA"}
|
|
@@ -14,9 +14,14 @@ import { AAVE_STYLE_ORACLES } from './addresses/aaveOracles';
|
|
|
14
14
|
import { AAVES_PER_CHAIN } from '../../utils';
|
|
15
15
|
import { parseRawAmount } from '../../utils/parsing';
|
|
16
16
|
export const formatAavePrice = (price, isV2 = false) => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
try {
|
|
18
|
+
return Number(formatEther(BigInt(price ?? '0') *
|
|
19
|
+
// 10^(18 - decimals)
|
|
20
|
+
10n ** (isV2 ? 0n : 10n)));
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return NaN;
|
|
24
|
+
}
|
|
20
25
|
};
|
|
21
26
|
const LENDLE_PAIR_MANTLE = '0x4c57BE599d0e0414785943569E9B6A66dA79Aa6b';
|
|
22
27
|
const AU_PAIR_MANTLE = '0x709503fbb50b10f921e812c48fbd5c5522a0b20c';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PublicClient } from 'viem';
|
|
2
|
-
export type MulticallRetryFunction = (chain: string, calls: any[], abi: any, batchSize?: number, maxRetries?: number, providerId?: number, allowFailure?: boolean) => Promise<any[]>;
|
|
2
|
+
export type MulticallRetryFunction = (chain: string, calls: any[], abi: any, batchSize?: number, maxRetries?: number, providerId?: number, allowFailure?: boolean, orverrides?: any) => Promise<any[]>;
|
|
3
3
|
export type GetEvmClientFunction = (chain: string, rpcId?: number) => PublicClient;
|
|
4
4
|
export interface ProviderDependencies {
|
|
5
5
|
multicallRetry: MulticallRetryFunction;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/types/providers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AAEnC,MAAM,MAAM,sBAAsB,GAAG,CACnC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,GAAG,EAAE,EACZ,GAAG,EAAE,GAAG,EACR,SAAS,CAAC,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,MAAM,EACnB,YAAY,CAAC,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/types/providers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AAEnC,MAAM,MAAM,sBAAsB,GAAG,CACnC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,GAAG,EAAE,EACZ,GAAG,EAAE,GAAG,EACR,SAAS,CAAC,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,MAAM,EACnB,YAAY,CAAC,EAAE,OAAO,EACtB,UAAU,CAAC,EAAE,GAAG,KACb,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;AAEnB,MAAM,MAAM,oBAAoB,GAAG,CACjC,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,KACX,YAAY,CAAA;AAEjB,MAAM,WAAW,oBAAoB;IACnC,cAAc,EAAE,sBAAsB,CAAA;IACtC,YAAY,EAAE,oBAAoB,CAAA;CACnC;AAED,MAAM,WAAW,4BAA4B;IAC3C,cAAc,CAAC,EAAE,sBAAsB,CAAA;IACvC,YAAY,CAAC,EAAE,oBAAoB,CAAA;CACpC"}
|
package/package.json
CHANGED
|
@@ -1,39 +1,62 @@
|
|
|
1
|
-
|
|
2
1
|
import {
|
|
3
2
|
AaveV3GeneralPublicResponse,
|
|
4
3
|
AaveV3GetreserveConfigDataIndexes,
|
|
5
4
|
AaveV3GetReservesIndexes,
|
|
6
5
|
YLDRTypeGetReserveConfigurationData,
|
|
7
|
-
YLDRTypeGetReserveDataIndexes
|
|
8
|
-
} from
|
|
9
|
-
import { AAVE_V3_EMODES } from
|
|
10
|
-
import { AdditionalYields } from
|
|
11
|
-
import { BPS, formatAaveRawApyToApr, parseRawAmount } from
|
|
12
|
-
import { Lender } from
|
|
13
|
-
import {
|
|
14
|
-
|
|
6
|
+
YLDRTypeGetReserveDataIndexes,
|
|
7
|
+
} from './types'
|
|
8
|
+
import { AAVE_V3_EMODES } from './publicCallBuild'
|
|
9
|
+
import { AdditionalYields } from '../../types'
|
|
10
|
+
import { BPS, formatAaveRawApyToApr, parseRawAmount } from '../../utils/parsing'
|
|
11
|
+
import { Lender } from '@1delta/asset-registry'
|
|
12
|
+
import {
|
|
13
|
+
getAaveAssets,
|
|
14
|
+
getAssetMeta,
|
|
15
|
+
LENDER_MODE_NO_MODE,
|
|
16
|
+
toGenericPriceKey,
|
|
17
|
+
toOracleKey,
|
|
18
|
+
} from '../../assets'
|
|
19
|
+
import { GenericTokenList } from '../types'
|
|
15
20
|
|
|
16
21
|
export const getAaveV3ReservesDataConverter = (
|
|
17
22
|
lender: Lender,
|
|
18
23
|
chainId: string,
|
|
19
24
|
prices: { [asset: string]: number },
|
|
20
25
|
additionalYields: AdditionalYields,
|
|
21
|
-
tokenList: GenericTokenList = {}
|
|
26
|
+
tokenList: GenericTokenList = {},
|
|
22
27
|
): [(data: any[]) => AaveV3GeneralPublicResponse | undefined, number] => {
|
|
23
|
-
if (lender.
|
|
28
|
+
if (lender.startsWith('AAVE_V3'))
|
|
29
|
+
return parseAave32(
|
|
30
|
+
chainId,
|
|
31
|
+
lender,
|
|
32
|
+
prices,
|
|
33
|
+
additionalYields,
|
|
34
|
+
tokenList,
|
|
35
|
+
) as any
|
|
24
36
|
switch (lender) {
|
|
25
37
|
/** AAVE V3 style with rewards from state */
|
|
26
|
-
case Lender.YLDR:
|
|
38
|
+
case Lender.YLDR:
|
|
39
|
+
return parseYLDRCall(
|
|
40
|
+
chainId,
|
|
41
|
+
lender,
|
|
42
|
+
additionalYields,
|
|
43
|
+
prices,
|
|
44
|
+
tokenList,
|
|
45
|
+
) as any
|
|
27
46
|
default: {
|
|
28
47
|
const assetsToQuery = getAaveAssets(chainId, lender)
|
|
29
|
-
const expectedNumberOfCalls =
|
|
48
|
+
const expectedNumberOfCalls =
|
|
49
|
+
assetsToQuery.length * 6 + AAVE_V3_EMODES.length + 1
|
|
30
50
|
|
|
31
51
|
return [
|
|
32
52
|
(data: any[]) => {
|
|
33
53
|
if (data.length !== expectedNumberOfCalls) {
|
|
34
54
|
return undefined
|
|
35
55
|
}
|
|
36
|
-
const emodeDataResult = data.slice(
|
|
56
|
+
const emodeDataResult = data.slice(
|
|
57
|
+
assetsToQuery.length * 6,
|
|
58
|
+
assetsToQuery.length * 6 + AAVE_V3_EMODES.length,
|
|
59
|
+
)
|
|
37
60
|
const resultReserves: any = {}
|
|
38
61
|
const decimalsCeiling = data[expectedNumberOfCalls - 1]
|
|
39
62
|
|
|
@@ -46,7 +69,7 @@ export const getAaveV3ReservesDataConverter = (
|
|
|
46
69
|
label: rawCfg.label,
|
|
47
70
|
borrowCollateralFactor: rawCfg.ltv / BPS,
|
|
48
71
|
collateralFactor: rawCfg.liquidationThreshold / BPS,
|
|
49
|
-
borrowFactor: 1
|
|
72
|
+
borrowFactor: 1,
|
|
50
73
|
}
|
|
51
74
|
})
|
|
52
75
|
|
|
@@ -61,16 +84,38 @@ export const getAaveV3ReservesDataConverter = (
|
|
|
61
84
|
const debtCeiling = data[i * 6 + 4]
|
|
62
85
|
const reserveEMode = data[i * 6 + 5]
|
|
63
86
|
|
|
64
|
-
const assetMeta = tokenList[asset] ?? {
|
|
87
|
+
const assetMeta = tokenList[asset] ?? {
|
|
88
|
+
...getAssetMeta(chainId, asset),
|
|
89
|
+
address: asset,
|
|
90
|
+
}
|
|
65
91
|
|
|
66
92
|
const decimals = assetMeta?.decimals ?? 18
|
|
67
93
|
|
|
68
|
-
const totalDeposits = parseRawAmount(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
94
|
+
const totalDeposits = parseRawAmount(
|
|
95
|
+
reserveData?.[AaveV3GetReservesIndexes.totalAToken]?.toString(),
|
|
96
|
+
decimals,
|
|
97
|
+
)
|
|
98
|
+
const totalDebtStable = parseRawAmount(
|
|
99
|
+
reserveData?.[
|
|
100
|
+
AaveV3GetReservesIndexes.totalStableDebt
|
|
101
|
+
]?.toString(),
|
|
102
|
+
decimals,
|
|
103
|
+
)
|
|
104
|
+
const totalDebt = parseRawAmount(
|
|
105
|
+
reserveData?.[
|
|
106
|
+
AaveV3GetReservesIndexes.totalVariableDebt
|
|
107
|
+
]?.toString(),
|
|
108
|
+
decimals,
|
|
109
|
+
)
|
|
110
|
+
const liquidity =
|
|
111
|
+
Number(totalDeposits) -
|
|
112
|
+
Number(totalDebt) -
|
|
113
|
+
Number(totalDebtStable)
|
|
114
|
+
const price =
|
|
115
|
+
prices[
|
|
116
|
+
toOracleKey(assetMeta?.assetGroup) ??
|
|
117
|
+
toGenericPriceKey(asset, chainId)
|
|
118
|
+
]
|
|
74
119
|
|
|
75
120
|
resultReserves[asset] = {
|
|
76
121
|
poolId: asset,
|
|
@@ -87,38 +132,78 @@ export const getAaveV3ReservesDataConverter = (
|
|
|
87
132
|
totalDebtUSD: Number(totalDebt) * price,
|
|
88
133
|
totalLiquidityUSD: liquidity * price,
|
|
89
134
|
// rates
|
|
90
|
-
depositRate: formatAaveRawApyToApr(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
135
|
+
depositRate: formatAaveRawApyToApr(
|
|
136
|
+
reserveData?.[
|
|
137
|
+
AaveV3GetReservesIndexes.liquidityRate
|
|
138
|
+
]?.toString(),
|
|
139
|
+
),
|
|
140
|
+
variableBorrowRate: formatAaveRawApyToApr(
|
|
141
|
+
reserveData?.[
|
|
142
|
+
AaveV3GetReservesIndexes.variableBorrowRate
|
|
143
|
+
]?.toString(),
|
|
144
|
+
),
|
|
145
|
+
stableBorrowRate: formatAaveRawApyToApr(
|
|
146
|
+
reserveData?.[
|
|
147
|
+
AaveV3GetReservesIndexes.stableBorrowRate
|
|
148
|
+
]?.toString(),
|
|
149
|
+
),
|
|
150
|
+
stakingYield:
|
|
151
|
+
additionalYields.intrinsicYields[
|
|
152
|
+
toOracleKey(assetMeta?.assetGroup)
|
|
153
|
+
] ?? 0,
|
|
94
154
|
// rewards
|
|
95
155
|
rewards: {},
|
|
96
156
|
}
|
|
97
157
|
const eModeCategory = Number(reserveEMode?.toString())
|
|
98
158
|
resultReserves[asset] = {
|
|
99
159
|
...resultReserves[asset],
|
|
100
|
-
decimals: Number(
|
|
160
|
+
decimals: Number(
|
|
161
|
+
configData?.[AaveV3GetreserveConfigDataIndexes.decimals],
|
|
162
|
+
),
|
|
101
163
|
|
|
102
164
|
config: {
|
|
103
|
-
...populateEModes(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
165
|
+
...populateEModes(
|
|
166
|
+
Number(configData?.[AaveV3GetreserveConfigDataIndexes.ltv]) /
|
|
167
|
+
BPS,
|
|
168
|
+
Number(
|
|
169
|
+
configData?.[
|
|
170
|
+
AaveV3GetreserveConfigDataIndexes.liquidationThreshold
|
|
171
|
+
],
|
|
172
|
+
) / BPS,
|
|
173
|
+
),
|
|
174
|
+
...(eModeCategory !== 0
|
|
175
|
+
? {
|
|
176
|
+
[eModeCategory]: {
|
|
177
|
+
category: eModeCategory,
|
|
178
|
+
label: eModeConfigs[eModeCategory]?.label ?? 'Default',
|
|
179
|
+
borrowCollateralFactor:
|
|
180
|
+
eModeConfigs[eModeCategory]?.borrowCollateralFactor ??
|
|
181
|
+
0,
|
|
182
|
+
collateralFactor:
|
|
183
|
+
eModeConfigs[eModeCategory]?.collateralFactor ?? 0,
|
|
184
|
+
borrowFactor: 1,
|
|
185
|
+
},
|
|
186
|
+
}
|
|
187
|
+
: {}),
|
|
114
188
|
},
|
|
115
189
|
|
|
116
190
|
// flags
|
|
117
|
-
collateralActive:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
191
|
+
collateralActive:
|
|
192
|
+
configData?.[
|
|
193
|
+
AaveV3GetreserveConfigDataIndexes.usageAsCollateralEnabled
|
|
194
|
+
],
|
|
195
|
+
borrowingEnabled:
|
|
196
|
+
configData?.[
|
|
197
|
+
AaveV3GetreserveConfigDataIndexes.borrowingEnabled
|
|
198
|
+
],
|
|
199
|
+
hasStable:
|
|
200
|
+
configData?.[
|
|
201
|
+
AaveV3GetreserveConfigDataIndexes.stableBorrowRateEnabled
|
|
202
|
+
],
|
|
203
|
+
isActive:
|
|
204
|
+
configData?.[AaveV3GetreserveConfigDataIndexes.isActive],
|
|
205
|
+
isFrozen:
|
|
206
|
+
configData?.[AaveV3GetreserveConfigDataIndexes.isFrozen],
|
|
122
207
|
|
|
123
208
|
// eMode
|
|
124
209
|
eMode: eModeConfigs[eModeCategory],
|
|
@@ -126,13 +211,16 @@ export const getAaveV3ReservesDataConverter = (
|
|
|
126
211
|
// caps
|
|
127
212
|
borrowCap: Number(reserveCaps[0]?.toString()),
|
|
128
213
|
supplyCap: Number(reserveCaps[1]?.toString()),
|
|
129
|
-
debtCeiling: parseRawAmount(
|
|
214
|
+
debtCeiling: parseRawAmount(
|
|
215
|
+
debtCeiling[i]?.toString(),
|
|
216
|
+
Number(decimalsCeiling?.toString()),
|
|
217
|
+
),
|
|
130
218
|
}
|
|
131
219
|
}
|
|
132
220
|
return {
|
|
133
221
|
data: resultReserves,
|
|
134
222
|
chainId,
|
|
135
|
-
eModes: eModeConfigs
|
|
223
|
+
eModes: eModeConfigs,
|
|
136
224
|
}
|
|
137
225
|
},
|
|
138
226
|
expectedNumberOfCalls,
|
|
@@ -141,31 +229,37 @@ export const getAaveV3ReservesDataConverter = (
|
|
|
141
229
|
}
|
|
142
230
|
}
|
|
143
231
|
|
|
144
|
-
const populateEModes = (
|
|
232
|
+
const populateEModes = (
|
|
233
|
+
borrowCollateralFactor: number,
|
|
234
|
+
collateralFactor: number,
|
|
235
|
+
) => {
|
|
145
236
|
let data: any = {}
|
|
146
|
-
AAVE_V3_EMODES.forEach(e => {
|
|
237
|
+
AAVE_V3_EMODES.forEach((e) => {
|
|
147
238
|
data[e] = {
|
|
148
239
|
category: e,
|
|
149
240
|
borrowCollateralFactor,
|
|
150
241
|
collateralFactor,
|
|
151
|
-
borrowFactor: 1
|
|
242
|
+
borrowFactor: 1,
|
|
152
243
|
}
|
|
153
244
|
})
|
|
154
245
|
return data
|
|
155
246
|
}
|
|
156
247
|
|
|
157
|
-
|
|
158
248
|
/** Replica of aave's bitmap checker */
|
|
159
249
|
function isReserveEnabledOnBitmap(
|
|
160
250
|
bitmap: bigint,
|
|
161
|
-
reserveIndex: number
|
|
251
|
+
reserveIndex: number,
|
|
162
252
|
): boolean {
|
|
163
|
-
return ((bitmap >> BigInt(reserveIndex)) & 1n) !== 0n
|
|
253
|
+
return ((bitmap >> BigInt(reserveIndex)) & 1n) !== 0n
|
|
164
254
|
}
|
|
165
255
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
256
|
+
export function parseYLDRCall(
|
|
257
|
+
chainId: string,
|
|
258
|
+
lender: Lender,
|
|
259
|
+
additionalYields: any,
|
|
260
|
+
prices: any,
|
|
261
|
+
tokenList: any,
|
|
262
|
+
): [(data: any[]) => any | undefined, number] {
|
|
169
263
|
const assetsToQuery = getAaveAssets(chainId, lender)
|
|
170
264
|
|
|
171
265
|
const expectedNumberOfCalls = assetsToQuery.length * 2
|
|
@@ -182,14 +276,27 @@ export function parseYLDRCall(chainId: string, lender: Lender, additionalYields:
|
|
|
182
276
|
const reserveData = data[i * 2]
|
|
183
277
|
const configData = data[i * 2 + 1]
|
|
184
278
|
|
|
185
|
-
const assetMeta = tokenList[asset] ?? {
|
|
279
|
+
const assetMeta = tokenList[asset] ?? {
|
|
280
|
+
...getAssetMeta(chainId, asset),
|
|
281
|
+
address: asset,
|
|
282
|
+
}
|
|
186
283
|
const decimals = assetMeta?.decimals ?? 18
|
|
187
284
|
|
|
188
|
-
const totalVariableDebt = parseRawAmount(
|
|
189
|
-
|
|
285
|
+
const totalVariableDebt = parseRawAmount(
|
|
286
|
+
reserveData?.[
|
|
287
|
+
YLDRTypeGetReserveDataIndexes.totalVariableDebt
|
|
288
|
+
]?.toString(),
|
|
289
|
+
decimals,
|
|
290
|
+
)
|
|
291
|
+
const totalAToken = parseRawAmount(
|
|
292
|
+
reserveData?.[YLDRTypeGetReserveDataIndexes.totalYToken]?.toString(),
|
|
293
|
+
decimals,
|
|
294
|
+
)
|
|
190
295
|
const liquidity = Number(totalAToken) - Number(totalVariableDebt)
|
|
191
296
|
|
|
192
|
-
const oracleKey =
|
|
297
|
+
const oracleKey =
|
|
298
|
+
toOracleKey(assetMeta?.assetGroup) ??
|
|
299
|
+
toGenericPriceKey(asset, chainId)
|
|
193
300
|
const price = prices[oracleKey] ?? 1
|
|
194
301
|
|
|
195
302
|
const totalDepositsUSD = Number(totalAToken) * price
|
|
@@ -210,27 +317,56 @@ export function parseYLDRCall(chainId: string, lender: Lender, additionalYields:
|
|
|
210
317
|
totalDebtUSD,
|
|
211
318
|
totalLiquidityUSD: liquidity * price,
|
|
212
319
|
// rates
|
|
213
|
-
depositRate: formatAaveRawApyToApr(
|
|
214
|
-
|
|
320
|
+
depositRate: formatAaveRawApyToApr(
|
|
321
|
+
reserveData?.[
|
|
322
|
+
YLDRTypeGetReserveDataIndexes.liquidityRate
|
|
323
|
+
]?.toString(),
|
|
324
|
+
),
|
|
325
|
+
variableBorrowRate: formatAaveRawApyToApr(
|
|
326
|
+
reserveData?.[
|
|
327
|
+
YLDRTypeGetReserveDataIndexes.variableBorrowRate
|
|
328
|
+
]?.toString(),
|
|
329
|
+
),
|
|
215
330
|
stableBorrowRate: 0,
|
|
216
331
|
stakingYield: additionalYields.intrinsicYields[oracleKey] ?? 0,
|
|
217
332
|
|
|
218
333
|
// rewards
|
|
219
334
|
rewards: {},
|
|
220
|
-
decimals: Number(
|
|
335
|
+
decimals: Number(
|
|
336
|
+
configData?.[YLDRTypeGetReserveConfigurationData.decimals],
|
|
337
|
+
),
|
|
221
338
|
config: {
|
|
222
339
|
[LENDER_MODE_NO_MODE]: {
|
|
223
340
|
category: LENDER_MODE_NO_MODE,
|
|
224
341
|
// collateral factors
|
|
225
|
-
borrowCollateralFactor:
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
342
|
+
borrowCollateralFactor:
|
|
343
|
+
Number(
|
|
344
|
+
configData?.[
|
|
345
|
+
YLDRTypeGetReserveConfigurationData.ltv
|
|
346
|
+
]?.toString(),
|
|
347
|
+
) / BPS,
|
|
348
|
+
collateralFactor:
|
|
349
|
+
Number(
|
|
350
|
+
configData?.[
|
|
351
|
+
YLDRTypeGetReserveConfigurationData.liquidationThreshold
|
|
352
|
+
]?.toString(),
|
|
353
|
+
) / BPS,
|
|
354
|
+
borrowFactor: 1,
|
|
355
|
+
},
|
|
229
356
|
},
|
|
230
|
-
liquidationBonus:
|
|
357
|
+
liquidationBonus:
|
|
358
|
+
Number(
|
|
359
|
+
configData?.[
|
|
360
|
+
YLDRTypeGetReserveConfigurationData.liquidationBonus
|
|
361
|
+
]?.toString(),
|
|
362
|
+
) / BPS,
|
|
231
363
|
// flags
|
|
232
|
-
collateralActive:
|
|
233
|
-
|
|
364
|
+
collateralActive:
|
|
365
|
+
configData?.[
|
|
366
|
+
YLDRTypeGetReserveConfigurationData.usageAsCollateralEnabled
|
|
367
|
+
],
|
|
368
|
+
borrowingEnabled:
|
|
369
|
+
configData?.[YLDRTypeGetReserveConfigurationData.borrowingEnabled],
|
|
234
370
|
hasStable: false,
|
|
235
371
|
isActive: configData?.[YLDRTypeGetReserveConfigurationData.isActive],
|
|
236
372
|
isFrozen: configData?.[YLDRTypeGetReserveConfigurationData.isFrozen],
|
|
@@ -240,24 +376,33 @@ export function parseYLDRCall(chainId: string, lender: Lender, additionalYields:
|
|
|
240
376
|
return {
|
|
241
377
|
data: resultReserves,
|
|
242
378
|
chainId,
|
|
243
|
-
eModes: {}
|
|
379
|
+
eModes: {},
|
|
244
380
|
}
|
|
245
381
|
},
|
|
246
382
|
expectedNumberOfCalls,
|
|
247
383
|
]
|
|
248
384
|
}
|
|
249
385
|
|
|
250
|
-
|
|
251
|
-
|
|
386
|
+
function parseAave32(
|
|
387
|
+
chainId: string,
|
|
388
|
+
lender: string,
|
|
389
|
+
prices: any,
|
|
390
|
+
additionalYields: any,
|
|
391
|
+
tokenList: any,
|
|
392
|
+
) {
|
|
252
393
|
const assetsToQuery = getAaveAssets(chainId, lender)
|
|
253
|
-
const expectedNumberOfCalls =
|
|
394
|
+
const expectedNumberOfCalls =
|
|
395
|
+
assetsToQuery.length * 5 + AAVE_V3_EMODES.length * 3 + 2
|
|
254
396
|
|
|
255
397
|
return [
|
|
256
398
|
(data: any[]) => {
|
|
257
399
|
if (data.length !== expectedNumberOfCalls) {
|
|
258
400
|
return undefined
|
|
259
401
|
}
|
|
260
|
-
const emodeDataResult = data.slice(
|
|
402
|
+
const emodeDataResult = data.slice(
|
|
403
|
+
assetsToQuery.length * 5,
|
|
404
|
+
assetsToQuery.length * 5 + AAVE_V3_EMODES.length * 3,
|
|
405
|
+
)
|
|
261
406
|
const resultReserves: any = {}
|
|
262
407
|
|
|
263
408
|
const reservesList = data[expectedNumberOfCalls - 2]
|
|
@@ -274,14 +419,15 @@ function parseAave32(chainId: string, lender: string, prices: any, additionalYie
|
|
|
274
419
|
label: rawCfg.label,
|
|
275
420
|
borrowCollateralFactor: rawCfg.ltv / BPS,
|
|
276
421
|
collateralFactor: rawCfg.liquidationThreshold / BPS,
|
|
277
|
-
borrowFactor: 1
|
|
422
|
+
borrowFactor: 1,
|
|
278
423
|
}
|
|
279
424
|
debtBitmap[mode] = emodeDataResult[3 * index + 1]
|
|
280
425
|
collateralBitmap[mode] = emodeDataResult[3 * index + 2]
|
|
281
426
|
})
|
|
282
|
-
|
|
283
427
|
// we get the reserves list to obtain the index per reserve
|
|
284
|
-
const lowerReservesList = (reservesList as string[])
|
|
428
|
+
const lowerReservesList = (reservesList as string[])?.map((a) =>
|
|
429
|
+
a.toLowerCase(),
|
|
430
|
+
)
|
|
285
431
|
|
|
286
432
|
for (let i = 0; i < assetsToQuery.length; i++) {
|
|
287
433
|
const asset = assetsToQuery[i]
|
|
@@ -293,14 +439,29 @@ function parseAave32(chainId: string, lender: string, prices: any, additionalYie
|
|
|
293
439
|
const reserveCaps = data[i * 5 + 3]
|
|
294
440
|
const debtCeiling = data[i * 5 + 4]
|
|
295
441
|
|
|
296
|
-
const assetMeta = tokenList[asset] ?? {
|
|
442
|
+
const assetMeta = tokenList[asset] ?? {
|
|
443
|
+
...getAssetMeta(chainId, asset),
|
|
444
|
+
address: asset,
|
|
445
|
+
}
|
|
297
446
|
|
|
298
447
|
const decimals = assetMeta?.decimals ?? 18
|
|
299
|
-
const totalDeposits = parseRawAmount(
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const
|
|
448
|
+
const totalDeposits = parseRawAmount(
|
|
449
|
+
reserveData?.[AaveV3GetReservesIndexes.totalAToken]?.toString(),
|
|
450
|
+
decimals,
|
|
451
|
+
)
|
|
452
|
+
const totalDebtStable = parseRawAmount(
|
|
453
|
+
reserveData?.[AaveV3GetReservesIndexes.totalStableDebt]?.toString(),
|
|
454
|
+
decimals,
|
|
455
|
+
)
|
|
456
|
+
const totalDebt = parseRawAmount(
|
|
457
|
+
reserveData?.[AaveV3GetReservesIndexes.totalVariableDebt]?.toString(),
|
|
458
|
+
decimals,
|
|
459
|
+
)
|
|
460
|
+
const liquidity =
|
|
461
|
+
Number(totalDeposits) - Number(totalDebt) - Number(totalDebtStable)
|
|
462
|
+
const price =
|
|
463
|
+
prices[toOracleKey(assetMeta?.assetGroup!)] ??
|
|
464
|
+
toGenericPriceKey(asset, chainId)
|
|
304
465
|
|
|
305
466
|
resultReserves[asset] = {
|
|
306
467
|
poolId: asset,
|
|
@@ -317,44 +478,82 @@ function parseAave32(chainId: string, lender: string, prices: any, additionalYie
|
|
|
317
478
|
totalDebtUSD: Number(totalDebt) * price,
|
|
318
479
|
totalLiquidityUSD: liquidity * price,
|
|
319
480
|
// rates
|
|
320
|
-
depositRate: formatAaveRawApyToApr(
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
481
|
+
depositRate: formatAaveRawApyToApr(
|
|
482
|
+
reserveData?.[AaveV3GetReservesIndexes.liquidityRate]?.toString(),
|
|
483
|
+
),
|
|
484
|
+
variableBorrowRate: formatAaveRawApyToApr(
|
|
485
|
+
reserveData?.[
|
|
486
|
+
AaveV3GetReservesIndexes.variableBorrowRate
|
|
487
|
+
]?.toString(),
|
|
488
|
+
),
|
|
489
|
+
stableBorrowRate: formatAaveRawApyToApr(
|
|
490
|
+
reserveData?.[
|
|
491
|
+
AaveV3GetReservesIndexes.stableBorrowRate
|
|
492
|
+
]?.toString(),
|
|
493
|
+
),
|
|
494
|
+
|
|
495
|
+
stakingYield:
|
|
496
|
+
additionalYields.intrinsicYields[
|
|
497
|
+
toOracleKey(assetMeta?.assetGroup)
|
|
498
|
+
] ?? 0,
|
|
325
499
|
// rewards
|
|
326
500
|
rewards: {},
|
|
327
501
|
}
|
|
328
|
-
const activeEmodes = AAVE_V3_EMODES.map(mode =>
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
502
|
+
const activeEmodes = AAVE_V3_EMODES.map((mode) =>
|
|
503
|
+
isReserveEnabledOnBitmap(
|
|
504
|
+
debtBitmap[mode],
|
|
505
|
+
lowerReservesList.indexOf(asset),
|
|
506
|
+
) ||
|
|
507
|
+
isReserveEnabledOnBitmap(
|
|
508
|
+
collateralBitmap[mode],
|
|
509
|
+
lowerReservesList.indexOf(asset),
|
|
510
|
+
)
|
|
511
|
+
? mode
|
|
512
|
+
: -1,
|
|
513
|
+
).filter((m) => m !== undefined && m > 0)
|
|
334
514
|
const eModeCategory = activeEmodes.length === 0 ? 0 : activeEmodes[0] // Number(emodeResult[index].toString())
|
|
335
515
|
|
|
336
516
|
resultReserves[asset] = {
|
|
337
517
|
...resultReserves[asset],
|
|
338
|
-
decimals: Number(
|
|
518
|
+
decimals: Number(
|
|
519
|
+
configData?.[AaveV3GetreserveConfigDataIndexes.decimals],
|
|
520
|
+
),
|
|
339
521
|
|
|
340
522
|
config: {
|
|
341
|
-
...populateEModes(
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
523
|
+
...populateEModes(
|
|
524
|
+
Number(configData?.[AaveV3GetreserveConfigDataIndexes.ltv]) / BPS,
|
|
525
|
+
Number(
|
|
526
|
+
configData?.[
|
|
527
|
+
AaveV3GetreserveConfigDataIndexes.liquidationThreshold
|
|
528
|
+
],
|
|
529
|
+
) / BPS,
|
|
530
|
+
),
|
|
531
|
+
...(eModeCategory !== 0
|
|
532
|
+
? {
|
|
533
|
+
[eModeCategory]: {
|
|
534
|
+
category: eModeCategory,
|
|
535
|
+
label: eModeConfigs[eModeCategory]?.label ?? 'Default',
|
|
536
|
+
borrowCollateralFactor:
|
|
537
|
+
eModeConfigs[eModeCategory]?.borrowCollateralFactor ?? 0,
|
|
538
|
+
collateralFactor:
|
|
539
|
+
eModeConfigs[eModeCategory]?.collateralFactor ?? 0,
|
|
540
|
+
borrowFactor: 1,
|
|
541
|
+
},
|
|
542
|
+
}
|
|
543
|
+
: {}),
|
|
352
544
|
},
|
|
353
545
|
|
|
354
546
|
// flags
|
|
355
|
-
collateralActive:
|
|
356
|
-
|
|
357
|
-
|
|
547
|
+
collateralActive:
|
|
548
|
+
configData?.[
|
|
549
|
+
AaveV3GetreserveConfigDataIndexes.usageAsCollateralEnabled
|
|
550
|
+
],
|
|
551
|
+
borrowingEnabled:
|
|
552
|
+
configData?.[AaveV3GetreserveConfigDataIndexes.borrowingEnabled],
|
|
553
|
+
hasStable:
|
|
554
|
+
configData?.[
|
|
555
|
+
AaveV3GetreserveConfigDataIndexes.stableBorrowRateEnabled
|
|
556
|
+
],
|
|
358
557
|
isActive: configData?.[AaveV3GetreserveConfigDataIndexes.isActive],
|
|
359
558
|
isFrozen: configData?.[AaveV3GetreserveConfigDataIndexes.isFrozen],
|
|
360
559
|
|
|
@@ -364,13 +563,16 @@ function parseAave32(chainId: string, lender: string, prices: any, additionalYie
|
|
|
364
563
|
// caps
|
|
365
564
|
borrowCap: Number(reserveCaps[0]?.toString()),
|
|
366
565
|
supplyCap: Number(reserveCaps[1]?.toString()),
|
|
367
|
-
debtCeiling: parseRawAmount(
|
|
566
|
+
debtCeiling: parseRawAmount(
|
|
567
|
+
debtCeiling[i]?.toString(),
|
|
568
|
+
Number(decimalsCeiling?.toString()),
|
|
569
|
+
),
|
|
368
570
|
}
|
|
369
571
|
}
|
|
370
572
|
return {
|
|
371
573
|
data: resultReserves,
|
|
372
574
|
chainId,
|
|
373
|
-
eModes: eModeConfigs
|
|
575
|
+
eModes: eModeConfigs,
|
|
374
576
|
}
|
|
375
577
|
},
|
|
376
578
|
expectedNumberOfCalls,
|
|
@@ -6,6 +6,47 @@ import { parseRawAmount } from '../../../utils/parsing'
|
|
|
6
6
|
import { MorphoMarket } from '../../morpho/types'
|
|
7
7
|
import { BalanceInfo, decodePackedDataset } from './decoder'
|
|
8
8
|
|
|
9
|
+
const balanceData = {
|
|
10
|
+
'0': {
|
|
11
|
+
borrowDiscountedCollateral: 0,
|
|
12
|
+
collateral: 0,
|
|
13
|
+
deposits: 0,
|
|
14
|
+
debt: 0,
|
|
15
|
+
adjustedDebt: 0,
|
|
16
|
+
nav: 0,
|
|
17
|
+
deposits24h: 0,
|
|
18
|
+
debt24h: 0,
|
|
19
|
+
nav24h: 0,
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// aggregated apr data
|
|
24
|
+
const aprData = {
|
|
25
|
+
'0': {
|
|
26
|
+
apr: 0,
|
|
27
|
+
borrowApr: 0,
|
|
28
|
+
depositApr: 0,
|
|
29
|
+
rewards: 0,
|
|
30
|
+
rewardApr: 0,
|
|
31
|
+
rewardDepositApr: 0,
|
|
32
|
+
rewardBorrowApr: 0,
|
|
33
|
+
stakingApr: 0,
|
|
34
|
+
stakingDepositApr: 0,
|
|
35
|
+
stakingBorrowApr: 0,
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
const userDataEmpty = (account: string, chainId: string) => {
|
|
39
|
+
return {
|
|
40
|
+
account,
|
|
41
|
+
chainId,
|
|
42
|
+
balanceData,
|
|
43
|
+
aprData,
|
|
44
|
+
userConfigs: {
|
|
45
|
+
'0': { selectedMode: 0, id: account },
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
9
50
|
export const getMorphoUserDataConverterWithlens = (
|
|
10
51
|
lender: Lender,
|
|
11
52
|
chainId: string,
|
|
@@ -25,6 +66,7 @@ export const getMorphoUserDataConverterWithlens = (
|
|
|
25
66
|
if (data.length !== expectedNumberOfCalls) {
|
|
26
67
|
return undefined
|
|
27
68
|
}
|
|
69
|
+
let marketsHandled: string[] = []
|
|
28
70
|
// de-chunk the calldata
|
|
29
71
|
const returnDatas = data.flatMap((d) => decodePackedDataset(d))
|
|
30
72
|
|
|
@@ -34,6 +76,7 @@ export const getMorphoUserDataConverterWithlens = (
|
|
|
34
76
|
let totalDeposits24h = 0
|
|
35
77
|
const balanceData = returnDatas[i]
|
|
36
78
|
const markeId = markets[balanceData.index]
|
|
79
|
+
marketsHandled.push(markeId)
|
|
37
80
|
const market = lenderData[markeId]?.params?.market
|
|
38
81
|
const { dataForMarket, addedDebt, addedDeposits } =
|
|
39
82
|
createMorphoEntryFromMarketWithLens(
|
|
@@ -68,6 +111,26 @@ export const getMorphoUserDataConverterWithlens = (
|
|
|
68
111
|
id: market.id,
|
|
69
112
|
}
|
|
70
113
|
}
|
|
114
|
+
|
|
115
|
+
for (const [marketId, pubData] of Object.entries(lenderData ?? {})) {
|
|
116
|
+
if (marketsHandled.includes(marketId)) continue;
|
|
117
|
+
const payload = {
|
|
118
|
+
chainId,
|
|
119
|
+
account,
|
|
120
|
+
lendingPositions: { '0': {} },
|
|
121
|
+
rewards: {},
|
|
122
|
+
userEMode: 0,
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const userData = userDataEmpty(account, chainId)
|
|
126
|
+
// @ts-ignore
|
|
127
|
+
datas[marketId] = {
|
|
128
|
+
...payload,
|
|
129
|
+
...userData,
|
|
130
|
+
// @ts-ignore
|
|
131
|
+
id: pubData?.params?.market.id,
|
|
132
|
+
}
|
|
133
|
+
}
|
|
71
134
|
return datas
|
|
72
135
|
},
|
|
73
136
|
expectedNumberOfCalls,
|
|
@@ -21,13 +21,17 @@ import { GetEvmClientFunction } from '../../types'
|
|
|
21
21
|
import { parseRawAmount } from '../../utils/parsing'
|
|
22
22
|
|
|
23
23
|
export const formatAavePrice = (price: string, isV2 = false): number => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
try {
|
|
25
|
+
return Number(
|
|
26
|
+
formatEther(
|
|
27
|
+
BigInt(price ?? '0') *
|
|
28
|
+
// 10^(18 - decimals)
|
|
29
|
+
10n ** (isV2 ? 0n : 10n),
|
|
30
|
+
),
|
|
31
|
+
)
|
|
32
|
+
} catch {
|
|
33
|
+
return NaN
|
|
34
|
+
}
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
const LENDLE_PAIR_MANTLE = '0x4c57BE599d0e0414785943569E9B6A66dA79Aa6b'
|
package/src/types/providers.ts
CHANGED
|
@@ -7,12 +7,13 @@ export type MulticallRetryFunction = (
|
|
|
7
7
|
batchSize?: number,
|
|
8
8
|
maxRetries?: number,
|
|
9
9
|
providerId?: number,
|
|
10
|
-
allowFailure?: boolean
|
|
10
|
+
allowFailure?: boolean,
|
|
11
|
+
orverrides?: any,
|
|
11
12
|
) => Promise<any[]>
|
|
12
13
|
|
|
13
14
|
export type GetEvmClientFunction = (
|
|
14
15
|
chain: string,
|
|
15
|
-
rpcId?: number
|
|
16
|
+
rpcId?: number,
|
|
16
17
|
) => PublicClient
|
|
17
18
|
|
|
18
19
|
export interface ProviderDependencies {
|
|
@@ -23,4 +24,4 @@ export interface ProviderDependencies {
|
|
|
23
24
|
export interface OptionalProviderDependencies {
|
|
24
25
|
multicallRetry?: MulticallRetryFunction
|
|
25
26
|
getEvmClient?: GetEvmClientFunction
|
|
26
|
-
}
|
|
27
|
+
}
|