Skip to main content
info

zkApp programmability is not yet available on the Mina Mainnet. You can get started now by deploying zkApps to the Berkeley Testnet.

o1js Built-in Data Types

o1js includes built in types and methods that you can use inside your zkApp and ZKPs (zero knowledge proofs). All o1js provable types are built using the Field type, each with unique properties and advantages. You can find detailed class description in o1js reference.

Bool

Bool is the type used for boolean values. It is equivalent to the boolean type in order languages and frameworks.

let x = new Bool(true);
let y = new Bool(false);

With the Bool type, you can call the contructor without new, just like the Field type. The convention is to use the constructor function without new:

let x = Bool(true);
let y = Bool(false);

You can also create a Bool from another Bool.

let x = Bool(true);
let y = Bool(x);

It is also possible to convert a Field into a Bool. However, you should make sure that the Field is "bool-like" (with a value either 1 or 0) before converting it.

let x = Field(1);

// The line below makes sure x can be safely converted into a `Bool`.
x.equals(Field(1)).or(x.equals(Field(0))).assertTrue(); // You will learn about the methods used here below.

let x_bool = Bool(x.value);

Bool type includes the following conditional methods and converters. Every method created and returns a new Bool. All conditional methods can be called with regular boolean values as well as Bool variables:

let x = Bool(true);
let y = Bool(false);
let z = y.not(); // True

let equals = x.equals(y); // False
let and = x.and(y); // False
let or = x.or(y); // True

let equals_from_boolean = x.equals(true); // True
let and_from_boolean = x.and(true); // True
let or_from_boolean = y.or(false); // False

You can also assert Bool variables. When you assert something, your zkApp method throws if the assertion fails. You can read more about assertions in the basic concepts chapter.

let x = Bool(true);
let y = Bool(false);

x.assertEquals(y); // Throws
x.assertTrue(); // Nothing happens, as assertion passes
x.assertFalse(); // Throws

You can also combine multiple logical expressions one after another.

let x = Bool(true);
let y = Bool(false);

x.not().and(y).or(true); // True

Important: All logical expressions are executed in the method call order, as with any other regular language or framework. However, as these are logical statements inside ZKPs, all expressions are always evaluated regardless of the logical ordering. See Conditionals for more details.

UInt32

UInt32 type is a 32 bit positive integer. You can create it using the new constructor or the UInt32.from() static method. The convention is to use UInt32.from().

Both the constructor and UInt32.from() method accepts number, string, bigint, Field, and UInt32 type. In order the conversion to be successful, the type should evaluate to "uint32-like", meaning an integer in the range [0, 23212^{32} - 1].

let x = new UInt32(45); // From number, use from() rather than the new constructor
let y = UInt32.from("788"); // From string
let z = UInt32.from(812934812n); // From bigint
let v = UInt32.from(Field(34)); // From Field
let w = UInt32.from(v); // From UInt32

let k = UInt32.from("asdfas"); // Throws, as this is not a number
let l = UInt32.from(9182394814234n) // Throws, as this bigint is bigger than 2^32 - 1

You can perform arithmetic operations on UInt32 variables using built in methods. These methods can also be called with "uint32-like" values. Each method creates and returns a new UInt32.

If the result of a method is not in the range of UIn32, either a too big or a negative number, the code throws an error.

let x = UInt32.from(45);
let y = UInt32.from(94);

let add = x.add(y); // Regular addition
let sub = x.sub(y); // If the result is out of range of the UInt32, the code throws
let mul = x.mul(47); // You can also use "uint32-like" values
let div = x.div(33); // This is an integer division, different from the Field type
let divMod = x.divMod(33); // Returns an object with the keys `quotient` and `rest`, both of the UInt32 type.

You can also perform logical operations on UInt32 types. All logical methods return a Bool variable.

let x = UInt32.from(45);

x.equals(45); // True
x.greaterThan(45); // False
x.greaterThanOrEquals(45); // True
x.lessThan(45); // False
x.lessThanOrEquals(45); // True

Similarly to the Bool type, you can assert logical statements.

let x = UInt32.from(45);

x.assertEquals(45); // True - passes
x.assertGreaterThan(45); // False - throws
x.assertGreaterThanOrEquals(45); // True - passes
x.assertLessThan(45); // False - throws
x.assertLessThanOrEquals(45); // True - passes

Finally, you can convert a UInt32 to a Field by calling the .value property.

let x = UInt32.from(45);
let y = x.value; // This is a Field with the value 45

UInt64

UInt64 type is the 64 bit version of UInt32 type. There is no difference between the two, except their range.

// Constructors

let x = new UInt64(45); // From number, use from() rather than the new constructor
let y = UInt64.from("788"); // From string
let z = UInt64.from(812934812n); // From bigint
let v = UInt64.from(Field(34)); // From Field
let w = UInt64.from(v); // From UInt64

let k = UInt64.from("asdfas"); // Throws, as this is not a number
let l = UInt64.from(91823948182394892384923849348923849238494234n) // Throws, as this bigint is bigger than 2^64

// Arithmetic Operations

let add = x.add(y); // Regular addition
let sub = x.sub(y); // If the result is out of range of the UInt64, the code throws
let mul = x.mul(47); // You can also use "uint64-like" values
let div = x.div(33); // This is an integer division, different from the Field type
let divMod = x.divMod(33); // Returns an object with the keys `quotient` and `rest`, both of the UInt64 type.

// Logical Operators

x.equals(45); // True
x.greaterThan(45); // False
x.greaterThanOrEquals(45); // True
x.lessThan(45); // False
x.lessThanOrEquals(45); // True

// Assertions

x.assertEquals(45); // True - passes
x.assertGreaterThan(45); // False - throws
x.assertGreaterThanOrEquals(45); // True - passes
x.assertLessThan(45); // False - throws
x.assertLessThanOrEquals(45); // True - passes

// Converting to Field

let y = x.value; // This is a Field with the value 45

Public and Private Keys

For accessing Mina blockchain accounts, you can use PublicKey and PrivateKey types of o1js. You can create a key from a base58 string.

const x_private = PrivateKey.fromBase58("EKEAzkozjN3TAQHLzBKD7RoscLojWeFMKLY2qgKEV8qqpxJYwpKb");
const y_private = PrivateKey.fromJSON("EKEAzkozjN3TAQHLzBKD7RoscLojWeFMKLY2qgKEV8qqpxJYwpKb");

const x = PublicKey.fromBase58("B62qp8KyqqSyKs8eQqtmzoRjuEG3Th9bD4ABuvMEGBCnkMKh3pWvGaQ");
const y = PublicKey.fromJSON("B62qp8KyqqSyKs8eQqtmzoRjuEG3Th9bD4ABuvMEGBCnkMKh3pWvGaQ");

You can also create a random PrivateKey or convert a PrivateKey to a PublicKey.

const random = PrivateKey.random();
const fromPrivKey = PublicKey.fromPrivateKey(random); // or equivalently random.toPublicKey()

Conditionals

Traditional conditional statements are not supported by o1js. This is due to the fact that o1js creates ZKPs, and conditional evaluation does not make sense in a ZKP. ZKPs are evaluated into mathematical statements when compiled by o1js, thus you cannot chose to evaluate a statement based on a truth value unknown at the compile time.

// This will NOT work
if (foo) {
x.assertEquals(y);
}

Nevertheless, you can use the o1js built-in Provable.if() method, which is a ternary operator:

let x = Provable.if(
Bool(foo), // The condition to evaluate inside if, must be of type Bool
a, // You can return any provable type from Provable.if
b // a and b must be of the same provable type
); // Behaves like `foo ? a : b`

Provable.if() allows you to include any logical computation you need inside your ZKP. There is nothing wrong with evaluating more complex expressions inside Provable.if() and returning the final result.

However, it works differently from ternary statements in the other languages and frameworks. Both arguments of Provable.if() always execute, regardless of the truth value of the condition. Provable.if() chooses which one to return based on the truth value and sets the new variable. This is why you should never assign anything outside of the if scope or assert anything.

let x = Provable.if(
Bool(foo),
// Note both of the functions below will be executed regardless of the value of `Bool(foo)`
(() => { // Here, we use arrow functions to return a Field element, but you can use any function that returns a provable type
return Field(5).div(3);
})(),
(() => {
return Field(5).div(2);
})()
); // This variable is of type Field

Loops

Loops also work differently in o1js because of the same reason with conditionals. In a mathematical statement, you cannot chose how many times to do something based on a variable that is not known at the compile time. Similarly, o1js does not allow you to use dynamically sized loops, loops that the iteration count is not known at the compilation.

// This will NOT work
while (foo) {
x.add(y);
}

You can overcome this limitation by using static sized loops that iterate always the same amount of time. You can perform provable operations with these loops using Provable.if().

Note that the example below is very inefficient due to the expensive Field comparison operations. This example is just to show how static sized loops may be used with Provable.if(). A much more efficient way of achieving the same would be using o1js recursion.

function powerOfTwo(pow: UInt64): Field {
const MAX_ITERATION_COUNT = 64; // pow is 64 bits

let result = Field(1); // Result of the calculation
let power = UInt64.from(0); // Current power of the iteration
let loopEnded = Bool(false); // Checking if the loop has ended yet

for (let i = 0; i <= MAX_ITERATION_COUNT; i++) { // Static sized loop
loopEnded = Provable.if( // End Condition
UInt64.from(i).equals(power), // In o1js, equality checks are much more efficient than comparisons
Bool(true),
loopEnded
);

result = result.mul(Provable.if( // Result
loopEnded,
Field(1),
Field(2)
));

power = power.add(Provable.if( // Power
loopEnded,
UInt64.from(0),
UInt64.from(1)
));
};

return result;
};

Functions

Functions work as you would expect in TS. However, your functions inside ZKPs must return provable types, just like anything in o1js. For example:

function addOneAndDouble(x: Field): Field {
return x.add(1).mul(2);
}

Warning: You should not use dynamically sized recursion with functions in o1js, just like dynamically sized loops. Static sized recursion is safe to use as the end condition is known at the compilation time. If you want to dynamically recurse a ZKP, you can use o1js recursion.

info

Note that this is not a full list of built in types and methods that exist in o1js. For a full list, see the o1js reference.