Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e4ced9e
fix: displaying debug logs from public function simulations
benesjan Feb 16, 2026
7336ba8
wip
benesjan Feb 17, 2026
090fc24
making logs not stateful on PublicProcessor
benesjan Feb 17, 2026
e66db6e
wip
benesjan Feb 16, 2026
0336f95
fix
benesjan Feb 16, 2026
ab67901
fix
benesjan Feb 18, 2026
5466b11
refactor: split ContractSelf into ContractSelfPrivate, ContractSelfPu…
benesjan Feb 18, 2026
4ab2217
fmt
benesjan Feb 18, 2026
2a1c9c0
refactor: authwit not consumable when reject all is set (#20533)
benesjan Feb 18, 2026
19e3174
final cleanup
benesjan Feb 18, 2026
9a5ba64
Merge branch 'next' into merge-train/fairies
Feb 18, 2026
45b3a45
Merge branch 'next' into merge-train/fairies
Feb 18, 2026
cd38849
fix: displaying debug logs from public function simulations (#20551)
benesjan Feb 18, 2026
9f279d6
Merge branch 'next' into merge-train/fairies
Feb 18, 2026
9e6dcc6
Merge branch 'next' into merge-train/fairies
Feb 18, 2026
aab3ca2
fix: allow compiling mixed (contract + noir script) aztec project (#2…
nchamo Feb 18, 2026
9694858
refactor: split ContractSelf into context-specific structs (#20624)
benesjan Feb 18, 2026
bb33335
feat: add optional additional scopes to wallet transaction API (#20487)
nchamo Feb 18, 2026
98ae645
Merge branch 'next' into merge-train/fairies
Feb 18, 2026
0f162ad
Merge branch 'next' into merge-train/fairies
Feb 18, 2026
b3c8ff8
Merge branch 'next' into merge-train/fairies
Feb 18, 2026
5560f3f
feat(aztec.js): add additionalScopes to ContractFunctionPattern in ca…
nchamo Feb 18, 2026
efb935d
Merge branch 'next' into merge-train/fairies
Feb 18, 2026
fab9e4c
Merge branch 'next' into merge-train/fairies
Feb 18, 2026
0ede46f
Merge branch 'next' into merge-train/fairies
Feb 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions docs/docs-developers/docs/resources/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,51 @@ Aztec is in active development. Each version may introduce breaking changes that

## TBD

### Scope enforcement for private state access (TXE and PXE)

Scope enforcement is now active across both TXE (test environment) and PXE (client). Previously, private execution could implicitly access any account's keys and notes. Now, only the caller (`from`) address is in scope by default, and accessing another address's private state requires explicitly granting scope.

#### Noir developers (TXE)

TXE now enforces scope isolation, matching PXE behavior. During private execution, only the caller's keys and notes are accessible. If a Noir test accesses private state of an address other than `from`, it will fail. When `from` is the zero address, scopes are empty (deny-all).

If your TXE tests fail with key or note access errors, ensure the test is calling from the correct address, or restructure the test to match the expected access pattern.

#### Aztec.js developers (PXE/Wallet)

The wallet now passes scopes to PXE, and only the `from` address is in scope by default. Auto-expansion of scopes for nested calls to registered accounts has been removed. A new `additionalScopes` option is available on `send()`, `simulate()`, and `deploy()` for cases where private execution needs access to another address's keys or notes.

**When do you need `additionalScopes`?**

1. **Deploying contracts whose constructor initializes private storage** (e.g., account contracts, or any contract using `SinglePrivateImmutable`/`SinglePrivateMutable` in the constructor). The contract's own address must be in scope so its nullifier key is accessible during initialization.

2. **Operations that access another contract's private state** (e.g., withdrawing from an escrow contract that nullifies the contract's own token notes).

```

**Example: deploying a contract with private storage (e.g., `PrivateToken`)**

```diff
const tokenDeployment = PrivateTokenContract.deployWithPublicKeys(
tokenPublicKeys, wallet, initialBalance, sender,
);
const tokenInstance = await tokenDeployment.getInstance();
await wallet.registerContract(tokenInstance, PrivateTokenContract.artifact, tokenSecretKey);
const token = await tokenDeployment.send({
from: sender,
+ additionalScopes: [tokenInstance.address],
});
```

**Example: withdrawing from an escrow contract**

```diff
await escrowContract.methods
.withdraw(token.address, amount, recipient)
- .send({ from: owner });
+ .send({ from: owner, additionalScopes: [escrowContract.address] });
```

### `simulateUtility` renamed to `executeUtility`

The `simulateUtility` method and related types have been renamed to `executeUtility` across the entire stack to better reflect that utility functions are executed, not simulated.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,23 @@
//! The `self` contract value.
//! The `self` contract value for private execution contexts.

use crate::{
context::{
calls::{PrivateCall, PrivateStaticCall, PublicCall, PublicStaticCall},
PrivateContext,
PublicContext,
UtilityContext,
},
event::{
event_emission::{emit_event_in_private, emit_event_in_public},
event_interface::EventInterface,
EventMessage,
},
context::{calls::{PrivateCall, PrivateStaticCall, PublicCall, PublicStaticCall}, PrivateContext},
event::{event_emission::emit_event_in_private, event_interface::EventInterface, EventMessage},
};
use crate::protocol::{address::AztecAddress, traits::{Deserialize, Serialize}};

/// Core interface for interacting with aztec-nr contract features.
/// Core interface for interacting with aztec-nr contract features in private execution contexts.
///
/// This struct is automatically injected into every [`external`](crate::macros::functions::external) and
/// [`internal`](crate::macros::functions::internal) contract function by the Aztec macro system and is accessible
/// through the `self` variable.
/// [`internal`](crate::macros::functions::internal) contract function marked with `"private"` by the Aztec macro
/// system and is accessible through the `self` variable.
///
/// ## Usage in Contract Functions
///
/// Once injected, you can use `self` to:
/// - Access storage: `self.storage.balances.at(owner).read()`
/// - Call contracts: `self.call(Token::at(address).transfer(recipient, amount))`
/// - Emit events: `self.emit(event).deliver_to(recipient, delivery_mode)` (private) or `self.emit(event)` (public)
/// - Emit events: `self.emit(event).deliver_to(recipient, delivery_mode)`
/// - Get the contract address: `self.address`
/// - Get the caller: `self.msg_sender()`
/// - Access low-level Aztec.nr APIs through the context: `self.context`
Expand All @@ -49,44 +40,37 @@ use crate::protocol::{address::AztecAddress, traits::{Deserialize, Serialize}};
///
/// ## Type Parameters
///
/// - `Context`: The execution context type - either `&mut PrivateContext`, `PublicContext`, or `UtilityContext`
/// - `Storage`: The contract's storage struct (defined with [`storage`](crate::macros::storage::storage), or `()` if
/// the contract has no storage
/// - `CallSelf`: Macro-generated type for calling contract's own non-view functions
/// - `EnqueueSelf`: Macro-generated type for enqueuing calls to the contract's own non-view functions
/// - `CallSelfStatic`: Macro-generated type for calling contract's own view functions
/// - `EnqueueSelfStatic`: Macro-generated type for enqueuing calls to the contract's own view functions
pub struct ContractSelf<Context, Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInternal> {
/// - `CallInternal`: Macro-generated type for calling internal functions
pub struct ContractSelfPrivate<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInternal> {
/// The address of this contract
pub address: AztecAddress,

/// The contract's storage instance, representing the struct to which the
/// [`storage`](crate::macros::storage::storage) macro was applied in your contract. If the contract has no
/// storage, the type of this will be `()`.
///
/// This storage instance is specialized for the current execution context (private, public, or utility) and
/// provides access to the contract's state variables. Each state variable accepts the context as a generic
/// parameter, which determines its available functionality. For example, a PublicImmutable variable can be read
/// from any context (public, private, or utility) but can only be written to from public contexts.
/// This storage instance is specialized for the current execution context (private) and
/// provides access to the contract's state variables.
///
/// ## Developer Note
///
/// If you've arrived here while trying to access your contract's storage while the `Storage` generic type is set
/// to unit type `()`, it means you haven't yet defined a Storage struct using the
/// [`storage`](crate::macros::storage::storage) macro in your contract. For guidance on setting this up, please
/// refer to our docs: https://docs.aztec.network/developers/docs/guides/smart_contracts/storage

pub storage: Storage,

/// The execution context whose type is determined by the [`external`](crate::macros::functions::external)
/// attribute of the contract function based on the external function type (private, public, or utility).
pub context: Context,
/// The private execution context.
pub context: &mut PrivateContext,

/// Provides type-safe methods for calling this contract's own non-view functions.
///
/// In private and public contexts this will be a struct with appropriate methods; in utility context it will be
/// the unit type `()`.
///
/// Example API:
/// ```noir
/// self.call_self.some_private_function(args)
Expand All @@ -95,9 +79,6 @@ pub struct ContractSelf<Context, Storage, CallSelf, EnqueueSelf, CallSelfStatic,

/// Provides type-safe methods for enqueuing calls to this contract's own non-view functions.
///
/// In private context this will be a struct with appropriate methods; in public and utility contexts it will be
/// the unit type `()`.
///
/// Example API:
/// ```noir
/// self.enqueue_self.some_public_function(args)
Expand All @@ -106,19 +87,13 @@ pub struct ContractSelf<Context, Storage, CallSelf, EnqueueSelf, CallSelfStatic,

/// Provides type-safe methods for calling this contract's own view functions.
///
/// In private and public contexts this will be a struct with appropriate methods; in utility context it will be
/// the unit type `()`.
///
/// Example API:
/// ```noir
/// self.call_self_static.some_view_function(args)
/// ```
pub call_self_static: CallSelfStatic,

/// Provides type-safe methods for enqueuing calls to this contract's own view functions.
///
/// In private context this will be a struct with appropriate methods; in public and utility contexts it will be
/// the unit type `()`.
/// Provides type-safe methods for enqueuing calls to the contract's own view functions.
///
/// Example API:
/// ```noir
Expand All @@ -128,24 +103,18 @@ pub struct ContractSelf<Context, Storage, CallSelf, EnqueueSelf, CallSelfStatic,

/// Provides type-safe methods for calling internal functions.
///
/// In private and public contexts this will be a struct with appropriate methods; in utility context it will be
/// the unit type `()`.
///
/// Example API:
/// ```noir
/// self.internal.some_internal_function(args)
/// ```
pub internal: CallInternal,
}

// Implementation for `ContractSelf` in private execution contexts.
//
// This implementation is used when an external or internal contract function is marked with "private".
impl<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInternal> ContractSelf<&mut PrivateContext, Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInternal> {
/// Creates a new `ContractSelf` instance for a private function.
impl<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInternal> ContractSelfPrivate<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInternal> {
/// Creates a new `ContractSelfPrivate` instance for a private function.
///
/// This constructor is called automatically by the macro system and should not be called directly.
pub fn new_private(
pub fn new(
context: &mut PrivateContext,
storage: Storage,
call_self: CallSelf,
Expand Down Expand Up @@ -335,8 +304,8 @@ impl<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInte

/// Enqueues a privacy-preserving public contract call function.
///
/// This is the same as [`ContractSelf::enqueue`], except it hides this calling contract's address from the target
/// public function (i.e. [`ContractSelf::msg_sender`] will panic).
/// This is the same as [`ContractSelfPrivate::enqueue`], except it hides this calling contract's address from the
/// target public function (i.e. [`ContractSelfPrivate::msg_sender`] will panic).
///
/// This means the origin of the call (msg_sender) will not be publicly visible to any blockchain observers, nor to
/// the target public function. If the target public function reads `self.msg_sender()` the call will revert.
Expand Down Expand Up @@ -426,141 +395,3 @@ impl<Storage, CallSelf, EnqueueSelf, CallSelfStatic, EnqueueSelfStatic, CallInte
call.set_as_teardown_incognito(self.context)
}
}

// Implementation for `ContractSelf` in public execution contexts.
//
// This implementation is used when an external or internal contract function is marked with "public".
impl<Storage, CallSelf, CallSelfStatic, CallInternal> ContractSelf<PublicContext, Storage, CallSelf, (), CallSelfStatic, (), CallInternal> {
/// Creates a new `ContractSelf` instance for a public function.
///
/// This constructor is called automatically by the macro system and should not be called directly.
pub fn new_public(
context: PublicContext,
storage: Storage,
call_self: CallSelf,
call_self_static: CallSelfStatic,
internal: CallInternal,
) -> Self {
Self {
context,
storage,
address: context.this_address(),
call_self,
enqueue_self: (),
call_self_static,
enqueue_self_static: (),
internal,
}
}

/// The address of the contract address that made this function call.
///
/// This is similar to Solidity's `msg.sender` value.
///
/// ## Incognito Calls
///
/// Contracts can call public functions from private ones hiding their identity (see
/// [`enqueue_incognito`](ContractSelf::enqueue_incognito)). This function reverts when executed in such a context.
///
/// If you need to handle these cases, use [`PublicContext::maybe_msg_sender`].
pub fn msg_sender(self: Self) -> AztecAddress {
self.context.maybe_msg_sender().unwrap()
}

/// Emits an event publicly.
///
/// Public events are emitted as plaintext and are therefore visible to everyone. This is is the same as Solidity
/// events on EVM chains.
///
/// Unlike private events, they don't require delivery of an event message.
///
/// # Example
/// ```noir
/// #[event]
/// struct Update { value: Field }
///
/// #[external("public")]
/// fn publish_update(value: Field) {
/// self.emit(Update { value });
/// }
/// ```
///
/// # Cost
///
/// Public event emission is achieved by emitting public transaction logs. A total of `N+1` fields are emitted,
/// where `N` is the serialization length of the event.
pub fn emit<Event>(&mut self, event: Event)
where
Event: EventInterface + Serialize,
{
emit_event_in_public(self.context, event);
}

/// Makes a public contract call.
///
/// Will revert if the called function reverts or runs out of gas.
///
/// # Arguments
/// * `call` - The object representing the public function to invoke.
///
/// # Returns
/// * `T` - Whatever data the called function has returned.
///
/// # Example
/// ```noir
/// self.call(Token::at(address).transfer_in_public(recipient, amount));
/// ```
///
pub unconstrained fn call<let M: u32, let N: u32, T>(self, call: PublicCall<M, N, T>) -> T
where
T: Deserialize,
{
call.call(self.context)
}

/// Makes a public read-only contract call.
///
/// 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<let M: u32, let N: u32, T>(self, call: PublicStaticCall<M, N, T>) -> T
where
T: Deserialize,
{
call.view(self.context)
}
}

// Implementation for `ContractSelf` in utility execution contexts.
//
// This implementation is used when an external or internal contract function is marked with "utility".
impl<Storage> ContractSelf<UtilityContext, Storage, (), (), (), (), ()> {
/// Creates a new `ContractSelf` instance for a utility function.
///
/// This constructor is called automatically by the macro system and should not be called directly.
pub fn new_utility(context: UtilityContext, storage: Storage) -> Self {
Self {
context,
storage,
address: context.this_address(),
call_self: (),
enqueue_self: (),
call_self_static: (),
enqueue_self_static: (),
internal: (),
}
}
}
Loading
Loading