Used this playlist and official docs.
- Install Node js and verify your version with
node --version // Example output: v18.18.0
- Verify npm is installed
npm --version // Example output: 10.4.0
- Install zkApp Cli
npm install -g zkapp-cli
- Verify zkApp Cli is installed
zk --version // Example output: 0.17.2
- Check the whole environment configurations and versions
zk system
- Move to a desired directory and create a new project. It automatically will install also 01js
zk project myFirstProject
- Select
none
as the project template when asked and open it in your IDE - Install dependencies in project folder
npm install
- A dummy contract named
Add.ts
will be generated in the/src
folder. This contract allows to set1
as the value for the state variablenum
and update it adding2
when calling theupdate()
.
import { Field, SmartContract, state, State, method } from 'o1js';
/**
* The Add contract initializes the state variable 'num' to be a Field(1) value by default when deployed.
* When the 'update' method is called, the Add contract adds Field(2) to its 'num' contract state.
*/
export class Add extends SmartContract {
@state(Field) num = State<Field>();
init() {
super.init();
this.num.set(Field(1));
}
@method async update() {
const currentState = this.num.getAndRequireEquals();
const newState = currentState.add(2);
this.num.set(newState);
}
}
- Another file named
interact.ts
will be generated in the/src
folder. This file takes care of interacting withAdd.ts
and is where ZKP is actually generated usingtx.prove()
method. - Build the smart contract
npm run build
- Create a local test network and give it a name when asked (e.g.
testnet
)
zk config
- Set the network type between testnet/mainnet when asked
- Set an API URL to interact with it (e.g
https://api.minascan.io/node/devnet/v1/graphql
but this may change in the future) - Set tx fee (e.g
0.1
) - Select the account that should pay for fees. You can also create one directly when asked, giving it an alias (e.g.
testpayer
). A private key and public key will be generated and stored in thetestpayer.json
file stored in the xkapp-cli folder somewhere in your computer. - Done! A link will be prompted in the console with a link to a faucet to get some tMINA tokens for testing to the newly created account. Request them and wait for the tx to be processed.
- Deploy the contract and select the newly created network when asked
zk deploy myFirstProject
- The console will log all the informations and addresses related to the deployment together with a link to the testnet explorer of the deploy tx
- Open that link and wait till the tx will be processed (example tx). In it you can find:
- the appState of the
Add.ts
contract just deployed with 1 in appState[0] - the
verificationKeyHash
(if you can't find it enableRaw JSON
toggle on the top-right corner of the page and search it in the page). This hash is used to identify if an account on Mina is a zkApp. While deploying this contract we actually created a Verifier contract which live on-chain and must have a verification key. Important: the contract code is not stored on chain, the zkApp can only verify the proof you send it through the verification Key.
- the appState of the
Within the file interact.ts
there is the code for interacting with the on-chain state. Interaction take place between the Prover (the feepayer
) and the Verifier (the zkApp deployed).
Let's explore the file interact.ts
(some lines are omitted here):
// reads privateKey and publickKey fields from testnet.json file
let zkAppKeysBase58: { privateKey: string; publicKey: string } = JSON.parse(
await fs.readFile(config.keyPath, 'utf8')
);
// creates a PrivateKey object converting from Base58 representation
let zkAppKey = PrivateKey.fromBase58(zkAppKeysBase58.privateKey)
// retrieves the corresponding public key of the zkApp
let zkAppAddress = zkAppKey.toPublicKey();
// creates a new instance of the Add contract and compile it
let zkApp = new Add(zkAppAddress);
await Add.compile();
// creates a tx object executing the Add.update() method locally and waits for its result
// then the tx object containing the result of the computation is used to generate a ZKP
// lastly the feepayer signs the tx and sends the proof on chain
console.log('build transaction and create proof...');
let tx = await Mina.transaction(
// feepayerAddress should be specified and its data are read from the file which path is stored in config.json . In this example are read from testpayer.json we created during deployment
{ sender: feepayerAddress, fee },
async () => {
await zkApp.update();
}
);
await tx.prove(); // generates the ZKP
const sentTx = await tx.sign([feepayerKey]).send();
The command to execute interact.ts
is:
npm run build && node build/src/interact.js DEPLOY_ALIAS // for this example DEPLOY_ALIAS=testnet
After it if we look to the appState of the zkApp we will see 3 in the field 0 since the update() method increased the initial state, which was 1, by 2.
TO-DO: