Building zkApps (zero-knowledge
applications) on the Mina Protocol using 01js is an exciting journey, especially for developers transitioning from Web2 or Web3 backgrounds. This guide will walk you through the process of creating a minimal currency contract, similar to Solidity's smart contracts, but adapted for Mina and zero-knowledge proofs (ZKPs).
What is 01js?
01js is a TypeScript library designed to simplify zkApp development on the Mina Protocol. It provides tools and abstractions to work with zero-knowledge proofs, enabling developers to build privacy-preserving and scalable decentralized applications.
What is Mina Protocol?
Mina is a lightweight blockchain that uses ZKPs to maintain a constant-sized blockchain (only 22 KB). This makes it highly efficient and accessible for developers and users alike. zkApps on Mina leverage ZKPs to enable private and verifiable computations.
Install zkApp CLI
To set up the necessary tooling, install the zkApp CLI globally:
npm install -g zkapp-cli
Initiate a New Project
Once you've completed the setup, it's time to begin building your application.
Set up a New Directory:
Go to or create a directory where you have the necessary write permissions.
Start a New Project: Use the zk project command to generate the structure of your application:
zk project o1js-currency
Select project options:
When asked, choose "none" for the UI option:
? Create an accompanying UI project too? …
next
svelte
nuxt
empty
> none
Verify successful project creation: After running the command, you should see this output confirming the successful creation of your project:
✔ Create an accompanying UI project too? · none
✔ UI: Set up project
✔ Initialize Git repo
✔ Set up project
✔ NPM install
✔ NPM build contract
✔ Set project name
✔ Git init commit
Success!
Next steps:
cd 01-hello-world
git remote add origin <your-repo-url>
git push -u origin main
After running the zk project command, a new directory named o1js-currency
will be created, containing all the necessary scaffolding for your project. This includes essential tools like Prettier (for code formatting), ESLint (for static code analysis), and Jest (for testing).
To verify, navigate to the project directory and inspect its contents:
cd o1js-currency
ls
These are the following structures:
LICENSE
README.md
babel.config.cjs
build
config.json
jest-resolver.cjs
jest.config.js
keys
node_modules
package-lock.json
package.json
src
tsconfig.json
You’ll execute commands from the root directory of the o1js-currency
folder and write your smart contract code inside the src
directory. During the build or deployment process, updates will be compiled into JavaScript in the build
directory.
Preparing the Project:
First, clean up the default files that come with the project scaffold.
Remove Unnecessary Files:
To remove the default files, run the following commands:
rm src/Add.ts
rm src/Add.test.ts
rm src/interact.ts
Create New Files:
The next step is to set up the necessary files for your project. Create the following files:
zk file src/currency
touch src/main.ts
The zk file
command will create two files: src/currency.ts
and src/currency.test.ts
.
Modify src/index.ts
:
Open the src/index.ts
file in your preferred text editor and replace its existing content with:
import { currency } from './currency';
export { currency };
The src/index.ts
file acts as the main entry point for exporting the key components of your project.
Develop the zkApp Smart Contract
This is where the fun begins – creating your smart contract!
Implement the Contract:
In the src/currency.ts
file, you’ll define the logic for your zkApp currency. The example file provides line numbers and detailed code walkthroughs to assist you in the implementation.
Smart Contract Class Overview
The currency smart contract extends the TokenContract
class, enhancing it with state management features and additional functionality for a custom cryptocurrency. Below is a thorough breakdown of the implementation and its key components.
Contract framework
export class currency extends TokenContract {
@state(Field) name = State<Field>();
@state(Field) symbol = State<Field>();
@state(Field) totalSupply = State<Field>();
@state(Field) decimals = State<Field>();
@state(PublicKey) owner = State<PublicKey>();
@state(Field) circulatoryAmount = State<Field>();
}
The smart contract is based on TokenContract
class, allowing it to inherit essential features of token management. The contract defines key state variables for managing name, symbol, decimal, totalsupply, owners, and circulatoryAmount ensuring proper tracking of the token’s state on the blockchain. It includes various functions for transferring tokens, updating token metadata (symbol, name), and adjusting supply levels.
Activation method
deploy(): Promise<void> {
super.deploy();
const permissionToEdit = Permissions.proof();
this.account.permissions.set({
...Permissions.default(),
editState: permissionToEdit,
setTokenSymbol: permissionToEdit,
send: permissionToEdit,
receive: permissionToEdit
});
return Promise.resolve();
}
The initialization method of a smart contract sets up its permissions upon deployment. It uses Permissions.proof()
to provide proof-based control, managing actions like state edits, token symbol updates, and the sending/receiving of tokens. This approach combines default permissions with specific allowances for sensitive operations, ensuring secure and flexible contract functionality.
Configuration
init() {
super.init();
this.name.set(stringToField("Pengu Mina"));
this.symbol.set(stringToField("P.M"));
this.account.tokenSymbol.set("P.M");
this.owner.set(users.owner.toPublicKey());
this.account.zkappUri.set("https://magenta-hollow-tiglon-795.mypinata.cloud/ipfs/bafybeig2mliemcvvx5yvdzo7ajsd34ys3i5lsauhbu7nrhqfmidkaqvrpi");
this.totalSupply.set(contract_int_vars.totalSupply);
this.circulatoryAmount.set(Field(0));
}
The initialization method configures the smart contract's state values during deployment. It sets the token's name and symbol using the stringToField
utility function, assigns the owner with the provided public key, and defines a metadata URI (zkappUri)
for external references like token details on IPFS. Additionally, it initializes the totalSupply
and sets the circulatoryAmount
to zero.
Contract Metadata
It Defines metadata for the token, such as its name, symbol, and URI. This information is typically stored on decentralized file storage systems like IPFS(InterPlanetary File System) to ensure that it remains accessible and immutable. The metadata may include additional fields like token description, logo, and links to the token’s whitepaper or website.
import { SmartContract, State, method, Field, PublicKey, Permissions } from 'o1js';
class CurrencyContract extends SmartContract {
@State(Field) totalSupply = State<Field>();
@State(Field) circulatingSupply = State<Field>();
// Permission to make updates to the contract state
init() {
super.init();
this.totalSupply.set(Field(1000000)); // 1 million tokens
this.circulatingSupply.set(Field(0)); // No circulating tokens yet
}
// Method to update the circulating supply
@method updateCirculatingSupply(amount: Field) {
const newSupply = this.circulatingSupply.get().add(amount);
this.circulatingSupply.set(newSupply);
}
}
The contract metadata serves to provide external users with essential information about the token and contract, ensuring transparency. The metadata details stored on decentralized systems, such as IPFS, include:
- Token Name and Symbol for easy identification.
- Metadata URI which points to the token’s external details, such as those hosted on IPFS.
- Permissions specifying who can perform certain operations (e.g., modify circulating supply).
Conclusion
O1js streamlines the development of privacy-preserving zkApps on the Mina Protocol, allowing developers to build efficient applications with ease. Its integration of TypeScript and zk-SNARKs empowers the creation of scalable, secure blockchain solutions.
Top comments (0)