diff --git a/lib_sol8/v3-core/contracts/libraries/FullMath.sol b/lib_sol8/v3-core/contracts/libraries/FullMath.sol index c5b77b9..2cf88f2 100644 --- a/lib_sol8/v3-core/contracts/libraries/FullMath.sol +++ b/lib_sol8/v3-core/contracts/libraries/FullMath.sol @@ -11,11 +11,12 @@ library FullMath { /// @param denominator The divisor /// @return result The 256-bit result /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv + // mulDiv is unchecked because it uses wrapping, i.e. over/underflow, to work correctly function mulDiv( uint256 a, uint256 b, uint256 denominator - ) internal pure returns (uint256 result) { + ) internal pure returns (uint256 result) {unchecked{ // 512-bit multiply [prod1 prod0] = a * b // Compute the product mod 2**256 and mod 2**256 - 1 // then use the Chinese Remainder Theorem to reconstruct @@ -103,7 +104,7 @@ library FullMath { // is no longer required. result = prod0 * inv; return result; - } + }} /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 /// @param a The multiplicand @@ -114,11 +115,11 @@ library FullMath { uint256 a, uint256 b, uint256 denominator - ) internal pure returns (uint256 result) { + ) internal pure returns (uint256 result) {unchecked{ result = mulDiv(a, b, denominator); if (mulmod(a, b, denominator) > 0) { require(result < type(uint256).max); result++; } - } + }} } diff --git a/test/TestFullMath.py b/test/TestFullMath.py new file mode 100644 index 0000000..cdf0882 --- /dev/null +++ b/test/TestFullMath.py @@ -0,0 +1,10 @@ + +Mask256 = 2**256-1 +MinusOne = -1 +MAXpos = Mask256 >> 1 +MAXneg = -MAXpos-1 + +print("MAXpos:", hex(MAXpos)) +print("MAXneg:", hex(MAXneg)) + +pass \ No newline at end of file diff --git a/test/TestFullMath.sol b/test/TestFullMath.sol new file mode 100644 index 0000000..ec39093 --- /dev/null +++ b/test/TestFullMath.sol @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.0; +pragma abicoder v2; + +import "forge-std/Test.sol"; +import '@uniswap/v3-core/contracts/libraries/FullMath.sol'; + +// FullMath relies on wrapping behavior. However, Solidity 0.8 checks by +// default and so FullMath will fail indicating overflow. We have modified +// FullMath to be unchecked. These tests verify that it still operated as +// intended. + +contract TestFullMath is Test { + + function setUp() public pure { + console2.log('FullMath setup()'); + } + + function testFullMath() public pure { + + console2.log('FullMath testFullMath()'); + + // Constants + + uint256 MinusOne = uint256(int256(-1)); + uint256 MAXneg = 2**255; + uint256 MAXpos = ~MAXneg; + + // Check Constants + require(MAXpos == MinusOne>>1); + require(MAXneg == MAXpos+1); + unchecked{ + require(MinusOne+1 == 0); + } + + // Case 1 -- Max negative values + + uint256 q = FullMath.mulDiv(MAXneg, MAXneg, MAXneg); // DUT + require(q == MAXneg, "case 1 failed"); // check + + // Case 2 -- All ones (-1) case + + q = FullMath.mulDiv(MinusOne, MinusOne, MinusOne); + require(q == MinusOne, "case 2 failed"); + + // Case 3 -- All max positive values case + + q = FullMath.mulDiv(MAXpos, MAXpos, MAXpos); + require(q == MAXpos, "case 3 failed"); + + // Case 4 -- Mixed pos and neg + + q = FullMath.mulDiv(MAXpos, MAXneg, MAXpos); + require(q == MAXneg, "case 4a failed"); + q = FullMath.mulDiv(MAXpos, MAXneg, MAXneg); + require(q == MAXpos, "case 4b failed"); + q = FullMath.mulDiv(MAXpos, MinusOne, MAXpos); + require(q == MinusOne, "case 4c failed"); + q = FullMath.mulDiv(MAXneg, MinusOne, MAXneg); + require(q == MinusOne, "case 4d failed"); + q = FullMath.mulDiv(MAXpos, MinusOne, MinusOne); + require(q == MAXpos, "case 4e failed"); + q = FullMath.mulDiv(MAXneg, MinusOne, MinusOne); + require(q == MAXneg, "case 4f failed"); + + // Case 10 -- various exponents + + uint256 aExp; + uint256 bExp; + uint256 dExp; + uint256 a; + uint256 b; + uint256 d; + uint256 qExpected; + + aExp = 255; + bExp = 255; + dExp = 255; + + a = 2**aExp; + b = 2**bExp; + d = 2**dExp; + qExpected = 2**(aExp+bExp-dExp); + + q = FullMath.mulDiv(a,b,d); // DUT + require(q == qExpected, "case 10 failed"); // check + + } +}