Please note that zkApp programmability is not yet available on Mina Mainnet, but zkApps can now be deployed to Berkeley Testnet.
This tutorial was last tested with SnarkyJS 0.8.0.
Tutorial 8: Custom Tokens
Overview
In this tutorial, we will discuss how to create custom tokens.
Mina comes with native support for custom tokens. Each account on Mina can also have tokens associated with it.
To create a new token, one creates a smart contract, which becomes the manager for the token, and uses that contract to set the rules around how the token can be mint, burned, and sent.
The manager account may also set a token symbol for its token, such as in this example, MYTKN
. Uniqueness is not enforced for token names. Instead the public key of the manager account is used to identify tokens.
In this tutorial, we will review smart contract code that creates and manages new tokens. You can find the full code of what we'll be building in this tutorial here.
You can also find a more extensive example, including all the ways to interact with token smart contracts, here for reference.
Basic Token Example
To create a token manager smart contract, we will create a normal smart contract, whose methods call special functions that manipulate tokens.
You can find a full copy of this file here.
First, we will bring in imports and setup the structure for our smart contract:
1 import {
2 SmartContract,
3 state,
4 State,
5 method,
6 DeployArgs,
7 Permissions,
8 UInt64,
9 PublicKey,
10 Signature,
11 } from 'snarkyjs';
12
13 const tokenSymbol = 'MYTKN';
14
15 export class BasicTokenContract extends SmartContract {
16 @state(UInt64) totalAmountInCirculation = State<UInt64>();
17
18 deploy(args: DeployArgs) {
19 super.deploy(args);
20
21 const permissionToEdit = Permissions.proof();
22
23 this.account.permissions.set({
24 ...Permissions.default(),
25 editState: permissionToEdit,
26 setTokenSymbol: permissionToEdit,
27 send: permissionToEdit,
28 receive: permissionToEdit,
29 });
30 }
31 }
We include a single state variable, totalAmountInCirculation
, which we will use to track how many tokens exist.
Next we will add an init method, and a method that mints tokens:
32 @method init() {
33 super.init();
34 this.account.tokenSymbol.set(tokenSymbol);
35 this.totalAmountInCirculation.set(UInt64.from(0));
36 }
37
38 @method mint(
39 receiverAddress: PublicKey,
40 amount: UInt64,
41 adminSignature: Signature
42 ) {
43 let totalAmountInCirculation = this.totalAmountInCirculation.get();
44 this.totalAmountInCirculation.assertEquals(totalAmountInCirculation);
45
46 let newTotalAmountInCirculation = totalAmountInCirculation.add(amount);
47
48 adminSignature
49 .verify(
50 this.address,
51 amount.toFields().concat(receiverAddress.toFields())
52 )
53 .assertTrue();
54
55 this.token.mint({
56 address: receiverAddress,
57 amount,
58 });
59
60 this.totalAmountInCirculation.set(newTotalAmountInCirculation);
61 }
In init
, we set our token symbol (in this case, MYTKN), and start tracking the amount in circulation by setting it to zero.
We also write a function to mint new tokens, and send them to a recipient. We check in this function that a signature has been provided by the zkApp account, so that not just anybody can call mint.
In mint, we also track how many tokens are in existence.
Lastly, we will write a send function:
63 @method sendTokens(
64 senderAddress: PublicKey,
65 receiverAddress: PublicKey,
66 amount: UInt64
67 ) {
68 this.token.send({
69 from: senderAddress,
70 to: receiverAddress,
71 amount,
72 });
73 }
Holders of our token will call this method to send tokens to other Mina accounts.
That completes a basic token! To see an example of interacting with this contract, see here.
And, to see an example of putting rules around a token, see this example of a token with a whitelist gating which public keys can interact with it here.
Building zkApps that interact with Tokens.
With zkApps, you can also build smart contracts that interact with tokens - say, swapping one token for another, or taking deposits of Mina tokens, etc.
This will come in a future part of this tutorial. For now, see this example here of a zkApp implementing an AMM-based DEX.
Conclusion
We have finished building a smart contract to manage a token, and shown how to build a smart contract that places custom rules over tokens!
To learn more on tokens, see the token docs.
Checkout Tutorial 9 to learn how to use recursive ZKPs with SnarkyJS, to implement zkRollups, large computations, and more.