otemaDiving into UniSwap

By akohad Oct11,2022

[ad_1]

Before you read it, you should watch this video and fully understand everything they say — LINK
We will work with this repository — LINK
And you need to press buttons on UniSwap itself as much as possible.
And here is a bonus documentation of UniSwap — LINK

This mini-scheme may be needed throughout the article to understand what we are doing in general

P.S.
This article was written only as a review of the uniswap v3 githab code, everything was written in parallel with the study of the technology itself, because of this there may be some inaccuracies and rudeness

Pools are created here

createPool

Here we are given the contracts of the two coins between which the pair will be formed, as well as the commission that will be set in this pair. The function returns us the address of the new pool that we formed

· The first thing this function does is check if our tokens are different.
· Then calculates the one with a smaller address and names the smaller one as token0 and the bigger one as token1
· Now it checks that token0 isn’t zero (token1 obviously doesn’t need to be checked)
· We assign to tickSpacing a value stored in the feeAmountTickSpacing variable at the fee address
· Check that tickSpacing is not zero.
· Check that there is no pool with our tokens and our commissions (3D array getPool returns the pool address using the given data).
· Call the deploy function passing there the address of the factory, both tokens, commissions and tickSpacing and then passing the returned data (the new pool address) to the pool variable which is then passed to the getPool array with both token positions, thereby saving it so that no one else can make such a pool

feeAmountTickSpacing

It stores the distance between the ticks, which corresponds to each particular value of the commission (initially set in the constructor, but there can also be custom distances, which are set by the contract owner separately for each value of the commission).

Deploy

deploy

We pass to this function the factory address, both tokens, commission and tickSpacing, and the output is the address of the new pool. In the function itself we create structure Parameters, then we create a new contract using constructor UniswapV3Pool and pass the address of this contract in variable pool (which we return), then we return parameters to initial values with delete, thereby saving gas

Tick
To achieve concentrated liquidity the once continuous spectrum of the price area has been divided by ticks

Ticks are the borders between areas in the price space. Ticks are arranged in such a way that an increase or decrease by 1 tick represents a 0.01% (1 basis point) increase or decrease in price at any point in the price space

Ticks serve as boundaries for liquidity positions. When a position is created, the provider must select the lower and upper ticks, which will represent the boundaries of their position

As the spot price changes during the swap, the pool contract will continuously exchange the outgoing asset for the incoming one, gradually using all the liquidity available during the current tick interval, until the next tick is reached. At that point, the contract switches to a new tick and activates any dormant liquidity within the position that has a boundary on the new active tick

Although each pool has the same number of underlying ticks, in practice only some of them can serve as active ticks. Because of the nature of v3 smart contracts, the distance between ticks is directly related to the swap commission. Lower commission levels provide closer potential active ticks, and higher commissions allow a relatively wider spacing between potential active ticks

While inactive ticks have no effect on transaction value during swaps, crossing an active tick increases the value of the transaction in which it is crossed because crossing a tick activates liquidity in any new positions using that tick as a boundary

In areas where capital efficiency is paramount, such as stable coin pairs, a narrower tick distance increases the granularity of liquidity provision and is likely to reduce the impact on price during swaps, resulting in much better prices for stable coin swaps.

That thing Factory creates, but what does it consist of itself?

IMPORTANT: Pool is a full-fledged smart contract, and Factory creates a full-fledged smart contract from input variables

The Position library stores all information on each specific position, i.e. for each case of depositing liquidity into the Pool.

Mint

This function is responsible for adding liquidity to the pool

We give the following set of variables as input:

· recipient — address of the pool to which we are going to add liquidity
· tickLower — bottom tick
· tickUpper — upper tick
· amount — the amount of liquidity, which we are adding
· data — any data to be passed to the callback

At the output of this function we get:

· amount0 — the amount of token0 that was paid for adding this amount of
· liquidity. Corresponds to the value in the callback
· amount1 — the amount of token1 that was paid for adding this amount of
· liquidity. Corresponds to the value in the callback

What does this function do?

· Checks if we are adding at least some liquidity and not zero
· After that, we define the proportions of tokens to be added (this is done in the _modifyPosition function, while ModifyPositionParams is needed for the adjustment to the necessary data type)
· After that we assign the values obtained from _modifyPosition to those we want the main mint function to return (amount0 and amount1)
· At the end, we update the values of coin pools

_modifyPosition.

This function calculates the proportions of coins we need to add to our pool

modify position
modify position 2

At the input of this function we give the structure ModifyPositionParams, and the output it gives us the position, amount0 and amount1

What the function does:

· Checks if the ticks are within the allowed limits
· Create a local variable _Slot0 of type Slot0 to save gas
· Assign the variable position to the output of the _updatePosition function
· Checks that there is a change in the liquidityDelta:

Compares the current tick and the limits of the set ticks, and if he current tick is below our tick boundaries, then we provide more token0 because it becomes more valuable the current tick is within our tick boundaries, then we provide an equal amount of coins the current tick is above our tick boundaries, then we provide more token1 because it becomes more valuable

_updatePosition

Based on the input data it returns this structure:

Slot0

ModifyPositionParams

modify position params

Collect

This function allows us to withdraw commissions from the pool

We give the following set of variables as input:

· recipient — address of the pool from which we will take the liquor
· tickLower — bottom tick
· tickUpper — upper tick
· amount0Requested — how much token0 should be taken from the
· commissions
· amount1Requested — how much token1 should be withdrawn from commissions

In the output the function gives us:

· amount0 — the amount of commissions collected in token0
· amount1 — amount of commissions collected in the token1

What does the function do?

· First we get the initial position of the msg.sender and the upper and lower tick
· After that we get the amount of liquor that can be withdrawn from each coin of the pair (based on the commissions)
· At the end we subtract the coins that we withdrew from our total position

This feature allows us to exchange coins

We give the following set of variables as input:

· recipient — address to receive funds from swap
· zeroForOne — direction of swap, true for token0 -> token1, false for token1 -> token0
· amountSpecified — Amount of swap, which implicitly sets swap as exact input (positive) or exact output (negative).
· sqrtPriceLimitX96 — The price limit of Q64.96 (a number with exactly defined comma, where 64 bits are responsible for the integer part and 96 for the fractional part) sqrt. If zero is for one, the price cannot be less than this value after the swap. If one is for zero, the price can not be greater than this value after the swap
· data — any data that should be passed to the callback

The output of this function is:

· amount0 — delta balance of token0 pool, exact when negative, minimal when positive
· amount1 — the delta of the token1 pool balance, exact with a negative value, minimal with a positive value

What does the function do?

· Checks if the swap is not zero
· Create a local variable slot0Start like slot0, pass slot0 into it, and check that the pool is active
· Check in which direction swap will take place (token0 -> token1 or token1 -> token0)
· In the slot0 structure, write false in the “unlocked” field to block the pool for the time of swap
· Create a cache variable of SwapCache type and pass into it the liquidity of the pair, timestamp of the block, coms for input coin, also zero out tickCumulatives (the total of ticks per second from timestamp of the block) and secondsPerLiquidityCumulativeX128s (total of seconds per unit of liquidity in the range for each second from timestamp of the block), and at the end we assign false to computedLatestObservation (true if we were able to count the above two values, false otherwise)
· Now we finally assign to exactInput the coin we want to swap
· We create a variable state of SwapState type and give it the volume of our swap, how much we have swapped (so 0), the current price in the pair, the tick price, the kmsa of input token, the amount of input token we paid as kmsa (initially set as 0), the liquidity in tick at the moment

· Now we start making conditional swaps until we have swapped the entire amount
· Initialize an empty Step structure of type StepComputations
· Pass the value from the state from the sqrtPriceX96 field into the Step variable in the sqrtPriceStartX96 field
· After that call the nextInitializedTickWithinOneWord function of the tickBitmap library and pass there the values state.tick (the current tick), tickSpacing (the distance between the ticks), zeroForOne, which returns two values — the next tick and whether it is initialized
· We make sure that we don’t exceed the minimum/maximum tick, since the bitmap tick doesn’t know about the limits and pass the result (the next tick) into the step structure in the tickNext field
· Then we assign the price of the next tick to the step structure in the sqrtPriceNextX96 field
· Calculates the value for passing to the target tick, price limit or the point where I/O amount is exhausted, all this is done via this library function. At the output we get 4 values and write them into state.sqrtPriceX96 (the current price in the pair), step.amountIn (the amount of swap to be made at this step), step.amountOut (the amount of swap that was made at this step), step.feeAmount (how much money was paid). At the end, we calculate which menta in the pool should be subtracted from which swap amount, and which should be added to.
· If the comsa on the pool is on, we calculate the comsa size, decrease step.feeAmount and increase state.protocolFee
· After that we update the commission in the pool by calling the FullMath.mulDiv function
· Next comes a big block, which can be described as a “tick shift if we reach the next price”

· Then we simply update the tick and record the result from the oracle (I think there will be a separate article about it), if the tick has changed, otherwise we simply update the price, equating it to state.sqrtPriceX96
· After that we update liquidity, if it has changed
· Now we change the commission in the coin pool, depending on which way we had the swap
· And finally, we do the transfer itself by calling the safeTransfer function we all know and changing token balances in the pool
· And in the end we unlock the pool again!

SwapCache

SwapState

StepComputations

tickBitmap.nextInitializedTickWithinOneWord

Thanks for reading! I hope the article was interesting and clear!

New to trading? Try crypto trading bots or copy trading

[ad_2]

Source link

By akohad

Related Post

Leave a Reply

Your email address will not be published. Required fields are marked *