@ant-design/icons#RetweetOutlined JavaScript Examples

The following examples show how to use @ant-design/icons#RetweetOutlined. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Swap.jsx    From Tai-Shang-NFT-Wallet with MIT License 4 votes vote down vote up
function Swap({ selectedProvider, tokenListURI }) {
  const [tokenIn, setTokenIn] = useState(defaultToken);
  const [tokenOut, setTokenOut] = useState(defaultTokenOut);
  const [exact, setExact] = useState();
  const [amountIn, setAmountIn] = useState();
  const [amountInMax, setAmountInMax] = useState();
  const [amountOut, setAmountOut] = useState();
  const [amountOutMin, setAmountOutMin] = useState();
  const [trades, setTrades] = useState();
  const [routerAllowance, setRouterAllowance] = useState();
  const [balanceIn, setBalanceIn] = useState();
  const [balanceOut, setBalanceOut] = useState();
  const [slippageTolerance, setSlippageTolerance] = useState(
    new Percent(Math.round(defaultSlippage * 100).toString(), "10000"),
  );
  const [timeLimit, setTimeLimit] = useState(defaultTimeLimit);
  const [swapping, setSwapping] = useState(false);
  const [approving, setApproving] = useState(false);
  const [settingsVisible, setSettingsVisible] = useState(false);
  const [swapModalVisible, setSwapModalVisible] = useState(false);

  const [tokenList, setTokenList] = useState([]);

  const [tokens, setTokens] = useState();

  const [invertPrice, setInvertPrice] = useState(false);

  const blockNumber = useBlockNumber(selectedProvider, 3000);

  const signer = selectedProvider.getSigner();
  const routerContract = new ethers.Contract(ROUTER_ADDRESS, IUniswapV2Router02ABI, signer);

  const _tokenListUri = tokenListURI || "https://gateway.ipfs.io/ipns/tokens.uniswap.org";

  const debouncedAmountIn = useDebounce(amountIn, 500);
  const debouncedAmountOut = useDebounce(amountOut, 500);

  const activeChainId = process.env.REACT_APP_NETWORK === "kovan" ? ChainId.KOVAN : ChainId.MAINNET;

  useEffect(() => {
    const getTokenList = async () => {
      console.log(_tokenListUri);
      try {
        const tokenList = await fetch(_tokenListUri);
        const tokenListJson = await tokenList.json();
        const filteredTokens = tokenListJson.tokens.filter(function (t) {
          return t.chainId === activeChainId;
        });
        const ethToken = WETH[activeChainId];
        ethToken.name = "Ethereum";
        ethToken.symbol = "ETH";
        ethToken.logoURI =
          "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png";
        const _tokenList = [ethToken, ...filteredTokens];
        setTokenList(_tokenList);
        const _tokens = tokenListToObject(_tokenList);
        setTokens(_tokens);
      } catch (e) {
        console.log(e);
      }
    };
    getTokenList();
  }, [tokenListURI]);

  const getTrades = async () => {
    if (tokenIn && tokenOut && (amountIn || amountOut)) {
      const pairs = arr => arr.map((v, i) => arr.slice(i + 1).map(w => [v, w])).flat();

      const baseTokens = tokenList
        .filter(function (t) {
          return ["DAI", "USDC", "USDT", "COMP", "ETH", "MKR", "LINK", tokenIn, tokenOut].includes(t.symbol);
        })
        .map(el => {
          return new Token(el.chainId, el.address, el.decimals, el.symbol, el.name);
        });

      const listOfPairwiseTokens = pairs(baseTokens);

      const getPairs = async list => {
        const listOfPromises = list.map(item => Fetcher.fetchPairData(item[0], item[1], selectedProvider));
        return Promise.all(listOfPromises.map(p => p.catch(() => undefined)));
      };

      const listOfPairs = await getPairs(listOfPairwiseTokens);

      let bestTrade;

      if (exact === "in") {
        setAmountInMax();
        bestTrade = Trade.bestTradeExactIn(
          listOfPairs.filter(item => item),
          new TokenAmount(tokens[tokenIn], ethers.utils.parseUnits(amountIn.toString(), tokens[tokenIn].decimals)),
          tokens[tokenOut],
          { maxNumResults: 3, maxHops: 1 },
        );
        if (bestTrade[0]) {
          setAmountOut(bestTrade[0].outputAmount.toSignificant(6));
        } else {
          setAmountOut();
        }
      } else if (exact === "out") {
        setAmountOutMin();
        bestTrade = Trade.bestTradeExactOut(
          listOfPairs.filter(item => item),
          tokens[tokenIn],
          new TokenAmount(tokens[tokenOut], ethers.utils.parseUnits(amountOut.toString(), tokens[tokenOut].decimals)),
          { maxNumResults: 3, maxHops: 1 },
        );
        if (bestTrade[0]) {
          setAmountIn(bestTrade[0].inputAmount.toSignificant(6));
        } else {
          setAmountIn();
        }
      }

      setTrades(bestTrade);

      console.log(bestTrade);
    }
  };

  useEffect(() => {
    getTrades();
  }, [tokenIn, tokenOut, debouncedAmountIn, debouncedAmountOut, slippageTolerance, selectedProvider]);

  useEffect(() => {
    if (trades && trades[0]) {
      if (exact === "in") {
        setAmountOutMin(trades[0].minimumAmountOut(slippageTolerance));
      } else if (exact === "out") {
        setAmountInMax(trades[0].maximumAmountIn(slippageTolerance));
      }
    }
  }, [slippageTolerance, amountIn, amountOut, trades]);

  const getBalance = async (_token, _account, _contract) => {
    let newBalance;
    if (_token === "ETH") {
      newBalance = await selectedProvider.getBalance(_account);
    } else {
      newBalance = await makeCall("balanceOf", _contract, [_account]);
    }
    return newBalance;
  };

  const getAccountInfo = async () => {
    if (tokens) {
      const accountList = await selectedProvider.listAccounts();

      if (tokenIn) {
        const tempContractIn = new ethers.Contract(tokens[tokenIn].address, erc20Abi, selectedProvider);
        const newBalanceIn = await getBalance(tokenIn, accountList[0], tempContractIn);
        setBalanceIn(newBalanceIn);

        let allowance;

        if (tokenIn === "ETH") {
          setRouterAllowance();
        } else {
          allowance = await makeCall("allowance", tempContractIn, [accountList[0], ROUTER_ADDRESS]);
          setRouterAllowance(allowance);
        }
      }

      if (tokenOut) {
        const tempContractOut = new ethers.Contract(tokens[tokenOut].address, erc20Abi, selectedProvider);
        const newBalanceOut = await getBalance(tokenOut, accountList[0], tempContractOut);
        setBalanceOut(newBalanceOut);
      }
    }
  };

  usePoller(getAccountInfo, 6000);

  const route = trades
    ? trades.length > 0
      ? trades[0].route.path.map(function (item) {
          return item.symbol;
        })
      : []
    : [];

  const updateRouterAllowance = async newAllowance => {
    setApproving(true);
    try {
      const tempContract = new ethers.Contract(tokens[tokenIn].address, erc20Abi, signer);
      const result = await makeCall("approve", tempContract, [ROUTER_ADDRESS, newAllowance]);
      console.log(result);
      setApproving(false);
      return true;
    } catch (e) {
      notification.open({
        message: "Approval unsuccessful",
        description: `Error: ${e.message}`,
      });
    }
  };

  const approveRouter = async () => {
    const approvalAmount =
      exact === "in"
        ? ethers.utils.hexlify(ethers.utils.parseUnits(amountIn.toString(), tokens[tokenIn].decimals))
        : amountInMax.raw.toString();
    console.log(approvalAmount);
    const approval = updateRouterAllowance(approvalAmount);
    if (approval) {
      notification.open({
        message: "Token transfer approved",
        description: `You can now swap up to ${amountIn} ${tokenIn}`,
      });
    }
  };

  const removeRouterAllowance = async () => {
    const approvalAmount = ethers.utils.hexlify(0);
    console.log(approvalAmount);
    const removal = updateRouterAllowance(approvalAmount);
    if (removal) {
      notification.open({
        message: "Token approval removed",
        description: `The router is no longer approved for ${tokenIn}`,
      });
    }
  };

  const executeSwap = async () => {
    setSwapping(true);
    try {
      let args;
      const metadata = {};

      let call;
      const deadline = Math.floor(Date.now() / 1000) + timeLimit;
      const path = trades[0].route.path.map(function (item) {
        return item.address;
      });
      console.log(path);
      const accountList = await selectedProvider.listAccounts();
      const address = accountList[0];

      if (exact === "in") {
        const _amountIn = ethers.utils.hexlify(ethers.utils.parseUnits(amountIn.toString(), tokens[tokenIn].decimals));
        const _amountOutMin = ethers.utils.hexlify(ethers.BigNumber.from(amountOutMin.raw.toString()));
        if (tokenIn === "ETH") {
          call = "swapExactETHForTokens";
          args = [_amountOutMin, path, address, deadline];
          metadata.value = _amountIn;
        } else {
          call = tokenOut === "ETH" ? "swapExactTokensForETH" : "swapExactTokensForTokens";
          args = [_amountIn, _amountOutMin, path, address, deadline];
        }
      } else if (exact === "out") {
        const _amountOut = ethers.utils.hexlify(
          ethers.utils.parseUnits(amountOut.toString(), tokens[tokenOut].decimals),
        );
        const _amountInMax = ethers.utils.hexlify(ethers.BigNumber.from(amountInMax.raw.toString()));
        if (tokenIn === "ETH") {
          call = "swapETHForExactTokens";
          args = [_amountOut, path, address, deadline];
          metadata.value = _amountInMax;
        } else {
          call = tokenOut === "ETH" ? "swapTokensForExactETH" : "swapTokensForExactTokens";
          args = [_amountOut, _amountInMax, path, address, deadline];
        }
      }
      console.log(call, args, metadata);
      const result = await makeCall(call, routerContract, args, metadata);
      console.log(result);
      notification.open({
        message: "Swap complete ?",
        description: (
          <>
            <Text>{`Swapped ${tokenIn} for ${tokenOut}, transaction: `}</Text>
            <Text copyable>{result.hash}</Text>
          </>
        ),
      });
      setSwapping(false);
    } catch (e) {
      console.log(e);
      setSwapping(false);
      notification.open({
        message: "Swap unsuccessful",
        description: `Error: ${e.message}`,
      });
    }
  };

  const showSwapModal = () => {
    setSwapModalVisible(true);
  };

  const handleSwapModalOk = () => {
    setSwapModalVisible(false);
    executeSwap();
  };

  const handleSwapModalCancel = () => {
    setSwapModalVisible(false);
  };

  const insufficientBalance = balanceIn
    ? parseFloat(ethers.utils.formatUnits(balanceIn, tokens[tokenIn].decimals)) < amountIn
    : null;
  const inputIsToken = tokenIn !== "ETH";
  const insufficientAllowance = !inputIsToken
    ? false
    : routerAllowance
    ? parseFloat(ethers.utils.formatUnits(routerAllowance, tokens[tokenIn].decimals)) < amountIn
    : null;
  const formattedBalanceIn = balanceIn
    ? parseFloat(ethers.utils.formatUnits(balanceIn, tokens[tokenIn].decimals)).toPrecision(6)
    : null;
  const formattedBalanceOut = balanceOut
    ? parseFloat(ethers.utils.formatUnits(balanceOut, tokens[tokenOut].decimals)).toPrecision(6)
    : null;

  const metaIn =
    tokens && tokenList && tokenIn
      ? tokenList.filter(function (t) {
          return t.address === tokens[tokenIn].address;
        })[0]
      : null;
  const metaOut =
    tokens && tokenList && tokenOut
      ? tokenList.filter(function (t) {
          return t.address === tokens[tokenOut].address;
        })[0]
      : null;

  const cleanIpfsURI = uri => {
    try {
      return uri.replace("ipfs://", "https://ipfs.io/ipfs/");
    } catch (e) {
      console.log(e, uri);
      return uri;
    }
  };

  const logoIn = metaIn ? cleanIpfsURI(metaIn.logoURI) : null;
  const logoOut = metaOut ? cleanIpfsURI(metaOut.logoURI) : null;

  const rawPrice = trades && trades[0] ? trades[0].executionPrice : null;
  const price = rawPrice ? rawPrice.toSignificant(7) : null;
  const priceDescription = rawPrice
    ? invertPrice
      ? `${rawPrice.invert().toSignificant(7)} ${tokenIn} per ${tokenOut}`
      : `${price} ${tokenOut} per ${tokenIn}`
    : null;

  const priceWidget = (
    <Space>
      <Text type="secondary">{priceDescription}</Text>
      <Button
        type="text"
        onClick={() => {
          setInvertPrice(!invertPrice);
        }}
      >
        <RetweetOutlined />
      </Button>
    </Space>
  );

  const swapModal = (
    <Modal title="Confirm swap" visible={swapModalVisible} onOk={handleSwapModalOk} onCancel={handleSwapModalCancel}>
      <Row>
        <Space>
          <img src={logoIn} alt={tokenIn} width="30" />
          {amountIn}
          {tokenIn}
        </Space>
      </Row>
      <Row justify="center" align="middle" style={{ width: 30 }}>
        <span>↓</span>
      </Row>
      <Row>
        <Space>
          <img src={logoOut} alt={tokenOut} width="30" />
          {amountOut}
          {tokenOut}
        </Space>
      </Row>
      <Divider />
      <Row>{priceWidget}</Row>
      <Row>
        {trades && ((amountOutMin && exact === "in") || (amountInMax && exact === "out"))
          ? exact === "in"
            ? `Output is estimated. You will receive at least ${amountOutMin.toSignificant(
                6,
              )} ${tokenOut} or the transaction will revert.`
            : `Input is estimated. You will sell at most ${amountInMax.toSignificant(
                6,
              )} ${tokenIn} or the transaction will revert.`
          : null}
      </Row>
    </Modal>
  );

  return (
    <Card
      title={
        <Space>
          <img src="https://ipfs.io/ipfs/QmXttGpZrECX5qCyXbBQiqgQNytVGeZW5Anewvh2jc4psg" width="40" alt="uniswapLogo" />
          <Typography>Uniswapper</Typography>
        </Space>
      }
      extra={
        <Button
          type="text"
          onClick={() => {
            setSettingsVisible(true);
          }}
        >
          <SettingOutlined />
        </Button>
      }
    >
      <Space direction="vertical">
        <Row justify="center" align="middle">
          <Card
            size="small"
            type="inner"
            title={`From${exact === "out" && tokenIn && tokenOut ? " (estimate)" : ""}`}
            extra={
              <>
                <img src={logoIn} alt={tokenIn} width="30" />
                <Button
                  type="link"
                  onClick={() => {
                    setAmountOut();
                    setAmountIn(ethers.utils.formatUnits(balanceIn, tokens[tokenIn].decimals));
                    setAmountOutMin();
                    setAmountInMax();
                    setExact("in");
                  }}
                >
                  {formattedBalanceIn}
                </Button>
              </>
            }
            style={{ width: 400, textAlign: "left" }}
          >
            <InputNumber
              style={{ width: "160px" }}
              min={0}
              size="large"
              value={amountIn}
              onChange={e => {
                setAmountOut();
                setTrades();
                setAmountIn(e);
                setExact("in");
              }}
            />
            <Select
              showSearch
              value={tokenIn}
              style={{ width: "120px" }}
              size="large"
              bordered={false}
              defaultValue={defaultToken}
              onChange={value => {
                console.log(value);
                if (value === tokenOut) {
                  console.log("switch!", tokenIn);
                  setTokenOut(tokenIn);
                  setAmountOut(amountIn);
                  setBalanceOut(balanceIn);
                }
                setTokenIn(value);
                setTrades();
                setAmountIn();
                setExact("out");
                setBalanceIn();
              }}
              filterOption={(input, option) => option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
              optionFilterProp="children"
            >
              {tokenList.map(token => (
                <Option key={token.symbol} value={token.symbol}>
                  {token.symbol}
                </Option>
              ))}
            </Select>
          </Card>
        </Row>
        <Row justify="center" align="middle">
          <Tooltip title={route.join("->")}>
            <span>↓</span>
          </Tooltip>
        </Row>
        <Row justify="center" align="middle">
          <Card
            size="small"
            type="inner"
            title={`To${exact === "in" && tokenIn && tokenOut ? " (estimate)" : ""}`}
            extra={
              <>
                <img src={logoOut} width="30" alt={tokenOut} />
                <Button type="text">{formattedBalanceOut}</Button>
              </>
            }
            style={{ width: 400, textAlign: "left" }}
          >
            <InputNumber
              style={{ width: "160px" }}
              size="large"
              min={0}
              value={amountOut}
              onChange={e => {
                setAmountOut(e);
                setAmountIn();
                setTrades();
                setExact("out");
              }}
            />
            <Select
              showSearch
              value={tokenOut}
              style={{ width: "120px" }}
              size="large"
              bordered={false}
              onChange={value => {
                console.log(value, tokenIn, tokenOut);
                if (value === tokenIn) {
                  console.log("switch!", tokenOut);
                  setTokenIn(tokenOut);
                  setAmountIn(amountOut);
                  setBalanceIn(balanceOut);
                }
                setTokenOut(value);
                setExact("in");
                setAmountOut();
                setTrades();
                setBalanceOut();
              }}
              filterOption={(input, option) => option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
              optionFilterProp="children"
            >
              {tokenList.map(token => (
                <Option key={token.symbol} value={token.symbol}>
                  {token.symbol}
                </Option>
              ))}
            </Select>
          </Card>
        </Row>
        <Row justify="center" align="middle">
          {priceDescription ? priceWidget : null}
        </Row>
        <Row justify="center" align="middle">
          <Space>
            {inputIsToken ? (
              <Button size="large" loading={approving} disabled={!insufficientAllowance} onClick={approveRouter}>
                {!insufficientAllowance && amountIn && amountOut ? "Approved" : "Approve"}
              </Button>
            ) : null}
            <Button
              size="large"
              loading={swapping}
              disabled={insufficientAllowance || insufficientBalance || !amountIn || !amountOut}
              onClick={showSwapModal}
            >
              {insufficientBalance ? "Insufficient balance" : "Swap!"}
            </Button>
            {swapModal}
          </Space>
        </Row>
      </Space>
      <Drawer
        visible={settingsVisible}
        onClose={() => {
          setSettingsVisible(false);
        }}
        width={500}
      >
        <Descriptions title="Details" column={1} style={{ textAlign: "left" }}>
          <Descriptions.Item label="blockNumber">{blockNumber}</Descriptions.Item>
          <Descriptions.Item label="routerAllowance">
            <Space>
              {routerAllowance ? ethers.utils.formatUnits(routerAllowance, tokens[tokenIn].decimals) : null}
              {routerAllowance > 0 ? <Button onClick={removeRouterAllowance}>Remove Allowance</Button> : null}
            </Space>
          </Descriptions.Item>
          <Descriptions.Item label="route">{route.join("->")}</Descriptions.Item>
          <Descriptions.Item label="exact">{exact}</Descriptions.Item>
          <Descriptions.Item label="bestPrice">
            {trades ? (trades.length > 0 ? trades[0].executionPrice.toSignificant(6) : null) : null}
          </Descriptions.Item>
          <Descriptions.Item label="nextMidPrice">
            {trades ? (trades.length > 0 ? trades[0].nextMidPrice.toSignificant(6) : null) : null}
          </Descriptions.Item>
          <Descriptions.Item label="priceImpact">
            {trades ? (trades.length > 0 ? trades[0].priceImpact.toSignificant(6) : null) : null}
          </Descriptions.Item>
          <Descriptions.Item label="slippageTolerance">
            <InputNumber
              defaultValue={defaultSlippage}
              min={0}
              max={100}
              precision={2}
              formatter={value => `${value}%`}
              parser={value => value.replace("%", "")}
              onChange={value => {
                console.log(value);

                const slippagePercent = new Percent(Math.round(value * 100).toString(), "10000");
                setSlippageTolerance(slippagePercent);
              }}
            />
          </Descriptions.Item>
          <Descriptions.Item label="amountInMax">{amountInMax ? amountInMax.toExact() : null}</Descriptions.Item>
          <Descriptions.Item label="amountOutMin">{amountOutMin ? amountOutMin.toExact() : null}</Descriptions.Item>
          <Descriptions.Item label="timeLimitInSeconds">
            <InputNumber
              min={0}
              max={3600}
              defaultValue={defaultTimeLimit}
              onChange={value => {
                console.log(value);
                setTimeLimit(value);
              }}
            />
          </Descriptions.Item>
        </Descriptions>
      </Drawer>
    </Card>
  );
}
Example #2
Source File: Swap.jsx    From moonshot with MIT License 4 votes vote down vote up
function Swap({ selectedProvider, tokenListURI }) {

  const [tokenIn, setTokenIn] = useState(defaultToken)
  const [tokenOut, setTokenOut] = useState(defaultTokenOut)
  const [exact, setExact] = useState()
  const [amountIn, setAmountIn] = useState()
  const [amountInMax, setAmountInMax] = useState()
  const [amountOut, setAmountOut] = useState()
  const [amountOutMin, setAmountOutMin] = useState()
  const [trades, setTrades] = useState()
  const [routerAllowance, setRouterAllowance] = useState()
  const [balanceIn, setBalanceIn] = useState()
  const [balanceOut, setBalanceOut] = useState()
  const [slippageTolerance, setSlippageTolerance] = useState(new Percent(Math.round(defaultSlippage*100).toString(), "10000"))
  const [timeLimit, setTimeLimit] = useState(defaultTimeLimit)
  const [swapping, setSwapping] = useState(false)
  const [approving, setApproving] = useState(false)
  const [settingsVisible, setSettingsVisible] = useState(false)
  const [swapModalVisible, setSwapModalVisible] = useState(false)

  const [tokenList, setTokenList] = useState([])

  const [tokens, setTokens] = useState()

  const [invertPrice, setInvertPrice] = useState(false)

  let blockNumber = useBlockNumber(selectedProvider, 3000)

  let signer = selectedProvider.getSigner()
  let routerContract = new ethers.Contract(ROUTER_ADDRESS, IUniswapV2Router02ABI, signer);

  let _tokenListUri = tokenListURI ? tokenListURI : 'https://gateway.ipfs.io/ipns/tokens.uniswap.org'

  const debouncedAmountIn = useDebounce(amountIn, 500);
  const debouncedAmountOut = useDebounce(amountOut, 500);

  const activeChainId = (process.env.REACT_APP_NETWORK === 'kovan' ? ChainId.KOVAN : ChainId.MAINNET)

  useEffect(() => {
    const getTokenList = async () => {
      console.log(_tokenListUri)
      try {
      let tokenList = await fetch(_tokenListUri)
      let tokenListJson = await tokenList.json()
      let filteredTokens = tokenListJson.tokens.filter(function (t) {
        return t.chainId === activeChainId
      })
      let ethToken = WETH[activeChainId]
      ethToken.name = 'Ethereum'
      ethToken.symbol = 'ETH'
      ethToken.logoURI = "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png"
      let _tokenList = [ethToken, ...filteredTokens]
      setTokenList(_tokenList)
      let _tokens = tokenListToObject(_tokenList)
      setTokens(_tokens)
    } catch (e) {
      console.log(e)
    }
    }
    getTokenList()
  },[tokenListURI])

  const getTrades = async () => {
    if(tokenIn && tokenOut && (amountIn || amountOut)) {

    let pairs = (arr) => arr.map( (v, i) => arr.slice(i + 1).map(w => [v,w]) ).flat();

    let baseTokens = tokenList.filter(function (t) {
      return ['DAI', 'USDC', 'USDT', 'COMP', 'ETH', 'MKR', 'LINK', tokenIn, tokenOut].includes(t.symbol)
    }).map((el) => {
      return new Token(el.chainId, el.address, el.decimals, el.symbol, el.name)
    })

    let listOfPairwiseTokens = pairs(baseTokens)

    const getPairs = async (list) => {

      let listOfPromises = list.map(item => Fetcher.fetchPairData(item[0], item[1], selectedProvider))
      return Promise.all(listOfPromises.map(p => p.catch(() => undefined)));
    }

    let listOfPairs = await getPairs(listOfPairwiseTokens)

    let bestTrade

    if(exact === 'in') {
      setAmountInMax()
      bestTrade = Trade.bestTradeExactIn(
      listOfPairs.filter(item => item),
      new TokenAmount(tokens[tokenIn], parseUnits(amountIn.toString(), tokens[tokenIn].decimals)),
      tokens[tokenOut], { maxNumResults: 3, maxHops: 1 })
      if(bestTrade[0]) {
        setAmountOut(bestTrade[0].outputAmount.toSignificant(6))
      } else { setAmountOut() }
    } else if (exact === 'out') {
      setAmountOutMin()
      bestTrade = Trade.bestTradeExactOut(
      listOfPairs.filter(item => item),
      tokens[tokenIn],
      new TokenAmount(tokens[tokenOut], parseUnits(amountOut.toString(), tokens[tokenOut].decimals)),
     { maxNumResults: 3, maxHops: 1 })
      if(bestTrade[0]) {
        setAmountIn(bestTrade[0].inputAmount.toSignificant(6))
      } else { setAmountIn() }
    }

    setTrades(bestTrade)

    console.log(bestTrade)

  }
  }

  useEffect(() => {
      getTrades()
  },[tokenIn, tokenOut, debouncedAmountIn, debouncedAmountOut, slippageTolerance, selectedProvider])

  useEffect(() => {
    if(trades && trades[0]) {
      if(exact === 'in') {
        setAmountOutMin(trades[0].minimumAmountOut(slippageTolerance))
      } else if (exact === 'out') {
        setAmountInMax(trades[0].maximumAmountIn(slippageTolerance))
      }
    }
  }, [slippageTolerance, amountIn, amountOut, trades])

  const getBalance = async (_token, _account, _contract) => {

    let newBalance
    if(_token === 'ETH') {
      newBalance = await selectedProvider.getBalance(_account)
    } else {
      newBalance = await makeCall('balanceOf', _contract, [_account])
    }
    return newBalance
  }

  const getAccountInfo = async () => {

    if(tokens) {

      let accountList = await selectedProvider.listAccounts()

      if(tokenIn) {
        let tempContractIn = new ethers.Contract(tokens[tokenIn].address, erc20Abi, selectedProvider);
        let newBalanceIn = await getBalance(tokenIn, accountList[0], tempContractIn)
        setBalanceIn(newBalanceIn)

        let allowance

        if(tokenIn === 'ETH') {
          setRouterAllowance()
        } else {
          allowance = await makeCall('allowance',tempContractIn,[accountList[0],ROUTER_ADDRESS])
          setRouterAllowance(allowance)
        }
        }

      if(tokenOut) {
        let tempContractOut = new ethers.Contract(tokens[tokenOut].address, erc20Abi, selectedProvider);
        let newBalanceOut = await getBalance(tokenOut, accountList[0], tempContractOut)
        setBalanceOut(newBalanceOut)
      }
    }
  }

  usePoller(getAccountInfo, 6000)

  let route = trades ? (trades.length > 0 ? trades[0].route.path.map(function(item) {
    return item['symbol'];
  }) : []) : []

  const updateRouterAllowance = async (newAllowance) => {
    setApproving(true)
    try {
    let tempContract = new ethers.Contract(tokens[tokenIn].address, erc20Abi, signer);
    let result = await makeCall('approve', tempContract, [ROUTER_ADDRESS, newAllowance])
    console.log(result)
    setApproving(false)
    return true
  } catch(e) {
      notification.open({
        message: 'Approval unsuccessful',
        description:
        `Error: ${e.message}`,
      });
    }
  }

  const approveRouter = async () => {
    let approvalAmount = exact === 'in' ? ethers.utils.hexlify(parseUnits(amountIn.toString(), tokens[tokenIn].decimals)) : amountInMax.raw.toString()
    console.log(approvalAmount)
    let approval = updateRouterAllowance(approvalAmount)
    if(approval) {
      notification.open({
        message: 'Token transfer approved',
        description:
        `You can now swap up to ${amountIn} ${tokenIn}`,
      });
    }
  }

  const removeRouterAllowance = async () => {
    let approvalAmount = ethers.utils.hexlify(0)
    console.log(approvalAmount)
    let removal = updateRouterAllowance(approvalAmount)
    if(removal) {
      notification.open({
        message: 'Token approval removed',
        description:
        `The router is no longer approved for ${tokenIn}`,
      });
    }
  }

  const executeSwap = async () => {
    setSwapping(true)
    try {
      let args
      let metadata = {}

      let call
      let deadline = Math.floor(Date.now() / 1000) + timeLimit
      let path = trades[0].route.path.map(function(item) {
        return item['address'];
      })
      console.log(path)
      let accountList = await selectedProvider.listAccounts()
      let address = accountList[0]

      if (exact === 'in') {
        let _amountIn = ethers.utils.hexlify(parseUnits(amountIn.toString(), tokens[tokenIn].decimals))
        let _amountOutMin = ethers.utils.hexlify(ethers.BigNumber.from(amountOutMin.raw.toString()))
        if (tokenIn === 'ETH') {
          call = 'swapExactETHForTokens'
          args = [_amountOutMin, path, address, deadline]
          metadata['value'] = _amountIn
        } else {
          call = tokenOut === 'ETH' ? 'swapExactTokensForETH' : 'swapExactTokensForTokens'
          args = [_amountIn, _amountOutMin, path, address, deadline]
        }
      } else if (exact === 'out') {
        let _amountOut = ethers.utils.hexlify(parseUnits(amountOut.toString(), tokens[tokenOut].decimals))
        let _amountInMax = ethers.utils.hexlify(ethers.BigNumber.from(amountInMax.raw.toString()))
        if (tokenIn === 'ETH') {
          call = 'swapETHForExactTokens'
          args = [_amountOut, path, address, deadline]
          metadata['value'] = _amountInMax
        } else {
          call = tokenOut === 'ETH' ? 'swapTokensForExactETH' : 'swapTokensForExactTokens'
          args = [_amountOut, _amountInMax, path, address, deadline]
        }
      }
      console.log(call, args, metadata)
      let result = await makeCall(call, routerContract, args, metadata)
      console.log(result)
      notification.open({
        message: 'Swap complete ?',
        description:
        <><Text>{`Swapped ${tokenIn} for ${tokenOut}, transaction: `}</Text><Text copyable>{result.hash}</Text></>,
      });
      setSwapping(false)
  } catch (e) {
    console.log(e)
    setSwapping(false)
    notification.open({
      message: 'Swap unsuccessful',
      description:
      `Error: ${e.message}`,
    });
  }
  }

  const showSwapModal = () => {
    setSwapModalVisible(true);
  };

  const handleSwapModalOk = () => {
    setSwapModalVisible(false);
    executeSwap()
  };

  const handleSwapModalCancel = () => {
    setSwapModalVisible(false);
  };

  let insufficientBalance = balanceIn ? parseFloat(formatUnits(balanceIn,tokens[tokenIn].decimals)) < amountIn : null
  let inputIsToken = tokenIn !== 'ETH'
  let insufficientAllowance = !inputIsToken ? false : routerAllowance ? parseFloat(formatUnits(routerAllowance,tokens[tokenIn].decimals)) < amountIn : null
  let formattedBalanceIn = balanceIn?parseFloat(formatUnits(balanceIn,tokens[tokenIn].decimals)).toPrecision(6):null
  let formattedBalanceOut = balanceOut?parseFloat(formatUnits(balanceOut,tokens[tokenOut].decimals)).toPrecision(6):null

  let metaIn = tokens && tokenList && tokenIn ? tokenList.filter(function (t) {
    return t.address === tokens[tokenIn].address
  })[0] : null
  let metaOut = tokens && tokenList && tokenOut ? tokenList.filter(function (t) {
    return t.address === tokens[tokenOut].address
    })[0] : null

  const cleanIpfsURI = (uri) => {
    try {
    return (uri).replace('ipfs://','https://ipfs.io/ipfs/')
  } catch(e) {
    console.log(e, uri)
    return uri
  }
  }

  let logoIn = metaIn?cleanIpfsURI(metaIn.logoURI):null
  let logoOut = metaOut?cleanIpfsURI(metaOut.logoURI):null

  let rawPrice = trades&&trades[0]?trades[0].executionPrice:null
  let price = rawPrice?rawPrice.toSignificant(7):null
  let priceDescription = rawPrice ? (invertPrice ? `${(rawPrice.invert()).toSignificant(7)} ${tokenIn} per ${tokenOut}` : `${price} ${tokenOut} per ${tokenIn}`) : null

  let priceWidget = (
    <Space>
    <Text type="secondary">{priceDescription}</Text>
    <Button type="text" onClick={() => {setInvertPrice(!invertPrice)}}><RetweetOutlined /></Button>
    </Space>
  )

  let swapModal = (
    <Modal title="Confirm swap" visible={swapModalVisible} onOk={handleSwapModalOk} onCancel={handleSwapModalCancel}>
      <Row><Space><img src={logoIn} alt={tokenIn} width='30'/>{amountIn}{tokenIn}</Space></Row>
      <Row justify='center' align='middle' style={{width:30}}><span>↓</span></Row>
      <Row><Space><img src={logoOut} alt={tokenOut} width='30'/>{amountOut}{tokenOut}</Space></Row>
      <Divider/>
      <Row>{priceWidget}</Row>
      <Row>{trades&&((amountOutMin && exact==='in') || (amountInMax && exact==='out'))?(exact==='in'?`Output is estimated. You will receive at least ${amountOutMin.toSignificant(6)} ${tokenOut} or the transaction will revert.`:`Input is estimated. You will sell at most ${amountInMax.toSignificant(6)} ${tokenIn} or the transaction will revert.`):null}</Row>
    </Modal>
  )

  return (
    <Card title={<Space><img src="https://ipfs.io/ipfs/QmXttGpZrECX5qCyXbBQiqgQNytVGeZW5Anewvh2jc4psg" width='40' alt='uniswapLogo'/><Typography>Uniswapper</Typography></Space>} extra={<Button type="text" onClick={() => {setSettingsVisible(true)}}><SettingOutlined /></Button>}>
    <Space direction="vertical">
    <Row justify="center" align="middle">
    <Card size="small" type="inner" title={`From${exact==='out' && tokenIn && tokenOut?' (estimate)':''}`} extra={<><img src={logoIn} alt={tokenIn} width='30'/><Button type="link" onClick={() => {
      setAmountOut()
      setAmountIn(formatUnits(balanceIn,tokens[tokenIn].decimals))
      setAmountOutMin()
      setAmountInMax()
      setExact('in')
    }}>{formattedBalanceIn}</Button></>} style={{ width: 400, textAlign: 'left' }}>
      <InputNumber style={{width: '160px'}} min={0} size={'large'} value={amountIn} onChange={(e) => {
        setAmountOut()
        setTrades()
        setAmountIn(e)
        setExact('in')
      }}/>
      <Select showSearch value={tokenIn} style={{width: '120px'}} size={'large'} bordered={false} defaultValue={defaultToken} onChange={(value) => {
        console.log(value)
        if(value===tokenOut) {
          console.log('switch!', tokenIn)
          setTokenOut(tokenIn)
          setAmountOut(amountIn)
          setBalanceOut(balanceIn)
        }
        setTokenIn(value)
        setTrades()
        setAmountIn()
        setExact('out')
        setBalanceIn()
      }} filterOption={(input, option) =>
      option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
      } optionFilterProp="children">
      {tokenList.map(token => (
        <Option key={token.symbol} value={token.symbol}>{token.symbol}</Option>
      ))}
      </Select>
    </Card>
    </Row>
    <Row justify="center" align="middle">
      <Tooltip title={route.join("->")}><span>↓</span></Tooltip>
    </Row>
    <Row justify="center" align="middle">
    <Card size="small" type="inner" title={`To${exact==='in' && tokenIn && tokenOut?' (estimate)':''}`} extra={<><img src={logoOut} width='30' alt={tokenOut}/><Button type="text">{formattedBalanceOut}</Button></>} style={{ width: 400, textAlign: 'left' }}>
      <InputNumber style={{width: '160px'}} size={'large'} min={0} value={amountOut} onChange={(e) => {
        setAmountOut(e)
        setAmountIn()
        setTrades()
        setExact('out')
      }}/>
      <Select showSearch value={tokenOut} style={{width: '120px'}} size={'large'} bordered={false} onChange={(value) => {
        console.log(value, tokenIn, tokenOut)
        if(value===tokenIn) {
          console.log('switch!', tokenOut)
          setTokenIn(tokenOut)
          setAmountIn(amountOut)
          setBalanceIn(balanceOut)
        }
        setTokenOut(value)
        setExact('in')
        setAmountOut()
        setTrades()
        setBalanceOut()
      }} filterOption={(input, option) =>
      option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
      } optionFilterProp="children">
      {tokenList.map(token => (
        <Option key={token.symbol} value={token.symbol}>{token.symbol}</Option>
      ))}
      </Select>
    </Card>
    </Row>
    <Row justify="center" align="middle">
      {priceDescription?priceWidget:null}
    </Row>
    <Row justify="center" align="middle">
    <Space>
      {inputIsToken?<Button size="large" loading={approving} disabled={!insufficientAllowance} onClick={approveRouter}>{(!insufficientAllowance&&amountIn&&amountOut)?'Approved':'Approve'}</Button>:null}
      <Button size="large" loading={swapping} disabled={insufficientAllowance || insufficientBalance || !amountIn || !amountOut} onClick={showSwapModal}>{insufficientBalance?'Insufficient balance':'Swap!'}</Button>
      {swapModal}
    </Space>
    </Row>
    </Space>
    <Drawer visible={settingsVisible} onClose={() => { setSettingsVisible(false) }} width={500}>
    <Descriptions title="Details" column={1} style={{textAlign: 'left'}}>
      <Descriptions.Item label="blockNumber">{blockNumber}</Descriptions.Item>
      <Descriptions.Item label="routerAllowance"><Space>{routerAllowance?formatUnits(routerAllowance,tokens[tokenIn].decimals):null}{routerAllowance>0?<Button onClick={removeRouterAllowance}>Remove Allowance</Button>:null}</Space></Descriptions.Item>
      <Descriptions.Item label="route">{route.join("->")}</Descriptions.Item>
      <Descriptions.Item label="exact">{exact}</Descriptions.Item>
      <Descriptions.Item label="bestPrice">{trades ? (trades.length > 0 ? trades[0].executionPrice.toSignificant(6) : null) : null}</Descriptions.Item>
      <Descriptions.Item label="nextMidPrice">{trades ? (trades.length > 0 ? trades[0].nextMidPrice.toSignificant(6) : null) : null}</Descriptions.Item>
      <Descriptions.Item label="priceImpact">{trades ? (trades.length > 0 ? trades[0].priceImpact.toSignificant(6) : null) : null}</Descriptions.Item>
      <Descriptions.Item label="slippageTolerance">{<InputNumber
        defaultValue={defaultSlippage}
        min={0}
        max={100}
        precision={2}
        formatter={value => `${value}%`}
        parser={value => value.replace('%', '')}
        onChange={(value) => {
          console.log(value)

         let slippagePercent = new Percent(Math.round(value*100).toString(), "10000")
         setSlippageTolerance(slippagePercent)
       }}
      />}</Descriptions.Item>
      <Descriptions.Item label="amountInMax">{amountInMax?amountInMax.toExact():null}</Descriptions.Item>
      <Descriptions.Item label="amountOutMin">{amountOutMin?amountOutMin.toExact():null}</Descriptions.Item>
      <Descriptions.Item label="timeLimitInSeconds">{<InputNumber
              min={0}
              max={3600}
              defaultValue={defaultTimeLimit}
              onChange={(value) => {
              console.log(value)
              setTimeLimit(value)
             }}
            />}</Descriptions.Item>
    </Descriptions>
    </Drawer>
    </Card>
  )

}
Example #3
Source File: index.js    From gobench with Apache License 2.0 4 votes vote down vote up
DefaultPage = ({ list, loading, total, dispatch }) => {
  const history = useHistory()
  const [fetching, setFetching] = useState(false)
  const [pagination, setPagination] = useState({
    current: 1,
    pageSize: 10,
    total: 0,
    showTotal: (total, range) => `${range[0]}-${range[1]} of ${total} scenarios`
  })
  const columns = [
    {
      title: 'ID',
      dataIndex: 'id',
      key: 'id',
      sorter: (a, b) => a.name.length - b.name.length
    },
    {
      title: 'Status',
      dataIndex: 'status',
      key: 'status',
      sorter: (a, b) => a.name.length - b.name.length,
      render: (text, item) => (
        <>
          <Link key={item.id} to={`/applications/${item.id}`}>
            <Tag color={statusColors[text]} key={item.id}>
              {text.toUpperCase()}
            </Tag>
          </Link>
        </>
      )
    },
    {
      title: 'Name',
      dataIndex: 'name',
      key: 'name',
      sorter: (a, b) => a.name.length - b.name.length,
      render: (text, item) => <Link key={item.id} to={`/applications/${item.id}`}>{text}</Link>
    },
    {
      title: 'Tags',
      dataIndex: 'tags',
      key: 'tags',
      render: (text) => {
        return (
          <>
            {formatTag(text).map(x => (
              <Tag color={x.color} key={x.index}>
                {x.text}
              </Tag>

            ))}
          </>
        )
      }
    },
    {
      title: 'Started at',
      dataIndex: 'started_at',
      key: 'started_at',
      sorter: (a, b) => a.name.length - b.name.length,
      render: x => {
        return moment(x).utc().format()
      }
    },
    {
      title: 'Duration',
      dataIndex: 'duration',
      key: 'duration',
      render: (x, item) => {
        const { started_at: startedAt, updated_at: updated } = item
        const start = moment(startedAt).utc()
        if (['provisioning', 'pending', 'error'].includes(item.status)) {
          return <span />
        }
        if (['finished', 'cancel'].includes(item.status)) {
          const end = moment(updated).utc()
          const diff = end.diff(start)
          const duration = moment.utc(diff).format('HH:mm:ss.SSS')
          return <span>{duration}</span>
        }
        const diff = moment.utc().diff(start)
        const duration = moment.utc(diff).format('HH:mm:ss.SSS')
        return <span>{duration}</span>
      }
    },
    {
      title: 'Action',
      dataIndex: 'action',
      key: 'action',
      render: (x, application) => {
        return (
          <div style={{ float: 'right' }} key={application.id}>
            <Button
              style={{ marginLeft: 5 }}
              type='default'
              onClick={() => clone(application)}
            >
              Clone
            </Button>
            {['running', 'pending'].includes(application.status) && (
              <Popconfirm
                title={`Are you sure cancel application ${application.name}?`}
                onConfirm={() => cancel(application.id)}
                okText='Yes'
                cancelText='No'
              >
                <Button
                  type='dashed'
                  style={{ marginLeft: 5 }}
                  danger
                >
                  Cancel
                </Button>
              </Popconfirm>
            )}
            {['finished', 'pending', 'error', 'cancel'].includes(application.status) && (
              <Popconfirm
                title={`Are you sure delete application ${application.name}?`}
                onConfirm={() => destroy(application.id)}
                okText='Yes'
                cancelText='No'
              >
                <Button
                  type='primary'
                  className='delete-button'
                  style={{ marginLeft: 5, color: 'white', backgroundColor: '#f5222d!important' }}
                  danger
                >
                  Delete
                </Button>
              </Popconfirm>
            )}
          </div>
        )
      }
    }
  ]
  useEffect(() => {
    if (!fetching) {
      dispatch({
        type: 'application/LIST',
        payload: { offset: (pagination.current - 1) * pagination.pageSize, limit: pagination.pageSize }
      })
      setFetching(true)
    }
  }, [list, total])
  useEffect(() => {
    if (total > 0 && total !== pagination.total) {
      setPagination({ ...pagination, total })
    }
  }, [total])
  const onTableChange = (pagination, filters, sorter, extra) => {
    setPagination(pagination)
    dispatch({
      type: 'application/LIST',
      payload: { offset: (pagination.current - 1) * pagination.pageSize, limit: pagination.pageSize, pagination, filters, sorter }
    })
  }
  const onSearch = (keyword) => {
    dispatch({
      type: 'application/LIST',
      payload: { offset: 0, limit: pagination.pageSize, keyword }
    })
  }
  const clone = (data) => {
    dispatch({
      type: 'application/CLONE',
      payload: { data }
    })
  }
  const cancel = (id) => {
    dispatch({
      type: 'application/CANCEL',
      payload: { id }
    })
  }
  const destroy = (id) => {
    dispatch({
      type: 'application/DELETE',
      payload: { id }
    })
  }
  return (
    <>
      <div className='application'>
        <Helmet title='Applications' />

        <div className='card'>
          <div className='card-header row'>
            <div className='col-md-6'>
              <div className='cui__utils__heading mb-0'>
                <h2>Benchmark Application</h2>
              </div>
              <div className='text-muted'>A distributed benchmark tool with Golang</div>
            </div>
            <div className='col-md-6'>
              <div className='text-right'>
                <Tooltip title='Refresh'>
                  <Button
                    icon={<RetweetOutlined />}
                    style={{ marginRight: 5 }}
                    onClick={onSearch}
                  />
                </Tooltip>
                <Button type='primary' onClick={() => history.push('/applications/create')}>Create Application</Button>
              </div>
            </div>
          </div>
          <div className='card-body'>
            <div className='application-header'>
              <div className='search-bar row'>
                <div className='col-md-6 col-xs-12' />
                <div className='col-md-6 col-xs-12'>
                  <Search
                    placeholder='input application name or tags'
                    enterButton='Search'
                    onSearch={value => onSearch(value)}
                  />
                </div>
              </div>
            </div>
            <Table
              dataSource={list}
              pagination={pagination}
              loading={loading}
              columns={columns}
              onChange={onTableChange}
              ellipsis
            />
          </div>
        </div>
      </div>
    </>
  )
}
Example #4
Source File: Swap.jsx    From nft-e2e-example with MIT License 4 votes vote down vote up
function Swap({ selectedProvider, tokenListURI }) {
  const [tokenIn, setTokenIn] = useState(defaultToken);
  const [tokenOut, setTokenOut] = useState(defaultTokenOut);
  const [exact, setExact] = useState();
  const [amountIn, setAmountIn] = useState();
  const [amountInMax, setAmountInMax] = useState();
  const [amountOut, setAmountOut] = useState();
  const [amountOutMin, setAmountOutMin] = useState();
  const [trades, setTrades] = useState();
  const [routerAllowance, setRouterAllowance] = useState();
  const [balanceIn, setBalanceIn] = useState();
  const [balanceOut, setBalanceOut] = useState();
  const [slippageTolerance, setSlippageTolerance] = useState(
    new Percent(Math.round(defaultSlippage * 100).toString(), "10000"),
  );
  const [timeLimit, setTimeLimit] = useState(defaultTimeLimit);
  const [swapping, setSwapping] = useState(false);
  const [approving, setApproving] = useState(false);
  const [settingsVisible, setSettingsVisible] = useState(false);
  const [swapModalVisible, setSwapModalVisible] = useState(false);

  const [tokenList, setTokenList] = useState([]);

  const [tokens, setTokens] = useState();

  const [invertPrice, setInvertPrice] = useState(false);

  const blockNumber = useBlockNumber(selectedProvider, 3000);

  const signer = selectedProvider.getSigner();
  const routerContract = new ethers.Contract(ROUTER_ADDRESS, IUniswapV2Router02ABI, signer);

  const _tokenListUri = tokenListURI || "https://gateway.ipfs.io/ipns/tokens.uniswap.org";

  const debouncedAmountIn = useDebounce(amountIn, 500);
  const debouncedAmountOut = useDebounce(amountOut, 500);

  const activeChainId = process.env.REACT_APP_NETWORK === "kovan" ? ChainId.KOVAN : ChainId.MAINNET;

  useEffect(() => {
    const getTokenList = async () => {
      console.log(_tokenListUri);
      try {
        const tokenList = await fetch(_tokenListUri);
        const tokenListJson = await tokenList.json();
        const filteredTokens = tokenListJson.tokens.filter(function (t) {
          return t.chainId === activeChainId;
        });
        const ethToken = WETH[activeChainId];
        ethToken.name = "Ethereum";
        ethToken.symbol = "ETH";
        ethToken.logoURI =
          "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png";
        const _tokenList = [ethToken, ...filteredTokens];
        setTokenList(_tokenList);
        const _tokens = tokenListToObject(_tokenList);
        setTokens(_tokens);
      } catch (e) {
        console.log(e);
      }
    };
    getTokenList();
  }, [tokenListURI]);

  const getTrades = async () => {
    if (tokenIn && tokenOut && (amountIn || amountOut)) {
      const pairs = arr => arr.map((v, i) => arr.slice(i + 1).map(w => [v, w])).flat();

      const baseTokens = tokenList
        .filter(function (t) {
          return ["DAI", "USDC", "USDT", "COMP", "ETH", "MKR", "LINK", tokenIn, tokenOut].includes(t.symbol);
        })
        .map(el => {
          return new Token(el.chainId, el.address, el.decimals, el.symbol, el.name);
        });

      const listOfPairwiseTokens = pairs(baseTokens);

      const getPairs = async list => {
        const listOfPromises = list.map(item => Fetcher.fetchPairData(item[0], item[1], selectedProvider));
        return Promise.all(listOfPromises.map(p => p.catch(() => undefined)));
      };

      const listOfPairs = await getPairs(listOfPairwiseTokens);

      let bestTrade;

      if (exact === "in") {
        setAmountInMax();
        bestTrade = Trade.bestTradeExactIn(
          listOfPairs.filter(item => item),
          new TokenAmount(tokens[tokenIn], parseUnits(amountIn.toString(), tokens[tokenIn].decimals)),
          tokens[tokenOut],
          { maxNumResults: 3, maxHops: 1 },
        );
        if (bestTrade[0]) {
          setAmountOut(bestTrade[0].outputAmount.toSignificant(6));
        } else {
          setAmountOut();
        }
      } else if (exact === "out") {
        setAmountOutMin();
        bestTrade = Trade.bestTradeExactOut(
          listOfPairs.filter(item => item),
          tokens[tokenIn],
          new TokenAmount(tokens[tokenOut], parseUnits(amountOut.toString(), tokens[tokenOut].decimals)),
          { maxNumResults: 3, maxHops: 1 },
        );
        if (bestTrade[0]) {
          setAmountIn(bestTrade[0].inputAmount.toSignificant(6));
        } else {
          setAmountIn();
        }
      }

      setTrades(bestTrade);

      console.log(bestTrade);
    }
  };

  useEffect(() => {
    getTrades();
  }, [tokenIn, tokenOut, debouncedAmountIn, debouncedAmountOut, slippageTolerance, selectedProvider]);

  useEffect(() => {
    if (trades && trades[0]) {
      if (exact === "in") {
        setAmountOutMin(trades[0].minimumAmountOut(slippageTolerance));
      } else if (exact === "out") {
        setAmountInMax(trades[0].maximumAmountIn(slippageTolerance));
      }
    }
  }, [slippageTolerance, amountIn, amountOut, trades]);

  const getBalance = async (_token, _account, _contract) => {
    let newBalance;
    if (_token === "ETH") {
      newBalance = await selectedProvider.getBalance(_account);
    } else {
      newBalance = await makeCall("balanceOf", _contract, [_account]);
    }
    return newBalance;
  };

  const getAccountInfo = async () => {
    if (tokens) {
      const accountList = await selectedProvider.listAccounts();

      if (tokenIn) {
        const tempContractIn = new ethers.Contract(tokens[tokenIn].address, erc20Abi, selectedProvider);
        const newBalanceIn = await getBalance(tokenIn, accountList[0], tempContractIn);
        setBalanceIn(newBalanceIn);

        let allowance;

        if (tokenIn === "ETH") {
          setRouterAllowance();
        } else {
          allowance = await makeCall("allowance", tempContractIn, [accountList[0], ROUTER_ADDRESS]);
          setRouterAllowance(allowance);
        }
      }

      if (tokenOut) {
        const tempContractOut = new ethers.Contract(tokens[tokenOut].address, erc20Abi, selectedProvider);
        const newBalanceOut = await getBalance(tokenOut, accountList[0], tempContractOut);
        setBalanceOut(newBalanceOut);
      }
    }
  };

  usePoller(getAccountInfo, 6000);

  const route = trades
    ? trades.length > 0
      ? trades[0].route.path.map(function (item) {
          return item.symbol;
        })
      : []
    : [];

  const updateRouterAllowance = async newAllowance => {
    setApproving(true);
    try {
      const tempContract = new ethers.Contract(tokens[tokenIn].address, erc20Abi, signer);
      const result = await makeCall("approve", tempContract, [ROUTER_ADDRESS, newAllowance]);
      console.log(result);
      setApproving(false);
      return true;
    } catch (e) {
      notification.open({
        message: "Approval unsuccessful",
        description: `Error: ${e.message}`,
      });
    }
  };

  const approveRouter = async () => {
    const approvalAmount =
      exact === "in"
        ? ethers.utils.hexlify(parseUnits(amountIn.toString(), tokens[tokenIn].decimals))
        : amountInMax.raw.toString();
    console.log(approvalAmount);
    const approval = updateRouterAllowance(approvalAmount);
    if (approval) {
      notification.open({
        message: "Token transfer approved",
        description: `You can now swap up to ${amountIn} ${tokenIn}`,
      });
    }
  };

  const removeRouterAllowance = async () => {
    const approvalAmount = ethers.utils.hexlify(0);
    console.log(approvalAmount);
    const removal = updateRouterAllowance(approvalAmount);
    if (removal) {
      notification.open({
        message: "Token approval removed",
        description: `The router is no longer approved for ${tokenIn}`,
      });
    }
  };

  const executeSwap = async () => {
    setSwapping(true);
    try {
      let args;
      const metadata = {};

      let call;
      const deadline = Math.floor(Date.now() / 1000) + timeLimit;
      const path = trades[0].route.path.map(function (item) {
        return item.address;
      });
      console.log(path);
      const accountList = await selectedProvider.listAccounts();
      const address = accountList[0];

      if (exact === "in") {
        const _amountIn = ethers.utils.hexlify(parseUnits(amountIn.toString(), tokens[tokenIn].decimals));
        const _amountOutMin = ethers.utils.hexlify(ethers.BigNumber.from(amountOutMin.raw.toString()));
        if (tokenIn === "ETH") {
          call = "swapExactETHForTokens";
          args = [_amountOutMin, path, address, deadline];
          metadata.value = _amountIn;
        } else {
          call = tokenOut === "ETH" ? "swapExactTokensForETH" : "swapExactTokensForTokens";
          args = [_amountIn, _amountOutMin, path, address, deadline];
        }
      } else if (exact === "out") {
        const _amountOut = ethers.utils.hexlify(parseUnits(amountOut.toString(), tokens[tokenOut].decimals));
        const _amountInMax = ethers.utils.hexlify(ethers.BigNumber.from(amountInMax.raw.toString()));
        if (tokenIn === "ETH") {
          call = "swapETHForExactTokens";
          args = [_amountOut, path, address, deadline];
          metadata.value = _amountInMax;
        } else {
          call = tokenOut === "ETH" ? "swapTokensForExactETH" : "swapTokensForExactTokens";
          args = [_amountOut, _amountInMax, path, address, deadline];
        }
      }
      console.log(call, args, metadata);
      const result = await makeCall(call, routerContract, args, metadata);
      console.log(result);
      notification.open({
        message: "Swap complete ?",
        description: (
          <>
            <Text>{`Swapped ${tokenIn} for ${tokenOut}, transaction: `}</Text>
            <Text copyable>{result.hash}</Text>
          </>
        ),
      });
      setSwapping(false);
    } catch (e) {
      console.log(e);
      setSwapping(false);
      notification.open({
        message: "Swap unsuccessful",
        description: `Error: ${e.message}`,
      });
    }
  };

  const showSwapModal = () => {
    setSwapModalVisible(true);
  };

  const handleSwapModalOk = () => {
    setSwapModalVisible(false);
    executeSwap();
  };

  const handleSwapModalCancel = () => {
    setSwapModalVisible(false);
  };

  const insufficientBalance = balanceIn
    ? parseFloat(formatUnits(balanceIn, tokens[tokenIn].decimals)) < amountIn
    : null;
  const inputIsToken = tokenIn !== "ETH";
  const insufficientAllowance = !inputIsToken
    ? false
    : routerAllowance
    ? parseFloat(formatUnits(routerAllowance, tokens[tokenIn].decimals)) < amountIn
    : null;
  const formattedBalanceIn = balanceIn
    ? parseFloat(formatUnits(balanceIn, tokens[tokenIn].decimals)).toPrecision(6)
    : null;
  const formattedBalanceOut = balanceOut
    ? parseFloat(formatUnits(balanceOut, tokens[tokenOut].decimals)).toPrecision(6)
    : null;

  const metaIn =
    tokens && tokenList && tokenIn
      ? tokenList.filter(function (t) {
          return t.address === tokens[tokenIn].address;
        })[0]
      : null;
  const metaOut =
    tokens && tokenList && tokenOut
      ? tokenList.filter(function (t) {
          return t.address === tokens[tokenOut].address;
        })[0]
      : null;

  const cleanIpfsURI = uri => {
    try {
      return uri.replace("ipfs://", "https://ipfs.io/ipfs/");
    } catch (e) {
      console.log(e, uri);
      return uri;
    }
  };

  const logoIn = metaIn ? cleanIpfsURI(metaIn.logoURI) : null;
  const logoOut = metaOut ? cleanIpfsURI(metaOut.logoURI) : null;

  const rawPrice = trades && trades[0] ? trades[0].executionPrice : null;
  const price = rawPrice ? rawPrice.toSignificant(7) : null;
  const priceDescription = rawPrice
    ? invertPrice
      ? `${rawPrice.invert().toSignificant(7)} ${tokenIn} per ${tokenOut}`
      : `${price} ${tokenOut} per ${tokenIn}`
    : null;

  const priceWidget = (
    <Space>
      <Text type="secondary">{priceDescription}</Text>
      <Button
        type="text"
        onClick={() => {
          setInvertPrice(!invertPrice);
        }}
      >
        <RetweetOutlined />
      </Button>
    </Space>
  );

  const swapModal = (
    <Modal title="Confirm swap" visible={swapModalVisible} onOk={handleSwapModalOk} onCancel={handleSwapModalCancel}>
      <Row>
        <Space>
          <img src={logoIn} alt={tokenIn} width="30" />
          {amountIn}
          {tokenIn}
        </Space>
      </Row>
      <Row justify="center" align="middle" style={{ width: 30 }}>
        <span>↓</span>
      </Row>
      <Row>
        <Space>
          <img src={logoOut} alt={tokenOut} width="30" />
          {amountOut}
          {tokenOut}
        </Space>
      </Row>
      <Divider />
      <Row>{priceWidget}</Row>
      <Row>
        {trades && ((amountOutMin && exact === "in") || (amountInMax && exact === "out"))
          ? exact === "in"
            ? `Output is estimated. You will receive at least ${amountOutMin.toSignificant(
                6,
              )} ${tokenOut} or the transaction will revert.`
            : `Input is estimated. You will sell at most ${amountInMax.toSignificant(
                6,
              )} ${tokenIn} or the transaction will revert.`
          : null}
      </Row>
    </Modal>
  );

  return (
    <Card
      title={
        <Space>
          <img src="https://ipfs.io/ipfs/QmXttGpZrECX5qCyXbBQiqgQNytVGeZW5Anewvh2jc4psg" width="40" alt="uniswapLogo" />
          <Typography>Uniswapper</Typography>
        </Space>
      }
      extra={
        <Button
          type="text"
          onClick={() => {
            setSettingsVisible(true);
          }}
        >
          <SettingOutlined />
        </Button>
      }
    >
      <Space direction="vertical">
        <Row justify="center" align="middle">
          <Card
            size="small"
            type="inner"
            title={`From${exact === "out" && tokenIn && tokenOut ? " (estimate)" : ""}`}
            extra={
              <>
                <img src={logoIn} alt={tokenIn} width="30" />
                <Button
                  type="link"
                  onClick={() => {
                    setAmountOut();
                    setAmountIn(formatUnits(balanceIn, tokens[tokenIn].decimals));
                    setAmountOutMin();
                    setAmountInMax();
                    setExact("in");
                  }}
                >
                  {formattedBalanceIn}
                </Button>
              </>
            }
            style={{ width: 400, textAlign: "left" }}
          >
            <InputNumber
              style={{ width: "160px" }}
              min={0}
              size="large"
              value={amountIn}
              onChange={e => {
                setAmountOut();
                setTrades();
                setAmountIn(e);
                setExact("in");
              }}
            />
            <Select
              showSearch
              value={tokenIn}
              style={{ width: "120px" }}
              size="large"
              bordered={false}
              defaultValue={defaultToken}
              onChange={value => {
                console.log(value);
                if (value === tokenOut) {
                  console.log("switch!", tokenIn);
                  setTokenOut(tokenIn);
                  setAmountOut(amountIn);
                  setBalanceOut(balanceIn);
                }
                setTokenIn(value);
                setTrades();
                setAmountIn();
                setExact("out");
                setBalanceIn();
              }}
              filterOption={(input, option) => option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
              optionFilterProp="children"
            >
              {tokenList.map(token => (
                <Option key={token.symbol} value={token.symbol}>
                  {token.symbol}
                </Option>
              ))}
            </Select>
          </Card>
        </Row>
        <Row justify="center" align="middle">
          <Tooltip title={route.join("->")}>
            <span>↓</span>
          </Tooltip>
        </Row>
        <Row justify="center" align="middle">
          <Card
            size="small"
            type="inner"
            title={`To${exact === "in" && tokenIn && tokenOut ? " (estimate)" : ""}`}
            extra={
              <>
                <img src={logoOut} width="30" alt={tokenOut} />
                <Button type="text">{formattedBalanceOut}</Button>
              </>
            }
            style={{ width: 400, textAlign: "left" }}
          >
            <InputNumber
              style={{ width: "160px" }}
              size="large"
              min={0}
              value={amountOut}
              onChange={e => {
                setAmountOut(e);
                setAmountIn();
                setTrades();
                setExact("out");
              }}
            />
            <Select
              showSearch
              value={tokenOut}
              style={{ width: "120px" }}
              size="large"
              bordered={false}
              onChange={value => {
                console.log(value, tokenIn, tokenOut);
                if (value === tokenIn) {
                  console.log("switch!", tokenOut);
                  setTokenIn(tokenOut);
                  setAmountIn(amountOut);
                  setBalanceIn(balanceOut);
                }
                setTokenOut(value);
                setExact("in");
                setAmountOut();
                setTrades();
                setBalanceOut();
              }}
              filterOption={(input, option) => option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
              optionFilterProp="children"
            >
              {tokenList.map(token => (
                <Option key={token.symbol} value={token.symbol}>
                  {token.symbol}
                </Option>
              ))}
            </Select>
          </Card>
        </Row>
        <Row justify="center" align="middle">
          {priceDescription ? priceWidget : null}
        </Row>
        <Row justify="center" align="middle">
          <Space>
            {inputIsToken ? (
              <Button size="large" loading={approving} disabled={!insufficientAllowance} onClick={approveRouter}>
                {!insufficientAllowance && amountIn && amountOut ? "Approved" : "Approve"}
              </Button>
            ) : null}
            <Button
              size="large"
              loading={swapping}
              disabled={insufficientAllowance || insufficientBalance || !amountIn || !amountOut}
              onClick={showSwapModal}
            >
              {insufficientBalance ? "Insufficient balance" : "Swap!"}
            </Button>
            {swapModal}
          </Space>
        </Row>
      </Space>
      <Drawer
        visible={settingsVisible}
        onClose={() => {
          setSettingsVisible(false);
        }}
        width={500}
      >
        <Descriptions title="Details" column={1} style={{ textAlign: "left" }}>
          <Descriptions.Item label="blockNumber">{blockNumber}</Descriptions.Item>
          <Descriptions.Item label="routerAllowance">
            <Space>
              {routerAllowance ? formatUnits(routerAllowance, tokens[tokenIn].decimals) : null}
              {routerAllowance > 0 ? <Button onClick={removeRouterAllowance}>Remove Allowance</Button> : null}
            </Space>
          </Descriptions.Item>
          <Descriptions.Item label="route">{route.join("->")}</Descriptions.Item>
          <Descriptions.Item label="exact">{exact}</Descriptions.Item>
          <Descriptions.Item label="bestPrice">
            {trades ? (trades.length > 0 ? trades[0].executionPrice.toSignificant(6) : null) : null}
          </Descriptions.Item>
          <Descriptions.Item label="nextMidPrice">
            {trades ? (trades.length > 0 ? trades[0].nextMidPrice.toSignificant(6) : null) : null}
          </Descriptions.Item>
          <Descriptions.Item label="priceImpact">
            {trades ? (trades.length > 0 ? trades[0].priceImpact.toSignificant(6) : null) : null}
          </Descriptions.Item>
          <Descriptions.Item label="slippageTolerance">
            <InputNumber
              defaultValue={defaultSlippage}
              min={0}
              max={100}
              precision={2}
              formatter={value => `${value}%`}
              parser={value => value.replace("%", "")}
              onChange={value => {
                console.log(value);

                const slippagePercent = new Percent(Math.round(value * 100).toString(), "10000");
                setSlippageTolerance(slippagePercent);
              }}
            />
          </Descriptions.Item>
          <Descriptions.Item label="amountInMax">{amountInMax ? amountInMax.toExact() : null}</Descriptions.Item>
          <Descriptions.Item label="amountOutMin">{amountOutMin ? amountOutMin.toExact() : null}</Descriptions.Item>
          <Descriptions.Item label="timeLimitInSeconds">
            <InputNumber
              min={0}
              max={3600}
              defaultValue={defaultTimeLimit}
              onChange={value => {
                console.log(value);
                setTimeLimit(value);
              }}
            />
          </Descriptions.Item>
        </Descriptions>
      </Drawer>
    </Card>
  );
}