diff --git a/.gas-snapshot b/.gas-snapshot index 00475861..f66c9b80 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -546,42 +546,42 @@ ERC721Test:testTransferFrom() (gas: 579090) ERC721Test:testTransferOwnershipNonOwner() (gas: 12707) ERC721Test:testTransferOwnershipSuccess() (gas: 54110) ERC721Test:testTransferOwnershipToZeroAddress() (gas: 15838) -MathTest:testCbrtRoundDown() (gas: 49935) -MathTest:testCbrtRoundUp() (gas: 50582) -MathTest:testCeilDiv() (gas: 14421) -MathTest:testFuzzCbrt(uint256,bool) (runs: 256, μ: 22258, ~: 22023) -MathTest:testFuzzCeilDiv(uint256,uint256) (runs: 256, μ: 9672, ~: 9699) -MathTest:testFuzzInt256Average(int256,int256) (runs: 256, μ: 8572, ~: 8572) -MathTest:testFuzzLog10(uint256,bool) (runs: 256, μ: 10139, ~: 10152) -MathTest:testFuzzLog2(uint256,bool) (runs: 256, μ: 9979, ~: 9985) -MathTest:testFuzzLog256(uint256,bool) (runs: 256, μ: 10015, ~: 10009) -MathTest:testFuzzMulDiv(uint256,uint256,uint256) (runs: 256, μ: 14136, ~: 13888) -MathTest:testFuzzMulDivDomain(uint256,uint256,uint256) (runs: 256, μ: 10676, ~: 10758) -MathTest:testFuzzSignum(int256) (runs: 256, μ: 8464, ~: 8456) -MathTest:testFuzzUint256Average(uint256,uint256) (runs: 256, μ: 8635, ~: 8635) -MathTest:testFuzzWadCbrt(uint256) (runs: 256, μ: 22218, ~: 21921) -MathTest:testFuzzWadExp(int256) (runs: 256, μ: 15257, ~: 15363) -MathTest:testFuzzWadLn(int256) (runs: 256, μ: 16748, ~: 16532) -MathTest:testInt256Average() (gas: 18313) -MathTest:testLog10RoundDown() (gas: 24985) -MathTest:testLog10RoundUp() (gas: 26166) -MathTest:testLog256RoundDown() (gas: 20754) -MathTest:testLog256RoundUp() (gas: 20887) -MathTest:testLog2RoundDown() (gas: 23913) -MathTest:testLog2RoundUp() (gas: 24076) -MathTest:testMulDivDivisionByZero() (gas: 11414) -MathTest:testMulDivOverflow() (gas: 11788) -MathTest:testMulDivRoundDownLargeValues() (gas: 17108) -MathTest:testMulDivRoundDownSmallValues() (gas: 11331) -MathTest:testMulDivRoundUpLargeValues() (gas: 17441) -MathTest:testMulDivRoundUpSmallValues() (gas: 11554) -MathTest:testSignum() (gas: 17356) -MathTest:testUint256Average() (gas: 12646) -MathTest:testWadCbrt() (gas: 48122) -MathTest:testWadExp() (gas: 33776) -MathTest:testWadExpOverflow() (gas: 11315) -MathTest:testWadLn() (gas: 30882) -MathTest:testWadLnNegativeValues() (gas: 11318) +MathTest:testCbrtRoundDown() (gas: 51840) +MathTest:testCbrtRoundUp() (gas: 52487) +MathTest:testCeilDiv() (gas: 14821) +MathTest:testFuzzCbrt(uint256,bool) (runs: 256, μ: 22380, ~: 22161) +MathTest:testFuzzCeilDiv(uint256,uint256) (runs: 256, μ: 9743, ~: 9770) +MathTest:testFuzzInt256Average(int256,int256) (runs: 256, μ: 8625, ~: 8625) +MathTest:testFuzzLog10(uint256,bool) (runs: 256, μ: 10207, ~: 10226) +MathTest:testFuzzLog2(uint256,bool) (runs: 256, μ: 9982, ~: 9994) +MathTest:testFuzzLog256(uint256,bool) (runs: 256, μ: 10091, ~: 10092) +MathTest:testFuzzMulDiv(uint256,uint256,uint256) (runs: 256, μ: 14325, ~: 14078) +MathTest:testFuzzMulDivDomain(uint256,uint256,uint256) (runs: 256, μ: 10811, ~: 10890) +MathTest:testFuzzSignum(int256) (runs: 256, μ: 8508, ~: 8500) +MathTest:testFuzzUint256Average(uint256,uint256) (runs: 256, μ: 8688, ~: 8688) +MathTest:testFuzzWadCbrt(uint256) (runs: 256, μ: 22230, ~: 21922) +MathTest:testFuzzWadExp(int256) (runs: 256, μ: 15309, ~: 15410) +MathTest:testFuzzWadLn(int256) (runs: 256, μ: 16829, ~: 16614) +MathTest:testInt256Average() (gas: 18843) +MathTest:testLog10RoundDown() (gas: 25912) +MathTest:testLog10RoundUp() (gas: 27093) +MathTest:testLog256RoundDown() (gas: 21459) +MathTest:testLog256RoundUp() (gas: 21592) +MathTest:testLog2RoundDown() (gas: 23966) +MathTest:testLog2RoundUp() (gas: 24129) +MathTest:testMulDivDivisionByZero() (gas: 11564) +MathTest:testMulDivOverflow() (gas: 11920) +MathTest:testMulDivRoundDownLargeValues() (gas: 17568) +MathTest:testMulDivRoundDownSmallValues() (gas: 11619) +MathTest:testMulDivRoundUpLargeValues() (gas: 17901) +MathTest:testMulDivRoundUpSmallValues() (gas: 11836) +MathTest:testSignum() (gas: 17752) +MathTest:testUint256Average() (gas: 12911) +MathTest:testWadCbrt() (gas: 48483) +MathTest:testWadExp() (gas: 34590) +MathTest:testWadExpOverflow() (gas: 11387) +MathTest:testWadLn() (gas: 31609) +MathTest:testWadLnNegativeValues() (gas: 11360) MerkleProofVerificationTest:testFuzzMultiProofVerifySingleLeaf(bytes32[],uint256) (runs: 256, μ: 1649985414, ~: 1649982220) MerkleProofVerificationTest:testFuzzVerify(bytes32[],uint256) (runs: 256, μ: 135979115, ~: 135975891) MerkleProofVerificationTest:testFuzzVerifyMultiProofMultipleLeaves(bool,bool,bool) (runs: 256, μ: 412477124, ~: 412477119) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04747cfb..89567312 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - [`MessageHashUtils`](https://github.com/pcaversaccio/snekmate/blob/v0.1.0/src/snekmate/utils/MessageHashUtils.vy): Move the `ECDSA` message hash methods to a separate `MessageHashUtils` library module. ([#227](https://github.com/pcaversaccio/snekmate/pull/227)) - [`SignatureChecker`](https://github.com/pcaversaccio/snekmate/blob/v0.1.0/src/snekmate/utils/SignatureChecker.vy): Make `SignatureChecker` module-friendly. ([#228](https://github.com/pcaversaccio/snekmate/pull/228)) - [`EIP712DomainSeparator`](https://github.com/pcaversaccio/snekmate/blob/v0.1.0/src/snekmate/utils/EIP712DomainSeparator.vy): Make `EIP712DomainSeparator` module-friendly. ([#229](https://github.com/pcaversaccio/snekmate/pull/229)) + - [`Math`](https://github.com/pcaversaccio/snekmate/blob/v0.1.0/src/snekmate/utils/Math.vy): Make `Math` module-friendly. ([#230](https://github.com/pcaversaccio/snekmate/pull/230)) - **Vyper Contract Deployer** - [`VyperDeployer`](https://github.com/pcaversaccio/snekmate/blob/v0.1.0/lib/utils/VyperDeployer.sol): Improve error message in the event of a Vyper compilation error. ([#219](https://github.com/pcaversaccio/snekmate/pull/219)) diff --git a/README.md b/README.md index 03a4ab59..aa85ba52 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,8 @@ src ├── ECDSAMock — "ECDSA Module Reference Implementation" ├── MessageHashUtilsMock — "MessageHashUtils Module Reference Implementation" ├── SignatureCheckerMock — "SignatureChecker Module Reference Implementation" - └── EIP712DomainSeparatorMock — "EIP712DomainSeparator Module Reference Implementation" + ├── EIP712DomainSeparatorMock — "EIP712DomainSeparator Module Reference Implementation" + └── MathMock — "Math Module Reference Implementation" ``` ## 🎛 Installation diff --git a/src/snekmate/utils/Math.vy b/src/snekmate/utils/Math.vy index e487cb69..13fb06b8 100644 --- a/src/snekmate/utils/Math.vy +++ b/src/snekmate/utils/Math.vy @@ -10,19 +10,17 @@ function is inspired by an existing implementation, it is properly referenced in the function docstring. The following functions have been added for convenience: - - `uint256_average` (`external` `pure` function), - - `int256_average` (`external` `pure` function), - - `ceil_div` (`external` `pure` function), - - `signum` (`external` `pure` function), - - `mul_div` (`external` `pure` function), - - `log_2` (`external` `pure` function), - - `log_10` (`external` `pure` function), - - `log_256` (`external` `pure` function), - - `wad_ln` (`external` `pure` function), - - `wad_exp` (`external` `pure` function), - - `cbrt` (`external` `pure` function), - - `wad_cbrt` (`external` `pure` function), + - `_uint256_average` (`internal` `pure` function), + - `_int256_average` (`internal` `pure` function), + - `_ceil_div` (`internal` `pure` function), + - `_signum` (`internal` `pure` function), + - `_mul_div` (`internal` `pure` function), - `_log_2` (`internal` `pure` function), + - `_log_10` (`internal` `pure` function), + - `_log_256` (`internal` `pure` function), + - `_wad_ln` (`internal` `pure` function), + - `_wad_exp` (`internal` `pure` function), + - `_cbrt` (`internal` `pure` function), - `_wad_cbrt` (`internal` `pure` function). """ @@ -38,9 +36,9 @@ def __init__(): pass -@external +@internal @pure -def uint256_average(x: uint256, y: uint256) -> uint256: +def _uint256_average(x: uint256, y: uint256) -> uint256: """ @dev Returns the average of two 32-byte unsigned integers. @notice Note that the result is rounded towards zero. For @@ -55,9 +53,9 @@ def uint256_average(x: uint256, y: uint256) -> uint256: return unsafe_add(x & y, (x ^ y) >> 1) -@external +@internal @pure -def int256_average(x: int256, y: int256) -> int256: +def _int256_average(x: int256, y: int256) -> int256: """ @dev Returns the average of two 32-byte signed integers. @notice Note that the result is rounded towards infinity. @@ -72,9 +70,9 @@ def int256_average(x: int256, y: int256) -> int256: return unsafe_add(unsafe_add(x >> 1, y >> 1), x & y & 1) -@external +@internal @pure -def ceil_div(x: uint256, y: uint256) -> uint256: +def _ceil_div(x: uint256, y: uint256) -> uint256: """ @dev Calculates "ceil(x / y)" for any strictly positive `y`. @notice The implementation is inspired by OpenZeppelin's @@ -90,9 +88,9 @@ def ceil_div(x: uint256, y: uint256) -> uint256: return 0 if (x == empty(uint256)) else unsafe_add(unsafe_div(x - 1, y), 1) -@external +@internal @pure -def signum(x: int256) -> int256: +def _signum(x: int256) -> int256: """ @dev Returns the indication of the sign of a 32-byte signed integer. @notice The function returns `-1` if `x < 0`, `0` if `x == 0`, and `1` @@ -105,9 +103,9 @@ def signum(x: int256) -> int256: return unsafe_sub(convert((x > 0), int256), convert((x < 0), int256)) -@external +@internal @pure -def mul_div(x: uint256, y: uint256, denominator: uint256, roundup: bool) -> uint256: +def _mul_div(x: uint256, y: uint256, denominator: uint256, roundup: bool) -> uint256: """ @dev Calculates "(x * y) / denominator" in 512-bit precision, following the selected rounding direction. @@ -151,8 +149,8 @@ def mul_div(x: uint256, y: uint256, denominator: uint256, roundup: bool) -> uint # "(x * y) % denominator != 0", which accordingly rules out # the possibility of "x * y = 2**256 - 1" and `denominator == 1`. return unsafe_add(unsafe_div(prod0, denominator), 1) - else: - return unsafe_div(prod0, denominator) + + return unsafe_div(prod0, denominator) # Ensure that the result is less than 2**256. Also, # prevents that `denominator == 0`. @@ -224,9 +222,9 @@ def mul_div(x: uint256, y: uint256, denominator: uint256, roundup: bool) -> uint return result -@external +@internal @pure -def log_2(x: uint256, roundup: bool) -> uint256: +def _log_2(x: uint256, roundup: bool) -> uint256: """ @dev Returns the log in base 2 of `x`, following the selected rounding direction. @@ -243,12 +241,44 @@ def log_2(x: uint256, roundup: bool) -> uint256: if (x == empty(uint256)): return empty(uint256) - return self._log_2(x, roundup) + value: uint256 = x + result: uint256 = empty(uint256) + + # The following lines cannot overflow because we have the well-known + # decay behaviour of `log_2(max_value(uint256)) < max_value(uint256)`. + if (x >> 128 != empty(uint256)): + value = x >> 128 + result = 128 + if (value >> 64 != empty(uint256)): + value = value >> 64 + result = unsafe_add(result, 64) + if (value >> 32 != empty(uint256)): + value = value >> 32 + result = unsafe_add(result, 32) + if (value >> 16 != empty(uint256)): + value = value >> 16 + result = unsafe_add(result, 16) + if (value >> 8 != empty(uint256)): + value = value >> 8 + result = unsafe_add(result, 8) + if (value >> 4 != empty(uint256)): + value = value >> 4 + result = unsafe_add(result, 4) + if (value >> 2 != empty(uint256)): + value = value >> 2 + result = unsafe_add(result, 2) + if (value >> 1 != empty(uint256)): + result = unsafe_add(result, 1) + + if (roundup and ((1 << result) < x)): + result = unsafe_add(result, 1) + + return result -@external +@internal @pure -def log_10(x: uint256, roundup: bool) -> uint256: +def _log_10(x: uint256, roundup: bool) -> uint256: """ @dev Returns the log in base 10 of `x`, following the selected rounding direction. @@ -260,14 +290,14 @@ def log_10(x: uint256, roundup: bool) -> uint256: to round up or not. The default `False` is round down. @return uint256 The 32-byte calculation result. """ - value: uint256 = x - result: uint256 = empty(uint256) - # For the special case `x == 0` we already return 0 here in order # not to iterate through the remaining code. if (x == empty(uint256)): return empty(uint256) + value: uint256 = x + result: uint256 = empty(uint256) + # The following lines cannot overflow because we have the well-known # decay behaviour of `log_10(max_value(uint256)) < max_value(uint256)`. if (x >= 10 ** 64): @@ -297,9 +327,9 @@ def log_10(x: uint256, roundup: bool) -> uint256: return result -@external +@internal @pure -def log_256(x: uint256, roundup: bool) -> uint256: +def _log_256(x: uint256, roundup: bool) -> uint256: """ @dev Returns the log in base 256 of `x`, following the selected rounding direction. @@ -313,14 +343,14 @@ def log_256(x: uint256, roundup: bool) -> uint256: to round up or not. The default `False` is round down. @return uint256 The 32-byte calculation result. """ - value: uint256 = x - result: uint256 = empty(uint256) - # For the special case `x == 0` we already return 0 here in order # not to iterate through the remaining code. if (x == empty(uint256)): return empty(uint256) + value: uint256 = x + result: uint256 = empty(uint256) + # The following lines cannot overflow because we have the well-known # decay behaviour of `log_256(max_value(uint256)) < max_value(uint256)`. if (x >> 128 != empty(uint256)): @@ -344,9 +374,9 @@ def log_256(x: uint256, roundup: bool) -> uint256: return result -@external +@internal @pure -def wad_ln(x: int256) -> int256: +def _wad_ln(x: int256) -> int256: """ @dev Calculates the natural logarithm of a signed integer with a precision of 1e18. @@ -358,8 +388,6 @@ def wad_ln(x: int256) -> int256: @param x The 32-byte variable. @return int256 The 32-byte calculation result. """ - value: int256 = x - assert x >= empty(int256), "Math: wad_ln undefined" # For the special case `x == 0` we already return 0 here in order @@ -367,6 +395,8 @@ def wad_ln(x: int256) -> int256: if (x == empty(int256)): return empty(int256) + value: int256 = x + # We want to convert `x` from "10 ** 18" fixed point to "2 ** 96" # fixed point. We do this by multiplying by "2 ** 96 / 10 ** 18". # But since "ln(x * C) = ln(x) + ln(C)" holds, we can just do nothing @@ -417,9 +447,9 @@ def wad_ln(x: int256) -> int256: 600_920_179_829_731_861_736_702_779_321_621_459_595_472_258_049_074_101_567_377_883_020_018_308) >> 174 -@external +@internal @pure -def wad_exp(x: int256) -> int256: +def _wad_exp(x: int256) -> int256: """ @dev Calculates the natural exponential function of a signed integer with a precision of 1e18. @@ -485,9 +515,9 @@ def wad_exp(x: int256) -> int256: convert(unsafe_sub(195, k), uint256), int256) -@external +@internal @pure -def cbrt(x: uint256, roundup: bool) -> uint256: +def _cbrt(x: uint256, roundup: bool) -> uint256: """ @dev Calculates the cube root of an unsigned integer. @notice Note that this function consumes about 1,600 to 1,800 gas units @@ -512,9 +542,9 @@ def cbrt(x: uint256, roundup: bool) -> uint256: return y -@external +@internal @pure -def wad_cbrt(x: uint256) -> uint256: +def _wad_cbrt(x: uint256) -> uint256: """ @dev Calculates the cube root of an unsigned integer with a precision of 1e18. @@ -530,71 +560,6 @@ def wad_cbrt(x: uint256) -> uint256: if (x == empty(uint256)): return empty(uint256) - return self._wad_cbrt(x) - - -@internal -@pure -def _log_2(x: uint256, roundup: bool) -> uint256: - """ - @dev An `internal` helper function that returns the log in base 2 - of `x`, following the selected rounding direction. - @notice Note that it returns 0 if given 0. The implementation is - inspired by OpenZeppelin's implementation here: - https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol. - @param x The 32-byte variable. - @param roundup The Boolean variable that specifies whether - to round up or not. The default `False` is round down. - @return uint256 The 32-byte calculation result. - """ - value: uint256 = x - result: uint256 = empty(uint256) - - # The following lines cannot overflow because we have the well-known - # decay behaviour of `log_2(max_value(uint256)) < max_value(uint256)`. - if (x >> 128 != empty(uint256)): - value = x >> 128 - result = 128 - if (value >> 64 != empty(uint256)): - value = value >> 64 - result = unsafe_add(result, 64) - if (value >> 32 != empty(uint256)): - value = value >> 32 - result = unsafe_add(result, 32) - if (value >> 16 != empty(uint256)): - value = value >> 16 - result = unsafe_add(result, 16) - if (value >> 8 != empty(uint256)): - value = value >> 8 - result = unsafe_add(result, 8) - if (value >> 4 != empty(uint256)): - value = value >> 4 - result = unsafe_add(result, 4) - if (value >> 2 != empty(uint256)): - value = value >> 2 - result = unsafe_add(result, 2) - if (value >> 1 != empty(uint256)): - result = unsafe_add(result, 1) - - if (roundup and ((1 << result) < x)): - result = unsafe_add(result, 1) - - return result - - -@internal -@pure -def _wad_cbrt(x: uint256) -> uint256: - """ - @dev An `internal` helper function that calculates the cube root of an - unsigned integer with a precision of 1e18. - @notice Note that this function consumes about 1,450 to 1,650 gas units - depending on the value of `x`. The implementation is inspired - by Curve Finance's implementation under the MIT license here: - https://github.com/curvefi/tricrypto-ng/blob/main/contracts/main/CurveCryptoMathOptimized3.vy. - @param x The 32-byte variable from which the cube root is calculated. - @return The 32-byte cubic root of `x` with a precision of 1e18. - """ # Since this cube root is for numbers with base 1e18, we have to scale # the input by 1e36 to increase the precision. This leads to an overflow # for very large numbers. So we conditionally sacrifice precision. diff --git a/src/snekmate/utils/mocks/Base64Mock.vy b/src/snekmate/utils/mocks/Base64Mock.vy index bf925cc8..7aa865f3 100644 --- a/src/snekmate/utils/mocks/Base64Mock.vy +++ b/src/snekmate/utils/mocks/Base64Mock.vy @@ -7,9 +7,11 @@ """ -# @dev We import and initialise the `Base64` module. +# @dev We import the `Base64` module. +# @notice Please note that the `Base64` module +# is stateless and therefore does not require +# the `initializes` keyword for initialisation. from .. import Base64 as b64 -initializes: b64 @deploy @@ -20,7 +22,7 @@ def __init__(): in the creation-time EVM bytecode, the constructor is declared as `payable`. """ - b64.__init__() + pass @external diff --git a/src/snekmate/utils/mocks/Create2AddressMock.vy b/src/snekmate/utils/mocks/Create2AddressMock.vy index 723faad4..b4975c6d 100644 --- a/src/snekmate/utils/mocks/Create2AddressMock.vy +++ b/src/snekmate/utils/mocks/Create2AddressMock.vy @@ -7,9 +7,11 @@ """ -# @dev We import and initialise the `Create2Address` module. +# @dev We import the `Create2Address` module. +# @notice Please note that the `Create2Address` +# module is stateless and therefore does not require +# the `initializes` keyword for initialisation. from .. import Create2Address as c2a -initializes: c2a @deploy @@ -20,7 +22,7 @@ def __init__(): in the creation-time EVM bytecode, the constructor is declared as `payable`. """ - c2a.__init__() + pass @external diff --git a/src/snekmate/utils/mocks/CreateAddressMock.vy b/src/snekmate/utils/mocks/CreateAddressMock.vy index d180120c..1d8f4b83 100644 --- a/src/snekmate/utils/mocks/CreateAddressMock.vy +++ b/src/snekmate/utils/mocks/CreateAddressMock.vy @@ -7,9 +7,11 @@ """ -# @dev We import and initialise the `CreateAddress` module. +# @dev We import the `CreateAddress` module. +# @notice Please note that the `CreateAddress` +# module is stateless and therefore does not require +# the `initializes` keyword for initialisation. from .. import CreateAddress as ca -initializes: ca @deploy @@ -20,7 +22,7 @@ def __init__(): in the creation-time EVM bytecode, the constructor is declared as `payable`. """ - ca.__init__() + pass @external diff --git a/src/snekmate/utils/mocks/ECDSAMock.vy b/src/snekmate/utils/mocks/ECDSAMock.vy index 8fa53385..f3c51ea5 100644 --- a/src/snekmate/utils/mocks/ECDSAMock.vy +++ b/src/snekmate/utils/mocks/ECDSAMock.vy @@ -7,9 +7,11 @@ """ -# @dev We import and initialise the `ECDSA` module. +# @dev We import the `ECDSA` module. +# @notice Please note that the `ECDSA` module +# is stateless and therefore does not require +# the `initializes` keyword for initialisation. from .. import ECDSA as ec -initializes: ec @deploy @@ -20,7 +22,7 @@ def __init__(): in the creation-time EVM bytecode, the constructor is declared as `payable`. """ - ec.__init__() + pass @external diff --git a/src/snekmate/utils/mocks/MathMock.vy b/src/snekmate/utils/mocks/MathMock.vy new file mode 100644 index 00000000..ff23c10f --- /dev/null +++ b/src/snekmate/utils/mocks/MathMock.vy @@ -0,0 +1,229 @@ +# pragma version ~=0.4.0b6 +""" +@title Math Module Reference Implementation +@custom:contract-name MathMock +@license GNU Affero General Public License v3.0 only +@author pcaversaccio +""" + + +# @dev We import the `Math` module. +# @notice Please note that the `Math` module +# is stateless and therefore does not require +# the `initializes` keyword for initialisation. +from .. import Math as ma + + +@deploy +@payable +def __init__(): + """ + @dev To omit the opcodes for checking the `msg.value` + in the creation-time EVM bytecode, the constructor + is declared as `payable`. + """ + pass + + +@external +@pure +def uint256_average(x: uint256, y: uint256) -> uint256: + """ + @dev Returns the average of two 32-byte unsigned integers. + @notice Note that the result is rounded towards zero. For + more details on finding the average of two unsigned + integers without an overflow, please refer to: + https://devblogs.microsoft.com/oldnewthing/20220207-00/?p=106223. + @param x The first 32-byte unsigned integer of the data set. + @param y The second 32-byte unsigned integer of the data set. + @return uint256 The 32-byte average (rounded towards zero) of + `x` and `y`. + """ + return ma._uint256_average(x, y) + + +@external +@pure +def int256_average(x: int256, y: int256) -> int256: + """ + @dev Returns the average of two 32-byte signed integers. + @notice Note that the result is rounded towards infinity. + For more details on finding the average of two signed + integers without an overflow, please refer to: + https://patents.google.com/patent/US6007232A/en. + @param x The first 32-byte signed integer of the data set. + @param y The second 32-byte signed integer of the data set. + @return int256 The 32-byte average (rounded towards infinity) + of `x` and `y`. + """ + return ma._int256_average(x, y) + + +@external +@pure +def ceil_div(x: uint256, y: uint256) -> uint256: + """ + @dev Calculates "ceil(x / y)" for any strictly positive `y`. + @notice The implementation is inspired by OpenZeppelin's + implementation here: + https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol. + @param x The 32-byte numerator. + @param y The 32-byte denominator. + @return uint256 The 32-byte rounded up result of "x/y". + """ + return ma._ceil_div(x, y) + + +@external +@pure +def signum(x: int256) -> int256: + """ + @dev Returns the indication of the sign of a 32-byte signed integer. + @notice The function returns `-1` if `x < 0`, `0` if `x == 0`, and `1` + if `x > 0`. For more details on finding the sign of a signed + integer, please refer to: + https://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign. + @param x The 32-byte signed integer variable. + @return int256 The 32-byte sign indication (`1`, `0`, or `-1`) of `x`. + """ + return ma._signum(x) + + +@external +@pure +def mul_div(x: uint256, y: uint256, denominator: uint256, roundup: bool) -> uint256: + """ + @dev Calculates "(x * y) / denominator" in 512-bit precision, + following the selected rounding direction. + @notice The implementation is inspired by Remco Bloemen's + implementation under the MIT license here: + https://xn--2-umb.com/21/muldiv. + Furthermore, the rounding direction design pattern is + inspired by OpenZeppelin's implementation here: + https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol. + @param x The 32-byte multiplicand. + @param y The 32-byte multiplier. + @param denominator The 32-byte divisor. + @param roundup The Boolean variable that specifies whether + to round up or not. The default `False` is round down. + @return uint256 The 32-byte calculation result. + """ + return ma._mul_div(x, y, denominator, roundup) + + +@external +@pure +def log_2(x: uint256, roundup: bool) -> uint256: + """ + @dev Returns the log in base 2 of `x`, following the selected + rounding direction. + @notice Note that it returns 0 if given 0. The implementation is + inspired by OpenZeppelin's implementation here: + https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol. + @param x The 32-byte variable. + @param roundup The Boolean variable that specifies whether + to round up or not. The default `False` is round down. + @return uint256 The 32-byte calculation result. + """ + return ma._log_2(x, roundup) + + +@external +@pure +def log_10(x: uint256, roundup: bool) -> uint256: + """ + @dev Returns the log in base 10 of `x`, following the selected + rounding direction. + @notice Note that it returns 0 if given 0. The implementation is + inspired by OpenZeppelin's implementation here: + https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol. + @param x The 32-byte variable. + @param roundup The Boolean variable that specifies whether + to round up or not. The default `False` is round down. + @return uint256 The 32-byte calculation result. + """ + return ma._log_10(x, roundup) + + +@external +@pure +def log_256(x: uint256, roundup: bool) -> uint256: + """ + @dev Returns the log in base 256 of `x`, following the selected + rounding direction. + @notice Note that it returns 0 if given 0. Also, adding one to the + rounded down result gives the number of pairs of hex symbols + needed to represent `x` as a hex string. The implementation is + inspired by OpenZeppelin's implementation here: + https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol. + @param x The 32-byte variable. + @param roundup The Boolean variable that specifies whether + to round up or not. The default `False` is round down. + @return uint256 The 32-byte calculation result. + """ + return ma._log_256(x, roundup) + + +@external +@pure +def wad_ln(x: int256) -> int256: + """ + @dev Calculates the natural logarithm of a signed integer with a + precision of 1e18. + @notice Note that it returns 0 if given 0. Furthermore, this function + consumes about 1,400 to 1,650 gas units depending on the value + of `x`. The implementation is inspired by Remco Bloemen's + implementation under the MIT license here: + https://xn--2-umb.com/22/exp-ln. + @param x The 32-byte variable. + @return int256 The 32-byte calculation result. + """ + return ma._wad_ln(x) + + +@external +@pure +def wad_exp(x: int256) -> int256: + """ + @dev Calculates the natural exponential function of a signed integer with + a precision of 1e18. + @notice Note that this function consumes about 810 gas units. The implementation + is inspired by Remco Bloemen's implementation under the MIT license here: + https://xn--2-umb.com/22/exp-ln. + @param x The 32-byte variable. + @return int256 The 32-byte calculation result. + """ + return ma._wad_exp(x) + + +@external +@pure +def cbrt(x: uint256, roundup: bool) -> uint256: + """ + @dev Calculates the cube root of an unsigned integer. + @notice Note that this function consumes about 1,600 to 1,800 gas units + depending on the value of `x` and `roundup`. The implementation is + inspired by Curve Finance's implementation under the MIT license here: + https://github.com/curvefi/tricrypto-ng/blob/main/contracts/main/CurveCryptoMathOptimized3.vy. + @param x The 32-byte variable from which the cube root is calculated. + @param roundup The Boolean variable that specifies whether + to round up or not. The default `False` is round down. + @return The 32-byte cube root of `x`. + """ + return ma._cbrt(x, roundup) + + +@external +@pure +def wad_cbrt(x: uint256) -> uint256: + """ + @dev Calculates the cube root of an unsigned integer with a precision + of 1e18. + @notice Note that this function consumes about 1,500 to 1,700 gas units + depending on the value of `x`. The implementation is inspired + by Curve Finance's implementation under the MIT license here: + https://github.com/curvefi/tricrypto-ng/blob/main/contracts/main/CurveCryptoMathOptimized3.vy. + @param x The 32-byte variable from which the cube root is calculated. + @return The 32-byte cubic root of `x` with a precision of 1e18. + """ + return ma._wad_cbrt(x) diff --git a/src/snekmate/utils/mocks/MessageHashUtilsMocks.vy b/src/snekmate/utils/mocks/MessageHashUtilsMocks.vy index c1437cf6..5fef7432 100644 --- a/src/snekmate/utils/mocks/MessageHashUtilsMocks.vy +++ b/src/snekmate/utils/mocks/MessageHashUtilsMocks.vy @@ -7,9 +7,11 @@ """ -# @dev We import and initialise the `MessageHashUtils` module. +# @dev We import the `MessageHashUtils` module. +# @notice Please note that the `MessageHashUtils` +# module is stateless and therefore does not require +# the `initializes` keyword for initialisation. from .. import MessageHashUtils as mu -initializes: mu @deploy @@ -20,7 +22,7 @@ def __init__(): in the creation-time EVM bytecode, the constructor is declared as `payable`. """ - mu.__init__() + pass @external diff --git a/src/snekmate/utils/mocks/SignatureCheckerMock.vy b/src/snekmate/utils/mocks/SignatureCheckerMock.vy index c6c9d9ab..60288c00 100644 --- a/src/snekmate/utils/mocks/SignatureCheckerMock.vy +++ b/src/snekmate/utils/mocks/SignatureCheckerMock.vy @@ -7,9 +7,11 @@ """ -# @dev We import and initialise the `SignatureChecker` module. +# @dev We import the `SignatureChecker` module. +# @notice Please note that the `SignatureChecker` +# module is stateless and therefore does not require +# the `initializes` keyword for initialisation. from .. import SignatureChecker as sc -initializes: sc # @dev We export (i.e. the runtime bytecode exposes these @@ -32,7 +34,7 @@ def __init__(): in the creation-time EVM bytecode, the constructor is declared as `payable`. """ - sc.__init__() + pass @external diff --git a/test/utils/Math.t.sol b/test/utils/Math.t.sol index 05f07280..f6accc6a 100644 --- a/test/utils/Math.t.sol +++ b/test/utils/Math.t.sol @@ -111,7 +111,10 @@ contract MathTest is Test { function setUp() public { math = IMath( - vyperDeployer.deployContract("src/snekmate/utils/", "Math") + vyperDeployer.deployContract( + "src/snekmate/utils/mocks/", + "MathMock" + ) ); }