Ensure the security of your smart contracts

How to Add a New Pool to Uniswap V3

Author: MixBytes team
Intro
A lot of existing projects use the new third version of a well-known Uniswap protocol as Oracle to get current price for many assets. Uniswap V3 can also be used in one of the strategies like yearn or harvest strategies as a pool of liquidity which can create profits for your deposit through time. Some projects also add integration with Uniswap V3 to increase user experience and make users' lives a little bit easier. In all of these scenarios it is crucial to check how the poisoned pool which can be added by a hacker to uniswap can affect the protocol. In order to check this scenario, we create a test in the hardhat environment.
An example of adding a new liquidity pool to UniswapV3 in PoC
The code snippet in the hardhat testing environment is presented below. All necessary notes to the code are given as well.

const tokenFactory = await ethers.getContractFactory("EvilToken");
const evilToken = await tokenFactory.connect(deployer).deploy();
const positionManager = await ethers.getContractAt('INonfungiblePositionManager', "0xC36442b4a4522E871399CD717aBDD847Ab11FE88");
const WETH = await ethers.getContractAt('IWETH', addresses.tokens.WETH);
// call approve for tokens before adding a new pool
call approveawait WETH.connect(deployer).approve(positionManager.address, ethers.utils.parseEther('0.1'), {gasPrice: 0});
await evilToken.connect(deployer).approve(positionManager.address, ethers.utils.parseEther('100'), {gasPrice: 0});
let multiCallParams = [
// first call
"0x13ead562" + // encoded function signature ( createAndInitializePoolIfNecessary(address, address, uint24, uint160) )
"000000000000000000000000" + evilToken.address.toLowerCase().substring(2) + // token1 address
"000000000000000000000000" +  WETH.address.toLowerCase().substring(2) + // token2 address
"00000000000000000000000000000000000000000000000000000000000001f4" + // fee
"000000000000000000000000000000000000000005b96aabfac7cdc4b3b58fc2", // sqrtPriceX96
// second call
"0x88316456" + // encoded function signature ( mint((address,address,uint24,int24,int24,uint256,uint256,uint256,uint256,address,uint256)) )
"000000000000000000000000" + evilToken.address.toLowerCase().substring(2) + // token1 address
"000000000000000000000000" +  WETH.address.toLowerCase().substring(2) + // token2 address
"00000000000000000000000000000000000000000000000000000000000001f4" + // fee
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff89f0e" + // tick lower
"0000000000000000000000000000000000000000000000000000000000010dd8" + // tick upper
"00000000000000000000000000000000000000000000000ad5a4b6712c4647c3" + // amount 1 desired
"000000000000000000000000000000000000000000000000016345785d8a0000" + // amount 2 desired
"00000000000000000000000000000000000000000000000acebaf563cd50439c" + // min amount 1 expected
"000000000000000000000000000000000000000000000000016261cfc3291456" + // min amount 2 expected 
"000000000000000000000000" + signer3.address.toLowerCase().substring(2) + // deployer address "00000000000000000000000000000000000000000000000000000000610bb8b6" // deadline
];
// adding a new liquidity pool through position manager
await positionManager.connect(deployer).multicall(multiCallParams, {gasPrice: 0});
In this test EvilToken is a standard ERC20 token which can be minted or burned by the deployer, positionManager is one of Uniswap V3 ecosystem's smart contracts which gives access to adding new pools of liquidity, WETH is a token representing wrapped ether. multiCallParams is an array of bytes which contains all necessary parameters to deploy a new liquidity pool. It is worth saying that some of the parameters used in multiCallParams cannot be easily calculated like sqrtPriceX96 or tick lower. To calculate these parameters, you can use a Uniswap V3 dApp in the ropsten network. It is very easy to calculate these parameters through dApp. First you create a new liquidity pool in dApp with parameters that you want to use and then you just decompile your transaction in ropsten etherscan and get values of the necessary parameters in byte representation.
Conclusion
Hereby you have learned how to add a new Uniswap V3 pool with user-created tokens for testing purposes in the hardhat framework. More about useful tools for auditors and developers see in our upcoming articles.
Additional readings and tutorials
Other posts