From c971040ec69fb6ee8a843895fa0bf4e723d4ddee Mon Sep 17 00:00:00 2001 From: Paul Razvan Berg Date: Tue, 13 Dec 2022 14:24:09 +0200 Subject: [PATCH] feat: add value types SD1x18 and UD2x18 --- CHANGELOG.md | 7 +++ README.md | 12 +++++ package.json | 2 +- src/SD1x18.sol | 16 ++++++ src/UD2x18.sol | 16 ++++++ src/test/Assertions.sol | 50 +++++++++++++++++++ test/sd1x18/conversion/SD1x18Conversion.t.sol | 14 ++++++ test/ud2x18/conversion/UD2x18Conversion.t.sol | 14 ++++++ 8 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 src/SD1x18.sol create mode 100644 src/UD2x18.sol create mode 100644 test/sd1x18/conversion/SD1x18Conversion.t.sol create mode 100644 test/ud2x18/conversion/UD2x18Conversion.t.sol diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ced8b64..298087c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Common Changelog](https://common-changelog.org/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +[3.1.0]: https://github.com/paulrberg/prb-math/compare/v3.0.0...v3.1.0 [3.0.0]: https://github.com/paulrberg/prb-math/compare/v2.5.0...v3.0.0 [2.5.0]: https://github.com/paulrberg/prb-math/compare/v2.4.3...v2.5.0 [2.4.3]: https://github.com/paulrberg/prb-math/compare/v2.4.2...v2.4.3 @@ -24,6 +25,12 @@ The format is based on [Common Changelog](https://common-changelog.org/), and th [1.0.1]: https://github.com/paulrberg/prb-math/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.com/paulrberg/prb-math/releases/tag/v1.0.0 +## [3.1.0] - 2022-12-13 + +### Added + +- Value types `SD1x18` and `UD2x18` (@paulrberg) + ## [3.0.0] - 2022-11-29 [1b82ea]: https://github.com/paulrberg/prb-math/commit/1b82ea diff --git a/README.md b/README.md index 8129a34b..06a5cba9 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,18 @@ function addRshiftEq() pure returns (bool result) { ``` +### Adjacent Value Types + +PRBMath provides adjacent value types that serve as abstractions over other vanilla types such as `int64`. The types currently available are: + +- `SD1x18` (int64) +- `UD2x18` (uint64) + +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. For that, you will have to unwrap them into a simple integer and then to the core +types `SD59x18` and `UD60x18`. + ### Assertions PRBMath is shipped with typed assertions that you can use for writing tests with [PRBTest](https://github.com/paulrberg/prb-test), which is based on diff --git a/package.json b/package.json index e0b757e2..4d09ed0b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@prb/math", "description": "Solidity library for advanced fixed-point math", - "version": "3.0.0", + "version": "3.1.0", "author": { "name": "Paul Razvan Berg", "url": "https://github.com/paulrberg" diff --git a/src/SD1x18.sol b/src/SD1x18.sol new file mode 100644 index 00000000..b608fe09 --- /dev/null +++ b/src/SD1x18.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.13; + +/// @notice The signed 1.18-decimal fixed-point number representation, which can have up to 1 digit and up to 18 decimals. +/// The values of this are bound by the minimum and the maximum values permitted by the underlying Solidity type int64. +/// This is useful when end users want to use int64 to save gas, e.g. with tight variable packing in contract storage. +type SD1x18 is int64; + +/*////////////////////////////////////////////////////////////////////////// + CONVERSION FUNCTIONS +//////////////////////////////////////////////////////////////////////////*/ + +/// @notice Wraps a signed integer into the SD1x18 type. +function sd1x18(int64 x) pure returns (SD1x18 result) { + result = SD1x18.wrap(x); +} diff --git a/src/UD2x18.sol b/src/UD2x18.sol new file mode 100644 index 00000000..18978b4e --- /dev/null +++ b/src/UD2x18.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.13; + +/// @notice The unsigned 2.18-decimal fixed-point number representation, which can have up to 2 digits and up to 18 decimals. +/// The values of this are bound by the minimum and the maximum values permitted by the underlying Solidity type uint64. +/// This is useful when end users want to use uint64 to save gas, e.g. with tight variable packing in contract storage. +type UD2x18 is uint64; + +/*////////////////////////////////////////////////////////////////////////// + CONVERSION FUNCTIONS +//////////////////////////////////////////////////////////////////////////*/ + +/// @notice Wraps a signed integer into the UD2x18 type. +function ud2x18(uint64 x) pure returns (UD2x18 result) { + result = UD2x18.wrap(x); +} diff --git a/src/test/Assertions.sol b/src/test/Assertions.sol index df79b7d3..46d85443 100644 --- a/src/test/Assertions.sol +++ b/src/test/Assertions.sol @@ -3,54 +3,104 @@ pragma solidity >=0.8.13; import { PRBTest } from "@prb/test/PRBTest.sol"; +import { SD1x18 } from "../SD1x18.sol"; import { SD59x18 } from "../SD59x18.sol"; +import { UD2x18 } from "../UD2x18.sol"; import { UD60x18 } from "../UD60x18.sol"; contract Assertions is PRBTest { + function assertEq(SD1x18 a, SD1x18 b) internal { + assertEq(SD1x18.unwrap(a), SD1x18.unwrap(b)); + } + function assertEq(SD59x18 a, SD59x18 b) internal { assertEq(SD59x18.unwrap(a), SD59x18.unwrap(b)); } + function assertEq(SD1x18 a, SD1x18 b, string memory err) internal { + assertEq(SD1x18.unwrap(a), SD1x18.unwrap(b), err); + } + function assertEq(SD59x18 a, SD59x18 b, string memory err) internal { assertEq(SD59x18.unwrap(a), SD59x18.unwrap(b), err); } + function assertEq(SD1x18 a, int256 b) internal { + assertEq(SD1x18.unwrap(a), b); + } + function assertEq(SD59x18 a, int256 b) internal { assertEq(SD59x18.unwrap(a), b); } + function assertEq(SD1x18 a, int256 b, string memory err) internal { + assertEq(SD1x18.unwrap(a), b, err); + } + function assertEq(SD59x18 a, int256 b, string memory err) internal { assertEq(SD59x18.unwrap(a), b, err); } + function assertEq(int256 a, SD1x18 b) internal { + assertEq(a, SD1x18.unwrap(b)); + } + function assertEq(int256 a, SD59x18 b) internal { assertEq(a, SD59x18.unwrap(b)); } + function assertEq(int256 a, SD1x18 b, string memory err) internal { + assertEq(a, SD1x18.unwrap(b), err); + } + function assertEq(int256 a, SD59x18 b, string memory err) internal { assertEq(a, SD59x18.unwrap(b), err); } + function assertEq(UD2x18 a, UD2x18 b) internal { + assertEq(UD2x18.unwrap(a), UD2x18.unwrap(b)); + } + function assertEq(UD60x18 a, UD60x18 b) internal { assertEq(UD60x18.unwrap(a), UD60x18.unwrap(b)); } + function assertEq(UD2x18 a, UD2x18 b, string memory err) internal { + assertEq(UD2x18.unwrap(a), UD2x18.unwrap(b), err); + } + function assertEq(UD60x18 a, UD60x18 b, string memory err) internal { assertEq(UD60x18.unwrap(a), UD60x18.unwrap(b), err); } + function assertEq(UD2x18 a, uint256 b) internal { + assertEq(UD2x18.unwrap(a), b); + } + function assertEq(UD60x18 a, uint256 b) internal { assertEq(UD60x18.unwrap(a), b); } + function assertEq(UD2x18 a, uint256 b, string memory err) internal { + assertEq(UD2x18.unwrap(a), b, err); + } + function assertEq(UD60x18 a, uint256 b, string memory err) internal { assertEq(UD60x18.unwrap(a), b, err); } + function assertEq(uint256 a, UD2x18 b) internal { + assertEq(a, UD2x18.unwrap(b)); + } + function assertEq(uint256 a, UD60x18 b) internal { assertEq(a, UD60x18.unwrap(b)); } + function assertEq(uint256 a, UD2x18 b, string memory err) internal { + assertEq(a, UD2x18.unwrap(b), err); + } + function assertEq(uint256 a, UD60x18 b, string memory err) internal { assertEq(a, UD60x18.unwrap(b), err); } diff --git a/test/sd1x18/conversion/SD1x18Conversion.t.sol b/test/sd1x18/conversion/SD1x18Conversion.t.sol new file mode 100644 index 00000000..5e4dc9df --- /dev/null +++ b/test/sd1x18/conversion/SD1x18Conversion.t.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.13; + +import "src/SD1x18.sol"; +import { BaseTest } from "../../BaseTest.t.sol"; + +/// @dev Collection of tests for the conversion functions available in the SD1x18 type. +contract SD1x18__ConversionTest is BaseTest { + function testSd1x18(int64 x) external { + SD1x18 actual = sd1x18(x); + SD1x18 expected = SD1x18.wrap(x); + assertEq(actual, expected); + } +} diff --git a/test/ud2x18/conversion/UD2x18Conversion.t.sol b/test/ud2x18/conversion/UD2x18Conversion.t.sol new file mode 100644 index 00000000..27ad5b3a --- /dev/null +++ b/test/ud2x18/conversion/UD2x18Conversion.t.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.13; + +import "src/UD2x18.sol"; +import { BaseTest } from "../../BaseTest.t.sol"; + +/// @dev Collection of tests for the conversion functions available in the UD2x18 type. +contract UD2x18__ConversionTest is BaseTest { + function testUd2x18(uint64 x) external { + UD2x18 actual = ud2x18(x); + UD2x18 expected = UD2x18.wrap(x); + assertEq(actual, expected); + } +}