Add/Import cusotm token on dex

you can manually push needed token objects to the supportedTokens list

may i know how exactly i will do it?

is this also what you mean under the text below
is it also possible to preload token i want the user see on the exchange once they visit the dex website page?

ex.
from: bnb
to: saitama -<-- i want this to be a default token display

1 Like

hey @dcoderk. So theres two ways you could go bout this Im. you could do a more crude way where the user needs to know the address of the token before hand and if they do they can add it to the dex.

This i know is probably not what you rlooking for as it doesnt involve querying an API of existing tokens. but i will explain that method below (although it will be more progrmatic and you maybe be able to achieve something with less work through morlaiis). This first way could be a good way to add your own custom tokens that arent gonna be listed on an API, especially if you developing locally. You could add the following function sin your dex contract

import "../node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract Dex {

    ......
    .....
    struct Token {
        
        string ticker;
        address tokenAddress;
    }

    modifier tokenExists(string memory ticker) {
        
        require(tokenMapping[ticker].tokenAddress != address(0), "token does not exixts");
        
        _;
    }
    
    function addToken(string memory ticker, address _tokenAddress) public onlyowners {
        
        for (uint i = 0; i < tokenList.length; i++) {
            
            require(keccak256(bytes(tokenList[i])) != keccak256(bytes(ticker)), "cannot add duplicate tokens");
        }
        
        require(keccak256(bytes(ERC20(_tokenAddress).symbol())) == keccak256(bytes(ticker)));
        
        tokenMapping[ticker] = Token(ticker, _tokenAddress);
        
        tokenList.push(ticker);
        
        emit tokenAdded(msg.sender, ticker, _tokenAddress, block.timestamp);
    }


function getTokenList() public view returns(string[] memory) {
        
        return tokenList;
    }
}

so you could create a token struct which has the address of your custom token and the ticker of it aswell. Then in the addToken() function you can add a custom token using the ticker and the address. But we have to be careful. a user could provide the right address but the wrong ticker so we need to handle this. The first check just makes sure duplicate tokens arent being added. The reason im using keccak256 here is because you cannot directly compare strings in soldiity. You could get over this by changimng the ticker type in your struct to bytes32 but i prever having it as a string then we can get around the string comparison problem by hashing the string and comparing the hashes.

Note how we import the ERC20 interface. We use this to make sure that they ticker the user is passing in is equal to the specified ticker of the token defined in the actual token contract. Once we do this we know that the user has inputted the correct tickerr for the address provided. Then we can instiate the token instance and push the ticker to the list of supported tokens for the dex.

Fetch from API through search bar
Now this approach is only for custom tokens. Im not sure if morallis has any functionality to allow you to append this to some database in your font end which you could do so you could query it there but if you really wanted to you could set up your own implementation this through mongoDB or something

Thats one approach for custom tokens. But this does not use any API and requires the user knows the token information before hand. Really this is for custom tokens. If you wanted to make a search bar toke search alreading exisiting tokens in some API then if your using react you could use somethong like the following. Note that i am taking this code from an application i made previous so you may have to fiddle with it to get it to work for your exact use case but its the structure that might help you. We can use react useState and useEffect to query an api from a search bar and each time the text in the search bar cahnges we can update the state and rerender an this will give you the result you want.

Ok so there is gonna be three files here and ill explain them but im not sure how used you are to react. So the first file is going to be our serach bar component. This component will be responsible for allowing the suser to search a token in a search bar and rerender the page each time the search term changes. Consider the code below

import React, { useState, useEffect } from "react";
import { Wrapper, Content } from "./SearchBar.styles";

const SearchBar = ({ setSearchTerm }) => {

    const [state, setState] = useState("");
    
    useEffect(() => {
        
        const timer = setTimeout(() => {

            setSearchTerm(state);
        }, 500);

        return () => clearTimeout(timer);

    }, [setSearchTerm, state])
    
    return (
        <Wrapper>
            <Content>
                <input type="text" placeholder="Search-Token" onChange={event => setState(event.currentTarget.value)} value={state} />
                
            </Content>
        </Wrapper>
    );
};

export default SearchBar;

so first things first we import react. We are going to need useState and useEffect to handle our rendering each time the user changes the search field. Note here that i import a Wrapper and Content. These are just styled components i made there only for css ill show that file at the bottom as its not important here.

we then create a component call searchBar. this component takes in a search term that the user types into the search bar. We set the inital state to an empty string because the user will not have typed anything initially.

I then use a useEffect() that will rerender the page 500 ms after the user changes the text in the search bar. Use a 500ms deylay or otherwise the page will rerender instantly on change which does not look nice

I then return my styled components. These are just for the structuring but you can think of the token search bar as an input field. Whenever the text in the input field changes (which we mionitor through the onChange event specified in the input filed we update or serach term state to the current value thats in the search bar.

Now that we have this component written we can go back to whatever you main index.js file is and put it all together

import React,  { useState, useEffect } from "react";
import SearchBar from "./SearchBar";

const initialSate = {
    results: [],
};

const TokenSearchComponent = () => {

    const [searchTerm, setSearchTerm] = useState("");
    const [state, setState] = useState(initialSate);
    const [isLoadingMore, setIsLoadingMore] = useState(false);

    console.log(searchTerm);
    const fetchTokens = async(searchTerm = "") => {

        try {

            setIsLoadingMore(true);

//you will havr to write this function yourslef. You could do some in a helper.js file no react needed
            const tokens = await TOKENAPI.fetchTokens(searchTerm);

            //now set state
            setState(prev => ({

                ...tokens,
                results:
                    [...tokens.results]
            }))
        } catch (err) {

            console.log(err)
        }
        setIsLoadingMore(false);
    }

    // //initial render
    useEffect(() => {

        console.log("grabbing from API")
        setState(initialSate)
        fetchTokens(searchTerm)
    }, [searchTerm]);
    console.log(state)
    
    return (
        <>
            <SearchBar setSearchTerm={setSearchTerm} /> 
        </>  
    );  
}

export default TokenSearchComponent;

Ok so this file is going to use our search bar component to use the search term we provide to fetch tokens for some API maybe the one Inch one for example.We again import react etc and also our search bar component. I define the inital state to an empty aray called resutls which will store the tokens. In the actual tokenSearch component We set the initial state to an empty string again and not here i have amde a loading state that you could use to set up a loading spinner inbetween API querys but its not really nessecary if you want to keep things as simple as possible. you should ideally also error handle by creating an error state.

But after i initialise the states i create a function call fecth token. I f you wanted to could use this as a custom hook to make this file read cleaner but for the purposes here its fine. This function will take in the search term and it will use it to fetch data from whatever API you want to fetch tokens from. Note that the line of code

const tokens = await TOKENAPI.fetchMovies(searchTerm);

is just something i made up here and you will have to configure this to be specific to how you would fecth token data really but im just using like this to show you the format and structure of how you could go about creating this dynamic search bar. So you query the api using the search term and when you finally get the results you can destructure them out using the es6 spread operstor as such and set them to the results array like i have done.

Once this is done just setLoading to false because we now have the tokens and are not awating results. WE then use a useEffect for the initial render where whch changes the initial state and here we also call our fetch token function so that each time the seraxh term changes we recall the fetch token fucntion.

I then just return the searchBar component above which takes in the search term as a prop. Theres one last file which will be app.js and this file you will just specify this main component above

import React from 'react';
import TokenSearchComponent from "./components/TokenSearchComponent"

const App = () => (
    
         <TokenSearchComponent />
         
);


export default App;

Lastly is the styles for the Wrapper and Content

import styled from "styled-components";

export const Wrapper = styled.div`

`;

export const Content = styled.div`
`;

just fill out here whatever styles you need.

Ok so a few things here. I edited this code from a different application i made for search data from an API. Now your gonna have to know specifically how the oneInchAPI works so that you can fecth data acordingly and modified the above structure to fit your needs. The code above is only to provide a structure of one possible solution for your problem and i hope it doesnt seem to complex or hard to follow and hat it gives you at least an idea of how to solve your problem

2 Likes

Thank for the info and sample code. I will check on it and see how can i use it.
I guess need to use react to achieve that because i am only using vanilla js
Pls. check screenshot to see what I want to achieve when user visit the swap page
so fromtoken is a default token ie. bnb and totoken is our own token which already on the network just need to load it first

Exchange-PancakeSwap-13-213

1 Like

you should be able to do it with simple javascript too, I don’t know the exact code that you have to use

1 Like

i see
is there no one can help or guide me how to implement that interface?

yeah it would be more tedious to use the serach bar technique using vanillajs. but you could get around this by just fecthing the entire list of tokens form say oneinch and then displaying the in a table. So when you click your BNB or CAKE there on that button click you would fecth the tokens from the API and then loop through the array of token responses and append them to a table that appears also when you click the button. Perhaps something like this

async function loadTokenTable() {
    
    //you would need to write this function yourself to fetch token data
    var results = FetchTokensFromAPI().then(function(result) {
      for (let i = 0; i < result.length; i++) {
        
        table.innerHTML += `
            <tr "class="tablerow">

     //again you will need to know the specifics of the 1inch api to know what these
     //return values are actually called for the address and token name
                <td id=${ result[i].tokenAddress}>${result[i].tokenName}</td>
            </tr>`
      }
    })
}

const tokenDropdown = document.geElementByID(""token-dropdown);
tokenDropdown.onclick = loadTokenData

You would need to make a lot of modifications to the above sample. Furstly the function that i say FetchTokensFromAPI() is something i made up and you would need to write this yourself to query the 1inch api to get the tokens. But after this functions finishes ideally the result would be the token list then you can use this token list to loop trhrough and dynamically populate a HTML table with the token names. Again i stress that you will need to make lot sof modications to the above as im just wrinting this out of my head.

Other What you can do here is also set the id of each table element to the token address. that way you can make an onliclck even so that when you click a certain table row, you will have access to the address of the token etc. By doing this you could use something like e.target.id on your table to realise this and get the address information onclick that is if you need it

note that im just grasping at many ideas here without detailled implementation because id have to actually set up a code editor and testing but hopefully it s enough to give you an idea.

Note one problem with this approach is dynamic HTML tables dissapear on page load. So preferabley what you could do is use local storage to prevent this or else on page load fecth the token data first so that you will always have the data to load the table when a user clicks the button shown in your snapshot above

ah ok thank you for more info
i will try working on this dex using react
as it easy to query/search api from react

1 Like

I passed my own token info into the trade.

onClick={() => {
              setToken({address: "0xc66cb95e814c10194313096c2dd660e77cf9b2de",
              decimals: 9,
              logoURI: "/logo.png",
              name: "JammaBeans",
              symbol: "Jamma"});
              onClose();
              }}

it seems to load the price and trade info.

however it fails when I try to do the trade. No metamask popup.

useInchDex.js:60

  1. {statusCode: 400, error: ‘Bad Request’, description: ‘cannot estimate’, meta: Array(0), requestId: ‘943a85b8-4f21-4955-a51a-67f98ce4e134’}

  2. description: “cannot estimate”

  3. error: “Bad Request”

  4. meta: Array(0)

1. length: 0
2. [[Prototype]]: Array(0)
  1. requestId: “943a85b8-4f21-4955-a51a-67f98ce4e134”
  2. statusCode: 400
  3. [[Prototype]]: Object
2 Likes

nice
that’s how i want to implement it
i think you need to fetch/call it from api
it cannot be hard coded as the token already on the network

1 Like

did you have enough funds in the account for that transaction? gas estimation may fail when there is not enough funds, but I don’t know if it is really related to that error

I have enough funds

It fails here in the useInchDex hook

await doSwap(params)
      .then((receipt) => {
        if (receipt.statusCode !== 400) {
          alert("Swap Complete");
        }
        console.log(receipt);
      })
      .catch((e) => alert(e.message));
  }

Im thinking it could be issue with coin slippage or maybe there is a list of supported tokens in the plugin that it rechecks, but could be anything.

Got it !!!

it was a slippage issue

useUnchDex.js

async function doSwap(params) {
    return await Moralis.Plugins.oneInch.swap({
      chain: params.chain, // The blockchain you want to use (eth/bsc/polygon)
      fromTokenAddress: params.fromToken.address, // The token you want to swap
      toTokenAddress: params.toToken.address, // The token you want to receive
      amount: Moralis.Units.Token(
        params.fromAmount,
        params.fromToken.decimals
      ).toString(),
      fromAddress: walletAddress, // Your wallet address
      slippage: 12,    //   I had to update here
    });
  }
3 Likes

amazing! We should add the slippage changer in the boilerplate I guess. Will work on it :muscle:

1 Like

so passing that code already working using your own token?

Yes just passing that info makes it work, but that was just me testing. this is what the code looked like before:

InchModal.jsk

onClick={() => {
              setToken(tokenList[token]);
                console.log(tokenList[token])
                onClose();
              }}

so my code just forced my token info in no matter what token I clicked on. now that the test worked I would say push your token info into the tokenList and it will appear in the list of choices.

i see
i just want it to be a default token
but i will try that also how it work on dex

btw is that the complete info how to implement that ?

I made a custom component for doing this:

import InchDexCustom from "components/InchDex/InchDexCustom";

<InchDexCustom chain="bsc" toTokenAddress="0xc66c...f9b2de" slippage={12}/>

How to use:

Change line 78 of useInchDex.js hook:

from
slippage: 1,

to
slippage: params.newslippage ? params.newslippage : 1,

Add this file to the InchDex folder in components:

InchDexCustom.jsx

import { useState, useEffect, useMemo } from "react";
import { useMoralis, useWeb3Contract } from "react-moralis";
import { useMoralisDapp } from "providers/MoralisDappProvider/MoralisDappProvider";
import InchModal from "./components/InchModal";
import useInchDex from "hooks/useInchDex";
import { Button, Card, Image, Input, InputNumber, Modal } from "antd";
import Text from "antd/lib/typography/Text";
import { ArrowDownOutlined } from "@ant-design/icons";
import useTokenPrice from "hooks/useTokenPrice";
import { tokenValue } from "helpers/formatters";
//import erc20Abi from "./contractAbi/standardErc20Abi" 
// import { useOneInchQuote } from "react-moralis";

const styles = {
  card: {
    width: "430px",
    boxShadow: "0 0.5rem 1.2rem rgb(189 197 209 / 20%)",
    border: "1px solid #e7eaf3",
    borderRadius: "1rem",
    fontSize: "16px",
    fontWeight: "500",
  },
  input: {
    padding: "0",
    fontWeight: "500",
    fontSize: "23px",
    display: "block",
    width: "100%",
  },
  priceSwap: {
    display: "flex",
    justifyContent: "space-between",
    fontSize: "15px",
    color: "#434343",
    marginTop: "8px",
    padding: "0 10px",
  },
};

const nativeAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";

const chainIds = {
  "0x1": "eth",
  "0x38": "bsc",
  "0x89": "polygon",
};

function InchDexCustom({ chain, toTokenAddress, slippage }) {
  const { trySwap, tokenList, getQuote } = useInchDex(chain);
console.log(tokenList)
  const { Moralis, isInitialized } = useMoralis();
  const { chainId } = useMoralisDapp();
  const [isFromModalActive, setFromModalActive] = useState(false);
  const [isToModalActive, setToModalActive] = useState(false);
  const [fromToken, setFromToken] = useState();
  const [toToken, setToToken] = useState();
  const [fromAmount, setFromAmount] = useState();
  const [quote, setQuote] = useState();
  const [currentTrade, setCurrentTrade] = useState();
  const { fetchTokenPrice } = useTokenPrice();
  const [tokenPricesUSD, setTokenPricesUSD] = useState({});
  console.log("fromToken", fromToken);
  const options = { chain: chain, addresses: toTokenAddress }; //--------------------
  const [ tokendata, settokendata] = useState([])
  const [newslippage, setSlippage] = useState(slippage)
  const fromTokenPriceUsd = useMemo(
    () => (tokenPricesUSD?.[fromToken?.["address"]] ? tokenPricesUSD[fromToken?.["address"]] : null),
    [tokenPricesUSD, fromToken]
  );

  const toTokenPriceUsd = useMemo(
    () => (tokenPricesUSD?.[toToken?.["address"]] ? tokenPricesUSD[toToken?.["address"]] : null),
    [tokenPricesUSD, toToken]
  );

  const fromTokenAmountUsd = useMemo(() => {
    if (!fromTokenPriceUsd || !fromAmount) return null;
    return `~$ ${(fromAmount * fromTokenPriceUsd).toFixed(4)}`;
  }, [fromTokenPriceUsd, fromAmount]);

  const toTokenAmountUsd = useMemo(() => {
    if (!toTokenPriceUsd || !quote) return null;
    return `~$ ${(Moralis.Units.FromWei(quote?.toTokenAmount, quote?.toToken?.decimals) * toTokenPriceUsd).toFixed(4)}`;
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [toTokenPriceUsd, quote]);


   function changeSlippage(e){
    setSlippage(e.target.value);
   }
  // tokenPrices
  useEffect(() => {
    if (!isInitialized || !fromToken || !chain) return null;
    fetchTokenPrice({ chain: chain, address: fromToken[["address"]] }).then((price) =>
      setTokenPricesUSD({
        ...tokenPricesUSD,
        [fromToken["address"]]: price["usdPrice"],
      })
    );
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [chain, isInitialized, fromToken]);

  useEffect(() => {
    if (!isInitialized || !toToken || !chain) return null;
    fetchTokenPrice({ chain: chain, address: toToken[["address"]] }).then((price) =>
      setTokenPricesUSD({
        ...tokenPricesUSD,
        [toToken["address"]]: price["usdPrice"],
      })
    );
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [chain, isInitialized, toToken]);

  useEffect(() => {
    if (!tokenList) return null;
    setFromToken(tokenList[nativeAddress]);
  }, [tokenList]);


  useEffect(async() => {
    const result =await Moralis.Web3API.token.getTokenMetadata(options)
    settokendata(result);
    if(!toToken ){
      setToToken(result[0]);
    }
   
     
    //.then(setCurrentTrade( fromToken, toTokenAddress, fromAmount, chain))
    console.log(result);
    if (tokendata){
      
    }
  }, [toToken]);

  const ButtonState = useMemo(() => {
    if (chainIds?.[chainId] !== chain) return { isActive: false, text: `Switch to ${chain}` };
    // if (chainIds[chainId] !== chain)

    if (!fromAmount) return { isActive: false, text: "Enter an amount" };
    if (fromAmount && currentTrade) return { isActive: true, text: "Swap" };
    return { isActive: false, text: "Select tokens" };
  }, [fromAmount, currentTrade, chainId, chain]);

  useEffect(() => {
    if (fromToken && toToken && fromAmount) setCurrentTrade({ fromToken, toToken, fromAmount, chain, newslippage });
  }, [toToken, fromToken, fromAmount, chain, newslippage]);

  useEffect(() => {
    if (currentTrade) getQuote(currentTrade).then((quote) => setQuote(quote));
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [currentTrade]);

  const PriceSwap = () => {
    const Quote = quote;
    if (!Quote || !tokenPricesUSD?.[toToken?.["address"]]) return null;
    if (Quote?.statusCode === 400) return <>{Quote.message}</>;
    console.log(Quote);
    const { fromTokenAmount, toTokenAmount } = Quote;
    const { symbol: fromSymbol } = fromToken;
    const { symbol: toSymbol } = toToken;
    const pricePerToken = parseFloat(
      tokenValue(fromTokenAmount, fromToken["decimals"]) / tokenValue(toTokenAmount, toToken["decimals"])
    ).toFixed(6);
    return (
      <Text style={styles.priceSwap}>
        Price:{" "}
        <Text>{`1 ${toSymbol} = ${pricePerToken} ${fromSymbol} ($${tokenPricesUSD[[toToken["address"]]].toFixed(
          6
        )})`}</Text>
      </Text>
    );
  };

  return (
    <>
      <Card style={styles.card} bodyStyle={{ padding: "18px" }}>
        <Card style={{ borderRadius: "1rem" }} bodyStyle={{ padding: "0.8rem" }}>
          <div style={{ marginBottom: "5px", fontSize: "14px", color: "#434343" }}>From</div>
          <div
            style={{
              display: "flex",
              flexFlow: "row nowrap",
            }}
          >
            <div>
              <InputNumber
                bordered={false}
                placeholder="0.00"
                style={{ ...styles.input, marginLeft: "-10px" }}
                onChange={setFromAmount}
                value={fromAmount}
              />
              <Text style={{ fontWeight: "600", color: "#434343" }}>{fromTokenAmountUsd}</Text>
            </div>
            <Button
              style={{
                height: "fit-content",
                display: "flex",
                justifyContent: "space-between",
                alignItems: "center",
                borderRadius: "0.6rem",
                padding: "5px 10px",
                fontWeight: "500",
                fontSize: "17px",
                gap: "7px",
                border: "none",
              }}
              onClick={() => setFromModalActive(true)}
            >
              {fromToken ? (
                <Image
                  src={fromToken?.logoURI || "https://etherscan.io/images/main/empty-token.png"}
                  alt="nologo"
                  width="30px"
                  preview={false}
                  style={{ borderRadius: "15px" }}
                />
              ) : (
                <span>Select a token</span>
              )}
              <span>{fromToken?.symbol}</span>
              <Arrow />
            </Button>
          </div>
        </Card>
        <div style={{ display: "flex", justifyContent: "center", padding: "10px" }}>
          <ArrowDownOutlined />
        </div>
        <Card style={{ borderRadius: "1rem" }} bodyStyle={{ padding: "0.8rem" }}>
          <div style={{ marginBottom: "5px", fontSize: "14px", color: "#434343" }}>To</div>
          <div
            style={{
              display: "flex",
              flexFlow: "row nowrap",
            }}
          >
            <div>
              <Input
                bordered={false}
                placeholder="0.00"
                style={styles.input}
                readOnly
                value={quote ? Moralis.Units.FromWei(quote?.toTokenAmount, quote?.toToken?.decimals).toFixed(6) : ""}
              />
              <Text style={{ fontWeight: "600", color: "#434343" }}>{toTokenAmountUsd}</Text>
            </div>
            <Button
              style={{
                height: "fit-content",
                display: "flex",
                justifyContent: "space-between",
                alignItems: "center",
                borderRadius: "0.6rem",
                padding: "5px 10px",
                fontWeight: "500",
                fontSize: "17px",
                gap: "7px",
                border: "none",
              }}
              onClick={() => setToModalActive(true)}
              type={toToken ? "default" : "primary"}
            >
              {toToken ? (
                <Image
                  src={toToken?.logoURI || "https://etherscan.io/images/main/empty-token.png"}
                  alt="nologo"
                  width="30px"
                  preview={false}
                  style={{ borderRadius: "15px" }}
                />
              ) : (
                <span>Select a token</span>
              )}
              <span>{toToken?.symbol}</span>
              <Arrow />
            </Button>
          </div>
        </Card>
        {quote && (
          <div>
            <Text
              style={{
                display: "flex",
                justifyContent: "space-between",
                fontSize: "15px",
                color: "#434343",
                marginTop: "8px",
                padding: "0 10px",
              }}
            >
              Estimated Gas: <Text>{quote?.estimatedGas}</Text>
            </Text>
            <PriceSwap />
          </div>
        )}
        
      <div style={{ display:"flex", justifyContent:"space-between", padding:"5px 10px"}}>
      <label htmlFor="">Slippage</label>
      <input type="range" min="0" onChange={changeSlippage} value={newslippage} max="50"  />
        <input type="number" onChange={changeSlippage} value={newslippage} max="50" min="0" name="" placeholder="slippage" id="" />
      </div>
        <Button
          type="primary"
          size="large"
          style={{
            width: "100%",
            marginTop: "15px",
            borderRadius: "0.6rem",
            height: "50px",
          }}
          onClick={() => trySwap(currentTrade)}
          disabled={!ButtonState.isActive}
        >
          {ButtonState.text}
        </Button>
      </Card>
      <Modal
        title="Select a token"
        visible={isFromModalActive}
        onCancel={() => setFromModalActive(false)}
        bodyStyle={{ padding: 0 }}
        width="450px"
        footer={null}
      >
        <InchModal
          open={isFromModalActive}
          onClose={() => setFromModalActive(false)}
          setToken={setFromToken}
          tokenList={tokenList}
        />
      </Modal>
      <Modal
        title="Select a token"
        visible={isToModalActive}
        onCancel={() => setToModalActive(false)}
        bodyStyle={{ padding: 0 }}
        width="450px"
        footer={null}
      >
        <InchModal
          open={isToModalActive}
          onClose={() => setToModalActive(false)}
          setToken={setToToken}
          tokenList={tokendata}
        />
      </Modal>
    </>
  );
}

export default InchDexCustom;

const Arrow = () => (
  <svg
    xmlns="http://www.w3.org/2000/svg"
    width="24"
    height="24"
    viewBox="0 0 24 24"
    strokeWidth="2"
    stroke="currentColor"
    fill="none"
    strokeLinecap="round"
    strokeLinejoin="round"
  >
    <path stroke="none" d="M0 0h24v24H0z" fill="none" />
    <polyline points="6 9 12 15 18 9" />
  </svg>
);
1 Like

nice
btw is that the complete code?
and another question can i also build a uniswap clone by only using vanilla js?

Brilliant help. Thanks. I have the same issue :+1:

1 Like

Bummer, tried adjusting slippage as above to 12 & 15 but still get this - any further thoughts?

more detail:

Ta