Mina Protocol: Create a Simple zkApp with Mina Protocol and IPFS

Learn how to create a simple zkApp with Mina Protocol and IPFS.

What is Mina Protocol?

Mina Protocol is a lightweight, compact blockchain protocol that uses a recursive composition of zk-SNARKs (Zero-Knowledge Succinct Non-Interactive Arguments of Knowledge) to enable participants to validate the entire chain's state in a single, small proof. This allows for extremely fast and efficient verification, which makes Mina Protocol highly scalable.

What is a zkApp?

"zkApp" stands for Zero-Knowledge Application, which is a type of decentralized application that uses zero-knowledge proofs to enhance privacy and security.

Zero-knowledge proofs are a cryptographic technique that allows one party to prove to another party that a certain statement is true, without revealing any additional information beyond the fact that the statement is indeed true. This means that a zkApp can allow users to perform certain actions or transactions without revealing their underlying data or identity to the network.

Read below to learn how to create a simple zkApp with Mina Protocol and IPFS.

Prerequisites:

1. First, download and install S3FS-FUSE on your local environment.

This tool is available on a Linux or macOS system.

2. Set up an Access Key file for use with S3FS-FUSE.

Set up a credentials file for S3FS at ${HOME}/.passwd-s3fs. You will need to save your Filebase Access and Secret keys to this file and give it owner permissions. You can do so with the following commands:

echo ACCESS_KEY_ID:SECRET_ACCESS_KEY > ${HOME}/.passwd-s3fs

chmod 600 ${HOME}/.passwd-s3fs

ACCESS_KEY_ID is your Filebase Access key, and SECRET_ACCESS_KEY is your Filebase Secret key. For more information on Filebase access keys, see here.

3. Mount your bucket.

You can mount a Filebase IPFS bucket with the command:

s3fs mybucket /path/to/mountpoint -o passwd_file=${HOME}/.passwd-s3fs -o url=https://s3.filebase.com

  • mybucket: name of your Filebase bucket

  • /path/to/mountpoint

4. Now, navigate into the mounted Filebase bucket.

Create a new folder for your project:

mkdir minaProtocolZKApp

5. Next, install the Mina zkApp CLI tool:

npm install -g zkapp-cli

6. Create a new project with the command:

zk project zkapp

7. Then, remove a few of the default files that come with a new project with the command:

rm src/Add.ts

rm src/Add.test.ts

rm src/interact.ts

8. Then, generate some new files that we’ll be using:

zk file src/Square

touch src/main.ts

9. Open the src/index.ts file, then insert the following content:

import { Square } from './Square.js';

export { Square };

10. Next, compile and build your project with the commands:

npm run build

node build/src/main.js

11. Now it’s time to write the smart contract for the zkApp. Open the src/Square.js file and insert the following smart contract code:

import {
  Field,
  SmartContract,
  state,
  State,
  method,
} from 'snarkyjs';

export class Square extends SmartContract {
  @state(Field) num = State<Field>();
  init() {
    super.init();
    this.num.set(Field(3));
  }
}

    this.num.set(Field(3));
  }
  @method update(square: Field) {
    const currentState = this.num.get();
    this.num.assertEquals(currentState);
    square.assertEquals(currentState.mul(currentState));
    this.num.set(square);
  }
}

This is a simple smart contract that retrieves the current state of the network, which is then used to update the zkApp’s current state on-chain.

12. Then open the src/main.ts file and insert the following code:

import { Square } from './Square.js';
import {
  isReady,
  shutdown,
  Field,
  Mina,
  PrivateKey,
  AccountUpdate,
} from 'snarkyjs';

await isReady;
console.log('SnarkyJS loaded')
const useProof = false;
const Local = Mina.LocalBlockchain({ proofsEnabled: useProof });
Mina.setActiveInstance(Local);

const { privateKey: deployerKey, publicKey: deployerAccount } = Local.testAccounts[0];
const { privateKey: senderKey, publicKey: senderAccount } = Local.testAccounts[1];

const zkAppPrivateKey = PrivateKey.random();
const zkAppAddress = zkAppPrivateKey.toPublicKey();

const zkAppInstance = new Square(zkAppAddress);
const deployTxn = await Mina.transaction(deployerAccount, () => {
  AccountUpdate.fundNewAccount(deployerAccount);
  zkAppInstance.deploy();
});
await deployTxn.sign([deployerKey, zkAppPrivateKey]).send();

const num0 = zkAppInstance.num.get();
console.log('state after init:', num0.toString());

console.log('Shutting down')
await shutdown();

13. Now if we compile and build our project again, we should get the following output:

npm run build && node build/src/main.js

...

SnarkyJS loaded

state after init: 3

Shutting down

14. Next, we’ll add a transaction to our zkApp. In the src/main.js code, add the following portion:

console.log('state after init:', num0.toString());
// ----------------------------------------------------
const txn1 = await Mina.transaction(senderAccount, () => {
  zkAppInstance.update(Field(9));
});
await txn1.prove();
await txn1.sign([senderKey]).send();
const num1 = zkAppInstance.num.get();
console.log('state after txn1:', num1.toString());
// ----------------------------------------------------
console.log('Shutting down')
await shutdown();

15. Then, compile and build the project again. This time, you should get the following output:

npm run build && node build/src/main.js

...

SnarkyJS loaded

state after init: 3

state after txn1: 9

Shutting down

You’ve just created your first zkApp!

Last updated