From 4b2bba7b0dacacacaa70d77332852d921fdd5616 Mon Sep 17 00:00:00 2001 From: cd-sigma Date: Sat, 9 Dec 2023 21:10:11 +0530 Subject: [PATCH 1/5] aave --- backend/api/api.js | 2 + .../controllers/health.factor.controller.js | 32 ++++ .../api/resolvers/health.factor.resolver.js | 16 ++ backend/api/routes/health.factor.route.js | 7 + backend/config/aave.v2.config.json | 8 + test.js | 178 ++++++++++++++++++ 6 files changed, 243 insertions(+) create mode 100644 backend/api/controllers/health.factor.controller.js create mode 100644 backend/api/resolvers/health.factor.resolver.js create mode 100644 backend/api/routes/health.factor.route.js create mode 100644 test.js diff --git a/backend/api/api.js b/backend/api/api.js index 6ebd934..c1a9d08 100644 --- a/backend/api/api.js +++ b/backend/api/api.js @@ -8,6 +8,7 @@ const consoleLib = require("../lib/console.lib") const responseLib = require("../lib/response.lib") const cmtRoutes= require("./routes/cmt.route") +const healthFactorRoutes = require("./routes/health.factor.route") const userRoutes = require("./routes/user.route") const feedRoutes = require("./routes/feed.route") const alertRoutes= require("./routes/alert.route") @@ -36,6 +37,7 @@ const port = 3001 app.use("/token", tokenRoutes) app.use("/feed", feedRoutes) app.use("/alert", alertRoutes) + app.use("/health/factor", healthFactorRoutes) app.listen(port, () => { consoleLib.logInfo(`API listening at http://localhost:${port}`) diff --git a/backend/api/controllers/health.factor.controller.js b/backend/api/controllers/health.factor.controller.js new file mode 100644 index 0000000..82cbf48 --- /dev/null +++ b/backend/api/controllers/health.factor.controller.js @@ -0,0 +1,32 @@ +const responseLib = require("../../lib/response.lib") + +const healthFactorResolver = require("../resolvers/health.factor.resolver") +const validatorUtil = require("../../util/validators.util") + +const resStatusEnum = require("../../enum/res.status.enum") + + +async function calculateHealthFactor(req, res) { + try { + let {address} = req.params + + if (validatorUtil.isEmpty(address)) { + return responseLib.sendResponse(res, null, "Missing user address", resStatusEnum.VALIDATION_ERROR) + } + + address = address.toLowerCase() + + + const healthFactor = await healthFactorResolver.calculateHealthFactor(address) + + return responseLib.sendResponse(res, healthFactor, null, resStatusEnum.SUCCESS) + + + } catch (error) { + return responseLib.sendResponse(res, null, error, resStatusEnum.INTERNAL_SERVER_ERROR) + } +} + +module.exports = { + calculateHealthFactor: calculateHealthFactor +} \ No newline at end of file diff --git a/backend/api/resolvers/health.factor.resolver.js b/backend/api/resolvers/health.factor.resolver.js new file mode 100644 index 0000000..8b77193 --- /dev/null +++ b/backend/api/resolvers/health.factor.resolver.js @@ -0,0 +1,16 @@ +const mongoLib = require("../../lib/mongo.lib") + + + +async function calculateHealthFactor(address) { + try { + + + } catch (error) { + throw error + } +} + +module.exports = { + calculateHealthFactor: calculateHealthFactor +} \ No newline at end of file diff --git a/backend/api/routes/health.factor.route.js b/backend/api/routes/health.factor.route.js new file mode 100644 index 0000000..3452a9b --- /dev/null +++ b/backend/api/routes/health.factor.route.js @@ -0,0 +1,7 @@ +const express = require("express") +const router = express.Router() +const healthFactorController = require("../controllers/health.factor.controller") + +router.route("/:address").get(healthFactorController.calculateHealthFactor) + +module.exports = router diff --git a/backend/config/aave.v2.config.json b/backend/config/aave.v2.config.json index 355d42e..73ebb9e 100644 --- a/backend/config/aave.v2.config.json +++ b/backend/config/aave.v2.config.json @@ -323,6 +323,14 @@ "symbol": "1INCH", "address": "0x111111111117dc0aa78b770fa6a738034120c302", "decimals": 18 + }, + { + "aTokenAddress": "0xce1871f791548600cb59efbefFC9c38719142079", + "aTokenSymbol": "aLUSD", + "stableDebtTokenAddress": "0x39f010127274b2dBdB770B45e1de54d974974526", + "variableDebtTokenAddress": "0x411066489AB40442d6Fc215aD7c64224120D33F2", + "symbol": "LUSD", + "address": "0x5f98805A4E8be255a32880FDeC7F6728C6568bA0" } ], "defaultHealthFactorThreshold": 1.1 diff --git a/test.js b/test.js new file mode 100644 index 0000000..24bf59e --- /dev/null +++ b/test.js @@ -0,0 +1,178 @@ +require("dotenv").config({path: "../../.env"}); +const _ = require("lodash"); + +const mongoLib = require('../../lib/mongo.lib'); +const web3Lib = require('../../lib/web3.lib'); +const alertLib = require('../../lib/alert.lib'); +const redisLib = require('../../lib/redis.lib'); +const consoleLib = require('../../lib/console.lib'); + +const TRANSMIT_METHOD_ID = "0xc9807539"; +const serviceConfig = require('../../configs/service.config'); +const positionModel = require('../../models/position.model'); +const wtfLogTypeEnum = require('../../enums/wtf.log.types.enum'); +const positionVulnerabilityEnum = require('../../enums/position.vulnerability.enum'); + +const aaveV2OracleConfig = require('../../configs/aave.v2.oracle.json'); +const aggregators = aaveV2OracleConfig.map((oracle) => oracle.aggregatorAddress.toLowerCase()); +const PRICE_ORACLE_CONTRACT = "0xa50ba011c48153de246e5192c8f9258a2ba79ca9" +const PROTOCOL_DATA_PROVIDER = "0x057835ad21a177dbdd3090bb1cae03eacf78fc6d" + +//check oracle for ens and weth and xsushi + +function calculateHealthFactor(position) { + try { + let supplied = position.metadata.supplied.map((supply) => { + let balance = supply.balance; + let liqudationThreshold = supply.liqudationThreshold; + let price = supply.price; + return balance * liqudationThreshold * price; + }) + + let borrowed = position.metadata.borrowed.map((borrow) => { + let balance = borrow.balance; + let price = borrow.price; + return balance * price; + }) + + let suppliedSum = _.sum(supplied); + let borrowedSum = _.sum(borrowed); + + return suppliedSum / borrowedSum + + } catch (error) { + consoleLib.logError(error); + } +} + + +(async () => { + try { + await mongoLib.connect(process.env.MONGO_URL); + await redisLib.connect(process.env.REDIS_URL); + + const web3 = web3Lib.getWebSocketWeb3Instance(process.env.ETH_NODE_WS_URL); + + web3.eth.subscribe('pendingTransactions', async (error, txHash) => { + let txnData = await web3.eth.getTransaction(txHash); + if (txnData && txnData.to && aggregators.includes(txnData.to.toLowerCase()) && txnData.input && txnData.input.slice(0, 10).toLowerCase() === TRANSMIT_METHOD_ID) { + let oracle = aaveV2OracleConfig.find((oracle) => oracle.aggregatorAddress.toLowerCase() === txnData.to.toLowerCase()); + let underlyingAsset = oracle.underlyingAsset; + + let decodedParams = web3.eth.abi.decodeParameters(["bytes", "bytes32[]", "bytes32[]", "bytes32"], "0x" + txnData.input.slice(10)); + let decodedReport = web3.eth.abi.decodeParameters(["bytes32", "bytes32", "uint192[]"], decodedParams[0]); + let obs = decodedReport[2]; + let newPrice = obs[parseInt(obs.length / 2)] / 10 ** 18; //TODO: store aggregator decimals in config + + let mongoStartTime = Date.now(); + let positionOfUnderlyingAssetInDanger = await mongoLib.findByQuery(positionModel, + { + $or: [ + {"metadata.supplied.underlying": underlyingAsset}, + {"metadata.borrowed.underlying": underlyingAsset} + ], + vulnerability: positionVulnerabilityEnum.HIGH + } + ); + + let dangreousPositions = []; + positionOfUnderlyingAssetInDanger.forEach((position) => { + calculateHealthFactor(position) < 1 && dangreousPositions.push(position); + }) + + + await alertLib.oracleUpdateAlert( + txnData.to, + oracle.desc, + txnData.hash, + newPrice, + positionOfUnderlyingAssetInDanger.length, + positionOfUnderlyingAssetInDanger.map((position) => position.owner), + (Date.now() - mongoStartTime) / 1000, + ) + } + }) + } catch (error) { + consoleLib.logError(error); + await alertLib.wtfAlert(serviceConfig.aaveLiquidator.serviceName, __filename, error, wtfLogTypeEnum.AAVE_LIQUIDATOR_SERVICE_FAILED) + process.exit(1); + } +})(); + +/** + * Paste one or more documents here + */ +// { +// "positionId": "aave_v2-ETH-lending-0xd75a171548b737783cae2a3123b50379c38d14ea", +// "__v": 0, +// "chain": "ETH", +// "createdAt": { +// "$date": "2023-11-28T13:35:38.243Z" +// }, +// "group": "lending", +// "isSafe": true, +// "metadata": { +// "borrowed": [ +// { +// "token": "0x531842cebbdd378f8ee36d171d6cc9c4fcf475ec", +// "balance": 3130.375381, +// "underlying": "0xdac17f958d2ee523a2206206994597c13d831ec7", +// "symbol": "USDT" +// }, +// { +// "token": "0x619beb58998ed2278e08620f97007e1116d5d25b", +// "balance": 507571.170478, +// "underlying": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", +// "symbol": "USDC" +// }, +// { +// "token": "0x6c3c78838c761c6ac7be9f59fe808ea2a6e4379d", +// "balance": 390776.0018851623, +// "underlying": "0x6b175474e89094c44da98b954eedeac495271d0f", +// "symbol": "DAI" +// } +// ], +// "supplied": [ +// { +// "token": "0x9ff58f4ffb29fa2266ab25e75e2a8b3503311656", +// "balance": 0.04345675, +// "underlying": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", +// "symbol": "WBTC" +// }, +// { +// "token": "0x030ba81f1c18d280636f32af80b9aad02cf0854e", +// "balance": 826.1031965669534, +// "underlying": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", +// "symbol": "WETH" +// }, +// { +// "token": "0x028171bca77440897b824ca71d1c56cac55b68a3", +// "balance": 4.359585533392954, +// "underlying": "0x6b175474e89094c44da98b954eedeac495271d0f", +// "symbol": "DAI" +// }, +// { +// "token": "0xbcca60bb61934080951369a648fb03df4f96263c", +// "balance": 8.370754, +// "underlying": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", +// "symbol": "USDC" +// }, +// { +// "token": "0x1982b2f5814301d4e9a8b0201555376e62f82428", +// "balance": 1e-18, +// "underlying": "0xae7ab96520de3a18e5e111b5eaab095312d7fe84", +// "symbol": "stETH" +// } +// ], +// "healthFactor": 1.8650456698586677, +// "totalSuppliedValue": 826.9031377601987, +// "totalBorrowedValue": 381.2528666999765, +// "totalUsdValue": 2858725.4963638578, +// "totalEthValue": 1208.1560044601752 +// }, +// "owner": "0xd75a171548b737783cae2a3123b50379c38d14ea", +// "protocolId": "aave_v2", +// "updatedAt": { +// "$date": "2023-12-08T13:28:14.716Z" +// } +// } \ No newline at end of file From 91a1e2f3007cfbcc9ccea3cdd95364782f614058 Mon Sep 17 00:00:00 2001 From: cd-sigma Date: Sat, 9 Dec 2023 22:36:31 +0530 Subject: [PATCH 2/5] aave v2 --- .../controllers/health.factor.controller.js | 9 +- .../api/resolvers/health.factor.resolver.js | 6 +- backend/api/views/index.html | 23 ++- test.js | 178 ------------------ 4 files changed, 32 insertions(+), 184 deletions(-) delete mode 100644 test.js diff --git a/backend/api/controllers/health.factor.controller.js b/backend/api/controllers/health.factor.controller.js index 82cbf48..30258c2 100644 --- a/backend/api/controllers/health.factor.controller.js +++ b/backend/api/controllers/health.factor.controller.js @@ -1,7 +1,9 @@ const responseLib = require("../../lib/response.lib") +const mongoLib = require("../../lib/mongo.lib") const healthFactorResolver = require("../resolvers/health.factor.resolver") const validatorUtil = require("../../util/validators.util") +const positionModel = require("../../model/position.model") const resStatusEnum = require("../../enum/res.status.enum") @@ -16,8 +18,13 @@ async function calculateHealthFactor(req, res) { address = address.toLowerCase() + const position = await mongoLib.findOneByQuery(positionModel, {owner: address}) - const healthFactor = await healthFactorResolver.calculateHealthFactor(address) + if (validatorUtil.isEmpty(position)) { + return responseLib.sendResponse(res, null, "No position found", resStatusEnum.VALIDATION_ERROR) + } + + const healthFactor = await healthFactorResolver.calculateHealthFactor(position) return responseLib.sendResponse(res, healthFactor, null, resStatusEnum.SUCCESS) diff --git a/backend/api/resolvers/health.factor.resolver.js b/backend/api/resolvers/health.factor.resolver.js index 8b77193..6b3aba7 100644 --- a/backend/api/resolvers/health.factor.resolver.js +++ b/backend/api/resolvers/health.factor.resolver.js @@ -1,9 +1,11 @@ const mongoLib = require("../../lib/mongo.lib") - -async function calculateHealthFactor(address) { +async function calculateHealthFactor(position) { try { + return position + + } catch (error) { diff --git a/backend/api/views/index.html b/backend/api/views/index.html index e514e8f..13adb2f 100644 --- a/backend/api/views/index.html +++ b/backend/api/views/index.html @@ -9,9 +9,11 @@ padding: 0; background-color: #f4f4f4; } + h1, h2 { color: #333; } + .form-container { background: #fff; padding: 20px; @@ -19,11 +21,13 @@ border-radius: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } + label { display: block; margin-bottom: 5px; font-weight: bold; } + input[type="text"], input[type="email"] { width: 100%; padding: 8px; @@ -31,6 +35,7 @@ border-radius: 4px; border: 1px solid #ddd; } + input[type="submit"] { background: #007bff; color: white; @@ -39,6 +44,7 @@ border-radius: 4px; cursor: pointer; } + input[type="submit"]:hover { background: #0056b3; } @@ -72,21 +78,32 @@

Validate Signature

+ + diff --git a/test.js b/test.js deleted file mode 100644 index 24bf59e..0000000 --- a/test.js +++ /dev/null @@ -1,178 +0,0 @@ -require("dotenv").config({path: "../../.env"}); -const _ = require("lodash"); - -const mongoLib = require('../../lib/mongo.lib'); -const web3Lib = require('../../lib/web3.lib'); -const alertLib = require('../../lib/alert.lib'); -const redisLib = require('../../lib/redis.lib'); -const consoleLib = require('../../lib/console.lib'); - -const TRANSMIT_METHOD_ID = "0xc9807539"; -const serviceConfig = require('../../configs/service.config'); -const positionModel = require('../../models/position.model'); -const wtfLogTypeEnum = require('../../enums/wtf.log.types.enum'); -const positionVulnerabilityEnum = require('../../enums/position.vulnerability.enum'); - -const aaveV2OracleConfig = require('../../configs/aave.v2.oracle.json'); -const aggregators = aaveV2OracleConfig.map((oracle) => oracle.aggregatorAddress.toLowerCase()); -const PRICE_ORACLE_CONTRACT = "0xa50ba011c48153de246e5192c8f9258a2ba79ca9" -const PROTOCOL_DATA_PROVIDER = "0x057835ad21a177dbdd3090bb1cae03eacf78fc6d" - -//check oracle for ens and weth and xsushi - -function calculateHealthFactor(position) { - try { - let supplied = position.metadata.supplied.map((supply) => { - let balance = supply.balance; - let liqudationThreshold = supply.liqudationThreshold; - let price = supply.price; - return balance * liqudationThreshold * price; - }) - - let borrowed = position.metadata.borrowed.map((borrow) => { - let balance = borrow.balance; - let price = borrow.price; - return balance * price; - }) - - let suppliedSum = _.sum(supplied); - let borrowedSum = _.sum(borrowed); - - return suppliedSum / borrowedSum - - } catch (error) { - consoleLib.logError(error); - } -} - - -(async () => { - try { - await mongoLib.connect(process.env.MONGO_URL); - await redisLib.connect(process.env.REDIS_URL); - - const web3 = web3Lib.getWebSocketWeb3Instance(process.env.ETH_NODE_WS_URL); - - web3.eth.subscribe('pendingTransactions', async (error, txHash) => { - let txnData = await web3.eth.getTransaction(txHash); - if (txnData && txnData.to && aggregators.includes(txnData.to.toLowerCase()) && txnData.input && txnData.input.slice(0, 10).toLowerCase() === TRANSMIT_METHOD_ID) { - let oracle = aaveV2OracleConfig.find((oracle) => oracle.aggregatorAddress.toLowerCase() === txnData.to.toLowerCase()); - let underlyingAsset = oracle.underlyingAsset; - - let decodedParams = web3.eth.abi.decodeParameters(["bytes", "bytes32[]", "bytes32[]", "bytes32"], "0x" + txnData.input.slice(10)); - let decodedReport = web3.eth.abi.decodeParameters(["bytes32", "bytes32", "uint192[]"], decodedParams[0]); - let obs = decodedReport[2]; - let newPrice = obs[parseInt(obs.length / 2)] / 10 ** 18; //TODO: store aggregator decimals in config - - let mongoStartTime = Date.now(); - let positionOfUnderlyingAssetInDanger = await mongoLib.findByQuery(positionModel, - { - $or: [ - {"metadata.supplied.underlying": underlyingAsset}, - {"metadata.borrowed.underlying": underlyingAsset} - ], - vulnerability: positionVulnerabilityEnum.HIGH - } - ); - - let dangreousPositions = []; - positionOfUnderlyingAssetInDanger.forEach((position) => { - calculateHealthFactor(position) < 1 && dangreousPositions.push(position); - }) - - - await alertLib.oracleUpdateAlert( - txnData.to, - oracle.desc, - txnData.hash, - newPrice, - positionOfUnderlyingAssetInDanger.length, - positionOfUnderlyingAssetInDanger.map((position) => position.owner), - (Date.now() - mongoStartTime) / 1000, - ) - } - }) - } catch (error) { - consoleLib.logError(error); - await alertLib.wtfAlert(serviceConfig.aaveLiquidator.serviceName, __filename, error, wtfLogTypeEnum.AAVE_LIQUIDATOR_SERVICE_FAILED) - process.exit(1); - } -})(); - -/** - * Paste one or more documents here - */ -// { -// "positionId": "aave_v2-ETH-lending-0xd75a171548b737783cae2a3123b50379c38d14ea", -// "__v": 0, -// "chain": "ETH", -// "createdAt": { -// "$date": "2023-11-28T13:35:38.243Z" -// }, -// "group": "lending", -// "isSafe": true, -// "metadata": { -// "borrowed": [ -// { -// "token": "0x531842cebbdd378f8ee36d171d6cc9c4fcf475ec", -// "balance": 3130.375381, -// "underlying": "0xdac17f958d2ee523a2206206994597c13d831ec7", -// "symbol": "USDT" -// }, -// { -// "token": "0x619beb58998ed2278e08620f97007e1116d5d25b", -// "balance": 507571.170478, -// "underlying": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", -// "symbol": "USDC" -// }, -// { -// "token": "0x6c3c78838c761c6ac7be9f59fe808ea2a6e4379d", -// "balance": 390776.0018851623, -// "underlying": "0x6b175474e89094c44da98b954eedeac495271d0f", -// "symbol": "DAI" -// } -// ], -// "supplied": [ -// { -// "token": "0x9ff58f4ffb29fa2266ab25e75e2a8b3503311656", -// "balance": 0.04345675, -// "underlying": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", -// "symbol": "WBTC" -// }, -// { -// "token": "0x030ba81f1c18d280636f32af80b9aad02cf0854e", -// "balance": 826.1031965669534, -// "underlying": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", -// "symbol": "WETH" -// }, -// { -// "token": "0x028171bca77440897b824ca71d1c56cac55b68a3", -// "balance": 4.359585533392954, -// "underlying": "0x6b175474e89094c44da98b954eedeac495271d0f", -// "symbol": "DAI" -// }, -// { -// "token": "0xbcca60bb61934080951369a648fb03df4f96263c", -// "balance": 8.370754, -// "underlying": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", -// "symbol": "USDC" -// }, -// { -// "token": "0x1982b2f5814301d4e9a8b0201555376e62f82428", -// "balance": 1e-18, -// "underlying": "0xae7ab96520de3a18e5e111b5eaab095312d7fe84", -// "symbol": "stETH" -// } -// ], -// "healthFactor": 1.8650456698586677, -// "totalSuppliedValue": 826.9031377601987, -// "totalBorrowedValue": 381.2528666999765, -// "totalUsdValue": 2858725.4963638578, -// "totalEthValue": 1208.1560044601752 -// }, -// "owner": "0xd75a171548b737783cae2a3123b50379c38d14ea", -// "protocolId": "aave_v2", -// "updatedAt": { -// "$date": "2023-12-08T13:28:14.716Z" -// } -// } \ No newline at end of file From 3b8b451a1fe9b2645bd33d3703e8106d2fda39d3 Mon Sep 17 00:00:00 2001 From: cd-sigma Date: Sun, 10 Dec 2023 01:30:47 +0530 Subject: [PATCH 3/5] liqudatio threshold --- .../aave.v2.protocol.data.provider.abi.json | 297 ++++++++++++++++++ .../controllers/health.factor.controller.js | 89 +++++- .../api/resolvers/health.factor.resolver.js | 135 +++++++- 3 files changed, 516 insertions(+), 5 deletions(-) create mode 100644 backend/abi/aave.v2.protocol.data.provider.abi.json diff --git a/backend/abi/aave.v2.protocol.data.provider.abi.json b/backend/abi/aave.v2.protocol.data.provider.abi.json new file mode 100644 index 0000000..7be964c --- /dev/null +++ b/backend/abi/aave.v2.protocol.data.provider.abi.json @@ -0,0 +1,297 @@ +[ + { + "inputs": [ + { + "internalType": "contract ILendingPoolAddressesProvider", + "name": "addressesProvider", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ADDRESSES_PROVIDER", + "outputs": [ + { + "internalType": "contract ILendingPoolAddressesProvider", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAllATokens", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + } + ], + "internalType": "struct AaveProtocolDataProvider.TokenData[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAllReservesTokens", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + } + ], + "internalType": "struct AaveProtocolDataProvider.TokenData[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "getReserveConfigurationData", + "outputs": [ + { + "internalType": "uint256", + "name": "decimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "ltv", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationThreshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationBonus", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveFactor", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "usageAsCollateralEnabled", + "type": "bool" + }, + { + "internalType": "bool", + "name": "borrowingEnabled", + "type": "bool" + }, + { + "internalType": "bool", + "name": "stableBorrowRateEnabled", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isFrozen", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "getReserveData", + "outputs": [ + { + "internalType": "uint256", + "name": "availableLiquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalStableDebt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalVariableDebt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidityRate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "variableBorrowRate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "stableBorrowRate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "averageStableBorrowRate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidityIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "variableBorrowIndex", + "type": "uint256" + }, + { + "internalType": "uint40", + "name": "lastUpdateTimestamp", + "type": "uint40" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + } + ], + "name": "getReserveTokensAddresses", + "outputs": [ + { + "internalType": "address", + "name": "aTokenAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "stableDebtTokenAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "variableDebtTokenAddress", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "getUserReserveData", + "outputs": [ + { + "internalType": "uint256", + "name": "currentATokenBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "currentStableDebt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "currentVariableDebt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "principalStableDebt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "scaledVariableDebt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "stableBorrowRate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidityRate", + "type": "uint256" + }, + { + "internalType": "uint40", + "name": "stableRateLastUpdated", + "type": "uint40" + }, + { + "internalType": "bool", + "name": "usageAsCollateralEnabled", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/backend/api/controllers/health.factor.controller.js b/backend/api/controllers/health.factor.controller.js index 30258c2..5b5661a 100644 --- a/backend/api/controllers/health.factor.controller.js +++ b/backend/api/controllers/health.factor.controller.js @@ -6,11 +6,89 @@ const validatorUtil = require("../../util/validators.util") const positionModel = require("../../model/position.model") const resStatusEnum = require("../../enum/res.status.enum") +const web3Lib = require("../../lib/web3.lib"); async function calculateHealthFactor(req, res) { try { + let web3 = await web3Lib.getWebSocketWeb3Instance(process.env.ETH_NODE_WS_URL); let {address} = req.params + let { + usdtAmount, + usdtPrice, + wbtcAmount, + wbtcPrice, + wethAmount, + wethPrice, + yfiAmount, + yfiPrice, + zrxAmount, + zrxPrice, + uniAmount, + uniPrice, + aaveAmount, + aavePrice, + batAmount, + batPrice, + busdAmount, + busdPrice, + daiAmount, + daiPrice, + enjAmount, + enjPrice, + kncAmount, + kncPrice, + linkAmount, + linkPrice, + manaAmount, + manaPrice, + mkrAmount, + mkrPrice, + renAmount, + renPrice, + snxAmount, + snxPrice, + susdAmount, + susdPrice, + tusdAmount, + tusdPrice, + usdcAmount, + usdcPrice, + crvAmount, + crvPrice, + balAmount, + balPrice, + xsushiAmount, + xsushiPrice, + renfilAmount, + renfilPrice, + raiAmount, + raiPrice, + amplAmount, + amplPrice, + usdpAmount, + usdpPrice, + dpiAmount, + dpiPrice, + fraxAmount, + fraxPrice, + feiAmount, + feiPrice, + stethAmount, + stethPrice, + ensAmount, + ensPrice, + gusdAmount, + gusdPrice, + ustAmount, + ustPrice, + cvxAmount, + cvxPrice, + oneinchAmount, + oneinchPrice, + lusdAmount, + lusdPrice + } = req.body if (validatorUtil.isEmpty(address)) { return responseLib.sendResponse(res, null, "Missing user address", resStatusEnum.VALIDATION_ERROR) @@ -24,7 +102,16 @@ async function calculateHealthFactor(req, res) { return responseLib.sendResponse(res, null, "No position found", resStatusEnum.VALIDATION_ERROR) } - const healthFactor = await healthFactorResolver.calculateHealthFactor(position) + const healthFactor = await healthFactorResolver.calculateHealthFactor(position, web3, usdtAmount, + usdtPrice, wbtcAmount, wbtcPrice, wethAmount, wethPrice, yfiAmount, yfiPrice, zrxAmount, zrxPrice, uniAmount, + uniPrice, aaveAmount, aavePrice, batAmount, batPrice, busdAmount, busdPrice, daiAmount, daiPrice, + enjAmount, enjPrice, kncAmount, kncPrice, linkAmount, linkPrice, manaAmount, manaPrice, mkrAmount, + mkrPrice, renAmount, renPrice, snxAmount, snxPrice, susdAmount, susdPrice, tusdAmount, tusdPrice, + usdcAmount, usdcPrice, crvAmount, crvPrice, balAmount, balPrice, xsushiAmount, xsushiPrice, renfilAmount, + renfilPrice, raiAmount, raiPrice, amplAmount, amplPrice, usdpAmount, usdpPrice, dpiAmount, dpiPrice, + fraxAmount, fraxPrice, feiAmount, feiPrice, stethAmount, stethPrice, ensAmount, ensPrice, gusdAmount, + gusdPrice, ustAmount, ustPrice, cvxAmount, cvxPrice, oneinchAmount, oneinchPrice, lusdAmount, lusdPrice + ) return responseLib.sendResponse(res, healthFactor, null, resStatusEnum.SUCCESS) diff --git a/backend/api/resolvers/health.factor.resolver.js b/backend/api/resolvers/health.factor.resolver.js index 6b3aba7..ac0a6c6 100644 --- a/backend/api/resolvers/health.factor.resolver.js +++ b/backend/api/resolvers/health.factor.resolver.js @@ -1,13 +1,62 @@ const mongoLib = require("../../lib/mongo.lib") +const AAVE_V2_PROTOCOL_DATA_PROVIDER_ABI = require("../../abi/aave.v2.protocol.data.provider.abi.json") +const aaveV2ProtocolDataProviderContract = "0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d" - -async function calculateHealthFactor(position) { +async function calculateHealthFactor(position, web3, usdtAmount, usdtPrice, wbtcAmount, wbtcPrice, wethAmount, wethPrice, yfiAmount, yfiPrice, zrxAmount, zrxPrice, uniAmount, uniPrice, aaveAmount, aavePrice, batAmount, batPrice, busdAmount, busdPrice, daiAmount, daiPrice, enjAmount, enjPrice, kncAmount, kncPrice, linkAmount, linkPrice, manaAmount, manaPrice, mkrAmount, mkrPrice, renAmount, renPrice, snxAmount, snxPrice, susdAmount, susdPrice, tusdAmount, tusdPrice, usdcAmount, usdcPrice, crvAmount, crvPrice, balAmount, balPrice, xsushiAmount, xsushiPrice, renfilAmount, renfilPrice, raiAmount, raiPrice, amplAmount, amplPrice, usdpAmount, usdpPrice, dpiAmount, dpiPrice, fraxAmount, fraxPrice, feiAmount, feiPrice, stethAmount, stethPrice, ensAmount, ensPrice, gusdAmount, gusdPrice, ustAmount, ustPrice, cvxAmount, cvxPrice, oneinchAmount, oneinchPrice, lusdAmount, lusdPrice) { try { - return position + const aaveV2ProtocolDataProvider = new web3.eth.Contract(AAVE_V2_PROTOCOL_DATA_PROVIDER_ABI, aaveV2ProtocolDataProviderContract) + + const liqudationThresholdForTusd = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x0000000000085d4780B73119b644AE5ecd22b376").call()).liquidationThreshold) / 10000 + const liqudationThresholdForUsdc = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").call()).liquidationThreshold) / 10000 + const liqudationThresholdForCrv = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xd533a949740bb3306d119cc777fa900ba034cd52").call()).liquidationThreshold) / 10000 + const liqudationThresholdForBal = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xba100000625a3754423978a60c9317c58a424e3d").call()).liquidationThreshold) / 10000 + const liqudationThresholdForXsushi = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x8798249c2E607446EfB7Ad49eC89dD1865Ff4272").call()).liquidationThreshold) / 10000 + const liqudationThresholdForUsdp = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x1456688345527be1f37e9e627da0837d6f08c925").call()).liquidationThreshold) / 10000 + const liqudationThresholdForDpi = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x1494ca1f11d487c2bbe4543e90080aeba4ba3c2b").call()).liquidationThreshold) / 10000 + const liqudationThresholdForFei = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x956f47f50a910163d8bf957cf5846d573e7f87ca").call()).liquidationThreshold) / 10000 + const liqudationThresholdForSteth = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xae7ab96520de3a18e5e111b5eaab095312d7fe84").call()).liquidationThreshold) / 10000 + const liqudationThresholdForEns = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x57f8160e1c59d4346abb5cdefac810967e14e04f").call()).liquidationThreshold) / 10000 + const liqudationThresholdForCvx = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x4e3fbd56cd56c3e72c1403e103b45db9da5b9d2b").call()).liquidationThreshold) / 10000 + const liqudationThresholdForOneinch = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x111111111117dc0aa78b770fa6a738034120c302").call()).liquidationThreshold) / 10000 + const liqudationThresholdForLusd = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x5f98805A4E8be255a32880FDeC7F6728C6568bA0").call()).liquidationThreshold) / 10000 + const liqudationThresholdForWbtc = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").call()).liquidationThreshold) / 10000 + const liqudationThresholdForWeth = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").call()).liquidationThreshold) / 10000 + const liqudationThresholdForYfi = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e").call()).liquidationThreshold) / 10000 + const liqudationThresholdForZrx = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xe41d2489571d322189246dafa5ebde1f4699f498").call()).liquidationThreshold) / 10000 + const liqudationThresholdForUni = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x1f9840a85d5af5bf1d1762f925bdaddc4201f984").call()).liquidationThreshold) / 10000 + const liqudationThresholdForAave = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9").call()).liquidationThreshold) / 10000 + const liqudationThresholdForBat = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x0d8775f648430679a709e98d2b0cb6250d2887ef").call()).liquidationThreshold) / 10000 + const liqudationThresholdForDai = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x6b175474e89094c44da98b954eedeac495271d0f").call()).liquidationThreshold) / 10000 + const liqudationThresholdForEnj = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xf629cbd94d3791c9250152bd8dfbdf380e2a3b9c").call()).liquidationThreshold) / 10000 + const liqudationThresholdForKnc = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xdd974d5c2e2928dea5f71b9825b8b646686bd200").call()).liquidationThreshold) / 10000 + const liqudationThresholdForLink = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x514910771af9ca656af840dff83e8264ecf986ca").call()).liquidationThreshold) / 10000 + const liqudationThresholdForMana = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x0f5d2fb29fb7d3cfee444a200298f468908cc942").call()).liquidationThreshold) / 10000 + const liqudationThresholdForMkr = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2").call()).liquidationThreshold) / 10000 + const liqudationThresholdForRen = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x408e41876cccdc0f92210600ef50372656052a38").call()).liquidationThreshold) / 10000 + const liqudationThresholdForSnx = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f").call()).liquidationThreshold) / 10000 + // let supplied = position.metadata.supplied.map((supply) => { + // let balance = supply.balance; + // let liqudationThreshold = supply.liqudationThreshold; + // let price = supply.price; + // return balance * liqudationThreshold * price; + // }) + // + // let borrowed = position.metadata.borrowed.map((borrow) => { + // let balance = borrow.balance; + // let price = borrow.price; + // return balance * price; + // }) + // + // let suppliedSum = _.sum(supplied); + // let borrowedSum = _.sum(borrowed); + // + // return suppliedSum / borrowedSum + return 0 + } catch (error) { throw error } @@ -15,4 +64,82 @@ async function calculateHealthFactor(position) { module.exports = { calculateHealthFactor: calculateHealthFactor -} \ No newline at end of file +} + +// { +// "positionId": "aave_v2-ETH-lending-0xb63e8a8d04999500a97470769d10c4395789836d", +// "__v": 0, +// "chain": "ETH", +// "createdAt": { +// "$date": "2023-11-28T13:35:37.883Z" +// }, +// "group": "lending", +// "isSafe": true, +// "metadata": { +// "borrowed": [ +// { +// "token": "0x619beb58998ed2278e08620f97007e1116d5d25b", +// "balance": 108497.668814, +// "underlying": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", +// "symbol": "USDC" +// } +// ], +// "supplied": [ +// { +// "token": "0x028171bca77440897b824ca71d1c56cac55b68a3", +// "balance": 0.00028578441805119, +// "underlying": "0x6b175474e89094c44da98b954eedeac495271d0f", +// "symbol": "DAI" +// }, +// { +// "token": "0x9a14e23a58edf4efdcb360f68cd1b95ce2081a2f", +// "balance": 224.2077575644477, +// "underlying": "0xc18360217d8f7ab5e7c516566761ea12ce7f9d72", +// "symbol": "ENS" +// }, +// { +// "token": "0x030ba81f1c18d280636f32af80b9aad02cf0854e", +// "balance": 206.10337309472408, +// "underlying": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", +// "symbol": "WETH" +// } +// ], +// "healthFactor": 3.8683249995745093, +// "totalSuppliedValue": 207.00069827767385, +// "totalBorrowedValue": 45.820581513987456, +// "totalUsdValue": 598222.9413218026, +// "totalEthValue": 252.8212797916613 +// }, +// "owner": "0xb63e8a8d04999500a97470769d10c4395789836d", +// "protocolId": "aave_v2", +// "updatedAt": { +// "$date": "2023-12-08T13:28:14.716Z" +// } +// } + +/* +function calculateHealthFactor(position) { + try { + let supplied = position.metadata.supplied.map((supply) => { + let balance = supply.balance; + let liqudationThreshold = supply.liqudationThreshold; + let price = supply.price; + return balance * liqudationThreshold * price; + }) + + let borrowed = position.metadata.borrowed.map((borrow) => { + let balance = borrow.balance; + let price = borrow.price; + return balance * price; + }) + + let suppliedSum = _.sum(supplied); + let borrowedSum = _.sum(borrowed); + + return suppliedSum / borrowedSum + + } catch (error) { + consoleLib.logError(error); + } +} + */ \ No newline at end of file From f104083c0173a98fb8ece5e36bfcc4681dc55e6c Mon Sep 17 00:00:00 2001 From: cd-sigma Date: Sun, 10 Dec 2023 04:08:36 +0530 Subject: [PATCH 4/5] aave --- .../controllers/health.factor.controller.js | 120 +++++------------- .../api/resolvers/health.factor.resolver.js | 23 +++- backend/api/routes/health.factor.route.js | 4 +- 3 files changed, 58 insertions(+), 89 deletions(-) diff --git a/backend/api/controllers/health.factor.controller.js b/backend/api/controllers/health.factor.controller.js index 5b5661a..86ff9c8 100644 --- a/backend/api/controllers/health.factor.controller.js +++ b/backend/api/controllers/health.factor.controller.js @@ -8,86 +8,44 @@ const positionModel = require("../../model/position.model") const resStatusEnum = require("../../enum/res.status.enum") const web3Lib = require("../../lib/web3.lib"); +async function defaultHealthFactor(req, res) { + try { + let web3 = await web3Lib.getWebSocketWeb3Instance(process.env.ETH_NODE_WS_URL); + let {address} = req.params + + if (validatorUtil.isEmpty(address)) { + return responseLib.sendResponse(res, null, "Missing user address", resStatusEnum.VALIDATION_ERROR) + } + + address = address.toLowerCase() + + const position = await mongoLib.findOneByQuery(positionModel, {owner: address}) + + if (validatorUtil.isEmpty(position)) { + return responseLib.sendResponse(res, null, "No position found", resStatusEnum.VALIDATION_ERROR) + } + + const healthFactor = await healthFactorResolver.defaultHealthFactor(position, web3) + + return responseLib.sendResponse(res, healthFactor, null, resStatusEnum.SUCCESS) + + } catch (error) { + return responseLib.sendResponse(res, null, error, resStatusEnum.INTERNAL_SERVER_ERROR) + } +} async function calculateHealthFactor(req, res) { try { let web3 = await web3Lib.getWebSocketWeb3Instance(process.env.ETH_NODE_WS_URL); let {address} = req.params let { - usdtAmount, - usdtPrice, - wbtcAmount, - wbtcPrice, - wethAmount, - wethPrice, - yfiAmount, - yfiPrice, - zrxAmount, - zrxPrice, - uniAmount, - uniPrice, - aaveAmount, - aavePrice, - batAmount, - batPrice, - busdAmount, - busdPrice, - daiAmount, - daiPrice, - enjAmount, - enjPrice, - kncAmount, - kncPrice, - linkAmount, - linkPrice, - manaAmount, - manaPrice, - mkrAmount, - mkrPrice, - renAmount, - renPrice, - snxAmount, - snxPrice, - susdAmount, - susdPrice, - tusdAmount, - tusdPrice, - usdcAmount, - usdcPrice, - crvAmount, - crvPrice, - balAmount, - balPrice, - xsushiAmount, - xsushiPrice, - renfilAmount, - renfilPrice, - raiAmount, - raiPrice, - amplAmount, - amplPrice, - usdpAmount, - usdpPrice, - dpiAmount, - dpiPrice, - fraxAmount, - fraxPrice, - feiAmount, - feiPrice, - stethAmount, - stethPrice, - ensAmount, - ensPrice, - gusdAmount, - gusdPrice, - ustAmount, - ustPrice, - cvxAmount, - cvxPrice, - oneinchAmount, - oneinchPrice, - lusdAmount, - lusdPrice + collateralAssets = [], + collateralAmounts = [], + collateralPrices = [], + debtAssets = [], + debtAmounts = [], + debtPrices = [], + liquidationThresholds = [] } = req.body if (validatorUtil.isEmpty(address)) { @@ -102,16 +60,7 @@ async function calculateHealthFactor(req, res) { return responseLib.sendResponse(res, null, "No position found", resStatusEnum.VALIDATION_ERROR) } - const healthFactor = await healthFactorResolver.calculateHealthFactor(position, web3, usdtAmount, - usdtPrice, wbtcAmount, wbtcPrice, wethAmount, wethPrice, yfiAmount, yfiPrice, zrxAmount, zrxPrice, uniAmount, - uniPrice, aaveAmount, aavePrice, batAmount, batPrice, busdAmount, busdPrice, daiAmount, daiPrice, - enjAmount, enjPrice, kncAmount, kncPrice, linkAmount, linkPrice, manaAmount, manaPrice, mkrAmount, - mkrPrice, renAmount, renPrice, snxAmount, snxPrice, susdAmount, susdPrice, tusdAmount, tusdPrice, - usdcAmount, usdcPrice, crvAmount, crvPrice, balAmount, balPrice, xsushiAmount, xsushiPrice, renfilAmount, - renfilPrice, raiAmount, raiPrice, amplAmount, amplPrice, usdpAmount, usdpPrice, dpiAmount, dpiPrice, - fraxAmount, fraxPrice, feiAmount, feiPrice, stethAmount, stethPrice, ensAmount, ensPrice, gusdAmount, - gusdPrice, ustAmount, ustPrice, cvxAmount, cvxPrice, oneinchAmount, oneinchPrice, lusdAmount, lusdPrice - ) + const healthFactor = await healthFactorResolver.calculateHealthFactor(position, web3, collateralAssets, collateralAmounts, collateralPrices, debtAssets, debtAmounts, debtPrices, liquidationThresholds) return responseLib.sendResponse(res, healthFactor, null, resStatusEnum.SUCCESS) @@ -122,5 +71,6 @@ async function calculateHealthFactor(req, res) { } module.exports = { - calculateHealthFactor: calculateHealthFactor + calculateHealthFactor: calculateHealthFactor, + defaultHealthFactor: defaultHealthFactor } \ No newline at end of file diff --git a/backend/api/resolvers/health.factor.resolver.js b/backend/api/resolvers/health.factor.resolver.js index ac0a6c6..9215a7f 100644 --- a/backend/api/resolvers/health.factor.resolver.js +++ b/backend/api/resolvers/health.factor.resolver.js @@ -1,10 +1,10 @@ const mongoLib = require("../../lib/mongo.lib") const AAVE_V2_PROTOCOL_DATA_PROVIDER_ABI = require("../../abi/aave.v2.protocol.data.provider.abi.json") const aaveV2ProtocolDataProviderContract = "0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d" +const priceLib = require("../../lib/price.lib") -async function calculateHealthFactor(position, web3, usdtAmount, usdtPrice, wbtcAmount, wbtcPrice, wethAmount, wethPrice, yfiAmount, yfiPrice, zrxAmount, zrxPrice, uniAmount, uniPrice, aaveAmount, aavePrice, batAmount, batPrice, busdAmount, busdPrice, daiAmount, daiPrice, enjAmount, enjPrice, kncAmount, kncPrice, linkAmount, linkPrice, manaAmount, manaPrice, mkrAmount, mkrPrice, renAmount, renPrice, snxAmount, snxPrice, susdAmount, susdPrice, tusdAmount, tusdPrice, usdcAmount, usdcPrice, crvAmount, crvPrice, balAmount, balPrice, xsushiAmount, xsushiPrice, renfilAmount, renfilPrice, raiAmount, raiPrice, amplAmount, amplPrice, usdpAmount, usdpPrice, dpiAmount, dpiPrice, fraxAmount, fraxPrice, feiAmount, feiPrice, stethAmount, stethPrice, ensAmount, ensPrice, gusdAmount, gusdPrice, ustAmount, ustPrice, cvxAmount, cvxPrice, oneinchAmount, oneinchPrice, lusdAmount, lusdPrice) { +async function calculateHealthFactor(position, web3, collateralAssets, collateralAmounts, collateralPrices, debtAssets, debtAmounts, debtPrices, liquidationThresholds) { try { - const aaveV2ProtocolDataProvider = new web3.eth.Contract(AAVE_V2_PROTOCOL_DATA_PROVIDER_ABI, aaveV2ProtocolDataProviderContract) const liqudationThresholdForTusd = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x0000000000085d4780B73119b644AE5ecd22b376").call()).liquidationThreshold) / 10000 @@ -37,7 +37,6 @@ async function calculateHealthFactor(position, web3, usdtAmount, usdtPrice, wbtc const liqudationThresholdForSnx = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f").call()).liquidationThreshold) / 10000 - // let supplied = position.metadata.supplied.map((supply) => { // let balance = supply.balance; // let liqudationThreshold = supply.liqudationThreshold; @@ -62,6 +61,24 @@ async function calculateHealthFactor(position, web3, usdtAmount, usdtPrice, wbtc } } +async function defaultHealthFactor(position, web3) { + try { + const aaveV2ProtocolDataProvider = new web3.eth.Contract(AAVE_V2_PROTOCOL_DATA_PROVIDER_ABI, aaveV2ProtocolDataProviderContract) + + for (const supply of position.metadata.supplied) { + const underlying = supply.underlying + const balance = supply.balance + const price = supply.price + const ethPrice = await priceLib.getPriceFromAavePriceOracle([underlying], web3) + const liqudationThreshold = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData(underlying).call()).liquidationThreshold) / 10000 + + } + + } catch (error) { + throw error + } +} + module.exports = { calculateHealthFactor: calculateHealthFactor } diff --git a/backend/api/routes/health.factor.route.js b/backend/api/routes/health.factor.route.js index 3452a9b..591975d 100644 --- a/backend/api/routes/health.factor.route.js +++ b/backend/api/routes/health.factor.route.js @@ -2,6 +2,8 @@ const express = require("express") const router = express.Router() const healthFactorController = require("../controllers/health.factor.controller") -router.route("/:address").get(healthFactorController.calculateHealthFactor) +router.route("/:address").get(healthFactorController.defaultHealthFactor) + +router.route("/calculate/:address").get(healthFactorController.calculateHealthFactor) module.exports = router From 3a7ea4d1bb1a76563840450119de6b7049691a3f Mon Sep 17 00:00:00 2001 From: cd-sigma Date: Sun, 10 Dec 2023 04:54:30 +0530 Subject: [PATCH 5/5] default healht factor --- .../controllers/health.factor.controller.js | 8 +- backend/api/controllers/token.controller.js | 5 +- .../api/resolvers/health.factor.resolver.js | 85 +++++++++++-------- backend/lib/price.lib.js | 13 ++- 4 files changed, 64 insertions(+), 47 deletions(-) diff --git a/backend/api/controllers/health.factor.controller.js b/backend/api/controllers/health.factor.controller.js index 86ff9c8..38f8f6f 100644 --- a/backend/api/controllers/health.factor.controller.js +++ b/backend/api/controllers/health.factor.controller.js @@ -54,13 +54,7 @@ async function calculateHealthFactor(req, res) { address = address.toLowerCase() - const position = await mongoLib.findOneByQuery(positionModel, {owner: address}) - - if (validatorUtil.isEmpty(position)) { - return responseLib.sendResponse(res, null, "No position found", resStatusEnum.VALIDATION_ERROR) - } - - const healthFactor = await healthFactorResolver.calculateHealthFactor(position, web3, collateralAssets, collateralAmounts, collateralPrices, debtAssets, debtAmounts, debtPrices, liquidationThresholds) + const healthFactor = await healthFactorResolver.calculateHealthFactor(web3, collateralAssets, collateralAmounts, collateralPrices, debtAssets, debtAmounts, debtPrices, liquidationThresholds) return responseLib.sendResponse(res, healthFactor, null, resStatusEnum.SUCCESS) diff --git a/backend/api/controllers/token.controller.js b/backend/api/controllers/token.controller.js index 4d34c7b..57af97a 100644 --- a/backend/api/controllers/token.controller.js +++ b/backend/api/controllers/token.controller.js @@ -23,9 +23,8 @@ async function getTokenInfo(req, res) { try { tokenPrice = (await priceLib.getTokenPriceFromChainlinkPriceOracle("eth", tokenAddress, web3)).usdPrice } catch (error) { - const usdPrice = await priceLib.getPriceFromAavePriceOracle([tokenAddress], web3) - const decimals = await priceLib.getDecimalsForAsset(tokenAddress, web3) - const ethAmount = parseFloat(usdPrice[0]) / 10 ** decimals + const ethPrice = await priceLib.getPriceFromAavePriceOracle([tokenAddress], web3) + const ethAmount = parseFloat(ethPrice[0]) / 10 ** 18 tokenPrice = await priceLib.convertEthAmountToUsd(ethAmount, web3) } diff --git a/backend/api/resolvers/health.factor.resolver.js b/backend/api/resolvers/health.factor.resolver.js index 9215a7f..73e7074 100644 --- a/backend/api/resolvers/health.factor.resolver.js +++ b/backend/api/resolvers/health.factor.resolver.js @@ -3,38 +3,45 @@ const AAVE_V2_PROTOCOL_DATA_PROVIDER_ABI = require("../../abi/aave.v2.protocol.d const aaveV2ProtocolDataProviderContract = "0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d" const priceLib = require("../../lib/price.lib") -async function calculateHealthFactor(position, web3, collateralAssets, collateralAmounts, collateralPrices, debtAssets, debtAmounts, debtPrices, liquidationThresholds) { +async function calculateHealthFactor(web3, collateralAssets, collateralAmounts, collateralPrices, debtAssets, debtAmounts, debtPrices, liquidationThresholds) { try { const aaveV2ProtocolDataProvider = new web3.eth.Contract(AAVE_V2_PROTOCOL_DATA_PROVIDER_ABI, aaveV2ProtocolDataProviderContract) - const liqudationThresholdForTusd = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x0000000000085d4780B73119b644AE5ecd22b376").call()).liquidationThreshold) / 10000 - const liqudationThresholdForUsdc = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").call()).liquidationThreshold) / 10000 - const liqudationThresholdForCrv = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xd533a949740bb3306d119cc777fa900ba034cd52").call()).liquidationThreshold) / 10000 - const liqudationThresholdForBal = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xba100000625a3754423978a60c9317c58a424e3d").call()).liquidationThreshold) / 10000 - const liqudationThresholdForXsushi = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x8798249c2E607446EfB7Ad49eC89dD1865Ff4272").call()).liquidationThreshold) / 10000 - const liqudationThresholdForUsdp = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x1456688345527be1f37e9e627da0837d6f08c925").call()).liquidationThreshold) / 10000 - const liqudationThresholdForDpi = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x1494ca1f11d487c2bbe4543e90080aeba4ba3c2b").call()).liquidationThreshold) / 10000 - const liqudationThresholdForFei = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x956f47f50a910163d8bf957cf5846d573e7f87ca").call()).liquidationThreshold) / 10000 - const liqudationThresholdForSteth = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xae7ab96520de3a18e5e111b5eaab095312d7fe84").call()).liquidationThreshold) / 10000 - const liqudationThresholdForEns = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x57f8160e1c59d4346abb5cdefac810967e14e04f").call()).liquidationThreshold) / 10000 - const liqudationThresholdForCvx = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x4e3fbd56cd56c3e72c1403e103b45db9da5b9d2b").call()).liquidationThreshold) / 10000 - const liqudationThresholdForOneinch = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x111111111117dc0aa78b770fa6a738034120c302").call()).liquidationThreshold) / 10000 - const liqudationThresholdForLusd = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x5f98805A4E8be255a32880FDeC7F6728C6568bA0").call()).liquidationThreshold) / 10000 - const liqudationThresholdForWbtc = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").call()).liquidationThreshold) / 10000 - const liqudationThresholdForWeth = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").call()).liquidationThreshold) / 10000 - const liqudationThresholdForYfi = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e").call()).liquidationThreshold) / 10000 - const liqudationThresholdForZrx = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xe41d2489571d322189246dafa5ebde1f4699f498").call()).liquidationThreshold) / 10000 - const liqudationThresholdForUni = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x1f9840a85d5af5bf1d1762f925bdaddc4201f984").call()).liquidationThreshold) / 10000 - const liqudationThresholdForAave = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9").call()).liquidationThreshold) / 10000 - const liqudationThresholdForBat = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x0d8775f648430679a709e98d2b0cb6250d2887ef").call()).liquidationThreshold) / 10000 - const liqudationThresholdForDai = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x6b175474e89094c44da98b954eedeac495271d0f").call()).liquidationThreshold) / 10000 - const liqudationThresholdForEnj = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xf629cbd94d3791c9250152bd8dfbdf380e2a3b9c").call()).liquidationThreshold) / 10000 - const liqudationThresholdForKnc = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xdd974d5c2e2928dea5f71b9825b8b646686bd200").call()).liquidationThreshold) / 10000 - const liqudationThresholdForLink = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x514910771af9ca656af840dff83e8264ecf986ca").call()).liquidationThreshold) / 10000 - const liqudationThresholdForMana = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x0f5d2fb29fb7d3cfee444a200298f468908cc942").call()).liquidationThreshold) / 10000 - const liqudationThresholdForMkr = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2").call()).liquidationThreshold) / 10000 - const liqudationThresholdForRen = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x408e41876cccdc0f92210600ef50372656052a38").call()).liquidationThreshold) / 10000 - const liqudationThresholdForSnx = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f").call()).liquidationThreshold) / 10000 + for (const cAsset of collateralAssets) { + const price = collateralPrices[cAsset] + const amount = collateralAmounts[cAsset] + const liquidationThreshold = liquidationThresholds[cAsset] + const priceInEth = priceLib.convertUsdAmountToEth(price * amount, web3) + } + + // const liqudationThresholdForTusd = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x0000000000085d4780B73119b644AE5ecd22b376").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForUsdc = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForCrv = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xd533a949740bb3306d119cc777fa900ba034cd52").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForBal = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xba100000625a3754423978a60c9317c58a424e3d").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForXsushi = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x8798249c2E607446EfB7Ad49eC89dD1865Ff4272").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForUsdp = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x1456688345527be1f37e9e627da0837d6f08c925").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForDpi = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x1494ca1f11d487c2bbe4543e90080aeba4ba3c2b").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForFei = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x956f47f50a910163d8bf957cf5846d573e7f87ca").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForSteth = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xae7ab96520de3a18e5e111b5eaab095312d7fe84").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForEns = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x57f8160e1c59d4346abb5cdefac810967e14e04f").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForCvx = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x4e3fbd56cd56c3e72c1403e103b45db9da5b9d2b").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForOneinch = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x111111111117dc0aa78b770fa6a738034120c302").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForLusd = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x5f98805A4E8be255a32880FDeC7F6728C6568bA0").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForWbtc = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForWeth = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForYfi = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForZrx = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xe41d2489571d322189246dafa5ebde1f4699f498").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForUni = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x1f9840a85d5af5bf1d1762f925bdaddc4201f984").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForAave = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForBat = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x0d8775f648430679a709e98d2b0cb6250d2887ef").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForDai = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x6b175474e89094c44da98b954eedeac495271d0f").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForEnj = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xf629cbd94d3791c9250152bd8dfbdf380e2a3b9c").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForKnc = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xdd974d5c2e2928dea5f71b9825b8b646686bd200").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForLink = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x514910771af9ca656af840dff83e8264ecf986ca").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForMana = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x0f5d2fb29fb7d3cfee444a200298f468908cc942").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForMkr = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForRen = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0x408e41876cccdc0f92210600ef50372656052a38").call()).liquidationThreshold) / 10000 + // const liqudationThresholdForSnx = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData("0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f").call()).liquidationThreshold) / 10000 // let supplied = position.metadata.supplied.map((supply) => { @@ -65,22 +72,28 @@ async function defaultHealthFactor(position, web3) { try { const aaveV2ProtocolDataProvider = new web3.eth.Contract(AAVE_V2_PROTOCOL_DATA_PROVIDER_ABI, aaveV2ProtocolDataProviderContract) - for (const supply of position.metadata.supplied) { - const underlying = supply.underlying - const balance = supply.balance - const price = supply.price - const ethPrice = await priceLib.getPriceFromAavePriceOracle([underlying], web3) - const liqudationThreshold = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData(underlying).call()).liquidationThreshold) / 10000 + for (const borrow of position.metadata.borrowed) { + const price = await priceLib.getPriceFromAavePriceOracle([borrow.underlying], web3) + borrow.price = price[0] / 10 ** 18 + borrow.usdPrice = await priceLib.convertEthAmountToUsd(borrow.price, web3) + borrow.liqudationThreshold = 0 + } + for (const supply of position.metadata.supplied) { + const price = await priceLib.getPriceFromAavePriceOracle([supply.underlying], web3) + supply.price = price[0] / 10 ** 18 + supply.usdPrice = await priceLib.convertEthAmountToUsd(supply.price, web3) + supply.liqudationThreshold = parseFloat((await aaveV2ProtocolDataProvider.methods.getReserveConfigurationData(supply.underlying).call()).liquidationThreshold) / 10000 } + return position } catch (error) { throw error } } module.exports = { - calculateHealthFactor: calculateHealthFactor + calculateHealthFactor: calculateHealthFactor, defaultHealthFactor: defaultHealthFactor } // { diff --git a/backend/lib/price.lib.js b/backend/lib/price.lib.js index 0a56cc2..e058a48 100644 --- a/backend/lib/price.lib.js +++ b/backend/lib/price.lib.js @@ -153,6 +153,16 @@ async function convertEthAmountToUsd(ethAmount, web3) { } } +async function convertUsdAmountToEth(usdAmount, web3) { + try { + let ethPrice = await getNativePriceFromChainlinkPriceOracle("eth", web3); + let ethAmount = usdAmount / ethPrice.usdPrice; + return ethAmount + } catch (error) { + throw error + } +} + async function getDecimalsForAsset(address, web3) { try { const decimalContract = new web3.eth.Contract(decimalAbi, address); @@ -169,5 +179,6 @@ module.exports = { getTokenPriceFromChainlinkPriceOracle: getTokenPriceFromChainlinkPriceOracle, getNativePriceFromChainlinkPriceOracle: getNativePriceFromChainlinkPriceOracle, convertEthAmountToUsd: convertEthAmountToUsd, - getDecimalsForAsset: getDecimalsForAsset + getDecimalsForAsset: getDecimalsForAsset, + convertUsdAmountToEth: convertUsdAmountToEth } \ No newline at end of file