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