February 27, 2023

Arbitrum DEX – List of Arbitrum DEXs and How to Build One

Table of Contents

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.   

Build an Arbitrum DEX - Sign Up with Moralis

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.

List of Arbitrum DEXs

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. 
Multiple Arbitrum DeFi Protocols Outlined
  • 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. 
Arbitrum DEXs Projects

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.

Title - Build Your Arbitrum DEX

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: 

  1. Set up your project
  2. Build a DEX header
  3. Build a token swap page
  4. Implement backend DEX functionality
  5. 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.

Arbitrum DEX Code on GitHub

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:

Arbitrum DEX Landing Page

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:

Arbitrum DEX Token Swap Page

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.

Arbitrum DEX Token List Code Structure

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:

Finalized Build of Arbitrum DEX Swap

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 adding props:
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 below fetchPrices 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 existing useEffect 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.   

Market Data API
Build amazing trading and portfolio dapps with this Market Data API. Keep users engaged with up-to-date data!
Market Data API
Related Articles
September 19, 2022

How to Create a Web3 Dapp in 3 Steps

January 10, 2023

The 2023 Guide to Avalanche’s Fuji Testnet & Avalanche Faucets

February 15, 2023

What is the Erigon Node Consensus Layer?

August 23, 2022

How to Use Filecoin in Unity for Storage

August 18, 2022

Exploring the Ultimate NodeJS EVM API

March 3, 2023

How to Build a Polygon Portfolio Tracker

October 25, 2023

What are Meta Transactions? Exploring ERC-2771

August 8, 2022

How to Buy NFT Outfits In-Game