This library implements two types of fixed-point decimal numbers, "wad" (18 decimals of precision) and "ray" (27 decimals of decimal numbers), available in signed (SignedWad
and SignedRay
) and unsigned (Wad
and Ray
) versions, written in Cairo for Starknet.
Wad
and Ray
are implemented as structs with a single u128
member for the value, while SignedWad
and SignedRay
are implemented as structs with a u128
member for the value and bool
member for the sign
(i.e. if the sign
is true
, then the value is negative).
This library includes arithmetic, comparison and conversion functions.
Addition and subtraction can be performed via the Add
, AddEq
, Sub
and SubEq
traits as follows:
a + b
a += b
a - b
a -= b
where both a
and b
are of the same type.
Multiplication and division can be performed via the the Mul
, MulEq
, Div
and DivEq
traits as follows:
a * b
a *= b
a / b
a /= b
where botha
andb
are of the same type.
There is also a set of functions for operations involving a Wad
and a Ray
:
wmul_wr
/wmul_rw
: Multiply aWad
value with aRay
value, and divide the product by oneWad
scale to return aRay
wdiv_rw
: Scale up aRay
value by oneWad
scale and divide the scaled value by aWad
value to return aRay
rmul_wr
/rmul_rw
: Multiply aWad
value with aRay
value, and divide the product by oneRay
scale to return aWad
rdiv_wr
: Scale up aWad
value by oneRay
scale and divide the scaled value by aRay
to return aWad
rdiv_ww
: Scale up aWad
value by oneRay
scale and divide the scaled value by aWad
to return aRay
For multiplication, the prefixes w
and r
denote whether the product is divided (i.e. scaled down) by a Wad
or Ray
respectively. For division, the prefixes w
and r
denote whether the first operand is multiplied (i.e. scaled up) by a Wad
or Ray
before the division respectively.
As these are fixed point operations, do take note that there will be a loss of precision.
The following values and functions for both Wad
and Ray
, and SignedWad
and SignedRay
, are available via the Zeroable
and Oneable
traits.
WadZeroable::zero()
/RayZeroable::zero()
: Returns the zero value forWad
andRay
respectivelyis_zero()
: Returns true if theWad
orRay
value is zerois_non_zero()
Returns true if theWad
orRay
value is not zeroWadOneable::one()
/RayOneable::one()
: Returns the scale value forWad
(i.e. 1018) andRay
(i.e. 1027) respectivelyis_one()
: Returns true if theWad
orRay
value is the scale value (i.e. 1018 and 1027 respectively)is_non_one()
Returns true if theWad
orRay
value is not the scale value (i.e. 1018 and 1027 respectively)
SignedWadZeroable::zero()
/SignedRayZeroable::zero()
: Returns the zero value forWad
andRay
respectivelyis_zero()
: Returns true if theSignedWad
orSignedRay
value is zero, regardless of thesign
.is_non_zero()
Returns true if theSignedWad
orSignedRay
value is not zeroSignedWadOneable::one()
/SignedRayOneable::one()
: Returns the positive scale value for "wad" (i.e. 1018) and "ray" (i.e. 1027) respectivelyis_one()
: Returns true if theSignedWad
orSignedRay
value is the positive scale value (i.e. 1018 and 1027 respectively)is_non_one()
Returns true if theSignedWad
orSignedRay
value is not the positive scale value (i.e. 1018 and 1027 respectively)
The bound values for both Wad
and Ray
can be obtained via the BoundedInt
trait.
BoundedWad::max()
: Returns the maximumWad
value of 2128 - 1BoundedWad::min()
: Returns the minimumWad
value of 0BoundedRay::max()
: Returns the maximumRay
value of 2128 - 1BoundedRay::min()
: Returns the minimumRay
value of 0
Comparison for both Wad
and Ray
, and SignedWad
and SignedRay
, can be performed via the PartialEq
and PartialOrd
traits as follows:
a == b
a != b
a > b
a >= b
a < b
a <= b
where both a
and b
are of the same type.
Any type that can be converted to a u128
via the Into
trait can similarly be converted to a Wad
or Ray
via the Into
trait.
Additionally, the following conversions from integer types are supported for SignedWad
and SignedRay
via the Into
trait`:
u128
->SignedWad
: Convert au128
to aSignedWad
without modifying the value, with thesign
set tofalse
u128
->SignedRay
: Convert au128
to aSignedRay
without modifying the value, with thesign
set tofalse
The following conversions from this library's types can also be performed via the Into
trait:
Wad
->felt252
: Convert aWad
to afelt252
without modifying the valueWad
->u256
: Convert aWad
to au256
without modifying the valueWad
->SignedWad
: Convert aWad
to aSignedWad
without modifying the value, with thesign
set tofalse
Wad
->SignedRay
: Multiply theWad
by 109 and return aSignedRay
with thesign
set tofalse
Ray
->Wad
: Divide theRay
value by 109 and return aWad
Ray
->SignedRay
: Convert aRay
to aSignedRay
without modifying the value, with thesign
set tofalse
SignedWad
->felt252
: Convert aSignedWad
to afelt252
without modifying the valueSignedRay
->felt252
: Convert aSignedRay
to afelt252
without modifying the value
The following conversions can be performed via the TryInto
trait:
Wad
->Option::<Ray>
: Multiply theWad
value by 109 and returnOption::Some<Ray>
if there is no overflow orOption::None
otherwiseu256
->Option::<Wad>
: ReturnsOption::Some<Wad>
if the value is within bounds ofu128
orOption::None
otherwiseSignedWad
->Option::<Wad>
: ReturnsOption::Some<Wad>
ifsign
is false orOption::None
otherwiseSignedRay
->Option::<Ray>
: ReturnsOption::Some<Ray>
ifsign
is false orOption::None
otherwise
The following functions are available for SignedWad
and SignedRay
via the Signed
trait:
is_negative()
: Returns true if the value is less than zerois_positive()
: Returns true if the value is greater than zero
To use this library, add the repository as a dependency in your Scarb.toml
:
[dependencies]
wadray = { git = "https://github.com/lindy-labs/wadray.git" }
then add the following line in your .cairo
file
use wadray::Wad;
fn add_wad(a: Wad, b: Wad) -> Wad {
a + b
}
You can also refer to the test file src/test_wadray.cairo
for another example.
To run the tests:
scarb test
This library has been formally verified using our tool Aegis. Specifications and their correctness proof can be found in this repository, the verification currently refers to revision 30f7664 of this repo.
We welcome contributions of any kind! Please feel free to submit an issue or open a PR if you have a solution to an existing bug.
This library is released under the MIT License.