Skip to content

Commit

Permalink
docs: Dapp tutorial (#2109)
Browse files Browse the repository at this point in the history
Tutorial on building a dapp. I went for the simplest possible options in
order to focus on aztec.js usage and avoid the need for other prior
knowledge: I used javascript instead of typescript, and set up a console
app instead of a web one. Does not include anything on account creation
since that is not a dapp concern but a wallet's responsibility.

Pending:
- [x] Add links to the next section on each page of the tutorial
- [x] Add a small page on testing (with a link to the longer testing
guide)
- [x] Run the code on the CI

Fixes #2047 
Fixes #2048 
Fixes #2049 
Fixes #2050 
Fixes #2051
  • Loading branch information
spalladino authored Sep 11, 2023
1 parent cca55f2 commit 573dbc2
Show file tree
Hide file tree
Showing 45 changed files with 739 additions and 81 deletions.
13 changes: 13 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1002,6 +1002,17 @@ jobs:
name: "Test"
command: ./scripts/cond_run_script end-to-end $JOB_NAME ./scripts/run_tests_local guides/dapp_testing.test.ts ./scripts/docker-compose-e2e-sandbox.yml
working_directory: yarn-project/end-to-end

guides-sample-dapp:
machine:
image: ubuntu-2004:202010-01
steps:
- *checkout
- *setup_env
- run:
name: "Test"
command: ./scripts/cond_run_script end-to-end $JOB_NAME ./scripts/run_tests_local sample-dapp ./scripts/docker-compose-e2e-sandbox.yml
working_directory: yarn-project/end-to-end

e2e-canary-test:
machine:
Expand Down Expand Up @@ -1443,6 +1454,7 @@ workflows:
- cli-docs-sandbox: *e2e_test
- guides-writing-an-account-contract: *e2e_test
- guides-dapp-testing: *e2e_test
- guides-sample-dapp: *e2e_test

- e2e-end:
requires:
Expand Down Expand Up @@ -1476,6 +1488,7 @@ workflows:
- cli-docs-sandbox
- guides-writing-an-account-contract
- guides-dapp-testing
- guides-sample-dapp
<<: *defaults

- deploy-dockerhub:
Expand Down
110 changes: 107 additions & 3 deletions docs/docs/dev_docs/dapps/tutorials/contract_deployment.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,107 @@
---
title: Contract Deployment
---
# Contract Deployment

To add contracts to your application, we'll start by creating a new `nargo` project. We'll then compile the contracts, and write a simple script to deploy them to our Sandbox.

:::info
Follow the instructions [here](../../getting_started/noir_contracts.md) to install `nargo` if you haven't done so already.
:::

## Initialise nargo project

Create a new `contracts` folder, and from there, initialise a new project called `private_token`:

```sh
mkdir contracts && cd contracts
nargo new --contract private_token
```

Then, open the `contracts/private_token/Nargo.toml` configuration file, and add the `aztec.nr` and `value_note` libraries as dependencies:

```toml
[dependencies]
aztec = { git="https://github.com/AztecProtocol/aztec-packages", tag="master", directory="yarn-project/noir-libs/noir-aztec" }
value_note = { git="https://github.com/AztecProtocol/aztec-packages", tag="master", directory="yarn-project/noir-libs/value-note" }
```

Last, copy-paste the code from the `PrivateToken` contract into `contracts/private_token/main.nr`:

#include_code all yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr rust

## Compile your contract

We'll now use the [Aztec CLI](../../cli/main.md) to [compile](../../contracts/compiling.md) our project. If you haven't installed the CLI already, you can install it locally to your project running:

```sh
yarn add -D @aztec/cli
```

Now run the following from your project root:

```sh
yarn aztec-cli compile contracts/private_token
```

:::info
If you are using Typescript, consider including the `--typescript` option to [generate type-safe wrappers](../../contracts/compiling.md#typescript-interfaces) for your contracts.
:::

This should have created an artifact `contracts/private_token/target/private_token-Main.json` with the interface and bytecode for your contract.

## Adding a second contract

For the purposes of this tutorial, we'll set up a second contract: a public token contract. Follow the same steps as above for initialising a new Nargo project, include the dependencies, and copy-paste the following code into `contracts/public_token/main.nr`:

#include_code all yarn-project/noir-contracts/src/contracts/public_token_contract/src/main.nr rust

Compile the contract with the CLI:

```sh
yarn aztec-cli compile contracts/public_token
```

With both contracts ready, we'll now proceed to deployment.

## Deploy your contracts

Let's now write a script for deploying your contracts to the Sandbox. We'll create an RPC client, and then use the `ContractDeployer` class to deploy our contracts, and store the deployment address to a local JSON file.

Create a new file `src/deploy.mjs`, importing the contract artifacts we have generated plus the dependencies we'll need, and with a call to a `main` function that we'll populate in a second:

```js
// src/deploy.mjs
import { writeFileSync } from 'fs';
import { createAztecRpcClient, ContractDeployer } from '@aztec/aztec.js';
import PrivateTokenArtifact from '../contracts/private_token/target/PrivateToken.json' assert { type: 'json' };
import PublicTokenArtifact from '../contracts/public_token/target/PublicToken.json' assert { type: 'json' };

async function main() { }

main().catch(err => {
console.error(`Error in deployment script: ${err}`);
process.exit(1);
});
```

Now we can deploy the contracts by adding the following code to the `src/deploy.mjs` file. Here, we are using the `ContractDeployer` class with the compiled artifact to send a new deployment transaction. The `wait` method will block execution until the transaction is successfully mined, and return a receipt with the deployed contract address.

#include_code dapp-deploy yarn-project/end-to-end/src/sample-dapp/deploy.mjs javascript

Note that the private token constructor expects an `owner` address to mint an initial set of tokens to. We are using the first account from the Sandbox for this.

:::info
If you are using the generated typescript classes, you can drop the generic `ContractDeployer` in favor of using the `deploy` method of the generated class, which will automatically load the artifact for you and type-check the constructor arguments:

```typescript
await PrivateToken.deploy(client, 100n, owner.address).send().wait();
```
:::

Run the snippet above as `node src/deploy.mjs`, and you should see the following output, along with a new `addresses.json` file in your project root:

```text
Private token deployed to 0x2950b0f290422ff86b8ee8b91af4417e1464ddfd9dda26de8af52dac9ea4f869
Public token deployed to 0x2b54f68fd1e18f7dcfa71e3be3c91bb06ecbe727a28d609e964c225a4b5549c8
```
## Next steps

Now that we have our contracts set up, it's time to actually [start writing our application that will be interacting with them](./contract_interaction.md).
138 changes: 135 additions & 3 deletions docs/docs/dev_docs/dapps/tutorials/contract_interaction.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,135 @@
---
title: Contract Interactions
---
# Contract interactions

In this section, we'll write the logic in our app that will interact with the contract we have previously deployed. We'll be using the accounts already seeded in the Sandbox.

## Showing user balance

Let's start by showing our user balance for the private token across their accounts. To do this, we can leverage the `getBalance` [unconstrained](../../contracts/functions.md#unconstrained-functions) view function of the private token contract:

#include_code getBalance yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr rust

:::info
Note that this function will only return a valid response for accounts registered in the RPC Server, since it requires access to the [user's private state](../../wallets/main.md#private-state). In other words, you cannot query the balance of another user for a private token contract.
:::

To do this, let's first initialise a new `Contract` instance using `aztec.js` that represents our deployed token contracts. Create a new `src/contracts.mjs` file with the imports for our artifacts and other dependencies:

```js
// src/contracts.mjs
import { Contract } from '@aztec/aztec.js';
import { readFileSync } from 'fs';
import PrivateTokenArtifact from '../contracts/private_token/target/PrivateToken.json' assert { type: 'json' };
import PublicTokenArtifact from '../contracts/public_token/target/PublicToken.json' assert { type: 'json' };
```

And then add the following code for initialising the `Contract` instances:

#include_code get-tokens yarn-project/end-to-end/src/sample-dapp/contracts.mjs javascript

:::info
You can use the typescript autogenerated interface instead of the generic `Contract` class to get type-safe methods.
:::

We can now get the private token instance in our main code in `src/index.mjs`, and query the private balance for each of the user accounts. To query a function, without sending a transaction, use the `view` function of the method:

#include_code showPrivateBalances yarn-project/end-to-end/src/sample-dapp/index.mjs javascript

Run this as `node src/index.mjs` and you should now see the following output:

```
Balance of 0x0c8a6673d7676cc80aaebe7fa7504cf51daa90ba906861bfad70a58a98bf5a7d: 100
Balance of 0x226f8087792beff8d5009eb94e65d2a4a505b70baf4a9f28d33c8d620b0ba972: 0
Balance of 0x0e1f60e8566e2c6d32378bdcadb7c63696e853281be798c107266b8c3a88ea9b: 0
```

## Transferring private tokens

Now that we can see the balance for each user, let's transfer tokens from one account to another. To do this, we will first need access to a `Wallet` object. This wraps access to an RPC Server and also provides an interface to craft and sign transactions on behalf of one of the user accounts.

We can initialise a wallet using one of the `getAccount` methods from `aztec.js``, along with the corresponding signing and encryption keys:

```js
import { getSchnorrAccount } from '@aztec/aztec.js';
const wallet = await getSchnorrAccount(client, ENCRYPTION_PRIVATE_KEY, SIGNING_PRIVATE_KEY).getWallet();
```

For ease of use, `aztec.js` also ships with a helper `getSandboxAccountsWallets` method that returns a wallet for each of the pre-initialised accounts in the Sandbox, so you can send transactions as any of them. We'll use one of these wallets to initialise the `Contract` instance that represents our private token contract, so every transaction sent through it will be sent through that wallet.

#include_code transferPrivateFunds yarn-project/end-to-end/src/sample-dapp/index.mjs javascript

Let's go step-by-step on this snippet. We first get wallets for two of the Sandbox accounts, and name them `owner` and `recipient`. Then, we initialise the private token `Contract` instance using the `owner` wallet, meaning that any transactions sent through it will have the `owner` as sender.

Next, we send a transfer transaction, moving 1 unit of balance to the `recipient` account address. This has no immediate effect, since the transaction first needs to be simulated locally and then submitted and mined. Only once this has finished we can query the balances again and see the effect of our transaction. We are using a `showPrivateBalances` helper function here which has the code we wrote in the section above.

Run this new snippet and you should see the following:

```text
Sent transfer transaction 16025a7c4f6c44611d7ac884a5c27037d85d9756a4924df6d97fb25f6e83a0c8
Balance of 0x0c8a6673d7676cc80aaebe7fa7504cf51daa90ba906861bfad70a58a98bf5a7d: 100
Balance of 0x226f8087792beff8d5009eb94e65d2a4a505b70baf4a9f28d33c8d620b0ba972: 0
Balance of 0x0e1f60e8566e2c6d32378bdcadb7c63696e853281be798c107266b8c3a88ea9b: 0
Awaiting transaction to be mined
Transaction has been mind on block 4
Balance of 0x0c8a6673d7676cc80aaebe7fa7504cf51daa90ba906861bfad70a58a98bf5a7d: 99
Balance of 0x226f8087792beff8d5009eb94e65d2a4a505b70baf4a9f28d33c8d620b0ba972: 1
Balance of 0x0e1f60e8566e2c6d32378bdcadb7c63696e853281be798c107266b8c3a88ea9b: 0
```

:::info
At the time of this writing, there are no events emitted when new private notes are received, so the only way to detect of a change in a user's private balance is via polling on every new block processed. This will change in a future release.
:::

## Working with public state

While they are [fundamentally differently](../../../concepts/foundation/state_model.md), the API for working with private and public functions and state from `aztec.js` is equivalent. To query the balance in public tokens for our user accounts, we can just call the `publicBalanceOf` view function in the contract:

#include_code showPublicBalances yarn-project/end-to-end/src/sample-dapp/index.mjs javascript

:::info
Since this is a public token contract we are working with, we can now query the balance for any address, not just those registered in our local RPC Server. We can also send funds to addresses for which we don't know their [public encryption key](../../../concepts/foundation/accounts/keys.md#encryption-keys).
:::

Here, since the public token contract does not mint any initial funds upon deployment, the balances for all of our user's accounts will be zero. But we can send a transaction to mint tokens to change this, using very similar code to the one for sending private funds:

#include_code mintPublicFunds yarn-project/end-to-end/src/sample-dapp/index.mjs javascript

And get the expected results:

```text
Sent mint transaction 041d5b4cc68bcb5c6cb45cd4c79f893d94f0df0792f66e6fddd7718c049fe925
Balance of 0x0c8a6673d7676cc80aaebe7fa7504cf51daa90ba906861bfad70a58a98bf5a7d: 0
Balance of 0x226f8087792beff8d5009eb94e65d2a4a505b70baf4a9f28d33c8d620b0ba972: 0
Balance of 0x0e1f60e8566e2c6d32378bdcadb7c63696e853281be798c107266b8c3a88ea9b: 0
Awaiting transaction to be mined
Transaction has been mined on block 5
Balance of 0x0c8a6673d7676cc80aaebe7fa7504cf51daa90ba906861bfad70a58a98bf5a7d: 100
Balance of 0x226f8087792beff8d5009eb94e65d2a4a505b70baf4a9f28d33c8d620b0ba972: 0
Balance of 0x0e1f60e8566e2c6d32378bdcadb7c63696e853281be798c107266b8c3a88ea9b: 0
```

Public functions can emit [unencrypted public logs](../../contracts/events.md#unencrypted-events), which we can query via the RPC Server interface. In particular, the public token contract emits a generic `Coins minted` whenever the `mint` method is called:

#include_code unencrypted_log yarn-project/noir-contracts/src/contracts/public_token_contract/src/main.nr rust

We can extend our code by querying the logs emitted on the last block when the minting transaction is mined:

#include_code showLogs yarn-project/end-to-end/src/sample-dapp/index.mjs javascript

Running the code again would now show an extra line with:

```text
Log: Coins minted
```

:::info
At the time of this writing, there is no event-based mechanism in the `aztec.js` library to subscribe to events. The only option to consume them is to poll on every new block detected. This will change in a future version.
:::

## Next steps

In the next and final section, we'll [set up automated tests for our application](./testing.md).
3 changes: 0 additions & 3 deletions docs/docs/dev_docs/dapps/tutorials/creating_accounts.md

This file was deleted.

27 changes: 21 additions & 6 deletions docs/docs/dev_docs/dapps/tutorials/main.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
---
title: Dapp Development Tutorials
title: Dapp Development Tutorial
---

Links to specific tutorials
In this tutorial we'll go through the steps for building a simple application that interacts with the Aztec Sandbox. We'll be building a console application using Javascript and NodeJS, but you may reuse the same concepts here for a web-based app. All Aztec libraries are written in Typescript and fully typed, so you can use Typescript instead of Javascript to make the most out of its type checker.

- Connecting to the RPC Server
- Creating Accounts
- Deploying a contract
- Contract Interactions
This tutorial will focus on environment setup, including creating accounts and deployments, as well as interacting with your contracts. It will not cover [how to write contracts in Noir](../../contracts/main.md).

The full code for this tutorial is [available on the `aztec-packages` repository](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/end-to-end/src/sample-dapp).

## Dependencies

- Linux or OSX environment
- [NodeJS](https://nodejs.org/) 18 or higher
- [Aztec Sandbox](../../getting_started/sandbox.md)
- [Aztec CLI](../../getting_started/cli.md)
- [Nargo](../../getting_started/noir_contracts.md) for building contracts

## Prerequisites

Basic understanding of NodeJS and Javascript should be enough to follow this tutorial. Along the way, we'll provide links to dig deeper into Aztec concepts as we introduce them.

## Get started

Let's get started with [setting up a new javascript project](./project_setup.md).
31 changes: 31 additions & 0 deletions docs/docs/dev_docs/dapps/tutorials/project_setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Setting up your project

Let's start by setting up a regular Javascript NodeJS project. Feel free to skip this part if you're already familiar with project setup and head directly to connecting to the Sandbox.

## Create a new project

We'll use [`yarn`](https://yarnpkg.com/) for managing our project and dependencies, though you can also use `npm` or your Javascript package manager of choice.

1. Ensure node version is 18 or more by running.

```sh
node -v
```

2. Create a new folder and initialize a new project.

```sh
mkdir sample-dapp
cd sample-dapp
yarn init -yp
```

3. Add the `aztec.js` library as a dependency:

```sh
yarn add @aztec/aztec.js
```

## Next steps

With your project already set up, let's [connect to the Sandbox RPC Server and grab an account to interact with it](./rpc_server.md).
Loading

0 comments on commit 573dbc2

Please sign in to comment.