text: async function getHecoUniPool(App, pool, poolAddress, stakingAddress) {    
      let q0, q1;
      const reserves = await pool.getReserves();
      q0 = reserves._reserve0;
      q1 = reserves._reserve1;
      const decimals = await pool.decimals();
      const token0 = await pool.token0();
      const token1 = await pool.token1();
      return { 
          symbol : await pool.symbol(),
          name : await pool.name(),
          address: poolAddress,
          token0,
          q0,
          token1,
          q1,
          totalSupply: await pool.totalSupply() / 10 ** decimals,
          stakingAddress: stakingAddress,
          staked: await pool.balanceOf(stakingAddress) / 10 ** decimals,
          decimals: decimals,
          unstaked: await pool.balanceOf(App.YOUR_ADDRESS) / 10 ** decimals,
          contract: pool,
          tokens : [token0, token1],
          is1inch : false
      };
  }
  
  async function getHec20(App, token, address, stakingAddress) {
      if (address == "0x0000000000000000000000000000000000000000") {
        return {
          address,
          name : "Huobi Token",
          symbol : "HT",
          totalSupply: 1e8,
          decimals: 18,
          staked: 0,
          unstaked: 0,
          contract: null,
          tokens:[address]
        }
      }
      const decimals = await token.decimals()
      return {
          address,
          name : await token.name(),
          symbol : await token.symbol(),
          totalSupply : await token.totalSupply(),
          decimals : decimals,
          staked:  await token.balanceOf(stakingAddress) / 10 ** decimals,
          unstaked: await token.balanceOf(App.YOUR_ADDRESS)  / 10 ** decimals,
          contract: token,
          tokens : [address]
      };
  }
  
  async function getHecoStoredToken(App, tokenAddress, stakingAddress, type) {
    switch (type) {
      case "uniswap": 
        const pool = new ethers.Contract(tokenAddress, UNI_ABI, App.provider);
        return await getHecoUniPool(App, pool, tokenAddress, stakingAddress);
      case "hrc20":
        const hrc20 = new ethers.Contract(tokenAddress, ERC20_ABI, App.provider);
        return await getHec20(App, hrc20, tokenAddress, stakingAddress);
    }
  }
  
  async function getHecoToken(App, tokenAddress, stakingAddress) {
      if (tokenAddress == "0x0000000000000000000000000000000000000000") {
        return getHec20(App, null, tokenAddress, "")
      }
      const type = window.localStorage.getItem(tokenAddress);
      if (type) return getHecoStoredToken(App, tokenAddress, stakingAddress, type);
      try {
        const pool = new ethers.Contract(tokenAddress, UNI_ABI, App.provider);
        const _token0 = await pool.token0();
        const uniPool = await getHecoUniPool(App, pool, tokenAddress, stakingAddress);
        window.localStorage.setItem(tokenAddress, "uniswap");
        return uniPool;
      }
      catch(err) {
      }
      try {
        const hrc20 = new ethers.Contract(tokenAddress, ERC20_ABI, App.provider);
        const _name = await hrc20.name();
        const hrc20tok = await getHec20(App, hrc20, tokenAddress, stakingAddress);
        window.localStorage.setItem(tokenAddress, "hrc20");
        return hrc20tok;
      }
      catch(err) {
        console.log(err);
        console.log(`Couldn't match ${tokenAddress} to any known token type.`);
      }
    }
  
  async function loadHecoSynthetixPoolInfo(App, tokens, prices, stakingAbi, stakingAddress,
      rewardTokenFunction, stakeTokenFunction) {
        const STAKING_POOL = new ethers.Contract(stakingAddress, stakingAbi, App.provider);
    
        if (!STAKING_POOL.callStatic[stakeTokenFunction]) {
          console.log("Couldn't find stake function ", stakeTokenFunction);
        }
        const stakeTokenAddress = await STAKING_POOL.callStatic[stakeTokenFunction]();
    
        const rewardTokenAddress = await STAKING_POOL.callStatic[rewardTokenFunction]();
    
        var stakeToken = await getHecoToken(App, stakeTokenAddress, stakingAddress);
    
        if (stakeTokenAddress.toLowerCase() === rewardTokenAddress.toLowerCase()) {
          stakeToken.staked = await STAKING_POOL.totalSupply() / 10 ** stakeToken.decimals;
        }
    
        var newPriceAddresses = stakeToken.tokens.filter(x =>
          x.toLowerCase() !=  "0xb34ab2f65c6e4f764ffe740ab83f982021faed6d" && //BSG can't be retrieved from Coingecko
          !getParameterCaseInsensitive(prices, x));
        var newPrices = await lookUpTokenPrices(newPriceAddresses);
        for (const key in newPrices) {
          if (newPrices[key]?.usd)
              prices[key] = newPrices[key];
        }
        var newTokenAddresses = stakeToken.tokens.filter(x =>
          !getParameterCaseInsensitive(tokens,x));
        for (const address of newTokenAddresses) {
            tokens[address] = await getHecoToken(App, address, stakingAddress);
        }
        if (!getParameterCaseInsensitive(tokens, rewardTokenAddress)) {
            tokens[rewardTokenAddress] = await getHecoToken(App, rewardTokenAddress, stakingAddress);
        }
        const rewardToken = getParameterCaseInsensitive(tokens, rewardTokenAddress);
    
        const rewardTokenTicker = rewardToken.symbol;
    
        const poolPrices = getPoolPrices(tokens, prices, stakeToken, "heco");
    
        const stakeTokenTicker = poolPrices.stakeTokenTicker;
    
        const stakeTokenPrice =
            prices[stakeTokenAddress]?.usd ?? getParameterCaseInsensitive(prices, stakeTokenAddress)?.usd;
        const rewardTokenPrice = getParameterCaseInsensitive(prices, rewardTokenAddress)?.usd;
    
        const periodFinish = await STAKING_POOL.periodFinish();
        const rewardRate = await STAKING_POOL.rewardRate();
        const weeklyRewards = (Date.now() / 1000 > periodFinish) ? 0 : rewardRate / 1e18 * 604800;
    
        const usdPerWeek = weeklyRewards * rewardTokenPrice;
    
        const staked_tvl = poolPrices.staked_tvl;
    
        const userStaked = await STAKING_POOL.balanceOf(App.YOUR_ADDRESS) / 10 ** stakeToken.decimals;
    
        const userUnstaked = stakeToken.unstaked;
    
        const earned = await STAKING_POOL.earned(App.YOUR_ADDRESS) / 10 ** rewardToken.decimals;
    
        return  {
          stakingAddress,
          poolPrices,
          stakeTokenAddress,
          rewardTokenAddress,
          stakeTokenTicker,
          rewardTokenTicker,
          stakeTokenPrice,
          rewardTokenPrice,
          weeklyRewards,
          usdPerWeek,
          staked_tvl,
          userStaked,
          userUnstaked,
          earned
        }
  }
  
  async function loadHecoSynthetixPool(App, tokens, prices, abi, address, rewardTokenFunction, stakeTokenFunction) {
      const info = await loadHecoSynthetixPoolInfo(App, tokens, prices, abi, address, rewardTokenFunction, stakeTokenFunction);
      return await printSynthetixPool(App, info, "heco");
  }
  
  async function loadHecoBasisFork(data) {
      const App = await init_ethers();
  
      _print(`Initialized ${App.YOUR_ADDRESS}`);
      _print("Reading smart contracts...\n");
  
      var tokens = {};
      var prices = {"0x55d398326f99059ff775485246999027b3197955" : { usd : 1 }};
      var totalStaked = 0;
      
      let p1 = await loadHecoSynthetixPool(App, tokens, prices, data.PoolABI, 
          data.SharePool.address, data.SharePool.rewardToken, data.SharePool.stakeToken);
      totalStaked += p1.staked_tvl;
      
      if (data.SharePool2) {
        let p3 = await loadHecoSynthetixPool(App, tokens, prices, data.PoolABI, 
            data.SharePool2.address, data.SharePool2.rewardToken, data.SharePool2.stakeToken);
        totalStaked += p3.staked_tvl;
      }
  
      let p2 = await loadHecoSynthetixPool(App, tokens, prices, data.PoolABI, 
          data.CashPool.address, data.CashPool.rewardToken, data.CashPool.stakeToken);
      totalStaked += p2.staked_tvl;
  
      if (data.SeedBanks) {
        let p = await loadMultipleHecoSynthetixPools(App, tokens, prices, data.SeedBanks)
        totalStaked += p.staked_tvl;
        if (p.totalUserStaked > 0) {
          _print(`You are staking a total of $${formatMoney(p.totalUserStaked)} at an APR of ${(p.totalAPR * 100).toFixed(2)}%\n`);
        }
      }
  
      if (!data.SeedBanks)
      {
        if (data.Boardrooms) {
          for (const boardroom of data.Boardrooms) {
            let br = await loadBoardroom(App, prices, boardroom.address, data.Oracle, data.UniswapLP, data.Cash,
                data.ShareTicker, data.CashTicker, data.ExpansionsPerDay, data.MaximumExpansion, 
                data.Decimals, boardroom.ratio, data.TargetMantissa);
            totalStaked += br.staked_tvl;
          }
        }
        else {
          let br = await loadBoardroom(App, prices, data.Boardroom, data.Oracle, data.UniswapLP, data.Cash,
              data.ShareTicker, data.CashTicker, data.ExpansionsPerDay, data.MaximumExpansion, 
              data.Decimals, 1, data.TargetMantissa);
          totalStaked += br.staked_tvl;
        }
      } 
  
      _print_bold(`Total staked: $${formatMoney(totalStaked)}`)
  
      hideLoading();
  }
  
  
  async function getHecoPoolInfo(app, chefContract, chefAddress, poolIndex, pendingRewardsFunction) {  
    const poolInfo = await chefContract.poolInfo(poolIndex);
    if (poolInfo.allocPoint == 0) {
      return {
        address: poolInfo.lpToken,
        allocPoints: poolInfo.allocPoint ?? 1,
        poolToken: null,
        userStaked : 0,
        pendingRewardTokens : 0,
      };
    }
    const poolToken = await getHecoToken(app, poolInfo.lpToken, chefAddress);
    const userInfo = await chefContract.userInfo(poolIndex, app.YOUR_ADDRESS);
    const pendingRewardTokens = await chefContract.callStatic[pendingRewardsFunction](poolIndex, app.YOUR_ADDRESS);
    const staked = userInfo.amount / 10 ** poolToken.decimals;
    return {
        address: poolInfo.lpToken,
        allocPoints: poolInfo.allocPoint ?? 1,
        poolToken: poolToken,
        userStaked : staked,
        pendingRewardTokens : pendingRewardTokens / 10 ** 18,
    };
  }
  
  async function loadHecoChefContract(App, tokens, prices, chef, chefAddress, chefAbi, rewardTokenTicker,
    rewardTokenFunction, rewardsPerBlockFunction, rewardsPerWeekFixed, pendingRewardsFunction,
    deathPoolIndices) {
    const chefContract = chef ?? new ethers.Contract(chefAddress, chefAbi, App.provider);
  
    const poolCount = parseInt(await chefContract.poolLength(), 10);
    const totalAllocPoints = await chefContract.totalAllocPoint();
  
    _print(`Found ${poolCount} pools.\n`)
  
    _print(`Showing incentivized pools only.\n`);
  
    var tokens = {};
  
    const rewardTokenAddress = await chefContract.callStatic[rewardTokenFunction]();
    const rewardToken = await getHecoToken(App, rewardTokenAddress, chefAddress);
    const rewardsPerWeek = rewardsPerWeekFixed ?? 
      await chefContract.callStatic[rewardsPerBlockFunction]() 
      / 10 ** rewardToken.decimals * 604800 / 3
  
    const poolInfos = await Promise.all([...Array(poolCount).keys()].map(async (x) =>
      await getHecoPoolInfo(App, chefContract, chefAddress, x, pendingRewardsFunction)));
  
    var tokenAddresses = [].concat.apply([], poolInfos.filter(x => x.poolToken).map(x => x.poolToken.tokens));
  
    await Promise.all(tokenAddresses.map(async (address) => {
        tokens[address] = await getHecoToken(App, address, chefAddress);
    }));
  
    if (deathPoolIndices) {   //load prices for the deathpool assets
      deathPoolIndices.map(i => poolInfos[i])
                       .map(poolInfo => 
        poolInfo.poolToken ? getPoolPrices(tokens, prices, poolInfo.poolToken, "heco") : undefined);
    }
  
    const poolPrices = poolInfos.map(poolInfo => poolInfo.poolToken ? getPoolPrices(tokens, prices, poolInfo.poolToken, "heco") : undefined);
  
  
    _print("Finished reading smart contracts.\n");
      
    for (i = 0; i < poolCount; i++) {
      if (poolPrices[i]) {
        printChefPool(App, chefAbi, chefAddress, prices, tokens, poolInfos[i], i, poolPrices[i],
          totalAllocPoints, rewardsPerWeek, rewardTokenTicker, rewardTokenAddress,
          pendingRewardsFunction, "heco");
      }
    }
  }
  
  
  const hrcTokens = [ 
    { "id": "huobi-token","symbol": "HT","contract":"0x5545153ccfca01fbd7dd11c0b23ba694d9509a6f" },
    { "id": "mdex","symbol": "MDX","contract":"0x25d2e80cb6b86881fd7e07dd263fb79f4abe033c" }, 
    { "id": "tether","symbol": "USDT", "contract": "0xa71edc38d189767582c38a3145b5873052c3e47a" },
    { "id": "ethereum","symbol": "ETH", "contract": "0xb55569893b397324c0d048c9709f40c23445540e" },
    { "id": "bitcoin","symbol": "HBTC", "contract": "0x66a79d23e58475d2738179ca52cd0b41d73f0bea" },
    { "id": "ethereum","symbol": "HETH", "contract": "0x64FF637fB478863B7468bc97D30a5bF3A428a1fD" },
    { "id": "hoo-token","symbol": "HOO", "contract": "0xE1d1F66215998786110Ba0102ef558b22224C016" },
    { "id": "gene","symbol": "GENE", "contract": "0x2CFa849e8506910b2564aFE5BdEF33Ba66C730Aa" },
    { "id": "husd","symbol": "HUSD", "contract": "0x0298c2b32eaE4da002a15f36fdf7615BEa3DA047" }
  ]
  
  async function getHecoPrices() {
    const idPrices = await lookUpPrices(hrcTokens.map(x => x.id));
    const prices = {}
    for (const bt of hrcTokens)
        if (idPrices[bt.id])
            prices[bt.contract] = idPrices[bt.id];
    return prices;
  }
  
  async function loadMultipleHecoSynthetixPools(App, tokens, prices, pools) {
    let totalStaked  = 0, totalUserStaked = 0, individualAPRs = [];
    const infos = await Promise.all(pools.map(p => 
      loadHecoSynthetixPoolInfo(App, tokens, prices, p.abi, p.address, p.rewardTokenFunction, p.stakeTokenFunction)));
    for (const i of infos) {
      let p = await printSynthetixPool(App, i, "heco");
      totalStaked += p.staked_tvl || 0;
      totalUserStaked += p.userStaked || 0;
      if (p.userStaked > 0) {
        individualAPRs.push(p.userStaked * p.apr / 100);
      }
    }
    let totalAPR = totalUserStaked == 0 ? 0 : individualAPRs.reduce((x,y)=>x+y, 0) / totalUserStaked;
    return { staked_tvl : totalStaked, totalUserStaked, totalAPR };
  }