FullMath works with Solidity 0.8
This commit is contained in:
@@ -11,11 +11,12 @@ library FullMath {
|
|||||||
/// @param denominator The divisor
|
/// @param denominator The divisor
|
||||||
/// @return result The 256-bit result
|
/// @return result The 256-bit result
|
||||||
/// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
|
/// @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(
|
function mulDiv(
|
||||||
uint256 a,
|
uint256 a,
|
||||||
uint256 b,
|
uint256 b,
|
||||||
uint256 denominator
|
uint256 denominator
|
||||||
) internal pure returns (uint256 result) {
|
) internal pure returns (uint256 result) {unchecked{
|
||||||
// 512-bit multiply [prod1 prod0] = a * b
|
// 512-bit multiply [prod1 prod0] = a * b
|
||||||
// Compute the product mod 2**256 and mod 2**256 - 1
|
// Compute the product mod 2**256 and mod 2**256 - 1
|
||||||
// then use the Chinese Remainder Theorem to reconstruct
|
// then use the Chinese Remainder Theorem to reconstruct
|
||||||
@@ -103,7 +104,7 @@ library FullMath {
|
|||||||
// is no longer required.
|
// is no longer required.
|
||||||
result = prod0 * inv;
|
result = prod0 * inv;
|
||||||
return result;
|
return result;
|
||||||
}
|
}}
|
||||||
|
|
||||||
/// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
|
/// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
|
||||||
/// @param a The multiplicand
|
/// @param a The multiplicand
|
||||||
@@ -114,11 +115,11 @@ library FullMath {
|
|||||||
uint256 a,
|
uint256 a,
|
||||||
uint256 b,
|
uint256 b,
|
||||||
uint256 denominator
|
uint256 denominator
|
||||||
) internal pure returns (uint256 result) {
|
) internal pure returns (uint256 result) {unchecked{
|
||||||
result = mulDiv(a, b, denominator);
|
result = mulDiv(a, b, denominator);
|
||||||
if (mulmod(a, b, denominator) > 0) {
|
if (mulmod(a, b, denominator) > 0) {
|
||||||
require(result < type(uint256).max);
|
require(result < type(uint256).max);
|
||||||
result++;
|
result++;
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
}
|
}
|
||||||
|
|||||||
10
test/TestFullMath.py
Normal file
10
test/TestFullMath.py
Normal 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
89
test/TestFullMath.sol
Normal 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
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user