Omniscia Maverick Protocol Audit

TickMath Manual Review Findings

TickMath Manual Review Findings

TMH-01M: Discrepancy of Whitepaper Formula 22

Description:

The implementation of TickMath::getTickL is meant to represent formula 22 of the whitepaper draft that calculates the aggregate liquidity of a tick based on the quadratic formula outlined in 21. The whitepaper deviates from the actual implementation of TickMath::getTickL which implements:

As can be observed, the formula implemented deviates from formula 22 as formula 22 will add the b / 2 factor instead of multiplying it.

Impact:

As the whitepaper is considered the source-of-truth in relation to the mathematical calculations of the TickMath contract, we consider this exhibit to be of minor severity if the implemented formula by TickMath::getTickL is correct.

The severity of this exhibit will be revisited once either the implementation or the whitepaper are corrected.

Example:

v2-common/contracts/libraries/TickMath.sol
124// need half b at this point
125// b is in (7.2e-9 (2^57 / 1e7 / 2), 2.8e29 (2^(78+57) * 1e7 / 2)) with bump
126// b is in a subset of the same range without bump
127b >>= 1;
128
129// b^2 is in (5.1e-17, 4.8e58); and will not overflow on either end;
130// A*B is in (3e-13 (2^78 / 1e18 * 1e-18), 1.9e45) without bump and is in a subset range with bump
131// A*B*diff/sqrtUpper is in (1.5e-17 (3e-13 * 5e-12 * 1e7), 7.6e58);
132
133// Since b^2 is at the upper edge of the precision range, we are not
134// able to multiply the argument of the sqrt by 1e18, instead, we move
135// this factor outside of the sqrt. The resulting loss of precision
136// means that this liquidity value is a lower bound on the tick
137// liquidity
138return
139 OzMath.mulDiv(
140 b +
141 Math.sqrt(
142 (OzMath.mulDiv(b, b, ONE) +
143 OzMath.mulDiv(reserveB.mulFloor(reserveA), diff, sqrtUpperTickPrice))
144 ) *
145 1e9,
146 sqrtUpperTickPrice,
147 diff
148 ) >> precisionBump;

Recommendation:

We evaluated the quadratic formula described in 21 and were unable to reproduce formula 22. Additionally, we were unable to properly correlate formula 21 to the formula implemented by TickMath::getTickL and instead calculated significantly more complex fractions for the quadratic equation of 21.

As the formula outlined in the whitepaper draft is incorrect, we advise it to be properly updated to reflect the latest implementation defined in TickMath::getTickL. Additionally, we advise a step-by-step solution of the quadratic formula to be performed as simplifications have occurred in between formula 21 and the implementation of TickMath::getTickL that cannot be reliably deduced by performing the b substitution as defined in the whitepaper.

Alleviation (175f8c39b19df69134add3aa8a2a042ce3047763):

The whitepaper draft has been updated to reflect the correct formula, addressing this exhibit as the whitepaper and code implementation are no longer desynchronized.

TMH-02M: Inexistent Accommodation of Rounding Errors

Description:

The TickMath::tickSqrtPrice function will not accommodate for rounding errors in contrast to its original Uniswap V3 counterpart, and rounding errors can occur for multiple tick values such as 319_487 which would be below the Constants::MAX_TICK limitation.

Impact:

The precise ramifications of inexistent rounding are difficult to identify, and would effectively mean that the square root price would be lower by a single unit in certain circumstances.

We do not consider the impact to be significant, however, we still consider it a flaw that should be remediated out of an abundance of caution.

Example:

v2-common/contracts/libraries/TickMath.sol
42/**
43 * @notice Calculate the square root price for a given tick and tick spacing.
44 * @param tickSpacing The tick spacing used for calculations.
45 * @param _tick The input tick value.
46 * @return _result The square root price.
47 */
48function tickSqrtPrice(uint256 tickSpacing, int32 _tick) internal pure returns (uint256 _result) {
49 unchecked {
50 uint256 tick = subTickIndex(tickSpacing, _tick);
51
52 uint256 ratio = tick & 0x1 != 0 ? 0xfffcb933bd6fad9d3af5f0b9f25db4d6 : 0x100000000000000000000000000000000;
53 if (tick & 0x2 != 0) ratio = (ratio * 0xfff97272373d41fd789c8cb37ffcaa1c) >> 128;
54 if (tick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656ac9229c67059486f389) >> 128;
55 if (tick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e81259b3cddc7a064941) >> 128;
56 if (tick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f67b19e8887e0bd251eb7) >> 128;
57 if (tick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98cd2e57b660be99eb2c4a) >> 128;
58 if (tick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c9838804e327cb417cafcb) >> 128;
59 if (tick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99d51e2cc356c2f617dbe0) >> 128;
60 if (tick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900aecf64236ab31f1f9dcb5) >> 128;
61 if (tick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac4d9194200696907cf2e37) >> 128;
62 if (tick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b88206f8abe8a3b44dd9be) >> 128;
63 if (tick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c578ef4f1d17b2b235d480) >> 128;
64 if (tick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd254ee83bdd3f248e7e785e) >> 128;
65 if (tick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d8f7dd10e744d913d033333) >> 128;
66 if (tick & 0x4000 != 0) ratio = (ratio * 0x70d869a156ddd32a39e257bc3f50aa9b) >> 128;
67 if (tick & 0x8000 != 0) ratio = (ratio * 0x31be135f97da6e09a19dc367e3b6da40) >> 128;
68 if (tick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7e5a9780b0cc4e25d61a56) >> 128;
69 if (tick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedbcb3a6ccb7ce618d14225) >> 128;
70 if (tick & 0x40000 != 0) ratio = (ratio * 0x2216e584f630389b2052b8db590e) >> 128;
71 if (_tick > 0) ratio = type(uint256).max / ratio;
72 _result = (ratio * ONE) >> 128;
73 }
74}

Recommendation:

We advise outputs of the TickMath::tickSqrtPrice function to be made consistent by incorporating a rounding approach similar to Uniswap V3.

Alleviation (175f8c39b19df69134add3aa8a2a042ce3047763):

The Maverick Protocol team evaluated this exhibit, and clarified that a rounding operation is unnecessary in the case of the Maverick Protocol AMM.

The Uniswap AMM needs to round upwards so as to be able to invert a price back to its tick via a logarithm operation.

As the Maverick Protocol AMM does not need to conduct such an operation and merely needs consistency in the function's outputs, the current implementation is adequately secure rendering this exhibit inapplicable.