Author: Alexey Kutsenko 👨💻
In this guide, we will show how we quickly and easily forked Uniswap V2.
There are two main repositories:
The uniswap-v2-core repository contains the key smart contracts: UniswapV2ERC20.sol
, UniswapV2Factory.sol
, and UniswapV2Pair.sol
, which implement the core functionality of the Uniswap V2 protocol. These smart contracts enable decentralized token exchange.
You need to deploy:
UniswapV2Factory.sol
. Responsible for creating and managing exchange pairs (liquidity pools). The factory smart contract also tracks all existing pairs and their addresses. When a new token pair is created, the factory deploys a newUniswapV2Pair.sol
smart contract, which represents the liquidity pool for this pair. TheUniswapV2Factory.sol
smart contract, in turn, importsUniswapV2Pair.sol
, which inherits fromUniswapV2ERC20.sol
.
The uniswap-v2-periphery
repository contains auxiliary smart contracts: UniswapV2Migrator.sol
, UniswapV2Router01.sol
, UniswapV2Router02.sol
, and libraries: SafeMath.sol
, UniswapV2Library.sol
, UniswapV2LiquidityMathLibrary.sol
, and UniswapV2OracleLibrary.sol
. These interact with the core smart contracts from uniswap-v2-core
. These smart contracts simplify interaction with the protocol for users and developers.
You need to deploy:
UniswapV2Router02.sol
. Provides a high-level interface for interacting with Uniswap pairs and pools. The main functionality includes functions for token swaps, adding liquidity, and removing liquidity. It includes the necessary libraries for protocol operation.
Thus, a user's interaction with the UniswapV2Router02.sol
smart contract can be represented as a diagram:
And the creation of a new pair smart contract looks like this:
To solve our task, three main steps are required: preparing the codebase, configuring the smart contracts, and deploying the smart contracts.
Make sure that Foundry it is installed, you can check with forge --version
.
At this stage, we initialize our project and clone the Uniswap V2 smart contract repositories.
- Initialize the project:
forge init UNISWAP-V2-FORK-SC && cd ./UNISWAP-V2-FORK-SC
- Add the v2-core and v2-periphery smart contracts:
git submodule add https://github.com/Uniswap/v2-core.git contracts/v2-core
andgit submodule add https://github.com/Uniswap/v2-periphery.git contracts/v2-periphery
- Add the
uniswap-lib
library, which is used inv2-periphery
smart contracts:git submodule add https://github.com/Uniswap/uniswap-lib lib/uniswap-lib
and the Foundry library required for scripts/testsgit submodule add https://github.com/foundry-rs/forge-std lib/forge-std
- Update the source path in our
foundry.toml
file:
//foundry.toml
[profile.default]
src = "contracts"
out = "out"
libs = ["lib"]
optimizer = true
optimizer_runs = 999999
- Add the file to the root directory:
//remappings.txt
@uniswap/lib/=lib/uniswap-lib/
@uniswap/v2-core/=contracts/v2-core/
@uniswap/v2-periphery/=contracts/v2-periphery/
forge-std/=lib/forge-std/src/
Uniswap uses the pairFor
method from the library smart contract for the operation of the UniswapV2Router02
smart contract UniswapV2Library.sol.
Using this method, the pair smart contract address is calculated based on:
hex'ff'
- the address of the smart contract
Factory.sol
- the addresses of the ERC-20 tokens in the pool
init code hash
.
Thus, Uniswap does not make any external calls to retrieve the pair address.
The init code hash
is the keccak256 hash of the bytecode responsible for deploying the UniswapV2Pair.sol
smart contract.
There are several ways to obtain it:
a) By adding code to the smart contract:
Add the following line to the Factory.sol
smart contract bytes32 public constant INIT_CODE_HASH = keccak256(abi.encodePacked(type(UniswapV2Pair).creationCode));![alt text](./images/uniswap-factory-hash.png) After deploying the
Factory.solsmart contract, we could retrieve the
init code hashvia the
INIT_CODE_HASH` method.
b) Via a JavaScript script:
const { ethers } = require('ethers');
const fs = require('fs');
const path = require('path');
const jsonFilePath = path.resolve(__dirname, '../out/UniswapV2Pair.sol/UniswapV2Pair.json');
async function computeInitCodeHash() {
try {
// Reading the ABI JSON file.
const contractJson = JSON.parse(fs.readFileSync(jsonFilePath, 'utf8'));
// Check for the presence of bytecode in the ABI JSON file
if (!contractJson.bytecode.object) {
throw new Error('Bytecode not found in the ABI JSON file');
}
// Calculate `INIT_CODE_HASH` using the smart contract bytecode.
const computedInitCodeHash = ethers.keccak256(contractJson.bytecode.object);
console.log('INIT_CODE_HASH:', computedInitCodeHash);
return computedInitCodeHash;
} catch (error) {
console.error('Error while calculating `INIT_CODE_HASH`:', error);
}
}
computeInitCodeHash();
To avoid adding code to the smart contract and modifying the original Uniswap smart contracts, we will calculate the hash using JavaScript.
As you may notice, the calculation uses the ethers
library, which can be installed with npm install --save ethers
Next, to get the smart contract bytecode, we need to run forge build
Finally, execute the calculation script node ./script/compute.js
The result will be displayed as follows:
The obtained hash must be inserted into the Uniswap library file UniswapV2Library.sol
without the 0x
prefix
Now, we proceed to the actual deployment of the smart contracts.
- Deploy the
Factory
smart contract. Its main functionality is the creation of token pairs.
forge create src/v2-core/UniswapV2Factory.sol:UniswapV2Factory --rpc-url https://polygonzkevm-mainnet.g.alchemy.com/v2/demo --private-key putYourPrivatekeyHere --constructor-args "putFeeToSetterAddressHere" --verify --etherscan-api-key ACCESS_KEY
https://polygonzkevm-mainnet.g.alchemy.com/v2/demo
- The RPC network where the deployment will take place.
putYourPrivatekeyHere
- The private key to be used for the deployment.
putFeeToSetterAddressHere
- The address that will be able to set the protocol fee.
ACCESS_KEY
- API key for smart contract verification. You can get it here. To do this, you need to register and create an API key.
During deployment, an error may occur if the blockchain does not support EIP-1559. In this case, you need to add the --legacy
flag.
_If you are a Windows user, you might encounter the error: "Failed to create wallet from private key. Private key is invalid hex: Odd number of digits." To resolve this issue, you need to remove the \r
symbol from the private key. You can do this by running the command PRIVATE_KEY=$(echo $PRIVATE_KEY | tr -d '\r')
- The
UniswapV2Router02
smart contract. It is responsible for adding and removing liquidity in pools and token swaps
forge create src/v2-periphery/UniswapV2Router02.sol:UniswapV2Router02 --rpc-url https://polygonzkevm-mainnet.g.alchemy.com/v2/demo --private-key putYourPrivatekeyHere --constructor-args "factoryAddressPutHere" "WETHAddressPutHere" --verify --etherscan-api-key ACCESS_KEY
https://polygonzkevm-mainnet.g.alchemy.com/v2/demo
- The RPC network where the deployment will take place.
putYourPrivatekeyHere
- The private key to be used for the deployment.
factoryAddressPutHere
- The address of the Factory
smart contract.
WETHAddressPutHere
- Wrapped Ether (WETH) address.
ACCESS_KEY
- API key for smart contract verification.
For the testnet, we deployed the original code of the WETH smart contract.
- Smart contract multicall.
The smart contract is designed for:
- Aggregating read results from multiple smart contracts into a single JSON-RPC request.
- Executing multiple state-changing blockchain calls within a single transaction.
You can read more details here.
Although this smart contract is not part of the group of smart contracts required for its operation, it is still necessary for integrating the frontend.
At the time of writing this guide, this smart contract is already deployed on the Polygon zkEVM testnet. You can view the full list of networks where the smart contract has already been deployed here.
Forking and deploying Uniswap V2 smart contracts may seem like a challenging task, but with the right tools and approach, it becomes much more accessible. In this guide, we covered the steps required to clone the source code, configure and deploy the smart contracts, and calculate the init code hash without modifying the original contracts.
Using libraries and tools such as Foundry and ethers.js significantly simplifies the process of developing and deploying smart contracts, allowing developers to focus on key aspects of integration and customization.
This guide demonstrates that with an understanding of basic concepts and accessible tools, developers can effectively fork and adapt popular protocols for their needs. These skills pave the way for creating new projects and products based on proven solutions like Uniswap V2.