# Generalized Swapping

This will be the hardest chapter of this milestone. Before updating the code, we need to understand how the algorithm of swapping in Uniswap V3 works.

You can think of a swap as of filling of an order: a user submits an order to buy a specified amount of tokens from a pool. The pool will use the available liquidity to “convert” the input amount into an output amount of the other token. If there’s not enough liquidity in the current price range, it’ll try to find liquidity in other price ranges (using the function we implemented in the previous chapter).

We’re now going to implement this logic in the `swap`

function, however going to stay only within the current price range for now–we’ll implement cross-tick swaps in the next milestone.

```
function swap(
address recipient,
bool zeroForOne,
uint256 amountSpecified,
bytes calldata data
) public returns (int256 amount0, int256 amount1) {
...
```

In the `swap`

function, we add two new parameters: `zeroForOne`

and `amountSpecified`

. `zeroForOne`

is the flag that controls swap direction: when `true`

, `token0`

is traded in for `token1`

; when `false,`

it’s the opposite. For example, if `token0`

is ETH and `token1`

is USDC, setting `zeroForOne`

to `true`

means buying USDC for ETH. `amountSpecified`

is the number of tokens the user wants to sell.

## Filling Orders

Since, in Uniswap V3, liquidity is stored in multiple price ranges, the Pool contract needs to find all liquidity that’s required to “fill an order” from the user. This is done via iterating over initialized ticks in a direction chosen by the user.

Before continuing, we need to define two new structures:

```
struct SwapState {
uint256 amountSpecifiedRemaining;
uint256 amountCalculated;
uint160 sqrtPriceX96;
int24 tick;
}
struct StepState {
uint160 sqrtPriceStartX96;
int24 nextTick;
uint160 sqrtPriceNextX96;
uint256 amountIn;
uint256 amountOut;
}
```

`SwapState`

maintains the current swap’s state. `amountSpecifiedRemaining`

tracks the remaining amount of tokens that need to be bought by the pool. When it’s zero, the swap is done. `amountCalculated`

is the out amount calculated by the contract. `sqrtPriceX96`

and `tick`

are the new current price and tick after a swap is done.

`StepState`

maintains the current swap step’s state. This structure tracks the state of **one iteration** of an “order filling”. `sqrtPriceStartX96`

tracks the price the iteration begins with. `nextTick`

is the next initialized tick that will provide liquidity for the swap and `sqrtPriceNextX96`

is the price at the next tick. `amountIn`

and `amountOut`

are amounts that can be provided by the liquidity of the current iteration.

After we implement cross-tick swaps (that is, swaps that happen across multiple price ranges), the idea of iterating will be clearer.

```
// src/UniswapV3Pool.sol
function swap(...) {
Slot0 memory slot0_ = slot0;
SwapState memory state = SwapState({
amountSpecifiedRemaining: amountSpecified,
amountCalculated: 0,
sqrtPriceX96: slot0_.sqrtPriceX96,
tick: slot0_.tick
});
...
```

Before filling an order, we initialize a `SwapState`

instance. We’ll loop until `amountSpecifiedRemaining`

is 0, which will mean that the pool has enough liquidity to buy `amountSpecified`

tokens from the user.

```
...
while (state.amountSpecifiedRemaining > 0) {
StepState memory step;
step.sqrtPriceStartX96 = state.sqrtPriceX96;
(step.nextTick, ) = tickBitmap.nextInitializedTickWithinOneWord(
state.tick,
1,
zeroForOne
);
step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.nextTick);
```

In the loop, we set up a price range that should provide liquidity for the swap. The range is from `state.sqrtPriceX96`

to `step.sqrtPriceNextX96`

, where the latter is the price at the next initialized tick (as returned by `nextInitializedTickWithinOneWord`

–we know this function from a previous chapter).

```
(state.sqrtPriceX96, step.amountIn, step.amountOut) = SwapMath
.computeSwapStep(
state.sqrtPriceX96,
step.sqrtPriceNextX96,
liquidity,
state.amountSpecifiedRemaining
);
```

Next, we’re calculating the amounts that can be provided by the current price range, and the new current price the swap will result in.

```
state.amountSpecifiedRemaining -= step.amountIn;
state.amountCalculated += step.amountOut;
state.tick = TickMath.getTickAtSqrtRatio(state.sqrtPriceX96);
}
```

The final step in the loop is updating the SwapState. `step.amountIn`

is the number of tokens the price range can buy from the user; `step.amountOut`

is the related number of the other token the pool can sell to the user. `state.sqrtPriceX96`

is the current price that will be set after the swap (recall that trading changes current price).

## SwapMath Contract

Let’s look closer at `SwapMath.computeSwapStep`

.

```
// src/lib/SwapMath.sol
function computeSwapStep(
uint160 sqrtPriceCurrentX96,
uint160 sqrtPriceTargetX96,
uint128 liquidity,
uint256 amountRemaining
)
internal
pure
returns (
uint160 sqrtPriceNextX96,
uint256 amountIn,
uint256 amountOut
)
{
...
```

This is the core logic of swapping. The function calculates swap amounts within one price range and respecting available liquidity. It’ll return: the new current price and input and output token amounts. Even though the input amount is provided by the user, we still calculate it to know how much of the user-specified input amount was processed by one call to `computeSwapStep`

.

```
bool zeroForOne = sqrtPriceCurrentX96 >= sqrtPriceTargetX96;
sqrtPriceNextX96 = Math.getNextSqrtPriceFromInput(
sqrtPriceCurrentX96,
liquidity,
amountRemaining,
zeroForOne
);
```

By checking the price, we can determine the direction of the swap. Knowing the direction, we can calculate the price after swapping the `amountRemaining`

of tokens. We’ll return to this function below.

After finding the new price, we can calculate the input and output amounts of the swap using the function we already have ( the same functions we used to calculate token amounts from liquidity in the `mint`

function):

```
amountIn = Math.calcAmount0Delta(
sqrtPriceCurrentX96,
sqrtPriceNextX96,
liquidity
);
amountOut = Math.calcAmount1Delta(
sqrtPriceCurrentX96,
sqrtPriceNextX96,
liquidity
);
```

And swap the amounts if the direction is opposite:

```
if (!zeroForOne) {
(amountIn, amountOut) = (amountOut, amountIn);
}
```

That’s it for `computeSwapStep`

!

## Finding Price by Swap Amount

Let’s now look at `Math.getNextSqrtPriceFromInput`

–the function calculates a $P $ given another $P $, liquidity, and input amount. It tells what the price will be after swapping the specified input amount of tokens, given the current price and liquidity.

The good news is that we already know the formulas: recall how we calculated `price_next`

in Python:

```
# When amount_in is token0
price_next = int((liq * q96 * sqrtp_cur) // (liq * q96 + amount_in * sqrtp_cur))
# When amount_in is token1
price_next = sqrtp_cur + (amount_in * q96) // liq
```

We’re going to implement this in Solidity:

```
// src/lib/Math.sol
function getNextSqrtPriceFromInput(
uint160 sqrtPriceX96,
uint128 liquidity,
uint256 amountIn,
bool zeroForOne
) internal pure returns (uint160 sqrtPriceNextX96) {
sqrtPriceNextX96 = zeroForOne
? getNextSqrtPriceFromAmount0RoundingUp(
sqrtPriceX96,
liquidity,
amountIn
)
: getNextSqrtPriceFromAmount1RoundingDown(
sqrtPriceX96,
liquidity,
amountIn
);
}
```

The function handles swapping in both directions. Since calculations are different, we’ll implement them in separate functions.

```
function getNextSqrtPriceFromAmount0RoundingUp(
uint160 sqrtPriceX96,
uint128 liquidity,
uint256 amountIn
) internal pure returns (uint160) {
uint256 numerator = uint256(liquidity) << FixedPoint96.RESOLUTION;
uint256 product = amountIn * sqrtPriceX96;
if (product / amountIn == sqrtPriceX96) {
uint256 denominator = numerator + product;
if (denominator >= numerator) {
return
uint160(
mulDivRoundingUp(numerator, sqrtPriceX96, denominator)
);
}
}
return
uint160(
divRoundingUp(numerator, (numerator / sqrtPriceX96) + amountIn)
);
}
```

In this function, we’re implementing two formulas. At the first `return`

, it implements the same formula we implemented in Python. This is the most precise formula, but it can overflow when multiplying `amountIn`

by `sqrtPriceX96`

. The formula is (we discussed it in “Output Amount Calculation”):
$P_{target} =ΔxP +LP L $

When it overflows, we use an alternative formula, which is less precise: $P_{target} =Δx+P L L $

Which is simply the previous formula with the numerator and the denominator divided by $P $ to get rid of the multiplication in the numerator.

The other function has simpler math:

```
function getNextSqrtPriceFromAmount1RoundingDown(
uint160 sqrtPriceX96,
uint128 liquidity,
uint256 amountIn
) internal pure returns (uint160) {
return
sqrtPriceX96 +
uint160((amountIn << FixedPoint96.RESOLUTION) / liquidity);
}
```

## Finishing the Swap

Now, let’s return to the `swap`

function and finish it.

By this moment, we have looped over the next initialized ticks, filled `amountSpecified`

specified by the user, calculated input and amount amounts, and found a new price and tick. Since, in this milestone, we’re implementing only swaps within one price range, this is enough. We now need to update the contract’s state, send tokens to the user, and get tokens in exchange.

```
if (state.tick != slot0_.tick) {
(slot0.sqrtPriceX96, slot0.tick) = (state.sqrtPriceX96, state.tick);
}
```

First, we set a new price and tick. Since this operation writes to the contract’s storage, we want to do it only if the new tick is different, to optimize gas consumption.

```
(amount0, amount1) = zeroForOne
? (
int256(amountSpecified - state.amountSpecifiedRemaining),
-int256(state.amountCalculated)
)
: (
-int256(state.amountCalculated),
int256(amountSpecified - state.amountSpecifiedRemaining)
);
```

Next, we calculate swap amounts based on the swap direction and the amounts calculated during the swap loop.

```
if (zeroForOne) {
IERC20(token1).transfer(recipient, uint256(-amount1));
uint256 balance0Before = balance0();
IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(
amount0,
amount1,
data
);
if (balance0Before + uint256(amount0) > balance0())
revert InsufficientInputAmount();
} else {
IERC20(token0).transfer(recipient, uint256(-amount0));
uint256 balance1Before = balance1();
IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(
amount0,
amount1,
data
);
if (balance1Before + uint256(amount1) > balance1())
revert InsufficientInputAmount();
}
```

Next, we exchange tokens with the user, depending on the swap direction. This piece is identical to what we had in Milestone 2, only handling of the other swap direction was added.

That’s it! Swapping is done!

## Testing

The tests won’t change significantly, we only need to pass the `amountSpecified`

and `zeroForOne`

to the `swap`

function. Output amount will change insignificantly though, because it’s now calculated in Solidity.

We can now test swapping in the opposite direction! I’ll leave this for you, as homework (just be sure to choose a small input amount so the whole swap can be handled by our single price range). Don’t hesitate to peek at my tests if this feels difficult!