How to Clone UniswapV2 (Backend)
You might be wondering how to kickstart building your decentralized exchange (DEX) project, or perhaps you're curious about the inner workings of the UniswapV2 contract or DEXs in general. Well, you're in the right spot. Welcome! In this tutorial, I'll guide you through the process of cloning UniswapV2 and bringing your project to life. I'll also provide insights into how the UniswapV2 contract operates. So, without any delay, let's jump right in!
Estimated time to follow along: 45 mins - 1 hour
But before we delve in, here's a list of things you need to have in place before you begin following this tutorial.
Prerequisites
Basic understanding of Ethereum and smart contracts.
Familiarity with Solidity.
Familiarity with some crypto terminologies.
Setting up the development environment: Remix and Metamask.
Overview of Decentralized Exchanges (DEX)
A Decentralized Exchange (DEX) is a platform where users can trade different cryptocurrencies directly with one another without the need for an intermediary or centralized authority. In contrast to traditional cryptocurrency exchanges, where transactions are managed by a central entity, DEXs operate using smart contracts on a blockchain. These contracts allow users to swap tokens or assets without having to trust a middleman.
Introduction to UniswapV2
UniswapV2, being the second version of the Uniswap protocol, is one of the most popular DEXs in the Ethereum ecosystem, which was built on the principle of the Automated Market Maker (AMM). Uniswap protocol allows for trustless token swaps without the need for order books or intermediaries.
Before delving deeper into the mechanics of UniswapV2, let's pause for a moment and backtrack to the earlier version of Uniswap, highlighting the difference with version two.
These are the primary differences, but as with any significant protocol upgrade, there are many other changes and adjustments in the background. The objective of these upgrades in UniswapV2 was to make the protocol more flexible, secure, and efficient. UniswapV2 has made important progress in decentralized finance (DeFi) by adding these new features, as shown in the diagram above, that make it more useful and solidify its position as a top decentralized exchange (DEX) in the Ethereum ecosystem.
Why Was UniswapV2 Needed?
Version two of Uniswap was introduced out of the need to address several limitations in the decentralized trading space. The earlier version, UniswapV1, while groundbreaking, had its challenges highlighted in the table above. Users couldn't swap one type of token directly for another without going through Ethereum first, which was like always needing to change your money to euros before buying pounds. UniswapV1 also had a vulnerability which UniswapV2 aimed to mitigate, you can check out the exploit in detail here. Plus, the system's methods for determining token prices needed refining, and there were opportunities to introduce more advanced trading features. So, in a nutshell, UniswapV2 stepped in to make trading simpler, more efficient, and even more decentralized, setting a new standard in crypto trading.
Why Not Clone UniswapV3?
You may have heard that UniswapV3 is active, and you might be wondering if it's a better choice to clone UniswapV3 instead of V2. Many AMMs, like Sushiswap, are a fork of UniswapV2. UniswapV2 is simpler compared to V3, making it easier to manage. Once you understand how UniswapV2 works, you'll have a good grasp of most AMMs, which will make it relatively straightforward to adapt to UniswapV3 as well.
Understanding UniswapV2 Architecture: Core vs Periphery
Before we consider cloning UniswapV2, It's important that you understand how UniswapV2 is structured first. UniswapV2's architecture consists of two parts: the Core and the Periphery.
The core part of the protocol handles its essential functions, like making and overseeing liquidity pools and enabling token exchanges. This core consists of the following three different contracts:
Pair Contract
Factory Contract
ERC20 Contract
On the other hand, the periphery is for interacting with the core. It comprises the Router Contract. We will explain the roles of these contracts in the next section.
UniswapV2's Core
Now, let's delve deeper into the core contracts of UniswapV2 and examine them closely. You can find the contracts here:
UniswapV2Factory.sol
The primary role of the factory contract is to create and manage the individual token pair contracts (liquidity pools) on Uniswap. Whenever a new token pair is to be created on Uniswap, this contract gets to work. It ensures that for each unique pair, there's only one corresponding pool. It keeps track of all the created pairs. So, when a user or a dApp wants to interact with a specific pair, the Factory provides the corresponding address of that pair's contract.
UniswapV2Pair.sol
Liquidity Pools: Liquidity pools are the big banks of tokens where users can trade. Liquidity providers put their tokens into these pools. They make decentralized trading work by ensuring there are always tokens available to trade with. Instead of waiting for a buyer or seller, like in traditional order-book-based exchanges, you're basically trading with the pool directly.
Token Reserves: Each pair contract maintains reserves of the two tokens, for example WETH/USDC pair. The contract keeps these reserves in balance according to the trades happening on the platform.
Adding/Removing Liquidity: Users can provide liquidity to a pair by depositing both tokens into the contract; for example, a user who wants to provide liquidity for the WETH/USDC pair will deposit both WETH and USDC tokens into the contract. In return, they receive liquidity tokens, which represent their share in the pool called the LP token. When they want to withdraw their provided liquidity, they can redeem these tokens.
Handling Token Swaps: When a user wants to swap one token for another, this contract processes the request. The reserves are adjusted, and based on a formula (the x*y=k invariant), it determines the amount of tokens the user receives. If you want to understand how much amount of tokens the user receives based on the constant product formula used by AMMs, checkout this video.
UniswapV2ERC20.sol
The UniswapV2ERC20 contract represents a certificate (LP Token) given to users when they provide tokens to UniswapV2's trading pairs. It's like when you give your coat at a restaurant's coat check, and you get a token or receipt in return. You can use that token later to claim your coat back.
Together, these core contracts form the foundation of UniswapV2, each with its specific role to make sure users have a smooth decentralized trading experience. This setup not only makes trading easy but also encourages users to be active in the ecosystem, all while keeping the platform secure and efficient.
UniswapV2's Periphery
The periphery contracts in UniswapV2 function as the interface layer for developers. While you can interact with the core contracts directly, it’s recommended to go through the periphery also known as Router, primarily to avoid potential pitfalls.
Periphery Contracts
UniswapV2Router01.sol: Consider this contract an older version of the interface. It has some problems, which is why we don't recommend using it anymore. But keep in mind, periphery contracts, like this one, don't hold any assets, so it's relatively easy to phase them out and switch to UniswapV2Router02 instead.
UniswapV2Router02.sol: This is the contract you should use for most Uniswap actions. It's the newer and more reliable interface that we suggest for integrating with dApps or making external calls. It simplifies your experience by hiding many of the complexities of direct core interaction.
UniswapV2Migrator.sol: This contract is used to migrate liquidity from UniswapV1 to UniswapV2.
Tutorial: Cloning the UniswapV2 Smart Contracts
Now, let's dive into the step-by-step process of cloning UniswapV2. If you want to build your own version of UniswapV2, whether it's for customization or learning purposes, you can use this guide.
1
Accessing the Contracts
1.1
- Core.sol
- Multicall.sol
- Router
- WETH
1.2
createPair
functionality, which creates new token trading pairs.- IUniswapV2Pair: This is the interface for the UniswapV2 Pair contract. It highlights the necessary functionalities a pair (token trading pair) must implement, such as managing liquidity or fetching reserves.
- IUniswapERC20: This defines the standard functions that the Uniswap version of the ERC20 contract should have.
- IERC20: This is the interface for the standard ERC20 token. It provides a blueprint for functions like
transfer
,approve
,balanceOf
, and so on. - IUniswapV2Callee: is an interface for the callback function used in UniswapV2 to handle certain advanced trading operations, enabling trade interactions.
- UniswapV2ERC20: This is Uniswap's own implementation of the ERC20 token standard. It includes functionalities from the IUniswapERC20 interface and has specifics that make it tailored for Uniswap's liquidity provision system.
- UniswapV2Pair: This implements the actual behavior of a token trading pair on Uniswap. It manages the reserves of each token in the pair, handles swaps, and deals with liquidity events.
- UniswapV2Factory: This is the contract that manages the creation of new token trading pairs. It's where new pairs are created, and it maintains a record of all available pairs.
- SafeMath library: This library offers mathematical operations with safety checks to prevent actions like overflows and underflows when dealing with arithmetic operations in Solidity.
- Math library: Offers basic math operations, often used in conjunction with SafeMath to facilitate calculations safely.
- UQ112x112: This is a library used for handling floating-point numbers since solidity lacks native support for floating-point numbers. So, UQ112x112 serves as the necessary library whenever floating-point numbers are required in Uniswap such as calculating token prices within liquidity pools and determining the token amounts involved in trade executions.
1.3
- IUniswapV2Factory
- IUniswapV2Pair
- IERC20
- SafeMath library
- IUniswapV2Router01: The interface which defines the available functions for interacting with UniswapV2. Other contracts use it to perform actions like swapping tokens, adding liquidity, and removing liquidity.
- IUniswapV2Router02: This is an updated version of IUniswapV2Router01, offering new features like the ability to swap tokens across multiple liquidity pools.
- IWETH: This contract interface defines the functions for interacting with WETH.
- UniswapV2Library: This library contract contains various functions used by the UniswapV2 protocol, including calculations for prices, retrieving liquidity pool reserves, and performing swaps.
- TransferHelper: This library contract includes functions for transferring tokens between addresses. The UniswapV2 protocol uses these functions for swaps, as well as adding and removing liquidity.
1.4
- Multicall.sol: This is the contract that allows you to batch multiple calls to other smart contracts in a single transaction.
- WETH.sol: This contract represents Wrapped ETH (WETH). WETH is an ERC-20 token that is pegged to the price of ETH. It is used in Uniswap to facilitate swaps between ERC-20 tokens.
2
Deployment of the UniswapV2Factory Contract
2.1
2.2
feeSetter
is the address responsible for adjusting the fees charged for swaps in Uniswap protocol.2.3
feeSetter
, proceed by clicking the Deploy tab to initiate the deployment of the Factory contract. This action will trigger a confirmation request for the contract deployment transaction in Metamask, similar to the following:2.4
3
UniswapV2Factory Contract Interaction
createPair()
function takes in two token addresses. When called, this function creates a new liquidity pool for these tokens if it doesn’t already exist.3.1
- To set the address that will receive the fees from swaps on the protocol, you would call the
setFeeTo()
function and pass in the address of the recipient. - To set the address that can change the
feeTo
address, you would call thesetFeeToSetter()
function and pass in the address of the newfeeToSetter
. - To get an array of all the liquidity pool addresses that have been created by the contract, you would call the
allPairs()
function. - To get the number of liquidity pool addresses that have been created by the contract, you would call the
allPairsLength()
function. - To get the address that is currently receiving the fees from swaps on the protocol, you would call the
feeTo()
function. - To get the address that can change the feeTo address, you would call the
feeToSetter()
function. - To get the address of the liquidity pool that contains the two tokens specified by the tokenA and tokenB addresses, you would call the
getPair()
function.
3.2
INIT_CODE_PAIR_HASH
function to get the Init_hash
.INIT_CODE_PAIR_HASH
constant is a hash of the bytecode of the UniswapV2Pair contract, which is used to verify the authenticity of the UniswapV2Pair contract when it is deployed. When a new UniswapV2Pair contract is deployed, the contract's bytecode is hashed and compared to the INIT_CODE_PAIR_HASH
constant. If the hashes match, then the contract is verified as authentic.3.3
Init_hash
in the pairFor()
function and remove the 0x
prefix. In this case, we should have this:246ba35fa37fb6bf3b71cd68dd22514e93a5d804976bfd9895b26b4434adb34d
3.4
4
UniswapV2Router02 and Multicall Contract Deployment
4.1
addLiquidity
function, removing liquidity with the removeLiquidity
function, and so on.4.2
4.3
Uniswap Version Roadmap
UniswapV3 was launched in May 2021. It is a major upgrade over V2, with a number of new features and improvements. V3 allows liquidity providers to specify the price range they want to provide liquidity for, which can help to reduce slippage and improve capital efficiency. V3 also supports multiple tokens per pool, which can make it easier to find liquidity for less popular tokens.
UniswapV4 builds on the improvements of V3 with even more features and functionality. Some of the features of V4 include:
Hooks: Buttons that developers can tweak to add new options to their pools, enabling things like dynamic swap fees, trading limits, and integration with other protocols for extra yield.
Singleton Contract: A unified contract for all token pairs, boosting gas efficiency and slashing the deployment cost of new pairs. It includes a flash accounting system for reduced gas fees.
Native ETH: Direct pairing with ETH, bypassing the need for WETH, making trades simpler and less costly.
Improved Developer Experience: V4 enhances the developer toolkit with features like ERC-1155 accounting and governance controls for better fee and setting management.
The future roadmap of Uniswap is still being developed, but it is clear that the protocol is constantly evolving and improving. As the DeFi space continues to grow, Uniswap is well-positioned to remain a leading player.
Conclusion
In this tutorial, we've explored decentralized exchanges, specifically diving into the inner workings of UniswapV2. We've covered everything from its structure to its core and periphery components, giving us the knowledge to create and deploy these contracts on a testnet. While we've mainly focused on V2, DeFi keeps evolving, with UniswapV3 and V4 already here. However, understanding V2 serves as a strong foundation for future endeavours. As developers, there are exciting opportunities ahead to innovate, improve, and delve deeper into the intricacies of UniswapV3. The decentralized future awaits your contributions.