diff --git a/docs/docs/dev_docs/dapps/tutorials/contract_interaction.md b/docs/docs/dev_docs/dapps/tutorials/contract_interaction.md index 7517ea13e0c..949a61b36ba 100644 --- a/docs/docs/dev_docs/dapps/tutorials/contract_interaction.md +++ b/docs/docs/dev_docs/dapps/tutorials/contract_interaction.md @@ -4,9 +4,9 @@ In this section, we'll write the logic in our app that will interact with the co ## 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: +Let's start by showing our user balance for the private token across their accounts. To do this, we can leverage the `balance_of_private` [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 +#include_code balance_of_private yarn-project/noir-contracts/src/contracts/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. diff --git a/docs/docs/dev_docs/getting_started/sandbox.md b/docs/docs/dev_docs/getting_started/sandbox.md index 9ca0ef7ca4e..acc80c8abbe 100644 --- a/docs/docs/dev_docs/getting_started/sandbox.md +++ b/docs/docs/dev_docs/getting_started/sandbox.md @@ -60,13 +60,13 @@ With the help of Aztec.js you will be able to: ## I have the Sandbox running, show me how to use it! -We will deploy a private token contract, and send tokens privately, using the Sandbox. +We will deploy a token contract, and send tokens privately, using the Sandbox. -Writing the contract itself is out of scope for this tutorial, so we will use a Private Token Contract which has been pre-supplied as an example. See [here](../contracts/main.md) for more information on how to write contracts for Aztec. +Writing the contract itself is out of scope for this tutorial, so we will use a Token Contract which has been pre-supplied as an example. See [here](../contracts/main.md) for more information on how to write contracts for Aztec. The following should work for MacOS, Linux or even WSL2 Ubuntu under Windows. -Let's create an empty project called `private-token`. If you are familiar with setting up Typescript projects then you can skip to step 6. +Let's create an empty project called `token`. If you are familiar with setting up Typescript projects then you can skip to step 6. Although both `yarn` and `npm` would work, this example uses `yarn`. Open the terminal and do the following @@ -79,8 +79,8 @@ node -v 2. Initialize a yarn project ```sh -mkdir private-token -cd private-token +mkdir token +cd token yarn init ``` @@ -88,9 +88,9 @@ This should ask a series of questions that you can fill like so: ``` yarn init v1.22.19 -question name (private-token): +question name (token): question version (1.0.0): -question description: My first private token contract +question description: My first token contract question entry point (index.js): question repository url: question author: Phil @@ -100,7 +100,7 @@ success Saved package.json Done in 23.60s. ``` -3. Create a `src` folder inside your new `private-token` directory: +3. Create a `src` folder inside your new `token` directory: ```sh mkdir src @@ -144,9 +144,9 @@ Add a `tsconfig.json` file into the project root, here is an example: ```json { - "name": "private-token", + "name": "token", "version": "1.0.0", - "description": "My first private token contract", + "description": "My first token contract", "main": "index.js", "author": "Phil", "license": "MIT", @@ -155,7 +155,7 @@ Add a `tsconfig.json` file into the project root, here is an example: "build": "yarn clean && tsc -b", "build:dev": "tsc -b --watch", "clean": "rm -rf ./dest tsconfig.tsbuildinfo", - "start": "yarn build && export DEBUG='private-token' && node ./dest/index.js" + "start": "yarn build && export DEBUG='token' && node ./dest/index.js" }, "devDependencies": { "@types/node": "^20.4.9", @@ -187,7 +187,15 @@ yarn start A successful run should show: ``` - private-token Aztec Sandbox Info { version: 1, chainId: 31337 } +0ms + token Aztec Sandbox Info { + version: 1, + chainId: 31337, + rollupAddress: EthAddress { + buffer: + }, + client: 'aztec-rpc@0.1.0', + compatibleNargoVersion: '0.11.1-aztec.0' + } ``` Great!. The Sandbox is running and we are able to interact with it. @@ -206,10 +214,18 @@ Continue with adding the following to the `index.ts` file in our example: Running `yarn start` should now output: ``` - private-token Aztec Sandbox Info { version: 1, chainId: 31337 } +0ms - private-token Creating accounts using schnorr signers... +2ms - private-token Created Alice's account at 0x054d89d0...f17e +23s - private-token Created Bob's account at 0x0a8410a1...7c48 +1ms + token Aztec Sandbox Info { + version: 1, + chainId: 31337, + rollupAddress: EthAddress { + buffer: + }, + client: 'aztec-rpc@0.1.0', + compatibleNargoVersion: '0.11.1-aztec.0' + } + token Creating accounts using schnorr signers... +3ms + token Created Alice's account at 0x1509b252...0027 +10s + token Created Bob's account at 0x031862e8...e7a3 +0ms ``` That might seem like a lot to digest but it can be broken down into the following steps: @@ -232,13 +248,20 @@ Now that we have our accounts setup, let's move on to deploy our private token c `yarn start` will now give the following output: ``` - private-token Aztec Sandbox Info { version: 1, chainId: 31337 } +0ms - private-token Creating accounts using schnorr signers... +2ms - private-token Created Alice's account at 0x054d89d0...f17e +23s - private-token Created Bob's account at 0x0a8410a1...7c48 +1ms - private-token Deploying private token contract minting an initial 1000000 tokens to Alice... +0ms - private-token Transaction status is mined +8s - private-token Contract successfully deployed at address 0x143e0af4...11b6 +7ms + token Aztec Sandbox Info { + version: 1, + chainId: 31337, + rollupAddress: EthAddress { + buffer: + }, + client: 'aztec-rpc@0.1.0', + compatibleNargoVersion: '0.11.1-aztec.0' + } + token Creating accounts using schnorr signers... +3ms + token Created Alice's account at 0x1509b252...0027 +10s + token Created Bob's account at 0x031862e8...e7a3 +0ms + token Deploying token contract minting an initial 1000000 tokens to Alice... +1ms + token Contract successfully deployed at address 0x1c3dc2ed...1362 +15s ``` We can break this down as follows: @@ -246,25 +269,17 @@ We can break this down as follows: 1. We create and send a contract deployment transaction to the network. 2. We wait for it to be successfully mined. 3. We retrieve the transaction receipt containing the transaction status and contract address. -4. We use the `getContractInfo()` api on the RPC Server to retrieve information about the reported contract address. -5. The fact that this api returns a valid object tells us that the contract was successfully deployed in a prior block. - -Our output will now be: - -``` - private-token Aztec Sandbox Info { version: 1, chainId: 31337 } +0ms - private-token Creating accounts using schnorr signers... +2ms - private-token Created Alice's account at 0x054d89d0...f17e +23s - private-token Created Bob's account at 0x0a8410a1...7c48 +1ms - private-token Deploying private token contract minting an initial 1000000 tokens to Alice... +0ms - private-token Contract successfully deployed at address 0x143e0af4...11b6 +7ms -``` +4. We connect to the contract with Alice +5. Alice initialize the contract with herself as the admin and a minter. +6. Alice adds Bob as minter. +7. Alice mints 1000000 tokens to be claimed by herself in private. +8. Alice claims the tokens privately. ## Viewing the balance of an account -A token contract wouldn't be very useful if you aren't able to query the balance of an account. As part of the deployment, tokens were minted to Alice. We can now call the contract's `getBalance()` function to retrieve the balances of the accounts. +A token contract wouldn't be very useful if you aren't able to query the balance of an account. As part of the deployment, tokens were minted to Alice. We can now call the contract's `balance_of_private()` function to retrieve the balances of the accounts. -#include_code getBalance /yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr rust +#include_code balance_of_private /yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr rust Call this function using the following code: @@ -273,15 +288,22 @@ Call this function using the following code: Running now should yield output: ``` - private-token Aztec Sandbox Info { version: 1, chainId: 31337 } +0ms - private-token Creating accounts using schnorr signers... +2ms - private-token Created Alice's account at 0x054d89d0...f17e +23s - private-token Created Bob's account at 0x0a8410a1...7c48 +1ms - private-token Deploying private token contract minting an initial 1000000 tokens to Alice... +0ms - private-token Transaction status is mined +8s - private-token Contract successfully deployed at address 0x143e0af4...11b6 +7ms - private-token Alice's balance 1000000 +4s - private-token Bob's balance 0 +3s + token Aztec Sandbox Info { + version: 1, + chainId: 31337, + rollupAddress: EthAddress { + buffer: + }, + client: 'aztec-rpc@0.1.0', + compatibleNargoVersion: '0.11.1-aztec.0' + } + token Creating accounts using schnorr signers... +3ms + token Created Alice's account at 0x1509b252...0027 +10s + token Created Bob's account at 0x031862e8...e7a3 +0ms + token Deploying token contract minting an initial 1000000 tokens to Alice... +1ms + token Contract successfully deployed at address 0x1c3dc2ed...1362 +15s + token Alice's balance 1000000 +9s + token Bob's balance 0 +33ms ``` In this section, we first created 2 instances of the `PrivateTokenContract` contract abstraction. One for each of our deployed accounts. This contract abstraction offers a Typescript interface reflecting the abi of the contract. We then call `getBalance()` as a `view` method. View methods can be thought as read-only. No transaction is submitted as a result but a user's state can be queried. @@ -290,30 +312,39 @@ We can see that each account has the expected balance of tokens. ## Creating and submitting transactions -Now lets transfer some funds from Alice to Bob by calling the `transfer` function on the contract. This function takes 3 arguments: +Now lets transfer some funds from Alice to Bob by calling the `transfer` function on the contract. This function takes 4 arguments: -1. The quantity of tokens to transfer. -2. The sender. -3. The recipient. +1. The sender. +2. The recipient. +3. The quantity of tokens to be transferred. +4. The nonce for the [authentication witness](../../concepts//foundation/accounts/main.md#authorizing-actions), or 0 if msg.sender equal sender. -#include_code transfer /yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr rust +#include_code transfer /yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr rust #include_code Transfer /yarn-project/end-to-end/src/e2e_sandbox_example.test.ts typescript Our output should now look like this: ``` - private-token Aztec Sandbox Info { version: 1, chainId: 31337 } +0ms - private-token Creating accounts using schnorr signers... +2ms - private-token Created Alice's account at 0x054d89d0...f17e +23s - private-token Created Bob's account at 0x0a8410a1...7c48 +1ms - private-token Deploying private token contract minting an initial 1000000 tokens to Alice... +0ms - private-token Contract successfully deployed at address 0x143e0af4...11b6 +7ms - private-token Alice's balance 1000000 +4s - private-token Bob's balance 0 +3s - private-token Transferring 543 tokens from Alice to Bob... +0ms - private-token Alice's balance 999457 +4s - private-token Bob's balance 543 +3s + token Aztec Sandbox Info { + version: 1, + chainId: 31337, + rollupAddress: EthAddress { + buffer: + }, + client: 'aztec-rpc@0.1.0', + compatibleNargoVersion: '0.11.1-aztec.0' + } + token Creating accounts using schnorr signers... +3ms + token Created Alice's account at 0x1509b252...0027 +10s + token Created Bob's account at 0x031862e8...e7a3 +0ms + token Deploying token contract minting an initial 1000000 tokens to Alice... +1ms + token Contract successfully deployed at address 0x1c3dc2ed...1362 +15s + token Alice's balance 1000000 +9s + token Bob's balance 0 +33ms + token Transferring 543 tokens from Alice to Bob... +0ms + token Alice's balance 999457 +5s + token Bob's balance 543 +40ms ``` Here, we used the same contract abstraction as was previously used for reading Alice's balance. But this time we called `send()` generating and sending a transaction to the network. After waiting for the transaction to settle we were able to check the new balance values. @@ -332,20 +363,28 @@ Let's mint some tokens to Bob's account: Our complete output should now be: ``` - private-token Aztec Sandbox Info { version: 1, chainId: 31337 } +0ms - private-token Creating accounts using schnorr signers... +2ms - private-token Created Alice's account at 0x054d89d0...f17e +23s - private-token Created Bob's account at 0x0a8410a1...7c48 +1ms - private-token Deploying private token contract minting an initial 1000000 tokens to Alice... +0ms - private-token Contract successfully deployed at address 0x143e0af4...11b6 +7ms - private-token Alice's balance 1000000 +4s - private-token Bob's balance 0 +3s - private-token Transferring 543 tokens from Alice to Bob... +0ms - private-token Alice's balance 999457 +4s - private-token Bob's balance 543 +3s - private-token Minting 10000 tokens to Bob... +1ms - private-token Alice's balance 999457 +4s - private-token Bob's balance 10543 +4s + token Aztec Sandbox Info { + version: 1, + chainId: 31337, + rollupAddress: EthAddress { + buffer: + }, + client: 'aztec-rpc@0.1.0', + compatibleNargoVersion: '0.11.1-aztec.0' + } + token Creating accounts using schnorr signers... +3ms + token Created Alice's account at 0x1509b252...0027 +10s + token Created Bob's account at 0x031862e8...e7a3 +0ms + token Deploying token contract minting an initial 1000000 tokens to Alice... +1ms + token Contract successfully deployed at address 0x1c3dc2ed...1362 +15s + token Alice's balance 1000000 +9s + token Bob's balance 0 +33ms + token Transferring 543 tokens from Alice to Bob... +0ms + token Alice's balance 999457 +5s + token Bob's balance 543 +40ms + token Minting 10000 tokens to Bob... +0ms + token Alice's balance 999457 +9s + token Bob's balance 10543 +47ms ``` That's it! We have successfully deployed a private token contract to an instance of the Aztec network and mined private state-transitioning transactions. We have also queried the resulting state all via the interfaces provided by the contract. diff --git a/yarn-project/end-to-end/src/e2e_sandbox_example.test.ts b/yarn-project/end-to-end/src/e2e_sandbox_example.test.ts index 0e4058a2015..e2258de2ca8 100644 --- a/yarn-project/end-to-end/src/e2e_sandbox_example.test.ts +++ b/yarn-project/end-to-end/src/e2e_sandbox_example.test.ts @@ -1,39 +1,27 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ - -/* eslint-disable import/no-duplicates */ // docs:start:imports import { AztecRPC, Fr, computeMessageSecretHash, + createAztecRpcClient, createDebugLogger, getSchnorrAccount, waitForSandbox, } from '@aztec/aztec.js'; -// docs:end:imports - -/* eslint-enable @typescript-eslint/no-unused-vars */ -// Note: this is a hack to make the docs use http://localhost:8080 and CI to use the SANDBOX_URL -import { createAztecRpcClient as createAztecRpcClient2 } from '@aztec/aztec.js'; import { GrumpkinScalar } from '@aztec/circuits.js'; import { TokenContract } from '@aztec/noir-contracts/types'; const { SANDBOX_URL = 'http://localhost:8080' } = process.env; +// docs:end:imports describe('e2e_sandbox_example', () => { - // Note: this is a hack to make the docs use http://localhost:8080 and CI to use the SANDBOX_URL - const createAztecRpcClient = (_url: string) => { - return createAztecRpcClient2(SANDBOX_URL!); - }; - it('sandbox example works', async () => { // docs:start:setup ////////////// CREATE THE CLIENT INTERFACE AND CONTACT THE SANDBOX ////////////// const logger = createDebugLogger('token'); - const sandboxUrl = 'http://localhost:8080'; // We create AztecRPC client connected to the sandbox URL - const aztecRpc = createAztecRpcClient(sandboxUrl); + const aztecRpc = createAztecRpcClient(SANDBOX_URL); // Wait for sandbox to be ready await waitForSandbox(aztecRpc); @@ -98,7 +86,6 @@ describe('e2e_sandbox_example', () => { ////////////// DEPLOY OUR TOKEN CONTRACT ////////////// // Deploy a token contract, create a contract abstraction object and link it to the owner's wallet - // The contract's constructor takes 2 arguments, the initial supply and the owner of that initial supply const initialSupply = 1_000_000n; logger(`Deploying token contract minting an initial ${initialSupply} tokens to Alice...`); @@ -107,6 +94,7 @@ describe('e2e_sandbox_example', () => { // Create the contract abstraction and link to Alice's wallet for future signing const tokenContractAlice = await TokenContract.at(contract.address, await accounts[0].getWallet()); + // Initialize the contract and add Bob as a minter await tokenContractAlice.methods._initialize({ address: alice }).send().wait(); await tokenContractAlice.methods.set_minter({ address: bob }, true).send().wait(); @@ -127,7 +115,8 @@ describe('e2e_sandbox_example', () => { ////////////// QUERYING THE TOKEN BALANCE FOR EACH ACCOUNT ////////////// // Bob wants to mint some funds, the contract is already deployed, create an abstraction and link it his wallet - const tokenContractBob = await TokenContract.at(contract.address, await accounts[1].getWallet()); + // Since we already have a token link, we can simply create a new instance of the contract linked to Bob's wallet + const tokenContractBob = tokenContractAlice.withWallet(await accounts[1].getWallet()); let aliceBalance = await tokenContractAlice.methods.balance_of_private({ address: alice }).view(); logger(`Alice's balance ${aliceBalance}`); diff --git a/yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr index e03c3fa613c..5bf49580ed8 100644 --- a/yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr @@ -65,7 +65,6 @@ contract PrivateToken { } // docs:end:mint - // docs:start:transfer // Transfers `amount` of tokens from msg_sender to a `recipient`. #[aztec(private)] fn transfer( @@ -83,9 +82,7 @@ contract PrivateToken { let recipient_balance = storage.balances.at(recipient); increment(recipient_balance, amount, recipient); } - // docs:end:transfer - // docs:start:getBalance // Helper function to get the balance of a user ("unconstrained" is a Noir alternative of Solidity's "view" function). unconstrained fn getBalance( owner: Field, @@ -98,7 +95,6 @@ contract PrivateToken { // Return the sum of all notes in the set. balance_utils::get_balance(owner_balance) } - // docs:end:getBalance // Computes note hash and nullifier. // Note 1: Needs to be defined by every contract producing logs. diff --git a/yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr index adfae41aab7..8edb9dec257 100644 --- a/yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr @@ -180,6 +180,7 @@ contract Token { 1 } + #[aztec(public)] fn transfer_public( from: AztecAddress, @@ -276,6 +277,7 @@ contract Token { 1 } + // docs:start:transfer #[aztec(private)] fn transfer( from: AztecAddress, @@ -301,6 +303,7 @@ contract Token { 1 } + // docs:end:transfer #[aztec(private)] fn burn( @@ -383,6 +386,7 @@ contract Token { storage.total_supply.read() } + // docs:start:balance_of_private unconstrained fn balance_of_private( owner: AztecAddress, ) -> Field { @@ -391,6 +395,7 @@ contract Token { balance_utils::get_balance(owner_balance) } + // docs:end:balance_of_private unconstrained fn balance_of_public( owner: AztecAddress,