Introduction
Factory Contract
Factory Implementation
parameters = PoolParameters({
factory: address(this),
token0: tokenX,
token1: tokenY,
tickSpacing: tickSpacing
});
pool = address(
new UniswapV3Pool{
salt: keccak256(abi.encodePacked(tokenX, tokenY, tickSpacing))
}()
);
delete parameters;
This piece looks weird because parameters
is not used. Uniswap uses Inversion of Control to pass parameters to a pool during deployment. Let’s look at the updated Pool contract constructor:
// src/UniswapV3Pool.sol
contract UniswapV3Pool is IUniswapV3Pool {
...
constructor() {
(factory, token0, token1, tickSpacing) = IUniswapV3PoolDeployer(
msg.sender
).parameters();
}
..
}
Aha! Pool expects its deployer to implement the IUniswapV3PoolDeployer
interface (which only defines the parameters()
getter) and calls it in the constructor during deployment to get the parameters. This is what the flow looks like:
Factory
: definesparameters
state variable (implementsIUniswapV3PoolDeployer
) and sets it before deploying a pool.Factory
: deploys a pool.Pool
: in the constructor, calls theparameters()
function on its deployer and expects that pool parameters are returned.Factory
: callsdelete parameters;
to clean up the slot of theparameters
state variable and to reduce gas consumption. This is a temporary state variable that has a value only during a call tocreatePool()
.
Pool Initialization
The PoolAddress
Library
Swap Path
Path Library
In code, a swap path is a sequence of bytes. In Solidity, a path can be built like this:
bytes.concat(
bytes20(address(weth)),
bytes3(uint24(60)),
bytes20(address(usdc)),
bytes3(uint24(10)),
bytes20(address(usdt)),
bytes3(uint24(60)),
bytes20(address(wbtc))
);
These are the functions that we’ll need to implement:
- calculating the number of pools in a path;
- figuring out if a path has multiple pools;
- extracting first pool parameters from a path;
- proceeding to the next pair in a path;
- and decoding first pool parameters.
Multi-Pool Swaps
User Interface
Auto Router npm Package
npm: @uniswap/smart-order-router
Code
https://github.com/Uniswap/swap-router-contracts
Logic
How do we find the shortest path between two tokens in such a mess?
The most suitable solution for such kinds of tasks is based on a graph. A graph is a data structure that consists of nodes (objects representing something) and edges (links connecting nodes). We can turn that mess of pools into a graph where each node is a token (that has a pool) and each edge is a pool this token belongs to. So a pool represented as a graph is two nodes connected with an edge. The above pools become this graph:
The biggest advantage graphs give us is the ability to traverse its nodes, from one node to another, to find paths. Specifically, we’ll use A* search algorithm. Feel free to learn about how the algorithm works, but, in our app, we’ll use a library to make our life easier. The set of libraries we’ll use is ngraph.ngraph for building graphs and ngraph.path for finding paths (it’s the latter that implements A* search algorithm, as well as some others).
In the UI app, let’s create a pathfinder. This will be a class that, when instantiated, turns a list of pairs into a graph to later use the graph to find the shortest path between two tokens.
https://github.com/Jeiwan/uniswapv3-code/blob/milestone_4/ui/src/lib/pathFinder.js
Tick Rounding
Code
Check repo