zkApp programmability is not yet available on the Mina Mainnet, but zkApps can now be deployed on Berkeley Testnet.
o1js Basic Concepts
o1js is a TypeScript (TS) library for writing general-purpose zk programs and writing zk smart contracts for Mina.
Field
Field elements are the basic unit of data in zero knowledge proof programming. Each field element can store a number up to almost 256 bits in size. You can think of a field element as a uint256 in Solidity.
For the cryptography inclined, the exact max value that a field can store is: 28,948,022,309,329,048,855,892,746,252,171,976,963,363,056,481,941,560,715,954,676,764,349,967,630,336
For example, in typical programming, you might use:
const sum = 1 + 3
.
In o1js, you write this as:
const sum = new Field(1).add(new Field(3))
This can be simplified as:
const sum = new Field(1).add(3)
Note that the 3 is auto-promoted to a field type to make this cleaner.
Built-in data types
Some common data types you may use are:
new Bool(x); // accepts true or false
new Field(x); // accepts an integer, or a numeric string if you want to represent a number greater than JavaScript can represent but within the max value that a field can store.
new UInt64(x); // accepts a Field - useful for constraining numbers to 64 bits
new UInt32(x); // accepts a Field - useful for constraining numbers to 32 bits
PrivateKey, PublicKey, Signature; // useful for accounts and signing
new Group(x, y); // a point on our elliptic curve, accepts two Fields/numbers/strings
Scalar; // the corresponding scalar field (different than Field)
CircuitString.from('some string'); // string of max length 128
In the case of Field
and Bool
, you can also call the constructor without new
:
let x = Field(10);
let b = Bool(true);
Conditionals
Traditional conditional statements are not supported by o1js:
// this will NOT work
if (foo) {
x.assertEquals(y);
}
Instead, use the o1js built-in Circuit.if()
method, which is a ternary operator:
const x = Circuit.if(new Bool(foo), a, b); // behaves like `foo ? a : b`
Functions
Functions work as you would expect in TypeScript. For example:
function addOneAndDouble(x: Field): Field {
return x.add(1).mul(2);
}
Common methods
Some frequently used common methods are:
let x = new Field(4); // x = 4
x = x.add(3); // x = 7
x = x.sub(1); // x = 6
x = x.mul(3); // x = 18
x = x.div(2); // x = 9
x = x.square(); // x = 81
x = x.sqrt(); // x = -9
let b = x.equals(8); // b = Bool(false)
b = x.greaterThan(8); // b = Bool(true)
b = b.not().or(b).and(b); // b = Bool(true)
b.toBoolean(); // true
let hash = Poseidon.hash([x]); // takes array of Fields, returns Field
let privKey = PrivateKey.random(); // create a private key
let pubKey = PublicKey.fromPrivateKey(privKey); // derive public key
let msg = [hash];
let sig = Signature.create(privKey, msg); // sign a message
sig.verify(pubKey, msg); // Bool(true)
For a full list, see the o1js reference.