mirror of
				https://github.com/Instadapp/assembly.git
				synced 2024-07-29 22:37:06 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			562 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			562 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { AbiItem } from "web3-utils";
 | |
| 
 | |
| import aaveV2ABI from "~/abis/read/aaveV2.json";
 | |
| import { computed, ref, watch } from "@nuxtjs/composition-api";
 | |
| import { useDSA } from "~/composables/useDSA";
 | |
| import { useWeb3 } from "@instadapp/vue-web3";
 | |
| import BigNumber from "bignumber.js";
 | |
| import atokensV2 from "~/constant/atokensV2";
 | |
| import tokens from "~/constant/tokens";
 | |
| import { Network, useNetwork } from "~/composables/useNetwork";
 | |
| import { useBigNumber } from "~/composables/useBigNumber";
 | |
| import { usePosition } from "~/composables/usePosition";
 | |
| import { useToken } from "~/composables/useToken";
 | |
| import { useSorting } from "~/composables/useSorting";
 | |
| import useEventBus from "../useEventBus";
 | |
| import addresses from "~/constant/addresses";
 | |
| 
 | |
| const {
 | |
|   times,
 | |
|   isZero,
 | |
|   div,
 | |
|   max,
 | |
|   gt,
 | |
|   minus,
 | |
|   ensureValue,
 | |
|   plus
 | |
| } = useBigNumber();
 | |
| const { getType } = usePosition();
 | |
| 
 | |
| export const position = ref<any>({
 | |
|   totalSupplyInEth: new BigNumber(0),
 | |
|   totalBorrowInEth: new BigNumber(0),
 | |
|   totalBorrowStableInEth: new BigNumber(0),
 | |
|   totalBorrowVariableInEth: new BigNumber(0),
 | |
|   maxBorrowLimitInEth: new BigNumber(0),
 | |
|   maxBorrowLiquidityLimitInEth: new BigNumber(0),
 | |
|   ethPriceInUsd: "0",
 | |
|   data: []
 | |
| });
 | |
| const totalSupply = computed(() =>
 | |
|   position.value
 | |
|     ? times(
 | |
|         position.value.totalSupplyInEth,
 | |
|         position.value.ethPriceInUsd
 | |
|       ).toFixed()
 | |
|     : 0
 | |
| );
 | |
| 
 | |
| const totalBorrow = computed(() =>
 | |
|   position.value
 | |
|     ? times(
 | |
|         position.value.totalBorrowInEth,
 | |
|         position.value.ethPriceInUsd
 | |
|       ).toFixed()
 | |
|     : 0
 | |
| );
 | |
| 
 | |
| const ethPriceInUsd = computed(() => position.value?.ethPriceInUsd);
 | |
| 
 | |
| const annualPercentageRateTypes = computed(() => [
 | |
|   { label: "Variable", value: "variable", rateMode: 2 },
 | |
|   { label: "Stable", value: "stable", rateMode: 1 }
 | |
| ]);
 | |
| 
 | |
| export function useAaveV2Position(
 | |
|   { overridePosition } = { overridePosition: null }
 | |
| ) {
 | |
|   overridePosition = overridePosition || (pos => pos);
 | |
| 
 | |
|   const { library, chainId } = useWeb3();
 | |
|   const { activeNetworkId } = useNetwork();
 | |
|   const { activeAccount } = useDSA();
 | |
|   const { getTokenByKey, allATokensV2 } = useToken();
 | |
|   const { byMaxSupplyOrBorrowDesc } = useSorting();
 | |
|   const { onEvent } = useEventBus();
 | |
| 
 | |
|   const resolver = computed(
 | |
|     () =>
 | |
|       // @ts-ignore
 | |
|       addresses[activeNetworkId.value]?.resolver?.aave_v2
 | |
|   );
 | |
| 
 | |
|   const fetchPosition = async () => {
 | |
|     if (!library.value) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     if (!activeAccount.value) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     const aaveResolverInstance = new library.value.eth.Contract(
 | |
|       aaveV2ABI as AbiItem[],
 | |
|       resolver.value
 | |
|     );
 | |
| 
 | |
|     const aaveTokensArr = atokensV2[activeNetworkId.value].allTokens.map(
 | |
|       a => tokens[activeNetworkId.value].getTokenByKey(a.root).address
 | |
|     );
 | |
| 
 | |
|     console.log(activeAccount.value.address, aaveTokensArr);
 | |
| 
 | |
|     const aaveRawData = await aaveResolverInstance.methods
 | |
|       .getPosition(activeAccount.value.address, aaveTokensArr)
 | |
|       .call();
 | |
| 
 | |
|     const newPos = calculateAavePosition(aaveRawData, activeNetworkId.value);
 | |
| 
 | |
|     return newPos;
 | |
|   };
 | |
| 
 | |
|   const refreshPosition = async () => {
 | |
|     position.value = await fetchPosition();
 | |
|   };
 | |
| 
 | |
|   onEvent("protocol::aaveV2::refresh", refreshPosition);
 | |
| 
 | |
|   watch(
 | |
|     library,
 | |
|     async val => {
 | |
|       if (val) {
 | |
|         refreshPosition();
 | |
|       }
 | |
|     },
 | |
|     { immediate: true }
 | |
|   );
 | |
| 
 | |
|   watch(
 | |
|     activeAccount,
 | |
|     async val => {
 | |
|       if (val) {
 | |
|         refreshPosition();
 | |
|       }
 | |
|     },
 | |
|     { immediate: true }
 | |
|   );
 | |
| 
 | |
|   const stats = computed(() =>
 | |
|     displayPositions.value.reduce(
 | |
|       (stats, { key, supply, borrow, priceInEth, factor, liquidation }) => {
 | |
|         if (key === "eth") {
 | |
|           stats.ethSupplied = supply;
 | |
|         }
 | |
| 
 | |
|         stats.totalSupplyInEth = plus(
 | |
|           stats.totalSupplyInEth,
 | |
|           times(supply, priceInEth)
 | |
|         ).toFixed();
 | |
|         stats.totalBorrowInEth = plus(
 | |
|           stats.totalBorrowInEth,
 | |
|           times(borrow, priceInEth)
 | |
|         ).toFixed();
 | |
|         stats.totalMaxBorrowLimitInEth = plus(
 | |
|           stats.totalMaxBorrowLimitInEth,
 | |
|           times(supply, times(priceInEth, factor))
 | |
|         ).toFixed();
 | |
|         stats.totalMaxLiquidationLimitInEth = plus(
 | |
|           stats.totalMaxLiquidationLimitInEth,
 | |
|           times(supply, times(priceInEth, liquidation))
 | |
|         ).toFixed();
 | |
| 
 | |
|         return stats;
 | |
|       },
 | |
|       {
 | |
|         totalSupplyInEth: "0",
 | |
|         totalBorrowInEth: "0",
 | |
|         totalMaxBorrowLimitInEth: "0",
 | |
|         totalMaxLiquidationLimitInEth: "0",
 | |
|         ethSupplied: "0"
 | |
|       }
 | |
|     )
 | |
|   );
 | |
| 
 | |
|   const rewardTokenPriceInUsd = computed(() => {
 | |
|     if (activeNetworkId.value === Network.Polygon) {
 | |
|       return ensureValue(
 | |
|         position.value.data.find(position => position.key === "matic")
 | |
|           ?.priceInUsd
 | |
|       );
 | |
|     }
 | |
|     return ensureValue(
 | |
|       position.value.data.find(position => position.key === "aave")?.priceInUsd
 | |
|     );
 | |
|   });
 | |
| 
 | |
|   const displayPositions = computed(() => {
 | |
|     if (!position.value) {
 | |
|       return [];
 | |
|     }
 | |
| 
 | |
|     return allATokensV2.value
 | |
|       .flatMap(atoken => {
 | |
|         const token = getTokenByKey(atoken.root);
 | |
| 
 | |
|         const atokenPosition = position.value.data.find(
 | |
|           x => x.key === atoken.root
 | |
|         );
 | |
| 
 | |
|         const p = getPositionOrDefaultPosition(token, atokenPosition);
 | |
| 
 | |
|         if (gt(p.supply, "0") && gt(p.borrow, "0")) {
 | |
|           return [
 | |
|             { ...p, type: "supply" },
 | |
|             { ...p, type: "borrow" }
 | |
|           ];
 | |
|         } else {
 | |
|           return [p];
 | |
|         }
 | |
|       })
 | |
|       .sort((a, b) =>
 | |
|         minus(
 | |
|           max(b.supplyUsd, b.borrowUsd),
 | |
|           max(a.supplyUsd, a.borrowUsd)
 | |
|         ).toNumber()
 | |
|       )
 | |
|       .map(overridePosition)
 | |
|       .sort(byMaxSupplyOrBorrowDesc);
 | |
|   });
 | |
| 
 | |
|   function getPositionOrDefaultPosition(token, position) {
 | |
|     if (!position) {
 | |
|       const defaultPosition = {
 | |
|         key: token.key,
 | |
|         aTokenKey: "",
 | |
|         aTokenBal: "0",
 | |
|         aDecimals: "0",
 | |
|         cf: "0",
 | |
|         ll: "0",
 | |
|         supply: "0",
 | |
|         supplyUsd: "0",
 | |
|         supplyRate: "0",
 | |
|         borrow: "0",
 | |
|         borrowUsd: "0",
 | |
|         borrowRate: "0",
 | |
|         type: "no",
 | |
|         isEnabledAsCollateral: true,
 | |
|         borrowEnabled: true,
 | |
|         availableLiquidity: "0",
 | |
|         stableBorrowEnabled: true,
 | |
|         borrowStable: "0",
 | |
|         borrowStableRate: "0",
 | |
|         supplyRewardRate: "0",
 | |
|         borrowRewardRate: "0"
 | |
|       };
 | |
| 
 | |
|       return defaultPosition;
 | |
|     }
 | |
| 
 | |
|     return {
 | |
|       key: token.key,
 | |
|       aTokenKey: position.aTokenKey,
 | |
|       aTokenBal: position.aTokenBal,
 | |
|       aDecimals: position.aDecimals,
 | |
|       cf: position.factor,
 | |
|       ll: position.liquidation,
 | |
|       factor: position.factor,
 | |
|       liquidation: position.liquidation,
 | |
|       supply: position.supply,
 | |
|       supplyUsd: times(position.supply, position.priceInUsd).toFixed(),
 | |
|       supplyRate: position.supplyRate,
 | |
|       borrow: position.borrow,
 | |
|       borrowUsd: times(position.borrow, position.priceInUsd).toFixed(),
 | |
|       borrowRate: position.borrowRate,
 | |
|       priceInEth: position.priceInEth,
 | |
|       type: getType(position),
 | |
|       isEnabledAsCollateral: position.isEnabledAsCollateral,
 | |
|       borrowEnabled: position.borrowEnabled,
 | |
|       availableLiquidity: position.availableLiquidity,
 | |
|       borrowStableUsd: times(
 | |
|         position.borrowStable,
 | |
|         position.priceInUsd
 | |
|       ).toFixed(),
 | |
|       stableBorrowEnabled: position.stableBorrowEnabled,
 | |
|       borrowStable: position.borrowStable,
 | |
|       borrowStableRate: position.borrowStableRate,
 | |
|       priceInUsd: position.priceInUsd,
 | |
|       supplyRewardRate: times(
 | |
|         position.supplyRewardRate,
 | |
|         rewardTokenPriceInUsd.value
 | |
|       ).toFixed(),
 | |
|       borrowRewardRate: times(
 | |
|         position.borrowRewardRate,
 | |
|         rewardTokenPriceInUsd.value
 | |
|       ).toFixed()
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   const maxLiquidation = computed(() => {
 | |
|     if (isZero(stats.value.totalSupplyInEth)) return "0";
 | |
| 
 | |
|     return max(
 | |
|       div(
 | |
|         stats.value.totalMaxLiquidationLimitInEth,
 | |
|         stats.value.totalSupplyInEth
 | |
|       ),
 | |
|       "0"
 | |
|     ).toFixed();
 | |
|   });
 | |
| 
 | |
|   const liquidationPrice = computed(() => {
 | |
|     if (isZero(stats.value.ethSupplied)) return "0";
 | |
| 
 | |
|     return max(
 | |
|       times(
 | |
|         div(
 | |
|           stats.value.totalBorrowInEth,
 | |
|           stats.value.totalMaxLiquidationLimitInEth
 | |
|         ),
 | |
|         ethPriceInUsd.value
 | |
|       ),
 | |
|       "0"
 | |
|     ).toFixed();
 | |
|   });
 | |
| 
 | |
|   const status = computed(() => {
 | |
|     if (
 | |
|       isZero(stats.value.totalSupplyInEth) &&
 | |
|       !isZero(stats.value.totalBorrowInEth)
 | |
|     )
 | |
|       return "1.1";
 | |
|     if (isZero(stats.value.totalSupplyInEth)) return "0";
 | |
| 
 | |
|     return max(
 | |
|       div(stats.value.totalBorrowInEth, stats.value.totalSupplyInEth),
 | |
|       "0"
 | |
|     ).toFixed();
 | |
|   });
 | |
| 
 | |
|   const liquidation = computed(() => {
 | |
|     if (isZero(stats.value.totalSupplyInEth)) return "0";
 | |
| 
 | |
|     return max(
 | |
|       div(stats.value.totalMaxBorrowLimitInEth, stats.value.totalSupplyInEth),
 | |
|       "0"
 | |
|     ).toFixed();
 | |
|   });
 | |
| 
 | |
|   return {
 | |
|     stats,
 | |
|     displayPositions,
 | |
|     position,
 | |
|     fetchPosition,
 | |
|     refreshPosition,
 | |
|     totalSupply,
 | |
|     totalBorrow,
 | |
|     status,
 | |
|     liquidation,
 | |
|     maxLiquidation,
 | |
|     liquidationPrice,
 | |
|     liquidationMaxPrice: ethPriceInUsd,
 | |
|     annualPercentageRateTypes
 | |
|   };
 | |
| }
 | |
| 
 | |
| function calculateAavePosition(res: any[], network: Network = Network.Mainnet) {
 | |
|   try {
 | |
|     const newPos = {
 | |
|       totalSupplyInEth: new BigNumber(0),
 | |
|       totalBorrowInEth: new BigNumber(0),
 | |
|       totalBorrowStableInEth: new BigNumber(0),
 | |
|       totalBorrowVariableInEth: new BigNumber(0),
 | |
|       maxBorrowLimitInEth: new BigNumber(0),
 | |
|       maxBorrowLiquidityLimitInEth: new BigNumber(0)
 | |
|       // tokens: {},
 | |
|     };
 | |
|     const dataPos = [];
 | |
|     atokensV2[network].allTokens.forEach((atoken, i) => {
 | |
|       const key = atoken.address;
 | |
| 
 | |
|       /* eslint-disable no-unused-vars */
 | |
|       const [
 | |
|         priceInEthInWei,
 | |
|         priceInUsdInWei,
 | |
|         supplyBalanceInWei,
 | |
|         borrowStableBalanceInWei,
 | |
|         borrowVariableBalanceInWei,
 | |
|         supplyRatePerBlock,
 | |
|         borrowStableRatePerBlock,
 | |
|         userStableBorrowRatePerBlock,
 | |
|         borrowVariableRatePerBlock,
 | |
|         isCollateralEnabled,
 | |
|         AaveTokenData
 | |
|       ] = res[0][i];
 | |
| 
 | |
|       const [
 | |
|         ltv,
 | |
|         threshold,
 | |
|         reserveFactor,
 | |
|         usageAsCollEnabled,
 | |
|         borrowEnabled,
 | |
|         stableBorrowEnabled,
 | |
|         isActive,
 | |
|         isFrozen,
 | |
|         totalSupply,
 | |
|         availableLiquidity,
 | |
|         totalStableDebt,
 | |
|         totalVariableDebt,
 | |
|         collateralEmission,
 | |
|         debtEmission,
 | |
|         aTokenAddress,
 | |
|         stableDebtTokenAddress,
 | |
|         variableDebtTokenAddress
 | |
|       ] = AaveTokenData;
 | |
|       /* eslint-enable no-unused-vars */
 | |
| 
 | |
|       const decimals = tokens[network].getTokenByKey(atoken.root).decimals;
 | |
|       const root = atoken.root;
 | |
|       const factor = new BigNumber(ltv).dividedBy(1e4);
 | |
|       const priceInEth = new BigNumber(priceInEthInWei).dividedBy(1e18);
 | |
|       const priceInUsd = new BigNumber(priceInUsdInWei)
 | |
|         .dividedBy(1e18)
 | |
|         .toFixed();
 | |
|       const supply = new BigNumber(supplyBalanceInWei).dividedBy(
 | |
|         10 ** decimals
 | |
|       );
 | |
|       const borrowStable = new BigNumber(borrowStableBalanceInWei).dividedBy(
 | |
|         10 ** decimals
 | |
|       );
 | |
|       const borrowVariable = new BigNumber(
 | |
|         borrowVariableBalanceInWei
 | |
|       ).dividedBy(10 ** decimals);
 | |
|       const supplyRate = new BigNumber(supplyRatePerBlock)
 | |
|         .dividedBy(1e27)
 | |
|         .toFixed();
 | |
|       const supplyYield = supplyRate;
 | |
|       const borrowStableRate = new BigNumber(borrowStableRatePerBlock)
 | |
|         .dividedBy(1e27)
 | |
|         .toFixed();
 | |
|       const userBorrowStableRate = new BigNumber(userStableBorrowRatePerBlock)
 | |
|         .dividedBy(1e27)
 | |
|         .toFixed();
 | |
|       const borrowVariableRate = new BigNumber(borrowVariableRatePerBlock)
 | |
|         .dividedBy(1e27)
 | |
|         .toFixed();
 | |
|       const borrowStableYield = borrowStableRate;
 | |
|       const borrowVariableYield = borrowVariableRate;
 | |
|       const liquidity = new BigNumber(availableLiquidity)
 | |
|         .dividedBy(10 ** decimals)
 | |
|         .multipliedBy(0.9999)
 | |
|         .toFixed();
 | |
|       const totalSupplied = new BigNumber(totalSupply)
 | |
|         .dividedBy(10 ** decimals)
 | |
|         .multipliedBy(priceInUsd);
 | |
|       const totalDebt = new BigNumber(totalVariableDebt)
 | |
|         .dividedBy(10 ** decimals)
 | |
|         .multipliedBy(priceInUsd);
 | |
|       const supplyRewardRate = new BigNumber(collateralEmission)
 | |
|         .multipliedBy(31536000)
 | |
|         .dividedBy(1e18)
 | |
|         .dividedBy(totalSupplied)
 | |
|         .toFixed();
 | |
|       const borrowRewardRate = new BigNumber(debtEmission)
 | |
|         .multipliedBy(31536000)
 | |
|         .dividedBy(1e18)
 | |
|         .dividedBy(totalDebt)
 | |
|         .toFixed();
 | |
| 
 | |
|       newPos.totalSupplyInEth = newPos.totalSupplyInEth.plus(
 | |
|         supply.multipliedBy(priceInEth)
 | |
|       );
 | |
|       newPos.maxBorrowLimitInEth = newPos.maxBorrowLimitInEth.plus(
 | |
|         supply.multipliedBy(priceInEth).multipliedBy(factor)
 | |
|       );
 | |
|       newPos.maxBorrowLiquidityLimitInEth = newPos.maxBorrowLiquidityLimitInEth.plus(
 | |
|         supply
 | |
|           .multipliedBy(priceInEth)
 | |
|           .multipliedBy((threshold / 10000).toString())
 | |
|       );
 | |
|       newPos.totalBorrowInEth = newPos.totalBorrowInEth.plus(
 | |
|         borrowStable.plus(borrowVariable).multipliedBy(priceInEth)
 | |
|       );
 | |
|       newPos.totalBorrowStableInEth = newPos.totalBorrowStableInEth.plus(
 | |
|         borrowStable.multipliedBy(priceInEth)
 | |
|       );
 | |
|       newPos.totalBorrowVariableInEth = newPos.totalBorrowVariableInEth.plus(
 | |
|         borrowVariable.multipliedBy(priceInEth)
 | |
|       );
 | |
| 
 | |
|       dataPos.push({
 | |
|         key: root,
 | |
|         aTokenAddr: key,
 | |
|         aTokenAddress,
 | |
|         stableDebtTokenAddress,
 | |
|         variableDebtTokenAddress,
 | |
|         aTokenBal: supplyBalanceInWei,
 | |
|         aTokenKey: atoken.key,
 | |
|         aDecimals: atoken.decimals.toString(),
 | |
|         priceInEth: priceInEth.toFixed(),
 | |
|         priceInUsd,
 | |
|         supply: supply.toFixed(),
 | |
|         borrowStable: borrowStable.toFixed(),
 | |
|         borrow: borrowVariable.toFixed(), // TODO: change later
 | |
|         supplyRate,
 | |
|         supplyYield,
 | |
|         borrowStableRate,
 | |
|         userBorrowStableRate,
 | |
|         borrowStableYield,
 | |
|         borrowRate: borrowVariableRate, // TODO: change later
 | |
|         borrowYield: borrowVariableYield, // TODO: change later
 | |
|         factor: factor.toFixed(),
 | |
|         liquidation: (threshold / 10000).toString(),
 | |
|         isEnabledAsCollateral: isCollateralEnabled,
 | |
|         borrowEnabled,
 | |
|         stableBorrowEnabled,
 | |
|         availableLiquidity: liquidity,
 | |
|         supplyRewardRate,
 | |
|         borrowRewardRate
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     const totalSupplyInEthIsZero = newPos.totalSupplyInEth.isZero();
 | |
|     const status = totalSupplyInEthIsZero
 | |
|       ? 0
 | |
|       : newPos.totalBorrowInEth.dividedBy(newPos.totalSupplyInEth).toFixed();
 | |
|     const liquidation = totalSupplyInEthIsZero
 | |
|       ? 0
 | |
|       : newPos.maxBorrowLimitInEth.dividedBy(newPos.totalSupplyInEth).toFixed();
 | |
|     const maxLiquidation = totalSupplyInEthIsZero
 | |
|       ? 0
 | |
|       : newPos.maxBorrowLiquidityLimitInEth
 | |
|           .dividedBy(newPos.totalSupplyInEth)
 | |
|           .toFixed();
 | |
|     const ethPrice = new BigNumber(res[1].ethPriceInUsd)
 | |
|       .dividedBy(1e8)
 | |
|       .toFixed();
 | |
|     const pendingRewards = new BigNumber(res[1].pendingRewards)
 | |
|       .dividedBy(1e18)
 | |
|       .toFixed();
 | |
| 
 | |
|     // @ts-ignore
 | |
|     newPos.totalSupplyInEth = newPos.totalSupplyInEth.toFixed();
 | |
|     // @ts-ignore
 | |
|     newPos.totalBorrowInEth = newPos.totalBorrowInEth.toFixed();
 | |
|     // @ts-ignore
 | |
|     newPos.maxBorrowLimitInEth = newPos.maxBorrowLimitInEth.toFixed();
 | |
|     // @ts-ignore
 | |
|     newPos.totalBorrowStableInEth = newPos.totalBorrowStableInEth.toFixed();
 | |
|     // @ts-ignore
 | |
|     newPos.maxBorrowLiquidityLimitInEth = newPos.maxBorrowLiquidityLimitInEth.toFixed();
 | |
|     // @ts-ignore
 | |
|     newPos.totalBorrowVariableInEth = newPos.totalBorrowVariableInEth.toFixed();
 | |
| 
 | |
|     // @ts-ignore
 | |
|     newPos.status = status;
 | |
|     // @ts-ignore
 | |
|     newPos.liquidation = liquidation;
 | |
|     // @ts-ignore
 | |
|     newPos.maxLiquidation = maxLiquidation;
 | |
|     // @ts-ignore
 | |
|     newPos.ethPriceInUsd = ethPrice;
 | |
|     // @ts-ignore
 | |
|     newPos.pendingRewards = pendingRewards;
 | |
|     // @ts-ignore
 | |
|     newPos.data = dataPos;
 | |
| 
 | |
|     return newPos;
 | |
|   } catch (error) {
 | |
|     console.error(error);
 | |
|     return Promise.reject(error);
 | |
|   }
 | |
| }
 | 
