# Tutorial 10: Account Updates

> Each zkApp transaction constructed by o1js is composed of one or more account updates. Use account updates to implement permissions, preconditions, composability, and tokens - the core features of zkApps.

Canonical URL: https://docs.minaprotocol.com/zkapps/tutorials/account-updates

The fundamental data structure that Mina transactions are built from is called an _account update_. Account updates are a flexible and powerful data structure that can express all kinds of updates, events, and preconditions you use to develop smart contracts.

:::info Mesa Upgrade

The account updates limit has been increased from ~6 to **16 segments per transaction**. See the [Mesa upgrade overview](/network-upgrades/mesa/glossary#larger-zkapp-transactions--mip9) for more details.

:::

Each zkApp transaction constructed by o1js is composed of one or more [AccountUpdate](https://docs.o1labs.org/o1js/api-reference/classes/AccountUpdate) classes, which are a set of instructions for the Mina network to perform, such as altering on-chain state, emitting an event, and so on.

Each `AccountUpdate` can make assertions about its account, apply updates to its account, and make assertions about its child `AccountUpdates`.

Transactions are structured as a list of trees of `AccountUpdates` applied with a [pre-order traversal](https://en.wikipedia.org/wiki/Tree_traversal).

## Permissions, Preconditions, and Composability

Permissions, preconditions, composability, and tokens are the core features of zkApps that are implemented using `AccountUpdates`.

To learn more, see these o1js docs:

- [Permissions](https://docs.o1labs.org/o1js/zkapps/permissions)
- [On-Chain Values](https://docs.o1labs.org/o1js/advanced-concepts/ZkApps/onChainPreconditions)

In this tutorial, you learn the essential account update features.

## AccountUpdate contents

The `AccountUpdate` class is a set of instructions for the Mina network. It includes preconditions (conditions that must be true for the account update to be applied) and a list of state updates that need to be authorized by a signature or proof.

Each [AccountUpdate](https://docs.o1labs.org/o1js/api-reference/classes/AccountUpdate) class has these components:

- `PublicKey`: The account address for the account update
- `TokenId`: A unique hash representing the custom token. Defaults to the MINA TokenId (`1`).

  Together, `PublicKey` and `TokenId` uniquely identify an account on the Mina network.

- `Preconditions`: Conditions that must be true for the account update to be applied. Corresponds to assertions in an o1js method.
- `Updates`: Things changed by the account update, such as including the zkApp state, permissions, and verification key.
- `BalanceChange`: Any changes to the balance
- `Authorization`: How the zkApp is authorized; must be a proof (corresponding to the verification key on the account), a signature, or none. See [Authorizations](/zkapps/writing-a-zkapp/introduction-to-zkapps/interact-with-mina).

Other `AccountUpdate` components are available to use, but are not covered in this tutorial:

- `MayUseToken`: Whether the zkApp has permissions to manipulate its token.
- `Layout`: Allows for assertions about the structure of an `AccountUpdate`.

## Account updates for a non-upgradable zkApp

If the verification key cannot be changed, the zkApp smart contract is considered non-upgradeable. The `setVerificationKey` permission sets the ability to change the verification key of the account.

Now, you can start building an example zkApp to explore permissions, preconditions, and composability with `AccountUpdates`.

### Visualize transactions

The pictures of transactions on this page were made with the [`mina-transaction-visualizer`](https://www.npmjs.com/package/mina-transaction-visualizer) library.

:::caution

The latest version of `mina-transaction-visualizer` is 0.1.0 (2023). It declares a peer dependency on `snarkyjs` 0.9.x, the name of o1js before the rename. It does not support o1js 3, and the example project for this tutorial does not use it.

:::

With o1js 3, you can print the account updates of a transaction with `toPretty()`. Add this line after `txn1.prove()` in `src/main.ts`:

```ts
console.log(JSON.stringify(txn1.toPretty(), null, 2));
```

The output is a list: the fee payer first, then each account update in the order of a pre-order traversal. A child account update has a `callDepth` field. The output shows the same `appState` changes, preconditions and authorization kinds as the pictures.

## Smart Contracts

In this tutorial, you build two smart contracts:

- `ProofsOnlyZkApp`: Non-upgradeable proof only
- `SecondaryZkApp`: the other zkApp

The full source code for this tutorial is provided in the [examples/zkapps/10-account-updates](https://github.com/MinaProtocol/docs2/tree/main/examples/zkapps/10-account-updates) directory on GitHub. The example project uses o1js 3.0.0, which needs Node.js 22.19.5 or later.

### Non-upgradeable proof only

The full example code is provided in the [examples/zkapps/10-account-updates/src/ProofsOnlyZkApp.ts](https://github.com/MinaProtocol/docs2/blob/main/examples/zkapps/10-account-updates/src/ProofsOnlyZkApp.ts) file.

Goal: Configure this zkApp to be modifiable only by using proofs.

For this example, the zkApp is not upgradable after it is deployed. This means that while the zkApp developer owns the private key to initially deploy the zkApp, after its first deployment, the zkApp requires proof authorization and consequently can be updated only transactions that fulfill the zkApp smart contract logic. The private key is no longer useful for anything.

This zkApp has methods that call other methods to let you explore the impacts to a transaction's account updates.

1. Start by adding the main contents of the zkApp:

  ```ts title="src/ProofsOnlyZkApp.ts"
  export class ProofsOnlyZkApp extends SmartContract {
    @state(Field) num = State<Field>();
    @state(Field) calls = State<Field>();

    async deploy() {
      await super.deploy();
      this.account.permissions.set({
        ...Permissions.default(),
        setDelegate: Permissions.proof(),
        setPermissions: Permissions.proof(),
        setVerificationKey: {
          auth: Permissions.proof(),
          txnVersion: TransactionVersion.current(),
        },
        setZkappUri: Permissions.proof(),
        setTokenSymbol: Permissions.proof(),
        incrementNonce: Permissions.proof(),
        setVotingFor: Permissions.proof(),
        setTiming: Permissions.proof(),
      });
    }

    @method async init() {
      this.account.provedState.getAndRequireEquals().assertFalse();

      super.init();
      this.num.set(Field(1));
      this.calls.set(Field(0));
    }
  ```

  This code configures the zkApp as described and initializes the zkApp with the values you want.

  By asserting that `provedState` is `false` in `init()`, you ensure that `init()` cannot be called again after the zkApp is set up during the initial deployment. Without this assertion, your zkApp could be reset by anyone calling the `init()` method on your zkApp.

  :::tip
  This assertion is a recommended best practice for most zkApps.
  :::

1. Next, add two functions:

  ```ts title="src/ProofsOnlyZkApp.ts"
  @method async add(incrementBy: Field) {
    this.account.provedState.getAndRequireEquals().assertTrue();

    const num = this.num.getAndRequireEquals();
    this.num.set(num.add(incrementBy));

    await this.incrementCalls();
  }

  @method async incrementCalls() {
    this.account.provedState.getAndRequireEquals().assertTrue();

    const calls = this.calls.getAndRequireEquals();
    this.calls.set(calls.add(Field(1)));
  }
  ```

  These methods also assert `provedState` is `true` to ensure the zkApp was initialized as expected because `provedState` becomes true after `init()` is invoked.

  :::tip
  This assertion is a recommended best practice for most zkApps.
  :::

  The `add()` method calls the `incrementCalls()` method. You can see how this is reflected in the `add()` transaction's `AccountUpdate` structure.

1. Finally, add one more function, `callSecondary()`, that calls a different zkApp:

  ```ts title="src/ProofsOnlyZkApp.ts"
  @method async callSecondary(secondaryAddr: PublicKey) {
    this.account.provedState.getAndRequireEquals().assertTrue();

    const secondaryContract = new SecondaryZkApp(secondaryAddr);
    const num = this.num.getAndRequireEquals();

    await secondaryContract.add(num);

    // NOTE this gets the state at the start of the transaction
    this.num.set(secondaryContract.num.get());

    await this.incrementCalls();
  }
  ```

  The `callSecondary()` method takes the address of the other zkApp, `SecondaryZkApp`, and calls a method on it. Note that the impact of calling that method occurs after this set of AccountUpdates—so when you call `secondaryContract.num.get()`, it gets the value before this transaction is applied.

Finally, look briefly at [SecondaryZkApp.ts](https://github.com/MinaProtocol/docs2/blob/main/examples/zkapps/10-account-updates/src/SecondaryZkApp.ts) that contains:

```ts title="src/SecondaryZkApp.ts"
export class SecondaryZkApp extends SmartContract {
  @state(Field) num = State<Field>();

  async deploy() {
    await super.deploy();
    this.account.permissions.set({
      ...Permissions.default(),
    });
  }

  @method async init() {
    this.account.provedState.getAndRequireEquals();
    this.account.provedState.get().assertFalse();

    super.init();
    this.num.set(Field(12));
  }

  @method async add(incrementBy: Field) {
    this.account.provedState.getAndRequireEquals();
    this.account.provedState.get().assertTrue();

    const num = this.num.getAndRequireEquals();
    this.num.set(num.add(incrementBy));
  }
}
```

You declare functions for initializing the account and the `add()` method that is called from the earlier `ProofOnlyZkApp`.

### Running Your Smart Contracts and Visualizing the AccountUpdates

Now it's time to learn about the [main.ts](https://github.com/MinaProtocol/docs2/blob/main/examples/zkapps/10-account-updates/src/main.ts) file that creates transactions with the earlier smart contracts. `main.ts` runs on a local blockchain (`Mina.LocalBlockchain`), with proofs off.

1. Define a legend. A legend is a map from each public key to a human-readable name. The visualizer used a legend to show these names in the pictures in place of the public keys:

  ```ts title="src/main.ts"
  const legend = {
    [proofsOnlyAddr.toBase58()]: 'proofsOnlyZkApp',
    [secondaryAddr.toBase58()]: 'secondaryZkApp',
    [deployerAccount.toBase58()]: 'deployer',
  };
  ```

1. Create, prove, sign and send a deploy transaction:

  ```ts title="src/main.ts"
  const deployTxn = await Mina.transaction(deployerAccount, async () => {
    AccountUpdate.fundNewAccount(deployerAccount, 2);
    await proofsOnlyInstance.deploy();
    await secondaryInstance.deploy();
  });

  await deployTxn.prove();
  deployTxn.sign([deployerKey, proofsOnlySk, secondarySk]);

  await deployTxn.send();
  ```

The visualizer shows `deployTxn` like this:

<img src="/img/tutorial-10-deploy_txn.png" width="100%" />

This [visualization](/img/tutorial-10-deploy_txn.png) is best viewed in a new tab.

The deploy transaction includes 5 accountUpdates represented as ovals. Described from left to right;

1. Takes the new account fee from the deployer for deploying the zkApps.
   Note the `-2` on the `balanceChange` field.
2. Deploys the `proofsOnlyZkApp` instance.
   Note the permissions are all set to the values in the zkApp's `deploy` field, and the `preconditions` asserting the nonce, so the transaction can't be applied more than once.
3. Initializes the `proofsOnlyZkApp`.
   Note the precondition that it can't already be in a proved state.
4. Deploys an instance of `secondaryZkApp`.
   Note the permissions here are set to default values, in contrast to the deployment in the `proofsOnlyZkApp` example.
5. Initializes the `secondaryZkApp` instance.

When the transaction is run on chain, these account updates are checked by the Mina network and applied if valid. Each `AccountUpdate` class includes either a proof corresponding to the verification key in the zkApp account on-chain or a signature corresponding to the zkApp address. In this transaction, the fee update and the two deploy updates are authorized by signatures, and the two init updates by proofs. After deployment, `proofsOnlyZkApp` accepts only proof authorization.

### Call `add()` on `proofsOnlyZkApp`

1. Call `add()` on your instance of `proofsOnlyZkApp`:

  ```ts title="src/main.ts"
  const txn1 = await Mina.transaction(deployerAccount, async () => {
    await proofsOnlyInstance.add(Field(4));
  });

  await txn1.prove();

  await txn1.sign([deployerKey]).send();
  ```

The visualizer shows `txn1` like this:

<img src="/img/tutorial-10-txn1.png" width="100%" />

See download link [here](/img/tutorial-10-txn1.png).

### Two AccountUpdates

Now there are two AccountUpdates:

- The parent corresponds to the `add()` method call
- The child corresponds to the `this.incrementCalls()` call that the parent makes.

One update is the child because it was called from the parent, which also implies that the parent has the child included as part of its proof. To learn more about parent/child account updates, see [Signing transactions and explicit account updates](/zkapps/writing-a-zkapp/introduction-to-zkapps/interact-with-mina#signing-transactions-and-explicit-account-updates) in the Payment to zkApp example.

As a reminder, this update corresponds to code:

```ts title="src/ProofsOnlyZkApp.ts"
@method async add(incrementBy: Field) {
  this.account.provedState.getAndRequireEquals().assertTrue();

  const num = this.num.getAndRequireEquals();
  this.num.set(num.add(incrementBy));

  await this.incrementCalls();
}

@method async incrementCalls() {
  this.account.provedState.getAndRequireEquals().assertTrue();

  const calls = this.calls.getAndRequireEquals();
  this.calls.set(calls.add(Field(1)));
}
```

View the [account updates visualization](/img/tutorial-10-txn1.png) again.

- The first AccountUpdate sets the state of `appState[0]` to `5` — corresponding to the value passed to `this.num.set()` in the `add()` method.
- In the second AccountUpdate, `appState[1]` is set to `1`—corresponding to the value passed to `this.calls.set()` in the `incrementCalls()` contract.

Finally, call `callSecondary` on your instance of `proofsOnlyZkApp`:

```ts title="src/main.ts"
const txn2 = await Mina.transaction(deployerAccount, async () => {
  await proofsOnlyInstance.callSecondary(secondaryAddr);
});

await txn2.prove();

await txn2.sign([deployerKey]).send();
```

The visualizer shows `txn2` like this:

<img src="/img/tutorial-10-txn2.png" width="100%" />

This [txn2 visualization](/img/tutorial-10-txn2.png) is best viewed in a new tab.

And a quick reminder of the code for `callSecondary()`:

```ts title="src/ProofsOnlyZkApp.ts"
@method async callSecondary(secondaryAddr: PublicKey) {
  this.account.provedState.getAndRequireEquals().assertTrue();

  const secondaryContract = new SecondaryZkApp(secondaryAddr);
  const num = this.num.getAndRequireEquals();

  await secondaryContract.add(num);

  // NOTE this gets the state at the start of the transaction
  this.num.set(secondaryContract.num.get());

  await this.incrementCalls();
}
```

This call produces three accountUpdates:

- `callSecondary()` (the parent)
- `secondaryZkApp.add()` (the left child)
- `incrementCalls()` (the right child)

As described in the code comment, `callSecondary` sets `this.num` to `12` which is the value of `secondaryContract` at the "start" of the transaction.

## Test it

The example project in this repository carries a test suite for the code on
this page:

```sh
cd examples/zkapps/10-account-updates
npm install
npm test
```

It asserts the account update structure that the visualizations on this page
show:

- The deploy transaction has the 5 account updates in the order that this page
  lists. The fee update takes 2 MINA from the deployer. Each deploy update has
  a nonce precondition, and each init update has a `provedState: false`
  precondition. `proofsOnlyZkApp` gets proof-only permissions and
  `secondaryZkApp` gets the default permissions.
- `add(4)` is a parent update that sets `appState[0]` to `5`, with a child
  `incrementCalls()` update that sets `appState[1]` to `1`.
- `callSecondary()` is a parent update with two children, in this order:
  `secondaryZkApp.add()` and `incrementCalls()`. It sets `num` to `12`, and
  the secondary `num` becomes `17`.

It also checks that the network **rejects** these transactions, and that the
state and the balances do not change:

- A second call to `init()`.
- An update that is signed with the private key of `proofsOnlyZkApp`. The same
  update on `secondaryZkApp` is accepted.
- An `add()` transaction whose state precondition is no longer true.
- A proved transaction that the fee payer did not sign.

`main.ts` compiles both contracts when proofs are on. The test suite turns
proofs off.

## Conclusion

Congratulations! You have explored the core features of AccountUpdates and learned about visualizing the AccountUpdates for a set of transactions. You can build more complicated transactions that involve multiple zkApps. This tutorial builds a foundational understanding of how o1js and zkApps work to enable permissions, preconditions, and composability.

Next: [Tutorial 11: Advanced Account Updates](advanced-account-updates) shows how a token contract approves the account updates that use its token, and [Tutorial 12: Cross-Contract Calls](cross-contract-calls) builds a transaction in which three contracts call each other. After it, two further walk-throughs build on the series: the [Anonymous Message Board](anonymous-message-board), and [Interacting with zkApps Server-Side](interacting-with-zkapps-server-side).
