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.
β‘οΈ 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.
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.
π 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
The logic to check if the user has approved a token allowance the selected sell token is setup here.
- 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.
- If there is no allowance, then we will write an approval to the sellToken's smart contract using wagmi's useSimulateContract() and useWriteContract().
- Lastly, use wagmi's useWaitForTransactionReceipt() to wait for the approval transaction to complete
Be aware that approvals cost gas. Looking for a gasless approach? Check out Gasless API.
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.
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);
Then, inject the signature that was just created into the quote.transaction.data
that was returned by the quote response. To help know where to inject it, quote.transaction.data
contains a string which is the letter f repeated 130 times. Replace this with the signature. Code here.
const MAGIC_CALLDATA_STRING = "f".repeat(130); // used when signing the eip712 message
quote?.transaction.data.replace(MAGIC_CALLDATA_STRING, signature.slice(2)) as Hex
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.replace(
MAGIC_CALLDATA_STRING,
signature.slice(2)
) as Hex,
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β
- Trust Wallet: https://github.com/trustwallet/assets/tree/master/blockchains
- Polygon Assets github: https://github.com/maticnetwork/polygon-token-assets/tree/main/assets/tokenAssets
- Tokenlists: https://tokenlists.org/
- Coin Gecko: https://tokenlists.org/token-list?url=https://tokens.coingecko.com/uniswap/all.json
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β
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!