diff --git a/noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr b/noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr index ca826720e141..42f52add989d 100644 --- a/noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr +++ b/noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr @@ -51,68 +51,10 @@ where } } - /// Makes the call to this private function. - /// - /// # Arguments - /// * `context` - The PrivateContext -- made magically available to the body - /// of every #[external("private")] function as `context`, through the - /// #[external("private")] annotation's macro. - /// - /// # Returns - /// * `T` - Whatever data the called function has returned. - /// - /// This enables contracts to interact with each other while maintaining - /// privacy. This "composability" of private contract functions is a key - /// feature of the Aztec network. - /// - /// If a user's transaction includes multiple private function calls, then - /// by the design of Aztec, the following information will remain private[1]: - /// - The function selectors and contract addresses of all private function - /// calls will remain private, so an observer of the public mempool will - /// not be able to look at a tx and deduce which private functions have - /// been executed. - /// - The arguments and return values of all private function calls will - /// remain private. - /// - The person who initiated the tx will remain private. - /// - The notes and nullifiers and private logs that are emitted by all - /// private function calls will (if designed well) not leak any user - /// secrets, nor leak which functions have been executed. - /// - /// [1] Caveats: Some of these privacy guarantees depend on how app - /// developers design their smart contracts. Some actions _can_ leak - /// information, such as: - /// - Calling an internal public function. - /// - Calling a public function and not setting msg_sender to Option::none - /// (see https://github.com/AztecProtocol/aztec-packages/pull/16433) - /// - Calling any public function will always leak details about the nature - /// of the transaction, so devs should be careful in their contract - /// designs. If it can be done in a private function, then that will give - /// the best privacy. - /// - Not padding the side-effects of a tx to some standardised, uniform - /// size. The kernel circuits can take hints to pad side-effects, so a - /// wallet should be able to request for a particular amount of padding. - /// Wallets should ideally agree on some standard. - /// - Padding should include: - /// - Padding the lengths of note & nullifier arrays - /// - Padding private logs with random fields, up to some standardised - /// size. - /// See also: https://docs.aztec.network/developers/resources/considerations/privacy_considerations - /// - /// # Advanced - /// * The call is added to the private call stack and executed by kernel - /// circuits after this function completes - /// * The called function can modify its own contract's private state - /// * Side effects from the called function are included in this transaction - /// * The call inherits the current transaction's context and gas limits - /// - /// # Arguments - /// * `context` - The PrivateContext -- made magically available to the body - /// of every #[external("private")] function as `context`, through the - /// #[external("private")] annotation's macro. - /// - /// # Returns - /// * `T` - Whatever data the called function has returned. - /// + /// **[DEPRECATED]** + /// This function is deprecated. Please use the new contract API: + /// `self.call(MyContract::at(address).my_private_function(...args))` + /// instead of manually constructing and calling `PrivateCallInterface`. pub fn call(self, context: &mut PrivateContext) -> T { execution_cache::store(self.args, self.args_hash); let returns_hash = context.call_private_function_with_args_hash( @@ -127,22 +69,7 @@ where returns_hash.get_preimage() } - /// Makes a _read-only_ call to this private function. - /// - /// This is similar to Solidity's `staticcall`. The called function - /// cannot modify state, emit L2->L1 messages, nor emit events. Any nested - /// calls are constrained to also be staticcalls. - /// - /// See `call` for more general info on private function calls. - /// - /// # Arguments - /// * `context` - The PrivateContext -- made magically available to the body - /// of every #[external("private")] function as `context`, through the - /// #[external("private")] annotation's macro. - /// - /// # Returns - /// * `T` - Whatever data the called function has returned. - /// + /// TODO(F-130): Drop this function. This should be present only on PrivateStaticCallInterface. pub fn view(self, context: &mut PrivateContext) -> T { execution_cache::store(self.args, self.args_hash); let returns_hash = context.call_private_function_with_args_hash( @@ -210,17 +137,10 @@ impl PrivateStaticCallInterface { } } - /// Makes a read-only call to this private function. - /// - /// This is similar to Solidity's `staticcall`. The called function - /// cannot modify state, emit L2->L1 messages, nor emit events. Any nested - /// calls are constrained to also be staticcalls. - /// - /// # Arguments - /// * `context` - The PrivateContext -- made magically available to the body - /// of every #[external("private")] function as `context`, through the - /// #[external("private")] annotation's macro. - /// + /// **[DEPRECATED]** + /// This function is deprecated. Please use the new contract API: + /// `self.view(MyContract::at(address).my_private_static_function(...args))` + /// instead of manually constructing and calling `PrivateCallInterface`. pub fn view(self, context: &mut PrivateContext) -> T where T: Deserialize, @@ -293,23 +213,15 @@ where } } - pub fn with_gas(self: &mut Self, gas_opts: GasOpts) -> &mut Self { + pub fn with_gas(mut self, gas_opts: GasOpts) -> Self { self.gas_opts = gas_opts; self } - /// Makes the call to this public function. - /// - /// Will revert if the called function reverts or runs out of gas. - /// - /// # Arguments - /// * `context` - The PublicContext -- made magically available to the body - /// of every #[external("public")] function as `context`, through the - /// #[external("public")] annotation's macro. - /// - /// # Returns - /// * `T` - Whatever data the called function has returned. - /// + /// **[DEPRECATED]** + /// This function is deprecated. Please use the new contract API: + /// `self.call(MyContract::at(address).my_public_function(...args))` + /// instead of manually constructing and calling `PublicCallInterface`. pub unconstrained fn call(self, context: PublicContext) -> T { let returns = context.call_public_function( self.target_contract, @@ -322,22 +234,7 @@ where Deserialize::deserialize(returns.as_array()) } - /// Makes a read-only call to this public function. - /// - /// This is similar to Solidity's `staticcall`. The called function - /// cannot modify state or emit events. Any nested calls are constrained to - /// also be staticcalls. - /// - /// Will revert if the called function reverts or runs out of gas. - /// - /// # Arguments - /// * `context` - The PublicContext -- made magically available to the body - /// of every #[external("public")] function as `context`, through the - /// #[external("public")] annotation's macro. - /// - /// # Returns - /// * `T` - Whatever data the called function has returned. - /// + /// TODO(F-130): Drop this function. This should be present only on PublicStaticCallInterface. pub unconstrained fn view(self, context: PublicContext) -> T { let returns = context.static_call_public_function( self.target_contract, @@ -350,109 +247,28 @@ where Deserialize::deserialize(returns.as_array()) } - /// Enqueues a call to this public function, to be executed later. - /// - /// Unlike private functions which execute immediately on the user's device, - /// public function calls are "enqueued" and executed some time later by a - /// block proposer. - /// - /// This means a public function cannot return any values back to a private - /// function, because by the time the public function is being executed, - /// the private function which called it has already completed execution. - /// (In fact, the private function has been executed and proven, along with - /// all other private function calls of the user's tx. A single proof of the - /// tx has been submitted to the Aztec network, and some time later a - /// proposer has picked the tx up from the mempool and begun executing all - /// of the enqueued public functions). - /// - /// # Privacy warning - /// Enqueueing a public function call is an inherently leaky action. - /// Many interesting applications will require some interaction with public - /// state, but smart contract developers should try to use public function - /// calls sparingly, and carefully. - /// _Internal_ public function calls are especially leaky, because they - /// completely leak which private contract made the call. - /// See also: https://docs.aztec.network/developers/resources/considerations/privacy_considerations - /// - /// # Arguments - /// * `context` - The PrivateContext -- made magically available to the body - /// of every #[external("private")] function as `context`, through the - /// #[external("private")] annotation's macro. - /// + /// **[DEPRECATED]** + /// This function is deprecated. Please use the new contract API: + /// `self.enqueue(MyContract::at(address).my_public_function(...args))` + /// instead of manually constructing and calling `PublicCallInterface`. pub fn enqueue(self, context: &mut PrivateContext) { self.enqueue_impl(context, false, false) } - /// Enqueues a call to this public function, to be executed later. - /// - /// As per `enqueue`, but hides this calling contract's address from the - /// target public function. - /// This means the origin of the call (msg_sender) will not be publicly - /// visible to any blockchain observers, nor to the target public function. - /// When the target public function reads `context.msg_sender()` it will - /// receive an `Option::none`. - /// - /// NOTES: - /// - Not all public functions will accept a msg_sender of "none". Many - /// public functions will require that msg_sender is "some" and will - /// revert otherwise. Therefore, if using `enqueue_incognito`, you must - /// understand whether the function you're calling will accept a - /// msg_sender of "none". - /// Lots of public bookkeeping patterns rely on knowing which address made - /// the call, so as to ascribe state against the caller's address. - /// (There are patterns whereby bookkeeping could instead be done in - /// private-land). - /// - If you are enqueueing a call to an _internal_ public function (i.e. - /// a public function that will only accept calls from other functions - /// of its own contract), then by definition a call to it cannot possibly - /// be "incognito": the msg_sender must be its own address, and indeed the - /// called public function will assert this. Tl;dr this is not usable for - /// enqueued internal public calls. - /// - /// # Arguments - /// * `context` - The PrivateContext -- made magically available to the body - /// of every #[external("private")] function as `context`, through the - /// #[external("private")] annotation's macro. - /// - /// Advanced: - /// - The kernel circuits will permit _any_ private function to set the - /// msg_sender field of any enqueued public function call to - /// NULL_MSG_SENDER_CONTRACT_ADDRESS. - /// - When the called public function calls `PublicContext::msg_sender()`, - /// aztec-nr will translate NULL_MSG_SENDER_CONTRACT_ADDRESS into - /// `Option::none` for familiarity to devs. - /// + /// **[DEPRECATED]** + /// This function is deprecated. Please use the new contract API: + /// `self.enqueue_incognito(MyContract::at(address).my_public_function(...args))` + /// instead of manually constructing and calling `PublicCallInterface`. pub fn enqueue_incognito(self, context: &mut PrivateContext) { self.enqueue_impl(context, false, true) } - /// Enqueues a read-only call to this public function. - /// - /// This is similar to Solidity's `staticcall`. The called function - /// cannot modify state, emit L2->L1 messages, nor emit events. Any nested - /// calls are constrained to also be staticcalls. - /// - /// # Arguments - /// * `context` - The PrivateContext -- made magically available to the body - /// of every #[external("private")] function as `context`, through the - /// #[external("private")] annotation's macro. - /// + /// TODO(F-130): Drop this function. This should be present only on PublicStaticCallInterface. pub fn enqueue_view(self, context: &mut PrivateContext) { self.enqueue_impl(context, true, false) } - /// Enqueues a read-only call to this public function. - /// - /// As per `enqueue_view`, but hides this calling contract's address from - /// the target public function. - /// - /// See `enqueue_incognito` for more details relating to hiding msg_sender. - /// - /// # Arguments - /// * `context` - The PrivateContext -- made magically available to the body - /// of every #[external("private")] function as `context`, through the - /// #[external("private")] annotation's macro. - /// + /// TODO(F-130): Drop this function. This should be present only on PublicStaticCallInterface. pub fn enqueue_view_incognito(self, context: &mut PrivateContext) { self.enqueue_impl(context, true, true) } @@ -474,50 +290,18 @@ where ) } - /// Enqueues a call to this public function, and designates it to be the - /// teardown function for this tx. Only one teardown function call can be - /// made by a tx. - /// - /// Niche function: Only wallet developers and paymaster contract developers - /// (aka Fee-payment contracts) will need to make use of this function. - /// - /// Aztec supports a three-phase execution model: setup, app logic, teardown. - /// The phases exist to enable a fee payer to take on the risk of paying - /// a transaction fee, safe in the knowledge that their payment (in whatever - /// token or method the user chooses) will succeed, regardless of whether - /// the app logic will succeed. The "setup" phase ensures the fee payer - /// has sufficient balance to pay the proposer their fees. - /// The teardown phase is primarily intended to: calculate exactly - /// how much the user owes, based on gas consumption, and refund the user - /// any change. - /// - /// Note: in some cases, the cost of refunding the user (i.e. DA costs of - /// tx side-effects) might exceed the refund amount. For app logic with - /// fairly stable and predictable gas consumption, a material refund amount - /// is unlikely. For app logic with unpredictable gas consumption, a - /// refund might be important to the user (e.g. if a hefty function reverts - /// very early). Wallet/FPC/Paymaster developers should be mindful of this. - /// - /// See `enqueue` for more information about enqueuing public function calls. - /// - /// # Arguments - /// * `context` - The PrivateContext -- made magically available to the body - /// of every #[external("private")] function as `context`, through the - /// #[external("private")] annotation's macro. - /// + /// **[DEPRECATED]** + /// This function is deprecated. Please use the new contract API: + /// `self.set_as_teardown(MyContract::at(address).my_public_function(...args))` + /// instead of manually constructing and setting the teardown function `PublicCallInterface`. pub fn set_as_teardown(self, context: &mut PrivateContext) { self.set_as_teardown_impl(context, false); } - /// Enqueues a call to this public function, and designates it to be the teardown - /// function for this tx. Only one teardown function call can be made by a - /// tx. - /// - /// As per `set_as_teardown`, but hides this calling contract's address from - /// the target public function. - /// - /// See `enqueue_incognito` for more details relating to hiding msg_sender. - /// + /// **[DEPRECATED]** + /// This function is deprecated. Please use the new contract API: + /// `self.set_as_teardown_incognito(MyContract::at(address).my_public_function(...args))` + /// instead of manually constructing and setting the teardown function `PublicCallInterface`. pub fn set_as_teardown_incognito(self, context: &mut PrivateContext) { self.set_as_teardown_impl(context, true); } @@ -590,27 +374,15 @@ where } } - pub fn with_gas(self: &mut Self, gas_opts: GasOpts) -> &mut Self { + pub fn with_gas(mut self, gas_opts: GasOpts) -> Self { self.gas_opts = gas_opts; self } - /// Makes the read-only call to this public function. - /// - /// This is similar to Solidity's `staticcall`. The called function - /// cannot modify state or emit events. Any nested calls are constrained to - /// also be staticcalls. - /// - /// Will revert if the called function reverts or runs out of gas. - /// - /// # Arguments - /// * `context` - The PublicContext -- made magically available to the body - /// of every #[external("public")] function as `context`, through the - /// #[external("public")] annotation's macro. - /// - /// # Returns - /// * `T` - Whatever data the called function has returned. - /// + /// **[DEPRECATED]** + /// This function is deprecated. Please use the new contract API: + /// `self.view(MyContract::at(address).my_public_static_function(...args))` + /// instead of manually constructing and calling `PublicStaticCallInterface`. pub unconstrained fn view(self, context: PublicContext) -> T { let returns = context.static_call_public_function( self.target_contract, @@ -621,17 +393,10 @@ where Deserialize::deserialize(returns.as_array()) } - /// Enqueues a read-only call to this public function. - /// - /// This is similar to Solidity's `staticcall`. The called function - /// cannot modify state, emit L2->L1 messages, nor emit events. Any nested - /// calls are constrained to also be staticcalls. - /// - /// # Arguments - /// * `context` - The PrivateContext -- made magically available to the body - /// of every #[external("private")] function as `context`, through the - /// #[external("private")] annotation's macro. - /// + /// **[DEPRECATED]** + /// This function is deprecated. Please use the new contract API: + /// `self.enqueue_view(MyContract::at(address).my_public_static_function(...args))` + /// instead of manually constructing and calling `PublicStaticCallInterface`. pub fn enqueue_view(self, context: &mut PrivateContext) { let calldata = self.args.push_front(self.selector.to_field()); let calldata_hash = hash_calldata(calldata); @@ -645,6 +410,10 @@ where ) } + /// **[DEPRECATED]** + /// This function is deprecated. Please use the new contract API: + /// `self.enqueue_view_incognito(MyContract::at(address).my_public_static_function(...args))` + /// instead of manually constructing and calling `PublicStaticCallInterface`. pub fn enqueue_view_incognito(self, context: &mut PrivateContext) { let calldata = self.args.push_front(self.selector.to_field()); let calldata_hash = hash_calldata(calldata); diff --git a/noir-projects/aztec-nr/aztec/src/contract_self.nr b/noir-projects/aztec-nr/aztec/src/contract_self.nr index 0f9e85811ed0..66f48514dba2 100644 --- a/noir-projects/aztec-nr/aztec/src/contract_self.nr +++ b/noir-projects/aztec-nr/aztec/src/contract_self.nr @@ -1,10 +1,19 @@ use crate::{ - context::{private_context::PrivateContext, public_context::PublicContext}, + context::{ + call_interfaces::{ + PrivateCallInterface, PrivateStaticCallInterface, PublicCallInterface, + PublicStaticCallInterface, + }, + private_context::PrivateContext, + public_context::PublicContext, + }, event::{event_emission::emit_event_in_private, event_interface::EventInterface}, }; use super::{context::utility_context::UtilityContext, event::event_emission::emit_event_in_public}; use protocol_types::{ - address::AztecAddress, constants::NULL_MSG_SENDER_CONTRACT_ADDRESS, traits::Serialize, + address::AztecAddress, + constants::NULL_MSG_SENDER_CONTRACT_ADDRESS, + traits::{Deserialize, Serialize}, }; /// `ContractSelf` is the core interface for interacting with an Aztec contract's own state and context. @@ -198,6 +207,291 @@ impl(&mut self, call: PrivateCallInterface) -> T + where + T: Deserialize, + { + call.call(self.context) + } + + /// Makes a read-only call to the private function defined by the `call` parameter. + /// + /// This is similar to Solidity's `staticcall`. The called function + /// cannot modify state, emit L2->L1 messages, nor emit events. Any nested + /// calls are constrained to also be static calls. + /// + /// # Arguments + /// * `call` - The object representing the read-only private function to invoke. + /// + /// # Returns + /// * `T` - Whatever data the called function has returned. + /// + /// # Example + /// ```noir + /// self.view(Token::at(address).balance_of_private(recipient)); + /// ``` + pub fn view(&mut self, call: PrivateStaticCallInterface) -> T + where + T: Deserialize, + { + call.view(self.context) + } + + /// Enqueues a call to the public function defined by the `call` parameter, + /// to be executed later. + /// + /// Unlike private functions which execute immediately on the user's device, + /// public function calls are "enqueued" and executed some time later by a + /// block proposer. + /// + /// This means a public function cannot return any values back to a private + /// function, because by the time the public function is being executed, + /// the private function which called it has already completed execution. + /// (In fact, the private function has been executed and proven, along with + /// all other private function calls of the user's tx. A single proof of the + /// tx has been submitted to the Aztec network, and some time later a + /// proposer has picked the tx up from the mempool and begun executing all + /// of the enqueued public functions). + /// + /// # Privacy warning + /// Enqueueing a public function call is an inherently leaky action. + /// Many interesting applications will require some interaction with public + /// state, but smart contract developers should try to use public function + /// calls sparingly, and carefully. + /// _Internal_ public function calls are especially leaky, because they + /// completely leak which private contract made the call. + /// See also: https://docs.aztec.network/developers/resources/considerations/privacy_considerations + /// + /// # Arguments + /// * `call` - The interface representing the public function to enqueue. + /// + /// TODO(F-131): We should drop T from here because it is strange as there + /// is no return value. The PublicCallInterface type seems to be defined + /// incorrectly. + pub fn enqueue(&mut self, call: PublicCallInterface) + where + T: Deserialize, + { + call.enqueue(self.context) + } + + /// Enqueues a read-only call to the public function defined by the `call` parameter. + /// + /// This is similar to Solidity's `staticcall`. The called function + /// cannot modify state, emit L2->L1 messages, nor emit events. Any nested + /// calls are constrained to also be static calls. + /// + /// # Arguments + /// * `call` - The object representing the read-only public function to enqueue. + /// + /// # Example + /// ```noir + /// self.enqueue_view(MyContract::at(address).assert_timestamp_less_than(timestamp)); + /// ``` + /// + /// TODO(F-131): We should drop T from here because it is strange as there + /// is no return value. The PublicCallInterface type seems to be defined + /// incorrectly. + pub fn enqueue_view(&mut self, call: PublicStaticCallInterface) + where + T: Deserialize, + { + call.enqueue_view(self.context) + } + + /// Enqueues a call to the public function defined by the `call` parameter, + /// to be executed later. + /// + /// As per `enqueue`, but hides this calling contract's address from the + /// target public function. + /// This means the origin of the call (msg_sender) will not be publicly + /// visible to any blockchain observers, nor to the target public function. + /// When the target public function reads `context.msg_sender()` it will + /// receive an `Option::none`. + /// + /// NOTES: + /// - Not all public functions will accept a msg_sender of "none". Many + /// public functions will require that msg_sender is "some" and will + /// revert otherwise. Therefore, if using `enqueue_incognito`, you must + /// understand whether the function you're calling will accept a + /// msg_sender of "none". + /// Lots of public bookkeeping patterns rely on knowing which address made + /// the call, so as to ascribe state against the caller's address. + /// (There are patterns whereby bookkeeping could instead be done in + /// private-land). + /// - If you are enqueueing a call to an _internal_ public function (i.e. + /// a public function that will only accept calls from other functions + /// of its own contract), then by definition a call to it cannot possibly + /// be "incognito": the msg_sender must be its own address, and indeed the + /// called public function will assert this. Tl;dr this is not usable for + /// enqueued internal public calls. + /// + /// # Arguments + /// * `call` - The object representing the public function to enqueue. + /// + /// # Example + /// ```noir + /// self.enqueue_incognito(Token::at(address).increase_total_supply_by(amount)); + /// ``` + /// + /// Advanced: + /// - The kernel circuits will permit _any_ private function to set the + /// msg_sender field of any enqueued public function call to + /// NULL_MSG_SENDER_CONTRACT_ADDRESS. + /// - When the called public function calls `PublicContext::msg_sender()`, + /// aztec-nr will translate NULL_MSG_SENDER_CONTRACT_ADDRESS into + /// `Option::none` for familiarity to devs. + /// + /// TODO(F-131): We should drop T from here because it is strange as there + /// is no return value. The PublicCallInterface type seems to be defined + /// incorrectly. + pub fn enqueue_incognito(&mut self, call: PublicCallInterface) + where + T: Deserialize, + { + call.enqueue_incognito(self.context) + } + + /// Enqueues a read-only call to the public function defined by the `call` parameter. + /// + /// As per `enqueue_view`, but hides this calling contract's address from + /// the target public function. + /// + /// See `enqueue_incognito` for more details relating to hiding msg_sender. + /// + /// # Arguments + /// * `call` - The object representing the read-only public function to enqueue. + /// + /// # Example + /// ```noir + /// self.enqueue_view_incognito(MyContract::at(address).assert_timestamp_less_than(timestamp)); + /// ``` + /// + /// TODO(F-131): We should drop T from here because it is strange as there + /// is no return value. The PublicCallInterface type seems to be defined + /// incorrectly. + pub fn enqueue_view_incognito(&mut self, call: PublicStaticCallInterface) + where + T: Deserialize, + { + call.enqueue_view_incognito(self.context) + } + + /// Enqueues a call to the public function defined by the `call` parameter, + /// and designates it to be the teardown function for this tx. Only one teardown + /// function call can be made by a tx. + /// + /// Niche function: Only wallet developers and paymaster contract developers + /// (aka Fee-payment contracts) will need to make use of this function. + /// + /// Aztec supports a three-phase execution model: setup, app logic, teardown. + /// The phases exist to enable a fee payer to take on the risk of paying + /// a transaction fee, safe in the knowledge that their payment (in whatever + /// token or method the user chooses) will succeed, regardless of whether + /// the app logic will succeed. The "setup" phase ensures the fee payer + /// has sufficient balance to pay the proposer their fees. + /// The teardown phase is primarily intended to: calculate exactly + /// how much the user owes, based on gas consumption, and refund the user + /// any change. + /// + /// Note: in some cases, the cost of refunding the user (i.e. DA costs of + /// tx side-effects) might exceed the refund amount. For app logic with + /// fairly stable and predictable gas consumption, a material refund amount + /// is unlikely. For app logic with unpredictable gas consumption, a + /// refund might be important to the user (e.g. if a hefty function reverts + /// very early). Wallet/FPC/Paymaster developers should be mindful of this. + /// + /// See `enqueue` for more information about enqueuing public function calls. + /// + /// # Arguments + /// * `call` - The object representing the public function to designate as teardown. + /// + /// TODO(F-131): We should drop T from here because it is strange as there + /// is no return value. The PublicCallInterface type seems to be defined + /// incorrectly. + pub fn set_as_teardown(&mut self, call: PublicCallInterface) + where + T: Deserialize, + { + call.set_as_teardown(self.context) + } + + /// Enqueues a call to the public function defined by the `call` parameter, + /// and designates it to be the teardown function for this tx. Only one teardown + /// function call can be made by a tx. + /// + /// As per `set_as_teardown`, but hides this calling contract's address from + /// the target public function. + /// + /// See `enqueue_incognito` for more details relating to hiding msg_sender. + /// + /// TODO(F-131): We should drop T from here because it is strange as there + /// is no return value. The PublicCallInterface type seems to be defined + /// incorrectly. + pub fn set_as_teardown_incognito(&mut self, call: PublicCallInterface) + where + T: Deserialize, + { + call.set_as_teardown_incognito(self.context) + } } /// Implementation for `ContractSelf` in public execution contexts. @@ -277,6 +571,54 @@ impl ContractSelf(self, call: PublicCallInterface) -> T + where + T: Deserialize, + { + call.call(self.context) + } + + /// Makes the read-only call to the public function defined by the `call` parameter. + /// + /// This is similar to Solidity's `staticcall`. The called function + /// cannot modify state or emit events. Any nested calls are constrained to + /// also be static calls. + /// + /// Will revert if the called function reverts or runs out of gas. + /// + /// # Arguments + /// * `call` - The object representing the read-only public function to invoke. + /// + /// # Returns + /// * `T` - Whatever data the called function has returned. + /// + /// # Example + /// ```noir + /// self.view(Token::at(address).balance_of_public(recipient)); + /// ``` + /// + pub unconstrained fn view(self, call: PublicStaticCallInterface) -> T + where + T: Deserialize, + { + call.view(self.context) + } } /// Implementation for `ContractSelf` in utility execution contexts. diff --git a/noir-projects/noir-contracts/contracts/app/amm_contract/src/main.nr b/noir-projects/noir-contracts/contracts/app/amm_contract/src/main.nr index 32d8eaa3cdd4..79d0f5fb9a61 100644 --- a/noir-projects/noir-contracts/contracts/app/amm_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/app/amm_contract/src/main.nr @@ -104,29 +104,27 @@ pub contract AMM { // We don't yet know how many tokens the sender will actually supply - that can only be computed during public // execution since the amounts supplied must have the same ratio as the live balances. We therefore transfer the // maximum amounts here, and prepare partial notes that return the change to the sender (if any). - let refund_token0_partial_note = token0 + let refund_token0_partial_note = self.call(token0 .transfer_to_public_and_prepare_private_balance_increase( sender, self.address, amount0_max, authwit_nonce, - ) - .call(self.context); + )); - let refund_token1_partial_note = token1 + let refund_token1_partial_note = self.call(token1 .transfer_to_public_and_prepare_private_balance_increase( sender, self.address, amount1_max, authwit_nonce, - ) - .call(self.context); + )); // The number of liquidity tokens to mint for the caller depends on both the live balances and the amount // supplied, both of which can only be known during public execution. We therefore prepare a partial note that // will get completed via minting. let liquidity_partial_note = - liquidity_token.prepare_private_balance_increase(sender).call(self.context); + self.call(liquidity_token.prepare_private_balance_increase(sender)); // We then complete the flow in public. Note that the type of operation and amounts will all be publicly known, // but the identity of the caller is not revealed despite us being able to send tokens to them by completing the @@ -162,10 +160,10 @@ pub contract AMM { // We read the current AMM balance of both tokens. Note that by the time this function is called the token // transfers have already been completed (since those calls were enqueued before this call), and so we need to // subtract the transfer amount to get the pre-deposit balance. - let balance0_plus_amount0_max = token0.balance_of_public(self.address).view(self.context); + let balance0_plus_amount0_max = self.view(token0.balance_of_public(self.address)); let balance0 = balance0_plus_amount0_max - amount0_max; - let balance1_plus_amount1_max = token1.balance_of_public(self.address).view(self.context); + let balance1_plus_amount1_max = self.view(token1.balance_of_public(self.address)); let balance1 = balance1_plus_amount1_max - amount1_max; // With the current balances known, we can calculate the token amounts to the pool, respecting the user's @@ -186,19 +184,21 @@ pub contract AMM { // We can simply skip the refund if the amount to return is 0 in order to save gas: the partial note will // simply stay in public storage and not be completed, but this is not an issue. if (refund_amount_token0 > 0 as u128) { - token0 - .finalize_transfer_to_private(refund_amount_token0, refund_token0_partial_note) - .call(self.context); + self.call(token0.finalize_transfer_to_private( + refund_amount_token0, + refund_token0_partial_note, + )); } if (refund_amount_token1 > 0 as u128) { - token1 - .finalize_transfer_to_private(refund_amount_token1, refund_token1_partial_note) - .call(self.context); + self.call(token1.finalize_transfer_to_private( + refund_amount_token1, + refund_token1_partial_note, + )); } // With the deposit amounts known, we can compute the number of liquidity tokens to mint and finalize the // depositor's partial note. - let total_supply = liquidity_token.total_supply().view(self.context); + let total_supply = self.view(liquidity_token.total_supply()); let liquidity_amount = if total_supply != 0 as u128 { // The liquidity token supply increases by the same ratio as the balances. In case one of the token balances // increased with a ratio different from the other one, we simply take the smallest value. @@ -254,16 +254,12 @@ pub contract AMM { // a function to privately burn, so we instead transfer the tokens into the AMM's public balance, and then have // the AMM publicly burn its own tokens. // TODO(#10287): consider adding a private burn - liquidity_token.transfer_to_public(sender, self.address, liquidity, authwit_nonce).call( - self.context, - ); + self.call(liquidity_token.transfer_to_public(sender, self.address, liquidity, authwit_nonce)); // We don't yet know how many tokens the sender will get - that can only be computed during public execution // since the it depends on the live balances. We therefore simply prepare partial notes to the sender. - let token0_partial_note = - token0.prepare_private_balance_increase(sender).call(self.context); - let token1_partial_note = - token1.prepare_private_balance_increase(sender).call(self.context); + let token0_partial_note = self.call(token0.prepare_private_balance_increase(sender)); + let token1_partial_note = self.call(token1.prepare_private_balance_increase(sender)); // We then complete the flow in public. Note that the type of operation and amounts will all be publicly known, // but the identity of the caller is not revealed despite us being able to send tokens to them by completing the @@ -294,9 +290,9 @@ pub contract AMM { // We need the current balance of both tokens as well as the liquidity token total supply in order to compute // the amounts to send the user. - let balance0 = token0.balance_of_public(self.address).view(self.context); - let balance1 = token1.balance_of_public(self.address).view(self.context); - let total_supply = liquidity_token.total_supply().view(self.context); + let balance0 = self.view(token0.balance_of_public(self.address)); + let balance1 = self.view(token1.balance_of_public(self.address)); + let total_supply = self.view(liquidity_token.total_supply()); // We calculate the amounts of token0 and token1 the user is entitled to based on the amount of liquidity they // are removing, and check that they are above the minimum amounts they requested. @@ -306,9 +302,9 @@ pub contract AMM { // We can now burn the liquidity tokens that had been privately transferred into the AMM, as well as complete // both partial notes. - liquidity_token.burn_public(self.address, liquidity, 0).call(self.context); - token0.finalize_transfer_to_private(amount0, token0_partial_note).call(self.context); - token1.finalize_transfer_to_private(amount1, token1_partial_note).call(self.context); + self.call(liquidity_token.burn_public(self.address, liquidity, 0)); + self.call(token0.finalize_transfer_to_private(amount0, token0_partial_note)); + self.call(token1.finalize_transfer_to_private(amount1, token1_partial_note)); } /// Privately swaps `amount_in` `token_in` tokens for at least `amount_out_mint` `token_out` tokens with the pool. @@ -335,11 +331,14 @@ pub contract AMM { // We transfer the full amount in, since it is an exact amount, and prepare a partial note for the amount out, // which will only be known during public execution as it depends on the live balances. - Token::at(token_in).transfer_to_public(sender, self.address, amount_in, authwit_nonce).call( - self.context, - ); + self.call(Token::at(token_in).transfer_to_public( + sender, + self.address, + amount_in, + authwit_nonce, + )); let token_out_partial_note = - Token::at(token_out).prepare_private_balance_increase(sender).call(self.context); + self.call(Token::at(token_out).prepare_private_balance_increase(sender)); self.enqueue_self._swap_exact_tokens_for_tokens( token_in, @@ -363,10 +362,10 @@ pub contract AMM { // transfer has already been completed as that function call was enqueued before this one. We therefore need to // subtract the amount in to get the pre-swap balances. let balance_in_plus_amount_in = - Token::at(token_in).balance_of_public(self.address).view(self.context); + self.view(Token::at(token_in).balance_of_public(self.address)); let balance_in = balance_in_plus_amount_in - amount_in; - let balance_out = Token::at(token_out).balance_of_public(self.address).view(self.context); + let balance_out = self.view(Token::at(token_out).balance_of_public(self.address)); // We can now compute the number of tokens to transfer and complete the partial note. let amount_out = get_amount_out(amount_in, balance_in, balance_out); @@ -405,17 +404,16 @@ pub contract AMM { // Technically the token out note does not need to be partial, since we do know the amount out, but we do want // to wait until the swap has been completed before committing the note to the tree to avoid it being spent too // early. - let change_token_in_partial_note = Token::at(token_in) + let change_token_in_partial_note = self.call(Token::at(token_in) .transfer_to_public_and_prepare_private_balance_increase( sender, self.address, amount_in_max, authwit_nonce, - ) - .call(self.context); + )); let token_out_partial_note = - Token::at(token_out).prepare_private_balance_increase(sender).call(self.context); + self.call(Token::at(token_out).prepare_private_balance_increase(sender)); self.enqueue_self._swap_tokens_for_exact_tokens( token_in, @@ -441,10 +439,10 @@ pub contract AMM { // transfer has already been completed as that function call was enqueued before this one. We therefore need to // subtract the amount in to get the pre-swap balances. let balance_in_plus_amount_in_max = - Token::at(token_in).balance_of_public(self.address).view(self.context); + self.view(Token::at(token_in).balance_of_public(self.address)); let balance_in = balance_in_plus_amount_in_max - amount_in_max; - let balance_out = Token::at(token_out).balance_of_public(self.address).view(self.context); + let balance_out = self.view(Token::at(token_out).balance_of_public(self.address)); // We can now compute the number of tokens we need to receive and complete the partial note with the change. let amount_in = get_amount_in(amount_out, balance_in, balance_out); @@ -452,9 +450,10 @@ pub contract AMM { let change = amount_in_max - amount_in; if (change > 0 as u128) { - Token::at(token_in) - .finalize_transfer_to_private(change, change_token_in_partial_note) - .call(self.context); + self.call(Token::at(token_in).finalize_transfer_to_private( + change, + change_token_in_partial_note, + )); } // Note again that we already knew the amount out, but for consistency we want to only commit this note once diff --git a/noir-projects/noir-contracts/contracts/app/app_subscription_contract/src/main.nr b/noir-projects/noir-contracts/contracts/app/app_subscription_contract/src/main.nr index 42759b668c4f..05c15ac1b398 100644 --- a/noir-projects/noir-contracts/contracts/app/app_subscription_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/app/app_subscription_contract/src/main.nr @@ -135,14 +135,12 @@ pub contract AppSubscription { let config = self.storage.config.read(); - Token::at(config.subscription_token_address) - .transfer_in_private( - self.msg_sender().unwrap(), - config.subscription_recipient_address, - config.subscription_price, - authwit_nonce, - ) - .call(self.context); + self.call(Token::at(config.subscription_token_address).transfer_in_private( + self.msg_sender().unwrap(), + config.subscription_recipient_address, + config.subscription_price, + authwit_nonce, + )); // Assert that the `current_block_number > expiry_block_number - SUBSCRIPTION_DURATION_IN_BLOCKS`. // --> We do that via the router contract to conceal which contract is performing the check. diff --git a/noir-projects/noir-contracts/contracts/app/claim_contract/src/main.nr b/noir-projects/noir-contracts/contracts/app/claim_contract/src/main.nr index f8798a2a7bae..054b17d87011 100644 --- a/noir-projects/noir-contracts/contracts/app/claim_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/app/claim_contract/src/main.nr @@ -67,8 +67,9 @@ pub contract Claim { self.context.push_nullifier(nullifier); // 4) Finally we mint the reward token to the sender of the transaction - Token::at(self.storage.reward_token.read()) - .mint_to_public(recipient, proof_retrieved_note.note.get_value()) - .enqueue(self.context); + self.enqueue(Token::at(self.storage.reward_token.read()).mint_to_public( + recipient, + proof_retrieved_note.note.get_value(), + )); } } diff --git a/noir-projects/noir-contracts/contracts/app/crowdfunding_contract/src/main.nr b/noir-projects/noir-contracts/contracts/app/crowdfunding_contract/src/main.nr index 2aecff5797c8..b74ecd132260 100644 --- a/noir-projects/noir-contracts/contracts/app/crowdfunding_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/app/crowdfunding_contract/src/main.nr @@ -80,7 +80,7 @@ pub contract Crowdfunding { assert(self.msg_sender().unwrap() == operator_address, "Not an operator"); // 2) Transfer the donation tokens from this contract to the operator - Token::at(config.donation_token).transfer(operator_address, amount).call(self.context); + self.call(Token::at(config.donation_token).transfer(operator_address, amount)); // 3) Emit a public event so that anyone can audit how much the operator has withdrawn self.enqueue_self._publish_donation_receipts(amount, operator_address); } diff --git a/noir-projects/noir-contracts/contracts/app/escrow_contract/src/main.nr b/noir-projects/noir-contracts/contracts/app/escrow_contract/src/main.nr index 7206fd4c3939..aa4c29eee231 100644 --- a/noir-projects/noir-contracts/contracts/app/escrow_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/app/escrow_contract/src/main.nr @@ -33,6 +33,6 @@ pub contract Escrow { let note = self.storage.owner.get_note(); assert(note.get_address() == sender); - Token::at(token).transfer(recipient, amount).call(self.context); + self.call(Token::at(token).transfer(recipient, amount)); } } diff --git a/noir-projects/noir-contracts/contracts/app/lending_contract/src/main.nr b/noir-projects/noir-contracts/contracts/app/lending_contract/src/main.nr index 57dee2179b97..2b2ae71083e4 100644 --- a/noir-projects/noir-contracts/contracts/app/lending_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/app/lending_contract/src/main.nr @@ -110,9 +110,12 @@ pub contract Lending { ) { let on_behalf_of = compute_identifier(secret, on_behalf_of, self.msg_sender().unwrap().to_field()); - let _res = Token::at(collateral_asset) - .transfer_to_public(from, self.address, amount, authwit_nonce) - .call(self.context); + let _res = self.call(Token::at(collateral_asset).transfer_to_public( + from, + self.address, + amount, + authwit_nonce, + )); // docs:start:enqueue_public self.enqueue_self._deposit(AztecAddress::from_field(on_behalf_of), amount, collateral_asset); // docs:end:enqueue_public @@ -125,9 +128,12 @@ pub contract Lending { on_behalf_of: Field, collateral_asset: AztecAddress, ) { - let _ = Token::at(collateral_asset) - .transfer_in_public(self.msg_sender().unwrap(), self.address, amount, authwit_nonce) - .call(self.context); + let _ = self.call(Token::at(collateral_asset).transfer_in_public( + self.msg_sender().unwrap(), + self.address, + amount, + authwit_nonce, + )); let _ = self.call_self._deposit( AztecAddress::from_field(on_behalf_of), amount, @@ -163,7 +169,7 @@ pub contract Lending { #[only_self] fn _withdraw(owner: AztecAddress, recipient: AztecAddress, amount: u128) { let asset = self.call_self.update_accumulator(); - let price = PriceFeed::at(asset.oracle).get_price(0).view(self.context).price; + let price = self.view(PriceFeed::at(asset.oracle).get_price(0)).price; let coll_loc = self.storage.collateral.at(owner); let collateral = coll_loc.read(); @@ -188,9 +194,12 @@ pub contract Lending { // @todo @LHerskind Support both shielding and transfers (for now just transfer) let collateral_asset = self.storage.collateral_asset.read(); - let _ = Token::at(collateral_asset) - .transfer_in_public(self.address, recipient, amount, 0) - .call(self.context); + let _ = self.call(Token::at(collateral_asset).transfer_in_public( + self.address, + recipient, + amount, + 0, + )); } #[external("private")] @@ -208,7 +217,7 @@ pub contract Lending { #[only_self] fn _borrow(owner: AztecAddress, to: AztecAddress, amount: u128) { let asset = self.call_self.update_accumulator(); - let price = PriceFeed::at(asset.oracle).get_price(0).view(self.context).price; + let price = self.view(PriceFeed::at(asset.oracle).get_price(0)).price; // Fetch collateral and static_debt, compute health of current position let collateral = self.storage.collateral.at(owner).read(); @@ -224,7 +233,7 @@ pub contract Lending { // @todo @LHerskind Need to support both private and public minting. let stable_coin = self.storage.stable_coin.read(); - let _ = Token::at(stable_coin).mint_to_public(to, amount).call(self.context); + let _ = self.call(Token::at(stable_coin).mint_to_public(to, amount)); } #[external("private")] @@ -239,7 +248,7 @@ pub contract Lending { let on_behalf_of = compute_identifier(secret, on_behalf_of, self.msg_sender().unwrap().to_field()); // docs:start:private_call - let _ = Token::at(stable_coin).burn_private(from, amount, authwit_nonce).call(self.context); + let _ = self.call(Token::at(stable_coin).burn_private(from, amount, authwit_nonce)); // docs:end:private_call self.enqueue_self._repay(AztecAddress::from_field(on_behalf_of), amount, stable_coin); } @@ -251,9 +260,11 @@ pub contract Lending { owner: AztecAddress, stable_coin: AztecAddress, ) { - let _ = Token::at(stable_coin) - .burn_public(self.msg_sender().unwrap(), amount, authwit_nonce) - .call(self.context); + let _ = self.call(Token::at(stable_coin).burn_public( + self.msg_sender().unwrap(), + amount, + authwit_nonce, + )); let _ = self.call_self._repay(owner, amount, stable_coin); } diff --git a/noir-projects/noir-contracts/contracts/app/orderbook_contract/src/main.nr b/noir-projects/noir-contracts/contracts/app/orderbook_contract/src/main.nr index c9cb1f65f8b1..ae4e987a1226 100644 --- a/noir-projects/noir-contracts/contracts/app/orderbook_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/app/orderbook_contract/src/main.nr @@ -84,14 +84,17 @@ pub contract Orderbook { let maker = self.msg_sender().unwrap(); // Transfer tokens from maker to the public balance of this contract. - Token::at(bid_token) - .transfer_to_public(maker, self.address, bid_amount, authwit_nonce) - .call(self.context); + self.call(Token::at(bid_token).transfer_to_public( + maker, + self.address, + bid_amount, + authwit_nonce, + )); // Prepare a partial note that will get completed once the order is fulfilled. Note that only the Orderbook // contract can complete the partial note. let maker_partial_note = - Token::at(ask_token).prepare_private_balance_increase(maker).call(self.context); + self.call(Token::at(ask_token).prepare_private_balance_increase(maker)); // We use the partial note as the order ID. Because partial notes emit a nullifier when created they are // unique, and so this guarantees that our order IDs are also unique without having to keep track of past @@ -129,18 +132,16 @@ pub contract Orderbook { let maker_partial_note = PartialUintNote::from_field(order_id); // Transfer the ask_amount from taker directly to the maker's partial note. - Token::at(ask_token) - .finalize_transfer_to_private_from_private( - taker, - maker_partial_note, - order.ask_amount, - authwit_nonce, - ) - .call(self.context); + self.call(Token::at(ask_token).finalize_transfer_to_private_from_private( + taker, + maker_partial_note, + order.ask_amount, + authwit_nonce, + )); // Prepare partial note for taker to receive bid_token let taker_partial_note = - Token::at(bid_token).prepare_private_balance_increase(taker).call(self.context); + self.call(Token::at(bid_token).prepare_private_balance_increase(taker)); // Nullify the order such that it cannot be fulfilled again. We emit a nullifier instead of deleting the order // from public storage because we get no refund for resetting public storage to zero and just emitting diff --git a/noir-projects/noir-contracts/contracts/app/token_bridge_contract/src/main.nr b/noir-projects/noir-contracts/contracts/app/token_bridge_contract/src/main.nr index 140e741c3618..185ba10b4740 100644 --- a/noir-projects/noir-contracts/contracts/app/token_bridge_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/app/token_bridge_contract/src/main.nr @@ -67,7 +67,7 @@ pub contract TokenBridge { ); // Mint tokens - Token::at(config.token).mint_to_public(to, amount).call(self.context); + self.call(Token::at(config.token).mint_to_public(to, amount)); } // docs:end:claim_public @@ -88,9 +88,11 @@ pub contract TokenBridge { self.context.message_portal(config.portal, content); // Burn tokens - Token::at(config.token).burn_public(self.msg_sender().unwrap(), amount, authwit_nonce).call( - self.context, - ); + self.call(Token::at(config.token).burn_public( + self.msg_sender().unwrap(), + amount, + authwit_nonce, + )); } // docs:end:exit_to_l1_public @@ -116,7 +118,7 @@ pub contract TokenBridge { ); // At last we mint the tokens - Token::at(config.token).mint_to_private(recipient, amount).call(self.context); + self.call(Token::at(config.token).mint_to_private(recipient, amount)); } // docs:start:exit_to_l1_private @@ -140,9 +142,7 @@ pub contract TokenBridge { self.context.message_portal(config.portal, content); // Burn tokens - Token::at(token).burn_private(self.msg_sender().unwrap(), amount, authwit_nonce).call( - self.context, - ); + self.call(Token::at(token).burn_private(self.msg_sender().unwrap(), amount, authwit_nonce)); } // docs:end:exit_to_l1_private } diff --git a/noir-projects/noir-contracts/contracts/app/uniswap_contract/src/main.nr b/noir-projects/noir-contracts/contracts/app/uniswap_contract/src/main.nr index b04d68a554e6..87731843fda1 100644 --- a/noir-projects/noir-contracts/contracts/app/uniswap_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/app/uniswap_contract/src/main.nr @@ -62,15 +62,18 @@ pub contract Uniswap { } let input_asset_bridge_config = - TokenBridge::at(input_asset_bridge).get_config_public().view(self.context); + self.view(TokenBridge::at(input_asset_bridge).get_config_public()); let input_asset = input_asset_bridge_config.token; let input_asset_bridge_portal_address = input_asset_bridge_config.portal; // Transfer funds to this contract - Token::at(input_asset) - .transfer_in_public(sender, self.address, input_amount, nonce_for_transfer_approval) - .call(self.context); + self.call(Token::at(input_asset).transfer_in_public( + sender, + self.address, + input_amount, + nonce_for_transfer_approval, + )); // Approve bridge to burn this contract's funds and exit to L1 Uniswap Portal let _ = self.call_self._approve_bridge_and_exit_input_asset_to_L1( @@ -81,7 +84,7 @@ pub contract Uniswap { // Create swap message and send to Outbox for Uniswap Portal // this ensures the integrity of what the user originally intends to do on L1. let output_asset_bridge_portal_address = - TokenBridge::at(output_asset_bridge).get_config_public().view(self.context).portal; + self.view(TokenBridge::at(output_asset_bridge).get_config_public()).portal; // ensure portal exists - else funds might be lost assert( !input_asset_bridge_portal_address.is_zero(), @@ -122,10 +125,9 @@ pub contract Uniswap { secret_hash_for_L1_to_l2_message: Field, // for when l1 uniswap portal inserts the message to consume output assets on L2 caller_on_L1: EthAddress, // ethereum address that can call this function on the L1 portal (0x0 if anyone can call) ) { - let input_asset_bridge_config = - TokenBridge::at(input_asset_bridge).get_config().view(self.context); + let input_asset_bridge_config = self.view(TokenBridge::at(input_asset_bridge).get_config()); let output_asset_bridge_config = - TokenBridge::at(output_asset_bridge).get_config().view(self.context); + self.view(TokenBridge::at(output_asset_bridge).get_config()); // Assert that user provided token address is same as expected by token bridge. // we can't directly use `input_asset_bridge.token` because that is a public method and public can't return data to private @@ -135,14 +137,12 @@ pub contract Uniswap { ); // Transfer funds to this contract - Token::at(input_asset) - .transfer_to_public( - self.msg_sender().unwrap(), - self.address, - input_amount, - nonce_for_transfer_to_public_approval, - ) - .call(self.context); + self.call(Token::at(input_asset).transfer_to_public( + self.msg_sender().unwrap(), + self.address, + input_amount, + nonce_for_transfer_to_public_approval, + )); // Approve bridge to burn this contract's funds and exit to L1 Uniswap Portal self.enqueue_self._approve_bridge_and_exit_input_asset_to_L1( @@ -209,9 +209,12 @@ pub contract Uniswap { let this_portal_address = self.storage.portal_address.read(); // Exit to L1 Uniswap Portal ! - TokenBridge::at(token_bridge) - .exit_to_l1_public(this_portal_address, amount, this_portal_address, authwit_nonce) - .call(self.context) + self.call(TokenBridge::at(token_bridge).exit_to_l1_public( + this_portal_address, + amount, + this_portal_address, + authwit_nonce, + )); } // docs:end:authwit_uniswap_set } diff --git a/noir-projects/noir-contracts/contracts/fees/fpc_contract/src/main.nr b/noir-projects/noir-contracts/contracts/fees/fpc_contract/src/main.nr index b7f4a571d8cf..27b7a4e0116d 100644 --- a/noir-projects/noir-contracts/contracts/fees/fpc_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/fees/fpc_contract/src/main.nr @@ -87,15 +87,17 @@ pub contract FPC { // fee juice/mana/gas limit. Currently the fee juice/AA exchange rate is fixed 1:1. // Pull the max fee from the user's balance of the accepted asset to the public balance of this contract. - token.transfer_to_public(user, self.address, max_fee, authwit_nonce).call(self.context); + self.call(token.transfer_to_public(user, self.address, max_fee, authwit_nonce)); // Prepare a partial note for the refund for the user. - let partial_note = token.prepare_private_balance_increase(user).call(self.context); + let partial_note = self.call(token.prepare_private_balance_increase(user)); // Set a public teardown function in which the refund will be paid back to the user by finalizing the partial note. - FPC::at(self.address) - ._complete_refund(accepted_asset, partial_note, max_fee) - .set_as_teardown(self.context); + self.set_as_teardown(FPC::at(self.address)._complete_refund( + accepted_asset, + partial_note, + max_fee, + )); // Set the FPC as the fee payer of the tx. self.context.set_as_fee_payer(); @@ -156,14 +158,19 @@ pub contract FPC { // We pull the max fee from the user's balance of the accepted asset to this contract. // docs:start:public_call - Token::at(config.accepted_asset) - .transfer_in_public(self.msg_sender().unwrap(), self.address, max_fee, authwit_nonce) - .enqueue(self.context); + self.enqueue(Token::at(config.accepted_asset).transfer_in_public( + self.msg_sender().unwrap(), + self.address, + max_fee, + authwit_nonce, + )); // docs:end:public_call - FPC::at(self.address) - ._pay_refund(self.msg_sender().unwrap(), max_fee, config.accepted_asset) - .set_as_teardown(self.context); + self.set_as_teardown(FPC::at(self.address)._pay_refund( + self.msg_sender().unwrap(), + max_fee, + config.accepted_asset, + )); // Set the FPC as the fee payer of the tx. self.context.set_as_fee_payer(); @@ -183,9 +190,12 @@ pub contract FPC { // TODO(#10805): Introduce a real exchange rate let refund = max_fee - actual_fee; - Token::at(accepted_asset) - .transfer_in_public(self.address, refund_recipient, refund, 0) - .call(self.context); + self.call(Token::at(accepted_asset).transfer_in_public( + self.address, + refund_recipient, + refund, + 0, + )); } /// Pulls all the accepted asset funds from this contract to the `to` address. Only the admin can call @@ -199,8 +209,8 @@ pub contract FPC { let token = Token::at(config.accepted_asset); // We send the full balance to `to`. - let balance = token.balance_of_public(self.address).view(self.context); - token.transfer_in_public(self.address, to, balance, 0).call(self.context); + let balance = self.view(token.balance_of_public(self.address)); + self.call(token.transfer_in_public(self.address, to, balance, 0)); } /// Note: Not marked as view as we need it to be callable as an entrypoint since in some places we need to obtain diff --git a/noir-projects/noir-contracts/contracts/protocol/contract_instance_registry/src/main.nr b/noir-projects/noir-contracts/contracts/protocol/contract_instance_registry/src/main.nr index dfc872ab0e77..d7703f1c61dc 100644 --- a/noir-projects/noir-contracts/contracts/protocol/contract_instance_registry/src/main.nr +++ b/noir-projects/noir-contracts/contracts/protocol/contract_instance_registry/src/main.nr @@ -104,9 +104,8 @@ pub contract ContractInstanceRegistry { universal_deploy: bool, ) { // contract class must be published in order to publish an instance - ContractClassRegistry::at(CONTRACT_CLASS_REGISTRY_CONTRACT_ADDRESS) - .assert_class_id_is_published(contract_class_id) - .call(self.context); + self.call(ContractClassRegistry::at(CONTRACT_CLASS_REGISTRY_CONTRACT_ADDRESS) + .assert_class_id_is_published(contract_class_id)); let deployer = if universal_deploy { AztecAddress::zero() diff --git a/noir-projects/noir-contracts/contracts/test/avm_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/avm_test_contract/src/main.nr index 7ae47f7c1938..f026bd0c0162 100644 --- a/noir-projects/noir-contracts/contracts/test/avm_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/avm_test_contract/src/main.nr @@ -16,7 +16,7 @@ pub contract AvmTest { avm_return, avm_revert, call, returndata_copy, returndata_size, success_copy, }; use dep::aztec::context::gas::GasOpts; - use dep::aztec::macros::{functions::external, storage::storage}; + use dep::aztec::macros::{functions::{external, view}, storage::storage}; use dep::aztec::oracle::get_contract_instance::{ get_contract_instance_class_id_avm, get_contract_instance_deployer_avm, get_contract_instance_initialization_hash_avm, @@ -167,6 +167,12 @@ pub contract AvmTest { 8 } + #[external("public")] + #[view] + fn set_opcode_u8_view() -> u8 { + 8 + } + #[external("public")] fn set_opcode_u32() -> u32 { 1 << 30 @@ -752,7 +758,7 @@ pub contract AvmTest { #[external("public")] fn nested_call_to_nothing() { let garbageAddress = AztecAddress::from_field(42); - AvmTest::at(garbageAddress).nested_call_to_nothing().call(self.context) + self.call(AvmTest::at(garbageAddress).nested_call_to_nothing()) } #[external("public")] @@ -774,10 +780,10 @@ pub contract AvmTest { da_gas: u32, ) -> pub Field { // Note: can't use self.call_self here because we need .with_gas() chaining - AvmTest::at(self.address) - .add_args_return(arg_a, arg_b) - .with_gas(GasOpts::new(l2_gas, da_gas)) - .call(self.context) + self.call(AvmTest::at(self.address).add_args_return(arg_a, arg_b).with_gas(GasOpts::new( + l2_gas, + da_gas, + ))) } // Use the `call_public_function` wrapper to initiate a nested call to the add function @@ -803,7 +809,7 @@ pub contract AvmTest { for i in 0..MAX_PUBLIC_CALLS_TO_UNIQUE_CONTRACT_CLASS_IDS + 2 { let addr = addrs[i]; if addr != AztecAddress::empty() { - let _ = AvmTest::at(addr).add_args_return(1, 2).call(self.context); + let _ = self.call(AvmTest::at(addr).add_args_return(1, 2)); } } } @@ -833,13 +839,13 @@ pub contract AvmTest { #[external("public")] fn create_same_nullifier_in_nested_call(nestedAddress: AztecAddress, nullifier: Field) { self.context.push_nullifier(nullifier); - AvmTest::at(nestedAddress).new_nullifier(nullifier).call(self.context); + self.call(AvmTest::at(nestedAddress).new_nullifier(nullifier)); } #[external("public")] fn create_different_nullifier_in_nested_call(nestedAddress: AztecAddress, nullifier: Field) { self.context.push_nullifier(nullifier); - AvmTest::at(nestedAddress).new_nullifier(nullifier + 1).call(self.context); + self.call(AvmTest::at(nestedAddress).new_nullifier(nullifier + 1)); } #[external("public")] @@ -863,7 +869,7 @@ pub contract AvmTest { */ #[external("private")] fn enqueue_public_from_private() { - AvmTest::at(self.address).set_opcode_u8().enqueue_view(self.context); + self.enqueue_self_static.set_opcode_u8_view(); self.enqueue_self.set_read_storage_single(5); } @@ -889,9 +895,8 @@ pub contract AvmTest { #[external("public")] fn call_instance_registry() { - let _ = ContractInstanceRegistry::at(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS) - .get_update_delay() - .view(self.context); + let _ = self.view(ContractInstanceRegistry::at(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS) + .get_update_delay()); } /************************************************************************ diff --git a/noir-projects/noir-contracts/contracts/test/counter_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/counter_contract/src/main.nr index 6f42feef6306..f5fe55ba8000 100644 --- a/noir-projects/noir-contracts/contracts/test/counter_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/counter_contract/src/main.nr @@ -83,7 +83,7 @@ pub contract Counter { let counters = self.storage.counters; counters.at(owner).add(1, owner); - Counter::at(other_counter).increment(owner).call(self.context); + self.call(Counter::at(other_counter).increment(owner)); } mod test { diff --git a/noir-projects/noir-contracts/contracts/test/import_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/import_test_contract/src/main.nr index b187ac00a859..b3ec13502967 100644 --- a/noir-projects/noir-contracts/contracts/test/import_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/import_test_contract/src/main.nr @@ -15,7 +15,7 @@ pub contract ImportTest { // See yarn-project/end-to-end/src/e2e_nested_contract.test.ts #[external("private")] fn call_no_args(target: AztecAddress) -> AztecAddress { - Test::at(target).get_this_address().call(self.context) + self.call(Test::at(target).get_this_address()) } // Calls the emit_nullifier_public on the Test contract at the target address @@ -23,7 +23,7 @@ pub contract ImportTest { // See yarn-project/end-to-end/src/e2e_nested_contract.test.ts #[external("private")] fn call_public_fn(target: AztecAddress) { - Test::at(target).emit_nullifier_public(1).enqueue(self.context); + self.enqueue(Test::at(target).emit_nullifier_public(1)); } // Calls the emit_nullifier_public on the Test contract at the target address @@ -31,6 +31,6 @@ pub contract ImportTest { // See yarn-project/end-to-end/src/e2e_nested_contract.test.ts #[external("public")] fn pub_call_public_fn(target: AztecAddress) { - Test::at(target).emit_nullifier_public(1).call(self.context); + self.call(Test::at(target).emit_nullifier_public(1)); } } diff --git a/noir-projects/noir-contracts/contracts/test/pending_note_hashes_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/pending_note_hashes_contract/src/main.nr index 04b793a4b85b..56d644a2568e 100644 --- a/noir-projects/noir-contracts/contracts/test/pending_note_hashes_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/pending_note_hashes_contract/src/main.nr @@ -349,7 +349,7 @@ pub contract PendingNoteHashes { fn create_max_notes(owner: AztecAddress) { let owner_balance = self.storage.balances.at(owner); - for i in 0..max_notes_per_call() { + for i in 0..self.internal.max_notes_per_call() { let note = ValueNote::new(i as Field, owner); owner_balance.insert(note).emit(owner, MessageDelivery.CONSTRAINED_ONCHAIN); } @@ -363,7 +363,7 @@ pub contract PendingNoteHashes { let _ = owner_balance.pop_notes(NoteGetterOptions::new()); } - #[contract_library_method] + #[internal("private")] fn max_notes_per_call() -> u32 { if MAX_NOTE_HASHES_PER_CALL > MAX_NOTE_HASH_READ_REQUESTS_PER_CALL { MAX_NOTE_HASH_READ_REQUESTS_PER_CALL diff --git a/noir-projects/noir-contracts/contracts/test/static_parent_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/static_parent_contract/src/main.nr index 8b4f5cedde19..3a9458d731d7 100644 --- a/noir-projects/noir-contracts/contracts/test/static_parent_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/static_parent_contract/src/main.nr @@ -3,7 +3,7 @@ use dep::aztec::macros::aztec; #[aztec] pub contract StaticParent { - use dep::aztec::{context::gas::GasOpts, macros::functions::external}; + use dep::aztec::{context::gas::GasOpts, macros::functions::{external, view}}; use dep::aztec::protocol_types::{ abis::function_selector::FunctionSelector, address::AztecAddress, }; @@ -11,6 +11,7 @@ pub contract StaticParent { // Public function to directly call another public function to the target_contract using the selector and value provided #[external("public")] + #[view] fn public_call( target_contract: AztecAddress, target_selector: FunctionSelector, @@ -36,6 +37,7 @@ pub contract StaticParent { // Just like function above but with 3 args. #[external("private")] + #[view] fn private_call_3_args( target_contract: AztecAddress, target_selector: FunctionSelector, @@ -86,7 +88,7 @@ pub contract StaticParent { value: Field, owner: AztecAddress, ) -> Field { - StaticChild::at(target_contract).private_get_value(value, owner).view(self.context) + self.view(StaticChild::at(target_contract).private_get_value(value, owner)) } // Private function to set a static context and verify correct propagation for nested private calls @@ -96,6 +98,9 @@ pub contract StaticParent { target_selector: FunctionSelector, args: [Field; 2], ) -> Field { + // Not using the new self.view(...) API because that doesn't allow for static calls to non-static functions + // which this test seems to rely on. TODO(F-130): Address this and replace e2e_static_calls.test.ts with Noir + // tests. StaticParent::at(self.address).private_call(target_contract, target_selector, args).view( self.context, ) @@ -108,9 +113,11 @@ pub contract StaticParent { target_selector: FunctionSelector, args: [Field; 3], ) -> Field { - StaticParent::at(self.address) - .private_call_3_args(target_contract, target_selector, args) - .view(self.context) + self.view(StaticParent::at(self.address).private_call_3_args( + target_contract, + target_selector, + args, + )) } // Public function to statically call another public function to the target_contract using the selector and value provided @@ -131,7 +138,7 @@ pub contract StaticParent { // Same as above but using a specific function from the interface #[external("public")] fn public_get_value_from_child(target_contract: AztecAddress, value: Field) -> Field { - StaticChild::at(target_contract).pub_get_value(value).view(self.context) + self.view(StaticChild::at(target_contract).pub_get_value(value)) } // Public function to set a static context and verify correct propagation for nested public calls @@ -160,7 +167,7 @@ pub contract StaticParent { // Same as above but using a specific function from the interface #[external("private")] fn enqueue_public_get_value_from_child(target_contract: AztecAddress, value: Field) { - StaticChild::at(target_contract).pub_get_value(value).enqueue_view(self.context); + self.enqueue_view(StaticChild::at(target_contract).pub_get_value(value)); } // Private function to set a static context and verify correct propagation of nested enqueuing of public calls @@ -171,8 +178,10 @@ pub contract StaticParent { args: [Field; 1], ) { // Call the target public function through the pub entrypoint statically - StaticParent::at(self.address) - .public_call(target_contract, target_selector, args[0]) - .enqueue_view(self.context) + self.enqueue_view(StaticParent::at(self.address).public_call( + target_contract, + target_selector, + args[0], + )) } } diff --git a/noir-projects/noir-contracts/contracts/test/updatable_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/updatable_contract/src/main.nr index 423ceeae88a9..25a28cbac5e8 100644 --- a/noir-projects/noir-contracts/contracts/test/updatable_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/updatable_contract/src/main.nr @@ -40,23 +40,20 @@ contract Updatable { #[external("private")] fn update_to(new_class_id: ContractClassId) { - ContractInstanceRegistry::at(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS) - .update(new_class_id) - .enqueue(self.context); + self.enqueue(ContractInstanceRegistry::at(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS) + .update(new_class_id)); } #[external("private")] fn set_update_delay(new_delay: u64) { - ContractInstanceRegistry::at(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS) - .set_update_delay(new_delay) - .enqueue(self.context); + self.enqueue(ContractInstanceRegistry::at(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS) + .set_update_delay(new_delay)); } #[external("public")] fn get_update_delay() -> u64 { - ContractInstanceRegistry::at(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS) - .get_update_delay() - .view(self.context) + self.view(ContractInstanceRegistry::at(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS) + .get_update_delay()) } #[external("utility")] diff --git a/noir-projects/noir-contracts/contracts/test/updated_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/updated_contract/src/main.nr index 5020135277e9..c87c6b792b20 100644 --- a/noir-projects/noir-contracts/contracts/test/updated_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/updated_contract/src/main.nr @@ -35,9 +35,8 @@ contract Updated { #[external("public")] fn get_update_delay() -> u64 { - ContractInstanceRegistry::at(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS) - .get_update_delay() - .view(self.context) + self.view(ContractInstanceRegistry::at(CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS) + .get_update_delay()) } #[external("utility")]