1 of 46

Snapps

2 of 46

Architecture

SnarkyJS

Hello World

Exercises

Wrap Up

(I)

(II)

(III)

(IV)

(V)

3 of 46

(I) Architecture

4 of 46

Mina Blockchain

22 KB

Fixed Size

Other Blockchains

350 GB

Increasing Size

5 of 46

  • Succinct blockchain
  • Recursive zk-SNARKs compress the blockchain into a single zk-SNARK.
  • Kimchi zk-SNARK is setupless, efficient, & extensible.�

Mina Blockchain

22 KB

Fixed Size

6 of 46

Ethereum dapps

Ethereum uses on-chain computation.

7 of 46

Mina snapps

Mina uses off-chain computation & on-chain verification.

8 of 46

Advantages

  • Blockchain scalability through succinctness.
  • User privacy through zero-knowledge.
  • Unlimited off-chain execution.

9 of 46

How does this work?

A “snapp” consists of two parts:

10 of 46

How does this work?

A SnarkyJS smart contract compiles down into 2 artifacts:

  1. Prover function: runs locally & generates a zero-knowledge proof.
  2. Verification key: stored on-chain to verify correctness of execution.

11 of 46

How does this work?

Deploying the verification key to the chain creates a “snapp account”.

12 of 46

How does this work?

Deploying the verification key to the chain creates a “snapp account”.

13 of 46

How does this work?

Deploying the verification key to the chain creates a “snapp account”.

14 of 46

Snapp methods

Each method of a snapp is actually compiled to a program which has

  • Input: The method’s arguments and on-chain values.
  • Output: A list of updates to perform and a list of preconditions related to the on-chain state.

15 of 46

(II) SnarkyJS

16 of 46

SnarkyJS

Snapps are written in TypeScript using SnarkyJS.

Uses existing open technologies.

  • Runs in the browser and NodeJS.
  • Can use existing JS/TS libraries and tools.
  • Excellent VSCode support.

17 of 46

Field elements

  • Basic unit of data in ZK programming.
  • Can store a number up to almost 256 bits in size. You can think of it as a uint256 in Solidity.
  • Field type in SnarkyJS.

18 of 46

Field elements

This can be simplified as:

In SnarkyJS, you would write this as:

In typical programming, you might use:

const sum = 1 + 3;

const sum = new Field(1).add(new Field(3));

const sum = new Field(1).add(3);

19 of 46

Other built-in types

SnarkyJS provides various useful types:

  • Field
  • UInt64
  • UInt32
  • PublicKey, PrivateKey, Signature
  • Optional
  • Bool
  • Group
  • Scalar

All have built-in methods on them.

20 of 46

Functions

Functions work as you would expect in TypeScript. For example:

function addOneAndDouble(x: Field): Field {

return x.add(1).mul(2);

}

function addOneAndDouble(x: Field): Field {

let xPlus1 = x.add(1);

return xPlus1.mul(2);

}

21 of 46

(III) Hello World

22 of 46

Getting set up

git clone https://github.com/o1-labs/snarkyjs-workshop.git

cd snarkyjs-workshop

npm install

  1. Install VSCode: code.visualstudio.com/download
  2. Install Node v16 (or later): nodejs.org/en/download/
  3. Clone the workshop files:

23 of 46

Hello world

We’ll write a smart contract with a single state variable named x.

It will have a method update that will let us replace x with its square.

x = 3

update(9)

x = 9

update(81)

x = 81

x = 3

update(9)

x = 9

update(75)

24 of 46

Hello world

To write a snapp, extend the SmartContract class of SnarkyJS.

class HelloWorld extends SmartContract {

...

}

25 of 46

Hello world

  • A snapp can contain some on-chain state
  • Here, x is of type Field. Only types built out of Field can be used for state variables

class HelloWorld extends SmartContract {

@state(Field) x: State<Field>;

...

}

class HelloWorld extends SmartContract {

@state(Field) x: State<Field>;

...

}

26 of 46

Hello world

The constructor of a smart contract describes how it will be initialized when deployed.

Default parameters:

  1. Initial balance of snapp account
  2. Account address

class HelloWorld extends SmartContract {

@state(Field) x: State<Field>;

constructor(

initialBalance: UInt64,

address: PublicKey,

x: Field

) {

super(address);

this.balance.addInPlace(initialBalance);

this.x = State.init(x);

}

...

}

27 of 46

Hello world

A smart contract can contain multiple methods, each with its own logic.

The @methods of a smart contract describe how it can be invoked when deployed.

class HelloWorld extends SmartContract {

@state(Field) x: State<Field>;

...

@method async update(squared: Field) {

const x = await this.x.get();

x.square().assertEquals(squared);

this.x.set(squared);

}

}

28 of 46

Hello world

The state of a snapp is public, unless explicitly stored as a commitment.

Any argument of a method is private, unless, for example, a method stores an argument directly in the state.

class HelloWorld extends SmartContract {

@state(Field) x: State<Field>;

...

@method async update(squared: Field) {

const x = await this.x.get();

x.square().assertEquals(squared);

this.x.set(squared);

}

}

29 of 46

Hello world

The update method is async because it calls .get()

We run off chain, and so fetch the current state of the snapp account from the chain.

class HelloWorld extends SmartContract {

@state(Field) x: State<Field>;

...

@method async update(squared: Field) {

const x = await this.x.get();

x.square().assertEquals(squared);

this.x.set(squared);

}

}

30 of 46

Under the hood

When we run update(9), we prove

“We ran the code in the update method with some arguments, and the result was

Updates:

set x to be 9

Assuming preconditions:

x = 3

class HelloWorld extends SmartContract {

@state(Field) x: State<Field>;

...

@method async update(squared: Field) {

const x = await this.x.get();

x.square().assertEquals(squared);

this.x.set(squared);

}

}

x = 3

update(9)

x = 9

31 of 46

Summary: Anatomy of a smart contract

  1. State variables (public)
  2. Constructor
  3. Methods (arguments are private by default)
    • Updates and preconditions

class HelloWorld extends SmartContract {

@state(Field) x: State<Field>;

constructor(

initialBalance: UInt64,

address: PublicKey,

x: Field

) {

...

}

@method async update(squared: Field) {

...

}

}

32 of 46

Running hello world

npx tsc && node dist/hello_world.js

33 of 46

(IV) Exercises

34 of 46

API exploration

You can explore the methods available using the API docs here:

docs.minaprotocol.com/en/snapps/snarkyjs-reference

Or by using autocomplete.

35 of 46

Exercises

For these exercises, we’ll use a mock Mina environment that runs locally.

36 of 46

Exercise 1

  • Open 1_exercise.ts
  • Implement update so that you have set x to its cube rather than its square
  • Run with npx tsc && node dist/1_exercise.js

x = 3

update(27)

x = 27

update(19683)

x = 19683

x = 3

update(27)

x = 27

update(75)

37 of 46

Exercise 2: Hashing

  • Open 2_exercise.ts
  • Use Poseidon.hash so that x is set to its image under the poseidon hash function when the update method is called.

38 of 46

Example: Payments

  • Open 3_exercise.ts
  • We’ll go through this together.

39 of 46

Payments review

  • Use:
    • .balance.addInPlace
    • .balance.subInPlace
  • All balance changes in a transaction must sum to 0.

40 of 46

Exercise 4: Functions and loops

  • In SnarkyJS, we can define normal TypeScript functions.
  • We can also use for-loops:
  • Open 4_exercise.ts
  • Implement hashNTimes so that value is set to its image under the poseidon hash function applied n times when the update method is called.

for (let i = 0; i < 10; ++i) {

...

}

41 of 46

Exercise 5: Logic

  • Bool class
  • Methods
  • Cannot use Bool in an if statement
  • Instead use “ternary”

and(y: Bool | boolean): Bool

or(y: Bool | boolean): Bool

not(): Bool

Circuit.if<T>(b: Bool | boolean, x: T, y: T): T

42 of 46

Exercise 5: Logic

  • Let’s define a snapp account that you can send from if you provide a signature from any of a list of public keys
  • 1 out of N multisig

43 of 46

Exercise 5 review: user-defined types

  • If you want to use a type as a method-argument or state variable, you must
    • Extend CircuitValue class
    • Use @prop decorator on properties

class SignatureWithSigner extends CircuitValue {

@prop signature: Signature;

@prop signer: PublicKey;

constructor(signature: Signature, signer: PublicKey) {

super();

this.signature = signature;

this.signer = signer;

}

}

44 of 46

Bonus: Recursion

  • SnarkyJS makes recursion a snap (😩)
  • Simple ZK-rollup in about 150 lines.

45 of 46

What we didn’t cover

  • Interacting with the Mina state (e.g. current block height)
  • The whole standard library API (e.g. group operations, Merkle trees).

46 of 46

minaprotocol.com/snarkyjs

github.com/o1-labs/snarkyjs

@MinaProtocol

https://discord.com/invite/Vexf4ED

github.com/o1-labs/snapp-cli

@o1labs