Math
Generally, all types that are usable in o1js are also usable out-of-the-box in protokit.
But there are some caveats to dealing with bounded integers (UInt64
, UInt32
).
Integers
By default, o1js exposes two classes UInt64
and UInt32
to protect you from overflows that can happen
if you use Field
directly.
Unfortunately, these don't work well with Protokit since they use the hard-failing o1js assertions instead of the protokit-specific assert
.
This leads to your application being non-complete and being unable to include failed transactions in settlements.
To overcome this, we created a number of classes in @proto-kit/library
that handle all of that for you while providing the same interface as the vanilla variants.
UInt32
UInt64
UInt112
UInt224
In addition to the methods known from o1js, we implement the sqrtMod()
and sqrtFloor()
functions to help facilitate square-root calculations.
Make sure to import from @proto-kit/library
when using UInt32
and UInt64
Creating protokit UInts
There are multiple ways of creating UInts, each of them with their own properties.
This example uses UInt64
, but can be used with any other UInt the same way.
The most straight forward one is using UInt64.from()
. This accepts UInt64 | string | bigint | number
.
It should be used for creation of a constant or for copying a existing UInt.
// Using string
const uint1 = UInt64.from("5");
// Using number
const uint2 = UInt64.from(5);
// Using another UInt64
const uint3 = UInt64.from(uint1);
To convert a Field into a UInt, you can use both the Safe
and Unsafe
fromField()
function.
// Creates a UInt64 and performs necessary range checks on the given `field`
UInt64.Safe.fromField(field);
// Creates a UInt64 without performing range checks.
// This safes constraints, but relies on the given field to be ensured to be in range
UInt64.Unsafe.fromField(Field(1));
Because range checks (which are used under the hood to enable the safety of UInts) are very expensive, these different methods of creating and transforming UInts are a way for developers to optimize and decrease proving effort.
Transforming UInts
Sometimes we need to go from a specific UInt bit-range to a different one. Let's say we want to go from UInt32 to UInt64 and back.
The upward direction is trivial and will not generate additional constraints
const uint64 = UInt64.Unsafe.fromField(uint32.value);
Going down to UInt32 again, we need to range-check the 64-bit number to ensure it is within 32-bits.
Therefore, we need to use the Safe.fromField
method.
const uint32 = UInt32.Safe.fromField(uint64.value);
For some UInts, simpler conversion functions exist.
UInt32.toUInt64()
UInt112.toUInt224()
Converting to o1js UInt64
Every UInt from the @proto-kit/library
package also has a conversion function to turn it into a o1js
UInt64
// Will fail the transaction if the value of uint exceeds 64 bits
const o1jsUint = uint.toO1UInt64()
// If the value of `uint` exceeds 64 bits, it will take the maximum value fitting into 64 bits
const o1jsUint2 = uint.toO1UInt64Clamped()