Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use descriptive link text for the zkApp Developers topics #407

Merged
merged 15 commits into from
Jun 1, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/zkapps/how-to-test-a-zkapp.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,8 @@ describe('Add smart contract integration test', () => {

## Learn more

See the [Jest](https://jestjs.io/docs/getting-started) documentation.
See the [Jest Getting Started](https://jestjs.io/docs/getting-started) documentation.

## Next Steps

Now that you know how to test a smart contract, you can learn [How to deploy a zkApp](how-to-deploy-a-zkapp).
Now that you know how to test a smart contract, you can learn [how to deploy a zkApp](how-to-deploy-a-zkapp).
253 changes: 120 additions & 133 deletions docs/zkapps/how-to-write-a-zkapp-ui.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,72 +18,70 @@ You can write the UI with any framework like React, Vue, or Svelte, or with plai

## Using one of the provided UI framework scaffolds

When you create a project using the zkApp CLI, you are prompted to choose a supported frameworks to be scaffolded as a part of your new zkApp project. For example, NuxtJS, Svelte Kit, and NextJS.

Adding the `--ui=<framework>` flag to the `zk project <myproj>` command: `zk project <myproj> --ui=<framework>`, where `<framework>` stands for a supported framework like `nuxt`, `svelte` and `next`.
When you create a project using the zkApp CLI, you can choose a supported framework to be scaffolded as a part of your zkApp project. For example, Next.js, Sveltkit, or Nuxt.js.
ymekuria marked this conversation as resolved.
Show resolved Hide resolved
barriebyron marked this conversation as resolved.
Show resolved Hide resolved

## Adding your smart contract as a dependency of the UI

To use one of the provided scaffolding options and add your smart contract to an existing frontend, a different UI framework or simply a plain HTML and JavaScript website, you can do so as well.
You can use one of the provided scaffolding options and add your smart contract to an existing frontend, a different UI framework, or a plain HTML and JavaScript website.

#### Setup
### Specify the smart contracts to import

The `index.ts` file is the entry point of your project. This file only imports all smart contract classes you want access to, and exports them. This pattern allows you to specify which smart contracts are available to import when consuming your project from npm within your UI.
The `index.ts` file is the entry point of your project that imports all smart contract classes you want access to and exports them to your smart contract. This pattern allows you to specify which smart contracts are available to import when consuming your project from npm within your UI.

```ts
import { YourSmartContract } from './YourSmartContract.js';

export { YourSmartContract };
```

#### Local development

The `npm link` command allows to use your smart contract within your UI project during local development without needing to publish it npm. This allows for rapid development.

1. Using a command line, change into your smart contract project directory using `cd <your-project>`, type `npm link`, and hit enter.
2. Using a command line, change into your UI project directory using `cd <your-ui-project>`, type `npm link <your-package-name>`, and hit enter. `your-package-name` is the `name` property used within your _smart contract's_ `package.json`.
3. Import into your UI project like normal, using `import { YourSmartContract } from ‘your-package-name’;`.

Remember to run `npm run build` in your smart contract directory after making any changes in order for changes to be reflected in the smart contract consumed by your UI project.
### Local development

:::tip
The `npm link` command allows you to use your smart contract within your UI project during local development without having to publish it to npm. This local use allows for rapid development. See the [npm link](https://docs.npmjs.com/cli/v8/commands/npm-link) reference docs.

For additional details about `npm link`, see the full npm <a href="https://docs.npmjs.com/cli/v8/commands/npm-link">reference docs</a>.
1. At the command line, change into your smart contract project directory using `cd <your-project>` and run the `npm link` command.
2. At the command line, change into your UI project directory using `cd <your-ui-project>` and run the `npm link <your-package-name>` command.
`your-package-name` is the `name` property used in your _smart contract's_ `package.json`.
3. Import the smart contracts into your UI project, using `import { YourSmartContract } from 'your-package-name';`.

:::
After making changes, remember to run `npm run build` in your smart contract directory so that the changes are reflected in the smart contract consumed by your UI project.

#### Publish to npm for production
### Publish to npm for production

1. **Create an npm account:** Create one <a href="https://www.npmjs.com/signup">here</a> if you don't have one yet.
2. **Login:** To sign in, enter `npm login` on the command line. You will be prompted to enter your username, password, and email address.
3. **Publish:** To publish your package, enter `npm publish` on the command line at the root of your smart contract project directory. If the package name already exists on npm, you will get an error. You can change the package name by changing the name property in your `package.json`.
1. Create an npm account.
If you don't have an account yet, go to npm [Sign Up](https://www.npmjs.com/signup).
2. Login to npm.
To sign in, run `npm login` on the command line. When prompted,enter your username, password, and email address.
3. Publish your package.
At the root of your smart contract project directory, run `npm publish`. An error occurs if the package name already exists. To change the package name, change the `name` property in your `package.json`.

:::tip

You can check if a package name already exists on npm using the <a href="https://docs.npmjs.com/cli/v7/commands/npm-search">npm search</a>
terminal command. To avoid naming collisions, npm allows to publish scoped packages:
`@your-username/your-package-name`. For additional details about publishing packages including scoped packages, see the full npm <a href="https://docs.npmjs.com/packages-and-modules/introduction-to-packages-and-modules">reference docs</a>.
To check if a package name exists on npm, use the [npm search](https://docs.npmjs.com/cli/v7/commands/npm-search) command. To avoid naming collisions, npm allows you to publish scoped packages: `@your-username/your-package-name`. See [Introduction to packages and modules](https://docs.npmjs.com/packages-and-modules/introduction-to-packages-and-modules) in the npm reference docs.

:::

#### Consuming your smart contract in your UI
### Consuming your smart contract in your UI

Once you have published your smart contract to npm, you can easily add it to any UI framework of your choosing by importing the package.
After you have published your smart contract to npm, you can add it to any UI framework by importing the package.

1. **Install your smart contract package:** To install your package, run the following npm command: `npm install your-package-name` from the root of your UI project directory. Or if you published a scoped npm package, run `npm install @your-username/your-project-name`.
2. **Import your smart contract package into the UI using:** `import { YourSmartContract } from ‘your-package-name’;` , where `YourSmartContract` is the named export that you chose in your smart contract.
1. Install your smart contract package.
- Run `npm install your-package-name` from the root of your UI project directory.
- If you published a scoped npm package, run `npm install @your-username/your-project-name`.
2. Import your smart contract package into the UI using:
```ts
import { YourSmartContract } from ‘your-package-name’;
```
where `YourSmartContract` is the named export that you chose in your smart contract.

:::tip

For a more performant UI, you may want to render your UI before importing and loading your smart contract. This allows the SnarkyJS wasm workers to perform initialization without blocking the UI.
For a more performant UI, render your UI before importing and loading your smart contract so the SnarkyJS wasm workers can perform initialization without blocking the UI.

For example, if your UI is built using React, loading the smart contract in a `useEffect`,
instead of a top level import, will give the UI time to render its components before
loading SnarkyJS.
For example, if your UI is built using React, instead of a top level import, load the smart contract in a `useEffect` to give the UI time to render its components before loading SnarkyJS.

:::

**Loading your contract with React**
### Loading your contract with React

```ts
useEffect(() => {
Expand All @@ -93,35 +91,37 @@ useEffect(() => {
}, []);
```

**Loading your contract with Svelte**
### Loading your contract with Svelte

```ts
onMount(async () => {
const { YourSmartContract } = await import('your-package-name');
});
```

**Loading your contract with Vue**
### Loading your contract with Vue

```ts
onMounted(async () => {
const { YourSmartContract } = await import('your-package-name');
});
```

#### Enabling COOP and COEP headers
### Enabling COOP and COEP headers

To load SnarkyJS code in your UI, you must set the [COOP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy)
and [COEP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy) headers as described below. These enable SnarkyJS' use of [SharedArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer), which SnarkyJS relies on to enable important WebAssembly features.
and [COEP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy) headers.

- `Cross-Origin-Opener-Policy` must be set to `same-origin`.
- `Cross-Origin-Embedder-Policy` must be set to `require-corp`.
These headers enable SnarkyJS to use [SharedArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer) that SnarkyJS relies on to enable important WebAssembly (Wasm) features.

You can enable these headers a number of different ways. If you deploy your UI to a host such as [Vercel](https://vercel.com/) or [Cloudflare Pages](https://pages.cloudflare.com/), you can set these headers in a custom configuration file (see below). Otherwise, you can set these headers in the server framework of your choice (e.g. Express for JavaScript).
- Set `Cross-Origin-Opener-Policy` to `same-origin`.
- Set `Cross-Origin-Embedder-Policy` to `require-corp`.

**Vercel**
You can enable these headers a number of different ways. If you deploy your UI to a host such as [Vercel](https://vercel.com/) or [Cloudflare Pages](https://pages.cloudflare.com/), you can set these headers in a custom configuration file. Otherwise, set these headers in the server framework of your choice (for example, Express for JavaScript).

If your app will be hosted on Vercel, you can set these headers via [`vercel.json`](https://vercel.com/docs/project-configuration).
#### Set headers for Vercel
barriebyron marked this conversation as resolved.
Show resolved Hide resolved

If your app will be hosted on Vercel, set the headers in [`vercel.json`](https://vercel.com/docs/project-configuration).

```json
{
Expand All @@ -137,9 +137,9 @@ If your app will be hosted on Vercel, you can set these headers via [`vercel.jso
}
```

**Cloudflare Pages**
### Set headers for Cloudflare Pages

To host your app on Cloudflare Pages, you can set these headers by using a [`_headers` file](https://developers.cloudflare.com/pages/platform/headers/).
To host your app on Cloudflare Pages, set the headers in a [`_headers` file](https://developers.cloudflare.com/pages/platform/headers/).

```
/*
Expand All @@ -149,113 +149,100 @@ To host your app on Cloudflare Pages, you can set these headers by using a [`_he

### Connecting your zkApp with a user's wallet

:::info
The Mina community has created a variety of different wallets. Only the [Auro Wallet for Chrome](https://www.aurowallet.com) supports interactions with zkApps.

Currently, only Auro Wallet's Chrome extension supports zkApp transactions. We
anticipate other wallets to add support over time, which they can do by using
the <a
href="https://www.npmjs.com/package/@aurowallet/mina-provider">`mina-provider`</a>
from NPM within their browser-extension wallet.
To interact with your zkApp, users of your zkApp must have the Auro Wallet installed:

:::
- `window.mina` is automatically available in the user's browser environment.
- Your zkApp uses this object to interact with the wallet.

1. Install <a
href="https://chrome.google.com/webstore/detail/auro-walletmina-protocol/cnmamaachppnkjgnildpdmkaakejnhae">Auro
Wallet for Chrome</a>. Users of your zkApp must have this wallet
installed in order to interact with your zkApp. Once installed, `window.mina`
is automatically available in the user's browser environment. Your
zkApp then uses this object to interact with the wallet.
1. Install the Chrome extension for [Auro Wallet](https://chrome.google.com/webstore/detail/auro-walletmina-protocol/cnmamaachppnkjgnildpdmkaakejnhae).

2. Get accounts - To fetch a user's list of Mina accounts, you can use the
`requestAccounts()` method. It can be useful to indicate if the user's wallet is successfully connected to your zkApp, such as in the screenshot below.
2. Get accounts.

```ts
let accounts;

try {
// Accounts is an array of string Mina addresses.
accounts = await window.mina.requestAccounts();

// Show first 6 and last 4 characters of user's Mina account.
const display = `${accounts[0].slice(0, 6)}...${accounts[0].slice(-4)}`;
} catch (err) {
// If the user has a wallet installed but has not created an account, an
// exception will be thrown. Consider showing "not connected" in your UI.
console.log(err.message);
}
```
To fetch a user's list of Mina accounts, use the `requestAccounts()` method:

<img src={require('@site/static/img/Mina_Provider_Header.png').default} />
```ts
let accounts;

3. Sending a transaction - After your user interacts with your zkApp, you can
sign and send the transaction using `sendTransaction()`. You receive a transaction ID as soon as the Mina network has received the
proposed transaction. But keep in mind that this does not guarantee that
the transaction is accepted in the network into an upcoming block.
try {
// Accounts is an array of string Mina addresses.
accounts = await window.mina.requestAccounts();

```ts
try {
// This is the public key of the deployed zkapp you want to interact with.
const zkAppAddress = 'B62qq8sm7JdsED6VuDKNWKLAi1Tvz1jrnffuud5gXMq3mgtd';

const tx = await Mina.transaction(() => {
const YourSmartContractInstance = new YourSmartContract(zkAppAddress);
YourSmartContractInstance.foo();
});

await tx.prove();

const { hash } = await window.mina.sendTransaction({
transaction: tx.toJSON(),
feePayer: {
fee: '',
memo: 'zk',
},
});

console.log(hash);
} catch (err) {
// You may want to show the error message in your UI to the user if the transaction fails.
console.log(err.message);
}
```
// Show first 6 and last 4 characters of user's Mina account.
const display = `${accounts[0].slice(0, 6)}...${accounts[0].slice(-4)}`;
} catch (err) {
// If the user has a wallet installed but has not created an account, an
// exception will be thrown. Consider showing "not connected" in your UI.
console.log(err.message);
}
```

:::tip
It is useful to indicate if the user's wallet is successfully connected to your zkApp:

If an error occurs when sending the transaction, you may want to show the error
message in your UI to the user.
<img src={require('@site/static/img/Mina_Provider_Header.png').default} />

:::
3. Send a transaction.

After your user interacts with your zkApp, you can sign and send the transaction using `sendTransaction()`. You receive a transaction ID as soon as the Mina network has received the proposed transaction. However, this does not guarantee that the transaction is accepted in the network into an upcoming block.

```ts
try {
// This is the public key of the deployed zkapp you want to interact with.
const zkAppAddress = 'B62qq8sm7JdsED6VuDKNWKLAi1Tvz1jrnffuud5gXMq3mgtd';

const tx = await Mina.transaction(() => {
const YourSmartContractInstance = new YourSmartContract(zkAppAddress);
YourSmartContractInstance.foo();
});

await tx.prove();

const { hash } = await window.mina.sendTransaction({
transaction: tx.toJSON(),
feePayer: {
fee: '',
memo: 'zk',
},
});

console.log(hash);
} catch (err) {
// You may want to show the error message in your UI to the user if the transaction fails.
console.log(err.message);
}
```

A best practice is to show the error message in your UI.

:::info

For additional details about the Mina Provider, see the full <a
href="https://docs.aurowallet.com/general/reference/api-reference/mina-provider-api
">reference docs</a>.
For details about the Mina Provider API, see the [Mina Provider](https://docs.aurowallet.com/general/reference/api-reference/mina-provider-api) API Reference docs.

:::

### Displaying assertion exceptions in your UI
### Display assertion exceptions in your UI

If an assertion exception occurs while a user interacts with any of your smart contract methods, you want to capture this and display a helpful message for the user in your UI.

1. Use a try-catch statement to catch exceptions when a user invokes a method on your smart contract.
2. Use a switch-case statement to identify which exception was thrown. Add a matching case for each unique assertion within your method. To assist with this, you may consider setting custom error messages for your assertions while writing the smart contract--for example: `INSUFFICIENT_BALANCE`.
3. Display a helpful error message for the user within your UI.

```ts
try {
YourSmartContract.yourMethod();
} catch (err) {
let uiErrorMessage;
switch (err.message) {
// A custom error thrown within YourSmartContract.yourMethod()
// when there is an insufficient balance.
case 'INSUFFICIENT_BALANCE':
// Set a helpful message to show the user in the UI.
uiErrorMessage =
'Your account has an insufficient balance for this transaction';
break;
// etc
2. Use a switch-case statement to identify which exception was thrown. Add a matching case for each unique assertion within your method. To assist with this, consider setting custom error messages for your assertions while writing the smart contract. For example: `INSUFFICIENT_BALANCE`.
3. Display a helpful error message for the user within your UI, like:

```ts
try {
YourSmartContract.yourMethod();
} catch (err) {
let uiErrorMessage;
switch (err.message) {
// A custom error thrown within YourSmartContract.yourMethod()
// when there is an insufficient balance.
case 'INSUFFICIENT_BALANCE':
// Set a helpful message to show the user in the UI.
uiErrorMessage =
'Your account has an insufficient balance for this transaction';
break;
// etc
}
}
}
```
```
Loading