JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 6969
  • Score
    100M100P100Q138682F
  • License MIT

Solidity library for advanced fixed-point math

Package Exports

    This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@prb/math) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

    Readme

    PRBMath GitHub Actions Foundry License: MIT

    Solidity library for advanced fixed-point math that operates with signed 59.18-decimal fixed-point and unsigned 60.18-decimal fixed-point numbers. The name of the number format is due to the integer part having up to 59/60 decimals and the fractional part having up to 18 decimals. The numbers are bound by the minimum and the maximum values permitted by the Solidity types int256 and uint256.

    • Operates with signed and unsigned denary fixed-point numbers, with 18 trailing decimals
    • Offers advanced math functions like logarithms, exponentials, powers and square roots
    • Provides type safety via user-defined value types
    • Gas efficient, but still user-friendly
    • Ergonomic developer experience thanks to using free functions instead of libraries
    • Bakes in overflow-safe multiplication and division via mulDiv
    • Reverts with custom errors instead of reason strings
    • Well-documented with NatSpec comments
    • Built and tested with Foundry

    I created this because I wanted a fixed-point math library that is at the same time intuitive, efficient and safe. I looked at ABDKMath64x64, which is fast, but it uses binary numbers which are counter-intuitive and non-familiar to humans. Then, I looked at Fixidity, which operates with denary numbers and has wide precision, but is slow and susceptible to phantom overflow. Finally, I looked at Solmate, which checks all the boxes mentioned thus far, but it doesn't offer type safety.

    Install

    Node.js

    This is the recommended approach.

    Install PRBMath using your favorite package manager, e.g., with Bun:

    bun add @prb/math

    Then, if you are using Foundry, you need to add this to your remappings.txt file:

    @prb/math/=node_modules/@prb/math/

    Git Submodules

    This installation method is not recommended, but it is available for those who prefer it.

    First, install the submodule using Forge:

    forge install --no-commit PaulRBerg/prb-math@release-v4

    Your .gitmodules file should now contain the following entry:

    [submodule "lib/prb-math"]
      branch = "release-v4"
      path = "lib/prb-math"
      url = "https://github.com/PaulRBerg/prb-math"

    Finally, add this to your remappings.txt file:

    @prb/math/=lib/prb-math/

    Usage

    There are two user-defined value types:

    1. SD59x18 (signed)
    2. UD60x18 (unsigned)

    If you don't know what a user-defined value type is, check out this blog post.

    If you don't need negative numbers, there's no point in using the signed flavor SD59x18. The unsigned flavor UD60x18 is more gas efficient.

    Note that PRBMath is not a library in the Solidity sense. It's just a collection of free functions.

    Importing

    It is recommended that you import PRBMath using specific symbols. Importing full files can result in Solidity complaining about duplicate definitions and static analyzers like Slither erroring, especially as repos grow and have more dependencies with overlapping names.

    pragma solidity >=0.8.19;
    
    import { SD59x18 } from "@prb/math/src/SD59x18.sol";
    import { UD60x18 } from "@prb/math/src/UD60x18.sol";

    Any function that is not available in the types directly has to be imported explicitly. Here's an example for the sd and the ud functions:

    pragma solidity >=0.8.19;
    
    import { SD59x18, sd } from "@prb/math/src/SD59x18.sol";
    import { UD60x18, ud } from "@prb/math/src/UD60x18.sol";

    Note that PRBMath can only be used in Solidity v0.8.19 and above.

    SD59x18

    // SPDX-License-Identifier: UNLICENSED
    pragma solidity >=0.8.19;
    
    import { SD59x18, sd } from "@prb/math/src/SD59x18.sol";
    
    contract SignedConsumer {
      /// @notice Calculates 5% of the given signed number.
      /// @dev Try this with x = 400e18.
      function signedPercentage(SD59x18 x) external pure returns (SD59x18 result) {
        SD59x18 fivePercent = sd(0.05e18);
        result = x.mul(fivePercent);
      }
    
      /// @notice Calculates the binary logarithm of the given signed number.
      /// @dev Try this with x = 128e18.
      function signedLog2(SD59x18 x) external pure returns (SD59x18 result) {
        result = x.log2();
      }
    }

    UD60x18

    // SPDX-License-Identifier: UNLICENSED
    pragma solidity >=0.8.19;
    
    import { UD60x18, ud } from "@prb/math/src/UD60x18.sol";
    
    contract UnsignedConsumer {
      /// @notice Calculates 5% of the given signed number.
      /// @dev Try this with x = 400e18.
      function unsignedPercentage(UD60x18 x) external pure returns (UD60x18 result) {
        UD60x18 fivePercent = ud(0.05e18);
        result = x.mul(fivePercent);
      }
    
      /// @notice Calculates the binary logarithm of the given signed number.
      /// @dev Try this with x = 128e18.
      function unsignedLog2(UD60x18 x) external pure returns (UD60x18 result) {
        result = x.log2();
      }
    }

    Features

    Because there's significant overlap between the features available in SD59x18 and UD60x18, there is only one table per section. If in doubt, refer to the source code, which is well-documented with NatSpec comments.

    Mathematical Functions

    Name Operator Description
    abs N/A Absolute value
    avg N/A Arithmetic average
    ceil N/A Smallest whole number greater than or equal to x
    div / Fixed-point division
    exp N/A Natural exponential e^x
    exp2 N/A Binary exponential 2^x
    floor N/A Greatest whole number less than or equal to x
    frac N/A Fractional part
    gm N/A Geometric mean
    inv N/A Inverse 1÷x
    ln N/A Natural logarithm ln(x)
    log10 N/A Common logarithm log10(x)
    log2 N/A Binary logarithm log2(x)
    mul * Fixed-point multiplication
    pow N/A Power function x^y
    powu N/A Power function x^y with y simple integer
    sqrt N/A Square root

    Adjacent Value Types

    PRBMath provides adjacent value types that serve as abstractions over other vanilla types:

    Value Type Underlying Type
    SD1x18 int64
    SD21x18 int128
    UD2x18 uint64
    UD21x18 uint128

    These are useful if you want to save gas by using a lower bit width integer, e.g., in a struct.

    Note that these types don't have any mathematical functionality. To do math with them, you will have to unwrap them into a simple integer and then to the core types SD59x18 and UD60x18.

    Casting Functions

    All PRBMath types have casting functions to and from all other types, including a few basic types like uint128 and uint40.

    Name Description
    intoSD1x18 Casts a number to SD1x18
    intoSD59x18 Casts a number to SD59x18
    intoUD2x18 Casts a number to UD2x18
    intoUD60x18 Casts a number to UD60x18
    intoUint256 Casts a number to uint256
    intoUint128 Casts a number to uint128
    intoUint40 Casts a number to uint40
    sd1x18 Alias for SD1x18.wrap
    sd59x18 Alias for SD59x18.wrap
    ud2x18 Alias for UD2x18.wrap
    ud60x18 Alias for UD60x18.wrap

    Conversion Functions

    The difference between "conversion" and "casting" is that conversion functions multiply or divide the inputs, whereas casting functions simply cast them.

    Name Description
    convert(SD59x18) Converts an SD59x18 number to a simple integer by dividing it by 1e18
    convert(UD60x18) Converts a UD60x18 number to a simple integer by dividing it by 1e18
    convert(int256) Converts a simple integer to SD59x18 by multiplying it by 1e18
    convert(uint256) Converts a simple integer to UD60x18 type by multiplying it by 1e18

    Helper Functions

    In addition to offering mathematical, casting, and conversion functions, PRBMath provides numerous helper functions for user-defined value types:

    Name Operator Description
    add + Checked addition
    and & Logical AND
    eq == Equality
    gt > Greater than operator
    gte >= Greater than or equal to
    isZero N/A Check if a number is zero
    lshift N/A Bitwise left shift
    lt < Less than
    lte <= Less than or equal to
    mod % Modulo
    neq != Not equal operator
    not ~ Negation operator
    or | Logical OR
    rshift N/A Bitwise right shift
    sub - Checked subtraction
    unary - Checked unary
    uncheckedAdd N/A Unchecked addition
    uncheckedSub N/A Unchecked subtraction
    xor ^ Exclusive or (XOR)

    These helpers are designed to streamline basic operations such as addition and equality checks, eliminating the need to constantly unwrap and re-wrap variables. However, it is important to be aware that utilizing these functions may result in increased gas costs compared to unwrapping and directly using the vanilla types.

    // SPDX-License-Identifier: UNLICENSED
    pragma solidity >=0.8.19;
    
    import { UD60x18, ud } from "@prb/math/src/UD60x18.sol";
    
    function addRshiftEq() pure returns (bool result) {
      UD60x18 x = ud(1e18);
      UD60x18 y = ud(3e18);
      y = y.add(x); // also: y = y + x
      y = y.rshift(2);
      result = x.eq(y); // also: y == x
    }
    

    Assertions

    PRBMath comes with typed assertions that you can use for writing tests with PRBTest, which is based on Foundry. This is useful if, for example, you would like to assert that two UD60x18 numbers are equal.

    pragma solidity >=0.8.19;
    
    import { UD60x18, ud } from "@prb/math/src/UD60x18.sol";
    import { Assertions as PRBMathAssertions } from "@prb/math/test/Assertions.sol";
    import { PRBTest } from "@prb/math/src/test/PRBTest.sol";
    
    contract MyTest is PRBTest, PRBMathAssertions {
      function testAdd() external {
        UD60x18 x = ud(1e18);
        UD60x18 y = ud(2e18);
        UD60x18 z = ud(3e18);
        assertEq(x.add(y), z);
      }
    }

    Gas Efficiency

    PRBMath is faster than ABDKMath for abs, exp, exp2, gm, inv, ln, log2, but it is slower than ABDKMath for avg, div, mul, powu and sqrt.

    The main reason why PRBMath lags behind ABDKMath's mul and div functions is that it operates with 256-bit word sizes, and so it has to account for possible intermediary overflow. ABDKMath, on the other hand, operates with 128-bit word sizes.

    Note: I did not find a good way to automatically generate gas reports for PRBMath. See the #134 discussion for more details about this issue.

    PRBMath

    Gas estimations based on the v2.0.1 and the v3.0.0 releases.

    SD59x18 Min Max Avg UD60x18 Min Max Avg
    abs 68 72 70 n/a n/a n/a n/a
    avg 95 105 100 avg 57 57 57
    ceil 82 117 101 ceil 78 78 78
    div 431 483 451 div 205 205 205
    exp 38 2797 2263 exp 1874 2742 2244
    exp2 63 2678 2104 exp2 1784 2652 2156
    floor 82 117 101 floor 43 43 43
    frac 23 23 23 frac 23 23 23
    gm 26 892 690 gm 26 893 691
    inv 40 40 40 inv 40 40 40
    ln 463 7306 4724 ln 419 6902 3814
    log10 104 9074 4337 log10 503 8695 4571
    log2 377 7241 4243 log2 330 6825 3426
    mul 455 463 459 mul 219 275 247
    pow 64 11338 8518 pow 64 10637 6635
    powu 293 24745 5681 powu 83 24535 5471
    sqrt 140 839 716 sqrt 114 846 710

    ABDKMath64x64

    Gas estimations based on the v3.0 release of ABDKMath. See my abdk-gas-estimations repo.

    Method Min Max Avg
    abs 88 92 90
    avg 41 41 41
    div 168 168 168
    exp 77 3780 2687
    exp2 77 3600 2746
    gavg 166 875 719
    inv 157 157 157
    ln 7074 7164 7126
    log2 6972 7062 7024
    mul 111 111 111
    pow 303 4740 1792
    sqrt 129 809 699

    Contributing

    Feel free to dive in! Open an issue, start a discussion or submit a PR.

    Pre Requisites

    You will need the following software on your machine:

    In addition, familiarity with Solidity is requisite.

    Set Up

    Clone this repository including submodules:

    $ git clone --recurse-submodules -j8 git@github.com:PaulRBerg/prb-math.git

    Then, inside the project's directory, run this to install the Node.js dependencies:

    $ bun install

    Now you can start making changes.

    Syntax Highlighting

    You will need the following VSCode extensions:

    Security

    The codebase has undergone audits by leading security experts from Cantina and Certora. For a comprehensive list of all audits conducted, see the SECURITY file.

    Caveat Emptor

    This is experimental software and is provided on an "as is" and "as available" basis. I do not give any warranties and will not be liable for any loss, direct or indirect through continued use of this codebase.

    Contact

    If you discover any bugs or security issues, please report them via Telegram.

    Acknowledgments

    • Mikhail Vladimirov for the insights he shared in the Math in Solidity article series.
    • Remco Bloemen for his work on overflow-safe multiplication and division, and for responding to the questions I asked him while developing the library.
    • Everyone who has contributed a PR to this repository.

    License

    This project is licensed under MIT.