@defisaver/positions-sdk 2.0.0 → 2.0.1
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/cjs/fluid/index.js +2 -1
- package/cjs/helpers/compoundHelpers/index.js +2 -2
- package/cjs/helpers/morphoBlueHelpers/index.js +90 -73
- package/cjs/services/utils.d.ts +1 -0
- package/cjs/services/utils.js +2 -1
- package/cjs/staking/staking.js +42 -22
- package/cjs/types/compound.d.ts +2 -0
- package/esm/fluid/index.js +3 -2
- package/esm/helpers/compoundHelpers/index.js +2 -2
- package/esm/helpers/morphoBlueHelpers/index.js +91 -74
- package/esm/services/utils.d.ts +1 -0
- package/esm/services/utils.js +1 -0
- package/esm/staking/staking.js +42 -22
- package/esm/types/compound.d.ts +2 -0
- package/package.json +1 -1
- package/src/fluid/index.ts +6 -2
- package/src/helpers/compoundHelpers/index.ts +11 -0
- package/src/helpers/morphoBlueHelpers/index.ts +108 -93
- package/src/services/utils.ts +2 -0
- package/src/staking/staking.ts +41 -22
- package/src/types/compound.ts +2 -0
package/cjs/fluid/index.js
CHANGED
|
@@ -199,9 +199,10 @@ const getAdditionalMarketRateForDex = (token1PerShare, token0PerShare, incentive
|
|
|
199
199
|
const getTradingApy = (poolAddress) => __awaiter(void 0, void 0, void 0, function* () {
|
|
200
200
|
let res;
|
|
201
201
|
try {
|
|
202
|
-
res = yield fetch(`https://api.fluid.instadapp.io/v2/1/dexes/${poolAddress}/apy`, { signal: AbortSignal.timeout(
|
|
202
|
+
res = yield fetch(`https://api.fluid.instadapp.io/v2/1/dexes/${poolAddress}/apy`, { signal: AbortSignal.timeout(utils_1.DEFAULT_TIMEOUT) });
|
|
203
203
|
}
|
|
204
204
|
catch (e) {
|
|
205
|
+
console.error('External API Failure: Fluid Trading Apy');
|
|
205
206
|
return '0';
|
|
206
207
|
}
|
|
207
208
|
if (!res.ok) {
|
|
@@ -38,7 +38,7 @@ const formatMarketData = (data, network, baseAssetPrice) => {
|
|
|
38
38
|
const assetInfo = (0, tokens_1.getAssetInfoByAddress)(data.tokenAddr, network);
|
|
39
39
|
const isWETH = assetInfo.symbol === 'WETH';
|
|
40
40
|
const price = (0, utils_1.getEthAmountForDecimals)(data.price, 8);
|
|
41
|
-
return (Object.assign(Object.assign({}, data), { priceInBaseAsset: (0, utils_1.getEthAmountForDecimals)(data.price, 8), price: new decimal_js_1.default(price).mul(baseAssetPrice).toString(), collateralFactor: (0, utils_1.getEthAmountForDecimals)(data.borrowCollateralFactor, 18), liquidationRatio: (0, utils_1.getEthAmountForDecimals)(data.liquidateCollateralFactor, 18), supplyCap: (0, utils_1.getEthAmountForDecimals)(data.supplyCap, assetInfo.decimals), totalSupply: (0, utils_1.getEthAmountForDecimals)(data.totalSupply, assetInfo.decimals), symbol: isWETH ? 'ETH' : assetInfo.symbol, supplyRate: '0', borrowRate: '0', canBeBorrowed: false, canBeSupplied: true }));
|
|
41
|
+
return (Object.assign(Object.assign({}, data), { borrowCollateralFactor: data.borrowCollateralFactor.toString(), liquidateCollateralFactor: data.liquidateCollateralFactor.toString(), liquidationFactor: data.liquidationFactor.toString(), supplyReserved: data.supplyReserved.toString(), priceInBaseAsset: (0, utils_1.getEthAmountForDecimals)(data.price, 8), price: new decimal_js_1.default(price).mul(baseAssetPrice).toString(), collateralFactor: (0, utils_1.getEthAmountForDecimals)(data.borrowCollateralFactor, 18), liquidationRatio: (0, utils_1.getEthAmountForDecimals)(data.liquidateCollateralFactor, 18), supplyCap: (0, utils_1.getEthAmountForDecimals)(data.supplyCap, assetInfo.decimals), totalSupply: (0, utils_1.getEthAmountForDecimals)(data.totalSupply, assetInfo.decimals), symbol: isWETH ? 'ETH' : assetInfo.symbol, supplyRate: '0', borrowRate: '0', canBeBorrowed: false, canBeSupplied: true }));
|
|
42
42
|
};
|
|
43
43
|
exports.formatMarketData = formatMarketData;
|
|
44
44
|
// TODO: maybe not hardcode decimals
|
|
@@ -46,7 +46,7 @@ const formatBaseData = (data, network, baseAssetPrice) => {
|
|
|
46
46
|
const assetInfo = (0, tokens_1.getAssetInfoByAddress)(data.tokenAddr, network);
|
|
47
47
|
const totalSupply = (0, utils_1.getEthAmountForDecimals)(new decimal_js_1.default(data.totalSupply).mul(data.supplyIndex).toString(), 15 + assetInfo.decimals);
|
|
48
48
|
const totalBorrow = (0, utils_1.getEthAmountForDecimals)(new decimal_js_1.default(data.totalBorrow).mul(data.borrowIndex).toString(), 15 + assetInfo.decimals);
|
|
49
|
-
return (Object.assign(Object.assign({}, data), { supplyRate: (0, moneymarket_1.aprToApy)(new decimal_js_1.default(data.supplyRate).div(1e18).mul(constants_1.SECONDS_PER_YEAR).mul(100)
|
|
49
|
+
return (Object.assign(Object.assign({}, data), { baseBorrowMin: data.baseBorrowMin.toString(), baseTrackingBorrowRewardsSpeed: data.baseTrackingBorrowRewardsSpeed.toString(), baseTrackingSupplyRewardsSpeed: data.baseTrackingSupplyRewardsSpeed.toString(), borrowIndex: data.borrowIndex.toString(), supplyIndex: data.supplyIndex.toString(), trackingBorrowIndex: data.trackingBorrowIndex.toString(), trackingSupplyIndex: data.trackingSupplyIndex.toString(), supplyRate: (0, moneymarket_1.aprToApy)(new decimal_js_1.default(data.supplyRate).div(1e18).mul(constants_1.SECONDS_PER_YEAR).mul(100)
|
|
50
50
|
.toString()), borrowRate: (0, moneymarket_1.aprToApy)(new decimal_js_1.default(data.borrowRate).div(1e18).mul(constants_1.SECONDS_PER_YEAR).mul(100)
|
|
51
51
|
.toString()), utilization: (0, utils_1.getEthAmountForDecimals)(data.utilization, 16), // utilization is totalSupply/totalBorrow in 1e18, but we need % so when we mul with 100 it's 16 decimals
|
|
52
52
|
totalSupply,
|
|
@@ -204,19 +204,29 @@ const REWARDS_QUERY = `
|
|
|
204
204
|
*/
|
|
205
205
|
const getReallocatableLiquidity = (marketId_1, ...args_1) => __awaiter(void 0, [marketId_1, ...args_1], void 0, function* (marketId, network = common_1.NetworkNumber.Eth) {
|
|
206
206
|
var _a;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
207
|
+
try {
|
|
208
|
+
const response = yield fetch(API_URL, {
|
|
209
|
+
method: 'POST',
|
|
210
|
+
headers: { 'Content-Type': 'application/json' },
|
|
211
|
+
body: JSON.stringify({
|
|
212
|
+
query: MARKET_QUERY,
|
|
213
|
+
variables: { uniqueKey: marketId, chainId: network },
|
|
214
|
+
}),
|
|
215
|
+
signal: AbortSignal.timeout(utils_1.DEFAULT_TIMEOUT),
|
|
216
|
+
});
|
|
217
|
+
const data = yield response.json();
|
|
218
|
+
const marketData = (_a = data === null || data === void 0 ? void 0 : data.data) === null || _a === void 0 ? void 0 : _a.marketByUniqueKey;
|
|
219
|
+
if (!marketData)
|
|
220
|
+
throw new Error('Market data not found');
|
|
221
|
+
return {
|
|
222
|
+
reallocatableLiquidity: marketData.reallocatableLiquidityAssets,
|
|
223
|
+
targetBorrowUtilization: marketData.targetBorrowUtilization,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
console.error('External API Failure: Morpho blue reallocatable liquidity', error);
|
|
228
|
+
throw new Error('Failed to fetch reallocatable liquidity');
|
|
229
|
+
}
|
|
220
230
|
});
|
|
221
231
|
exports.getReallocatableLiquidity = getReallocatableLiquidity;
|
|
222
232
|
/**
|
|
@@ -257,71 +267,78 @@ exports.getLiquidityToAllocate = getLiquidityToAllocate;
|
|
|
257
267
|
*/
|
|
258
268
|
const getReallocation = (market_1, assetsData_1, amountToBorrow_1, ...args_1) => __awaiter(void 0, [market_1, assetsData_1, amountToBorrow_1, ...args_1], void 0, function* (market, assetsData, amountToBorrow, network = common_1.NetworkNumber.Eth) {
|
|
259
269
|
var _a, _b, _c;
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const vaultAllocations = marketData.publicAllocatorSharedLiquidity.filter((item) => (0, utils_1.compareAddresses)(item.vault.address, vaultAddress));
|
|
295
|
-
for (const item of vaultAllocations) {
|
|
270
|
+
try {
|
|
271
|
+
const { marketId, loanToken } = market;
|
|
272
|
+
const response = yield fetch(API_URL, {
|
|
273
|
+
method: 'POST',
|
|
274
|
+
headers: { 'Content-Type': 'application/json' },
|
|
275
|
+
body: JSON.stringify({
|
|
276
|
+
query: MARKET_QUERY,
|
|
277
|
+
variables: { uniqueKey: marketId, chainId: network },
|
|
278
|
+
}),
|
|
279
|
+
signal: AbortSignal.timeout(utils_1.DEFAULT_TIMEOUT),
|
|
280
|
+
});
|
|
281
|
+
const data = yield response.json();
|
|
282
|
+
const marketData = (_a = data === null || data === void 0 ? void 0 : data.data) === null || _a === void 0 ? void 0 : _a.marketByUniqueKey;
|
|
283
|
+
if (!marketData)
|
|
284
|
+
throw new Error('Market data not found');
|
|
285
|
+
const loanAssetInfo = (0, tokens_1.getAssetInfoByAddress)(loanToken, network);
|
|
286
|
+
const { totalBorrow, totalSupply } = assetsData[loanAssetInfo.symbol] || { totalBorrow: '0', totalSupply: '0' };
|
|
287
|
+
const totalBorrowWei = (0, tokens_1.assetAmountInWei)(totalBorrow, loanAssetInfo.symbol);
|
|
288
|
+
const totalSupplyWei = (0, tokens_1.assetAmountInWei)(totalSupply, loanAssetInfo.symbol);
|
|
289
|
+
const newTotalBorrowAssets = new decimal_js_1.default(totalBorrowWei).add(amountToBorrow).toString();
|
|
290
|
+
const newUtil = new decimal_js_1.default(newTotalBorrowAssets).div(totalSupplyWei).toString();
|
|
291
|
+
const newUtilScaled = new decimal_js_1.default(newUtil).mul(1e18).toString();
|
|
292
|
+
if (new decimal_js_1.default(newUtilScaled).lt(marketData.targetBorrowUtilization))
|
|
293
|
+
return { vaults: [], withdrawals: [] };
|
|
294
|
+
const liquidityToAllocate = (0, exports.getLiquidityToAllocate)(amountToBorrow, totalBorrowWei, totalSupplyWei, marketData.targetBorrowUtilization, marketData.reallocatableLiquidityAssets);
|
|
295
|
+
const vaultTotalAssets = marketData.publicAllocatorSharedLiquidity.reduce((acc, item) => {
|
|
296
|
+
const vaultAddress = item.vault.address;
|
|
297
|
+
acc[vaultAddress] = new decimal_js_1.default(acc[vaultAddress] || '0').add(item.assets).toString();
|
|
298
|
+
return acc;
|
|
299
|
+
}, {});
|
|
300
|
+
const sortedVaults = Object.entries(vaultTotalAssets).sort(([, a], [, b]) => new decimal_js_1.default(b || '0').sub(a || '0').toNumber());
|
|
301
|
+
const withdrawalsPerVault = {};
|
|
302
|
+
let totalReallocated = '0';
|
|
303
|
+
for (const [vaultAddress] of sortedVaults) {
|
|
296
304
|
if (new decimal_js_1.default(totalReallocated).gte(liquidityToAllocate))
|
|
297
305
|
break;
|
|
298
|
-
const
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
306
|
+
const vaultAllocations = marketData.publicAllocatorSharedLiquidity.filter((item) => (0, utils_1.compareAddresses)(item.vault.address, vaultAddress));
|
|
307
|
+
for (const item of vaultAllocations) {
|
|
308
|
+
if (new decimal_js_1.default(totalReallocated).gte(liquidityToAllocate))
|
|
309
|
+
break;
|
|
310
|
+
const itemAmount = item.assets;
|
|
311
|
+
const leftToAllocate = new decimal_js_1.default(liquidityToAllocate).sub(totalReallocated).toString();
|
|
312
|
+
const amountToTake = new decimal_js_1.default(itemAmount).lt(leftToAllocate) ? itemAmount : leftToAllocate;
|
|
313
|
+
totalReallocated = new decimal_js_1.default(totalReallocated).add(amountToTake).toString();
|
|
314
|
+
const withdrawal = [
|
|
315
|
+
[
|
|
316
|
+
item.allocationMarket.loanAsset.address,
|
|
317
|
+
(_b = item.allocationMarket.collateralAsset) === null || _b === void 0 ? void 0 : _b.address,
|
|
318
|
+
(_c = item.allocationMarket.oracle) === null || _c === void 0 ? void 0 : _c.address,
|
|
319
|
+
item.allocationMarket.irmAddress,
|
|
320
|
+
item.allocationMarket.lltv,
|
|
321
|
+
],
|
|
322
|
+
amountToTake.toString(),
|
|
323
|
+
item.allocationMarket.uniqueKey,
|
|
324
|
+
];
|
|
325
|
+
if (!withdrawalsPerVault[vaultAddress]) {
|
|
326
|
+
withdrawalsPerVault[vaultAddress] = [];
|
|
327
|
+
}
|
|
328
|
+
withdrawalsPerVault[vaultAddress].push(withdrawal);
|
|
315
329
|
}
|
|
316
|
-
withdrawalsPerVault[vaultAddress].push(withdrawal);
|
|
317
330
|
}
|
|
331
|
+
const vaults = Object.keys(withdrawalsPerVault);
|
|
332
|
+
const withdrawals = vaults.map((vaultAddress) => withdrawalsPerVault[vaultAddress].sort((a, b) => a[2].localeCompare(b[2])).map(w => [w[0], w[1]]));
|
|
333
|
+
return {
|
|
334
|
+
vaults,
|
|
335
|
+
withdrawals,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
console.error('External API Failure: Morpho blue reallocation', error);
|
|
340
|
+
throw new Error('Failed to fetch reallocation data');
|
|
318
341
|
}
|
|
319
|
-
const vaults = Object.keys(withdrawalsPerVault);
|
|
320
|
-
const withdrawals = vaults.map((vaultAddress) => withdrawalsPerVault[vaultAddress].sort((a, b) => a[2].localeCompare(b[2])).map(w => [w[0], w[1]]));
|
|
321
|
-
return {
|
|
322
|
-
vaults,
|
|
323
|
-
withdrawals,
|
|
324
|
-
};
|
|
325
342
|
});
|
|
326
343
|
exports.getReallocation = getReallocation;
|
|
327
344
|
const getRewardsForMarket = (marketId_1, ...args_1) => __awaiter(void 0, [marketId_1, ...args_1], void 0, function* (marketId, network = common_1.NetworkNumber.Eth) {
|
package/cjs/services/utils.d.ts
CHANGED
|
@@ -24,3 +24,4 @@ export declare const isEnabledOnBitmap: (bitmap: number, assetId: number) => big
|
|
|
24
24
|
export declare const MAXUINT: string;
|
|
25
25
|
export declare const isMaxuint: (amount: string) => boolean;
|
|
26
26
|
export declare const isMainnetNetwork: (network: NetworkNumber) => network is NetworkNumber.Eth;
|
|
27
|
+
export declare const DEFAULT_TIMEOUT = 2000;
|
package/cjs/services/utils.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.isMainnetNetwork = exports.isMaxuint = exports.MAXUINT = exports.isEnabledOnBitmap = exports.mapRange = exports.bytesToString = exports.ethToWethByAddress = exports.wethToEthByAddress = exports.handleWbtcLegacy = exports.getEthAmountForDecimals = exports.getWeiAmountForDecimals = exports.compareAddresses = exports.isAddress = exports.ADDRESS_REGEX = exports.getAbiItem = exports.wstEthToStEth = exports.stEthToWstEth = exports.wethToEth = exports.ethToWeth = exports.addToArrayIf = exports.addToObjectIf = exports.isLayer2Network = void 0;
|
|
6
|
+
exports.DEFAULT_TIMEOUT = exports.isMainnetNetwork = exports.isMaxuint = exports.MAXUINT = exports.isEnabledOnBitmap = exports.mapRange = exports.bytesToString = exports.ethToWethByAddress = exports.wethToEthByAddress = exports.handleWbtcLegacy = exports.getEthAmountForDecimals = exports.getWeiAmountForDecimals = exports.compareAddresses = exports.isAddress = exports.ADDRESS_REGEX = exports.getAbiItem = exports.wstEthToStEth = exports.stEthToWstEth = exports.wethToEth = exports.ethToWeth = exports.addToArrayIf = exports.addToObjectIf = exports.isLayer2Network = void 0;
|
|
7
7
|
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
8
8
|
const tokens_1 = require("@defisaver/tokens");
|
|
9
9
|
const common_1 = require("../types/common");
|
|
@@ -63,3 +63,4 @@ const isMaxuint = (amount) => (0, exports.compareAddresses)(exports.MAXUINT, amo
|
|
|
63
63
|
exports.isMaxuint = isMaxuint;
|
|
64
64
|
const isMainnetNetwork = (network) => network === common_1.NetworkNumber.Eth;
|
|
65
65
|
exports.isMainnetNetwork = isMainnetNetwork;
|
|
66
|
+
exports.DEFAULT_TIMEOUT = 2000; // 2 seconds
|
package/cjs/staking/staking.js
CHANGED
|
@@ -16,34 +16,54 @@ exports.calculateNetApy = exports.isEligibleForEthenaUSDeRewards = exports.calcu
|
|
|
16
16
|
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
17
17
|
const memoizee_1 = __importDefault(require("memoizee"));
|
|
18
18
|
const constants_1 = require("../constants");
|
|
19
|
+
const utils_1 = require("../services/utils");
|
|
19
20
|
const getSsrApy = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
try {
|
|
22
|
+
const res = yield fetch('https://fe.defisaver.com/api/sky/data', { signal: AbortSignal.timeout(utils_1.DEFAULT_TIMEOUT) });
|
|
23
|
+
const data = yield res.json();
|
|
24
|
+
return new decimal_js_1.default(data.data.skyData[0].sky_savings_rate_apy).mul(100).toString();
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
console.error('External API Failure: Failed to fetch SSR APY from external API', e);
|
|
28
|
+
return '0';
|
|
29
|
+
}
|
|
23
30
|
});
|
|
24
31
|
const getSuperOETHApy = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
body: JSON.stringify({
|
|
31
|
-
query: '\n query OTokenApy($chainId: Int!, $token: String!) {\n oTokenApies(\n limit: 1\n orderBy: timestamp_DESC\n where: {chainId_eq: $chainId, otoken_containsInsensitive: $token}\n ) {\n apy7DayAvg\n apy14DayAvg\n apy30DayAvg\n apr\n apy\n }\n}\n ',
|
|
32
|
-
variables: {
|
|
33
|
-
token: '0xdbfefd2e8460a6ee4955a68582f85708baea60a3',
|
|
34
|
-
chainId: 8453,
|
|
32
|
+
try {
|
|
33
|
+
const res = yield fetch('https://origin.squids.live/origin-squid/graphql', {
|
|
34
|
+
method: 'POST',
|
|
35
|
+
headers: {
|
|
36
|
+
'Content-Type': 'application/json',
|
|
35
37
|
},
|
|
36
|
-
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
body: JSON.stringify({
|
|
39
|
+
query: '\n query OTokenApy($chainId: Int!, $token: String!) {\n oTokenApies(\n limit: 1\n orderBy: timestamp_DESC\n where: {chainId_eq: $chainId, otoken_containsInsensitive: $token}\n ) {\n apy7DayAvg\n apy14DayAvg\n apy30DayAvg\n apr\n apy\n }\n}\n ',
|
|
40
|
+
variables: {
|
|
41
|
+
token: '0xdbfefd2e8460a6ee4955a68582f85708baea60a3',
|
|
42
|
+
chainId: 8453,
|
|
43
|
+
},
|
|
44
|
+
}),
|
|
45
|
+
signal: AbortSignal.timeout(utils_1.DEFAULT_TIMEOUT),
|
|
46
|
+
});
|
|
47
|
+
const data = yield res.json();
|
|
48
|
+
return new decimal_js_1.default(data.data.oTokenApies[0].apy).mul(100).toString();
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
console.error('External API Failure: Failed to fetch Super OETH APY from external API', e);
|
|
52
|
+
return '0';
|
|
53
|
+
}
|
|
40
54
|
});
|
|
41
55
|
const getApyFromDfsApi = (asset) => __awaiter(void 0, void 0, void 0, function* () {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
56
|
+
try {
|
|
57
|
+
const res = yield fetch(`https://fe.defisaver.com/api/staking/apy?asset=${asset}`, { signal: AbortSignal.timeout(utils_1.DEFAULT_TIMEOUT) });
|
|
58
|
+
if (!res.ok)
|
|
59
|
+
throw new Error(`Failed to fetch APY for ${asset}`);
|
|
60
|
+
const data = yield res.json();
|
|
61
|
+
return String(data.apy);
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
console.error(`External API Failure: Failed to fetch APY for ${asset} from DFS API`, e);
|
|
65
|
+
return '0';
|
|
66
|
+
}
|
|
47
67
|
});
|
|
48
68
|
exports.STAKING_ASSETS = ['cbETH', 'wstETH', 'cbETH', 'rETH', 'sDAI', 'weETH', 'sUSDe', 'osETH', 'ezETH', 'ETHx', 'rsETH', 'pufETH', 'wrsETH', 'wsuperOETHb', 'sUSDS', 'PT eUSDe May', 'PT sUSDe July', 'PT USDe July', 'PT eUSDe Aug', 'tETH', 'PT sUSDe Sep', 'PT USDe Sep'];
|
|
49
69
|
exports.getStakingApy = (0, memoizee_1.default)((asset) => __awaiter(void 0, void 0, void 0, function* () {
|
package/cjs/types/compound.d.ts
CHANGED
|
@@ -50,7 +50,9 @@ export interface CompoundV2AssetData extends CompoundAssetData {
|
|
|
50
50
|
export interface CompoundV3AssetData extends CompoundAssetData {
|
|
51
51
|
borrowCollateralFactor: string;
|
|
52
52
|
liquidateCollateralFactor: string;
|
|
53
|
+
liquidationFactor: string;
|
|
53
54
|
minDebt: string;
|
|
55
|
+
supplyReserved: string;
|
|
54
56
|
liquidationRatio: string;
|
|
55
57
|
supplyCap: string;
|
|
56
58
|
priceInBaseAsset: string;
|
package/esm/fluid/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import { assetAmountInEth, getAssetInfo, getAssetInfoByAddress, } from '@defisav
|
|
|
12
12
|
import { NetworkNumber } from '../types/common';
|
|
13
13
|
import { FluidVaultType, } from '../types';
|
|
14
14
|
import { BTCPriceFeedContractViem, DFSFeedRegistryContractViem, ETHPriceFeedContractViem, FeedRegistryContractViem, FluidViewContractViem, } from '../contracts';
|
|
15
|
-
import { compareAddresses, getEthAmountForDecimals, isMainnetNetwork } from '../services/utils';
|
|
15
|
+
import { compareAddresses, DEFAULT_TIMEOUT, getEthAmountForDecimals, isMainnetNetwork, } from '../services/utils';
|
|
16
16
|
import { getFluidAggregatedData, mergeAssetData, mergeUsedAssets, parseDexBorrowData, parseDexSupplyData, } from '../helpers/fluidHelpers';
|
|
17
17
|
import { getFluidMarketInfoById, getFluidVersionsDataForNetwork, getFTokenAddress } from '../markets';
|
|
18
18
|
import { USD_QUOTE, ZERO_ADDRESS } from '../constants';
|
|
@@ -193,9 +193,10 @@ const getAdditionalMarketRateForDex = (token1PerShare, token0PerShare, incentive
|
|
|
193
193
|
const getTradingApy = (poolAddress) => __awaiter(void 0, void 0, void 0, function* () {
|
|
194
194
|
let res;
|
|
195
195
|
try {
|
|
196
|
-
res = yield fetch(`https://api.fluid.instadapp.io/v2/1/dexes/${poolAddress}/apy`, { signal: AbortSignal.timeout(
|
|
196
|
+
res = yield fetch(`https://api.fluid.instadapp.io/v2/1/dexes/${poolAddress}/apy`, { signal: AbortSignal.timeout(DEFAULT_TIMEOUT) });
|
|
197
197
|
}
|
|
198
198
|
catch (e) {
|
|
199
|
+
console.error('External API Failure: Fluid Trading Apy');
|
|
199
200
|
return '0';
|
|
200
201
|
}
|
|
201
202
|
if (!res.ok) {
|
|
@@ -32,14 +32,14 @@ export const formatMarketData = (data, network, baseAssetPrice) => {
|
|
|
32
32
|
const assetInfo = getAssetInfoByAddress(data.tokenAddr, network);
|
|
33
33
|
const isWETH = assetInfo.symbol === 'WETH';
|
|
34
34
|
const price = getEthAmountForDecimals(data.price, 8);
|
|
35
|
-
return (Object.assign(Object.assign({}, data), { priceInBaseAsset: getEthAmountForDecimals(data.price, 8), price: new Dec(price).mul(baseAssetPrice).toString(), collateralFactor: getEthAmountForDecimals(data.borrowCollateralFactor, 18), liquidationRatio: getEthAmountForDecimals(data.liquidateCollateralFactor, 18), supplyCap: getEthAmountForDecimals(data.supplyCap, assetInfo.decimals), totalSupply: getEthAmountForDecimals(data.totalSupply, assetInfo.decimals), symbol: isWETH ? 'ETH' : assetInfo.symbol, supplyRate: '0', borrowRate: '0', canBeBorrowed: false, canBeSupplied: true }));
|
|
35
|
+
return (Object.assign(Object.assign({}, data), { borrowCollateralFactor: data.borrowCollateralFactor.toString(), liquidateCollateralFactor: data.liquidateCollateralFactor.toString(), liquidationFactor: data.liquidationFactor.toString(), supplyReserved: data.supplyReserved.toString(), priceInBaseAsset: getEthAmountForDecimals(data.price, 8), price: new Dec(price).mul(baseAssetPrice).toString(), collateralFactor: getEthAmountForDecimals(data.borrowCollateralFactor, 18), liquidationRatio: getEthAmountForDecimals(data.liquidateCollateralFactor, 18), supplyCap: getEthAmountForDecimals(data.supplyCap, assetInfo.decimals), totalSupply: getEthAmountForDecimals(data.totalSupply, assetInfo.decimals), symbol: isWETH ? 'ETH' : assetInfo.symbol, supplyRate: '0', borrowRate: '0', canBeBorrowed: false, canBeSupplied: true }));
|
|
36
36
|
};
|
|
37
37
|
// TODO: maybe not hardcode decimals
|
|
38
38
|
export const formatBaseData = (data, network, baseAssetPrice) => {
|
|
39
39
|
const assetInfo = getAssetInfoByAddress(data.tokenAddr, network);
|
|
40
40
|
const totalSupply = getEthAmountForDecimals(new Dec(data.totalSupply).mul(data.supplyIndex).toString(), 15 + assetInfo.decimals);
|
|
41
41
|
const totalBorrow = getEthAmountForDecimals(new Dec(data.totalBorrow).mul(data.borrowIndex).toString(), 15 + assetInfo.decimals);
|
|
42
|
-
return (Object.assign(Object.assign({}, data), { supplyRate: aprToApy(new Dec(data.supplyRate).div(1e18).mul(SECONDS_PER_YEAR).mul(100)
|
|
42
|
+
return (Object.assign(Object.assign({}, data), { baseBorrowMin: data.baseBorrowMin.toString(), baseTrackingBorrowRewardsSpeed: data.baseTrackingBorrowRewardsSpeed.toString(), baseTrackingSupplyRewardsSpeed: data.baseTrackingSupplyRewardsSpeed.toString(), borrowIndex: data.borrowIndex.toString(), supplyIndex: data.supplyIndex.toString(), trackingBorrowIndex: data.trackingBorrowIndex.toString(), trackingSupplyIndex: data.trackingSupplyIndex.toString(), supplyRate: aprToApy(new Dec(data.supplyRate).div(1e18).mul(SECONDS_PER_YEAR).mul(100)
|
|
43
43
|
.toString()), borrowRate: aprToApy(new Dec(data.borrowRate).div(1e18).mul(SECONDS_PER_YEAR).mul(100)
|
|
44
44
|
.toString()), utilization: getEthAmountForDecimals(data.utilization, 16), // utilization is totalSupply/totalBorrow in 1e18, but we need % so when we mul with 100 it's 16 decimals
|
|
45
45
|
totalSupply,
|
|
@@ -14,7 +14,7 @@ import { calculateNetApy } from '../../staking';
|
|
|
14
14
|
import { NetworkNumber, } from '../../types/common';
|
|
15
15
|
import { borrowOperations, SECONDS_PER_YEAR, WAD } from '../../constants';
|
|
16
16
|
import { MorphoBlueViewContractViem } from '../../contracts';
|
|
17
|
-
import { compareAddresses } from '../../services/utils';
|
|
17
|
+
import { compareAddresses, DEFAULT_TIMEOUT } from '../../services/utils';
|
|
18
18
|
import { getViemProvider } from '../../services/viem';
|
|
19
19
|
export const getMorphoBlueAggregatedPositionData = ({ usedAssets, assetsData, marketInfo }) => {
|
|
20
20
|
var _a, _b, _c, _d, _e, _f;
|
|
@@ -194,19 +194,29 @@ const REWARDS_QUERY = `
|
|
|
194
194
|
*/
|
|
195
195
|
export const getReallocatableLiquidity = (marketId_1, ...args_1) => __awaiter(void 0, [marketId_1, ...args_1], void 0, function* (marketId, network = NetworkNumber.Eth) {
|
|
196
196
|
var _a;
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
197
|
+
try {
|
|
198
|
+
const response = yield fetch(API_URL, {
|
|
199
|
+
method: 'POST',
|
|
200
|
+
headers: { 'Content-Type': 'application/json' },
|
|
201
|
+
body: JSON.stringify({
|
|
202
|
+
query: MARKET_QUERY,
|
|
203
|
+
variables: { uniqueKey: marketId, chainId: network },
|
|
204
|
+
}),
|
|
205
|
+
signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
|
|
206
|
+
});
|
|
207
|
+
const data = yield response.json();
|
|
208
|
+
const marketData = (_a = data === null || data === void 0 ? void 0 : data.data) === null || _a === void 0 ? void 0 : _a.marketByUniqueKey;
|
|
209
|
+
if (!marketData)
|
|
210
|
+
throw new Error('Market data not found');
|
|
211
|
+
return {
|
|
212
|
+
reallocatableLiquidity: marketData.reallocatableLiquidityAssets,
|
|
213
|
+
targetBorrowUtilization: marketData.targetBorrowUtilization,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
console.error('External API Failure: Morpho blue reallocatable liquidity', error);
|
|
218
|
+
throw new Error('Failed to fetch reallocatable liquidity');
|
|
219
|
+
}
|
|
210
220
|
});
|
|
211
221
|
/**
|
|
212
222
|
* Get liquidity to allocate for a given amount to borrow.
|
|
@@ -245,71 +255,78 @@ export const getLiquidityToAllocate = (amountToBorrow, totalBorrow, totalSupply,
|
|
|
245
255
|
*/
|
|
246
256
|
export const getReallocation = (market_1, assetsData_1, amountToBorrow_1, ...args_1) => __awaiter(void 0, [market_1, assetsData_1, amountToBorrow_1, ...args_1], void 0, function* (market, assetsData, amountToBorrow, network = NetworkNumber.Eth) {
|
|
247
257
|
var _a, _b, _c;
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const vaultAllocations = marketData.publicAllocatorSharedLiquidity.filter((item) => compareAddresses(item.vault.address, vaultAddress));
|
|
283
|
-
for (const item of vaultAllocations) {
|
|
258
|
+
try {
|
|
259
|
+
const { marketId, loanToken } = market;
|
|
260
|
+
const response = yield fetch(API_URL, {
|
|
261
|
+
method: 'POST',
|
|
262
|
+
headers: { 'Content-Type': 'application/json' },
|
|
263
|
+
body: JSON.stringify({
|
|
264
|
+
query: MARKET_QUERY,
|
|
265
|
+
variables: { uniqueKey: marketId, chainId: network },
|
|
266
|
+
}),
|
|
267
|
+
signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
|
|
268
|
+
});
|
|
269
|
+
const data = yield response.json();
|
|
270
|
+
const marketData = (_a = data === null || data === void 0 ? void 0 : data.data) === null || _a === void 0 ? void 0 : _a.marketByUniqueKey;
|
|
271
|
+
if (!marketData)
|
|
272
|
+
throw new Error('Market data not found');
|
|
273
|
+
const loanAssetInfo = getAssetInfoByAddress(loanToken, network);
|
|
274
|
+
const { totalBorrow, totalSupply } = assetsData[loanAssetInfo.symbol] || { totalBorrow: '0', totalSupply: '0' };
|
|
275
|
+
const totalBorrowWei = assetAmountInWei(totalBorrow, loanAssetInfo.symbol);
|
|
276
|
+
const totalSupplyWei = assetAmountInWei(totalSupply, loanAssetInfo.symbol);
|
|
277
|
+
const newTotalBorrowAssets = new Dec(totalBorrowWei).add(amountToBorrow).toString();
|
|
278
|
+
const newUtil = new Dec(newTotalBorrowAssets).div(totalSupplyWei).toString();
|
|
279
|
+
const newUtilScaled = new Dec(newUtil).mul(1e18).toString();
|
|
280
|
+
if (new Dec(newUtilScaled).lt(marketData.targetBorrowUtilization))
|
|
281
|
+
return { vaults: [], withdrawals: [] };
|
|
282
|
+
const liquidityToAllocate = getLiquidityToAllocate(amountToBorrow, totalBorrowWei, totalSupplyWei, marketData.targetBorrowUtilization, marketData.reallocatableLiquidityAssets);
|
|
283
|
+
const vaultTotalAssets = marketData.publicAllocatorSharedLiquidity.reduce((acc, item) => {
|
|
284
|
+
const vaultAddress = item.vault.address;
|
|
285
|
+
acc[vaultAddress] = new Dec(acc[vaultAddress] || '0').add(item.assets).toString();
|
|
286
|
+
return acc;
|
|
287
|
+
}, {});
|
|
288
|
+
const sortedVaults = Object.entries(vaultTotalAssets).sort(([, a], [, b]) => new Dec(b || '0').sub(a || '0').toNumber());
|
|
289
|
+
const withdrawalsPerVault = {};
|
|
290
|
+
let totalReallocated = '0';
|
|
291
|
+
for (const [vaultAddress] of sortedVaults) {
|
|
284
292
|
if (new Dec(totalReallocated).gte(liquidityToAllocate))
|
|
285
293
|
break;
|
|
286
|
-
const
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
294
|
+
const vaultAllocations = marketData.publicAllocatorSharedLiquidity.filter((item) => compareAddresses(item.vault.address, vaultAddress));
|
|
295
|
+
for (const item of vaultAllocations) {
|
|
296
|
+
if (new Dec(totalReallocated).gte(liquidityToAllocate))
|
|
297
|
+
break;
|
|
298
|
+
const itemAmount = item.assets;
|
|
299
|
+
const leftToAllocate = new Dec(liquidityToAllocate).sub(totalReallocated).toString();
|
|
300
|
+
const amountToTake = new Dec(itemAmount).lt(leftToAllocate) ? itemAmount : leftToAllocate;
|
|
301
|
+
totalReallocated = new Dec(totalReallocated).add(amountToTake).toString();
|
|
302
|
+
const withdrawal = [
|
|
303
|
+
[
|
|
304
|
+
item.allocationMarket.loanAsset.address,
|
|
305
|
+
(_b = item.allocationMarket.collateralAsset) === null || _b === void 0 ? void 0 : _b.address,
|
|
306
|
+
(_c = item.allocationMarket.oracle) === null || _c === void 0 ? void 0 : _c.address,
|
|
307
|
+
item.allocationMarket.irmAddress,
|
|
308
|
+
item.allocationMarket.lltv,
|
|
309
|
+
],
|
|
310
|
+
amountToTake.toString(),
|
|
311
|
+
item.allocationMarket.uniqueKey,
|
|
312
|
+
];
|
|
313
|
+
if (!withdrawalsPerVault[vaultAddress]) {
|
|
314
|
+
withdrawalsPerVault[vaultAddress] = [];
|
|
315
|
+
}
|
|
316
|
+
withdrawalsPerVault[vaultAddress].push(withdrawal);
|
|
303
317
|
}
|
|
304
|
-
withdrawalsPerVault[vaultAddress].push(withdrawal);
|
|
305
318
|
}
|
|
319
|
+
const vaults = Object.keys(withdrawalsPerVault);
|
|
320
|
+
const withdrawals = vaults.map((vaultAddress) => withdrawalsPerVault[vaultAddress].sort((a, b) => a[2].localeCompare(b[2])).map(w => [w[0], w[1]]));
|
|
321
|
+
return {
|
|
322
|
+
vaults,
|
|
323
|
+
withdrawals,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
catch (error) {
|
|
327
|
+
console.error('External API Failure: Morpho blue reallocation', error);
|
|
328
|
+
throw new Error('Failed to fetch reallocation data');
|
|
306
329
|
}
|
|
307
|
-
const vaults = Object.keys(withdrawalsPerVault);
|
|
308
|
-
const withdrawals = vaults.map((vaultAddress) => withdrawalsPerVault[vaultAddress].sort((a, b) => a[2].localeCompare(b[2])).map(w => [w[0], w[1]]));
|
|
309
|
-
return {
|
|
310
|
-
vaults,
|
|
311
|
-
withdrawals,
|
|
312
|
-
};
|
|
313
330
|
});
|
|
314
331
|
export const getRewardsForMarket = (marketId_1, ...args_1) => __awaiter(void 0, [marketId_1, ...args_1], void 0, function* (marketId, network = NetworkNumber.Eth) {
|
|
315
332
|
var _a;
|
package/esm/services/utils.d.ts
CHANGED
|
@@ -24,3 +24,4 @@ export declare const isEnabledOnBitmap: (bitmap: number, assetId: number) => big
|
|
|
24
24
|
export declare const MAXUINT: string;
|
|
25
25
|
export declare const isMaxuint: (amount: string) => boolean;
|
|
26
26
|
export declare const isMainnetNetwork: (network: NetworkNumber) => network is NetworkNumber.Eth;
|
|
27
|
+
export declare const DEFAULT_TIMEOUT = 2000;
|
package/esm/services/utils.js
CHANGED
|
@@ -37,3 +37,4 @@ export const isEnabledOnBitmap = (bitmap, assetId) => (BigInt(bitmap) >> BigInt(
|
|
|
37
37
|
export const MAXUINT = '115792089237316195423570985008687907853269984665640564039457584007913129639935';
|
|
38
38
|
export const isMaxuint = (amount) => compareAddresses(MAXUINT, amount);
|
|
39
39
|
export const isMainnetNetwork = (network) => network === NetworkNumber.Eth;
|
|
40
|
+
export const DEFAULT_TIMEOUT = 2000; // 2 seconds
|
package/esm/staking/staking.js
CHANGED
|
@@ -10,34 +10,54 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
import Dec from 'decimal.js';
|
|
11
11
|
import memoize from 'memoizee';
|
|
12
12
|
import { BLOCKS_IN_A_YEAR } from '../constants';
|
|
13
|
+
import { DEFAULT_TIMEOUT } from '../services/utils';
|
|
13
14
|
const getSsrApy = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
try {
|
|
16
|
+
const res = yield fetch('https://fe.defisaver.com/api/sky/data', { signal: AbortSignal.timeout(DEFAULT_TIMEOUT) });
|
|
17
|
+
const data = yield res.json();
|
|
18
|
+
return new Dec(data.data.skyData[0].sky_savings_rate_apy).mul(100).toString();
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
console.error('External API Failure: Failed to fetch SSR APY from external API', e);
|
|
22
|
+
return '0';
|
|
23
|
+
}
|
|
17
24
|
});
|
|
18
25
|
const getSuperOETHApy = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
body: JSON.stringify({
|
|
25
|
-
query: '\n query OTokenApy($chainId: Int!, $token: String!) {\n oTokenApies(\n limit: 1\n orderBy: timestamp_DESC\n where: {chainId_eq: $chainId, otoken_containsInsensitive: $token}\n ) {\n apy7DayAvg\n apy14DayAvg\n apy30DayAvg\n apr\n apy\n }\n}\n ',
|
|
26
|
-
variables: {
|
|
27
|
-
token: '0xdbfefd2e8460a6ee4955a68582f85708baea60a3',
|
|
28
|
-
chainId: 8453,
|
|
26
|
+
try {
|
|
27
|
+
const res = yield fetch('https://origin.squids.live/origin-squid/graphql', {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: {
|
|
30
|
+
'Content-Type': 'application/json',
|
|
29
31
|
},
|
|
30
|
-
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
body: JSON.stringify({
|
|
33
|
+
query: '\n query OTokenApy($chainId: Int!, $token: String!) {\n oTokenApies(\n limit: 1\n orderBy: timestamp_DESC\n where: {chainId_eq: $chainId, otoken_containsInsensitive: $token}\n ) {\n apy7DayAvg\n apy14DayAvg\n apy30DayAvg\n apr\n apy\n }\n}\n ',
|
|
34
|
+
variables: {
|
|
35
|
+
token: '0xdbfefd2e8460a6ee4955a68582f85708baea60a3',
|
|
36
|
+
chainId: 8453,
|
|
37
|
+
},
|
|
38
|
+
}),
|
|
39
|
+
signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
|
|
40
|
+
});
|
|
41
|
+
const data = yield res.json();
|
|
42
|
+
return new Dec(data.data.oTokenApies[0].apy).mul(100).toString();
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
console.error('External API Failure: Failed to fetch Super OETH APY from external API', e);
|
|
46
|
+
return '0';
|
|
47
|
+
}
|
|
34
48
|
});
|
|
35
49
|
const getApyFromDfsApi = (asset) => __awaiter(void 0, void 0, void 0, function* () {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
50
|
+
try {
|
|
51
|
+
const res = yield fetch(`https://fe.defisaver.com/api/staking/apy?asset=${asset}`, { signal: AbortSignal.timeout(DEFAULT_TIMEOUT) });
|
|
52
|
+
if (!res.ok)
|
|
53
|
+
throw new Error(`Failed to fetch APY for ${asset}`);
|
|
54
|
+
const data = yield res.json();
|
|
55
|
+
return String(data.apy);
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
console.error(`External API Failure: Failed to fetch APY for ${asset} from DFS API`, e);
|
|
59
|
+
return '0';
|
|
60
|
+
}
|
|
41
61
|
});
|
|
42
62
|
export const STAKING_ASSETS = ['cbETH', 'wstETH', 'cbETH', 'rETH', 'sDAI', 'weETH', 'sUSDe', 'osETH', 'ezETH', 'ETHx', 'rsETH', 'pufETH', 'wrsETH', 'wsuperOETHb', 'sUSDS', 'PT eUSDe May', 'PT sUSDe July', 'PT USDe July', 'PT eUSDe Aug', 'tETH', 'PT sUSDe Sep', 'PT USDe Sep'];
|
|
43
63
|
export const getStakingApy = memoize((asset) => __awaiter(void 0, void 0, void 0, function* () {
|
package/esm/types/compound.d.ts
CHANGED
|
@@ -50,7 +50,9 @@ export interface CompoundV2AssetData extends CompoundAssetData {
|
|
|
50
50
|
export interface CompoundV3AssetData extends CompoundAssetData {
|
|
51
51
|
borrowCollateralFactor: string;
|
|
52
52
|
liquidateCollateralFactor: string;
|
|
53
|
+
liquidationFactor: string;
|
|
53
54
|
minDebt: string;
|
|
55
|
+
supplyReserved: string;
|
|
54
56
|
liquidationRatio: string;
|
|
55
57
|
supplyCap: string;
|
|
56
58
|
priceInBaseAsset: string;
|
package/package.json
CHANGED
package/src/fluid/index.ts
CHANGED
|
@@ -22,7 +22,9 @@ import {
|
|
|
22
22
|
import {
|
|
23
23
|
BTCPriceFeedContractViem, DFSFeedRegistryContractViem, ETHPriceFeedContractViem, FeedRegistryContractViem, FluidViewContractViem,
|
|
24
24
|
} from '../contracts';
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
compareAddresses, DEFAULT_TIMEOUT, getEthAmountForDecimals, isMainnetNetwork,
|
|
27
|
+
} from '../services/utils';
|
|
26
28
|
import {
|
|
27
29
|
getFluidAggregatedData,
|
|
28
30
|
mergeAssetData,
|
|
@@ -256,8 +258,10 @@ const getAdditionalMarketRateForDex = (token1PerShare: string, token0PerShare: s
|
|
|
256
258
|
const getTradingApy = async (poolAddress: EthAddress) => {
|
|
257
259
|
let res;
|
|
258
260
|
try {
|
|
259
|
-
res = await fetch(`https://api.fluid.instadapp.io/v2/1/dexes/${poolAddress}/apy`,
|
|
261
|
+
res = await fetch(`https://api.fluid.instadapp.io/v2/1/dexes/${poolAddress}/apy`,
|
|
262
|
+
{ signal: AbortSignal.timeout(DEFAULT_TIMEOUT) });
|
|
260
263
|
} catch (e) {
|
|
264
|
+
console.error('External API Failure: Fluid Trading Apy');
|
|
261
265
|
return '0';
|
|
262
266
|
}
|
|
263
267
|
if (!res.ok) {
|
|
@@ -19,6 +19,10 @@ export const formatMarketData = (data: any, network: NetworkNumber, baseAssetPri
|
|
|
19
19
|
const price = getEthAmountForDecimals(data.price, 8);
|
|
20
20
|
return ({
|
|
21
21
|
...data,
|
|
22
|
+
borrowCollateralFactor: data.borrowCollateralFactor.toString(),
|
|
23
|
+
liquidateCollateralFactor: data.liquidateCollateralFactor.toString(),
|
|
24
|
+
liquidationFactor: data.liquidationFactor.toString(),
|
|
25
|
+
supplyReserved: data.supplyReserved.toString(),
|
|
22
26
|
priceInBaseAsset: getEthAmountForDecimals(data.price, 8),
|
|
23
27
|
price: new Dec(price).mul(baseAssetPrice).toString(),
|
|
24
28
|
collateralFactor: getEthAmountForDecimals(data.borrowCollateralFactor, 18),
|
|
@@ -40,6 +44,13 @@ export const formatBaseData = (data: any, network: NetworkNumber, baseAssetPrice
|
|
|
40
44
|
const totalBorrow = getEthAmountForDecimals(new Dec(data.totalBorrow).mul(data.borrowIndex).toString(), 15 + assetInfo.decimals);
|
|
41
45
|
return ({
|
|
42
46
|
...data,
|
|
47
|
+
baseBorrowMin: data.baseBorrowMin.toString(),
|
|
48
|
+
baseTrackingBorrowRewardsSpeed: data.baseTrackingBorrowRewardsSpeed.toString(),
|
|
49
|
+
baseTrackingSupplyRewardsSpeed: data.baseTrackingSupplyRewardsSpeed.toString(),
|
|
50
|
+
borrowIndex: data.borrowIndex.toString(),
|
|
51
|
+
supplyIndex: data.supplyIndex.toString(),
|
|
52
|
+
trackingBorrowIndex: data.trackingBorrowIndex.toString(),
|
|
53
|
+
trackingSupplyIndex: data.trackingSupplyIndex.toString(),
|
|
43
54
|
supplyRate: aprToApy(new Dec(data.supplyRate).div(1e18).mul(SECONDS_PER_YEAR).mul(100)
|
|
44
55
|
.toString()),
|
|
45
56
|
borrowRate: aprToApy(new Dec(data.borrowRate).div(1e18).mul(SECONDS_PER_YEAR).mul(100)
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
} from '../../types';
|
|
15
15
|
import { borrowOperations, SECONDS_PER_YEAR, WAD } from '../../constants';
|
|
16
16
|
import { MorphoBlueViewContractViem } from '../../contracts';
|
|
17
|
-
import { compareAddresses } from '../../services/utils';
|
|
17
|
+
import { compareAddresses, DEFAULT_TIMEOUT } from '../../services/utils';
|
|
18
18
|
import { getViemProvider } from '../../services/viem';
|
|
19
19
|
|
|
20
20
|
export const getMorphoBlueAggregatedPositionData = ({ usedAssets, assetsData, marketInfo }: { usedAssets: MMUsedAssets, assetsData: MorphoBlueAssetsData, marketInfo: MorphoBlueMarketInfo }): MorphoBlueAggregatedPositionData => {
|
|
@@ -214,21 +214,30 @@ const REWARDS_QUERY = `
|
|
|
214
214
|
* @returns The reallocatable liquidity and target borrow utilization
|
|
215
215
|
*/
|
|
216
216
|
export const getReallocatableLiquidity = async (marketId: string, network: NetworkNumber = NetworkNumber.Eth): Promise<{ reallocatableLiquidity: string, targetBorrowUtilization: string }> => {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
217
|
+
try {
|
|
218
|
+
const response = await fetch(API_URL, {
|
|
219
|
+
method: 'POST',
|
|
220
|
+
headers: { 'Content-Type': 'application/json' },
|
|
221
|
+
body: JSON.stringify({
|
|
222
|
+
query: MARKET_QUERY,
|
|
223
|
+
variables: { uniqueKey: marketId, chainId: network },
|
|
224
|
+
}),
|
|
225
|
+
signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const data: { data: { marketByUniqueKey: MorphoBlueRealloactionMarketData } } = await response.json();
|
|
229
|
+
const marketData: MorphoBlueRealloactionMarketData = data?.data?.marketByUniqueKey;
|
|
230
|
+
|
|
231
|
+
if (!marketData) throw new Error('Market data not found');
|
|
230
232
|
|
|
231
|
-
|
|
233
|
+
return {
|
|
234
|
+
reallocatableLiquidity: marketData.reallocatableLiquidityAssets,
|
|
235
|
+
targetBorrowUtilization: marketData.targetBorrowUtilization,
|
|
236
|
+
};
|
|
237
|
+
} catch (error) {
|
|
238
|
+
console.error('External API Failure: Morpho blue reallocatable liquidity', error);
|
|
239
|
+
throw new Error('Failed to fetch reallocatable liquidity');
|
|
240
|
+
}
|
|
232
241
|
};
|
|
233
242
|
|
|
234
243
|
/**
|
|
@@ -269,90 +278,96 @@ export const getLiquidityToAllocate = (amountToBorrow: string, totalBorrow: stri
|
|
|
269
278
|
* @returns The vaults and withdrawals needed to reallocate liquidity
|
|
270
279
|
*/
|
|
271
280
|
export const getReallocation = async (market: MorphoBlueMarketData, assetsData: MorphoBlueAssetsData, amountToBorrow: string, network: NetworkNumber = NetworkNumber.Eth): Promise<{ vaults: string[], withdrawals: (string | string[])[][][] }> => {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
acc
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
([, a]: [string, string], [, b]: [string, string]) => new Dec(b || '0').sub(a || '0').toNumber(),
|
|
312
|
-
);
|
|
313
|
-
|
|
314
|
-
const withdrawalsPerVault: Record<string, [string[], string, string][]> = {};
|
|
315
|
-
let totalReallocated = '0';
|
|
316
|
-
for (const [vaultAddress] of sortedVaults) {
|
|
317
|
-
if (new Dec(totalReallocated).gte(liquidityToAllocate)) break;
|
|
281
|
+
try {
|
|
282
|
+
const { marketId, loanToken } = market;
|
|
283
|
+
const response = await fetch(API_URL, {
|
|
284
|
+
method: 'POST',
|
|
285
|
+
headers: { 'Content-Type': 'application/json' },
|
|
286
|
+
body: JSON.stringify({
|
|
287
|
+
query: MARKET_QUERY,
|
|
288
|
+
variables: { uniqueKey: marketId, chainId: network },
|
|
289
|
+
}),
|
|
290
|
+
signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
const data: { data: { marketByUniqueKey: MorphoBlueRealloactionMarketData } } = await response.json();
|
|
294
|
+
const marketData: MorphoBlueRealloactionMarketData = data?.data?.marketByUniqueKey;
|
|
295
|
+
|
|
296
|
+
if (!marketData) throw new Error('Market data not found');
|
|
297
|
+
|
|
298
|
+
const loanAssetInfo = getAssetInfoByAddress(loanToken, network);
|
|
299
|
+
const { totalBorrow, totalSupply } = assetsData[loanAssetInfo.symbol] || { totalBorrow: '0', totalSupply: '0' };
|
|
300
|
+
const totalBorrowWei = assetAmountInWei(totalBorrow!, loanAssetInfo.symbol);
|
|
301
|
+
const totalSupplyWei = assetAmountInWei(totalSupply!, loanAssetInfo.symbol);
|
|
302
|
+
|
|
303
|
+
const newTotalBorrowAssets = new Dec(totalBorrowWei).add(amountToBorrow).toString();
|
|
304
|
+
|
|
305
|
+
const newUtil = new Dec(newTotalBorrowAssets).div(totalSupplyWei).toString();
|
|
306
|
+
const newUtilScaled = new Dec(newUtil).mul(1e18).toString();
|
|
307
|
+
|
|
308
|
+
if (new Dec(newUtilScaled).lt(marketData.targetBorrowUtilization)) return { vaults: [], withdrawals: [] };
|
|
309
|
+
|
|
310
|
+
const liquidityToAllocate = getLiquidityToAllocate(amountToBorrow, totalBorrowWei, totalSupplyWei, marketData.targetBorrowUtilization, marketData.reallocatableLiquidityAssets);
|
|
311
|
+
|
|
312
|
+
const vaultTotalAssets = marketData.publicAllocatorSharedLiquidity.reduce(
|
|
313
|
+
(acc: Record<string, string>, item: MorphoBluePublicAllocatorItem) => {
|
|
314
|
+
const vaultAddress = item.vault.address;
|
|
315
|
+
acc[vaultAddress] = new Dec(acc[vaultAddress] || '0').add(item.assets).toString();
|
|
316
|
+
return acc;
|
|
317
|
+
},
|
|
318
|
+
{},
|
|
319
|
+
);
|
|
318
320
|
|
|
319
|
-
const
|
|
320
|
-
(
|
|
321
|
+
const sortedVaults = Object.entries(vaultTotalAssets).sort(
|
|
322
|
+
([, a]: [string, string], [, b]: [string, string]) => new Dec(b || '0').sub(a || '0').toNumber(),
|
|
321
323
|
);
|
|
322
|
-
|
|
324
|
+
|
|
325
|
+
const withdrawalsPerVault: Record<string, [string[], string, string][]> = {};
|
|
326
|
+
let totalReallocated = '0';
|
|
327
|
+
for (const [vaultAddress] of sortedVaults) {
|
|
323
328
|
if (new Dec(totalReallocated).gte(liquidityToAllocate)) break;
|
|
324
|
-
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
329
|
+
|
|
330
|
+
const vaultAllocations = marketData.publicAllocatorSharedLiquidity.filter(
|
|
331
|
+
(item: MorphoBluePublicAllocatorItem) => compareAddresses(item.vault.address, vaultAddress),
|
|
332
|
+
);
|
|
333
|
+
for (const item of vaultAllocations) {
|
|
334
|
+
if (new Dec(totalReallocated).gte(liquidityToAllocate)) break;
|
|
335
|
+
const itemAmount = item.assets;
|
|
336
|
+
const leftToAllocate = new Dec(liquidityToAllocate).sub(totalReallocated).toString();
|
|
337
|
+
const amountToTake = new Dec(itemAmount).lt(leftToAllocate) ? itemAmount : leftToAllocate;
|
|
338
|
+
totalReallocated = new Dec(totalReallocated).add(amountToTake).toString();
|
|
339
|
+
const withdrawal: [string[], string, string] = [
|
|
340
|
+
[
|
|
341
|
+
item.allocationMarket.loanAsset.address,
|
|
342
|
+
item.allocationMarket.collateralAsset?.address,
|
|
343
|
+
item.allocationMarket.oracle?.address,
|
|
344
|
+
item.allocationMarket.irmAddress,
|
|
345
|
+
item.allocationMarket.lltv,
|
|
346
|
+
],
|
|
347
|
+
amountToTake.toString(),
|
|
348
|
+
item.allocationMarket.uniqueKey,
|
|
349
|
+
];
|
|
350
|
+
if (!withdrawalsPerVault[vaultAddress]) {
|
|
351
|
+
withdrawalsPerVault[vaultAddress] = [];
|
|
352
|
+
}
|
|
353
|
+
withdrawalsPerVault[vaultAddress].push(withdrawal);
|
|
341
354
|
}
|
|
342
|
-
withdrawalsPerVault[vaultAddress].push(withdrawal);
|
|
343
355
|
}
|
|
344
|
-
}
|
|
345
356
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
357
|
+
const vaults = Object.keys(withdrawalsPerVault);
|
|
358
|
+
const withdrawals = vaults.map(
|
|
359
|
+
(vaultAddress) => withdrawalsPerVault[vaultAddress].sort(
|
|
360
|
+
(a, b) => a[2].localeCompare(b[2]),
|
|
361
|
+
).map(w => [w[0], w[1]]),
|
|
362
|
+
);
|
|
363
|
+
return {
|
|
364
|
+
vaults,
|
|
365
|
+
withdrawals,
|
|
366
|
+
};
|
|
367
|
+
} catch (error) {
|
|
368
|
+
console.error('External API Failure: Morpho blue reallocation', error);
|
|
369
|
+
throw new Error('Failed to fetch reallocation data');
|
|
370
|
+
}
|
|
356
371
|
};
|
|
357
372
|
|
|
358
373
|
export const getRewardsForMarket = async (marketId: string, network: NetworkNumber = NetworkNumber.Eth) => {
|
package/src/services/utils.ts
CHANGED
|
@@ -60,3 +60,5 @@ export const MAXUINT:string = '1157920892373161954235709850086879078532699846656
|
|
|
60
60
|
export const isMaxuint = (amount: string) => compareAddresses(MAXUINT, amount);
|
|
61
61
|
|
|
62
62
|
export const isMainnetNetwork = (network: NetworkNumber) => network === NetworkNumber.Eth;
|
|
63
|
+
|
|
64
|
+
export const DEFAULT_TIMEOUT = 2000; // 2 seconds
|
package/src/staking/staking.ts
CHANGED
|
@@ -2,37 +2,56 @@ import Dec from 'decimal.js';
|
|
|
2
2
|
import memoize from 'memoizee';
|
|
3
3
|
import { MMAssetsData, MMUsedAssets } from '../types/common';
|
|
4
4
|
import { BLOCKS_IN_A_YEAR } from '../constants';
|
|
5
|
+
import { DEFAULT_TIMEOUT } from '../services/utils';
|
|
5
6
|
|
|
6
7
|
const getSsrApy = async () => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
try {
|
|
9
|
+
const res = await fetch('https://fe.defisaver.com/api/sky/data',
|
|
10
|
+
{ signal: AbortSignal.timeout(DEFAULT_TIMEOUT) });
|
|
11
|
+
const data = await res.json();
|
|
12
|
+
return new Dec(data.data.skyData[0].sky_savings_rate_apy).mul(100).toString();
|
|
13
|
+
} catch (e) {
|
|
14
|
+
console.error('External API Failure: Failed to fetch SSR APY from external API', e);
|
|
15
|
+
return '0';
|
|
16
|
+
}
|
|
10
17
|
};
|
|
11
18
|
|
|
12
19
|
const getSuperOETHApy = async () => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
body: JSON.stringify({
|
|
19
|
-
query: '\n query OTokenApy($chainId: Int!, $token: String!) {\n oTokenApies(\n limit: 1\n orderBy: timestamp_DESC\n where: {chainId_eq: $chainId, otoken_containsInsensitive: $token}\n ) {\n apy7DayAvg\n apy14DayAvg\n apy30DayAvg\n apr\n apy\n }\n}\n ',
|
|
20
|
-
variables: {
|
|
21
|
-
token: '0xdbfefd2e8460a6ee4955a68582f85708baea60a3',
|
|
22
|
-
chainId: 8453,
|
|
20
|
+
try {
|
|
21
|
+
const res = await fetch('https://origin.squids.live/origin-squid/graphql', {
|
|
22
|
+
method: 'POST',
|
|
23
|
+
headers: {
|
|
24
|
+
'Content-Type': 'application/json',
|
|
23
25
|
},
|
|
24
|
-
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
body: JSON.stringify({
|
|
27
|
+
query: '\n query OTokenApy($chainId: Int!, $token: String!) {\n oTokenApies(\n limit: 1\n orderBy: timestamp_DESC\n where: {chainId_eq: $chainId, otoken_containsInsensitive: $token}\n ) {\n apy7DayAvg\n apy14DayAvg\n apy30DayAvg\n apr\n apy\n }\n}\n ',
|
|
28
|
+
variables: {
|
|
29
|
+
token: '0xdbfefd2e8460a6ee4955a68582f85708baea60a3',
|
|
30
|
+
chainId: 8453,
|
|
31
|
+
},
|
|
32
|
+
}),
|
|
33
|
+
signal: AbortSignal.timeout(DEFAULT_TIMEOUT),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const data = await res.json();
|
|
37
|
+
return new Dec(data.data.oTokenApies[0].apy).mul(100).toString();
|
|
38
|
+
} catch (e) {
|
|
39
|
+
console.error('External API Failure: Failed to fetch Super OETH APY from external API', e);
|
|
40
|
+
return '0';
|
|
41
|
+
}
|
|
29
42
|
};
|
|
30
43
|
|
|
31
44
|
const getApyFromDfsApi = async (asset: string) => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
45
|
+
try {
|
|
46
|
+
const res = await fetch(`https://fe.defisaver.com/api/staking/apy?asset=${asset}`,
|
|
47
|
+
{ signal: AbortSignal.timeout(DEFAULT_TIMEOUT) });
|
|
48
|
+
if (!res.ok) throw new Error(`Failed to fetch APY for ${asset}`);
|
|
49
|
+
const data = await res.json();
|
|
50
|
+
return String(data.apy);
|
|
51
|
+
} catch (e) {
|
|
52
|
+
console.error(`External API Failure: Failed to fetch APY for ${asset} from DFS API`, e);
|
|
53
|
+
return '0';
|
|
54
|
+
}
|
|
36
55
|
};
|
|
37
56
|
|
|
38
57
|
export const STAKING_ASSETS = ['cbETH', 'wstETH', 'cbETH', 'rETH', 'sDAI', 'weETH', 'sUSDe', 'osETH', 'ezETH', 'ETHx', 'rsETH', 'pufETH', 'wrsETH', 'wsuperOETHb', 'sUSDS', 'PT eUSDe May', 'PT sUSDe July', 'PT USDe July', 'PT eUSDe Aug', 'tETH', 'PT sUSDe Sep', 'PT USDe Sep'];
|
package/src/types/compound.ts
CHANGED
|
@@ -63,7 +63,9 @@ export interface CompoundV2AssetData extends CompoundAssetData {
|
|
|
63
63
|
export interface CompoundV3AssetData extends CompoundAssetData {
|
|
64
64
|
borrowCollateralFactor: string,
|
|
65
65
|
liquidateCollateralFactor: string,
|
|
66
|
+
liquidationFactor: string,
|
|
66
67
|
minDebt: string,
|
|
68
|
+
supplyReserved: string,
|
|
67
69
|
liquidationRatio: string,
|
|
68
70
|
supplyCap: string,
|
|
69
71
|
priceInBaseAsset: string,
|