Please note that zkApp programmability is not yet available on Mina Mainnet, but zkApps can now be deployed to Berkeley Testnet.
On-Chain Values
We already saw how you can access the current on-chain state of a zkApp account. Similarly, you can access many other on-chain values in a zkApp.
Just to give you an idea, here are two possible use cases:
- Say you want to let users vote on a proposal, but only within a specific timespan. To do that, you can make your zkApp require that the current timestamp lies in a certain range.
- In DeFi, you might need to compute amounts relative to a balance. For example, paying a yield of
0.001
times the account balance requires you to get the current on-chain balance.
We group on-chain values in two categories:
- Network: includes the current timestamp, block height, total Mina in circulation and other network state
- Account: includes fields and properties of the zkApp account, such as balance, nonce and delegate
The subfields of both categories are are accessible on this.network
and this.account
in your smart contract.
For example, the timestamp is on this.network.timestamp
, and it has four methods on it:
this.network.timestamp.get();
this.network.timestamp.assertEquals(timestamp);
this.network.timestamp.assertBetween(lower, upper);
With two of them you are already familiar: On-chain state has the same get()
and assertEquals()
methods. assertBetween()
gives you even more power: it allows you to make assert that the timestamp is between lower
and upper
(inclusive).
Example: restricting timestamps
Let's see how assertBetween()
can be used in the voting example mentioned earlier. Assume we want to allow voting throughout September 2022. Timestamps are represented as a UInt64
in milliseconds since the UNIX epoch. We can use the JS Date
object to easily convert to this representation. In the simplest case, our zkApp could just hard-code the dates:
const startDate = UInt64.from(Date.UTC(2022, 9, 1));
const endDate = UInt64.from(Date.UTC(2022, 10, 1));
class VotingApp extends SmartContract {
// ...
@method vote(...) {
this.network.timestamp.assertBetween(startDate, endDate);
// ...
}
}
A more refined example could store the current start date in an on-chain state variable, which can be reset by some process which is also encoded by the zkApp.
In addition to using a predefined range, you can also construct a range that depends on another variable, such as the current time. For example, a DEX with an order book could support orders that expire after an hour. So, the range should be [now, now + 1h]
, such as below:
const now = this.network.timestamp.get();
this.network.timestamp.assertBetween(now, now.add(60 * 60 * 1000));
Network reference
For completeness, below is the full list of network states you can use and make assertions about in your zkApp.
All of those fields have a get()
and an assertEquals()
method, and the subset that represent
"ordered values" (those that are UInt32
or UInt64
), also have assertBetween()
.
Of course, there's no need to remember this -- just type this.network.
and let IntelliSense guide you!
// current UNIX time in milliseconds, as measured by the block producer
this.network.timestamp.get(): UInt64;
// length of the blockchain, also known as block height
this.network.blockchainLength.get(): UInt32;
// total minted currency measured in units of 1e-9 MINA
this.network.totalCurrency.get(): UInt64;
// slots since genesis / hardfork -- a "slot" is the Mina-native time unit of 3 minutes
this.network.globalSlotSinceGenesis.get(): UInt32;
this.network.globalSlotSinceHardFork.get(): UInt32;
// hash of the snarked ledger -- i.e., the state of Mina included in the blockchain proof
this.network.snarkedLedgerHash.get(): Field;
// minimum window density in our consensus algorithm
this.network.minWindowDensity.get(): UInt32;
// consensus data relevant to the current staking epoch
this.network.stakingEpochData.ledger.hash.get(): Field;
this.network.stakingEpochData.ledger.totalCurrency.get(): UInt64;
this.network.stakingEpochData.epochLength.get(): UInt32;
this.network.stakingEpochData.seed.get(): Field;
this.network.stakingEpochData.lockCheckpoint.get(): Field;
this.network.stakingEpochData.startCheckpoint.get(): Field;
// consensus data relevant to the next, upcoming staking epoch
this.network.nextEpochData.ledger.hash.get(): Field;
this.network.nextEpochData.ledger.totalCurrency.get(): UInt64;
this.network.nextEpochData.epochLength.get(): UInt32;
this.network.nextEpochData.seed.get(): Field;
this.network.nextEpochData.lockCheckpoint.get(): Field;
this.network.nextEpochData.startCheckpoint.get(): Field;
Account reference
Here's the full list of values you can access on the zkApp account. Like the network states, these have get()
and assertEquals()
.
Balance and nonce also have assertBetween()
.
// the account balance; this might be nanoMINA or a custom token
this.account.balance.get(): UInt64;
// account nonce -- increases by 0 or 1 in every transaction
this.account.nonce.get(): UInt32;
// the account the zkApp delegates its stake to (default: its own address)
this.account.delegate.get(): PublicKey;
// boolean indicating whether an account is new (= didn't exist before the transaction)
this.account.isNew.get(): Bool;
// boolean indicating whether all 8 on-chain state fields where last changed by a transaction
// authorized by a zkApp proof (as opposed to a signature)
this.account.provedState.get(): Bool;
// hash receipt which includes all prior transaction to an account
this.account.receiptChainHash.get(): Field;
Bailing out
In some rare cases, you might, for whatever reason, want to get()
an on-chain value without constraining it to any value.
If you try this, you'll see that SnarkyJS throws a helpful error reminding you to use assertEquals()
and asserBetween()
.
As an escape hatch, if you want to get()
a value and are really sure you want to not constrain the on-chain value in any way,
there's assertNothing()
on all of these fields (including on-chain state). Use at your own risk.
assertNothing()
should be rarely used and could cause security issues through unexpected behavior if used improperly. Be certain you know what you're doing before using this.