Skip to content

Commit

Permalink
🐞 Fix sqrtWad precision loss for large numbers (#946)
Browse files Browse the repository at this point in the history
Co-authored-by: RareSkills <[email protected]>
  • Loading branch information
Vectorized and RareSkills committed Jun 3, 2024
1 parent c55500d commit 8626350
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 21 deletions.
38 changes: 19 additions & 19 deletions .gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ ERC1967FactoryTest:testUpgradeAndCallWithRevert() (gas: 265824)
ERC1967FactoryTest:testUpgradeUnauthorized() (gas: 270340)
ERC1967FactoryTest:testUpgradeWithCorruptedProxy() (gas: 263294)
ERC1967FactoryTest:test__codesize() (gas: 32011)
ERC20Invariants:invariantBalanceSum() (runs: 256, calls: 128000, reverts: 78740)
ERC20Invariants:invariantBalanceSum() (runs: 256, calls: 128000, reverts: 77980)
ERC20Invariants:test__codesize() (gas: 7532)
ERC20Test:testApprove() (gas: 35730)
ERC20Test:testApprove(address,uint256) (runs: 278, μ: 30751, ~: 31181)
Expand Down Expand Up @@ -257,15 +257,15 @@ ERC4337Test:testValidateUserOp() (gas: 484978)
ERC4337Test:test__codesize() (gas: 57354)
ERC4626Test:testDepositWithNoApprovalReverts() (gas: 16569)
ERC4626Test:testDepositWithNotEnoughApprovalReverts() (gas: 90179)
ERC4626Test:testDifferentialFullMulDiv(uint256,uint256,uint256) (runs: 278, μ: 3316, ~: 3107)
ERC4626Test:testDifferentialFullMulDiv(uint256,uint256,uint256) (runs: 278, μ: 3321, ~: 3319)
ERC4626Test:testMetadata() (gas: 14354)
ERC4626Test:testMintWithNoApprovalReverts() (gas: 16543)
ERC4626Test:testMintZero() (gas: 53551)
ERC4626Test:testMultipleMintDepositRedeemWithdraw() (gas: 410466)
ERC4626Test:testRedeemWithNoShareAmountReverts() (gas: 10918)
ERC4626Test:testRedeemWithNotEnoughShareAmountReverts() (gas: 143210)
ERC4626Test:testSingleDepositWithdraw(uint128) (runs: 278, μ: 201955, ~: 201960)
ERC4626Test:testSingleMintRedeem(uint128) (runs: 278, μ: 200921, ~: 200926)
ERC4626Test:testSingleDepositWithdraw(uint128) (runs: 278, μ: 201956, ~: 201963)
ERC4626Test:testSingleMintRedeem(uint128) (runs: 278, μ: 200921, ~: 200928)
ERC4626Test:testTryGetAssetDecimals() (gas: 31562481)
ERC4626Test:testUseVirtualShares() (gas: 2434858)
ERC4626Test:testVaultInteractionsForSomeoneElse() (gas: 296637)
Expand Down Expand Up @@ -428,7 +428,7 @@ FixedPointMathLibTest:testFullMulDivUp(uint256,uint256,uint256) (runs: 278, μ:
FixedPointMathLibTest:testFullMulDivUpRevertsIfRoundedUpResultOverflowsCase1() (gas: 3694)
FixedPointMathLibTest:testFullMulDivUpRevertsIfRoundedUpResultOverflowsCase2() (gas: 3727)
FixedPointMathLibTest:testGcd() (gas: 4228)
FixedPointMathLibTest:testGcd(uint256,uint256) (runs: 278, μ: 6496, ~: 2800)
FixedPointMathLibTest:testGcd(uint256,uint256) (runs: 278, μ: 6499, ~: 2800)
FixedPointMathLibTest:testLambertW0WadAccuracy() (gas: 7164)
FixedPointMathLibTest:testLambertW0WadAccuracy(uint184) (runs: 278, μ: 2275, ~: 408)
FixedPointMathLibTest:testLambertW0WadKnownValues() (gas: 1689664)
Expand All @@ -450,7 +450,7 @@ FixedPointMathLibTest:testLnWadNegativeReverts() (gas: 3340)
FixedPointMathLibTest:testLnWadOverflowReverts() (gas: 3315)
FixedPointMathLibTest:testLnWadSmall() (gas: 2708)
FixedPointMathLibTest:testLog10() (gas: 76168)
FixedPointMathLibTest:testLog10(uint256,uint256) (runs: 278, μ: 2181, ~: 2210)
FixedPointMathLibTest:testLog10(uint256,uint256) (runs: 278, μ: 2180, ~: 2210)
FixedPointMathLibTest:testLog10Up() (gas: 4369)
FixedPointMathLibTest:testLog2() (gas: 243209)
FixedPointMathLibTest:testLog256() (gas: 22831)
Expand Down Expand Up @@ -485,7 +485,7 @@ FixedPointMathLibTest:testMulWadUp(uint256,uint256) (runs: 278, μ: 873, ~: 1065
FixedPointMathLibTest:testMulWadUpEdgeCases() (gas: 815)
FixedPointMathLibTest:testMulWadUpOverflowReverts(uint256,uint256) (runs: 263, μ: 3834, ~: 3834)
FixedPointMathLibTest:testPackUnpackSci() (gas: 129349)
FixedPointMathLibTest:testPackUnpackSci(uint256) (runs: 278, μ: 30187, ~: 30182)
FixedPointMathLibTest:testPackUnpackSci(uint256) (runs: 278, μ: 30189, ~: 30185)
FixedPointMathLibTest:testRPow() (gas: 3320)
FixedPointMathLibTest:testRPowOverflowReverts() (gas: 4974)
FixedPointMathLibTest:testRawAdd(int256,int256) (runs: 278, μ: 440, ~: 440)
Expand All @@ -512,18 +512,18 @@ FixedPointMathLibTest:testSMulWadOverflowRevertsOnCondition1(int256,int256) (run
FixedPointMathLibTest:testSMulWadOverflowRevertsOnCondition2(int256) (runs: 262, μ: 3706, ~: 3706)
FixedPointMathLibTest:testSMulWadOverflowTrickDifferential(int256,int256) (runs: 278, μ: 600, ~: 610)
FixedPointMathLibTest:testSci() (gas: 1838614)
FixedPointMathLibTest:testSci(uint256) (runs: 278, μ: 35255, ~: 40394)
FixedPointMathLibTest:testSci2(uint256) (runs: 278, μ: 961, ~: 955)
FixedPointMathLibTest:testSci(uint256) (runs: 278, μ: 35190, ~: 40023)
FixedPointMathLibTest:testSci2(uint256) (runs: 278, μ: 962, ~: 955)
FixedPointMathLibTest:testSqrt() (gas: 42598)
FixedPointMathLibTest:testSqrt(uint256) (runs: 278, μ: 1054, ~: 1064)
FixedPointMathLibTest:testSqrtBack(uint256) (runs: 278, μ: 8984, ~: 341)
FixedPointMathLibTest:testSqrtHashed(uint256) (runs: 278, μ: 53177, ~: 53364)
FixedPointMathLibTest:testSqrtHashed(uint256) (runs: 278, μ: 53179, ~: 53571)
FixedPointMathLibTest:testSqrtHashedSingle() (gas: 53063)
FixedPointMathLibTest:testSqrtWad() (gas: 7426)
FixedPointMathLibTest:testSqrtWad(uint256) (runs: 278, μ: 1561, ~: 1565)
FixedPointMathLibTest:testSqrtWad(uint256) (runs: 278, μ: 1563, ~: 1565)
FixedPointMathLibTest:testZeroFloorSub(uint256,uint256) (runs: 278, μ: 544, ~: 518)
FixedPointMathLibTest:testZeroFloorSubCasted(uint32,uint32,uint256) (runs: 278, μ: 886, ~: 922)
FixedPointMathLibTest:test__codesize() (gas: 43139)
FixedPointMathLibTest:test__codesize() (gas: 43146)
GasBurnerLibTest:testBurnGas() (gas: 1700805)
GasBurnerLibTest:test__codesize() (gas: 1189)
InitializableTest:testDisableInitializers() (gas: 42010)
Expand Down Expand Up @@ -690,16 +690,16 @@ LibMapTest:test__codesize() (gas: 12035)
LibPRNGTest:testExponentialWad() (gas: 4394348)
LibPRNGTest:testLCGGas() (gas: 20803)
LibPRNGTest:testLazyShufflerGet() (gas: 298238)
LibPRNGTest:testLazyShufflerGetOutOfBoundsReverts(uint256,uint256) (runs: 278, μ: 26640, ~: 26667)
LibPRNGTest:testLazyShufflerGetOutOfBoundsReverts(uint256,uint256) (runs: 278, μ: 26635, ~: 26667)
LibPRNGTest:testLazyShufflerNoStorageCollisions() (gas: 266115)
LibPRNGTest:testLazyShufflerProducesShuffledRange(uint256) (runs: 278, μ: 153333, ~: 91387)
LibPRNGTest:testLazyShufflerProducesShuffledRange(uint256) (runs: 278, μ: 171571, ~: 91626)
LibPRNGTest:testLazyShufflerProducesShuffledRange2() (gas: 8974962)
LibPRNGTest:testLazyShufflerProducesShuffledRangeWithGrow(uint256,uint256) (runs: 278, μ: 189560, ~: 193196)
LibPRNGTest:testLazyShufflerProducesShuffledRangeWithGrow(uint256,uint256) (runs: 278, μ: 189907, ~: 201001)
LibPRNGTest:testLazyShufflerRestart() (gas: 274850)
LibPRNGTest:testLazyShufflerRevertsOnDoubleInit() (gas: 26824)
LibPRNGTest:testLazyShufflerRevertsOnFinshedNext(uint256) (runs: 278, μ: 62842, ~: 54980)
LibPRNGTest:testLazyShufflerRevertsOnGrowWithInvalidLength(uint256,uint256) (runs: 278, μ: 24688, ~: 24600)
LibPRNGTest:testLazyShufflerRevertsOnInitWithInvalidLength(uint256) (runs: 278, μ: 22893, ~: 23497)
LibPRNGTest:testLazyShufflerRevertsOnFinshedNext(uint256) (runs: 278, μ: 62378, ~: 54808)
LibPRNGTest:testLazyShufflerRevertsOnGrowWithInvalidLength(uint256,uint256) (runs: 278, μ: 24686, ~: 24600)
LibPRNGTest:testLazyShufflerRevertsOnInitWithInvalidLength(uint256) (runs: 278, μ: 22895, ~: 23561)
LibPRNGTest:testLazyShufflerRevertsOnZeroLengthNext() (gas: 27776)
LibPRNGTest:testPRNGGas() (gas: 25667)
LibPRNGTest:testPRNGNext() (gas: 16162)
Expand Down Expand Up @@ -1197,7 +1197,7 @@ UpgradeableBeaconTest:testUpgradeableSolidityBeaconOnlyOwnerFunctions() (gas: 26
UpgradeableBeaconTest:testUpgradeableYulBeaconOnlyFnSelectorNotRecognised() (gas: 172796)
UpgradeableBeaconTest:testUpgradeableYulBeaconOnlyOwnerFunctions() (gas: 198398)
UpgradeableBeaconTest:test__codesize() (gas: 8808)
WETHInvariants:invariantTotalSupplyEqualsBalance() (runs: 256, calls: 128000, reverts: 59492)
WETHInvariants:invariantTotalSupplyEqualsBalance() (runs: 256, calls: 128000, reverts: 59403)
WETHInvariants:test__codesize() (gas: 5264)
WETHTest:testDeposit() (gas: 68090)
WETHTest:testDeposit(uint256) (runs: 278, μ: 67950, ~: 68384)
Expand Down
19 changes: 17 additions & 2 deletions src/utils/FixedPointMathLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -190,13 +190,15 @@ library FixedPointMathLib {

/// @dev Equivalent to `x` to the power of `y`.
/// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`.
/// Note: This function is an approximation.
function powWad(int256 x, int256 y) internal pure returns (int256) {
// Using `ln(x)` means `x` must be greater than 0.
return expWad((lnWad(x) * y) / int256(WAD));
}

/// @dev Returns `exp(x)`, denominated in `WAD`.
/// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
/// Note: This function is an approximation. Monotonically increasing.
function expWad(int256 x) internal pure returns (int256 r) {
unchecked {
// When the result is less than 0.5 we return zero.
Expand Down Expand Up @@ -266,6 +268,7 @@ library FixedPointMathLib {

/// @dev Returns `ln(x)`, denominated in `WAD`.
/// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
/// Note: This function is an approximation. Monotonically increasing.
function lnWad(int256 x) internal pure returns (int256 r) {
/// @solidity memory-safe-assembly
assembly {
Expand Down Expand Up @@ -341,6 +344,7 @@ library FixedPointMathLib {
/// @dev Returns `W_0(x)`, denominated in `WAD`.
/// See: https://en.wikipedia.org/wiki/Lambert_W_function
/// a.k.a. Product log function. This is an approximation of the principal branch.
/// Note: This function is an approximation. Monotonically increasing.
function lambertW0Wad(int256 x) internal pure returns (int256 w) {
// forgefmt: disable-next-item
unchecked {
Expand Down Expand Up @@ -683,10 +687,15 @@ library FixedPointMathLib {
}

/// @dev Returns the square root of `x`, denominated in `WAD`.
/// Note: This function can cause precision loss due to the limits of 256-bit square root.
/// - If `x >= type(uint256).max / 10 ** 18`: `sqrt(x) * 10 ** 9`
/// - Otherwise `sqrt(x * 10 ** 18)`
/// The output is bounded by the following relation
/// `sqrtWad(x) >= sqrt(x) * 10 ** 9 && sqrtWad(x) <= (sqrt(x) + 1) * 10 ** 9`.
function sqrtWad(uint256 x) internal pure returns (uint256 z) {
unchecked {
z = 10 ** 9;
if (x <= type(uint256).max / 10 ** 36 - 1) {
if (x <= (type(uint256).max / 10 ** 18) - 1) {
x *= 10 ** 18;
z = 1;
}
Expand All @@ -695,10 +704,16 @@ library FixedPointMathLib {
}

/// @dev Returns the cube root of `x`, denominated in `WAD`.
/// Note: This function can cause precision loss due to the limits of 256-bit cube root.
/// - If `x >= type(uint256).max / 10 ** 36 * 10 ** 18`: `cbrt(x) * 10 ** 12`
/// - Else if `x >= type(uint256).max / 10 ** 36`: `cbrt(x * 10 ** 18) * 10 ** 6`
/// - Otherwise `cbrt(x * 10 ** 36)`
/// The output is bounded by the following relation
/// `cbrtWad(x) >= cbrt(x) * 10 ** 12 && cbrtWad(x) <= (cbrt(x) + 1) * 10 ** 12`.
function cbrtWad(uint256 x) internal pure returns (uint256 z) {
unchecked {
z = 10 ** 12;
if (x <= (type(uint256).max / 10 ** 36) * 10 ** 18 - 1) {
if (x <= ((type(uint256).max / 10 ** 36) * 10 ** 18) - 1) {
if (x >= type(uint256).max / 10 ** 36) {
x *= 10 ** 18;
z = 10 ** 6;
Expand Down

0 comments on commit 8626350

Please sign in to comment.