text: import { Contract, ethers } from "ethers";
  import * as COINS from "./constants/coins";
  
  const ROUTER = require("./build/UniswapV2Router02.json");
  const ERC20 = require("./build/ERC20.json");
  const FACTORY = require("./build/IUniswapV2Factory.json");
  const PAIR = require("./build/IUniswapV2Pair.json");
  
  export function getProvider() {
    return new ethers.providers.Web3Provider(window.ethereum);
  }
  
  export function getSigner(provider) {
    return provider.getSigner();
  }
  
  export async function getNetwork(provider) {
    const network = await provider.getNetwork();
    return network.chainId;
  }
  
  export function getRouter(address, signer) {
    return new Contract(address, ROUTER.abi, signer);
  }
  
  export function getWeth(address, signer) {
    return new Contract(address, ERC20.abi, signer);
  }
  
  export function getFactory(address, signer) {
    return new Contract(address, FACTORY.abi, signer);
  }
  
  export async function getAccount() {
    const accounts = await window.ethereum.request({
      method: "eth_requestAccounts",
    });
  
    return accounts[0];
  }
  
  //This function checks if a ERC20 token exists for a given address
  //    `address` - The Ethereum address to be checked
  //    `signer` - The current signer
  export function doesTokenExist(address, signer) {
    try {
      return new Contract(address, ERC20.abi, signer);
    } catch (err) {
      return false;
    }
  }
  
  // This function returns an object with 2 fields: `balance` which container's the account's balance in the particular token,
  // and `symbol` which is the abbreviation of the token name. To work correctly it must be provided with 4 arguments:
  //    `accountAddress` - An Ethereum address of the current user's account
  //    `address` - An Ethereum address of the token to check for (either a token or AUT)
  //    `provider` - The current provider
  //    `signer` - The current signer
  export async function getBalanceAndSymbol(
    accountAddress,
    address,
    provider,
    signer
  ) {
    try {
      if (address === COINS.AUTONITY.address) {
        const balanceRaw = await provider.getBalance(accountAddress);
  
        return {
          balance: ethers.utils.formatEther(balanceRaw),
          symbol: COINS.AUTONITY.abbr,
        };
      } else {
        const token = new Contract(address, ERC20.abi, signer);
        const balanceRaw = await token.balanceOf(accountAddress);
        const symbol = await token.symbol();
  
        return {
          balance: ethers.utils.formatEther(balanceRaw),
          symbol: symbol,
        };
      }
    } catch (err) {
      return false;
    }
  }
  
  // This function swaps two particular tokens / AUT, it can handle switching from AUT to ERC20 token, ERC20 token to AUT, and ERC20 token to ERC20 token.
  // No error handling is done, so any issues can be caught with the use of .catch()
  // To work correctly, there needs to be 7 arguments:
  //    `address1` - An Ethereum address of the token to trade from (either a token or AUT)
  //    `address2` - An Ethereum address of the token to trade to (either a token or AUT)
  //    `amount` - A float or similar number representing the value of address1's token to trade
  //    `routerContract` - The router contract to carry out this trade
  //    `accountAddress` - An Ethereum address of the current user's account
  //    `signer` - The current signer
  export async function swapTokens(
    address1,
    address2,
    amount,
    routerContract,
    accountAddress,
    signer
  ) {
    const tokens = [address1, address2];
    const time = Math.floor(Date.now() / 1000) + 200000;
    const deadline = ethers.BigNumber.from(time);
  
    const amountIn = ethers.utils.parseEther(amount.toString());
    const amountOut = await routerContract.callStatic.getAmountsOut(
      amountIn,
      tokens
    );
  
    const token1 = new Contract(address1, ERC20.abi, signer);
    await token1.approve(routerContract.address, amountIn);
    const wethAddress = await routerContract.WETH();
  
    if (address1 === wethAddress) {
      // Eth -> Token
      await routerContract.swapExactETHForTokens(
        amountOut[1],
        tokens,
        accountAddress,
        deadline,
        { value: amountIn }
      );
    } else if (address2 === wethAddress) {
      // Token -> Eth
      await routerContract.swapExactTokensForETH(
        amountIn,
        amountOut[1],
        tokens,
        accountAddress,
        deadline
      );
    } else {
      await routerContract.swapExactTokensForTokens(
        amountIn,
        amountOut[1],
        tokens,
        accountAddress,
        deadline
      );
    }
  }
  
  //This function returns the conversion rate between two token addresses
  //    `address1` - An Ethereum address of the token to swaped from (either a token or AUT)
  //    `address2` - An Ethereum address of the token to swaped to (either a token or AUT)
  //    `amountIn` - Amount of the token at address 1 to be swaped from
  //    `routerContract` - The router contract to carry out this swap
  export async function getAmountOut(
    address1,
    address2,
    amountIn,
    routerContract
  ) {
    try {
      const values_out = await routerContract.getAmountsOut(
        ethers.utils.parseEther(amountIn),
        [address1, address2]
      );
      const amount_out = ethers.utils.formatEther(values_out[1]);
      return Number(amount_out);
    } catch {
      return false;
    }
  }
  
  // This function calls the pair contract to fetch the reserves stored in a the liquidity pool between the token of address1 and the token
  // of address2. Some extra logic was needed to make sure that the results were returned in the correct order, as
  // `pair.getReserves()` would always return the reserves in the same order regardless of which order the addresses were.
  //    `address1` - An Ethereum address of the token to trade from (either a ERC20 token or AUT)
  //    `address2` - An Ethereum address of the token to trade to (either a ERC20 token or AUT)
  //    `pair` - The pair contract for the two tokens
  export async function fetchReserves(address1, address2, pair) {
    try {
      const reservesRaw = await pair.getReserves();
      let results = [
        Number(ethers.utils.formatEther(reservesRaw[0])),
        Number(ethers.utils.formatEther(reservesRaw[1])),
      ];
  
      return [
        (await pair.token0()) === address1 ? results[0] : results[1],
        (await pair.token1()) === address2 ? results[1] : results[0],
      ];
    } catch (err) {
      console.log("no reserves yet");
      return [0, 0];
    }
  }
  
  // This function returns the reserves stored in a the liquidity pool between the token of address1 and the token
  // of address2, as well as the liquidity tokens owned by accountAddress for that pair.
  //    `address1` - An Ethereum address of the token to trade from (either a token or AUT)
  //    `address2` - An Ethereum address of the token to trade to (either a token or AUT)
  //    `factory` - The current factory
  //    `signer` - The current signer
  export async function getReserves(
    address1,
    address2,
    factory,
    signer,
    accountAddress
  ) {
    const pairAddress = await factory.getPair(address1, address2);
    const pair = new Contract(pairAddress, PAIR.abi, signer);
  
    const reservesRaw = await fetchReserves(address1, address2, pair);
    const liquidityTokens_BN = await pair.balanceOf(accountAddress);
    const liquidityTokens = Number(
      ethers.utils.formatEther(liquidityTokens_BN)
    ).toFixed(2);
  
    return [
      reservesRaw[0].toFixed(2),
      reservesRaw[1].toFixed(2),
      liquidityTokens,
    ];
  }
  