Arbitrum is one of many scaling solutions for Ethereum. Thanks to its optimistic rollup protocol, transactions become cheaper and faster. Additionally, projects built on Arbitrum inherit Ethereum-level security. So, if you want to build a DEX featuring fast transactions and low gas fees, then developing an Arbitrum DEX is the way to go!
Now, building a DEX on any chain may sound like a daunting task. However, the process becomes pretty straightforward with the right guidance and tools. Also, since Arbitrum is an Ethereum L2 (layer-2) solution, developers can use the same tools as when building on top of Ethereum or other EVM-compatible chains. As such, the Web3 APIs from Moralis, NodeJS, React, wagmi, and the 1inch aggregator allow you to build an Arbitrum DEX in less than two hours. Hereโs how easy it is to use Moralis when building on Arbitrum:
Moralis.start({ apiKey: process.env.MORALIS_KEY, defaultEvmApiChain: EvmChain.ARBITRUM })
However, before you deploy your DEX or other dapps (decentralized applications) on the Arbitrum mainnet, youโll want to test things on a testnet. For that purpose, youโll need a reliable Arbitrum Goerli faucet.
If you are eager to start the practical portion of this article, create your free Moralis account and use the upcoming tutorial! However, if you need some inspiration first, make sure to check out our list of decentralized exchanges on Arbitrum below.

List of Decentralized Exchanges on Arbitrum
The following list of Arbitrum DEX examples can serve as a great source of inspiration. So, feel free to visit these exchanges and take them for a spin.

These are the most popular DEXs and DeFi protocols on Arbitrum:
- Uniswap was the first Ethereum-based exchange. It continues to offer ERC-20 token swaps through liquidity pools. Aside from Ethereum, Uniswap supports Polygon, Optimism, and Arbitrum.
- Sushi is another reputable DEX that supports multiple networks and enables users to swap a vast range of crypto. Sushi supports the same four networks as Uniswap.
- Slingshot is a popular token-swapping protocol with 0% fees. It supports the following networks: Arbitrum, Polygon, BNB Chain, and Optimism.
- Mycelium focuses exclusively on Arbitrum โ it is a native decentralized trading protocol on this L2.
- Shell Protocol is a wrapping protocol that serves as a DeFi development toolkit on Arbitrum.

- Dolomite is a margin trading protocol based on Arbitrum.
- FS focuses on decentralizing trading and investing in tokens, tokenized shares or NFTs, and startup equity. Aside from Arbitrum, it supports BNB Chain, Ethereum, Optimism, and Polygon.
- Cap is an Arbitrum-based decentralized leverage trading protocol with low fees and a decent trading volume.
- Dexible is a multi-chain decentralized trading protocol. It supports Arbitrum, Avalanche, BNB Chain, Ethereum, Optimism, and Polygon.
- UniDex is an aggregator that allows traders to get the best rate on the market. It also supports perpetual leverage trading of crypto, forex, ETFs, and more.

Other Arbitrum DEX platforms and protocols include:
- MES Protocol
- Piper Finance
- Bebop
- Squid
- 1inch
Note: Arbitrum is a relatively new network. Thus, there are existing and new projects adding support for the network daily. As such, the above list of Arbitrum DEX alternatives is far from complete.

Arbitrum DEX Tutorial
You have two options if you want to learn how to build your own Arbitrum DEX. You can use the video below focusing on the Ethereum chain and implement the minor adjustments to target Arbitrum. Or, you may use the upcoming sections as your guide. In either case, youโll have to complete the following five stages to get to the finish line:
- Set up your project
- Build a DEX header
- Build a token swap page
- Implement backend DEX functionality
- Integrate the 1inch aggregator for Arbitrum
Note: Moving forward, make sure to use our GitHub repository. That way, you wonโt have to start from scratch. Plus, you donโt need to worry about styling files, and you can devote your maximum attention to DEX functionalities.

Initial Project Setup
Start by cloning our code as demonstrated in the above screenshot. Next, use Visual Studio Code (VSC), open a new project, and clone the code by using the above-copied URL with the following command:
git clone https://github.com/IAmJaysWay/dexStarter
Then, cd
into the โdexStarterโ folder:
cd dexStarter
Within the โdexStarterโ folder, youโll see the โdexโ and โdexBackโ folders. The former contains the frontend components for this project, while the latter holds the backend scripts. With our starter code, you are starting with simple React (frontend) and NodeJS (backend) apps. To make our template scripts function properly, you must install the required dependencies. Thus, cd
into the frontend folder, where you need to run the following command:
npm install
After installing the dependencies, run your React app with this command:
npm run start
Then, you may visit โlocalhost:3000โ to see the initial state of your Arbitrum DEX frontend:

Build a DEX Header
From the โdex/srcโ folder, open โApp.jsโ. Inside that script, import the Header.js
component:
import Header from "./components/Header";
Then, youโll be able to add the Header
component to the App
function:
function App() { return ( <div className="App"> <Header /> </div> ) }
Moving on, focus on the โHeader.jsโ script, located in the โdex/src/componentsโ directory. At the top of that script, import the logo and chain image:
import Logo from "../moralis-logo.svg"; import Arbitrum from "../arbitrum.svg";
Note: Our template scripts focus on the Ethereum chain. So, youโll have to find an Arbitrum icon (arbitrum.svg) and add it to the โdex/srcโ directory. Then, as you proceed, change the relevant lines of code accordingly.
You also want to tweak the Header
function so it will display the logo, menu options, the relevant chain, and the โConnectโ button:
function Header(props) { const {address, isConnected, connect} = props; return ( <header> <div className="leftH"> <img src={Logo} alt="logo" className="logo" /> <div className="headerItem">Swap</div> <div className="headerItem">Tokens</div> </div> <div className="rightH"> <div className="headerItem"> <img src={Arbitrum} alt="eth" className="arbitrum" /> Arbitrum </div> <div className="connectButton" onClick={connect}> {isConnected ? (address.slice(0,4) +"..." +address.slice(38)) : "Connect"} </div> </div> </header> ); }
With the above lines of code in place, your DEX header should look as follows (with โArbitrumโ instead of โEthereumโ):
At this point, the โSwapโ and โTokensโ options above are inactive. So, you need to activate them by returning to the โApp.jsโ file and importing the Swap
and Tokens
components and Routes
:
import Swap from "./components/Swap"; import Tokens from "./components/Tokens"; import { Routes, Route } from "react-router-dom";
In addition, inside the Header
div, add the mainWindow
div with proper route paths:
<Header connect={connect} isConnected={isConnected} address={address} /> <div className="mainWindow"> <Routes> <Route path="/" element={<Swap isConnected={isConnected} address={address} />} /> <Route path="/tokens" element={<Tokens />} /> </Routes> </div>
You also need to tweak the โHeader.jsโ script. Start by importing Link
at the top (below the existing imports):
import { Link } from "react-router-dom";
Then, wrap root and tokens
path links around the Swap
and Tokens
divs:
<Link to="/" className="link"> <div className="headerItem">Swap</div> </Link> <Link to="/tokens" className="link"> <div className="headerItem">Tokens</div> </Link>
As a result, your frontend should now enable you to switch between the โSwapโ and โTokensโ pages:
Build a Token Swap Page
To cover the fronted DEX functionality, you want to use the Ant Design UI framework components. Open the โSwap.jsโ script and add the following lines of code:
import React, { useState, useEffect } from "react"; import { Input, Popover, Radio, Modal, message } from "antd"; import { ArrowDownOutlined, DownOutlined, SettingOutlined, } from "@ant-design/icons";
Then, you want to create a tradeBox
div on the โSwapโ page. The following lines of code also cover a slippage setting option:
function Swap() { const [slippage, setSlippage] = useState(2.5); function handleSlippageChange(e) { setSlippage(e.target.value); } const settings = ( <> <div>Slippage Tolerance</div> <div> <Radio.Group value={slippage} onChange={handleSlippageChange}> <Radio.Button value={0.5}>0.5%</Radio.Button> <Radio.Button value={2.5}>2.5%</Radio.Button> <Radio.Button value={5}>5.0%</Radio.Button> </Radio.Group> </div> </> ); return ( <div className="tradeBox"> <div className="tradeBoxHeader"> <h4>Swap</h4> <Popover content={settings} title="Settings" trigger="click" placement="bottomRight" > <SettingOutlined className="cog" /> </Popover> </div> </div> </> ); }
The following screenshot indicates the progress of your DEXโs frontend:

Add Token Input Fields
In order to enable your Arbitrum DEX users to select the tokens they want to swap, you need to add the appropriate input fields. Hence, you need to refocus on the tradeBox
div and create the inputs
div (below the tradeBoxHeader
div):
<div className="inputs"> <Input placeholder="0" value={tokenOneAmount} onChange={changeAmount} disabled={!prices} /> <Input placeholder="0" value={tokenTwoAmount} disabled={true} /> <div className="switchButton" onClick={switchTokens}> <ArrowDownOutlined className="switchArrow" /> </div> <div className="assetOne" onClick={() => openModal(1)}> <img src={tokenOne.img} alt="assetOneLogo" className="assetLogo" /> {tokenOne.ticker} <DownOutlined /> </div> <div className="assetTwo" onClick={() => openModal(2)}> <img src={tokenTwo.img} alt="assetOneLogo" className="assetLogo" /> {tokenTwo.ticker} <DownOutlined /> </div>
Plus, below the above Slippage
state variable, you need to add the appropriate state variables for your input fields:
const [tokenOneAmount, setTokenOneAmount] = useState(null); const [tokenTwoAmount, setTokenTwoAmount] = useState(null); const [tokenOne, setTokenOne] = useState(tokenList[0]); const [tokenTwo, setTokenTwo] = useState(tokenList[1]); const [isOpen, setIsOpen] = useState(false); const [changeToken, setChangeToken] = useState(1);
You also need to add the following functions, which will handle the changing of token amounts and the switching of โfrom/toโ. So, add the following lines of code below the handleSlippageChange
function:
function changeAmount(e) { setTokenOneAmount(e.target.value); if(e.target.value && prices){ setTokenTwoAmount((e.target.value * prices.ratio).toFixed(2)) }else{ setTokenTwoAmount(null); } } function switchTokens() { setPrices(null); setTokenOneAmount(null); setTokenTwoAmount(null); const one = tokenOne; const two = tokenTwo; setTokenOne(two); setTokenTwo(one); fetchPrices(two.address, one.address); }
To offer users the option to select tokens, we prepared โtokenList.jsonโ. The latter contained an array of tokens: their tickers, icons, names, addresses, and decimals.

Note: Our list of tokens was designed to support the Ethereum chain. However, since the token price in USD is the same across the chains, you can use the same list for Arbitrum.
In order to utilize our token list, import โtokenList.jsonโ into your โSwap.jsโ script:
import tokenList from "../tokenList.json";
Add Token Selection Modals
The above-presented inputs
div contains two openModal
functions. As such, you need to tweak your script so that these functions will work properly. To that end, add the following lines of code within return
, just above the tradeBox
div:
return ( <> {contextHolder} <Modal open={isOpen} footer={null} onCancel={() => setIsOpen(false)} title="Select a token" > <div className="modalContent"> {tokenList?.map((e, i) => { return ( <div className="tokenChoice" key={i} onClick={() => modifyToken(i)} > <img src={e.img} alt={e.ticker} className="tokenLogo" /> <div className="tokenChoiceNames"> <div className="tokenName">{e.name}</div> <div className="tokenTicker">{e.ticker}</div> </div> </div> ); })} </div> </Modal>
Furthermore, also add the openModal
and modifyToken
functions below the existing functions:
function openModal(asset) { setChangeToken(asset); setIsOpen(true); } function modifyToken(i){ setPrices(null); setTokenOneAmount(null); setTokenTwoAmount(null); if (changeToken === 1) { setTokenOne(tokenList[i]); fetchPrices(tokenList[i].address, tokenTwo.address) } else { setTokenTwo(tokenList[i]); fetchPrices(tokenOne.address, tokenList[i].address) } setIsOpen(false); }
Finally, add the following line of code below the inputs
div to implement the โSwapโ button:
<div className="swapButton" disabled={!tokenOneAmount || !isConnected} onClick={fetchDexSwap}>Swap</div>
With all of the aforementioned tweaks in place, your Arbitrum DEX frontend should be ready:
Implement Backend Arbitrum DEX Functionality
If you remember, the โdexBackโ folder holds the backend scripts. Among others, this is the place where you can find the โ.env.exampleโ file, where youโll store your Web3 API key. So, in case you havenโt done so yet, create your free Moralis account and access your admin area. Then, copy your API key from the โWeb3 APIsโ page:
With your Web3 API key inside the โ.env.exampleโ file, also rename the file to โ.envโ. Then, open a new terminal and cd
into the โdexStarterโ folder and then into โdexBackโ. Once inside your backed directory, you are ready to install all the backend dependencies by running the following command:
npm install
Next, you must focus on your backendโs โindex.jsโ script to implement the Moralis getTokenPrice
endpoint.
Note: We encourage you to use the Moralis Web3 documentation to explore the โGet ERC-20 token priceโ endpoint.
The following app.get
function (inside the backendโs โindex.jsโ script) ensures that the /tokenPrice
endpoint fetches token prices in USD:
app.get("/tokenPrice", async (req, res) => { const {query} = req; const responseOne = await Moralis.EvmApi.token.getTokenPrice({ address: query.addressOne }) const responseTwo = await Moralis.EvmApi.token.getTokenPrice({ address: query.addressTwo }) const usdPrices = { tokenOne: responseOne.raw.usdPrice, tokenTwo: responseTwo.raw.usdPrice, ratio: responseOne.raw.usdPrice/responseTwo.raw.usdPrice } return res.status(200).json(usdPrices); });
After updating and saving your โindex.jsโ file, enter โnode index.jsโ into your backend terminal to run your backend.
Note: You can access the final backend โindex.jsโ file on our โdexFinalโ GitHub repo page:
If you look at the bottom of the โindex.jsโ script, you can see the Moralis.start
function. The latter initializes Moralis for Ethereum by default. As noted above, since we are only fetching token prices, which are the same on all chains, we can focus on Ethereum even though we are building an Arbitrum DEX. However, if you were to create a token list that uses Arbitrum token addresses, youโd need to initialize Moralis for the Arbitrum network:
Moralis.start({ apiKey: process.env.MORALIS_KEY, defaultEvmApiChain: EvmChain.ARBITRUM })
Frontend-Backend Communication
Another important aspect of building a DEX is getting token prices from your backend (which we covered above) to your frontend. To implement this communication, return to your โSwap.jsโ script. There, import Axios just below the existing imports:
import axios from "axios";
To properly implement the communication between the frontend and backend, you also need to add the following state variable:
const [prices, setPrices] = useState(null);
Plus, make sure to add the following fetchPrices
async function and its corresponding useEffect
below the modifyToken
function:
async function fetchPrices(one, two){ const res = await axios.get(`http://localhost:3001/tokenPrice`, { params: {addressOne: one, addressTwo: two} }) setPrices(res.data) } useEffect(()=>{ fetchPrices(tokenList[0].address, tokenList[1].address) }, [])
With the above tweaks in place, the DEX is able to obtain token prices and calculate their ratios. Furthermore, using this data, your frontend can automatically populate the amount of the token pair:

Web3 Authentication โ Connecting Web3 Wallets
Itโs time to activate the โConnectโ button in your DEXโs header. To implement Web3 authentication the easy way, you can use the wagmi library. So, open your frontendโs โindex.jsโ file, which is located inside the โdex/srcโ directory. At the top of that script, import the following components and a public provider from wagmi:
import { configureChains, arbitrum, WagmiConfig, createClient } from "wagmi"; import { publicProvider } from "wagmi/providers/public";
Next, configure the chains, and create a client by adding the following snippets of code below the imports:
const { provider, webSocketProvider } = configureChains( [arbitrum], [publicProvider()] ); const client = createClient({ autoConnect: true, provider, webSocketProvider, });
Nonetheless, donโt forget to wrap BrowserRouter
with WagmiConfig
:
<React.StrictMode> <WagmiConfig client={client}> <BrowserRouter> <App /> </BrowserRouter> </WagmiConfig> </React.StrictMode>
Note: You can access the final frontend โindex.jsโ script on the โdexFinalโ GitHub repo. However, keep in mind that the โindex.jsโ script on the frontend focuses on the Ethereum chain.
You must also tweak your โApp.jsโ script to ensure the โConnectโ button works properly. Again, first, import the wagmi components and MetaMask connector below:
import { useConnect, useAccount } from "wagmi"; import { MetaMaskConnector } from "wagmi/connectors/metaMask";
Then, focus on the App
function in order to destructure the address and connect a new user. So, above return
, add the following lines of code:
const { address, isConnected } = useAccount(); const { connect } = useConnect({ connector: new MetaMaskConnector(), });
Note: In case you want a more detailed code walkthrough regarding the โConnectโ button, watch the above video, starting at 57:25.
Once you implement the above lines of code, youโll be able to use the โConnectโ button to connect to your Arbitrum DEX with your MetaMask wallet:
Note: Make sure to add the Arbitrum network to MetaMask:
Integrate the 1inch Aggregator for Arbitrum
The simplest way to implement the actual DEX functionalities is to use the 1inch aggregator. The latter supports most EVM-compatible chains, including Arbitrum. If you wish to learn how to use the 1inch docs to obtain the correct API endpoint, utilize the above video (1:04:14) but instead focus on the Arbitrum chain:
However, you can simply reopen the โSwap.jsโ script and add the following snippets of code to finalize your Arbitrum DEX:
- Import the following hooks from wagmi:
import { useSendTransaction, useWaitForTransaction } from "wagmi";
- Tweak your
Swap
function by addingprops
:
function Swap(props) { const { address, isConnected } = props;
- Add a new state variable (below the existing state variables) to store transaction details and wait for a transaction to go through:
const [txDetails, setTxDetails] = useState({ to:null, data: null, value: null, }); const {data, sendTransaction} = useSendTransaction({ request: { from: address, to: String(txDetails.to), data: String(txDetails.data), value: String(txDetails.value), } }) const { isLoading, isSuccess } = useWaitForTransaction({ hash: data?.hash, })
- The
fetchDexSwap
async function that you need to add belowfetchPrices
contains the required 1inch API links for the Arbitrum chain (chain ID: 42161). You can fetch those API links from the โSwaggerโ section of the 1inch documentation. Plus, you need to create your token list with Arbitrum addresses for the following lines of code to function properly:
async function fetchDexSwap(){ const allowance = await axios.get(`https://api.1inch.io/v5.0/42161/approve/allowance?tokenAddress=${tokenOne.address}&walletAddress=${address}`) if(allowance.data.allowance === "0"){ const approve = await axios.get(`https://api.1inch.io/v5.0/42161/approve/transaction?tokenAddress=${tokenOne.address}`) setTxDetails(approve.data); console.log("not approved") return } const tx = await axios.get(`https://api.1inch.io/v5.0/42161/swap?fromTokenAddress=${tokenOne.address}&toTokenAddress=${tokenTwo.address}&amount=${tokenOneAmount.padEnd(tokenOne.decimals+tokenOneAmount.length, '0')}&fromAddress=${address}&slippage=${slippage}`) let decimals = Number(`1E${tokenTwo.decimals}`) setTokenTwoAmount((Number(tx.data.toTokenAmount)/decimals).toFixed(2)); setTxDetails(tx.data.tx); }
- Add an additional three
useEffect
functions under the existinguseEffect
function to cover transaction details and pending transactions:
useEffect(()=>{ if(txDetails.to && isConnected){ sendTransaction(); } }, [txDetails]) useEffect(()=>{ messageApi.destroy(); if(isLoading){ messageApi.open({ type: 'loading', content: 'Transaction is Pending...', duration: 0, }) } },[isLoading]) useEffect(()=>{ messageApi.destroy(); if(isSuccess){ messageApi.open({ type: 'success', content: 'Transaction Successful', duration: 1.5, }) }else if(txDetails.to){ messageApi.open({ type: 'error', content: 'Transaction Failed', duration: 1.50, }) } },[isSuccess])
Arbitrum DEX โ List of Arbitrum DEXs and How to Build One โ Summary
In todayโs article, you first learned about the leading DEXs and DeFi protocols on Arbitrum. Then, you had a chance to combine the power of one of those DEX aggregators (1inch) and Moralis to build your own Arbitrum DEX. Using our template scripts, you were able to focus exclusively on DEX functionalities by creating a swap token page. Along the way, you also learned how to obtain your Web3 API and how to target the Arbitrum network with Moralis. So, not only do you now know how to create an Arbitrum DEX, but youโre also ready to start building other killer dapps on Arbitrum.
One of the best things about Moralis is that you donโt need to limit yourself to a single chain. Thanks to the cross-chain interoperability of Moralis, you can easily create multi-chain dapps. If you have your own ideas, use the Moralis docs to help you make the most out of this enterprise-grade Web3 API toolset. However, if you need additional inspiration or idea sparks, make sure to check out our Web3 development video tutorials that await you on the Moralis YouTube channel or explore the Moralis blog. Some of the latest topics there focus on data availability in blockchains, an Oasis testnet faucet, how to use ChatGPT to mint an NFT, how to mint NFTs on Aptos, and much more.
Read More: moralis.io