Step-by-Step Guide to Building a zkApp with O1js

Zero-knowledge applications (zkApps) enable privacy-preserving decentralized apps on Mina Protocol. This tutorial uses O1js (formerly SnarkyJS), a TypeScript library for building zk-SNARK circuits, to create a simple zkApp.

Prerequisites


This content originally appeared on DEV Community and was authored by uratmangun

Zero-knowledge applications (zkApps) enable privacy-preserving decentralized apps on Mina Protocol. This tutorial uses O1js (formerly SnarkyJS), a TypeScript library for building zk-SNARK circuits, to create a simple zkApp.

Prerequisites

  1. Basic knowledge of TypeScript/JavaScript.
  2. Node.js (v18+ recommended).
  3. Familiarity with Mina Protocol concepts (e.g., zk-SNARKs).
  4. Terminal/CLI proficiency.

1. Environment Setup

Install Dependencies

npm install -g zkapp-cli # Mina zkApp CLI tool
npm install -g typescript ts-node # TypeScript tools

2. Initialize Project

Create a new zkApp project using the Mina CLI:

zk create my-zkapp --template simple # Use the "simple" template
cd my-zkapp
npm install

Project Structure

my-zkapp/
├── src/
│   ├── contracts/    # zkApp smart contracts
│   ├── tests/        # Test files
│   └── index.ts      # Main entry (optional)
├── zkapp.config.json # Configuration
└── package.json

3. Write a zkApp Contract

Create src/contracts/NumberUpdate.ts:

import {
  SmartContract,
  State,
  state,
  method,
  PublicKey,
  PrivateKey,
} from 'o1js';

export class NumberUpdate extends SmartContract {
  @state(Field) number = State<Field>(); // On-chain state

  init() {
    super.init();
    this.number.set(Field(0)); // Initialize state
  }

  // Method to update the number with a constraint
  @method updateNumber(newNumber: Field) {
    const currentNumber = this.number.get();
    this.number.assertEquals(currentNumber); // Verify current state
    newNumber.assertLessThan(Field(100)); // Custom constraint: new number < 100
    this.number.set(newNumber); // Update state
  }
}

Key Concepts

  • @state: Declares on-chain state.
  • @method: Defines a zk-SNARK circuit (private computation).
  • Field: A primitive for finite field arithmetic.

4. Compile the Contract

Compile to generate proofs and AVM bytecode:

zk compile src/contracts/NumberUpdate.ts
  • Compilation may take 2-10 minutes (generates zk-SNARK keys).

5. Write Tests

Create src/tests/NumberUpdate.test.ts:

import { Test, expect } from 'zken';
import { NumberUpdate } from '../contracts/NumberUpdate';
import { Field, PrivateKey } from 'o1js';

describe('NumberUpdate', () => {
  let zkApp: NumberUpdate;
  let deployer: PrivateKey;

  beforeAll(async () => {
    deployer = PrivateKey.random(); // Test account
  });

  beforeEach(() => {
    zkApp = new NumberUpdate(deployer.toPublicKey());
  });

  it('updates number correctly', async () => {
    await zkApp.compile(); // Ensure contract is compiled

    // Deploy
    const tx = await Mina.transaction(deployer, () => {
      zkApp.deploy();
      zkApp.updateNumber(Field(42)); // Update to 42
    });
    await tx.prove(); // Generate proof
    await tx.sign([deployer]).send(); // Submit to testnet

    // Verify on-chain state
    expect(zkApp.number.get()).toEqual(Field(42));
  });
});

Run tests:

zk test

6. Deploy to Mina Network

Configure Network

Update zkapp.config.json:

{
  "networks": {
    "berkeley": {
      "url": "https://proxy.berkeley.minaexplorer.com/graphql",
      "keyPath": "./keys/berkeley.json"
    }
  }
}

Fund Account & Deploy

  1. Get testnet MINA from Mina Faucet.
  2. Deploy:
zk deploy:berkeley src/contracts/NumberUpdate.ts \
  --key-file ./keys/berkeley.json

7. Build a Frontend (React Example)

Install dependencies:

npm install react @mina_ui/core

Example component (src/index.tsx):

import { useState } from 'react';
import { NumberUpdate } from './contracts/NumberUpdate';
import { Mina, PublicKey } from 'o1js';

export default function App() {
  const [number, setNumber] = useState<number>(0);

  const updateNumber = async () => {
    const mina = Mina.connect('https://berkeley.minaexplorer.com');
    const contractAddress = PublicKey.fromBase58('YOUR_DEPLOYED_ADDRESS');
    const contract = new NumberUpdate(contractAddress);

    const tx = await Mina.transaction({ sender: contractAddress }, () => {
      contract.updateNumber(Field(number));
    });
    await tx.prove();
    await tx.send();
  };

  return (
    <div>
      <input type="number" onChange={(e) => setNumber(Number(e.target.value))} />
      <button onClick={updateNumber}>Update Privately</button>
    </div>
  );
}

8. Advanced Features

Add Privacy with @method.private

@method private validateSecret(secret: Field) {
  Poseidon.hash([secret]).assertEquals(this.account.hash);
}

Gas Optimization

  • Use @method({ gasBudget: 0.1 }) to limit gas costs.
  • Batch proofs using Mina.transactionBatch().

Best Practices

  1. Testing: Cover all circuit branches with unit tests.
  2. Security: Audit constraints to prevent invalid state transitions.
  3. Gas Costs: Optimize complex circuits with @method.runUnchecked.

Conclusion

You’ve built a zkApp that updates a number with privacy guarantees! Expand by:

  • Adding more complex business logic.
  • Integrating with off-chain oracles.
  • Exploring token standards (e.g., zkTokens).

Resources:


This content originally appeared on DEV Community and was authored by uratmangun


Print Share Comment Cite Upload Translate Updates
APA

uratmangun | Sciencx (2025-01-23T23:37:40+00:00) Step-by-Step Guide to Building a zkApp with O1js. Retrieved from https://www.scien.cx/2025/01/23/step-by-step-guide-to-building-a-zkapp-with-o1js/

MLA
" » Step-by-Step Guide to Building a zkApp with O1js." uratmangun | Sciencx - Thursday January 23, 2025, https://www.scien.cx/2025/01/23/step-by-step-guide-to-building-a-zkapp-with-o1js/
HARVARD
uratmangun | Sciencx Thursday January 23, 2025 » Step-by-Step Guide to Building a zkApp with O1js., viewed ,<https://www.scien.cx/2025/01/23/step-by-step-guide-to-building-a-zkapp-with-o1js/>
VANCOUVER
uratmangun | Sciencx - » Step-by-Step Guide to Building a zkApp with O1js. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/01/23/step-by-step-guide-to-building-a-zkapp-with-o1js/
CHICAGO
" » Step-by-Step Guide to Building a zkApp with O1js." uratmangun | Sciencx - Accessed . https://www.scien.cx/2025/01/23/step-by-step-guide-to-building-a-zkapp-with-o1js/
IEEE
" » Step-by-Step Guide to Building a zkApp with O1js." uratmangun | Sciencx [Online]. Available: https://www.scien.cx/2025/01/23/step-by-step-guide-to-building-a-zkapp-with-o1js/. [Accessed: ]
rf:citation
» Step-by-Step Guide to Building a zkApp with O1js | uratmangun | Sciencx | https://www.scien.cx/2025/01/23/step-by-step-guide-to-building-a-zkapp-with-o1js/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.