diff --git a/cspell.json b/cspell.json index dd5586ddbc8..42e5efb8715 100644 --- a/cspell.json +++ b/cspell.json @@ -270,9 +270,6 @@ "unexcluded", "unfinalised", "unprefixed", - "unshield", - "unshielding", - "unshields", "unzipit", "updateable", "upperfirst", diff --git a/docs/docs/guides/developer_guides/smart_contracts/how_to_compile_contract.md b/docs/docs/guides/developer_guides/smart_contracts/how_to_compile_contract.md index 414fc543bf4..c356ddb1544 100644 --- a/docs/docs/guides/developer_guides/smart_contracts/how_to_compile_contract.md +++ b/docs/docs/guides/developer_guides/smart_contracts/how_to_compile_contract.md @@ -246,7 +246,7 @@ contract FPC { #[private] fn fee_entrypoint_private(amount: Field, asset: AztecAddress, secret_hash: Field, nonce: Field) { assert(asset == storage.other_asset.read_private()); - Token::at(asset).unshield(context.msg_sender(), context.this_address(), amount, nonce).call(&mut context); + Token::at(asset).transfer_to_public(context.msg_sender(), context.this_address(), amount, nonce).call(&mut context); FPC::at(context.this_address()).pay_fee_with_shielded_rebate(amount, asset, secret_hash).enqueue(&mut context); } diff --git a/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/authwit.md b/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/authwit.md index 71c085f3ca6..dce64ae625e 100644 --- a/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/authwit.md +++ b/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/authwit.md @@ -175,7 +175,7 @@ In the snippet below, this is done as a separate contract call, but can also be We have cases where we need a non-wallet contract to approve an action to be executed by another contract. One of the cases could be when making more complex defi where funds are passed along. When doing so, we need the intermediate contracts to support approving of actions on their behalf. -This is fairly straight forward to do using the `auth` library which includes logic for updating values in the public auth registry. Namely, you can prepare the `message_hash` using `compute_authwit_message_hash_from_call` and then simply feed it into the `set_authorized` function (both are in `auth` library) to update the value. +This is fairly straight forward to do using the `auth` library which includes logic for updating values in the public auth registry. Namely, you can prepare the `message_hash` using `compute_authwit_message_hash_from_call` and then simply feed it into the `set_authorized` function (both are in `auth` library) to update the value. When another contract later is consuming the authwit using `assert_current_call_valid_authwit_public` it will be calling the registry, and spend that authwit. @@ -197,7 +197,7 @@ sequenceDiagram activate AC; AC->>CC: Swap 1000 token A to B activate CC; - CC->>T: unshield 1000 tokens from Alice Account to CCS + CC->>T: Transfer to public 1000 tokens from Alice Account to CCS activate T; T->>AC: Have you approved this?? AC-->>A: Please give me an AuthWit diff --git a/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/common_patterns/index.md b/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/common_patterns/index.md index e4a9a3e5eb7..848fffa0e1f 100644 --- a/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/common_patterns/index.md +++ b/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/common_patterns/index.md @@ -131,7 +131,7 @@ To send a note to someone, they need to have a key which we can encrypt the note There are several patterns here: 1. Give the contract a key and share it amongst all participants. This leaks privacy, as anyone can see all the notes in the contract. -2. `Unshield` funds into the contract - this is used in the [Uniswap smart contract example where a user sends private funds into a Uniswap Portal contract which eventually withdraws to L1 to swap on L1 Uniswap](../../../../../tutorials/examples/uniswap/index.md). This works like Ethereum - to achieve contract composability, you move funds into the public domain. This way the contract doesn't even need keys. +2. `transfer_to_public` funds into the contract - this is used in the [Uniswap smart contract example where a user sends private funds into a Uniswap Portal contract which eventually withdraws to L1 to swap on L1 Uniswap](../../../../../tutorials/examples/uniswap/index.md). This works like Ethereum - to achieve contract composability, you move funds into the public domain. This way the contract doesn't even need keys. There are several other designs we are discussing through [in this discourse post](https://discourse.aztec.network/t/how-to-handle-private-escrows-between-two-parties/2440) but they need some changes in the protocol or in our demo contract. If you are interested in this discussion, please participate in the discourse post! diff --git a/docs/docs/protocol-specs/gas-and-fees/tx-setup-and-teardown.md b/docs/docs/protocol-specs/gas-and-fees/tx-setup-and-teardown.md index 6ea9057ecbb..b5b4783462c 100644 --- a/docs/docs/protocol-specs/gas-and-fees/tx-setup-and-teardown.md +++ b/docs/docs/protocol-specs/gas-and-fees/tx-setup-and-teardown.md @@ -37,7 +37,7 @@ Consider a user, Alice, who does not have FPA but wishes to interact with the ne Suppose there is a Fee Payment Contract (FPC) that has been deployed by another user to the network. Alice can structure her transaction as follows: -0. Before the transaction, Alice creates a private authwit in her wallet, allowing the FPC to unshield a specified amount of BananaCoin from Alice's private balance to the FPC's public balance. +0. Before the transaction, Alice creates a private authwit in her wallet, allowing the FPC to transfer to public a specified amount of BananaCoin from Alice's private balance to the FPC's public balance. 1. Private setup: - Alice calls a private function on the FPC which is exposed for public fee payment in BananaCoin. - The FPC checks that the amount of teardown gas Alice has allocated is sufficient to cover the gas associated with the teardown function it will use to provide a refund to Alice. diff --git a/docs/docs/tutorials/codealong/contract_tutorials/token_contract.md b/docs/docs/tutorials/codealong/contract_tutorials/token_contract.md index 9638102a0d6..efb6fda0342 100644 --- a/docs/docs/tutorials/codealong/contract_tutorials/token_contract.md +++ b/docs/docs/tutorials/codealong/contract_tutorials/token_contract.md @@ -77,9 +77,9 @@ These are functions that have transparent logic, will execute in a publicly veri These are functions that have private logic and will be executed on user devices to maintain privacy. The only data that is submitted to the network is a proof of correct execution, new data commitments and nullifiers, so users will not reveal which contract they are interacting with or which function they are executing. The only information that will be revealed publicly is that someone executed a private transaction on Aztec. - `redeem_shield` enables accounts to claim tokens that have been made private via `mint_private` or `shield` by providing the secret -- `unshield` enables an account to send tokens from their private balance to any other account's public balance +- `transfer_to_public` enables an account to send tokens from their private balance to any other account's public balance - `transfer` enables an account to send tokens from their private balance to another account's private balance -- `transferFrom` enables an account to send tokens from another account's private balance to another account's private balance +- `transfer_from` enables an account to send tokens from another account's private balance to another account's private balance - `cancel_authwit` enables an account to cancel an authorization to spend tokens - `burn` enables tokens to be burned privately @@ -87,7 +87,7 @@ These are functions that have private logic and will be executed on user devices Internal functions are functions that can only be called by the contract itself. These can be used when the contract needs to call one of it's public functions from one of it's private functions. -- `_increase_public_balance` increases the public balance of an account when `unshield` is called +- `_increase_public_balance` increases the public balance of an account when `transfer_to_public` is called - `_reduce_total_supply` reduces the total supply of tokens when a token is privately burned To clarify, let's review some details of the Aztec transaction lifecycle, particularly how a transaction "moves through" these contexts. @@ -133,7 +133,6 @@ We are importing: - `CompressedString` to hold the token symbol - Types from `aztec::prelude` -- `compute_secret_hash` that will help with the shielding and unshielding, allowing someone to claim a token from private to public - Types for storing note types ### Types files @@ -206,7 +205,7 @@ First, storage is initialized. Then the function checks that the `msg_sender` is This public function allows an account approved in the public `minters` mapping to create new private tokens that can be claimed by anyone that has the pre-image to the `secret_hash`. -First, public storage is initialized. Then it checks that the `msg_sender` is an approved minter. Then a new `TransparentNote` is created with the specified `amount` and `secret_hash`. You can read the details of the `TransparentNote` in the `types.nr` file [here (GitHub link)](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/noir-projects/noir-contracts/contracts/token_contract/src/types.nr#L61). The `amount` is added to the existing public `total_supply` and the storage value is updated. Then the new `TransparentNote` is added to the `pending_shields` using the `insert_from_public` function, which is accessible on the `PrivateSet` type. Then it's ready to be claimed by anyone with the `secret_hash` pre-image using the `redeem_shield` function. +First, public storage is initialized. Then it checks that the `msg_sender` is an approved minter. Then a new `TransparentNote` is created with the specified `amount` and `secret_hash`. You can read the details of the `TransparentNote` in the `types.nr` file [here (GitHub link)](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/noir-projects/noir-contracts/contracts/token_contract/src/types.nr#L61). The `amount` is added to the existing public `total_supply` and the storage value is updated. Then the new `TransparentNote` is added to the `pending_shields` using the `insert_from_public` function, which is accessible on the `PrivateSet` type. Then it's ready to be claimed by anyone with the `secret_hash` pre-image using the `redeem_shield` function. #include_code mint_private /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust @@ -251,7 +250,7 @@ Private functions are declared with the `#[private]` macro above the function na fn redeem_shield( ``` -As described in the [execution contexts section above](#execution-contexts), private function logic and transaction information is hidden from the world and is executed on user devices. Private functions update private state, but can pass data to the public execution context (e.g. see the [`unshield`](#unshield) function). +As described in the [execution contexts section above](#execution-contexts), private function logic and transaction information is hidden from the world and is executed on user devices. Private functions update private state, but can pass data to the public execution context (e.g. see the [`transfer_to_public`](#transfer_to_public) function). Storage is referenced as `storage.variable`. @@ -265,7 +264,7 @@ The function returns `1` to indicate successful execution. #include_code redeem_shield /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust -#### `unshield` +#### `transfer_to_public` This private function enables un-shielding of private `UintNote`s stored in `balances` to any Aztec account's `public_balance`. @@ -273,7 +272,7 @@ After initializing storage, the function checks that the `msg_sender` is authori The function returns `1` to indicate successful execution. -#include_code unshield /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust +#include_code transfer_to_public /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust #### `transfer` @@ -303,7 +302,7 @@ Internal functions are functions that can only be called by this contract. The f #### `_increase_public_balance` -This function is called from [`unshield`](#unshield). The account's private balance is decremented in `shield` and the public balance is increased in this function. +This function is called from [`transfer_to_public`](#transfer_to_public). The account's private balance is decremented in `shield` and the public balance is increased in this function. #include_code increase_public_balance /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust diff --git a/docs/docs/tutorials/examples/uniswap/l2_contract.md b/docs/docs/tutorials/examples/uniswap/l2_contract.md index da656ec2b2c..b8f9101e7a1 100644 --- a/docs/docs/tutorials/examples/uniswap/l2_contract.md +++ b/docs/docs/tutorials/examples/uniswap/l2_contract.md @@ -3,7 +3,7 @@ title: L2 Contracts (Aztec) sidebar_position: 1 --- -This page goes over the code in the L2 contract for Uniswap, which works alongside a [token bridge (codealong tutorial)](../../codealong/contract_tutorials/advanced/token_bridge/index.md). +This page goes over the code in the L2 contract for Uniswap, which works alongside a [token bridge (codealong tutorial)](../../codealong/contract_tutorials/advanced/token_bridge/index.md). ## Main.nr @@ -47,8 +47,8 @@ This uses a util function `compute_swap_private_content_hash()` - find that [her This flow works similarly to the public flow with a few notable changes: - Notice how in the `swap_private()`, user has to pass in `token` address which they didn't in the public flow? Since `swap_private()` is a private method, it can't read what token is publicly stored on the token bridge, so instead the user passes a token address, and `_assert_token_is_same()` checks that this user provided address is same as the one in storage. Note that because public functions are executed by the sequencer while private methods are executed locally, all public calls are always done after all private calls are done. So first the burn would happen and only later the sequencer asserts that the token is same. Note that the sequencer just sees a request to `execute_assert_token_is_same` and therefore has no context on what the appropriate private method was. If the assertion fails, then the kernel circuit will fail to create a proof and hence the transaction will be dropped. -- In the public flow, the user calls `transfer_public()`. Here instead, the user calls `unshield()`. Why? The user can't directly transfer their private tokens (their notes) to the uniswap contract, because later the Uniswap contract has to approve the bridge to burn these notes and withdraw to L1. The authwit flow for the private domain requires a signature from the `sender`, which in this case would be the Uniswap contract. For the contract to sign, it would need a private key associated to it. But who would operate this key? -- To work around this, the user can unshield their private tokens into Uniswap L2 contract. Unshielding would convert user's private notes to public balance. It is a private method on the token contract that reduces a user’s private balance and then calls a public method to increase the recipient’s (ie Uniswap) public balance. **Remember that first all private methods are executed and then later all public methods will be - so the Uniswap contract won’t have the funds until public execution begins.** +- In the public flow, the user calls `transfer_public()`. Here instead, the user calls `transfer_to_public()`. Why? The user can't directly transfer their private tokens (their notes) to the uniswap contract, because later the Uniswap contract has to approve the bridge to burn these notes and withdraw to L1. The authwit flow for the private domain requires a signature from the `sender`, which in this case would be the Uniswap contract. For the contract to sign, it would need a private key associated to it. But who would operate this key? +- To work around this, the user can transfer to public their private tokens into Uniswap L2 contract. Transferring to public would convert user's private notes to public balance. It is a private method on the token contract that reduces a user’s private balance and then calls a public method to increase the recipient’s (ie Uniswap) public balance. **Remember that first all private methods are executed and then later all public methods will be - so the Uniswap contract won’t have the funds until public execution begins.** - Now uniswap has public balance (like with the public flow). Hence, `swap_private()` calls the internal public method which approves the input token bridge to burn Uniswap’s tokens and calls `exit_to_l1_public` to create an L2 → L1 message to exit to L1. - Constructing the message content for swapping works exactly as the public flow except instead of specifying who would be the Aztec address that receives the swapped funds, we specify a secret hash (`secret_hash_for_redeeming_minted_notes`). Only those who know the preimage to the secret can later redeem the minted notes to themselves. diff --git a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr index 708f24fe0b8..05118734198 100644 --- a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr @@ -31,9 +31,9 @@ contract FPC { nonce: Field, ) { assert(asset == storage.other_asset.read_private()); - Token::at(asset).unshield(context.msg_sender(), context.this_address(), amount, nonce).call( - &mut context, - ); + Token::at(asset) + .transfer_to_public(context.msg_sender(), context.this_address(), amount, nonce) + .call(&mut context); context.set_as_fee_payer(); // Would like to get back to // FPC::at(context.this_address()).pay_refund_with_shielded_rebate(amount, asset, secret_hash).set_public_teardown_function(&mut context); diff --git a/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr b/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr index 077be6fea40..938d403c803 100644 --- a/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr @@ -112,7 +112,7 @@ contract Lending { let on_behalf_of = compute_identifier(secret, on_behalf_of, context.msg_sender().to_field()); let _res = Token::at(collateral_asset) - .unshield(from, context.this_address(), amount, nonce) + .transfer_to_public(from, context.this_address(), amount, nonce) .call(&mut context); // docs:start:enqueue_public Lending::at(context.this_address()) diff --git a/noir-projects/noir-contracts/contracts/nft_contract/src/main.nr b/noir-projects/noir-contracts/contracts/nft_contract/src/main.nr index f058088abc7..b9b7b610e6d 100644 --- a/noir-projects/noir-contracts/contracts/nft_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/nft_contract/src/main.nr @@ -256,7 +256,7 @@ contract NFT { fn _finalize_transfer_to_private( from: AztecAddress, token_id: Field, - note_transient_storage_slot: Field, + hiding_point_slot: Field, context: &mut PublicContext, storage: Storage<&mut PublicContext>, ) { @@ -268,7 +268,7 @@ contract NFT { // Finalize the partial note with the `token_id` let finalization_payload = - NFTNote::finalization_payload().new(context, note_transient_storage_slot, token_id); + NFTNote::finalization_payload().new(context, hiding_point_slot, token_id); // At last we emit the note hash and the final log finalization_payload.emit(); diff --git a/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr index 8443c54e0f1..4ef9953c938 100644 --- a/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_bridge_contract/src/main.nr @@ -158,7 +158,7 @@ contract TokenBridge { #[public] #[internal] fn _call_mint_on_token(amount: Field, secret_hash: Field) { - Token::at(storage.token.read()).mint_private(amount, secret_hash).call(&mut context); + Token::at(storage.token.read()).mint_private_old(amount, secret_hash).call(&mut context); } // docs:end:call_mint_on_token diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr index 2e764587bbc..cca2c1111dc 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr @@ -208,8 +208,9 @@ contract Token { } // docs:end:mint_public // docs:start:mint_private + // TODO(benesjan): To be nuked in a followup PR. #[public] - fn mint_private(amount: Field, secret_hash: Field) { + fn mint_private_old(amount: Field, secret_hash: Field) { assert(storage.minters.at(context.msg_sender()).read(), "caller is not minter"); let pending_shields = storage.pending_shields; let mut note = TransparentNote::new(amount, secret_hash); @@ -313,9 +314,9 @@ contract Token { )); } // docs:end:redeem_shield - // docs:start:unshield + // docs:start:transfer_to_public #[private] - fn unshield(from: AztecAddress, to: AztecAddress, amount: Field, nonce: Field) { + fn transfer_to_public(from: AztecAddress, to: AztecAddress, amount: Field, nonce: Field) { if (!from.eq(context.msg_sender())) { assert_current_call_valid_authwit(&mut context, from); } else { @@ -330,7 +331,7 @@ contract Token { ); Token::at(context.this_address())._increase_public_balance(to, amount).enqueue(&mut context); } - // docs:end:unshield + // docs:end:transfer_to_public // docs:start:transfer #[private] fn transfer(to: AztecAddress, amount: Field) { @@ -485,13 +486,15 @@ contract Token { // Transfers token `amount` from public balance of message sender to a private balance of `to`. #[private] fn transfer_to_private(to: AztecAddress, amount: Field) { + // We check the minter permissions in the enqueued call as that allows us to avoid the need for `SharedMutable` + // which is less efficient. let from = context.msg_sender(); let token = Token::at(context.this_address()); // We prepare the transfer. let hiding_point_slot = _prepare_transfer_to_private(to, &mut context, storage); - // At last we finalize the transfer. Usafe of the `unsafe` method here is safe because we set the `from` + // At last we finalize the transfer. Usage of the `unsafe` method here is safe because we set the `from` // function argument to a message sender, guaranteeing that he can transfer only his own tokens. token._finalize_transfer_to_private_unsafe(from, amount, hiding_point_slot).enqueue( &mut context, @@ -578,7 +581,7 @@ contract Token { fn _finalize_transfer_to_private( from: AztecAddress, amount: Field, - note_transient_storage_slot: Field, + hiding_point_slot: Field, context: &mut PublicContext, storage: Storage<&mut PublicContext>, ) { @@ -591,7 +594,70 @@ contract Token { // Then we finalize the partial note with the `amount` let finalization_payload = - UintNote::finalization_payload().new(context, note_transient_storage_slot, amount); + UintNote::finalization_payload().new(context, hiding_point_slot, amount); + + // At last we emit the note hash and the final log + finalization_payload.emit(); + } + + /// Mints token `amount` to a private balance of `to`. Message sender has to have minter permissions (checked + /// in the enqueud call). + #[private] + fn mint_to_private(to: AztecAddress, amount: Field) { + let from = context.msg_sender(); + let token = Token::at(context.this_address()); + + // We prepare the transfer. + let hiding_point_slot = _prepare_transfer_to_private(to, &mut context, storage); + + // At last we finalize the mint. Usage of the `unsafe` method here is safe because we set the `from` + // function argument to a message sender, guaranteeing that only a message sender with minter permissions + // can successfully execute the function. + token._finalize_mint_to_private_unsafe(from, amount, hiding_point_slot).enqueue(&mut context); + } + + /// Finalizes a mint of token `amount` to a private balance of `to`. The mint must be prepared by calling + /// `prepare_transfer_to_private` first and the resulting + /// `hiding_point_slot` must be passed as an argument to this function. + /// + /// Note: This function is only an optimization as it could be replaced by a combination of `mint_public` + /// and `finalize_transfer_to_private`. It is however used very commonly so it makes sense to optimize it + /// (e.g. used during token bridging, in AMM liquidity token etc.). + #[public] + fn finalize_mint_to_private(amount: Field, hiding_point_slot: Field) { + assert(storage.minters.at(context.msg_sender()).read(), "caller is not minter"); + + _finalize_mint_to_private(amount, hiding_point_slot, &mut context, storage); + } + + #[public] + #[internal] + fn _finalize_mint_to_private_unsafe( + from: AztecAddress, + amount: Field, + hiding_point_slot: Field, + ) { + // We check the minter permissions as it was not done in `mint_to_private` function. + assert(storage.minters.at(from).read(), "caller is not minter"); + _finalize_mint_to_private(amount, hiding_point_slot, &mut context, storage); + } + + #[contract_library_method] + fn _finalize_mint_to_private( + amount: Field, + hiding_point_slot: Field, + context: &mut PublicContext, + storage: Storage<&mut PublicContext>, + ) { + let amount = U128::from_integer(amount); + + // First we increase the total supply by the `amount` + let supply = storage.total_supply.read().add(amount); + storage.total_supply.write(supply); + + // Then we finalize the partial note with the `amount` + let finalization_payload = + UintNote::finalization_payload().new(context, hiding_point_slot, amount); // At last we emit the note hash and the final log finalization_payload.emit(); diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test.nr index a02821b3d5b..35a4c1f1aff 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/test.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test.nr @@ -4,8 +4,8 @@ mod utils; mod transfer_public; mod transfer_private; mod transfer_to_private; +mod transfer_to_public; mod refunds; -mod unshielding; mod minting; mod reading_constants; mod shielding; diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_to_public.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_to_public.nr new file mode 100644 index 00000000000..63f84481c74 --- /dev/null +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_to_public.nr @@ -0,0 +1,139 @@ +use crate::test::utils; +use crate::Token; +use dep::authwit::cheatcodes as authwit_cheatcodes; +use dep::aztec::oracle::random::random; + +#[test] +unconstrained fn transfer_to_public_on_behalf_of_self() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, _, mint_amount) = + utils::setup_and_mint_private(/* with_account_contracts */ false); + + let transfer_to_public_amount = mint_amount / 10; + Token::at(token_contract_address) + .transfer_to_public(owner, owner, transfer_to_public_amount, 0) + .call(&mut env.private()); + utils::check_private_balance( + token_contract_address, + owner, + mint_amount - transfer_to_public_amount, + ); + utils::check_public_balance(token_contract_address, owner, transfer_to_public_amount); +} + +#[test] +unconstrained fn transfer_to_public_on_behalf_of_other() { + let (env, token_contract_address, owner, recipient, mint_amount) = + utils::setup_and_mint_private(/* with_account_contracts */ true); + + let transfer_to_public_amount = mint_amount / 10; + let transfer_to_public_call_interface = Token::at(token_contract_address).transfer_to_public( + owner, + recipient, + transfer_to_public_amount, + 0, + ); + authwit_cheatcodes::add_private_authwit_from_call_interface( + owner, + recipient, + transfer_to_public_call_interface, + ); + // Impersonate recipient + env.impersonate(recipient); + // transfer_to_public tokens + transfer_to_public_call_interface.call(&mut env.private()); + utils::check_private_balance( + token_contract_address, + owner, + mint_amount - transfer_to_public_amount, + ); + utils::check_public_balance(token_contract_address, recipient, transfer_to_public_amount); +} + +#[test(should_fail_with = "Balance too low")] +unconstrained fn transfer_to_public_failure_more_than_balance() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, _, mint_amount) = + utils::setup_and_mint_private(/* with_account_contracts */ false); + + let transfer_to_public_amount = mint_amount + 1; + Token::at(token_contract_address) + .transfer_to_public(owner, owner, transfer_to_public_amount, 0) + .call(&mut env.private()); +} + +#[test(should_fail_with = "invalid nonce")] +unconstrained fn transfer_to_public_failure_on_behalf_of_self_non_zero_nonce() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, _, mint_amount) = + utils::setup_and_mint_private(/* with_account_contracts */ false); + + let transfer_to_public_amount = mint_amount + 1; + Token::at(token_contract_address) + .transfer_to_public(owner, owner, transfer_to_public_amount, random()) + .call(&mut env.private()); +} + +#[test(should_fail_with = "Balance too low")] +unconstrained fn transfer_to_public_failure_on_behalf_of_other_more_than_balance() { + let (env, token_contract_address, owner, recipient, mint_amount) = + utils::setup_and_mint_private(/* with_account_contracts */ true); + + let transfer_to_public_amount = mint_amount + 1; + let transfer_to_public_call_interface = Token::at(token_contract_address).transfer_to_public( + owner, + recipient, + transfer_to_public_amount, + 0, + ); + authwit_cheatcodes::add_private_authwit_from_call_interface( + owner, + recipient, + transfer_to_public_call_interface, + ); + // Impersonate recipient + env.impersonate(recipient); + // transfer_to_public tokens + transfer_to_public_call_interface.call(&mut env.private()); +} + +#[test(should_fail_with = "Authorization not found for message hash")] +unconstrained fn transfer_to_public_failure_on_behalf_of_other_invalid_designated_caller() { + let (env, token_contract_address, owner, recipient, mint_amount) = + utils::setup_and_mint_private(/* with_account_contracts */ true); + + let transfer_to_public_amount = mint_amount + 1; + let transfer_to_public_call_interface = Token::at(token_contract_address).transfer_to_public( + owner, + recipient, + transfer_to_public_amount, + 0, + ); + authwit_cheatcodes::add_private_authwit_from_call_interface( + owner, + owner, + transfer_to_public_call_interface, + ); + // Impersonate recipient + env.impersonate(recipient); + // transfer_to_public tokens + transfer_to_public_call_interface.call(&mut env.private()); +} + +#[test(should_fail_with = "Authorization not found for message hash")] +unconstrained fn transfer_to_public_failure_on_behalf_of_other_no_approval() { + let (env, token_contract_address, owner, recipient, mint_amount) = + utils::setup_and_mint_private(/* with_account_contracts */ true); + + let transfer_to_public_amount = mint_amount + 1; + let transfer_to_public_call_interface = Token::at(token_contract_address).transfer_to_public( + owner, + recipient, + transfer_to_public_amount, + 0, + ); + // Impersonate recipient + env.impersonate(recipient); + // transfer_to_public tokens + transfer_to_public_call_interface.call(&mut env.private()); +} diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/unshielding.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/unshielding.nr deleted file mode 100644 index 9d7f4f23c1c..00000000000 --- a/noir-projects/noir-contracts/contracts/token_contract/src/test/unshielding.nr +++ /dev/null @@ -1,115 +0,0 @@ -use crate::test::utils; -use crate::Token; -use dep::authwit::cheatcodes as authwit_cheatcodes; -use dep::aztec::oracle::random::random; - -#[test] -unconstrained fn unshield_on_behalf_of_self() { - // Setup without account contracts. We are not using authwits here, so dummy accounts are enough - let (env, token_contract_address, owner, _, mint_amount) = - utils::setup_and_mint_private(/* with_account_contracts */ false); - - let unshield_amount = mint_amount / 10; - Token::at(token_contract_address).unshield(owner, owner, unshield_amount, 0).call( - &mut env.private(), - ); - utils::check_private_balance(token_contract_address, owner, mint_amount - unshield_amount); - utils::check_public_balance(token_contract_address, owner, unshield_amount); -} - -#[test] -unconstrained fn unshield_on_behalf_of_other() { - let (env, token_contract_address, owner, recipient, mint_amount) = - utils::setup_and_mint_private(/* with_account_contracts */ true); - - let unshield_amount = mint_amount / 10; - let unshield_call_interface = - Token::at(token_contract_address).unshield(owner, recipient, unshield_amount, 0); - authwit_cheatcodes::add_private_authwit_from_call_interface( - owner, - recipient, - unshield_call_interface, - ); - // Impersonate recipient - env.impersonate(recipient); - // Unshield tokens - unshield_call_interface.call(&mut env.private()); - utils::check_private_balance(token_contract_address, owner, mint_amount - unshield_amount); - utils::check_public_balance(token_contract_address, recipient, unshield_amount); -} - -#[test(should_fail_with = "Balance too low")] -unconstrained fn unshield_failure_more_than_balance() { - // Setup without account contracts. We are not using authwits here, so dummy accounts are enough - let (env, token_contract_address, owner, _, mint_amount) = - utils::setup_and_mint_private(/* with_account_contracts */ false); - - let unshield_amount = mint_amount + 1; - Token::at(token_contract_address).unshield(owner, owner, unshield_amount, 0).call( - &mut env.private(), - ); -} - -#[test(should_fail_with = "invalid nonce")] -unconstrained fn unshield_failure_on_behalf_of_self_non_zero_nonce() { - // Setup without account contracts. We are not using authwits here, so dummy accounts are enough - let (env, token_contract_address, owner, _, mint_amount) = - utils::setup_and_mint_private(/* with_account_contracts */ false); - - let unshield_amount = mint_amount + 1; - Token::at(token_contract_address).unshield(owner, owner, unshield_amount, random()).call( - &mut env.private(), - ); -} - -#[test(should_fail_with = "Balance too low")] -unconstrained fn unshield_failure_on_behalf_of_other_more_than_balance() { - let (env, token_contract_address, owner, recipient, mint_amount) = - utils::setup_and_mint_private(/* with_account_contracts */ true); - - let unshield_amount = mint_amount + 1; - let unshield_call_interface = - Token::at(token_contract_address).unshield(owner, recipient, unshield_amount, 0); - authwit_cheatcodes::add_private_authwit_from_call_interface( - owner, - recipient, - unshield_call_interface, - ); - // Impersonate recipient - env.impersonate(recipient); - // Unshield tokens - unshield_call_interface.call(&mut env.private()); -} - -#[test(should_fail_with = "Authorization not found for message hash")] -unconstrained fn unshield_failure_on_behalf_of_other_invalid_designated_caller() { - let (env, token_contract_address, owner, recipient, mint_amount) = - utils::setup_and_mint_private(/* with_account_contracts */ true); - - let unshield_amount = mint_amount + 1; - let unshield_call_interface = - Token::at(token_contract_address).unshield(owner, recipient, unshield_amount, 0); - authwit_cheatcodes::add_private_authwit_from_call_interface( - owner, - owner, - unshield_call_interface, - ); - // Impersonate recipient - env.impersonate(recipient); - // Unshield tokens - unshield_call_interface.call(&mut env.private()); -} - -#[test(should_fail_with = "Authorization not found for message hash")] -unconstrained fn unshield_failure_on_behalf_of_other_no_approval() { - let (env, token_contract_address, owner, recipient, mint_amount) = - utils::setup_and_mint_private(/* with_account_contracts */ true); - - let unshield_amount = mint_amount + 1; - let unshield_call_interface = - Token::at(token_contract_address).unshield(owner, recipient, unshield_amount, 0); - // Impersonate recipient - env.impersonate(recipient); - // Unshield tokens - unshield_call_interface.call(&mut env.private()); -} diff --git a/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr b/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr index f1625dc6119..a9219a45144 100644 --- a/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr @@ -115,8 +115,8 @@ contract Uniswap { input_asset_bridge: AztecAddress, input_amount: Field, output_asset_bridge: AztecAddress, - // params for using the unshield approval - nonce_for_unshield_approval: Field, + // params for using the transfer_to_public approval + nonce_for_transfer_to_public_approval: Field, // params for the swap uniswap_fee_tier: Field, // which uniswap tier to use (eg 3000 for 0.3% fee) minimum_output_amount: Field, // minimum output amount to receive (slippage protection for the swap) @@ -133,11 +133,11 @@ contract Uniswap { // Transfer funds to this contract Token::at(input_asset) - .unshield( + .transfer_to_public( context.msg_sender(), context.this_address(), input_amount, - nonce_for_unshield_approval, + nonce_for_transfer_to_public_approval, ) .call(&mut context); diff --git a/yarn-project/aztec.js/src/fee/private_fee_payment_method.ts b/yarn-project/aztec.js/src/fee/private_fee_payment_method.ts index 93618e26136..2c4f17dae49 100644 --- a/yarn-project/aztec.js/src/fee/private_fee_payment_method.ts +++ b/yarn-project/aztec.js/src/fee/private_fee_payment_method.ts @@ -57,9 +57,9 @@ export class PrivateFeePaymentMethod implements FeePaymentMethod { await this.wallet.createAuthWit({ caller: this.paymentContract, action: { - name: 'unshield', + name: 'transfer_to_public', args: [this.wallet.getCompleteAddress().address, this.paymentContract, maxFee, nonce], - selector: FunctionSelector.fromSignature('unshield((Field),(Field),Field,Field)'), + selector: FunctionSelector.fromSignature('transfer_to_public((Field),(Field),Field,Field)'), type: FunctionType.PRIVATE, isStatic: false, to: this.asset, diff --git a/yarn-project/aztec/src/examples/token.ts b/yarn-project/aztec/src/examples/token.ts index 488377f79d7..754640d49e2 100644 --- a/yarn-project/aztec/src/examples/token.ts +++ b/yarn-project/aztec/src/examples/token.ts @@ -1,5 +1,5 @@ import { getSingleKeyAccount } from '@aztec/accounts/single_key'; -import { type AccountWallet, BatchCall, Fr, createPXEClient } from '@aztec/aztec.js'; +import { type AccountWallet, Fr, createPXEClient } from '@aztec/aztec.js'; import { createDebugLogger } from '@aztec/foundation/log'; import { TokenContract } from '@aztec/noir-contracts.js/Token'; @@ -41,15 +41,7 @@ async function main() { // Mint tokens to Alice logger.info(`Minting ${ALICE_MINT_BALANCE} more coins to Alice...`); - - // We don't have the functionality to mint to private so we mint to the Alice's address in public and transfer - // the tokens to private. We use BatchCall to speed the process up. - await new BatchCall(aliceWallet, [ - token.methods.mint_public(aliceWallet.getAddress(), ALICE_MINT_BALANCE).request(), - token.methods.transfer_to_private(aliceWallet.getAddress(), ALICE_MINT_BALANCE).request(), - ]) - .send() - .wait(); + await tokenAlice.methods.mint_to_private(aliceWallet.getAddress(), ALICE_MINT_BALANCE).send().wait(); logger.info(`${ALICE_MINT_BALANCE} tokens were successfully minted by Alice and transferred to private`); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/unshielding.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/unshielding.test.ts index 40c9dcc4273..e23372a34c0 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/unshielding.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/unshielding.test.ts @@ -31,7 +31,7 @@ describe('e2e_blacklist_token_contract unshielding', () => { await asset.methods.unshield(wallets[0].getAddress(), wallets[0].getAddress(), amount, 0).send().wait(); - tokenSim.unshield(wallets[0].getAddress(), wallets[0].getAddress(), amount); + tokenSim.transferToPublic(wallets[0].getAddress(), wallets[0].getAddress(), amount); }); it('on behalf of other', async () => { @@ -54,7 +54,7 @@ describe('e2e_blacklist_token_contract unshielding', () => { wallets[1].setScopes([wallets[1].getAddress(), wallets[0].getAddress()]); await action.send().wait(); - tokenSim.unshield(wallets[0].getAddress(), wallets[1].getAddress(), amount); + tokenSim.transferToPublic(wallets[0].getAddress(), wallets[1].getAddress(), amount); // Perform the transfer again, should fail const txReplay = asset diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public_to_private.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public_to_private.test.ts index 60e71effabe..8f95f9be159 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public_to_private.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public_to_private.test.ts @@ -51,8 +51,8 @@ describe('e2e_cross_chain_messaging token_bridge_public_to_private', () => { await crossChainTestHarness.expectPublicBalanceOnL2(ownerAddress, bridgeAmount - shieldAmount); await crossChainTestHarness.expectPrivateBalanceOnL2(ownerAddress, shieldAmount); - // Unshield the tokens again, sending them to the same account, however this can be any account. - await crossChainTestHarness.unshieldTokensOnL2(shieldAmount); + // Transfer to public the tokens again, sending them to the same account, however this can be any account. + await crossChainTestHarness.transferToPublicOnL2(shieldAmount); await crossChainTestHarness.expectPublicBalanceOnL2(ownerAddress, bridgeAmount); await crossChainTestHarness.expectPrivateBalanceOnL2(ownerAddress, 0n); }); diff --git a/yarn-project/end-to-end/src/e2e_fees/account_init.test.ts b/yarn-project/end-to-end/src/e2e_fees/account_init.test.ts index 53b0b18e510..11542650945 100644 --- a/yarn-project/end-to-end/src/e2e_fees/account_init.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/account_init.test.ts @@ -125,7 +125,7 @@ describe('e2e_fees account_init', () => { // the new account should have paid the full fee to the FPC await expect(t.getBananaPrivateBalanceFn(bobsAddress)).resolves.toEqual([mintedBananas - maxFee]); - // the FPC got paid through "unshield", so it's got a new public balance + // the FPC got paid through "transfer_to_public", so it's got a new public balance await expect(t.getBananaPublicBalanceFn(bananaFPC.address)).resolves.toEqual([ fpcsInitialPublicBananas + actualFee, ]); diff --git a/yarn-project/end-to-end/src/e2e_fees/dapp_subscription.test.ts b/yarn-project/end-to-end/src/e2e_fees/dapp_subscription.test.ts index 681ef173277..3391e624cc5 100644 --- a/yarn-project/end-to-end/src/e2e_fees/dapp_subscription.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/dapp_subscription.test.ts @@ -99,7 +99,7 @@ describe('e2e_fees dapp_subscription', () => { it('should allow Alice to subscribe by paying privately with bananas', async () => { /** PRIVATE SETUP - we first unshield `MAX_FEE` BC from alice's private balance to the FPC's public balance + we first transfer `MAX_FEE` BC from alice's private balance to the FPC's public balance PUBLIC APP LOGIC we then privately transfer `SUBSCRIPTION_AMOUNT` BC from alice to bob's subscription contract diff --git a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts index 1687554c577..4d1f37ccdc5 100644 --- a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts @@ -128,7 +128,12 @@ describe('e2e_lending_contract', () => { const nonce = Fr.random(); await wallet.createAuthWit({ caller: lendingContract.address, - action: collateralAsset.methods.unshield(lendingAccount.address, lendingContract.address, depositAmount, nonce), + action: collateralAsset.methods.transfer_to_public( + lendingAccount.address, + lendingContract.address, + depositAmount, + nonce, + ), }); await lendingSim.progressSlots(SLOT_JUMP); lendingSim.depositPrivate(lendingAccount.address, lendingAccount.key(), depositAmount); @@ -157,7 +162,12 @@ describe('e2e_lending_contract', () => { const nonce = Fr.random(); await wallet.createAuthWit({ caller: lendingContract.address, - action: collateralAsset.methods.unshield(lendingAccount.address, lendingContract.address, depositAmount, nonce), + action: collateralAsset.methods.transfer_to_public( + lendingAccount.address, + lendingContract.address, + depositAmount, + nonce, + ), }); await lendingSim.progressSlots(SLOT_JUMP); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/unshielding.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_public.test.ts similarity index 81% rename from yarn-project/end-to-end/src/e2e_token_contract/unshielding.test.ts rename to yarn-project/end-to-end/src/e2e_token_contract/transfer_to_public.test.ts index da7e80485d5..dbecfab9212 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/unshielding.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_public.test.ts @@ -3,8 +3,8 @@ import { Fr, computeAuthWitMessageHash } from '@aztec/aztec.js'; import { DUPLICATE_NULLIFIER_ERROR } from '../fixtures/fixtures.js'; import { TokenContractTest } from './token_contract_test.js'; -describe('e2e_token_contract unshielding', () => { - const t = new TokenContractTest('unshielding'); +describe('e2e_token_contract transfer_to_public', () => { + const t = new TokenContractTest('transfer_to_public'); let { asset, accounts, tokenSim, wallets } = t; beforeAll(async () => { @@ -28,9 +28,9 @@ describe('e2e_token_contract unshielding', () => { const amount = balancePriv / 2n; expect(amount).toBeGreaterThan(0n); - await asset.methods.unshield(accounts[0].address, accounts[0].address, amount, 0).send().wait(); + await asset.methods.transfer_to_public(accounts[0].address, accounts[0].address, amount, 0).send().wait(); - tokenSim.unshield(accounts[0].address, accounts[0].address, amount); + tokenSim.transferToPublic(accounts[0].address, accounts[0].address, amount); }); it('on behalf of other', async () => { @@ -42,23 +42,23 @@ describe('e2e_token_contract unshielding', () => { // We need to compute the message we want to sign and add it to the wallet as approved const action = asset .withWallet(wallets[1]) - .methods.unshield(accounts[0].address, accounts[1].address, amount, nonce); + .methods.transfer_to_public(accounts[0].address, accounts[1].address, amount, nonce); // Both wallets are connected to same node and PXE so we could just insert directly // But doing it in two actions to show the flow. const witness = await wallets[0].createAuthWit({ caller: accounts[1].address, action }); await wallets[1].addAuthWitness(witness); - // We give wallets[1] access to wallets[0]'s notes to unshield the note. + // We give wallets[1] access to wallets[0]'s notes to transfer the tokens to public. wallets[1].setScopes([wallets[1].getAddress(), wallets[0].getAddress()]); await action.send().wait(); - tokenSim.unshield(accounts[0].address, accounts[1].address, amount); + tokenSim.transferToPublic(accounts[0].address, accounts[1].address, amount); // Perform the transfer again, should fail const txReplay = asset .withWallet(wallets[1]) - .methods.unshield(accounts[0].address, accounts[1].address, amount, nonce) + .methods.transfer_to_public(accounts[0].address, accounts[1].address, amount, nonce) .send(); await expect(txReplay.wait()).rejects.toThrow(DUPLICATE_NULLIFIER_ERROR); }); @@ -70,7 +70,7 @@ describe('e2e_token_contract unshielding', () => { expect(amount).toBeGreaterThan(0n); await expect( - asset.methods.unshield(accounts[0].address, accounts[0].address, amount, 0).simulate(), + asset.methods.transfer_to_public(accounts[0].address, accounts[0].address, amount, 0).simulate(), ).rejects.toThrow('Assertion failed: Balance too low'); }); @@ -80,7 +80,7 @@ describe('e2e_token_contract unshielding', () => { expect(amount).toBeGreaterThan(0n); await expect( - asset.methods.unshield(accounts[0].address, accounts[0].address, amount, 1).simulate(), + asset.methods.transfer_to_public(accounts[0].address, accounts[0].address, amount, 1).simulate(), ).rejects.toThrow('Assertion failed: invalid nonce'); }); @@ -93,7 +93,7 @@ describe('e2e_token_contract unshielding', () => { // We need to compute the message we want to sign and add it to the wallet as approved const action = asset .withWallet(wallets[1]) - .methods.unshield(accounts[0].address, accounts[1].address, amount, nonce); + .methods.transfer_to_public(accounts[0].address, accounts[1].address, amount, nonce); // Both wallets are connected to same node and PXE so we could just insert directly // But doing it in two actions to show the flow. @@ -112,7 +112,7 @@ describe('e2e_token_contract unshielding', () => { // We need to compute the message we want to sign and add it to the wallet as approved const action = asset .withWallet(wallets[2]) - .methods.unshield(accounts[0].address, accounts[1].address, amount, nonce); + .methods.transfer_to_public(accounts[0].address, accounts[1].address, amount, nonce); const expectedMessageHash = computeAuthWitMessageHash( { caller: accounts[2].address, action }, { chainId: wallets[0].getChainId(), version: wallets[0].getVersion() }, diff --git a/yarn-project/end-to-end/src/fixtures/token_utils.ts b/yarn-project/end-to-end/src/fixtures/token_utils.ts index a0c570648a2..d557bbea2ff 100644 --- a/yarn-project/end-to-end/src/fixtures/token_utils.ts +++ b/yarn-project/end-to-end/src/fixtures/token_utils.ts @@ -1,4 +1,4 @@ -import { type AztecAddress, BatchCall, type DebugLogger, type Wallet, retryUntil } from '@aztec/aztec.js'; +import { type AztecAddress, type DebugLogger, type Wallet, retryUntil } from '@aztec/aztec.js'; import { TokenContract } from '@aztec/noir-contracts.js'; export async function deployToken(adminWallet: Wallet, initialAdminBalance: bigint, logger: DebugLogger) { @@ -23,14 +23,8 @@ export async function mintTokensToPrivate( recipient: AztecAddress, amount: bigint, ) { - // We don't have the functionality to mint to private so we mint to the minter address in public and transfer - // the tokens to the recipient in private. We use BatchCall to speed the process up. - await new BatchCall(minterWallet, [ - token.methods.mint_public(minterWallet.getAddress(), amount).request(), - token.methods.transfer_to_private(recipient, amount).request(), - ]) - .send() - .wait(); + const tokenAsMinter = await TokenContract.at(token.address, minterWallet); + await tokenAsMinter.methods.mint_to_private(recipient, amount).send().wait(); } const awaitUserSynchronized = async (wallet: Wallet, owner: AztecAddress) => { diff --git a/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts b/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts index a73e8fbd0b8..e78d1973d38 100644 --- a/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts +++ b/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts @@ -3,7 +3,6 @@ import { AccountManager, AuthWitness, type AuthWitnessProvider, - BatchCall, type CompleteAddress, Fr, GrumpkinScalar, @@ -64,15 +63,8 @@ describe('guides/writing_an_account_contract', () => { const token = await TokenContract.deploy(wallet, address, 'TokenName', 'TokenSymbol', 18).send().deployed(); logger.info(`Deployed token contract at ${token.address}`); - // We don't have the functionality to mint to private so we mint to the minter address in public and transfer - // the tokens to the recipient in private. We use BatchCall to speed the process up. const mintAmount = 50n; - await new BatchCall(wallet, [ - token.methods.mint_public(address, mintAmount).request(), - token.methods.transfer_to_private(address, mintAmount).request(), - ]) - .send() - .wait(); + await token.methods.mint_to_private(address, mintAmount).send().wait(); const balance = await token.methods.balance_of_private(address).simulate(); logger.info(`Balance of wallet is now ${balance}`); diff --git a/yarn-project/end-to-end/src/sample-dapp/index.mjs b/yarn-project/end-to-end/src/sample-dapp/index.mjs index 5b0d6822a4c..62f43ba68d0 100644 --- a/yarn-project/end-to-end/src/sample-dapp/index.mjs +++ b/yarn-project/end-to-end/src/sample-dapp/index.mjs @@ -1,6 +1,6 @@ // docs:start:imports import { getInitialTestAccountsWallets } from '@aztec/accounts/testing'; -import { BatchCall, createPXEClient, waitForPXE } from '@aztec/aztec.js'; +import { createPXEClient, waitForPXE } from '@aztec/aztec.js'; import { fileURLToPath } from '@aztec/foundation/url'; import { getToken } from './contracts.mjs'; @@ -38,15 +38,9 @@ async function mintPrivateFunds(pxe) { await showPrivateBalances(pxe); + // We mint tokens to the owner const mintAmount = 20n; - // We don't have the functionality to mint to private so we mint to the owner address in public and transfer - // the tokens to the recipient in private. We use BatchCall to speed the process up. - await new BatchCall(ownerWallet, [ - token.methods.mint_public(ownerWallet.getAddress(), mintAmount).request(), - token.methods.transfer_to_private(ownerWallet.getAddress(), mintAmount).request(), - ]) - .send() - .wait(); + await token.methods.mint_to_private(ownerWallet.getAddress(), mintAmount).send().wait(); await showPrivateBalances(pxe); } diff --git a/yarn-project/end-to-end/src/shared/browser.ts b/yarn-project/end-to-end/src/shared/browser.ts index d0d7363ca85..75e1019196e 100644 --- a/yarn-project/end-to-end/src/shared/browser.ts +++ b/yarn-project/end-to-end/src/shared/browser.ts @@ -251,9 +251,8 @@ export const browserTestSuite = ( console.log(`Contract Deployed: ${token.address}`); - // We don't use the `mintTokensToPrivate` util as it is not available here - await token.methods.mint_public(owner.getAddress(), initialBalance).send().wait(); - await token.methods.transfer_to_private(owner.getAddress(), initialBalance).send().wait(); + // We mint tokens to the owner + await token.methods.mint_to_private(owner.getAddress(), initialBalance).send().wait(); return [txHash.toString(), token.address.toString()]; }, diff --git a/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts b/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts index c611c233e46..a32510fdb8a 100644 --- a/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts +++ b/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts @@ -369,9 +369,9 @@ export class CrossChainTestHarness { await this.l2Token.methods.redeem_shield(this.ownerAddress, shieldAmount, secret).send().wait(); } - async unshieldTokensOnL2(unshieldAmount: bigint, nonce = Fr.ZERO) { - this.logger.info('Unshielding tokens'); - await this.l2Token.methods.unshield(this.ownerAddress, this.ownerAddress, unshieldAmount, nonce).send().wait(); + async transferToPublicOnL2(amount: bigint, nonce = Fr.ZERO) { + this.logger.info('Transferring tokens to public'); + await this.l2Token.methods.transfer_to_public(this.ownerAddress, this.ownerAddress, amount, nonce).send().wait(); } /** diff --git a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts index c3ef0f64e31..d33bcb1ccab 100644 --- a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts +++ b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts @@ -207,16 +207,16 @@ export const uniswapL1L2TestSuite = ( const wethL2BalanceBeforeSwap = await wethCrossChainHarness.getL2PrivateBalanceOf(ownerAddress); const daiL2BalanceBeforeSwap = await daiCrossChainHarness.getL2PrivateBalanceOf(ownerAddress); - // 3. Owner gives uniswap approval to unshield funds to self on its behalf - logger.info('Approving uniswap to unshield funds to self on my behalf'); - const nonceForWETHUnshieldApproval = new Fr(1n); + // 3. Owner gives uniswap approval to transfer the funds to public to self on its behalf + logger.info('Approving uniswap to transfer funds to public to self on my behalf'); + const nonceForWETHTransferToPublicApproval = new Fr(1n); await ownerWallet.createAuthWit({ caller: uniswapL2Contract.address, - action: wethCrossChainHarness.l2Token.methods.unshield( + action: wethCrossChainHarness.l2Token.methods.transfer_to_public( ownerAddress, uniswapL2Contract.address, wethAmountToBridge, - nonceForWETHUnshieldApproval, + nonceForWETHTransferToPublicApproval, ), }); @@ -231,7 +231,7 @@ export const uniswapL1L2TestSuite = ( wethCrossChainHarness.l2Bridge.address, wethAmountToBridge, daiCrossChainHarness.l2Bridge.address, - nonceForWETHUnshieldApproval, + nonceForWETHTransferToPublicApproval, uniswapFeeTier, minimumOutputAmount, secretHashForRedeemingDai, @@ -610,15 +610,20 @@ export const uniswapL1L2TestSuite = ( // Edge cases for the private flow: // note - tests for uniswapPortal.sol and minting asset on L2 are covered in other tests. - it('swap_private reverts without unshield approval', async () => { + it('swap_private reverts without transfer to public approval', async () => { // swap should fail since no withdraw approval to uniswap: - const nonceForWETHUnshieldApproval = new Fr(2n); + const nonceForWETHTransferToPublicApproval = new Fr(2n); const expectedMessageHash = computeAuthWitMessageHash( { caller: uniswapL2Contract.address, action: wethCrossChainHarness.l2Token.methods - .unshield(ownerAddress, uniswapL2Contract.address, wethAmountToBridge, nonceForWETHUnshieldApproval) + .transfer_to_public( + ownerAddress, + uniswapL2Contract.address, + wethAmountToBridge, + nonceForWETHTransferToPublicApproval, + ) .request(), }, { chainId: ownerWallet.getChainId(), version: ownerWallet.getVersion() }, @@ -631,7 +636,7 @@ export const uniswapL1L2TestSuite = ( wethCrossChainHarness.l2Bridge.address, wethAmountToBridge, daiCrossChainHarness.l2Bridge.address, - nonceForWETHUnshieldApproval, + nonceForWETHTransferToPublicApproval, uniswapFeeTier, minimumOutputAmount, Fr.random(), @@ -647,16 +652,16 @@ export const uniswapL1L2TestSuite = ( await wethCrossChainHarness.mintTokensPrivateOnL2(wethAmountToBridge); await wethCrossChainHarness.expectPrivateBalanceOnL2(ownerAddress, wethAmountToBridge); - // 2. owner gives uniswap approval to unshield funds: - logger.info('Approving uniswap to unshield funds to self on my behalf'); - const nonceForWETHUnshieldApproval = new Fr(3n); + // 2. owner gives uniswap approval to transfer the funds to public: + logger.info('Approving uniswap to transfer funds to public to self on my behalf'); + const nonceForWETHTransferToPublicApproval = new Fr(3n); await ownerWallet.createAuthWit({ caller: uniswapL2Contract.address, - action: wethCrossChainHarness.l2Token.methods.unshield( + action: wethCrossChainHarness.l2Token.methods.transfer_to_public( ownerAddress, uniswapL2Contract.address, wethAmountToBridge, - nonceForWETHUnshieldApproval, + nonceForWETHTransferToPublicApproval, ), }); @@ -669,7 +674,7 @@ export const uniswapL1L2TestSuite = ( daiCrossChainHarness.l2Bridge.address, // but dai bridge! wethAmountToBridge, daiCrossChainHarness.l2Bridge.address, - nonceForWETHUnshieldApproval, + nonceForWETHTransferToPublicApproval, uniswapFeeTier, minimumOutputAmount, Fr.random(), @@ -803,16 +808,16 @@ export const uniswapL1L2TestSuite = ( logger.info('minting weth on L2'); await wethCrossChainHarness.mintTokensPrivateOnL2(wethAmountToBridge); - // Owner gives uniswap approval to unshield funds to self on its behalf - logger.info('Approving uniswap to unshield funds to self on my behalf'); - const nonceForWETHUnshieldApproval = new Fr(4n); + // Owner gives uniswap approval to transfer the funds to public to self on its behalf + logger.info('Approving uniswap to transfer the funds to public to self on my behalf'); + const nonceForWETHTransferToPublicApproval = new Fr(4n); await ownerWallet.createAuthWit({ caller: uniswapL2Contract.address, - action: wethCrossChainHarness.l2Token.methods.unshield( + action: wethCrossChainHarness.l2Token.methods.transfer_to_public( ownerAddress, uniswapL2Contract.address, wethAmountToBridge, - nonceForWETHUnshieldApproval, + nonceForWETHTransferToPublicApproval, ), }); const wethL2BalanceBeforeSwap = await wethCrossChainHarness.getL2PrivateBalanceOf(ownerAddress); @@ -828,7 +833,7 @@ export const uniswapL1L2TestSuite = ( wethCrossChainHarness.l2Bridge.address, wethAmountToBridge, daiCrossChainHarness.l2Bridge.address, - nonceForWETHUnshieldApproval, + nonceForWETHTransferToPublicApproval, uniswapFeeTier, minimumOutputAmount, secretHashForRedeemingDai, diff --git a/yarn-project/end-to-end/src/simulators/lending_simulator.ts b/yarn-project/end-to-end/src/simulators/lending_simulator.ts index 557677b5da4..9340a165553 100644 --- a/yarn-project/end-to-end/src/simulators/lending_simulator.ts +++ b/yarn-project/end-to-end/src/simulators/lending_simulator.ts @@ -117,7 +117,7 @@ export class LendingSimulator { } depositPrivate(from: AztecAddress, onBehalfOf: Fr, amount: bigint) { - this.collateralAsset.unshield(from, this.lendingContract.address, amount); + this.collateralAsset.transferToPublic(from, this.lendingContract.address, amount); this.deposit(onBehalfOf, amount); } diff --git a/yarn-project/end-to-end/src/simulators/token_simulator.ts b/yarn-project/end-to-end/src/simulators/token_simulator.ts index c4addad4b21..03caf19ab16 100644 --- a/yarn-project/end-to-end/src/simulators/token_simulator.ts +++ b/yarn-project/end-to-end/src/simulators/token_simulator.ts @@ -65,7 +65,7 @@ export class TokenSimulator { this.balancesPrivate.set(to.toString(), toBalance + amount); } - public unshield(from: AztecAddress, to: AztecAddress, amount: bigint) { + public transferToPublic(from: AztecAddress, to: AztecAddress, amount: bigint) { const fromBalance = this.balancesPrivate.get(from.toString()) || 0n; const toBalance = this.balancePublic.get(to.toString()) || 0n; expect(fromBalance).toBeGreaterThanOrEqual(amount); diff --git a/yarn-project/protocol-contracts/src/protocol_contract_data.ts b/yarn-project/protocol-contracts/src/protocol_contract_data.ts index 12ee3e96237..d7f5390acb2 100644 --- a/yarn-project/protocol-contracts/src/protocol_contract_data.ts +++ b/yarn-project/protocol-contracts/src/protocol_contract_data.ts @@ -50,14 +50,14 @@ export const ProtocolContractAddress: Record }; export const ProtocolContractLeaf = { - AuthRegistry: Fr.fromString('0x16f00633c07cb18f29a819d16a8b5140dea24787ca2a030dc54379a8e6896e3e'), + AuthRegistry: Fr.fromString('0x2c8f0ae77bbed1244767430f7bf1badf98219c40a1dfc1bba1409caf1a9a01cd'), ContractInstanceDeployer: Fr.fromString('0x04a661c9d4d295fc485a7e0f3de40c09b35366343bce8ad229106a8ef4076fe5'), ContractClassRegisterer: Fr.fromString('0x147ba3294403576dbad10f86d3ffd4eb83fb230ffbcd5c8b153dd02942d0611f'), MultiCallEntrypoint: Fr.fromString('0x154b701b41d6cf6da7204fef36b2ee9578b449d21b3792a9287bf45eba48fd26'), - FeeJuice: Fr.fromString('0x2422b0101aba5f5050e8c086a6bdd62b185b188acfba58c77b0016769b96efd6'), - Router: Fr.fromString('0x1cedd0ce59239cb4d55408257d8942bdbd1ac6f0ddab9980157255391dbaa596'), + FeeJuice: Fr.fromString('0x14c807b45f74c73f4b7c7aa85162fc1ae78fafa81ece6c22426fec928edd9471'), + Router: Fr.fromString('0x2cf76c7258a26c77ec56a5b4903996b1e0a21313f78e166f408e955393144665'), }; export const protocolContractTreeRoot = Fr.fromString( - '0x21fb5ab0a0a9b4f282d47d379ec7b5fdf59457a19a593c17f7c29954e1e88dec', + '0x27d6a49f48a8f07db1170672df19cec71fcb71d97588503150628483943e2a14', );