# Interacting with zkApps server-side

> Interact with a zkApp server-side or from your machine.

Canonical URL: https://docs.minaprotocol.com/zkapps/tutorials/interacting-with-zkapps-server-side

While user-facing zkApps can be written for the browser, sometimes it is useful to interact with a zkApp server-side or on your own machine. For example, when initializing a zkApp using programmatically generated information, deploying a zkApp in custom ways, or writing scripts that create transactions depending on real-world (periodically updating an on-chain value with signed data, like a keeper for an oracle) or on-chain events.

Interacting with zkApps server-side is useful for some use cases. For example, if you need to create a custom account for your zkApp to deploy a zkApp to a different key than the fee payer key. You can programmatically parameterize a zkApp before you initialize it. You can even create a smart contract programmatically for users as part of an application.

## Prerequisites

Ensure your environment meets the [Prerequisites](/zkapps/tutorials#prerequisites) for zkApp Developer Tutorials. o1js 3 requires Node.js 22.19.5 or later.

The script on this page connects to Devnet. Before you start, complete [Tutorial 3: Deploy to a Live Network](deploying-to-a-network), which reuses the smart contract `Square` from [Tutorial 1: Hello World](hello-world). Tutorial 3 gives you these items, which this script uses:

- A deploy alias in `config.json`, made with the `zk config` command
- A fee payer account that has tMINA from the faucet
- A zkApp account where `zk deploy` deployed the `Square` contract

To try the same flow without a network, a funded account or key files, go to [Run it without a network](#run-it-without-a-network).

The [example project](https://github.com/MinaProtocol/docs2/tree/main/examples/zkapps/interacting-with-zkApps-server-side) uses o1js `^3.0.0`. CI builds it on every pull request, and runs and tests its local version (`src/local.ts`). CI does not run the Devnet script (`src/main.ts`), because that script needs funds and keys.

## Interact with the deployed Smart Contract

Now that you successfully created and deployed your project following [Tutorial 3: Deploy to a Live Network](deploying-to-a-network), you can write a script to interact with the smart contract.

### Building on Tutorial 3

For this tutorial, you add a script to the project that you already built for [Tutorial 3: Deploy to a Live Network](/zkapps/tutorials/deploying-to-a-network).

You run commands from the root of the `03-deploying-to-a-live-network` directory as you work in the `src` directory on files that contain the TypeScript code for the smart contract.

Each time you make updates, then build or deploy, the TypeScript code is compiled into JavaScript in the `build` directory.

### Helper functions in utils.ts

To make this script more convenient to write, use the provided helper functions.

1. Download the [utils.ts](https://github.com/MinaProtocol/docs2/blob/main/examples/zkapps/interacting-with-zkApps-server-side/src/utils.ts) file.

1. Place the file in the project `03-deploying-to-a-live-network/src` folder.

1. Read through the code to understand what it is doing to implement its functionality.

  The `utils.ts` file contains two functions:

  - `loopUntilAccountExists()` waits until an account exists on the network
  - `deploy()` deploys your zkApp programmatically, if no verification key is on the zkApp account yet

### Read the deploy alias

1. Download the [main.ts](https://github.com/MinaProtocol/docs2/blob/main/examples/zkapps/interacting-with-zkApps-server-side/src/main.ts) example file.

1. Because you are building on your earlier work, place the `main.ts` file in the project `03-deploying-to-a-live-network/src` folder.

1. Review the imports:

  ```ts title="src/main.ts"
  import { Mina, PrivateKey } from 'o1js';
  import { Square } from './Square.js';

  import fs from 'fs';
  import { deploy, loopUntilAccountExists } from './utils.js';
  ```

1. Review the code that reads the deploy alias. The name of the alias is the first argument on the command line (`process.argv[2]`). The `zk config` command wrote the alias to `config.json` in Tutorial 3:

  ```ts title="src/main.ts"
  // `zk config` (Tutorial 3) wrote a deploy alias to config.json. The alias names
  // the GraphQL endpoint, the network kind, the fee, the zkApp key file and the
  // fee payer key file.
  const deployAlias = process.argv[2];
  if (!deployAlias) {
    throw Error('usage: node build/src/main.js <deploy-alias>');
  }
  const config = JSON.parse(fs.readFileSync('config.json', 'utf8'));
  const alias = config.deployAliases?.[deployAlias];
  if (!alias) {
    throw Error(`config.json has no deploy alias "${deployAlias}"`);
  }
  ```

### Connect to a remote network

Set the active instance to the remote network of the deploy alias. Earlier tutorials set the active instance to a simulated local blockchain, which is fast for development but only available on your local machine and is not decentralized.

The connection is made through the GraphQL endpoint exposed by a Mina node. For the `devnet` alias of Tutorial 3, the endpoint is `https://devnet-plain-1.gcp.o1test.net/graphql`. By connecting to the remote Devnet network, you can provide smart contracts that are globally accessible and provide strong guarantees around state due to both Mina's decentralization and its succinct state proof.

The deploy alias also sets the transaction fee, in MINA. Transaction fees in code are declared in nanomina, so the script converts the value. For the fee of `0.1` from Tutorial 3, the result is 100,000,000 nanomina:

```ts title="src/main.ts"
const Network = Mina.Network({ networkId: alias.networkId, mina: alias.url });
Mina.setActiveInstance(Network);

// The fee in config.json is in MINA. Transaction fees in code are in nanomina.
const transactionFee = Math.round(Number(alias.fee) * 1e9);
```

You could also run a Mina node locally and instead use its GraphQL endpoint. In other blockchains a local node would be very heavyweight, but because Mina is succinct this is actually a reasonable option. See the Node Operator [Getting Started](/node-operators/block-producer-node/getting-started) docs.

### Public/private key pairs

You already generated two key pairs when you ran the `zk config` command in [Tutorial 3: Deploy to a Live Network](/zkapps/tutorials/deploying-to-a-network#deploy-alias):

- The zkApp key pair, in `keys/devnet.json`. `zk deploy` deployed the `Square` contract to this account.
- The fee payer key pair, in `${HOME}/.cache/zkapp-cli/keys/03-deploy.json`. You funded this account at the faucet.

The deploy alias in `config.json` holds the path of each file, as `keyPath` and `feepayerKeyPath`. Public and private keys in Mina are commonly stored in Base58 for easy readability. In Mina, public keys start with `B62` and private keys start with `EK` for easy differentiability.

Review the code that reads the two private keys:

```ts title="src/main.ts"
function readPrivateKey(path: string) {
  return PrivateKey.fromBase58(JSON.parse(fs.readFileSync(path, 'utf8')).privateKey);
}

// The fee payer pays for every transaction. It is the account that you funded
// at the faucet in Tutorial 3.
const deployerPrivateKey = readPrivateKey(alias.feepayerKeyPath);
const deployerPublicKey = deployerPrivateKey.toPublicKey();

// The zkApp has its own key, not the fee payer's. `zk deploy` deployed the
// Square contract to this key in Tutorial 3.
const zkAppPrivateKey = readPrivateKey(alias.keyPath);
```

The fee payer and the zkApp are two different accounts. The fee payer signs and pays for each transaction. The zkApp account holds the contract and its state.

You can run this code now with:

```sh
$ npm run build && node build/src/main.js devnet
```

- The `npm run build` command creates JavaScript code in the `build` directory.
- The `&&` operator links two commands together.
- The `node build/src/main.js devnet` command runs the code in `src/main.ts` with the `devnet` deploy alias.

### Wait for accounts to be ready

Next, review the code that waits for the fee payer account to be ready. If the fee payer account does not exist yet, because the faucet transaction is not in a block yet, the script waits and shows the faucet URL. When the account exists, the script shows its nonce and its balance:

```ts title="src/main.ts"
let account = await loopUntilAccountExists({
  account: deployerPublicKey,
  eachTimeNotExist: () => {
    console.log(
      'Deployer account does not exist. ' +
        'Request funds at faucet ' +
        'https://faucet.minaprotocol.com/?address=' +
        deployerPublicKey.toBase58()
    );
  },
  isZkAppAccount: false,
});

console.log(
  `Using fee payer account with nonce ${account.nonce}, balance ${account.balance}`
);
```

Then the script compiles the smart contract and makes sure that the zkApp is deployed:

```ts title="src/main.ts"
console.log('Compiling smart contract...');
let { verificationKey } = await Square.compile();

const zkAppPublicKey = zkAppPrivateKey.toPublicKey();
let zkapp = new Square(zkAppPublicKey);

// Programmatic deploy:
//   Besides the CLI, you can also create accounts programmatically. This is useful if you need
//   more custom account creation - say deploying a zkApp to a different key than the fee payer
//   key, programmatically parameterizing a zkApp before initializing it, or creating Smart
//   Contracts programmatically for users as part of an application.
await deploy(deployerPrivateKey, zkAppPrivateKey, zkapp, verificationKey);

await loopUntilAccountExists({
  account: zkAppPublicKey,
  eachTimeNotExist: () =>
    console.log('waiting for zkApp account to be deployed...'),
  isZkAppAccount: true,
});

let num = (await zkapp.num.fetch())!;
console.log(`current value of num is ${num}`);
```

The `deploy()` function from `utils.ts` first reads the zkApp account. You deployed the contract with `zk deploy` in Tutorial 3, so the account has a verification key: `deploy()` shows that the zkApp is found deployed, and does not send a transaction. If the account has no verification key, `deploy()` sends a deploy transaction, signed with the fee payer key and the zkApp key. To see how this works, see the code in [utils.ts](https://github.com/MinaProtocol/docs2/blob/main/examples/zkapps/interacting-with-zkApps-server-side/src/utils.ts).

Then the script calls `loopUntilAccountExists()` with `isZkAppAccount: true`. This checks that the account exists and that it has zkApp state, which shows that the zkApp was deployed successfully.

After the zkApp has been deployed, the script fetches the current value of `zkapp.num` (the on-chain state defined on the `SmartContract`) and shows it. If you did not update the contract after Tutorial 3, the value is `3`, because that's how it is set in the smart contract's `init()` function. The `init()` function is called automatically during the first deploy (not during re-deploys).

### Send an update transaction

Finally, here is code that sends an update transaction. It calls `update()` with the square of the current value of `num`:

```ts title="src/main.ts"
let transaction = await Mina.transaction(
  { sender: deployerPublicKey, fee: transactionFee },
  async () => {
    await zkapp.update(num.mul(num));
  }
);

// fill in the proof - this can take a while...
console.log('Creating an execution proof...');
let time0 = performance.now();
await transaction.prove();
let time1 = performance.now();
console.log(`creating proof took ${(time1 - time0) / 1e3} seconds`);

// sign transaction with the deployer account
transaction.sign([deployerPrivateKey]);

console.log('Sending the transaction...');
let pendingTransaction = await transaction.send();
```

To send an update transaction, perform the following steps:

1. Construct the transaction with `Mina.transaction`. This is where you call `zkapp.update()`, the custom method defined on the smart contract.
2. Create a proof of the transaction. This can take up to a minute.
3. Sign the transaction and send it to the network.

When sending the transaction using `transaction.send()`, an object called `pendingTransaction` is returned and provides information about how the transaction went and waits for inclusion in a block:

```ts title="src/main.ts"
if (pendingTransaction.status === 'rejected') {
  console.log('error sending transaction (see above)');
  process.exit(0);
}

console.log(
  `See transaction at https://minascan.io/devnet/tx/${pendingTransaction.hash}
Waiting for transaction to be included...`
);
await pendingTransaction.wait();

console.log(`updated state! ${await zkapp.num.fetch()}`);
```

This code uses several functionalities of the pending transaction:

- `pendingTransaction.status` indicates the initial processing status of the transaction, with `pending` signifying that the transaction has been accepted for processing by the network, and `rejected` indicating that the transaction was immediately deemed invalid and rejected by the GraphQL endpoint.
- `pendingTransaction.hash` is the transaction hash that you can use to look up the transaction in a block explorer.
- `pendingTransaction.wait()` is especially useful as it returns a promise that resolves when a transaction is included into a block, and rejects when the transaction fails. This takes several minutes, so you might not want to block the main thread on this in a real application.

Finally, after the transaction was successfully applied on the Mina blockchain, you can double-check that your state was updated by fetching it again with `zkapp.num.fetch()`.

## Run it without a network

The Devnet script needs a funded account, a key file and a live node, so you
cannot try it until you have all three, and neither can CI. The example
project in this repository carries a second entry point, `src/local.ts`, that
runs the same flow against a local blockchain:

```sh
cd examples/zkapps/interacting-with-zkApps-server-side
npm install
npm start
```

The two addresses change on each run, because the test accounts and the zkApp key are random:

```text
fee payer : B62q...
zkApp     : B62q...

deploying...
current value of num is 3

updating num to its square...
updated state! 9

rejecting an update that is not the square...
rejected, as it should be: Field.assertEquals(): 10 != 81
state is still 9
```

It keeps what this page teaches — deploying the zkApp to a key that is **not**
the fee payer's, and driving it from a script — and drops only what needs a
network. In particular there is no `loopUntilAccountExists`. A remote node is
eventually consistent, so the account is not readable the moment the
transaction is sent; a local blockchain applies the transaction as it is sent,
so there is nothing to poll for:

```ts title="src/local.ts"
const deployTx = await Mina.transaction(deployer, async () => {
  // The fee payer pays the account creation fee for the new zkApp account.
  AccountUpdate.fundNewAccount(deployer);
  await zkApp.deploy();
});
await deployTx.prove();
await deployTx.sign([deployer.key, zkAppPrivateKey]).send();

// `main.ts` polls `loopUntilAccountExists` here. A remote node is eventually
// consistent, so the account is not readable the instant the transaction is
// sent. A local blockchain applies the transaction as it is sent, so there is
// nothing to wait for.
const num = zkApp.num.get();
console.log(`current value of num is ${num.toString()}`);
```

`npm test` asserts the same ground, including the account creation fee the fee
payer pays for the zkApp account and the rejection of an update that is not
the square.

## Conclusion

You have finished writing a script to initialize the state and interact with it! You can also run this script multiple times to update `num` to its square.

Check out other tutorials and documentation to keep going!
