Uniswap V3 – Why Does Uniswap Use Q Notation vs. the Uint256 Ratio Price?

uniswapuniswapv3

Question:

Why does Uniswap use Q notation versus the uint256 ratio price? A price in uniswap is always in token1/token0. Which token is which depends on the hex value assigned by the contract address when it's created.

(token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);

So for example the WETH-USDC pool on ethereum the addresses are

> (WETH) 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 > (USDC) 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 
true

So WETH is token 1.

The best documentation I've found on the topic is in this article https://blog.uniswap.org/uniswap-v3-math-primer which states

Q notation specifies the parameters of the binary fixed point number
format, which allows variables to remain integers, but function
similarly to floating point numbers. Variables that must be as precise
as possible in Uniswap v3 are represented with a maximum of 256 bits
and account both for overflow and potential rounding issues. By using
Q notation, the protocol can ensure that granular decimal precision is
not lost.


My thoughts

The only upside I see, assuming that ratios have to be calculated in token1/token0 at some point and don't risk overflows as a result, is that when you store this variable which is a uinit160 (sqrtPriceX96) in a struct it takes less space and thus uses less gas than the full uint256.

And if you're not using the struct you're not gaining much of anything by using a smaller value, in fact you may even be spending more gas as seen from this stack post

"The EVM works with 256bit/32byte words (debatable design decision). Every operation is based on these base units. If your data is smaller, further operations are needed to downscale from 256 bits to 8 bits, hence why you see increased costs."

Why does uint8 cost more gas than uint256?

If someone can confirm that's the case it would be very helpful.

Best Answer

Uniswap utilizes a fixed-point number representation(Q-notation) to achieve a higher precision.

sqrtX96 means that the variable has been multiplied by 2**96 to guarantee 96 bits of precision during the computation.

Multiplying a variable by 2**96 is equivalent to shifting its bits to the left by 96 bits. During the computation, the variable now have 96 more bits with express its value. The added 96 bits will be lost in the end when the variable is divided by 2**96(shifting right 96 bits), but the added precision has been reflected to the final value.

If uint256 is used instead, some amount of precision will be lost on every step of the computation. This is because in Solidity division rounds towards zero. While the computation with fixed-point number also loses some precision, it's 96 bits more precise when compared to that of uint256.

Related Topic