FullMath works with Solidity 0.8

This commit is contained in:
7400
2023-10-26 12:00:42 -07:00
parent 499782828a
commit cd8d3e9da1
3 changed files with 104 additions and 4 deletions

View File

@@ -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++;
}
}
}}
}

10
test/TestFullMath.py Normal file
View File

@@ -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

89
test/TestFullMath.sol Normal file
View File

@@ -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
}
}