This lesson will test our understanding of the Pool, Router and Quoter contracts.

It’s all fine and good to understand what the contracts do, but the goal of MEV enjoyooooors is to identify and profit from misaligned markets. Identifying misaligned markets is all about prices, prices, prices!

So we will take our very familiar and comfy DAI/WETH pool, run a local fork, and then beat on this pool until we understand how swaps affect the price, how liquidity and ticks are affected for various trade sizes, and how to properly predict the end state for some fictional trades.

The end goal is to build another set of degenbot helper classes. Again, nice to know how all this works, but even better to figure it out and then abstract away the details.

## Lesson Structure

This will be a two-part lesson. The first part covers how to predict and verify swaps within a single tick boundary. The second part covers the same topic, but for swaps that cross tick boundaries.

## Console Exploration

This will be a console-heavy lesson, so let’s get ready. Spin up your local fork at block height **15686250**.

We will be working with these addresses:

Liquidity Pool (DAI/WETH): 0xC2e9F25Be6257c210d7Adf0D4Cd6E3E881ba25f8

Quoter: 0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6

QuoterV2: 0x61fFE014bA17989E743c5F6cB21bF9697530B21e

Router: 0xE592427A0AEce92De3Edee1F18E0157C05861564

WETH: 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2

DAI: 0x6B175474E89094C44Da98b954EedeAC495271d0F

```
>>> lp = Contract.from_explorer(
'0xC2e9F25Be6257c210d7Adf0D4Cd6E3E881ba25f8'
)
>>> quoter = Contract.from_explorer(
'0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6'
)
>>> quoterv2 = Contract.from_explorer(
'0x61fFE014bA17989E743c5F6cB21bF9697530B21e'
)
>>> router = Contract.from_explorer(
'0xE592427A0AEce92De3Edee1F18E0157C05861564'
)
>>> weth = Contract.from_explorer(
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'
)
>>> dai = Contract.from_explorer(
'0x6B175474E89094C44Da98b954EedeAC495271d0F'
)
```

Now let’s get ourselves some fake WETH, grant approval for swapping it via the router, and set a chain snapshot to easily return to “zero”.

```
>>> weth.deposit({'from':accounts[0],'value':500*10**18})
Transaction sent: 0x06e240acfbd41fed3a1d6fc68e2ce93f369419fa60c5d62e8d6c5850b24463b7
Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 6
Transaction confirmed Block: 15686252 Gas used: 43738 (0.36%)
<Transaction '0x06e240acfbd41fed3a1d6fc68e2ce93f369419fa60c5d62e8d6c5850b24463b7'>
>>> weth.approve(router.address,500*10**18,{'from':accounts[0]})
Transaction sent: 0xb98a364af99c1e896bcca4ec40f5add4b59f3744a1274d913db47233dbb7e348
Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 7
Transaction confirmed Block: 15686253 Gas used: 43964 (0.37%)
<Transaction '0xb98a364af99c1e896bcca4ec40f5add4b59f3744a1274d913db47233dbb7e348'>
>>> chain.snapshot()
>>> chain.height
15686253
```

Now we have 500 WETH, a liquidity pool, and freedom to poke at it until we achieve enlightenment.

A Zen koan for this exercise: what is the sound of one pool swapping?

### Same-Tick Swap

The first thing we want to test is swap behavior of the pool inside a single tick. We’ll do it by calculating a WETH swap that will keep us inside the current tick.

First let’s find the current tick, sqrtPriceX96, and liquidity, then get a quote to see the effect:

```
>>> current_sqrtpricex96, current_tick, *others = lp.slot0()
>>> current_tick
-72269
>>> lp.liquidity()
1548654344361108748118783
```

Recall again that liquidity is provided inside of a tick range. The lower boundary of the current tick range will be at `tickSpacing * (current_tick // tickSpacing)`

and the upper boundary will be at `tickSpacing * (current_tick // tickSpacing + 1)`

.

Here are those values:

```
>>> lp.tickSpacing() * (current_tick//lp.tickSpacing())
-72300
>>> lp.tickSpacing() * (current_tick//lp.tickSpacing() + 1)
-72240
```

So the largest possible single-tick swap in the “positive” direction will drive the tick to -72240.

From the white paper, we find this equation:

Δ𝑥 = Δ(1 / √𝑃) · 𝐿 (Eq. 6.16 - token0 (x) swapped out for a given change in `sqrtPrice`

)

And we know that `sqrtPrice`

is a function of ticks:

𝑖𝑐 = log(base=√1.0001)√𝑃 (Eq. 6.8 - tick index as a function of sqrtPrice)

First, verify equation 6.8 against the current pool state:

```
>>> current_sqrtpricex96
2136349984626364120081779203
>>> import math
>>> math.log(
current_sqrtpricex96*2**(-96),
math.sqrt(1.0001)
)
-72268.27446817268
```

NOTE: the pool contract stores `sqrtPriceX96`

as a Q64.96 value, so we transform it to `sqrtPrice`

by multiplying by 2^-96.

EVM does floor division by default, so we can modify this a little by surrounding the expression with `floor()`

:

```
>>> math.floor(
math.log(
current_sqrtpricex96*2**(-96),
math.sqrt(1.0001)
)
)
-72269
```

So now we’ve verified that the current tick matches the current `sqrtPrice`

. And we know that the a maximum single-tick swap will drive the tick up to -72240. We know the current tick and the final tick, which allows us to solve for the change in `sqrtPrice`

.

The `sqrtPrice`

at tick -72240 is, using equation 6.2 from the white paper:

```
>>> 1.0001**(-72240/2)
0.02700267315767631
```

And the current `sqrtPrice`

is:

```
>>> current_sqrtpricex96*2**(-96)
0.02696452772385997
```

From the white paper, we know some things:

`Liquidity`

𝐿 does not change inside a tick intervalThe virtual reserves of token0 (𝑥) and token1 (𝑦) are related to

`Liquidity`

, 𝐿 = √𝑥𝑦 (Eq. 6.3)Token swaps inside a single tick are related only to

`Liquidity`

and`sqrtPrice`

(Eq. 6.14 and 6.16)

From our results above, we calculate the change in √𝑃:

```
>>> 1.0001**(-72240/2) - current_sqrtpricex96*2**(-96)
3.8145433816340335e-05
```

And from this value, calculate the amount of token1 (WETH) needed to drive the `sqrtPrice`

to our target:

```
>>> (1.0001**(-72240/2)-current_sqrtpricex96*2**(-96)) * lp.liquidity()
5.907409179721461e+19
```

Which is 59.07 WETH, after correcting for decimals.

So let’s do a test swap and check the new results:

```
>>> tx = router.exactInputSingle(
(
weth.address,
dai.address,
3000,
accounts[0],
99999999999999999,
59.074*10**18,
0,
0
),
{'from':accounts[0]}
)
Transaction sent: 0xde1f9912477b0a0b28db27113f6e83233430a8ad26b184f7952ba3755cfd27cb
Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 8
Transaction confirmed Block: 15686254 Gas used: 135755 (1.13%)
>>> lp.slot0()
(2139363105995855777310422166, -72241, 622, 1500, 1500, 0, True)
```

We ended up shy of the target tick by 1. We can confirm we’re still inside the same liquidity range at this new tick:

```
>>> lp.liquidity()
1548654344361108748118783
```

Let’s see how close we can get by reverting the chain and trying it again with more WETH:

## Keep reading with a 7-day free trial

Subscribe to Degen Code to keep reading this post and get 7 days of free access to the full post archives.