mirror of
				https://github.com/Instadapp/assembly.git
				synced 2024-07-29 22:37:06 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			483 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			483 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { AbiItem } from "web3-utils";
 | |
| 
 | |
| import compoundABI from "~/abis/read/compound.json";
 | |
| import { computed, ref, watch } from "@nuxtjs/composition-api";
 | |
| import { useDSA } from "~/composables/useDSA";
 | |
| import { useWeb3 } from "~/composables/useWeb3";
 | |
| import BigNumber from "bignumber.js";
 | |
| import tokens from "~/constant/tokens";
 | |
| import { Network } from "~/composables/useNetwork";
 | |
| import { useBigNumber } from "~/composables/useBigNumber";
 | |
| import { usePosition } from "~/composables/usePosition";
 | |
| import { useToken } from "~/composables/useToken";
 | |
| import addresses from "~/constant/addresses";
 | |
| import ctokens from "~/constant/ctokens";
 | |
| import tokenIdMapping from "~/constant/tokenIdMapping";
 | |
| import { useSorting } from "~/composables/useSorting";
 | |
| 
 | |
| const {
 | |
|   times,
 | |
|   isZero,
 | |
|   div,
 | |
|   max,
 | |
|   gt,
 | |
|   minus,
 | |
|   ensureValue,
 | |
|   plus
 | |
| } = useBigNumber();
 | |
| const { getType } = usePosition();
 | |
| 
 | |
| 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);
 | |
| 
 | |
| export function useCompoundPosition(
 | |
|   { overridePosition } = { overridePosition: null }
 | |
| ) {
 | |
|   overridePosition = overridePosition || (pos => pos);
 | |
| 
 | |
|   const { web3, networkName } = useWeb3();
 | |
|   const { activeAccount } = useDSA();
 | |
|   const { getTokenByKey } = useToken();
 | |
|   const { byMaxSupplyOrBorrowDesc } = useSorting()
 | |
|   const resolver = computed(() => addresses.mainnet.resolver.compound);
 | |
| 
 | |
|   const fetchPosition = async () => {
 | |
|     if (!web3.value) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     if (!activeAccount.value) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     const resolverInstance = new web3.value.eth.Contract(
 | |
|       compoundABI as AbiItem[],
 | |
|       resolver.value
 | |
|     );
 | |
| 
 | |
|     const tokensArr = ctokens[networkName.value].allTokens.map(a => a.address);
 | |
| 
 | |
|     const compoundRawData = await resolverInstance.methods
 | |
|       .getPosition(activeAccount.value.address, tokensArr)
 | |
|       .call();
 | |
| 
 | |
|     const newPos = calculateCompoundPosition(
 | |
|       compoundRawData,
 | |
|       networkName.value
 | |
|     );
 | |
| 
 | |
|     return newPos;
 | |
|   };
 | |
| 
 | |
|   const refreshPosition = async () => {
 | |
|     position.value = await fetchPosition();
 | |
|   };
 | |
| 
 | |
|   watch(
 | |
|     web3,
 | |
|     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 }) => {
 | |
|         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();
 | |
| 
 | |
|         return stats;
 | |
|       },
 | |
|       {
 | |
|         totalSupplyInEth: "0",
 | |
|         totalBorrowInEth: "0",
 | |
|         totalMaxBorrowLimitInEth: "0",
 | |
|         ethSupplied: "0"
 | |
|       }
 | |
|     )
 | |
|   );
 | |
| 
 | |
|   const rewardTokenPriceInUsd = computed(() => {
 | |
|     return ensureValue(
 | |
|       position.value.data.find(position => position.key === "comp")?.priceInUsd
 | |
|     );
 | |
|   });
 | |
| 
 | |
|   const displayPositions = computed(() => {
 | |
|     if (!position.value) {
 | |
|       return [];
 | |
|     }
 | |
| 
 | |
|     return ctokens[networkName.value].allTokens
 | |
|       .flatMap(ctoken => {
 | |
|         const token = getTokenByKey(ctoken.root);
 | |
|         if (!token) {
 | |
|           return [];
 | |
|         }
 | |
| 
 | |
|         const ctokenPosition = overridePosition(
 | |
|           position.value.data.find(x => x.cTokenId === ctoken.id)
 | |
|         );
 | |
| 
 | |
|         if (!ctokenPosition) {
 | |
|           return [];
 | |
|         }
 | |
| 
 | |
|         const p = getPositionOrDefaultPosition(token, ctokenPosition);
 | |
|         if (gt(p.supply, "0") && gt(p.borrow, "0")) {
 | |
|           return [
 | |
|             { ...p, type: "supply" },
 | |
|             { ...p, type: "borrow" }
 | |
|           ];
 | |
|         } else {
 | |
|           return [p];
 | |
|         }
 | |
|       })
 | |
|       .filter(p => {
 | |
|         if (
 | |
|           tokenIdMapping.archived.compound.includes(p.tokenId) &&
 | |
|           isZero(p.supply) &&
 | |
|           isZero(p.borrow)
 | |
|         ) {
 | |
|           return false;
 | |
|         }
 | |
|         return true;
 | |
|       })
 | |
|       .sort(byMaxSupplyOrBorrowDesc);
 | |
|   });
 | |
| 
 | |
|   function getPositionOrDefaultPosition(token, pos) {
 | |
|     if (!pos) {
 | |
|       const defaultPosition = {
 | |
|         key: token?.key,
 | |
|         tokenId: `${token.symbol}-A`,
 | |
|         ctknBalance: "0",
 | |
|         cTokenDecimals: "0",
 | |
|         cf: "0",
 | |
|         supply: "0",
 | |
|         supplyUsd: "0",
 | |
|         supplyRate: "0",
 | |
|         supplyYield: "0",
 | |
|         borrow: "0",
 | |
|         borrowUsd: "0",
 | |
|         borrowRate: "0",
 | |
|         borrowYield: "0",
 | |
|         type: "no",
 | |
|         borrowEnabled: true,
 | |
|         supplyRewardRate: "0",
 | |
|         borrowRewardRate: "0",
 | |
|         priceInUsd: "0"
 | |
|       };
 | |
| 
 | |
|       return defaultPosition;
 | |
|     }
 | |
| 
 | |
|     return {
 | |
|       key: token?.key,
 | |
|       tokenId: pos.cTokenId,
 | |
|       ctknBalance: pos.ctknBalance,
 | |
|       cTokenDecimals: pos.cTokenDecimals,
 | |
|       cf: pos.factor,
 | |
|       supply: pos.supply,
 | |
|       supplyUsd: times(pos.supply, pos.priceInUsd).toFixed(),
 | |
|       supplyRate: pos.supplyRate,
 | |
|       supplyYield: pos.supplyYield,
 | |
|       borrow: pos.borrow,
 | |
|       borrowUsd: times(pos.borrow, pos.priceInUsd).toFixed(),
 | |
|       borrowRate: pos.borrowRate,
 | |
|       borrowYield: pos.borrowYield,
 | |
|       borrowEnabled: pos.borrowEnabled,
 | |
|       type: getType(pos),
 | |
|       supplyRewardRate: times(
 | |
|         pos.compSupplyApy,
 | |
|         rewardTokenPriceInUsd.value
 | |
|       ).toFixed(),
 | |
|       borrowRewardRate: times(
 | |
|         pos.compBorrowApy,
 | |
|         rewardTokenPriceInUsd.value
 | |
|       ).toFixed(),
 | |
|       priceInUsd: pos.priceInUsd,
 | |
|       priceInEth: pos.priceInEth,
 | |
|       factor: pos.factor,
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   const liquidationPrice = computed(() => {
 | |
|     if (isZero(stats.value.ethSupplied)) return "0";
 | |
| 
 | |
|     return max(
 | |
|       times(
 | |
|         div(
 | |
|           stats.value.totalBorrowInEth,
 | |
|           stats.value.totalMaxBorrowLimitInEth
 | |
|         ),
 | |
|         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: position,
 | |
|     fetchPosition,
 | |
|     refreshPosition,
 | |
|     totalSupply,
 | |
|     totalBorrow,
 | |
|     status,
 | |
|     liquidation,
 | |
|     liquidationPrice,
 | |
|     liquidationMaxPrice: ethPriceInUsd
 | |
|   };
 | |
| }
 | |
| 
 | |
| function calculateCompoundPosition(
 | |
|   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: {},
 | |
|       data: []
 | |
|     };
 | |
|     const dataPos = [];
 | |
|     let ethPrice = "0";
 | |
|     ctokens[network].allTokens.forEach((ctoken, i) => {
 | |
|       const key = ctoken.address;
 | |
|       const [
 | |
|         priceInEthInWei,
 | |
|         priceInUsdInWei,
 | |
|         exchangeRateInWei,
 | |
|         cBalanceInWei,
 | |
|         borrowInWei,
 | |
|         totalBorrowsInWei,
 | |
|         totalSuppliedInWei,
 | |
|         borrowCapInWei,
 | |
|         supplyRatePerBlock,
 | |
|         borrowRatePerBlock,
 | |
|         collateralFactorInWei,
 | |
|         compSpeed,
 | |
|         isComped,
 | |
|         isBorrowPaused
 | |
|       ] = res[0][i];
 | |
| 
 | |
|       const decimals = tokens[network].getTokenByKey(ctoken.root).decimals;
 | |
|       const root = ctoken.root;
 | |
| 
 | |
|       const blocksInYear = 2398050;
 | |
| 
 | |
|       const priceInEth = new BigNumber(priceInEthInWei).dividedBy(1e18);
 | |
|       const priceInUsd = new BigNumber(priceInUsdInWei).dividedBy(1e18);
 | |
|       const exchangeRate = new BigNumber(exchangeRateInWei).dividedBy(1e18);
 | |
|       const supply = new BigNumber(cBalanceInWei)
 | |
|         .multipliedBy(exchangeRate)
 | |
|         .dividedBy(10 ** decimals);
 | |
|       const borrow = new BigNumber(borrowInWei).dividedBy(10 ** decimals);
 | |
|       const supplyRate = new BigNumber(supplyRatePerBlock)
 | |
|         .multipliedBy(blocksInYear)
 | |
|         .dividedBy(1e18);
 | |
|       const supplyYield = supplyRate
 | |
|         .dividedBy(365)
 | |
|         .plus(1)
 | |
|         .exponentiatedBy(365)
 | |
|         .minus(1)
 | |
|         .toFixed(18);
 | |
|       const borrowRate = new BigNumber(borrowRatePerBlock)
 | |
|         .multipliedBy(blocksInYear)
 | |
|         .dividedBy(1e18);
 | |
|       const borrowYield = borrowRate
 | |
|         .dividedBy(365)
 | |
|         .plus(1)
 | |
|         .exponentiatedBy(365)
 | |
|         .minus(1)
 | |
|         .toFixed(18);
 | |
|       const totalBorrow = new BigNumber(totalBorrowsInWei).dividedBy(
 | |
|         10 ** decimals
 | |
|       );
 | |
|       const borrowCap = new BigNumber(borrowCapInWei)
 | |
|         .dividedBy(10 ** decimals)
 | |
|         .multipliedBy(0.9999)
 | |
|         .toFixed();
 | |
|       const collateralFactor = new BigNumber(collateralFactorInWei)
 | |
|         .dividedBy(1e18)
 | |
|         .toFixed();
 | |
|       const totalSupplied = new BigNumber(totalSuppliedInWei)
 | |
|         .dividedBy(10 ** decimals)
 | |
|         .multipliedBy(priceInUsd);
 | |
|       const compSupplyApy = new BigNumber(compSpeed)
 | |
|         .multipliedBy(blocksInYear)
 | |
|         .dividedBy(1e18)
 | |
|         .dividedBy(totalSupplied)
 | |
|         .toFixed();
 | |
|       const compBorrowApy = new BigNumber(compSpeed)
 | |
|         .multipliedBy(blocksInYear)
 | |
|         .dividedBy(1e18)
 | |
|         .dividedBy(totalBorrow.multipliedBy(priceInUsd))
 | |
|         .toFixed();
 | |
| 
 | |
|       newPos.totalSupplyInEth = newPos.totalSupplyInEth.plus(
 | |
|         supply.multipliedBy(priceInEth)
 | |
|       );
 | |
|       newPos.maxBorrowLimitInEth = newPos.maxBorrowLimitInEth.plus(
 | |
|         supply.multipliedBy(priceInEth).multipliedBy(ctoken.factor)
 | |
|       );
 | |
|       newPos.totalBorrowInEth = newPos.totalBorrowInEth.plus(
 | |
|         borrow.multipliedBy(priceInEth)
 | |
|       );
 | |
| 
 | |
|       if (ctoken.root === "eth") ethPrice = priceInUsd.toFixed();
 | |
|       dataPos.push({
 | |
|         key: root,
 | |
|         cTokenAddr: key,
 | |
|         cTokenKey: ctoken.key,
 | |
|         cTokenDecimals: ctoken.decimals.toString(),
 | |
|         cTokenId: ctoken.id,
 | |
|         priceInEth: priceInEth.toFixed(),
 | |
|         priceInUsd: priceInUsd.toFixed(),
 | |
|         exchangeRate: exchangeRate.toFixed(),
 | |
|         ctknBalance: cBalanceInWei,
 | |
|         supply: supply.toFixed(),
 | |
|         borrow: borrow.toFixed(),
 | |
|         supplyRate: supplyRate.toFixed(),
 | |
|         supplyYield,
 | |
|         borrowRate: borrowRate.toFixed(),
 | |
|         borrowYield,
 | |
|         factor: collateralFactor,
 | |
|         totalBorrow: totalBorrow.toFixed(),
 | |
|         borrowCap,
 | |
|         isComped,
 | |
|         borrowEnabled: !isBorrowPaused,
 | |
|         compSupplyApy,
 | |
|         compBorrowApy
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     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();
 | |
|     //@ts-ignore
 | |
|     newPos.totalSupplyInEth = newPos.totalSupplyInEth.toFixed();
 | |
|     //@ts-ignore
 | |
|     newPos.totalBorrowInEth = newPos.totalBorrowInEth.toFixed();
 | |
|     //@ts-ignore
 | |
|     newPos.maxBorrowLimitInEth = newPos.maxBorrowLimitInEth.toFixed();
 | |
| 
 | |
|     //@ts-ignore
 | |
|     newPos.status = status;
 | |
| 
 | |
|     //@ts-ignore
 | |
|     newPos.liquidation = liquidation;
 | |
| 
 | |
|     //@ts-ignore
 | |
|     newPos.ethPriceInUsd = ethPrice;
 | |
| 
 | |
|     const comp = res[1];
 | |
|     //@ts-ignore
 | |
|     newPos.compBalance = new BigNumber(comp.balance).dividedBy(1e18).toFixed();
 | |
| 
 | |
|     //@ts-ignore
 | |
|     newPos.compAccrued = new BigNumber(comp.allocated)
 | |
|       .dividedBy(1e18)
 | |
|       .toFixed();
 | |
|     //@ts-ignore
 | |
|     newPos.compDelegate = comp.delegate;
 | |
|     //@ts-ignore
 | |
|     newPos.compVotes = comp.votes;
 | |
| 
 | |
|     newPos.data = dataPos;
 | |
| 
 | |
|     return newPos;
 | |
|   } catch (error) {
 | |
|     console.error(error);
 | |
|     return Promise.reject(error);
 | |
|   }
 | |
| }
 | 
