mirror of
				https://github.com/Instadapp/assembly.git
				synced 2024-07-29 22:37:06 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			288 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			288 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import {
 | 
						|
  computed,
 | 
						|
  reactive,
 | 
						|
  onMounted,
 | 
						|
  useContext,
 | 
						|
  watch
 | 
						|
} from "@nuxtjs/composition-api";
 | 
						|
import BigNumber from "bignumber.js";
 | 
						|
import abis from "~/constant/abis";
 | 
						|
import addresses from "~/constant/addresses";
 | 
						|
import tokens from "~/constant/tokens";
 | 
						|
import uniPoolTokens from "~/constant/uniPoolTokens";
 | 
						|
import { useDSA } from "./useDSA";
 | 
						|
import { Network, useNetwork } from "./useNetwork";
 | 
						|
import { useWeb3 } from "@instadapp/vue-web3";
 | 
						|
import Web3 from "web3";
 | 
						|
import { AbiItem } from "web3-utils";
 | 
						|
import { useToken } from "./useToken";
 | 
						|
import { useBigNumber } from "./useBigNumber";
 | 
						|
import { useSorting } from "./useSorting";
 | 
						|
 | 
						|
const balances = reactive({
 | 
						|
  user: {
 | 
						|
    mainnet: {},
 | 
						|
    polygon: {}
 | 
						|
  },
 | 
						|
  dsa: {
 | 
						|
    mainnet: {},
 | 
						|
    polygon: {}
 | 
						|
  }
 | 
						|
});
 | 
						|
 | 
						|
const prices = reactive({
 | 
						|
  mainnet: {},
 | 
						|
  polygon: {}
 | 
						|
});
 | 
						|
 | 
						|
export function useBalances() {
 | 
						|
  const { $axios } = useContext();
 | 
						|
  const { times, plus, ensureValue } = useBigNumber();
 | 
						|
  const { account, library } = useWeb3();
 | 
						|
  const { activeNetworkId } = useNetwork()
 | 
						|
  const { activeAccount } = useDSA();
 | 
						|
  const { getTokenByKey } = useToken();
 | 
						|
  const { by } = useSorting();
 | 
						|
 | 
						|
  onMounted(async () => {
 | 
						|
    prices.mainnet = await $axios.$get("https://api.instadapp.io/defi/prices");
 | 
						|
    prices.polygon = await $axios.$get(
 | 
						|
      "https://api.instadapp.io/defi/polygon/prices"
 | 
						|
    );
 | 
						|
  });
 | 
						|
  const fetchBalances = async (refresh = false) => {
 | 
						|
    if (!balances.user || refresh) {
 | 
						|
      if (!account.value) return;
 | 
						|
      balances.user = {
 | 
						|
        mainnet:
 | 
						|
          activeNetworkId.value === Network.Mainnet
 | 
						|
            ? await getBalances(account.value, Network.Mainnet, library.value)
 | 
						|
            : {},
 | 
						|
        polygon:
 | 
						|
          activeNetworkId.value === Network.Polygon
 | 
						|
            ? await getBalances(account.value, Network.Polygon, library.value)
 | 
						|
            : {}
 | 
						|
      };
 | 
						|
    }
 | 
						|
 | 
						|
    if (!balances.dsa || refresh) {
 | 
						|
      if (!activeAccount.value) return;
 | 
						|
 | 
						|
      balances.dsa = {
 | 
						|
        mainnet:
 | 
						|
          activeNetworkId.value === Network.Mainnet
 | 
						|
            ? await getBalances(
 | 
						|
                activeAccount.value.address,
 | 
						|
                Network.Mainnet,
 | 
						|
                library.value
 | 
						|
              )
 | 
						|
            : {},
 | 
						|
        polygon:
 | 
						|
          activeNetworkId.value === Network.Polygon
 | 
						|
            ? await getBalances(
 | 
						|
                activeAccount.value.address,
 | 
						|
                Network.Polygon,
 | 
						|
                library.value
 | 
						|
              )
 | 
						|
            : {}
 | 
						|
      };
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
  const getBalanceByKey = (tokenKey, network = null, type = "dsa") => {
 | 
						|
    return getBalanceByAddress(getTokenByKey(tokenKey)?.address, network, type);
 | 
						|
  };
 | 
						|
 | 
						|
  const getBalanceByAddress = (address, network = null, type = "dsa") => {
 | 
						|
    return (
 | 
						|
      balances[type]?.[network || activeNetworkId.value][address]?.balance || "0"
 | 
						|
    );
 | 
						|
  };
 | 
						|
 | 
						|
  const getBalanceRawByKey = (tokenKey, network = null, type = "dsa") => {
 | 
						|
    return (
 | 
						|
      balances[type]?.[network || activeNetworkId.value][
 | 
						|
        getTokenByKey(tokenKey)?.address
 | 
						|
      ]?.raw || "0"
 | 
						|
    );
 | 
						|
  };
 | 
						|
 | 
						|
  const netWorth = (address, type = "dsa") => {
 | 
						|
    const balance = getBalanceByAddress(address, activeNetworkId.value, type);
 | 
						|
    const price = ensureValue(prices[activeNetworkId.value][address]).toFixed();
 | 
						|
 | 
						|
    return times(balance, price).toFixed();
 | 
						|
  };
 | 
						|
 | 
						|
  const balanceTotal = computed(() =>
 | 
						|
    tokens[activeNetworkId.value].allTokens.reduce(
 | 
						|
      (totalNetWorth, token) =>
 | 
						|
        plus(totalNetWorth, netWorth(token.address)).toFixed(),
 | 
						|
      "0"
 | 
						|
    )
 | 
						|
  );
 | 
						|
 | 
						|
  const getAssets = (type = "dsa") => {
 | 
						|
    return tokens[activeNetworkId.value].allTokens
 | 
						|
      .map(token => ({
 | 
						|
        ...token,
 | 
						|
        balance: getBalanceByAddress(token.address, activeNetworkId.value, type),
 | 
						|
        netWorth: netWorth(token.address, type)
 | 
						|
      }))
 | 
						|
      .sort(by("-netWorth"));
 | 
						|
  };
 | 
						|
 | 
						|
  watch(library, () => {
 | 
						|
    fetchBalances(true);
 | 
						|
  });
 | 
						|
  return {
 | 
						|
    balances,
 | 
						|
    fetchBalances,
 | 
						|
    getBalanceByKey,
 | 
						|
    getBalanceRawByKey,
 | 
						|
    balanceTotal,
 | 
						|
    prices,
 | 
						|
    getAssets
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
async function getBalances(
 | 
						|
  owner,
 | 
						|
  network: Network,
 | 
						|
  web3: Web3,
 | 
						|
  additionalTokens = []
 | 
						|
) {
 | 
						|
  try {
 | 
						|
    const tokenResolverABI = abis.resolver.balance;
 | 
						|
    const tokenResolverAddr = addresses[network].resolver.balance;
 | 
						|
 | 
						|
    const tokensArr = tokens[network].allTokens;
 | 
						|
    const tokensList =
 | 
						|
      network === Network.Mainnet
 | 
						|
        ? [...tokensArr, ...uniPoolTokens[network].allTokens]
 | 
						|
        : tokensArr;
 | 
						|
 | 
						|
    let tokensAddrArr = tokensList.map(a => a.address);
 | 
						|
    const tokenResolverInstance = new web3.eth.Contract(
 | 
						|
      tokenResolverABI as AbiItem[],
 | 
						|
      tokenResolverAddr
 | 
						|
    );
 | 
						|
 | 
						|
    const tokensAddrArrLength = tokensAddrArr.length;
 | 
						|
    let additionalTokensInfo;
 | 
						|
    let isNotTokens;
 | 
						|
    if (additionalTokens && additionalTokens.length) {
 | 
						|
      additionalTokens = additionalTokens.filter(
 | 
						|
        token =>
 | 
						|
          !tokensArr.find(a => a.address.toLowerCase() === token.toLowerCase())
 | 
						|
      );
 | 
						|
    }
 | 
						|
    if (additionalTokens && additionalTokens.length) {
 | 
						|
      additionalTokensInfo = await getTokensDetails(
 | 
						|
        additionalTokens,
 | 
						|
        network,
 | 
						|
        web3
 | 
						|
      );
 | 
						|
      isNotTokens = Object.fromEntries(
 | 
						|
        additionalTokens
 | 
						|
          .filter((val, index) => !additionalTokensInfo[index].isToken)
 | 
						|
          .map(val => [val, { isToken: false }])
 | 
						|
      );
 | 
						|
      additionalTokens = additionalTokens.filter(
 | 
						|
        (val, index) => additionalTokensInfo[index].isToken
 | 
						|
      );
 | 
						|
      additionalTokensInfo = additionalTokensInfo.filter(val => val.isToken);
 | 
						|
      tokensAddrArr = tokensAddrArr.concat(additionalTokens);
 | 
						|
    }
 | 
						|
 | 
						|
    const tokenBalances = await tokenResolverInstance.methods
 | 
						|
      .getBalances(owner, tokensAddrArr)
 | 
						|
      .call();
 | 
						|
 | 
						|
    let tokensBalObj = {};
 | 
						|
    tokenBalances.forEach((a, i) => {
 | 
						|
      const tokenAddress = web3.utils.toChecksumAddress(tokensAddrArr[i]);
 | 
						|
      let tokenData;
 | 
						|
      if (i < tokensAddrArrLength) {
 | 
						|
        tokenData = {
 | 
						|
          ...tokensList[i],
 | 
						|
          decimals: tokensList[i].decimals.toString()
 | 
						|
        };
 | 
						|
      } else {
 | 
						|
        tokenData = additionalTokensInfo[i - tokensAddrArrLength];
 | 
						|
      }
 | 
						|
      const { name, symbol, decimals, type, isStableCoin, key } = tokenData;
 | 
						|
      tokensBalObj[tokenAddress] = {
 | 
						|
        address: tokenAddress,
 | 
						|
        name,
 | 
						|
        symbol,
 | 
						|
        decimals,
 | 
						|
        type,
 | 
						|
        isStableCoin,
 | 
						|
        key,
 | 
						|
        balance: new BigNumber(a).dividedBy(10 ** tokenData.decimals).toFixed(),
 | 
						|
        raw: String(a)
 | 
						|
      };
 | 
						|
    });
 | 
						|
    tokensBalObj = { ...tokensBalObj, ...isNotTokens };
 | 
						|
 | 
						|
    return tokensBalObj;
 | 
						|
  } catch (error) {
 | 
						|
    return Promise.reject(error);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
const storedTokens = {};
 | 
						|
 | 
						|
async function getTokensDetails(addressArr, network: Network, web3: Web3) {
 | 
						|
  try {
 | 
						|
    const balanceInstance = new web3.eth.Contract(
 | 
						|
      abis.resolver.balance as AbiItem[],
 | 
						|
      addresses[network].resolver.balance
 | 
						|
    );
 | 
						|
 | 
						|
    const result = [];
 | 
						|
    for (let i = 0; i < addressArr.length; i++) {
 | 
						|
      let details = tokens[network].getTokenByAddress(addressArr[i]);
 | 
						|
      if (!details) {
 | 
						|
        details = uniPoolTokens[network].getTokenByAddress(addressArr[i]);
 | 
						|
      }
 | 
						|
      if (!details) {
 | 
						|
        details = storedTokens[addressArr[i]];
 | 
						|
      } else {
 | 
						|
        details.isToken = true;
 | 
						|
      }
 | 
						|
      try {
 | 
						|
        if (!details) {
 | 
						|
          details = await balanceInstance.methods
 | 
						|
            .getTokenDetails([addressArr[i]])
 | 
						|
            .call();
 | 
						|
          const { name, symbol, decimals, isToken } = details[0];
 | 
						|
          details = {
 | 
						|
            name,
 | 
						|
            symbol,
 | 
						|
            decimals,
 | 
						|
            isToken,
 | 
						|
            type: "token",
 | 
						|
            isStableCoin: false,
 | 
						|
            key: symbol.toLowerCase()
 | 
						|
          };
 | 
						|
          storedTokens[addressArr[i]] = details;
 | 
						|
        }
 | 
						|
      } catch (error) {
 | 
						|
        if (
 | 
						|
          error.message &&
 | 
						|
          error.message === "Returned error: execution reverted"
 | 
						|
        ) {
 | 
						|
          details = { isToken: false };
 | 
						|
        } else {
 | 
						|
          throw error;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      details.address = addressArr[i];
 | 
						|
      result[i] = details;
 | 
						|
    }
 | 
						|
    return result;
 | 
						|
  } catch (error) {}
 | 
						|
}
 |