Math in Solidity

Due to Solidity not supporting numbers with the fractional part, math in Solidity is somewhat complicated. Solidity gives us integer and unsigned integer types, which are not enough for more or less complex math calculations.

Another difficulty is gas consumption: the more complex an algorithm, the more gas it consumes. Thus, if we need to have advanced math operations (like exp, ln, and sqrt), we want them to be as gas efficient as possible.

Another big problem is the possibility of under/overflow. When multiplying uint256 numbers, there’s a risk of an overflow: the resulting number might be so big that it won’t fit into 256 bits.

All these difficulties force us to use third-party math libraries that implement advanced math operations and, ideally, optimize their gas consumption. In the case when there’s no library for an algorithm we need, we’ll have to implement it ourselves, which is a difficult task if we need to implement a unique computation.

Re-Using Math Contracts

In our Uniswap V3 implementation, we’re going to use two third-party math contracts:

  1. PRBMath, which is a great library of advanced fixed-point math algorithms. We’ll use the mulDiv function to handle overflows when multiplying and then dividing integer numbers.
  2. TickMath from the original Uniswap V3 repo. This contract implements two functions, getSqrtRatioAtTick and getTickAtSqrtRatio, which convert ’s to ticks and back.

Let’s focus on the latter.

In our contracts, we’ll need to convert ticks to corresponding and back. The formulas are:

These are complex mathematical operations (for Solidity, at least) and they require high precision because we don’t want to allow rounding errors when calculating prices. To have better precision and optimization we’ll need a unique implementation.

If you look at the original code of getSqrtRatioAtTick and getTickAtSqrtRatio you’ll see that they’re quite complex: there’re a lot of magic numbers (like 0xfffcb933bd6fad37aa2d162d1a594001), multiplication, and bitwise operations. At this point, we’re not going to analyze the code or re-implement it since this is a very advanced and somewhat different topic. We’ll use the contract as is. And, in a later milestone, we’ll break down the computations.