Skip to main content
Version: 2.0

How to build a token swap dApp with Swap API (Next.js)

An example ERC-20 swap application built on Next.js with 0x Swap API.


This guide will give you a tour of the Swap API demo app built on Next.js. It covers core concepts that can be easily transferred into any token swapping dApp that you build.

tip

⚑️ Quicklink to the Swap API v2 demo code repo

What we will build​

This example app we will be walking through, and it demonstrates many of the same principles used in production-level swapping dApps such as Matcha.xyz.

Swap API enables your users to easily and conveniently trade tokens at the best prices directly in your app. With one simple integration, 0x unlocks thousands of tokens on the most popular blockchains and aggregated liquidity from 100+ AMMs and professional market makers.

This demo app covers best practices for how to use the 0x Swap API's /price endpoint for indicative pricing and the /quote endpoint for firm quotes.

Video tutorial version here:

Pre-requisites​

Before diving into this walk-through, it's essential to ensure you have the familiarity with the following tools and concepts:

Additionally, we will be building our token swap dApp on the Base network, which provides a cost-effective environment for experimentation. You can always modify this app work with any of the Swap API supported chains.

info

Here is the github repo, feel free to use it to get started quickly for your own project: https://github.com/0xProject/0x-examples/tree/main/swap-v2-next-app

Core Concepts​

To build our token swap dApp, we will focus on the following core components:

  • 🌈 RainbowKit
  • 🏷 PriceView
  • πŸ’Έ QuoteView
  • πŸͺ™ Token lists

What is it?​

These components are transferable to almost any token swapping app being built.

Swap v2 dApp Core Concepts

🌈 RainbowKit​

What is it?​

RainbowKit is a React library that makes it easy to add wallet connection to your dapp. We are using their Next.js App Router example in our app.

Check out their installation instructions to understand the configurations. You can configure your desired chains and generate the required connectors.

Code​

Setup WalletConnect projectId in providers.tsx​

RainbowKit relies on WalletConnect. Obtain a free projectId from WalletConnect Cloud and replace the key in the .env file.

// Inside /swap-v2-next-app/.env
# To get a RainbowKit relies on WalletConnect, get a free projectId here:
# https://cloud.walletconnect.com/app
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID="ADD_YOUR_KEY"
// Inside /app/providers/tsx
const projectId = process.env.NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID as string; // Uses your free key setup in .env

🏷 PriceView​

What is it?​

This component is for users to browse for a price without committing to a trade, aka, get the indicative price. An indicative price is used when users just want to check the price they could receive on a trade, for this we will use the /swap/permit2/price endpoint.

Later on in the QuoteView, when a user is actually ready to make a trade, the app will ping /swap/permit2/quote which returns an order that is ready to submitted on-chain.

This is also important because /quote returns back an order in which a Market Maker must commit their assets to settle that trade. So if we ping /quote too much when we really are just asking for a price and not ready to submit an order, then this can clog up the system!

UI/UX Walk-through​

And if we look through the UI, here are some notable things to callout:

  • User selects sell and buy tokens from the token selectors
  • Users inputs a sellAmount
  • Users can β€œConnect Wallet” powered by RainbowKit
  • Whenever sellAmount changes, the app fetches a price even if a wallet is not connect
  • Displays returned buyAmount
  • When users find a price they like, they can connect their wallet, and switch to the correct network (e.g. Base), the button now says "Approve if the user has enough of the sell token
    • The Approval button allows users to set a token allowance
    • This is standard practice when users need to give a token allowance for a third-party to move funds on our behalf, in this case the Permit2 contract, to trade the user’s ERC20 tokens on their behalf.
    • Users can set an amount they are comfortable with, the default we’ve set is a really big number, but they can also choose only the amount they have in their wallet
  • When we are happy with this trade, our approve button is now β€œReview Trade because we already approved the token allowance, so we can move forward

Code​

The code for this component lives in /app/components/price.tsx. The logic for when the component appears in UI is in /app/page.tsx, it is displayed if the user has not clicked "Review Trade" to finalize the price.

Fetch price with /swap/permit2/price​

The price is fetched using /swap/permit2/price. This API request is wrapped behind /app/api/price/route.ts and triggered in the UI by useEffect here in /app/components/price.tsx

Wrapping our API key protects it because all API requests are viewable by if someone inspects the browser, but we don’t want them inspecting an finding our keys. Instead, when the user queries for an indicative price, it pings our API setup in /app/api/price/route.ts and that pings the Swap API using our API key in the header.

This example uses a demo key which should not be used in production or outside of this demo; however, you can get your own key by going from the 0x Dashboard and following the Getting Started guide.

In addition, we use Next.js App Router's built-in data fetching pattern and the useEffect hook to make the request. By using useEffect, we will get a stream of data updates constantly and automatically, and the UI will quickly respond to any changes, such as when the user inputs a new sellAmount.

Token Allowances & Approvals​

Before placing a trade on the PageView, users must set allowances on all tokens involved. A token allowance lets a third party move funds on your behalf. Essentially, you permit them to move your tokens.

In our case, we want to approve an allowance for Permit2 contract to trade our ERC20 tokens for us. To do this, we need to approve a specific allowance, allowing this contract to move a certain amount of our ERC20 tokens on our behalf. Read more about token allowances

token allowance ui

The logic to check if the user has approved a token allowance the selected sell token is setup here.

  1. First, we need to check if the spender (Permit2) has an allowance already. We can use wagmi's useReadContract() hook to read from the sellToken's "allowance" function.
  2. If there is no allowance, then we will write an approval to the sellToken's smart contract using wagmi's useSimulateContract() and useWriteContract().
  3. Lastly, use wagmi's useWaitForTransactionReceipt() to wait for the approval transaction to complete
tip

Be aware that approvals cost gas. Looking for a gasless approach? Check out Gasless API.

tip

Need to quickly revoke an allowance while testing? To revoke an allowance, you can set the allowance to 0. This can be done programmatically or through a UI such as https://revoke.cash/ .

πŸ’Έ QuoteView​

What is it?​

The QuoteView component provides users with an overview of the transaction details before executing the token swap. Earlier in the PriceView, we provided users an indicative price because they were just browsing for pricing information, so did not need a full 0x order. On the QuoteView, users are ready to fill the order, so we need to provide them a firm quote, and the Market Makers can know to reserve the proper assets to settle the trade.

swap demo quoteview

UI/UX Walk-through​

  • Displays the sell and buy Amounts that the user pays and receives, respectively
    • I’ve formatted it but you can get the symbols, decimal points for formatting from the token list. The amounts are the buy and sell Amounts returned from the quote
  • From here, the user can β€œPlace Order” which creates, signs, and sends a new transaction to the network.

Code​

The code for this component lives in /app/components/quote.tsx. The logic for when the component appears in UI is in /app/page.tsx, it is displayed if the user has approved the token allowance, and clicked "Review Trade" to finalize the trade selection.

Fetch quote with /swap/permit2/quote​

Pinging /swap/permit2/quote is wrapped behind /app/api/quote/route.ts, which is triggered by the useEffect hook here in /app/component/quote.tsx, similar to what we did for PriceView.

Sign the Permit2 EIP-712 message​

Before submitting the quote order to the blockchain, we need to sign the permit2.eip712 object from our quote response. Code here.

// sign permit2.eip712 returned from quote

let signature: Hex;
signature = await signTypedDataAsync(quote.permit2.eip712);

Append signature length and signature data to transaction.data​

Next, append the signature length and signature data to transaction.data. The format should be <sig len><sig data>, where:

  • <sig len>: 32-byte unsigned big-endian integer representing the length of the signature
  • <sig data>: The actual signature data
import { concat, numberToHex, size } from 'viem';

const signatureLengthInHex = numberToHex(size(signature), {
signed: false,
size: 32,
});
quote.transaction.data = concat([transaction.data, signatureLengthInHex, signature]);

Submit the transaction to the network​

The last step is to submit the transaction with all the required parameters using your preferred web3 library (e.g. wagmi, viem, ethers.js, web3.js). In this example, we use wagmi's useSendTransaction.

Pass the required params we get from the quote response to sendTransaction. Code here.

sendTransaction({
account: walletClient?.account.address,
gas: !!quote?.transaction.gas ? BigInt(quote?.transaction.gas) : undefined,
to: quote?.transaction.to,
data: quote?.transaction.data,
chainId: chainId,
});

πŸͺ™ Token lists​

What is it?​

Token Lists allow users to select their desired tokens for swapping. For ERC20 tokens, these lists typically include crucial metadata - such as the token names (Wrapped Ether), symbol (ETH), address, and logoURI - which can be leveraged by apps such as ours.

Thankfully there are several established sources that curate and open-source this information. As developers, we can choose to ingest the entire list or customize our own token lists based off of these sources.

Notable lists​

Code​

In our demo, we curated a token list in /src/constants.ts. In production level apps, it's common practice to maintain a token list since some apps don't support all available tokens.

Monetize your swap integration​

And as your business grows, Swap API offers low-friction ways to monetize your products.

Web3 teams can leverage Swap API to unlock new revenue streams by easily tapping into affiliate fee and trade surplus features. Regardless of whether you’re on a free or paid plan, monetization options are available to all integrators.

Swap API offers two monetization options out-of-the-box:

  • Collect affiliate fees (i.e. trading fee or commission) ← shown in this demo app
  • Collect trade surplus (i.e. positive slippage)

For more details about monetizing, check out the How to monetize your app using 0x Swap API guide which discusses pricing considerations and shows code samples for how to easily implement these options.

Try it out​

info

Here is code for the demo app, feel free to use it to get started quickly for your own project: https://github.com/0xProject/0x-examples/tree/main/swap-v2-next-app

Conclusion​

By following the best practices outlined in this blog post, you can create a user-friendly and effective dApp that enables trustless token swapping on our supported chains.

Happy swapping!