diff --git a/.circleci/config.yml b/.circleci/config.yml index 32d9ccd7cfe..2abd7304e4f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -631,18 +631,6 @@ jobs: command: cond_run_script end-to-end ./scripts/run_tests_local e2e_private_airdrop.test.ts environment: { DEBUG: "aztec:*" } - e2e-private-token-contract: - machine: - image: ubuntu-2204:2023.07.2 - resource_class: large - steps: - - *checkout - - *setup_env - - run: - name: "Test" - command: cond_run_script end-to-end ./scripts/run_tests_local e2e_private_token_contract.test.ts - environment: { DEBUG: "aztec:*" } - e2e-sandbox-example: machine: image: ubuntu-2204:2023.07.2 @@ -1355,7 +1343,6 @@ workflows: - e2e-lending-contract: *e2e_test - e2e-token-contract: *e2e_test - e2e-private-airdrop: *e2e_test - - e2e-private-token-contract: *e2e_test - e2e-sandbox-example: *e2e_test - e2e-multi-transfer-contract: *e2e_test - e2e-block-building: *e2e_test @@ -1394,7 +1381,6 @@ workflows: - e2e-lending-contract - e2e-token-contract - e2e-private-airdrop - - e2e-private-token-contract - e2e-sandbox-example - e2e-multi-transfer-contract - e2e-block-building diff --git a/bootstrap_docker.sh b/bootstrap_docker.sh index 45dbc3fe70d..bbc081d0b59 100755 --- a/bootstrap_docker.sh +++ b/bootstrap_docker.sh @@ -36,5 +36,5 @@ build_local $PROJECT_NAME if [ -z "$PROJECT_NAME" ]; then echo echo "Success! You could now run e.g.:" - echo " docker run -ti --rm aztecprotocol/end-to-end:latest e2e_private_token_contract.test" + echo " docker run -ti --rm aztecprotocol/end-to-end:latest e2e_token_contract.test" fi diff --git a/docs/docs/dev_docs/cli/main.md b/docs/docs/dev_docs/cli/main.md index 8abcec843d5..d74ff15b1cd 100644 --- a/docs/docs/dev_docs/cli/main.md +++ b/docs/docs/dev_docs/cli/main.md @@ -87,7 +87,8 @@ export ADDRESS2= ## Deploying a Token Contract -We will now deploy the private token contract using the `deploy` command, minting 1000000 initial tokens to address `0x175310d40cd3412477db1c2a2188efd586b63d6830115fbb46c592a6303dbf6c`. Make sure to replace this address with one of the two you created earlier. +We will now deploy a token contract using the `deploy` command, and set an address of the admin via a constructor argument. +Make sure to replace this address with one of the two you created earlier. #include_code deploy yarn-project/end-to-end/src/cli_docs_sandbox.test.ts bash @@ -97,52 +98,41 @@ Save the contract address as an environment variable. We will use it later. export CONTRACT_ADDRESS= ``` -:::info -If you use a different address in the constructor above, you will get an error when running the deployment. This is because you need to register an account in the sandbox before it can receive private notes. When you create a new account, it gets automatically registered. Alternatively, you can register an account you do not own along with its public key using the `register-recipient` command. -::: - -This command takes 1 mandatory positional argument which is the path to the contract artifact file in a JSON format (e.g. `contracts/target/PrivateToken.json`). -Alternatively you can pass the name of an example contract as exported by `@aztec/noir-contracts` (run `aztec-cli example-contracts` to see the full list of contracts available). - -The command takes a few optional arguments while the most important one is: - -- `--args` - Arguments to the constructor of the contract. In this case we have minted 1000000 initial tokens to the aztec address 0x20d3321707d53cebb168568e25c5c62a853ae1f0766d965e00d6f6c4eb05d599. +- `--args` - Arguments to the constructor of the contract. In this case we have set an address as admin. The CLI tells us that the contract was successfully deployed. We can use the `check-deploy` command to verify that a contract has been successfully deployed to that address: #include_code check-deploy yarn-project/end-to-end/src/cli_docs_sandbox.test.ts bash -## Calling a View Method - -When we deployed the token contract, an initial supply of tokens was minted to the address provided in the constructor. We can now query the `getBalance()` method on the contract to retrieve the balance of that address. Make sure to replace the `contract-address` with the deployment address you got from the previous command, and the `args` with the account you used in the constructor. - -#include_code call yarn-project/end-to-end/src/cli_docs_sandbox.test.ts bash - -The `call` command calls a read-only method on a contract, one that will not generate a transaction to be sent to the network. The arguments here are: - -- `--args` - The address for which we want to retrieve the balance. -- `--contract-artifact` - The artifact of the contract we are calling. -- `--contract-address` - The address of the deployed contract - -As you can see from the result, this address has a balance of 1000000, as expected. When using the Sandbox, you are able to query the balance of any account that has been created in the system, even the accounts created by default. You may wonder why this is, as you haven't provided the private keys for these accounts. The Sandbox contains a component known as the Private Execution Environment (PXE). When an account is created, this component stores the provided encryption private key and is able to read the account's private state meaning that the Sandbox can report the balance of any of it's accounts. More information about the account model can be found [here](../../concepts/foundation/accounts/main.md). - ## Sending a Transaction -We can now send a transaction to the network. We will transfer funds from the owner of the initial minted tokens to our other account. For this we will use the `send` command, which expects as arguments the quantity of tokens to be transferred, the sender's address, and the recipient's address. Make sure to replace all addresses in this command with the ones for your run. - -#include_code send yarn-project/end-to-end/src/cli_docs_sandbox.test.ts bash - -We called the `transfer` function of the contract and provided these arguments: +We can now send a transaction to the network. We will mint funds in the public domain. +To form and submit the transaction we will use the `send` command of `aztec-cli`. +The `send` command expect the function name as the first unnamed argument and the following named arguments: - `--args` - The list of arguments to the function call. - `--contract-artifact` - The artifact of the contract to call. - `--contract-address` - The deployed address of the contract to call. -- `--private-key` - The private key of the sender +- `--private-key` - The private key of the sender. + +#include_code send yarn-project/end-to-end/src/cli_docs_sandbox.test.ts bash + +We called the `mint_public` function and provided it with the 2 arguments it expects: the recipient's address and the amount to be minted. Make sure to replace all addresses in this command with yours. The command output tells us the details of the transaction such as its hash and status. We can use this hash to query the receipt of the transaction at a later time: #include_code get-tx-receipt yarn-project/end-to-end/src/cli_docs_sandbox.test.ts bash -Let's now call `getBalance()` on each of our accounts and we should see updated values: +## Calling an Unconstrained (View) Function + +Now that the `mint_public` tx has been settled we can call the `balance_of_public` unconstrained function: + +#include_code call yarn-project/end-to-end/src/cli_docs_sandbox.test.ts bash + +The `call` command calls a read-only method on a contract, one that will not generate a transaction to be sent to the network. The arguments here are: + +- `--args` - The address for which we want to retrieve the balance. +- `--contract-artifact` - The artifact of the contract we are calling. +- `--contract-address` - The address of the deployed contract -#include_code calls yarn-project/end-to-end/src/cli_docs_sandbox.test.ts bash +As you can see from the result, this address has a public balance of 543, as expected. \ No newline at end of file diff --git a/docs/docs/dev_docs/contracts/compiling.md b/docs/docs/dev_docs/contracts/compiling.md index 4751ff03738..78a871a76c1 100644 --- a/docs/docs/dev_docs/contracts/compiling.md +++ b/docs/docs/dev_docs/contracts/compiling.md @@ -46,27 +46,85 @@ You can also generate these interfaces from prebuilt artifacts using the `genera aztec-cli generate-typescript ./path/to/my_aztec_contract_project ``` -Example code generated from the [PrivateToken](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr) contract: +Below is typescript code generated from the [Token](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr) contract: ```ts showLineNumbers -export class PrivateTokenContract extends ContractBase { - /** Creates a contract instance at the given address. */ - public static async at(address: AztecAddress, wallet: Wallet) { ... } +export class TokenContract extends ContractBase { + private constructor(completeAddress: CompleteAddress, wallet: Wallet, portalContract = EthAddress.ZERO) { + super(completeAddress, TokenContractArtifact, wallet, portalContract); + } + + /** + * Creates a contract instance. + * @param address - The deployed contract's address. + * @param wallet - The wallet to use when interacting with the contract. + * @returns A promise that resolves to a new Contract instance. + */ + public static async at(address: AztecAddress, wallet: Wallet) { + return Contract.at(address, TokenContract.artifact, wallet) as Promise; + } + + /** + * Creates a tx to deploy a new instance of this contract. + */ + public static deploy(pxe: PXE, admin: AztecAddressLike) { + return new DeployMethod(Point.ZERO, pxe, TokenContractArtifact, Array.from(arguments).slice(1)); + } + + /** + * Creates a tx to deploy a new instance of this contract using the specified public key to derive the address. + */ + public static deployWithPublicKey(pxe: PXE, publicKey: PublicKey, admin: AztecAddressLike) { + return new DeployMethod(publicKey, pxe, TokenContractArtifact, Array.from(arguments).slice(2)); + } - /** Creates a tx to deploy a new instance of this contract. */ - public static deploy(pxe: PXE, initial_supply: FieldLike, owner: FieldLike) { ... } + /** + * Returns this contract's artifact. + */ + public static get artifact(): ContractArtifact { + return TokenContractArtifact; + } /** Type-safe wrappers for the public methods exposed by the contract. */ public methods!: { - /** getBalance(owner: field) */ - getBalance: ((owner: FieldLike) => ContractFunctionInteraction) & Pick; - /** mint(amount: field, owner: field) */ - mint: ((amount: FieldLike, owner: FieldLike) => ContractFunctionInteraction) & Pick; + /** balance_of_private(owner: struct) */ + balance_of_private: ((owner: AztecAddressLike) => ContractFunctionInteraction) & Pick; + + /** balance_of_public(owner: struct) */ + balance_of_public: ((owner: AztecAddressLike) => ContractFunctionInteraction) & Pick; + + /** shield(from: struct, amount: field, secret_hash: field, nonce: field) */ + shield: (( + from: AztecAddressLike, + amount: FieldLike, + secret_hash: FieldLike, + nonce: FieldLike, + ) => ContractFunctionInteraction) & + Pick; + + /** total_supply() */ + total_supply: (() => ContractFunctionInteraction) & Pick; + + /** transfer(from: struct, to: struct, amount: field, nonce: field) */ + transfer: (( + from: AztecAddressLike, + to: AztecAddressLike, + amount: FieldLike, + nonce: FieldLike, + ) => ContractFunctionInteraction) & + Pick; - /** transfer(amount: field, sender: field, recipient: field) */ - transfer: ((amount: FieldLike, sender: FieldLike, recipient: FieldLike) => ContractFunctionInteraction) & + /** transfer_public(from: struct, to: struct, amount: field, nonce: field) */ + transfer_public: (( + from: AztecAddressLike, + to: AztecAddressLike, + amount: FieldLike, + nonce: FieldLike, + ) => ContractFunctionInteraction) & Pick; + + ... }; } ``` @@ -91,37 +149,87 @@ You can also generate these interfaces from prebuilt artifacts using the `genera aztec-cli generate-noir-interface ./path/to/my_aztec_contract_project ``` -Example code generated from the [PrivateToken](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr) contract: +Below is an example interface, also generated from the [Token](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr) contract: ```rust -impl PrivateTokenPrivateContextInterface { - fn at(address: Field) -> Self { - Self { address } +impl TokenPrivateContextInterface { + pub fn at(address: Field) -> Self { + Self { + address, + } } - - fn mint( - self, context: &mut PrivateContext, amount: Field, owner: Field + + pub fn burn( + self, + context: &mut PrivateContext, + from: FromBurnStruct, + amount: Field, + nonce: Field ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 2]; - serialized_args[0] = amount; - serialized_args[1] = owner; + let mut serialized_args = [0; 3]; + serialized_args[0] = from.address; + serialized_args[1] = amount; + serialized_args[2] = nonce; - // 0x1dc9c3c0 is the function selector for `mint(field,field)` - context.call_private_function(self.address, 0x1dc9c3c0, serialized_args) + context.call_private_function(self.address, 0xd4fcc96e, serialized_args) } + + + pub fn burn_public( + self, + context: &mut PrivateContext, + from: FromBurnPublicStruct, + amount: Field, + nonce: Field + ) { + let mut serialized_args = [0; 3]; + serialized_args[0] = from.address; + serialized_args[1] = amount; + serialized_args[2] = nonce; + context.call_public_function(self.address, 0xb0e964d5, serialized_args) + } + ... + +} - fn transfer( - self, context: &mut PrivateContext, amount: Field, sender: Field, recipient: Field +impl TokenPublicContextInterface { + pub fn at(address: Field) -> Self { + Self { + address, + } + } + + pub fn burn_public( + self, + context: PublicContext, + from: FromBurnPublicStruct, + amount: Field, + nonce: Field ) -> [Field; RETURN_VALUES_LENGTH] { let mut serialized_args = [0; 3]; + serialized_args[0] = from.address; + serialized_args[1] = amount; + serialized_args[2] = nonce; + + context.call_public_function(self.address, 0xb0e964d5, serialized_args) + } + + + pub fn mint_private( + self, + context: PublicContext, + amount: Field, + secret_hash: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialized_args = [0; 2]; serialized_args[0] = amount; - serialized_args[1] = sender; - serialized_args[2] = recipient; + serialized_args[1] = secret_hash; - // 0xdcd4c318 is the function selector for `transfer(field,field,field)` - context.call_private_function(self.address, 0xdcd4c318, serialized_args) + context.call_public_function(self.address, 0x10763932, serialized_args) } + + } ``` diff --git a/docs/docs/dev_docs/contracts/deploying.md b/docs/docs/dev_docs/contracts/deploying.md index 303196229e1..07c0f00586c 100644 --- a/docs/docs/dev_docs/contracts/deploying.md +++ b/docs/docs/dev_docs/contracts/deploying.md @@ -87,58 +87,26 @@ const tx = ExampleContract.deploy(pxe).send({ -### Deploying private token contract -To give you a more complete example we will deploy the `PrivateToken` contract whose artifacts are included in the `@aztec/noir-contracts` package. +### Deploying token contract +To give you a more complete example we will deploy a `Token` contract whose artifacts are included in the `@aztec/noir-contracts` package. -The contract has `initial_supply` and `owner` as constructor arguments. -Because the contract sends a note to the owner specified inside the constructor, we need their public key to encrypt the note with. For this, we first need to register the owner as a recipient inside the PXE with the following command: +The contract has `admin` as a constructor argument. +We will deploy the contract with the `aztec-cli` and pass the `admin` address as an argument. ```bash -aztec-cli register-recipient --address 0x147392a39e593189902458f4303bc6e0a39128c5a1c1612f76527a162d36d529 --public-key 0x26e193aef4f83c70651485b5526c6d01a36d763223ab24efd1f9ff91b394ac0c20ad99d0ef669dc0dde8d5f5996c63105de8e15c2c87d8260b9e6f02f72af622 --partial-address 0x200e9a6c2d2e8352012e51c6637659713d336405c29386c7c4ac56779ab54fa7 +aztec-cli deploy TokenContractArtifact --args 0x147392a39e593189902458f4303bc6e0a39128c5a1c1612f76527a162d36d529 ``` ```ts -const aztecAddress = AztecAddress.fromString("0x147392a39e593189902458f4303bc6e0a39128c5a1c1612f76527a162d36d529"); -const publicKey = Point.fromString("0x26e193aef4f83c70651485b5526c6d01a36d763223ab24efd1f9ff91b394ac0c20ad99d0ef669dc0dde8d5f5996c63105de8e15c2c87d8260b9e6f02f72af622"); -const partialAddress = Fr.fromString("0x200e9a6c2d2e8352012e51c6637659713d336405c29386c7c4ac56779ab54fa7"); - -const completeAddress = CompleteAddress.create(aztecAddress, publicKey, partialKey); -await pxe.registerRecipient(completeAddress); -``` - - - - - -When you create a new account, it gets automatically registered. It can be verified by calling `aztec-cli get-accounts` OR in aztec.js by using `await pxe.getRegisteredAccounts()` - -> **NOTE 1**: If we didn't register owner as a recipient we could not encrypt a note for the owner and the contract deployment would fail because constructor execution would fail (we need owner's public key to encrypt a note). - -> **NOTE 2**: If a note recipient is one of the accounts inside the PXE, we don't need to register it as a recipient because we already have the public key available. - -Once the recipient is registered we can deploy the contract: - - - - -```bash -aztec-cli deploy PrivateTokenContractArtifact --args 1000 0x147392a39e593189902458f4303bc6e0a39128c5a1c1612f76527a162d36d529 -``` - - - - -```ts -// PrivateTokenContract is the TS interface that is automatically generated when compiling the contract with the `-ts` flag. -const initialBalance = 1000n; -const owner = AztecAddress.from("0x147392a39e593189902458f4303bc6e0a39128c5a1c1612f76527a162d36d529"); -const contract = await PrivateTokenContract.deploy(wallet, initialBalance, owner).send().deployed(); +const admin = AztecAddress.from("0x147392a39e593189902458f4303bc6e0a39128c5a1c1612f76527a162d36d529"); +// TokenContract is the TS interface that is automatically generated when compiling the contract with the `-ts` flag. +const contract = await TokenContract.deploy(wallet, admin).send().deployed(); logger(`Contract deployed at ${contract.address}`); ``` @@ -154,14 +122,14 @@ If we pass the salt as an argument: ```bash -aztec-cli deploy PrivateTokenContractArtifact --args 1000 0x147392a39e593189902458f4303bc6e0a39128c5a1c1612f76527a162d36d529 --salt 0x123 +aztec-cli deploy TokenContractArtifact --args 0x147392a39e593189902458f4303bc6e0a39128c5a1c1612f76527a162d36d529 --salt 0x123 ``` ```ts -const contract = await PrivateTokenContract.deploy(wallet, initialBalance, owner).send({ contractAddressSalt: Fr.fromString("0x123") }).deployed(); +const contract = await TokenContract.deploy(wallet, admin).send({ contractAddressSalt: Fr.fromString("0x123") }).deployed(); ``` diff --git a/docs/docs/dev_docs/getting_started/sandbox.md b/docs/docs/dev_docs/getting_started/sandbox.md index 6d4e2bf1eca..43acfa7f924 100644 --- a/docs/docs/dev_docs/getting_started/sandbox.md +++ b/docs/docs/dev_docs/getting_started/sandbox.md @@ -12,7 +12,7 @@ The Aztec Sandbox aims to provide a local development system against which you c For a quickstart checkout [the Quickstart section](./quickstart.md) ::: -Here we will walkthrough the process of retrieving the Sandbox, installing the client libraries and using it to deploy and use a fully private token contract on the Aztec network using Aztec.js. +Here we will walkthrough the process of retrieving the Sandbox, installing the client libraries and using it to deploy and use a fully token contract on the Aztec network using Aztec.js. You can find the [complete tutorial code here](https://github.com/AztecProtocol/dev-rel/tree/main/tutorials/sandbox-tutorial/token). @@ -46,6 +46,16 @@ Within a few seconds the Sandbox should be up and running! +:::info +To start anvil in a fork mode set `FORK_URL` and `FORK_BLOCK_NUMBER` environment variables before running the script. +You can do so by running: +```sh +export FORK_URL=https://mainnet.infura.io/v3/your-infura-key +export FORK_BLOCK_NUMBER=13300000 +``` +If `FORK_BLOCK_NUMBER` is not set, it defaults to genesis block number. +::: + ## Project setup We will deploy a pre-compiled token contract, and send tokens privately, using the Sandbox. @@ -118,7 +128,7 @@ yarn add @aztec/aztec.js @aztec/noir-contracts typescript @types/node "version": "1.0.0", "description": "My first token contract", "main": "index.js", - "author": "Phil", + "author": "1000x Dev", "license": "MIT", "type": "module", "scripts": { @@ -136,13 +146,7 @@ yarn add @aztec/aztec.js @aztec/noir-contracts typescript @types/node } ``` -7. Next, install Aztec related dependencies - -```sh -yarn add @aztec/aztec.js @aztec/noir-contracts -``` - -8. Create an `index.ts` file in the `src` directory with the following sandbox connection setup: +7. Create an `index.ts` file in the `src` directory with the following sandbox connection setup: ```ts #include_code imports /yarn-project/end-to-end/src/e2e_sandbox_example.test.ts raw @@ -154,7 +158,7 @@ async function main() { main(); ``` -9. Finally, run the package: +8. Finally, run the package: In the project root, run @@ -166,14 +170,31 @@ A successful run should show: ``` token Aztec Sandbox Info { - version: 1, - chainId: 31337, - rollupAddress: EthAddress { - buffer: - }, - client: 'pxe@0.1.0', - compatibleNargoVersion: '0.11.1-aztec.0' - } + token sandboxVersion: '#include_aztec_short_version', + token compatibleNargoVersion: '#include_noir_version', + token chainId: 31337, + token protocolVersion: 1, + token l1ContractAddresses: { + token rollupAddress: EthAddress { + token buffer: + token }, + token registryAddress: EthAddress { + token buffer: + token }, + token inboxAddress: EthAddress { + token buffer: + token }, + token outboxAddress: EthAddress { + token buffer: + token }, + token contractDeploymentEmitterAddress: EthAddress { + token buffer: + token }, + token decoderHelperAddress: EthAddress { + token buffer: + token } + token } + token } +0ms ``` Great! The Sandbox is running and we are able to interact with it. @@ -200,19 +221,37 @@ Now that we have our accounts loaded, let's move on to deploy our pre-compiled t ``` token Aztec Sandbox Info { - version: 1, - chainId: 31337, - rollupAddress: EthAddress { - buffer: - }, - client: 'pxe@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 sandboxVersion: '#include_aztec_short_version', + token compatibleNargoVersion: '#include_noir_version', + token chainId: 31337, + token protocolVersion: 1, + token l1ContractAddresses: { + token rollupAddress: EthAddress { + token buffer: + token }, + token registryAddress: EthAddress { + token buffer: + token }, + token inboxAddress: EthAddress { + token buffer: + token }, + token outboxAddress: EthAddress { + token buffer: + token }, + token contractDeploymentEmitterAddress: EthAddress { + token buffer: + token }, + token decoderHelperAddress: EthAddress { + token buffer: + token } + token } + token } +0ms + token Loaded alice's account at 0x25048e8c...70d0 +4s + token Loaded bob's account at 0x115f123b...6483 +0ms + token Deploying token contract... +0ms + token Contract successfully deployed at address 0x11a03dce...afc7 +5s + token Minting tokens to Alice... +18ms + token 1000000 tokens were successfully minted and redeemed by Alice +10s ``` We can break this down as follows: @@ -222,15 +261,14 @@ We can break this down as follows: 3. We retrieve the transaction receipt containing the transaction status and contract address. 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 1,000,000 tokens to be claimed by herself in private. -8. Alice claims the tokens privately. +6. Alice mints 1,000,000 tokens to be claimed by herself in private. +7. Alice redeems 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 `balance_of_private()` function to retrieve the balances of the accounts. -Here is the `balance_of_private` code from the contract (no need to paste it into `index.ts`): +Here is the `balance_of_private` code from the contract (do not to paste it into `index.ts`): #include_code balance_of_private /yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr rust @@ -242,24 +280,47 @@ Running now should yield output: ``` token Aztec Sandbox Info { - version: 1, - chainId: 31337, - rollupAddress: EthAddress { - buffer: - }, - client: 'pxe@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 sandboxVersion: '#include_aztec_short_version', + token compatibleNargoVersion: '#include_noir_version', + token chainId: 31337, + token protocolVersion: 1, + token l1ContractAddresses: { + token rollupAddress: EthAddress { + token buffer: + token }, + token registryAddress: EthAddress { + token buffer: + token }, + token inboxAddress: EthAddress { + token buffer: + token }, + token outboxAddress: EthAddress { + token buffer: + token }, + token contractDeploymentEmitterAddress: EthAddress { + token buffer: + token }, + token decoderHelperAddress: EthAddress { + token buffer: + token } + token } + token } +0ms + token Loaded alice's account at 0x25048e8c...70d0 +4s + token Loaded bob's account at 0x115f123b...6483 +0ms + token Deploying token contract... +0ms + token Contract successfully deployed at address 0x1b388d99...4b55 +4s + token Minting tokens to Alice... +10ms + token 1000000 tokens were successfully minted and redeemed by Alice +10s + token Alice's balance 1000000 +80ms + token Bob's balance 0 +31ms ``` -In this section, we created 2 instances of the `TokenContract` 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. +Above, we created a second instance of the `TokenContract` contract class. +This time pertaining to Bob. +This class offers a TypeScript bindings of our `Token` contract.. +We then call `balance_of_private()` 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. We can see that each account has the expected balance of tokens. @@ -288,38 +349,68 @@ Our output should now look like this: ``` token Aztec Sandbox Info { - version: 1, - chainId: 31337, - rollupAddress: EthAddress { - buffer: - }, - client: 'pxe@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 sandboxVersion: '#include_aztec_short_version', + token compatibleNargoVersion: '#include_noir_version', + token chainId: 31337, + token protocolVersion: 1, + token l1ContractAddresses: { + token rollupAddress: EthAddress { + token buffer: + token }, + token registryAddress: EthAddress { + token buffer: + token }, + token inboxAddress: EthAddress { + token buffer: + token }, + token outboxAddress: EthAddress { + token buffer: + token }, + token contractDeploymentEmitterAddress: EthAddress { + token buffer: + token }, + token decoderHelperAddress: EthAddress { + token buffer: + token } + token } + token } +0ms + token Loaded alice's account at 0x25048e8c...70d0 +4s + token Loaded bob's account at 0x115f123b...6483 +0ms + token Deploying token contract... +0ms + token Contract successfully deployed at address 0x01d8af7d...9a4d +5s + token Minting tokens to Alice... +18ms + token 1000000 tokens were successfully minted and redeemed by Alice +11s + token Alice's balance 1000000 +59ms 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 Alice's balance 999457 +6s + token Bob's balance 543 +39ms ``` 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. -Finally, the contract has a `mint` function that can be used to generate new tokens for an account. This takes 2 arguments: +Finally, the contract has 2 `mint` functions that can be used to generate new tokens for an account. +We will focus only on `mint_private`. +This function is public but it mints tokens privately. +This function takes: -1. The quantity of tokens to be minted. -2. The recipient of the new tokens. +1. A quantity of tokens to be minted. +2. A secret hash. Here is the Noir code: -#include_code mint /yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr rust +#include_code mint_private /yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr rust + +This function is public and it inserts a new note into the private data tree and increases the total token supply by the amount minted. -Let's mint some tokens to Bob's account using Typescript, add this to `index.ts`: +To make the note spendable the note has to be redeemed. +A user can do that by calling the `redeem_shield` function: + +#include_code redeem_shield /yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr rust + +Notice that this function is private and that it takes a secret as an input argument. + +Let's now use these functions to mint some tokens to Bob's account using Typescript, add this to `index.ts`: #include_code Mint /yarn-project/end-to-end/src/e2e_sandbox_example.test.ts typescript @@ -327,30 +418,48 @@ Our complete output should now be: ``` token Aztec Sandbox Info { - version: 1, - chainId: 31337, - rollupAddress: EthAddress { - buffer: - }, - client: 'pxe@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 sandboxVersion: '#include_aztec_short_version', + token compatibleNargoVersion: '#include_noir_version', + token chainId: 31337, + token protocolVersion: 1, + token l1ContractAddresses: { + token rollupAddress: EthAddress { + token buffer: + token }, + token registryAddress: EthAddress { + token buffer: + token }, + token inboxAddress: EthAddress { + token buffer: + token }, + token outboxAddress: EthAddress { + token buffer: + token }, + token contractDeploymentEmitterAddress: EthAddress { + token buffer: + token }, + token decoderHelperAddress: EthAddress { + token buffer: + token } + token } + token } +0ms + token Loaded alice's account at 0x25048e8c...70d0 +4s + token Loaded bob's account at 0x115f123b...6483 +0ms + token Deploying token contract... +0ms + token Contract successfully deployed at address 0x03a0bb2c...02c2 +7s + token Minting tokens to Alice... +19ms + token 1000000 tokens were successfully minted and redeemed by Alice +9s + token Alice's balance 1000000 +43ms + token Bob's balance 0 +31ms 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 +6s + token Bob's balance 543 +36ms + token Minting 10000 tokens to Bob... +5s token Alice's balance 999457 +9s - token Bob's balance 10543 +47ms + token Bob's balance 10543 +43ms ``` -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. +That's it! We have successfully deployed a 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. You can find the [complete tutorial code here](https://github.com/AztecProtocol/dev-rel/tree/main/tutorials/sandbox-tutorial/token). diff --git a/docs/docs/dev_docs/tutorials/testing.md b/docs/docs/dev_docs/tutorials/testing.md index 5a5780de271..bc6275ea0ee 100644 --- a/docs/docs/dev_docs/tutorials/testing.md +++ b/docs/docs/dev_docs/tutorials/testing.md @@ -12,48 +12,65 @@ Let's start with a simple example for a test using the [Sandbox](../getting_star #include_code sandbox-example /yarn-project/end-to-end/src/guides/dapp_testing.test.ts typescript -This test sets up the environment by creating a client to the Private Execution Environment (PXE) running on the Sandbox on port 8080. It then creates two new accounts, dubbed `owner` and `recipient`. Last, it deploys an instance of the [`PrivateTokenContract`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr), minting an initial 100 tokens to the owner. +This test sets up the environment by creating a client to the Private Execution Environment (PXE) running on the Sandbox on port 8080. It then creates two new accounts, dubbed `owner` and `recipient`. Last, it deploys an instance of the [Token contract](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr), minting an initial 100 tokens to the owner. Once we have this setup, the test itself is simple. We check the balance of the `recipient` user to ensure it has no tokens, send and await a deployment transaction, and then check the balance again to ensure it was increased. Note that all numeric values are represented as [native bigints](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) to avoid loss of precision. :::info -We are using the `PrivateTokenContract` [typescript interface](../contracts/compiling.md#typescript-interfaces) to get type-safe methods for deploying and interacting with the token contract. +We are using the `Token` contract's typescript interface. Follow the [typescript interface section](../contracts/compiling.md#typescript-interfaces) to get type-safe methods for deploying and interacting with the token contract. ::: To run the test, first make sure the Sandbox is running on port 8080, and then [run your tests using jest](https://jestjs.io/docs/getting-started#running-from-command-line). Your test should pass, and you should see the following output in the Sandbox logs, where each chunk corresponds to a transaction. Note how this test run has a total of four transactions: two for deploying the account contracts for the `owner` and `recipient`, another for deploying the token contract, and a last one for actually executing the transfer. ```text -pxe_service Registered account 0x2efa51d2e67581aef4578e8cc647a1af2e3f40e9872deeda0919e5f77cb8b2d2 -pxe_service Added contract SchnorrAccount at 0x2efa51d2e67581aef4578e8cc647a1af2e3f40e9872deeda0919e5f77cb8b2d2 -node Simulating tx 19bfe4fb2569be2168f01eefe5e5a4284d6c1678f17ab5e94c6ba9c811bcb214 -node Simulated tx 19bfe4fb2569be2168f01eefe5e5a4284d6c1678f17ab5e94c6ba9c811bcb214 succeeds -pxe_service Executed local simulation for 19bfe4fb2569be2168f01eefe5e5a4284d6c1678f17ab5e94c6ba9c811bcb214 -pxe_service Sending transaction 19bfe4fb2569be2168f01eefe5e5a4284d6c1678f17ab5e94c6ba9c811bcb214 -node Received tx 19bfe4fb2569be2168f01eefe5e5a4284d6c1678f17ab5e94c6ba9c811bcb214 +pxe_service Registered account 0x061c94e053745973521de1826db5a1ee24af60a3c203c294570a35bd5afa3286 +pxe_service Added contract SchnorrAccount at 0x061c94e053745973521de1826db5a1ee24af60a3c203c294570a35bd5afa3286 +node Simulating tx 09023befa12d01db063235ef6d611ed1c7ba4625dac9d14cc0f1ff4dd8a00264 +node Simulated tx 09023befa12d01db063235ef6d611ed1c7ba4625dac9d14cc0f1ff4dd8a00264 succeeds +pxe_service Executed local simulation for 09023befa12d01db063235ef6d611ed1c7ba4625dac9d14cc0f1ff4dd8a00264 +pxe_service Sending transaction 09023befa12d01db063235ef6d611ed1c7ba4625dac9d14cc0f1ff4dd8a00264 +node Received tx 09023befa12d01db063235ef6d611ed1c7ba4625dac9d14cc0f1ff4dd8a00264 +sequencer Retrieved 1 txs from P2P pool +sequencer Building block 1 with 1 transactions +sequencer Submitted rollup block 1 with 1 transactions + +pxe_service Registered account 0x109e72d06371d98cef1a10ec137e01139edb64b3b399c1b761d12d06de15af3c +pxe_service Added contract SchnorrAccount at 0x109e72d06371d98cef1a10ec137e01139edb64b3b399c1b761d12d06de15af3c +node Simulating tx 179d347ff45fc8da90155c38c7fd4358737fc7447b1f2ea2b3ff1fbc9dfb3d47 +node Simulated tx 179d347ff45fc8da90155c38c7fd4358737fc7447b1f2ea2b3ff1fbc9dfb3d47 succeeds +pxe_service Executed local simulation for 179d347ff45fc8da90155c38c7fd4358737fc7447b1f2ea2b3ff1fbc9dfb3d47 +pxe_service Sending transaction 179d347ff45fc8da90155c38c7fd4358737fc7447b1f2ea2b3ff1fbc9dfb3d47 +node Received tx 179d347ff45fc8da90155c38c7fd4358737fc7447b1f2ea2b3ff1fbc9dfb3d47 +sequencer Retrieved 1 txs from P2P pool +sequencer Building block 2 with 1 transactions sequencer Submitted rollup block 2 with 1 transactions -pxe_service Registered account 0x12ef7ceb5064da3a729f598a6a50585059794fdcf347a6fc9bb317002162e3db -pxe_service Added contract SchnorrAccount at 0x12ef7ceb5064da3a729f598a6a50585059794fdcf347a6fc9bb317002162e3db -node Simulating tx 0f195f8f6fb8fe29cf8159c5c664c1288788f1151a5413ec0e35cf378de74794 -node Simulated tx 0f195f8f6fb8fe29cf8159c5c664c1288788f1151a5413ec0e35cf378de74794 succeeds -pxe_service Executed local simulation for 0f195f8f6fb8fe29cf8159c5c664c1288788f1151a5413ec0e35cf378de74794 -pxe_service Sending transaction 0f195f8f6fb8fe29cf8159c5c664c1288788f1151a5413ec0e35cf378de74794 -node Received tx 0f195f8f6fb8fe29cf8159c5c664c1288788f1151a5413ec0e35cf378de74794 +pxe_service Added contract Token at 0x1345499a3f8325d614fa1d49cc2a6f21211d608c74728439076943f92340b936 +node Simulating tx 0298295963669a4a1ccaddb40d78722c00136aad196b85306d63e4885799b1d8 +node Simulated tx 0298295963669a4a1ccaddb40d78722c00136aad196b85306d63e4885799b1d8 succeeds +pxe_service Executed local simulation for 0298295963669a4a1ccaddb40d78722c00136aad196b85306d63e4885799b1d8 +pxe_service Sending transaction 0298295963669a4a1ccaddb40d78722c00136aad196b85306d63e4885799b1d8 +node Received tx 0298295963669a4a1ccaddb40d78722c00136aad196b85306d63e4885799b1d8 +sequencer Retrieved 1 txs from P2P pool +sequencer Building block 3 with 1 transactions sequencer Submitted rollup block 3 with 1 transactions -pxe_service Added contract PrivateToken at 0x24e691d8bde970ab9e84fe669ea5ac8019c32c199a55aaa8a3e704db763af88f -node Simulating tx 0101e1a3d73c3a112a18b7e4954edfe611d74ae0dc59e1688221ecda982ba943 -node Simulated tx 0101e1a3d73c3a112a18b7e4954edfe611d74ae0dc59e1688221ecda982ba943 succeeds -pxe_service Executed local simulation for 0101e1a3d73c3a112a18b7e4954edfe611d74ae0dc59e1688221ecda982ba943 -pxe_service Sending transaction 0101e1a3d73c3a112a18b7e4954edfe611d74ae0dc59e1688221ecda982ba943 -node Received tx 0101e1a3d73c3a112a18b7e4954edfe611d74ae0dc59e1688221ecda982ba943 +node Simulating tx 085665fbbad776a727cb92c4b62fbe2f57c83dfbccd191852e3c17efc12fdd4b +node Simulated tx 085665fbbad776a727cb92c4b62fbe2f57c83dfbccd191852e3c17efc12fdd4b succeeds +pxe_service Executed local simulation for 085665fbbad776a727cb92c4b62fbe2f57c83dfbccd191852e3c17efc12fdd4b +pxe_service Sending transaction 085665fbbad776a727cb92c4b62fbe2f57c83dfbccd191852e3c17efc12fdd4b +node Received tx 085665fbbad776a727cb92c4b62fbe2f57c83dfbccd191852e3c17efc12fdd4b +sequencer Retrieved 1 txs from P2P pool +sequencer Building block 4 with 1 transactions sequencer Submitted rollup block 4 with 1 transactions -node Simulating tx 2132767911fbbe67e24a3e51bc769ba2ae874eb1ba56e69cef8fc9e2c5eba04c -node Simulated tx 2132767911fbbe67e24a3e51bc769ba2ae874eb1ba56e69cef8fc9e2c5eba04c succeeds -pxe_service Executed local simulation for 2132767911fbbe67e24a3e51bc769ba2ae874eb1ba56e69cef8fc9e2c5eba04c -pxe_service Sending transaction 2132767911fbbe67e24a3e51bc769ba2ae874eb1ba56e69cef8fc9e2c5eba04c -node Received tx 2132767911fbbe67e24a3e51bc769ba2ae874eb1ba56e69cef8fc9e2c5eba04c +node Simulating tx 2755e9f5ad308fd135f606daf6208f5d711a3a6de6e4630d15d269af59f03e1c +node Simulated tx 2755e9f5ad308fd135f606daf6208f5d711a3a6de6e4630d15d269af59f03e1c succeeds +pxe_service Executed local simulation for 2755e9f5ad308fd135f606daf6208f5d711a3a6de6e4630d15d269af59f03e1c +pxe_service Sending transaction 2755e9f5ad308fd135f606daf6208f5d711a3a6de6e4630d15d269af59f03e1c +node Received tx 2755e9f5ad308fd135f606daf6208f5d711a3a6de6e4630d15d269af59f03e1c +sequencer Retrieved 1 txs from P2P pool +sequencer Building block 5 with 1 transactions sequencer Submitted rollup block 5 with 1 transactions ``` diff --git a/docs/src/components/GithubCode/index.js b/docs/src/components/GithubCode/index.js index f7c41e9797c..7cbb04d6629 100644 --- a/docs/src/components/GithubCode/index.js +++ b/docs/src/components/GithubCode/index.js @@ -12,7 +12,7 @@ import github from "prism-react-renderer/themes/github"; * Inside a markdown file: * * import GithubCode from '../src/components/GithubCode'; - * + * */ const GithubCode = ({ diff --git a/yarn-project/aztec-sandbox/package.json b/yarn-project/aztec-sandbox/package.json index 8e263193536..1427b98fe91 100644 --- a/yarn-project/aztec-sandbox/package.json +++ b/yarn-project/aztec-sandbox/package.json @@ -21,7 +21,7 @@ "formatting:fix": "run -T prettier -w ./src", "build:dev": "tsc -b --watch", "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --passWithNoTests", - "run:example:token": "DEBUG='aztec:*' node ./dest/examples/private_token_contract.js" + "run:example:token": "DEBUG='aztec:*' node ./dest/examples/token.js" }, "inherits": [ "../package.common.json" diff --git a/yarn-project/aztec-sandbox/src/examples/private_token_contract.ts b/yarn-project/aztec-sandbox/src/examples/private_token_contract.ts deleted file mode 100644 index 8b71aa22f99..00000000000 --- a/yarn-project/aztec-sandbox/src/examples/private_token_contract.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { - AccountWallet, - AztecAddress, - Contract, - GrumpkinScalar, - createPXEClient, - createRecipient, - getUnsafeSchnorrAccount, -} from '@aztec/aztec.js'; -import { createDebugLogger } from '@aztec/foundation/log'; -import { PrivateTokenContract } from '@aztec/noir-contracts/types'; - -const logger = createDebugLogger('aztec:http-rpc-client'); - -export const privateKey = GrumpkinScalar.fromString('ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'); - -const url = 'http://localhost:8080'; - -const pxe = createPXEClient(url); -let wallet: AccountWallet; - -const INITIAL_BALANCE = 333n; -const SECONDARY_AMOUNT = 33n; - -/** - * Deploys the Private Token contract. - * @param owner - The address that the initial balance will belong to. - * @returns An Aztec Contract object with the private token's artifact. - */ -async function deployZKContract(owner: AztecAddress) { - logger('Deploying L2 contract...'); - const contract = await PrivateTokenContract.deploy(pxe, INITIAL_BALANCE, owner).send().deployed(); - logger('L2 contract deployed'); - return contract; -} - -/** - * Gets a user's balance from a Private Token contract. - * @param contract - The Private Token contract. - * @param ownerAddress - Balance owner's Aztec Address. - * @returns The owner's current balance of the token. - */ -async function getBalance(contract: Contract, ownerAddress: AztecAddress) { - return await contract.methods.getBalance(ownerAddress).view({ from: ownerAddress }); -} - -/** - * Main function. - */ -async function main() { - logger('Running ZK contract test on HTTP interface.'); - - wallet = await getUnsafeSchnorrAccount(pxe, privateKey).waitDeploy(); - const owner = wallet.getCompleteAddress(); - const recipient = await createRecipient(pxe); - - logger(`Created Owner account ${owner.toString()}`); - - const zkContract = await deployZKContract(owner.address); - const [balance1] = await zkContract.methods.getBalance(owner.address).view({ from: owner.address }); - logger(`Initial owner balance: ${balance1}`); - - // Mint more tokens - logger(`Minting ${SECONDARY_AMOUNT} more coins`); - await zkContract.methods.mint(SECONDARY_AMOUNT, owner.address).send().wait({ interval: 0.5 }); - const balanceAfterMint = await getBalance(zkContract, owner.address); - logger(`Owner's balance is now: ${balanceAfterMint}`); - - // Perform a transfer - logger(`Transferring ${SECONDARY_AMOUNT} tokens from owner to another account.`); - await zkContract.methods.transfer(SECONDARY_AMOUNT, recipient.address).send().wait({ interval: 0.5 }); - const balanceAfterTransfer = await getBalance(zkContract, owner.address); - const receiverBalance = await getBalance(zkContract, recipient.address); - logger(`Owner's balance is now ${balanceAfterTransfer}`); - logger(`The transfer receiver's balance is ${receiverBalance}`); -} - -main() - .then(() => { - logger('Finished running successfully.'); - process.exit(0); - }) - .catch(err => { - logger.error('Error in main fn: ', err); - process.exit(1); - }); diff --git a/yarn-project/aztec-sandbox/src/examples/token.ts b/yarn-project/aztec-sandbox/src/examples/token.ts new file mode 100644 index 00000000000..ac549ee6436 --- /dev/null +++ b/yarn-project/aztec-sandbox/src/examples/token.ts @@ -0,0 +1,90 @@ +import { + AccountWallet, + Fr, + GrumpkinScalar, + NotePreimage, + computeMessageSecretHash, + createPXEClient, + getUnsafeSchnorrAccount, +} from '@aztec/aztec.js'; +import { createDebugLogger } from '@aztec/foundation/log'; +import { TokenContract } from '@aztec/noir-contracts/types'; + +const logger = createDebugLogger('aztec:http-rpc-client'); + +export const alicePrivateKey = GrumpkinScalar.random(); +export const bobPrivateKey = GrumpkinScalar.random(); + +const url = 'http://localhost:8080'; + +const pxe = createPXEClient(url); + +let aliceWallet: AccountWallet; +let bobWallet: AccountWallet; + +const ALICE_MINT_BALANCE = 333n; +const TRANSFER_AMOUNT = 33n; + +/** + * Main function. + */ +async function main() { + logger('Running token contract test on HTTP interface.'); + + aliceWallet = await getUnsafeSchnorrAccount(pxe, alicePrivateKey).waitDeploy(); + bobWallet = await getUnsafeSchnorrAccount(pxe, bobPrivateKey).waitDeploy(); + const alice = aliceWallet.getCompleteAddress(); + const bob = bobWallet.getCompleteAddress(); + + logger(`Created Alice and Bob accounts: ${alice.address.toString()}, ${bob.address.toString()}`); + + logger('Deploying Token...'); + const token = await TokenContract.deploy(pxe, alice).send().deployed(); + logger('Token deployed'); + + // Create the contract abstraction and link it to Alice's and Bob's wallet for future signing + const tokenAlice = await TokenContract.at(token.address, aliceWallet); + const tokenBob = await TokenContract.at(token.address, bobWallet); + + // Mint tokens to Alice + logger(`Minting ${ALICE_MINT_BALANCE} more coins to Alice...`); + + // Create a secret and a corresponding hash that will be used to mint funds privately + const aliceSecret = Fr.random(); + const aliceSecretHash = await computeMessageSecretHash(aliceSecret); + const receipt = await tokenAlice.methods.mint_private(ALICE_MINT_BALANCE, aliceSecretHash).send().wait(); + + // Add the newly created "pending shield" note to PXE + const pendingShieldsStorageSlot = new Fr(5); // The storage slot of `pending_shields` is 5. + const preimage = new NotePreimage([new Fr(ALICE_MINT_BALANCE), aliceSecretHash]); + await pxe.addNote(alice.address, token.address, pendingShieldsStorageSlot, preimage, receipt.txHash); + + // Make the tokens spendable by redeeming them using the secret (converts the "pending shield note" created above + // to a "token note") + await tokenAlice.methods.redeem_shield(alice, ALICE_MINT_BALANCE, aliceSecret).send().wait(); + logger(`${ALICE_MINT_BALANCE} tokens were successfully minted and redeemed by Alice`); + + const balanceAfterMint = await tokenAlice.methods.balance_of_private(alice).view(); + logger(`Tokens successfully minted. New Alice's balance: ${balanceAfterMint}`); + + // We will now transfer tokens from Alice to Bob + logger(`Transferring ${TRANSFER_AMOUNT} tokens from Alice to Bob...`); + await tokenAlice.methods.transfer(alice, bob, TRANSFER_AMOUNT, 0).send().wait(); + + // Check the new balances + const aliceBalance = await tokenAlice.methods.balance_of_private(alice).view(); + logger(`Alice's balance ${aliceBalance}`); + + const bobBalance = await tokenBob.methods.balance_of_private(bob).view(); + logger(`Bob's balance ${bobBalance}`); +} + +main() + .then(() => { + logger('Finished running successfully.'); + process.exit(0); + }) + .catch(err => { + logger.error('Error in main fn: ', err); + process.exit(1); + }); diff --git a/yarn-project/end-to-end/src/cli_docs_sandbox.test.ts b/yarn-project/end-to-end/src/cli_docs_sandbox.test.ts index e862b00cc1b..cfa14445de5 100644 --- a/yarn-project/end-to-end/src/cli_docs_sandbox.test.ts +++ b/yarn-project/end-to-end/src/cli_docs_sandbox.test.ts @@ -109,7 +109,6 @@ PendingCommitmentsContractArtifact PokeableTokenContractArtifact PriceFeedContractArtifact PrivateTokenAirdropContractArtifact -PrivateTokenContractArtifact PublicTokenContractArtifact SchnorrAccountContractArtifact SchnorrHardcodedAccountContractArtifact @@ -249,13 +248,10 @@ Accounts found: clearLogs(); - // Set some of the found addresses as address2 for later use - const address2 = AztecAddress.fromString(fetchedAddresses[1].groups?.address as string); - // Test deploy docs = ` // docs:start:deploy -% aztec-cli deploy PrivateTokenContractArtifact --args 1000000 $ADDRESS +% aztec-cli deploy TokenContractArtifact --args $ADDRESS Contract deployed at 0x1ae8eea0dc265fb7f160dae62cc8912686d8a9ed78e821fbdd8bcedc54c06d0f // docs:end:deploy @@ -286,38 +282,12 @@ Contract found at 0x1ae8eea0dc265fb7f160dae62cc8912686d8a9ed78e821fbdd8bcedc54c0 clearLogs(); - // Test call - docs = ` -// docs:start:call -% aztec-cli call getBalance \ - --args $ADDRESS \ - --contract-artifact PrivateTokenContractArtifact \ - --contract-address $CONTRACT_ADDRESS - -View result: 1000000n -// docs:end:call -`; - command = docs - .split('\n')[2] - .split('aztec-cli ')[1] - .replace('$ADDRESS', newAddress.toString()) - .replace('$CONTRACT_ADDRESS', contractAddress.toString()); - await run(command); - - let foundBalance = findInLogs(/View\sresult:\s+(?\S+)/)?.groups?.data; - expect(foundBalance!).toEqual(`${BigInt(1000000).toString()}n`); - - clearLogs(); - - // We reset CLI so that we can call the same command again later on - resetCli(); - // Test send docs = ` // docs:start:send -% aztec-cli send transfer \ - --args 543 $ADDRESS2 \ - --contract-artifact PrivateTokenContractArtifact \ +% aztec-cli send mint_public \ + --args $ADDRESS 543 \ + --contract-artifact TokenContractArtifact \ --contract-address $CONTRACT_ADDRESS \ --private-key $PRIVATE_KEY @@ -332,7 +302,7 @@ Block hash: 163697608599543b2bee9652f543938683e4cdd0f94ac506e5764d8b908d43d4 command = docs .split('\n')[2] .split('aztec-cli ')[1] - .replace('$ADDRESS2', address2.toString()) + .replace('$ADDRESS', newAddress.toString()) .replace('$CONTRACT_ADDRESS', contractAddress.toString()) .replace('$PRIVATE_KEY', foundPrivateKey!); await run(command); @@ -377,17 +347,13 @@ Transaction receipt: clearLogs(); - // get balance + // Test call docs = ` -// docs:start:calls -% aztec-cli call getBalance -a $ADDRESS -c PrivateTokenContractArtifact -ca $CONTRACT_ADDRESS - -View result: 999457n - -% aztec-cli call getBalance -a $ADDRESS2 -c PrivateTokenContractArtifact -ca $CONTRACT_ADDRESS +// docs:start:call +% aztec-cli call balance_of_public -a $ADDRESS -c TokenContractArtifact -ca $CONTRACT_ADDRESS View result: 543n -// docs:end:calls +// docs:end:call `; command = docs .split('\n')[2] @@ -397,23 +363,7 @@ View result: 543n await run(command); - foundBalance = findInLogs(/View\sresult:\s+(?\S+)/)?.groups?.data; - expect(foundBalance!).toEqual(`${BigInt(999457).toString()}n`); - - clearLogs(); - resetCli(); - - command = docs - .split('\n')[6] - .split('aztec-cli ')[1] - .replace('$ADDRESS2', address2.toString()) - .replace('$CONTRACT_ADDRESS', contractAddress.toString()); - - await run(command); - - foundBalance = findInLogs(/View\sresult:\s+(?\S+)/)?.groups?.data; + const foundBalance = findInLogs(/View\sresult:\s+(?\S+)/)?.groups?.data; expect(foundBalance!).toEqual(`${BigInt(543).toString()}n`); - - clearLogs(); }, 60_000); }); diff --git a/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts b/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts index 111f82b08d8..8e0ccaf2752 100644 --- a/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts @@ -56,7 +56,7 @@ describe('e2e_escrow_contract', () => { .deployed(); logger(`Escrow contract deployed at ${escrowContract.address}`); - // Deploy Private Token contract and mint funds for the escrow contract + // Deploy Token contract and mint funds for the escrow contract token = await TokenContract.deploy(wallet, owner).send().deployed(); const mintAmount = 100n; diff --git a/yarn-project/end-to-end/src/e2e_private_token_contract.test.ts b/yarn-project/end-to-end/src/e2e_private_token_contract.test.ts deleted file mode 100644 index 1cc13f97e78..00000000000 --- a/yarn-project/end-to-end/src/e2e_private_token_contract.test.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { AztecAddress, Wallet } from '@aztec/aztec.js'; -import { DebugLogger } from '@aztec/foundation/log'; -import { PrivateTokenContract } from '@aztec/noir-contracts/types'; -import { AztecNode, CompleteAddress, TxStatus } from '@aztec/types'; - -import { expectsNumOfEncryptedLogsInTheLastBlockToBe, setup } from './fixtures/utils.js'; - -describe('e2e_private_token_contract', () => { - let aztecNode: AztecNode | undefined; - let wallet: Wallet; - let logger: DebugLogger; - let owner: AztecAddress; - let receiver: AztecAddress; - let teardown: () => Promise; - let contract: PrivateTokenContract; - - beforeEach(async () => { - let accounts: CompleteAddress[]; - ({ teardown, aztecNode, accounts, wallet, logger } = await setup(2)); - owner = accounts[0].address; - receiver = accounts[1].address; - }, 100_000); - - afterEach(() => teardown()); - - const expectBalance = async (owner: AztecAddress, expectedBalance: bigint) => { - const balance = await contract.methods.getBalance(owner).view({ from: owner }); - logger(`Account ${owner} balance: ${balance}`); - expect(balance).toBe(expectedBalance); - }; - - const deployContract = async (initialBalance: bigint, owner: AztecAddress) => { - logger(`Deploying L2 contract...`); - contract = await PrivateTokenContract.deploy(wallet, initialBalance, owner).send().deployed(); - logger(`L2 contract deployed at ${contract.address}`); - }; - - /** - * Milestone 1.3. - * https://hackmd.io/AG5rb9DyTRu3y7mBptWauA - */ - it('1.3 should deploy private token contract with initial token minted to the account', async () => { - const initialBalance = 987n; - await deployContract(initialBalance, owner); - await expectBalance(owner, initialBalance); - await expectBalance(receiver, 0n); - - await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 1); - }, 30_000); - - /** - * Milestone 1.4. - */ - it('1.4 should call mint and increase balance', async () => { - const mintAmount = 65n; - - await deployContract(0n, owner); - await expectBalance(owner, 0n); - - await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 0); - - const tx = contract.methods.mint(mintAmount, owner).send(); - - await tx.isMined({ interval: 0.1 }); - const receipt = await tx.getReceipt(); - - expect(receipt.status).toBe(TxStatus.MINED); - await expectBalance(owner, mintAmount); - - await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 1); - }, 60_000); - - /** - * Milestone 1.5. - */ - it('1.5 should call transfer and increase balance of another account', async () => { - const initialBalance = 987n; - const transferAmount = 654n; - - await deployContract(initialBalance, owner); - - await expectBalance(owner, initialBalance); - await expectBalance(receiver, 0n); - - await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 1); - - const tx = contract.methods.transfer(transferAmount, receiver).send(); - - await tx.isMined({ interval: 0.1 }); - const receipt = await tx.getReceipt(); - - expect(receipt.status).toBe(TxStatus.MINED); - - await expectBalance(owner, initialBalance - transferAmount); - await expectBalance(receiver, transferAmount); - - await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 2); - }, 60_000); -}); 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 86efb877d6d..487f34a141c 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 @@ -54,30 +54,33 @@ describe('e2e_sandbox_example', () => { // docs:start:Deployment ////////////// DEPLOY OUR TOKEN CONTRACT ////////////// - // Deploy a token contract, create a contract abstraction object and link it to the owner's wallet const initialSupply = 1_000_000n; + logger(`Deploying token contract...`); - logger(`Deploying token contract minting an initial ${initialSupply} tokens to Alice...`); + // Deploy the contract and set Alice as the admin while doing so const contract = await TokenContract.deploy(pxe, alice).send().deployed(); + logger(`Contract successfully deployed at address ${contract.address.toShortString()}`); - // Create the contract abstraction and link to Alice's wallet for future signing + // Create the contract abstraction and link it to Alice's wallet for future signing const tokenContractAlice = await TokenContract.at(contract.address, accounts[0]); - // add Bob as a minter - await tokenContractAlice.methods.set_minter(bob, true).send().wait(); - - logger(`Contract successfully deployed at address ${contract.address.toShortString()}`); - - const secret = Fr.random(); - const secretHash = await computeMessageSecretHash(secret); + // Create a secret and a corresponding hash that will be used to mint funds privately + const aliceSecret = Fr.random(); + const aliceSecretHash = await computeMessageSecretHash(aliceSecret); - const receipt = await tokenContractAlice.methods.mint_private(initialSupply, secretHash).send().wait(); + logger(`Minting tokens to Alice...`); + // Mint the initial supply privately "to secret hash" + const receipt = await tokenContractAlice.methods.mint_private(initialSupply, aliceSecretHash).send().wait(); + // Add the newly created "pending shield" note to PXE const pendingShieldsStorageSlot = new Fr(5); // The storage slot of `pending_shields` is 5. - const preimage = new NotePreimage([new Fr(initialSupply), secretHash]); + const preimage = new NotePreimage([new Fr(initialSupply), aliceSecretHash]); await pxe.addNote(alice, contract.address, pendingShieldsStorageSlot, preimage, receipt.txHash); - await tokenContractAlice.methods.redeem_shield(alice, initialSupply, secret).send().wait(); + // Make the tokens spendable by redeeming them using the secret (converts the "pending shield note" created above + // to a "token note") + await tokenContractAlice.methods.redeem_shield(alice, initialSupply, aliceSecret).send().wait(); + logger(`${initialSupply} tokens were successfully minted and redeemed by Alice`); // docs:end:Deployment // ensure that token contract is registered in PXE @@ -125,14 +128,22 @@ describe('e2e_sandbox_example', () => { ////////////// MINT SOME MORE TOKENS TO BOB'S ACCOUNT ////////////// // Now mint some further funds for Bob + + // Alice is nice and she adds Bob as a minter + await tokenContractAlice.methods.set_minter(bob, true).send().wait(); + + const bobSecret = Fr.random(); + const bobSecretHash = await computeMessageSecretHash(bobSecret); + // Bob now has a secret 🥷 + const mintQuantity = 10_000n; logger(`Minting ${mintQuantity} tokens to Bob...`); - const mintPrivateReceipt = await tokenContractBob.methods.mint_private(mintQuantity, secretHash).send().wait(); + const mintPrivateReceipt = await tokenContractBob.methods.mint_private(mintQuantity, bobSecretHash).send().wait(); - const bobPendingShield = new NotePreimage([new Fr(mintQuantity), secretHash]); + const bobPendingShield = new NotePreimage([new Fr(mintQuantity), bobSecretHash]); await pxe.addNote(bob, contract.address, pendingShieldsStorageSlot, bobPendingShield, mintPrivateReceipt.txHash); - await tokenContractBob.methods.redeem_shield(bob, mintQuantity, secret).send().wait(); + await tokenContractBob.methods.redeem_shield(bob, mintQuantity, bobSecret).send().wait(); // Check the new balances aliceBalance = await tokenContractAlice.methods.balance_of_private(alice).view(); diff --git a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts index fcc21d8a756..8f10f3e4ca0 100644 --- a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts +++ b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts @@ -18,7 +18,7 @@ const { PXE_URL = 'http://localhost:8080', ETHEREUM_HOST = 'http://localhost:854 describe('guides/dapp/testing', () => { describe('on in-proc sandbox', () => { - describe('private token contract', () => { + describe('token contract', () => { let pxe: PXE; let stop: () => Promise; let owner: AccountWallet; @@ -64,7 +64,7 @@ describe('guides/dapp/testing', () => { }); // docs:start:sandbox-example - describe('private token contract', () => { + describe('token contract', () => { let pxe: PXE; let owner: AccountWallet; let recipient: AccountWallet; @@ -96,7 +96,7 @@ describe('guides/dapp/testing', () => { }); // docs:end:sandbox-example - describe('private token contract with initial accounts', () => { + describe('token contract with initial accounts', () => { let pxe: PXE; let owner: AccountWallet; let recipient: AccountWallet; diff --git a/yarn-project/noir-contracts/Nargo.toml b/yarn-project/noir-contracts/Nargo.toml index a1df90d29c2..1097b1ae118 100644 --- a/yarn-project/noir-contracts/Nargo.toml +++ b/yarn-project/noir-contracts/Nargo.toml @@ -15,7 +15,6 @@ members = [ "src/contracts/pokeable_token_contract", "src/contracts/price_feed_contract", "src/contracts/private_token_airdrop_contract", - "src/contracts/private_token_contract", "src/contracts/public_token_contract", "src/contracts/schnorr_account_contract", "src/contracts/schnorr_hardcoded_account_contract", diff --git a/yarn-project/noir-contracts/src/contracts/private_token_contract/Nargo.toml b/yarn-project/noir-contracts/src/contracts/private_token_contract/Nargo.toml deleted file mode 100644 index f3e29ac1bf5..00000000000 --- a/yarn-project/noir-contracts/src/contracts/private_token_contract/Nargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -# docs:start:importing-aztec -[package] -name = "private_token_contract" -authors = [""] -compiler_version = "0.1" -type = "contract" - -[dependencies] -# highlight-next-line:importing-aztec -aztec = { path = "../../../../aztec-nr/aztec" } -value_note = { path = "../../../../aztec-nr/value-note"} -# docs:end:importing-aztec \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/private_token_contract/src/interface.nr b/yarn-project/noir-contracts/src/contracts/private_token_contract/src/interface.nr deleted file mode 100644 index 19b526671a3..00000000000 --- a/yarn-project/noir-contracts/src/contracts/private_token_contract/src/interface.nr +++ /dev/null @@ -1,67 +0,0 @@ -/* Autogenerated file, do not edit! */ - -use dep::std; -use dep::aztec::context::{ PrivateContext, PublicContext }; -use dep::aztec::constants_gen::RETURN_VALUES_LENGTH; - - - -// Interface for calling PrivateToken functions from a private context -struct PrivateTokenPrivateContextInterface { - address: Field, -} - -impl PrivateTokenPrivateContextInterface { - pub fn at(address: Field) -> Self { - Self { - address, - } - } - - pub fn mint( - self, - context: &mut PrivateContext, - amount: Field, - owner: Field - ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 2]; - serialized_args[0] = amount; - serialized_args[1] = owner; - - context.call_private_function(self.address, 0x1535439c, serialized_args) - } - - - pub fn transfer( - self, - context: &mut PrivateContext, - amount: Field, - recipient: Field - ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 2]; - serialized_args[0] = amount; - serialized_args[1] = recipient; - - context.call_private_function(self.address, 0xc0888d22, serialized_args) - } - -} - - - - -// Interface for calling PrivateToken functions from a public context -struct PrivateTokenPublicContextInterface { - address: Field, -} - -impl PrivateTokenPublicContextInterface { - pub fn at(address: Field) -> Self { - Self { - address, - } - } - -} - - 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 deleted file mode 100644 index c999ee9939e..00000000000 --- a/yarn-project/noir-contracts/src/contracts/private_token_contract/src/main.nr +++ /dev/null @@ -1,105 +0,0 @@ -// docs:start:all -contract PrivateToken { - use dep::std::option::Option; - use dep::value_note::{ - balance_utils, - utils::{increment, decrement}, - value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods}, - }; - use dep::aztec::{ - context::{PrivateContext, PublicContext, Context}, - note::{ - note_header::NoteHeader, - utils as note_utils, - }, - state_vars::{map::Map, set::Set}, - }; - - struct Storage { - // maps an aztec address to its balance - balances: Map>, - } - - impl Storage { - fn init(context: Context) -> pub Self { - Storage { - balances: Map::new( - context, - 1, // Storage slot - |context, slot| { - Set::new(context, slot, ValueNoteMethods) - }, - ), - } - } - } - - // Constructs the contract and sets `initial_supply` which is fully owned by `owner`. - #[aztec(private)] - fn constructor( - initial_supply: Field, - owner: Field - ) { - - // Insert new note to a set of user notes and emit the newly created encrypted note preimage via oracle call. - let owner_balance = storage.balances.at(owner); - if (initial_supply != 0) { - increment(owner_balance, initial_supply, owner); - } - } - - // docs:start:mint - // Mints `amount` of tokens to `owner`. - #[aztec(private)] - fn mint( - amount: Field, - owner: Field - ) { - - - // Insert new note to a set of user notes and emit the newly created encrypted note preimage via oracle call. - let owner_balance = storage.balances.at(owner); - increment(owner_balance, amount, owner); - } - // docs:end:mint - - // Transfers `amount` of tokens from msg_sender to a `recipient`. - #[aztec(private)] - fn transfer( - amount: Field, - recipient: Field, - ) { - - let sender = context.msg_sender(); - - // Pick from the set of sender's notes to spend amount. - let sender_balance = storage.balances.at(sender); - decrement(sender_balance, amount, sender); - - // Creates new note for the recipient. - let recipient_balance = storage.balances.at(recipient); - increment(recipient_balance, amount, recipient); - } - - // Helper function to get the balance of a user ("unconstrained" is a Noir alternative of Solidity's "view" function). - unconstrained fn getBalance( - owner: Field, - ) -> Field { - - - // Get the set of notes owned by the user. - let owner_balance = storage.balances.at(owner); - - // Return the sum of all notes in the set. - balance_utils::get_balance(owner_balance) - } - - // Computes note hash and nullifier. - // Note 1: Needs to be defined by every contract producing logs. - // Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes. - unconstrained fn compute_note_hash_and_nullifier(contract_address: Field, nonce: Field, storage_slot: Field, preimage: [Field; VALUE_NOTE_LEN]) -> [Field; 4] { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, preimage) - } -} -// docs:end:all \ No newline at end of file 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 73e0c9f3360..20d226fcf85 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 @@ -276,11 +276,15 @@ contract Token { ) -> Field { let pending_shields = storage.pending_shields; let secret_hash = compute_secret_hash(secret); + // Get 1 note (set_limit(1)) which has amount stored in field with index 0 (select(0, amount)) and secret_hash + // stored in field with index 1 (select(1, secret_hash)). let options = NoteGetterOptions::new().select(0, amount).select(1, secret_hash).set_limit(1); let notes = pending_shields.get_notes(options); let note = notes[0].unwrap_unchecked(); + // Remove the note from the pending shields set pending_shields.remove(note); + // Add the token note to user's balances set storage.balances.at(to).add(SafeU120::new(amount)); 1