> For the complete documentation index, see [llms.txt](/llms.txt)

# Migration notes

Aztec is in active development. Each version may introduce breaking changes that affect compatibility with previous versions. This page documents common errors and difficulties you might encounter when upgrading, along with guidance on how to resolve them.

## 6.0.0

### \[Aztec.nr] ECDSA note and protocol contract interfaces are published with aztec-nr

`ecdsa_public_key_note` and the protocol contract interfaces now ship in `aztec-labs-eng/aztec-nr`. Depending on them from `AztecProtocol/aztec-packages` or `aztec-labs-eng/aztec-node` pulls in a second copy of `aztec`, which fails to compile with errors such as `No matching impl found` or `found type &mut PrivateContext`. Depend on them from `aztec-labs-eng/aztec-nr`, at the same tag as `aztec`:

```diff
-ecdsa_public_key_note = { git = "https://github.com/AztecProtocol/aztec-packages", tag = "v5.2.0", directory = "noir-projects/noir-contracts/contracts/libs/ecdsa_public_key_note" }
-contract_instance_registry = { git = "https://github.com/AztecProtocol/aztec-packages", tag = "v5.2.0", directory = "noir-projects/noir-contracts/contracts/protocol_interface/contract_instance_registry_interface" }
-fee_juice = { git = "https://github.com/AztecProtocol/aztec-packages", tag = "v5.2.0", directory = "noir-projects/noir-contracts/contracts/protocol_interface/fee_juice_interface" }
+ecdsa_public_key_note = { git = "https://github.com/aztec-labs-eng/aztec-nr", tag = "v<version>", directory = "ecdsa-public-key-note" }
+contract_instance_registry = { git = "https://github.com/aztec-labs-eng/aztec-nr", tag = "v<version>", directory = "contract-instance-registry-interface" }
+fee_juice = { git = "https://github.com/aztec-labs-eng/aztec-nr", tag = "v<version>", directory = "fee-juice-interface" }
```

### \[Aztec.nr] Fact collections are scoped by `FactScope`

The `aztec::facts` functions now take the collection's scope as an `aztec::facts::FactScope` instead of an `AztecAddress`, and `FactCollection::scope` is a `FactScope`. `FactScope::account(address)` is the scope of a single account, as before. The new `FactScope::public()` is shared by all accounts: any execution of the contract can access its collections, whichever accounts are in scope.

**Migration:**

```diff
- use aztec::facts::record_retractable_fact;
+ use aztec::facts::{FactScope, record_retractable_fact};

- record_retractable_fact(contract_address, recipient, type_id, collection_id, fact_type_id, payload, origin_block);
+ record_retractable_fact(
+     contract_address,
+     FactScope::account(recipient),
+     type_id,
+     collection_id,
+     fact_type_id,
+     payload,
+     origin_block,
+ );
```

This is a breaking change to the PXE oracle interface (version 32 → 33): contracts must be recompiled against the updated `aztec-nr` to run against the new PXE.

## 6.0.0-rc.3

### \[Aztec.nr] `AuthRegistry` re-pinned at a new address

A further standard-contract re-pin, against oracle interface version 32, moves the canonical `AuthRegistry` to a new address and class id. Authorizations set on the previous `AuthRegistry` instance must be set again on the new one. This is separate from the re-pin that shipped in 6.0.0-rc.1, which moved only `MultiCallEntrypoint` and `HandshakeRegistry`.

### \[Protocol] The contract address domain separator is bumped to V3; every contract address changes

The domain separator that goes into contract address derivation (`preaddress = poseidon2(DOM_SEP__CONTRACT_ADDRESS_Vn, public_keys_hash, partial_address)`) is bumped: `DOM_SEP__CONTRACT_ADDRESS_V2` (`4099338721`) is replaced by `DOM_SEP__CONTRACT_ADDRESS_V3 = 993442748` (`hash_to_u32("az_dom_sep", "contract_address_v3")`). Every protocol version rotates this separator on purpose: an address can never exist on two rollup instances, so funds cannot be sent to an address that is live on one rollup and dead on another. As a result, every contract address, including account addresses, differs from the address the same class, salt, initialization and public keys produced under V2. The `V` number is the separator revision, not the protocol version.

Nothing needs to change in contract code, but anything that hard-codes an address must be updated. The canonical addresses of the standard contracts, which are deterministic in their pinned artifacts and the separator, move to:

| Contract            | V2 address                                                           | V3 address                                                           |
| ------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------- |
| AuthRegistry        | `0x2e05e8fbfb2ce9a31e4b4ae5097c01b79d9b3529b1e861016c9c9a5d62fdf7c9` | `0x2ba8714679fbee823f108f366f878ce3249861637de705b0ac7cfbd0f81b3a9c` |
| MultiCallEntrypoint | `0x09b93e8efd54a70cd718bc4d8ecf52c51db8150d300c8877c56796ee8c505137` | `0x265552a432ba2f9420ce5283bec14d7385e7a4accaff6c82648efe312d42fcc2` |
| PublicChecks        | `0x0468c918e7d7e077f4fa75e275891db9bee8faaeafab83e870eeb39dbfaddbbe` | `0x2cfebe9733e41d10357b4362dc434a24a7c53f12e952f5727a38c6da7ec6d21a` |
| HandshakeRegistry   | `0x24d2effac2983dfcae3aea48019aa10b80a7d2cfddc52cdbd9b90745ed0b0fc7` | `0x198aa5f21f7a13761f4729c544fb6edcae73a484d57b248b6390eb0dc5c06040` |

`STANDARD_*_ADDRESS` in `@aztec-labs/standard-contracts` and `aztec::standard_addresses` carry the new values, so code that reads them needs only a recompile. HandshakeRegistry derives addresses itself, through `get_public_keys`, so the separator is compiled into its bytecode and the pinned standard-contract build is refreshed with it. That recompile also picks up the current aztec-nr in AuthRegistry and MultiCallEntrypoint, whose bytecode changes with it, so their artifact hashes and addresses move for that reason as well as for the separator. PublicChecks' bytecode is unchanged by the recompile; its address moves through the separator alone.

The protocol contracts keep their magic addresses (`1`, `2`, ...). Their registration nullifiers are siloed by those magic addresses rather than by the derived ones, so the genesis constants move for a narrower reason: ContractInstanceRegistry calls `AztecAddress::compute` when it publishes an instance, so the separator is in its bytecode, its class id rotates, and exactly one of the six seeded registration nullifiers changes with it.

### \[Aztec.nr] `Writer::advance_offset` is removed

Write the skipped fields instead, e.g. with their own `stream_serialize`:

```diff
- writer.advance_offset(<T as Serialize>::N);
+ value.stream_serialize(writer);
```

`Serialize for Option<T>` now fails on a `None` whose payload is non-zero. `Option::none()` has a zero payload, so its encoding is unchanged and no contract or client change is needed.

### \[Aztec.nr] Batch nullifier status oracle

The single-nullifier existence oracle is replaced by `aztec::oracle::nullifiers::get_nullifier_statuses`, which checks any number of nullifiers in one call and reports, for each settled one, the block it was included in. `check_nullifier_exists` is kept and now calls the new oracle.

This is a breaking change to the PXE oracle interface (version 31 → 32): contracts must be recompiled against the updated `aztec-nr` to run against the new PXE.

### \[Aztec.nr] `OriginBlock` replaced by `BlockReference`

`aztec::facts::OriginBlock` has been removed. Retractable facts now identify their origin block with `aztec::oracle::block_reference::BlockReference`, which has the same `block_number` and `block_hash` fields.

**Migration:**

```diff
- use aztec::facts::{OriginBlock, record_retractable_fact};
+ use aztec::facts::record_retractable_fact;
+ use aztec::oracle::block_reference::BlockReference;

- let origin_block = OriginBlock { block_number, block_hash };
+ let origin_block = BlockReference { block_number, block_hash };
```

### \[AztecNode] `getBlockHashMembershipWitness` requires an existing reference block; new `getBlockHashMembershipWitnessAtArchive`

`getBlockHashMembershipWitness(referenceBlock, blockHash)` returns a witness against the reference block header's `lastArchive.root`, the archive before the reference block was appended. v5 nodes also accepted a number-only reference one past the latest block, answering against the latest block's archive without that block existing. v6 does not: every reference, including a bare number, must name a block the node has, and a number past the tip fails once the node's hold-off for unseen blocks runs out.

The node also checks that the archive it proves against is exactly the one the reference header commits to, and throws if its world state holds a different archive at that height (for instance after a reorg) instead of answering from it.

To prove membership against a given archive root, including a block's own post-block archive, use the new `getBlockHashMembershipWitnessAtArchive({ archive }, blockHash)`. It accepts only an `{ archive }` selector, needs no later block to exist, and throws if the node does not know the root. Picking a root does not make it canonical or final: callers must choose a root their verifier accepts.

```diff
- // Witness against the archive after block N, via the block after it
- const witness = await aztecNode.getBlockHashMembershipWitness(BlockNumber(n + 1), blockHash);
+ const witness = await aztecNode.getBlockHashMembershipWitnessAtArchive({ archive: blockN.archive.root }, blockHash);
```

Callers that anchor on a block header, such as PXE and its oracles, keep using `getBlockHashMembershipWitness`.

## 6.0.0-rc.1

### \[Aztec.nr] `MultiCallEntrypoint` and `HandshakeRegistry` re-pinned at new addresses

The standard contracts have been re-pinned against the v6.0.0-rc.1 toolchain. The canonical `MultiCallEntrypoint` and `HandshakeRegistry` move to new addresses and class ids; `AuthRegistry` and `PublicChecks` keep theirs. Handshakes established with the previous registry instance are not visible to the new one and must be re-established.

### \[Protocol] The protocol nullifier is derived from the tx request's salt alone; `tx_request_salt` becomes `protocol_nullifier`

The protocol nullifier, which the init kernel always inserts at index 0 of a transaction's nullifiers, is now `silo(NULL_MSG_SENDER, H(origin, chain_id, version, salt))` instead of `H(tx_request)`. Gas settings and the first call's arguments are no longer part of its preimage, so two transactions built with the same salt (a fee bump, or a cancellation) share it and at most one of them can be mined. The proof is still pinned to the full tx request by the init kernel. The kernel cannot check that the salt is random, so wallets must draw it fresh for every transaction: two requests with the same origin and salt are mutually exclusive.

The `tx_request_salt` field of `PrivateCircuitPublicInputs`, `PrivateContextInputs` and `PrivateTxConstantData` is replaced by `protocol_nullifier`: the siloed protocol nullifier, kernel-checked and identical on every private call of the transaction. The raw salt no longer reaches app circuits. This is set by the framework and PXE; contracts need no changes beyond recompiling. `TxRequest` gains `computeProtocolNullifierValue()` / `computeProtocolNullifier()` in Aztec.js (`compute_protocol_nullifier_value` / `compute_protocol_nullifier` in Noir); `TxRequest.hash()` (Noir: `impl Hash for TxRequest`) and `DOM_SEP__TX_REQUEST` are removed, since computing the old protocol nullifier was their only job; and `computeProtocolNullifier(value)` in `@aztec-labs/stdlib/hash` takes the unsiloed value.

Nothing in accounts, auth witnesses or wallets changes with this release; the field is only renamed and its value redefined. It is the protocol half of the design that lets accounts and auth witnesses bind to `protocol_nullifier` for replay protection, cancellation and fee bumping, which lands in the framework separately.

### \[Node] `ACVM_*` config renamed to `NOIR_EXECUTE_*`

Protocol circuits are now executed with noir's `noir-execute` rather than the `acvm` binary, so the configuration naming the executor follows the tool. The environment variables, the CLI flags and the config fields are renamed; nothing about what you point them at changes, beyond the binary itself no longer being called `acvm`.

| Old                           | New                                  |
| ----------------------------- | ------------------------------------ |
| `$ACVM_BINARY_PATH`           | `$NOIR_EXECUTE_BINARY_PATH`          |
| `$ACVM_WORKING_DIRECTORY`     | `$NOIR_EXECUTE_WORKING_DIRECTORY`    |
| `--sequencer.acvmBinaryPath`  | `--sequencer.noirExecuteBinaryPath`  |
| `--proverNode.acvmBinaryPath` | `--proverNode.noirExecuteBinaryPath` |
| `acvmBinaryPath`              | `noirExecuteBinaryPath`              |
| `acvmWorkingDirectory`        | `noirExecuteWorkingDirectory`        |

The same rename applies to the `acvmWorkingDirectory` flags (`--sequencer.acvmWorkingDirectory`, `--proverNode.acvmWorkingDirectory`). The release image sets the new variables itself, and its default working directory moves from `/usr/src/acvm` to `/usr/src/noir-execute`.

**Migration:**

```diff
- ACVM_BINARY_PATH=/usr/src/labs-aztec-toolchain/bin/noir-execute
- ACVM_WORKING_DIRECTORY=/usr/src/acvm
+ NOIR_EXECUTE_BINARY_PATH=/usr/src/labs-aztec-toolchain/bin/noir-execute
+ NOIR_EXECUTE_WORKING_DIRECTORY=/usr/src/noir-execute
```

There is no fallback to the old names, and a node still setting only `ACVM_BINARY_PATH` fails to start rather than running degraded: a prover agent exits with `Requested real proving but no path to bb or noir-execute binaries provided`, and a prover node or sequencer configured for real proofs throws from `BBNativeRollupProver.new` when it tries to stat an undefined path. Only a node running without real proofs falls back to WASM simulation, which is much slower.

Type names keep the ACVM spelling (`NativeACVMSimulator`, `ACVMConfig`, `ACVMWitness`), since those describe the ACIR VM and its witness format rather than the tool that runs it.

### \[Protocol] Block header commits to a per-block tx effects tree

The L2 block header gains a `tx_effects_tree_root` field: the root of a per-block merkle tree with one leaf per transaction, where each leaf binds the transaction hash to a structured hash of its effects (note hashes, nullifiers, L2-to-L1 messages, public data writes, and logs). Holding a block header is now enough to verify that a transaction was included in that block and produced exactly a given set of effects with a short membership proof, instead of replaying the checkpoint's full sponge blob. The root is computed by the rollup circuits and is available on `BlockHeader` as `txEffectsTreeRoot`; `TxEffect.computeTxEffectLeaf()` and `Body.computeTxEffectsTreeRoot()` compute the leaves and root from published data.

This is a breaking protocol change: the block header serialization grew by one field, so every transaction hash, verification key, contract artifact, canonical contract address, and the genesis constants change. Contracts must be recompiled against the updated `aztec-nr`. The PXE oracle interface version was bumped (30 → 31), as was the TXE oracle interface version (8 → 9), and existing PXE databases are re-initialized on next open (data schema version 14 → 15). Nodes resync their archiver, which now also stores the tx effects tree leaves of each block (archiver data schema version 9 → 10).

### \[npm] Packages moved to the `@aztec-labs` scope

Every package published from this repository is now scoped `@aztec-labs` instead of `@aztec`. Update your dependencies and imports:

```diff
-import { Contract } from '@aztec/aztec.js';
+import { Contract } from '@aztec-labs/aztec.js';
```

```diff
-  "@aztec/aztec.js": "<version>",
-  "@aztec/accounts": "<version>",
+  "@aztec-labs/aztec.js": "<version>",
+  "@aztec-labs/accounts": "<version>",
```

Subpath imports are unaffected beyond the scope: `@aztec/foundation/log` becomes `@aztec-labs/foundation/log`.

The `@aztec` scope keeps the packages published for earlier versions, so an existing project pinned to an older release continues to install unchanged. There is no `@aztec-labs` release of those older versions.

The packages now depend on upstream `viem` instead of the `@aztec/viem` fork. If your project aliased `viem` to the fork (`"viem": "npm:@aztec/viem@<version>"`), depend on upstream `viem` at the version `@aztec-labs/aztec.js` uses instead.

### \[npm] Foundation packages moved to the `@aztec-foundation` scope

The packages built by the foundation repository and consumed here - `bb.js`, `bb-avm-sim`, `cdb`, `wsdb`, `ipc-runtime`, the `noir-*` packages, `l1-artifacts`, `constants-codegen` and the protocol artifacts packages - are now scoped `@aztec-foundation` instead of `@aztec`. Update any direct dependency on them:

```diff
-import { Barretenberg } from '@aztec/bb.js';
+import { Barretenberg } from '@aztec-foundation/bb.js';
```

```diff
-  "@aztec/bb.js": "<version>",
-  "@aztec/noir-noir_js": "<version>",
+  "@aztec-foundation/bb.js": "<version>",
+  "@aztec-foundation/noir-noir_js": "<version>",
```

Most projects reach these packages only through `@aztec-labs/*`, which carries the change for them; only a direct dependency needs updating.

The `@aztec` scope keeps the packages published for earlier versions, so an existing project pinned to an older release continues to install unchanged. There is no `@aztec-foundation` release of those older versions: the scope starts at `6.0.0-nightly.20260901`.

### \[Aztec.nr] aztec-nr moved to `aztec-labs-eng/aztec-nr`

The aztec-nr crates are now published at <https://github.com/aztec-labs-eng/aztec-nr>. The previous repository, `AztecProtocol/aztec-nr`, has no tags from v6 onwards, so a project that only bumps its `tag` fails to compile with `Remote branch v6.x.x not found`. Update the `git` field of every aztec-nr dependency in your `Nargo.toml` files as well:

```diff
-aztec = { git = "https://github.com/AztecProtocol/aztec-nr", tag = "v5.2.0", directory = "aztec" }
+aztec = { git = "https://github.com/aztec-labs-eng/aztec-nr", tag = "v<version>", directory = "aztec" }
```

`aztec compile` warns when a dependency still points at the previous repository.

### \[Aztec.nr] `DelayedPublicMutable` rejects delays below one hour

`DelayedPublicMutable` now enforces a minimum delay of one hour (`aztec::state_vars::DELAYED_PUBLIC_MUTABLE_MINIMUM_DELAY`, 3600 seconds). Declaring a state variable with a shorter initial delay fails to compile, and `schedule_delay_change` reverts when passed a shorter delay:

```noir
// Fails to compile: the delay is below DELAYED_PUBLIC_MUTABLE_MINIMUM_DELAY.
paused: DelayedPublicMutable<bool, 300, Context>,
```

Shorter delays make the time window users have to read chain state, produce a proof and get it included too short, effectively limiting access to the system, which is an unintended side-effect of controlling the delay.

Raise any initial delay below one hour - a delay of at least a couple of hours remains the recommendation.

### \[CLI] Removed `bridge-erc20`

`aztec bridge-erc20` performed only the L1 half of a token bridge deposit: it approved the ERC20 and called `depositToAztecPublic` (or `depositToAztecPrivate`) on a TokenPortal you supplied, then printed a claim secret for you to redeem on L2 yourself. Using it required already having deployed your own L1 TokenPortal and its L2 counterpart.

Use `L1ToL2TokenPortalManager` from `@aztec-labs/aztec.js/ethereum` instead, which is what the command wrapped:

```ts
import { L1ToL2TokenPortalManager } from '@aztec-labs/aztec.js/ethereum';

const manager = new L1ToL2TokenPortalManager(portalAddress, tokenAddress, handlerAddress, l1Client, logger);
const { claimAmount, claimSecret } = await manager.bridgeTokensPublic(recipient, amount, mint);
```

Fee juice bridging is unaffected: keep using `aztec-wallet bridge-fee-juice`.

### \[CLI] Removed `get-canonical-sponsored-fpc-address`, replaced by a wallet alias

`aztec-wallet` now aliases the canonical SponsoredFPC as `contracts:SponsoredFPC` on startup, alongside the protocol and standard contracts, so commands can name it instead of taking its address:

```bash
aztec-wallet register-contract contracts:SponsoredFPC SponsoredFPC --salt 0
aztec-wallet send transfer --payment method=fpc-sponsored,fpc=contracts:SponsoredFPC ...
```

If you need the address itself (for a script or a non-wallet consumer), use `getSponsoredFPCAddress` from `@aztec-labs/cli/cli-utils`.

### \[CLI] Removed `inspect-contract`, `compute-selector`, `generate-secret-and-hash`, `parse-parameter-struct`, and `example-contracts`

The `aztec` CLI no longer includes these contract-inspection helpers, which predate the current tooling and had no remaining use:

* `aztec inspect-contract`: contract class details and function selectors are available programmatically via `getContractClassFromArtifact` and `FunctionSelector.fromSignature` in `@aztec-labs/aztec.js`.
* `aztec compute-selector`: use `FunctionSelector.fromSignature` from `@aztec-labs/aztec.js`.
* `aztec generate-secret-and-hash`: use `Fr.random()` and `computeSecretHash` from `@aztec-labs/aztec.js`.
* `aztec parse-parameter-struct`: no replacement.
* `aztec example-contracts`: the list of example contracts is the set of subpath exports of `@aztec-labs/noir-contracts.js`.

### \[Aztec.js] Contract artifacts preserve the names of `#[abi(tag)]` globals

Globals exported from a Noir contract with `#[abi(tag)]` now keep their names in the artifact: entries in `ContractArtifact.outputs.globals` are `{ name, value }` objects as emitted by the compiler, where the names used to be stripped on load. This lets TypeScript read contract constants by name instead of duplicating their values:

```noir
#[abi(constants)]
pub global EXPORTED_FIELD_CONSTANT: Field = 1234;
#[abi(constants)]
pub global EXPORTED_STRING_CONSTANT: str<8> = "exported";
```

```typescript
import { getGlobalsByTag } from '@aztec-labs/aztec.js/abi';

const { EXPORTED_FIELD_CONSTANT, EXPORTED_STRING_CONSTANT } = getGlobalsByTag(MyContractArtifact, 'constants');
```

There is no backwards compatibility path: artifacts compiled before Noir exported global names (with bare, unnamed entries) are rejected on load and must be recompiled with the current toolchain. The artifact hash commits to the contract artifact's ABI outputs (`ContractArtifact.outputs`), so adding names to `outputs.globals` changes the artifact hash, the contract class ID, and any addresses derived from it. The PXE data schema version was bumped accordingly, so existing PXE databases resync on first open.

[Learn how to export globals under one or more tags and read them from TypeScript](/developers/docs/aztec-nr/framework-description/contract_artifact.md#exported-globals).

### \[Aztec.js] A function's return type is a single `returnType`, not a `returnTypes` list

`FunctionAbi.returnTypes` is deprecated in favor of the single optional `FunctionAbi.returnType`, since multiple return values are already expressed as one `tuple` type. Read it through `getFunctionReturnType(abi)`, which also resolves artifacts serialized before `returnType` existed. `FunctionCall.returnTypes` is likewise replaced by `FunctionCall.returnType`.

`decodeFromAbi` now takes the single type a function returns (or `undefined`) instead of a list, and returns `undefined` for a function that returns nothing, where it previously returned `[]`. To decode a list of values, such as a function's arguments, use the new `decodeEachFromAbi`.

**Migration:**

```diff
- decodeFromAbi(abi.returnTypes, values)
+ decodeFromAbi(getFunctionReturnType(abi), values)

- decodeFromAbi(fn.parameters.map(param => param.type), args) as AbiDecoded[]
+ decodeEachFromAbi(fn.parameters.map(param => param.type), args)
```

### \[Aztec.js] Protocol contracts removed from `@aztec-labs/noir-contracts.js`

`@aztec-labs/noir-contracts.js` no longer includes the protocol contracts: the `FeeJuice`, `ContractClassRegistry`, and `ContractInstanceRegistry` artifacts and typed wrappers have been removed from the package, so imports such as `@aztec-labs/noir-contracts.js/FeeJuice` no longer resolve. These names are also no longer available to the `aztec` CLI's contract-name lookup (e.g. in `aztec example-contracts`).

Protocol contracts are distributed via `@aztec-labs/protocol-contracts` (artifacts and canonical deployment data), and typed wrappers for them are exported from `@aztec-labs/aztec.js/protocol`. The `aztec.js` wrappers are bound to the contract's canonical address, so attaching takes only the wallet: there is no address parameter.

**Migration:**

```diff
- import { FeeJuiceContract } from '@aztec-labs/noir-contracts.js/FeeJuice';
+ import { FeeJuiceContract } from '@aztec-labs/aztec.js/protocol';

- const feeJuice = FeeJuiceContract.at(ProtocolContractAddress.FeeJuice, wallet);
+ const feeJuice = FeeJuiceContract.withWallet(wallet);
```

### \[Aztec.js] Protocol contract wrappers: `at(wallet)` deprecated in favor of `withWallet(wallet)`

The protocol contract wrappers exported from `@aztec-labs/aztec.js/protocol` (`FeeJuiceContract`, `ContractClassRegistryContract`, `ContractInstanceRegistryContract`) rename their static `at(wallet)` to `withWallet(wallet)`. These wrappers are bound to the contract's canonical address, so their only parameter is the wallet to act through; `withWallet` states that directly and matches the existing `withWallet` instance method, whereas the one-argument `at` read as if it took an address. `at(wallet)` still works but is deprecated and will be removed in a future release.

**Migration:**

```diff
- const feeJuice = FeeJuiceContract.at(wallet);
+ const feeJuice = FeeJuiceContract.withWallet(wallet);
```

### \[Aztec.nr] Domain separators moved out of the protocol constants module

Nine `DOM_SEP__*` domain separators that used to live in the protocol constants module (`aztec::protocol::constants`, generated from `noir-protocol-circuits`) have moved into the `aztec` crate, next to the code that uses them. None of them were ever protocol constants (each hash is computed per-contract), so they no longer belong on the protocol export.

Two remain public, at a new path:

| Constant                           | Old path                     | New path                    |
| ---------------------------------- | ---------------------------- | --------------------------- |
| `DOM_SEP__PARTIAL_NOTE_COMMITMENT` | `aztec::protocol::constants` | `aztec::note::partial_note` |
| `DOM_SEP__NOTE_COMPLETION_LOG_TAG` | `aztec::protocol::constants` | `aztec::note::partial_note` |

```diff
- use aztec::protocol::constants::{DOM_SEP__NOTE_COMPLETION_LOG_TAG, DOM_SEP__PARTIAL_NOTE_COMMITMENT};
+ use aztec::note::partial_note::{DOM_SEP__NOTE_COMPLETION_LOG_TAG, DOM_SEP__PARTIAL_NOTE_COMMITMENT};
```

The other seven are now crate-internal (`pub(crate)`) and can no longer be imported from outside the `aztec` crate: `DOM_SEP__AUTHWIT_NULLIFIER`, `DOM_SEP__TX_NULLIFIER`, `DOM_SEP__SINGLE_USE_CLAIM_NULLIFIER`, `DOM_SEP__CONSTRAINED_MSG_NULLIFIER`, `DOM_SEP__ECDH_SUBKEY`, `DOM_SEP__ECDH_FIELD_MASK`, and `DOM_SEP__INITIALIZATION_NULLIFIER`.

**Impact**: Contracts that use aztec-nr's high-level APIs (notes, authwit, state variables, message delivery, ECDH) are unaffected, since these separators are applied internally. A contract that imported one of these constants directly must either switch to the new `aztec::note::partial_note` path (for the two public ones) or, for the now-internal ones, call the corresponding aztec-nr helper instead of recomputing the hash by hand. The generated TypeScript `DomainSeparator` enum in `@aztec-labs/constants` / `@aztec-labs/stdlib` likewise no longer contains the seven removed members (their values were unused in TypeScript).

Account contracts that implement their own entrypoint and hashed `DOM_SEP__TX_NULLIFIER` to emit the cancellation nullifier should call `compute_tx_nullifier` instead:

```diff
- use aztec::protocol::{constants::DOM_SEP__TX_NULLIFIER, hash::poseidon2_hash_with_separator};
+ use aztec::authwit::account::compute_tx_nullifier;

- context.push_nullifier_unsafe(poseidon2_hash_with_separator([tx_nonce], DOM_SEP__TX_NULLIFIER));
+ context.push_nullifier_unsafe(compute_tx_nullifier(tx_nonce));
```

### \[Aztec Node] `GasPrice` renamed to `FeesPerGas` in `@aztec-labs/ethereum`

The `GasPrice` interface exported from `@aztec-labs/ethereum/l1-tx-utils` is now `FeesPerGas`, matching viem's name for the same thing, and the methods and fields carrying it are renamed to match. These values were never a price paid: they are the EIP-1559 caps a transaction is sent with (`maxFeePerGas`, `maxPriorityFeePerGas`, `maxFeePerBlobGas`), and what the transaction actually pays is decided at inclusion. The fields inside the type are unchanged.

| Old                                         | New                                           |
| ------------------------------------------- | --------------------------------------------- |
| `GasPrice`                                  | `FeesPerGas`                                  |
| `ReadOnlyL1TxUtils.getGasPrice()`           | `ReadOnlyL1TxUtils.getFeesPerGas()`           |
| `L1TxState.gasPrice`                        | `L1TxState.feesPerGas`                        |
| `L1TxState.gasPriceHistory`                 | `L1TxState.feesPerGasHistory`                 |
| `TimedOutTxState.gasPriceHistory`           | `TimedOutTxState.feesPerGasHistory`           |
| `TimedOutTxState.finalGasPrice`             | `TimedOutTxState.finalFeesPerGas`             |
| `L1Deployer.sendTransaction()` → `gasPrice` | `L1Deployer.sendTransaction()` → `feesPerGas` |

**Migration:**

```diff
- import type { GasPrice } from '@aztec-labs/ethereum/l1-tx-utils';
+ import type { FeesPerGas } from '@aztec-labs/ethereum/l1-tx-utils';

- const gasPrice = await l1TxUtils.getGasPrice(gasConfig, true, 0);
+ const feesPerGas = await l1TxUtils.getFeesPerGas(gasConfig, true, 0);

  const { state } = await l1TxUtils.sendTransaction(request);
- logger.info(`Sent at ${state.gasPrice.maxFeePerGas}`);
+ logger.info(`Sent at ${state.feesPerGas.maxFeePerGas}`);
```

**Impact**: A rename with no behavior change. Two on-disk formats move with it, neither of which needs operator action:

* The node's L1 transaction state store writes the new key but still reads states written under the old one, so a node restarting on this version keeps monitoring transactions it sent before the upgrade.
* Failed-L1-transaction debug records (written when `L1_TX_FAILED_STORE` is set) now use `sentFeesPerGas` and `sentFeesPerGasLadder` instead of `sentGasPrice` and `sentGasPriceLadder`. Records written before the upgrade keep the old keys, and parsing one through `FailedL1TxSchema` returns it without those fields, though the values are still present in the stored JSON under the old names.

## 5.2.0

### \[Aztec.nr] Note types declared inside a contract must be `pub`

Noir v1.0.0-beta.25 enforces the caller's visibility when comptime code resolves a trait method into a typed expression. The `#[aztec]` macro resolves each note type's `NoteType::get_id` that way when it generates `compute_note_hash_and_nullifier`, so a note struct declared directly inside the `contract` module without `pub` no longer compiles:

```text
error: Function `get_id` is private
    ┌─ .../aztec/src/macros/utils.nr:152:5
    │ `get_id` is declared in `MyContract`
```

Note types declared in their own module are unaffected, since they are already `pub` for the contract to reach them.

**Migration:**

```diff
  #[derive(Packable, Eq)]
  #[note]
- struct MyNote {
+ pub struct MyNote {
      owner: AztecAddress,
  }
```

### \[Sequencer] A sequencer with p2p enabled no longer proposes while it has no connected peers

The sequencer skips building and proposing a checkpoint when p2p is enabled and it is connected to fewer than `SEQ_MIN_PEERS_TO_PROPOSE` peers (default `1`). Proposing into an empty peer set burns the slot and can publish a checkpoint whose transaction data nobody received. L1-only duties (governance and slashing votes, prune, invalidation) are unaffected, and setups that disable p2p entirely (sandbox, local network, single node) still propose as before.

A peerless node also warns periodically, stops filing data-withholding slashing offenses, and rejects incoming transactions until it reconnects.

**Impact**: set `SEQ_MIN_PEERS_TO_PROPOSE=0` to restore the previous behaviour. Fixing the underlying peer connectivity is the real remedy.

### \[Node API] JSON-RPC internal errors now return code `-32603`

The JSON-RPC server returned `-32600` ("Invalid Request") for internal server failures. It now returns `-32603` ("Internal error"), which is what the JSON-RPC 2.0 specification defines for that case. Genuine malformed-request errors still return `-32600`.

**Impact**: clients that branch on `-32600` to detect an internal server failure must also handle `-32603`.

### \[Node API] `GET /status` now returns a JSON body with per-component health

The status endpoint previously set only an HTTP status code and returned no body. It now responds with `application/json` describing per-component health, for example:

```json
{ "ok": true, "components": { "p2p": { "healthy": true, "enabled": true, "connectedPeers": 12 } } }
```

`StatusCheckFn` widens from `() => boolean | Promise<boolean>` to also allow a structured result, and the new `ComponentStatus` / `ServerStatus` / `ServerStatusFn` types plus `SafeJsonRpcServer.getStatus()` are exported. The p2p component fails the check when the connected peer count is below `P2P_HEALTH_MIN_PEERS` (default `0`, which never fails).

**Impact**: consumers that only read the HTTP status code are unaffected. Custom status callbacks that returned a bare boolean keep working.

### \[Aztec.js] `deserializeArrayFromVector` removed from `@aztec/foundation/serialize`

`deserializeArrayFromVector` has been deleted from `@aztec/foundation`'s serialization helpers and is no longer exported.

**Impact**: decode length-prefixed vectors with `BufferReader` (`reader.readVector(...)`) instead.

## 5.1.0

### \[Aztec.nr] Canonical HandshakeRegistry re-pinned at a new address

The canonical `HandshakeRegistry` has been re-pinned so that it includes the owner's address in its `PrivateMutable` initialization nullifiers, keeping the handshake state of accounts that share keys independent. The registry moves to a new address. Handshakes established with the previous registry instance are not visible to the new one and must be re-established. The other standard contracts keep their addresses.

### \[Aztec.nr] Note property selectors are typed and use packed-layout indices

The selectors in the generated `properties()` used the field's position in the note struct declaration, which pointed at the wrong packed field for any note with an earlier field packing to more than one `Field` (a `Point`, an array, a nested struct). Selector indices are now the field's offset in the note's packed representation, so `select`/`sort` criteria constrain the field they name.

Breaking changes:

* `PropertySelector<T>` carries the selected property's type. Hand-constructed literals need a type annotation, e.g. `let selector: PropertySelector<Field> = PropertySelector { index: 0, offset: 0, length: 32 };`.
* `select`/`sort` reject properties that pack to more than one `Field` at compile time.
* `select` takes its value typed as the property's type. Cast the value if a mixed-type comparison was intentional.
* `properties()` cannot be used with a custom `Packable` layout. Define property selectors manually for such notes.
* Every note field type must implement `Packable`, even when the note's own `Packable` is hand-written.

## 5.0.1

### \[Aztec.nr] History note nullification helpers renamed and restricted to own-contract notes

The `history::note` helpers that recompute a note's nullifier have been renamed with a `local_` prefix and now assert that the note belongs to the executing contract:

| Old                                | New                                      |
| ---------------------------------- | ---------------------------------------- |
| `assert_note_was_valid_by`         | `assert_local_note_was_valid_by`         |
| `assert_note_was_nullified_by`     | `assert_local_note_was_nullified_by`     |
| `assert_note_was_not_nullified_by` | `assert_local_note_was_not_nullified_by` |

These helpers derive the note's nullifier from the executing contract's app-siloed nullifier key, and it is not possible for a contract to retrieve the app-siloed nullifier for a different contract, as this would constitute leakage of key material. These helpers incorrectly used the local contract's key siloing, which for a note of a different contract resulted in the non-inclusion check passing unconditionally and wrongly reporting a nullified note as not nullified. The helpers now assert `contract_address == context.this_address()` and fail with "Note nullification history is only supported for the executing contract's own notes" otherwise.

**Impact**: Update call sites to the new names. Own-contract usage (the common case) is unaffected beyond the rename. `assert_note_existed_by` is unchanged and still supports notes of any contract, since note-hash inclusion involves no keys.

### \[Aztec.nr] `set_as_fee_payer` now asserts it is called during the setup phase

`PrivateContext::set_as_fee_payer` now asserts that execution is still in the setup (non-revertible) phase, i.e. that `end_setup` has not yet been called by any function in the transaction. Electing a fee payer in the revertible phase was never safe: compensation collected by the fee payer after `end_setup` can be discarded if a public call later reverts, while the protocol still debits the fee payer's fee-juice balance.

**Impact**: A transaction in which `set_as_fee_payer` runs after the setup phase has ended now fails with `fee payer must be elected during the setup phase`. Standard fee payment flows, which call `set_as_fee_payer` before or together with `end_setup`, are unaffected.

### \[PXE] Stores are now selected by `(l1ChainId, rollupAddress, schemaVersion)` instead of being wiped on mismatch

Previously, connecting a PXE or embedded wallet to a different or redeployed rollup, or bumping the store schema version, wiped the existing on-disk store in place. That meant master account keys could be destroyed simply by pointing a wallet at a different network. PXE data stores now exist per `(l1ChainId, rollupAddress, schemaVersion)` triple, and switching networks (or upgrading) selects or creates the matching store instead of overwriting previous ones. The embedded wallet's `wallet_data` store is partitioned the same way, so accounts and aliases are per network: switching networks starts with an empty account list until accounts are re-imported, and switching back finds the originals intact.

**Impact**: The first start after upgrading to this version begins with a fresh, empty store; the pre-upgrade data is not deleted. On Node.js environments (lmdb-v2) pre-upgrade data stays at `<dataDirectory>/<name>` while new per-identity `pxe_data` stores live under `<dataDirectory>/<name>-stores/`. The embedded Node.js wallet previously stored data in cwd-relative, rollup-address-suffixed directories instead (`pxe_data_<rollupAddress>/pxe_data` for the PXE store, `wallet_data_<rollupAddress>/wallet_data` for the wallet store): if you used it before this release, that is where the old data lives. The embedded wallet now defaults its data root to `aztec-wallet-data/`, with per-identity wallet stores under `<dataDirectory>/wallet_data-stores/` on Node.js and OPFS store names prefixed `wallet_data_` in the browser. Browser apps can enumerate and clean up `pxe_data` and `wallet_data` stores for networks no longer in use with the new `listStores()` / `deleteStore()` utilities:

```ts
import { deleteStore, listStores } from "@aztec/kv-store/sqlite-opfs";

const names = await listStores();
await deleteStore(names[0]);
```

This change also removes `createStore` from `@aztec/kv-store/sqlite-opfs` and `@aztec/kv-store/deprecated/indexeddb`: stores are now opened by name with `AztecSQLiteOPFSStore.open` / `AztecIndexedDBStore.open`.

## 5.0.0

### \[PXE] Local PXE database is reset on upgrade

The persisted tagging stores now key every entry by the self-describing `<kind>:<secret>:<app>` form of `AppTaggingSecret`; unconstrained secrets previously used a two-part `<secret>:<app>` key. This bumps the PXE data schema version, and there is no forward migration for the old keys: on first open the PXE clears any database whose stored schema version differs from the current one. The wipe resets the entire PXE store, not just the tagging data, because all of it shares one backing database.

**Impact**: On upgrade your local PXE state is reset. You must re-register accounts and re-sync from genesis. Wallets should surface a "your local state was reset, please re-register accounts and re-sync" path.

### \[Aztec.nr] `TestEnvironmentOptions::with_tagging_secret_strategy` replaced

`TestEnvironmentOptions::with_tagging_secret_strategy` is now `with_default_tag_secret_strategy_all_modes` for tests that want the same default wallet strategy for both onchain delivery modes. The new naming reflects that these helpers configure the TXE default wallet strategy hook; contract-fixed delivery derivations bypass that default.

For mode-specific defaults and hook semantics, see the [`resolveTaggingSecretStrategy` test helper docs](/developers/docs/foundational-topics/pxe/execution_hooks.md#resolvetaggingsecretstrategy).

### \[Aztec.nr] L1-to-L2 message consumption takes the secret as an array

`PrivateContext::consume_l1_to_l2_message` and `PublicContext::consume_l1_to_l2_message` now take the message secret as an arbitrary-length array `[Field; N]` instead of a single `Field`, so a consumer can derive its secret hash from more than one field. The helpers `compute_secret_hash` and `compute_l1_to_l2_message_nullifier` are likewise now generic over the secret length. A single-field secret behaves exactly as before (the hashes are unchanged for `N = 1`) — just wrap it in an array.

**Migration:**

```diff
- context.consume_l1_to_l2_message(content, secret, sender, leaf_index);
+ context.consume_l1_to_l2_message(content, [secret], sender, leaf_index);
```

```diff
- let secret_hash = compute_secret_hash(secret);
+ let secret_hash = compute_secret_hash([secret]);
```

**Impact**: Contracts that consume L1-to-L2 messages, or that call `compute_secret_hash` / `compute_l1_to_l2_message_nullifier`, must wrap their single-field secret in an array. Already-deployed contracts are unaffected: their unchanged bytecode keeps working, as PXE serves the previous L1-to-L2 membership-witness oracle through a compatibility adaptor.

### \[Aztec.js] `computeL1ToL2MessageNullifier` replaced by `computeFeeJuiceMessageNullifier`

The `@aztec/stdlib` helper `computeL1ToL2MessageNullifier(contract, messageHash, secret)`, which returned the siloed message nullifier, has been removed. Its replacement `computeFeeJuiceMessageNullifier(messageHash, secret)` returns the **unsiloed** nullifier — siloing now happens at the point where the nullifier is looked up. `getL1ToL2MessageWitness` accordingly takes an optional `{ contractAddress, nullifier }` (unsiloed) and silos internally. `computeSecretHash` is unchanged, and `getNonNullifiedL1ToL2MessageWitness` keeps the same signature.

**Migration:**

```diff
- const nullifier = await computeL1ToL2MessageNullifier(contract, messageHash, secret);
+ // computeFeeJuiceMessageNullifier returns the UNSILOED nullifier; silo it before looking it up, or hand the
+ // unsiloed value to getL1ToL2MessageWitness which silos for you.
+ const nullifier = await siloNullifier(contract, await computeFeeJuiceMessageNullifier(messageHash, secret));
```

**Impact**: Only affects code calling these low-level messaging helpers directly - most integrations use `getNonNullifiedL1ToL2MessageWitness`, which is unchanged.

### \[Aztec.js] Account signing keys are no longer derived from the privacy secret

Schnorr account signing keys used to be derived from the account's privacy secret (via the now-removed `deriveSigningKey`), which meant the ownership key could be reconstructed from a value the PXE holds. The relationship is now reversed: the signing key is the root, and the privacy secret is derived from it with `deriveSecretKeyFromSigningKey` (exported from `@aztec/accounts/utils`).

As a result:

* `deriveSigningKey` is removed.
* `getSchnorrAccountContractAddress` and `getSchnorrInitializerlessAccountContractAddress` now take the signing key first and an optional secret: `(signingPrivateKey, salt, secretKey?)`. When `secretKey` is omitted it is derived from the signing key.
* The embedded wallet's `createSchnorrAccount` and `createSchnorrInitializerlessAccount` now require an explicit signing key argument.

**Migration:**

```diff
- import { deriveSigningKey } from '@aztec/stdlib/keys';
- const signingKey = deriveSigningKey(secret);
- const address = await getSchnorrAccountContractAddress(secret, salt, signingKey);
+ import { GrumpkinScalar } from '@aztec/aztec.js/fields';
+ const signingKey = GrumpkinScalar.random(); // supply your own signing key
+ const address = await getSchnorrAccountContractAddress(signingKey, salt, secret);
```

**Impact**: Account addresses change, since both the signing key and the privacy secret feed the address. Code that derived the signing key from the secret, or passed the secret first to the address helpers, no longer compiles.

### \[Aztec.nr] `MessageContext` removed: message processing uses `ResolvedTx`

`aztec::messages::processing::MessageContext` has been removed in favor of the new `ResolvedTx`, which carries the same `tx_hash`, `unique_note_hashes_in_tx`, and `first_nullifier_in_tx`, plus `block_number` and `block_hash`. The offchain handoff type `OffchainMessageWithContext` is likewise renamed to `OffchainMessageWithTx`.

This affects contracts that implement a custom message handler (registered via `AztecConfig::custom_message_handler`): the handler's context parameter is now a `ResolvedTx`.

**Migration:**

```diff
  use aztec::messages::processing::{
-     enqueue_event_for_validation, MessageContext,
+     enqueue_event_for_validation, ResolvedTx,
  };

  unconstrained fn handle_my_message(
      // ...
-     message_context: MessageContext,
+     resolved_tx: ResolvedTx,
      scope: AztecAddress,
  ) {
-     // ...message_context.tx_hash...
+     // ...resolved_tx.tx_hash...
  }
```

### \[PXE] `pxe.updateContract` removed and `pxe.registerContract` no longer takes an artifact

Registering classes and instances are now separate, unvalidated operations. `registerContractClass(artifact)` registers a class, `registerContract(instance)` registers an instance and no longer takes an artifact. `registerContract` does not check that PXE knows the contract's artifact: a missing artifact surfaces only when the contract is later simulated.

**Migration:**

* `pxe.registerContract` now takes the instance directly (its address preimage) and returns the derived address. Register the class separately via `registerContractClass`:

```diff
- await pxe.registerContract({ instance, artifact });
+ await pxe.registerContractClass(artifact);
+ await pxe.registerContract(instance);
```

If you were calling it without an artifact, just drop the wrapping object: `pxe.registerContract({ instance })` becomes `pxe.registerContract(instance)`. The `wallet.registerContract(instance, artifact?, secretKeyOrKeys?)` convenience is unchanged and performs both registrations for you.

* To make a new class's code available after an onchain upgrade, register the new artifact instead of calling `updateContract`:

```diff
- await pxe.updateContract(address, newArtifact);
+ await pxe.registerContractClass(newArtifact);
```

The new class is used automatically once the upgrade takes effect on chain; no further PXE action is needed. Registering it beforehand is harmless: until the update activates, the node still resolves the contract's current class to the previous one, so it keeps running its old code.

* `pxe.getContractInstance(address)` and `wallet.getContractMetadata(address).instance` now return the contract's **address preimage**, which no longer includes `currentContractClassId`.

### \[Aztec.js] `AccountWithSecretKey` removed, read account keys from the `AccountManager` or PXE

`AccountWithSecretKey` was a thin wrapper that bundled an account's transaction signer with its master secret key, used mainly to print or export the secret. It has been removed, and `AccountManager.getAccount()` now returns the plain `Account` signer. The wrapper's extra methods are no longer available on that value:

* `getSecretKey()`: read it from the `AccountManager`, which still exposes `getSecretKey()`.
* `getEncryptionSecret()`: this was unused and has been removed. To recover an account's encryption (address) secret, pass its master incoming viewing secret key to `computeAddressSecret`. You can read that key, along with the account's other master secret keys, from `pxe.getAccountSecretKeys(address)`.

**Migration:**

```diff

- import { AccountWithSecretKey } from '@aztec/aztec.js/account';
-
- const account = await accountManager.getAccount();
- const secretKey = account.getSecretKey();
+ const secretKey = accountManager.getSecretKey();
```

To do what `AccountWithSecretKey` was meant for (exporting an account into a separate PXE or wallet), account registration accepts a full set of master secret keys instead of only a single seed. `wallet.registerContract(instance, artifact?, secretKeyOrKeys?)` takes either an `Fr` seed (as before) or a `MasterSecretKeys` object (exported from `@aztec/aztec.js/keys`), for an account whose privacy keys were generated independently rather than from one seed.

The PXE never receives the seed nor the message-signing and fallback secret keys: it is not trusted to hold them. The wallet derives the account's privacy keys and passes the PXE only the four privacy secret keys (nullifier-hiding, incoming-viewing, outgoing-viewing, tagging) plus the message-signing and fallback *public* keys. Accordingly, `pxe.getAccountSecretKeys(address)` returns only those four privacy secret keys.

**Impact**: Importing `AccountWithSecretKey`, or calling `getSecretKey()`/`getEncryptionSecret()` on the result of `getAccount()`, no longer compiles. The signer `getAccount()` returns is otherwise unchanged, and passing a single `Fr` or a `MasterSecretKeys` to `wallet.registerContract` keeps working.

### \[CLI] `aztec-wallet` `--secret-key` is renamed to `--signing-key`

`aztec-wallet` accounts are now rooted on their signing key rather than on a privacy secret. The `--secret-key` option (on `create-account` and `simulate`) is renamed to `--signing-key`, and the `SECRET_KEY` environment variable to `SIGNING_KEY`. When creating an account, a random signing key is generated by default and the privacy secret is derived from it.

**Migration:**

```diff
- aztec-wallet create-account --secret-key 0x...
+ aztec-wallet create-account --signing-key 0x...
```

**Impact**: The value you save and restore for an account is now its signing key, and account addresses change, so existing wallet databases and funded addresses are not carried over.

### \[Bot] `senderPrivateKey` is now the account signing key

The transaction bot previously derived its account's signing key from the configured `senderPrivateKey`. That value is now used directly as the signing key, with the privacy secret derived from it.

**Impact**: The bot's account address changes for a given `senderPrivateKey`, so the account must be re-funded at its new address.

### \[PXE] Unconstrained delivery defaults to a non-interactive handshake for external recipients

When no `resolveTaggingSecretStrategy` hook is configured, onchain unconstrained delivery now defaults to a non-interactive handshake when the recipient is external (an account whose keys the wallet does not hold), instead of an address-derived shared secret. A self-send (the recipient is one of the wallet's own accounts) still uses an address-derived secret, which needs no handshake and leaves no onchain trace.

**Impact**: An external recipient can now discover unconstrained-delivered messages without having registered the sender in advance, but establishing the handshake publishes an onchain marker derived from the recipient's address (anyone who knows that address can tell a handshake was created for them, though not by whom nor the contents). Wallets that want the previous behavior can configure a `resolveTaggingSecretStrategy` hook that returns an `address-derived` strategy.

### \[Aztec.nr] `PrivateContext` data fields are no longer public

`PrivateContext`'s data fields are now private (or crate-internal): its public API is now exclusively its methods. Contracts that read these fields directly must switch to the corresponding getter. A new `get_side_effect_counter()` getter exposes the side-effect counter, and a new `is_static_call()` getter replaces reaching into `inputs.call_context`. The `get_anchor_block_header()` getter already existed.

**Migration:**

```diff
- let header = context.anchor_block_header;
+ let header = context.get_anchor_block_header();

- let counter = context.side_effect_counter;
+ let counter = context.get_side_effect_counter();

- let is_static = context.inputs.call_context.is_static_call;
+ let is_static = context.is_static_call();
```

**Impact**: Direct field access on `PrivateContext` (e.g. `context.anchor_block_header`, `context.side_effect_counter`, `context.inputs`) no longer compiles. Contract state should be read through the context's methods.

### \[PXE] Browser KV-store default is now SQLite-OPFS; the IndexedDB entrypoint moved and will be deprecated

The browser PXE data store and the embedded wallet (`@aztec/wallets`) now persist to SQLite-OPFS instead of IndexedDB by default. The recommended way to obtain the browser backend is `@aztec/kv-store/sqlite-opfs`.

**Migration:**

```diff
- import { createStore } from '@aztec/kv-store/indexeddb';
+ import { createStore } from '@aztec/kv-store/sqlite-opfs';
```

If you must stay on IndexedDB for now, import from the deprecated entrypoint instead:

```diff
- import { createStore } from '@aztec/kv-store/indexeddb';
+ import { createStore } from '@aztec/kv-store/deprecated/indexeddb';
```

**Impact**: Existing IndexedDB-backed data is not migrated, so browser PXE and wallet state starts fresh on SQLite-OPFS (the v5 protocol upgrade wipes local state regardless). SQLite-OPFS also holds an exclusive, origin-wide lock on its store directory, so a second browser tab opening the same store will fail. Consequently, we recommend to explicitly manage this case in your app if it uses `EmbeddedWallet`.

### \[Aztec.js] `getPublicEvents` is now cursor-paginated

`getPublicEvents` returns a single page of events (at most `MAX_LOGS_PER_TAG`, the node's per-tag page size) and pages instead of the `maxLogsHit` flag, which didn't provide any way to fetch the next page of events:

* The result's `maxLogsHit` boolean is replaced by `nextCursor`. When `nextCursor` is present, more events might exist; pass it as the next query's `afterEvent` to fetch the following page. When it is absent, the range is exhausted.
* The filter's `afterLog` cursor is renamed to `afterEvent`.
* Both cursors are the new `EventCursor` type (exported from `@aztec/aztec.js/events`), not the node-layer `LogCursor`.

**Migration:**

```diff
- const { events, maxLogsHit } = await getPublicEvents(node, MyContract.events.MyEvent, { contractAddress });
+ // One page:
+ const { events, nextCursor } = await getPublicEvents(node, MyContract.events.MyEvent, { contractAddress });
+
+ // All events:
+ const all = [];
+ let afterEvent;
+ do {
+   const page = await getPublicEvents(node, MyContract.events.MyEvent, { contractAddress, afterEvent });
+   all.push(...page.events);
+   afterEvent = page.nextCursor;
+ } while (afterEvent);
```

**Impact**: Reading `maxLogsHit` or passing `afterLog` no longer compiles. Previously a single call was silently capped at `MAX_LOGS_PER_TAG` events with no usable way to continue, so the old API was unusable anyway. You can now page through the full set with `afterEvent`/`nextCursor`.

### \[PXE] Sender and shared-secret registration unified into `TaggingSecretSource`

The PXE methods for registering tagging-secret sources have been replaced by a single set that takes a `TaggingSecretSource` discriminated union. `registerSender`/`getSenders`/`removeSender` and `registerSharedSecret`/`removeSharedSecret` are gone; use `registerTaggingSecretSource`/`removeTaggingSecretSource`/`getTaggingSecretSources` instead. The `Wallet` interface (`wallet.registerSender`, `getAddressBook`) is unchanged, so this only affects code that talks to a `PXE` instance directly.

| Before                                        | After                                                                              |
| --------------------------------------------- | ---------------------------------------------------------------------------------- |
| `pxe.registerSender(address)`                 | `pxe.registerTaggingSecretSource({ kind: 'address-derived', sender: address })`    |
| `pxe.removeSender(address)`                   | `pxe.removeTaggingSecretSource({ kind: 'address-derived', sender: address })`      |
| `pxe.getSenders()`                            | `pxe.getTaggingSecretSources({ kind: 'address-derived' })`                         |
| `pxe.registerSharedSecret(recipient, secret)` | `pxe.registerTaggingSecretSource({ kind: 'arbitrary-secret', recipient, secret })` |
| `pxe.removeSharedSecret(recipient, secret)`   | `pxe.removeTaggingSecretSource({ kind: 'arbitrary-secret', recipient, secret })`   |

### \[Aztec.js] Unchecked `AztecAddress` constructors renamed with an `Unsafe` suffix

The synchronous `AztecAddress` constructors that build an address from a raw value do not verify that the value is a valid address (the x-coordinate of a point on the Grumpkin curve, which is what allows it to be encrypted to). An invalid value is accepted silently and only fails later, when a transaction is sent. To make this obvious at the call site, they now carry an `Unsafe` suffix:

| Before                    | After                           |
| ------------------------- | ------------------------------- |
| `AztecAddress.fromField`  | `AztecAddress.fromFieldUnsafe`  |
| `AztecAddress.fromBigInt` | `AztecAddress.fromBigIntUnsafe` |
| `AztecAddress.fromNumber` | `AztecAddress.fromNumberUnsafe` |
| `AztecAddress.fromString` | `AztecAddress.fromStringUnsafe` |

**Migration:**

```diff
- const address = AztecAddress.fromBigInt(123n);
+ const address = AztecAddress.fromBigIntUnsafe(123n);
```

For a random, genuinely valid address in tests use `AztecAddress.random()`, and to check an untrusted value use `address.isValid()`. The serialization constructors `fromBuffer` and `fromFields` keep their names (they are part of the (de)serialization interface and read addresses from already-validated data), but their docs now note that they perform no validation either.

### Cross-contract utility calls now have a `msg_sender`

A utility function called by another contract (utility to utility, or private to utility) can read the calling contract's address via `self.msg_sender()`, mirroring private and public functions. A top-level utility call (e.g. invoked directly by a wallet or dapp) has no caller: `self.msg_sender()` panics, and `self.context.maybe_msg_sender()` returns `Option::none()`.

`msg_sender` is only set for cross-contract calls, where it is taken from the call graph and so cannot be forged. A directly-invoked utility (from a wallet or dapp) has no verifiable caller, so it exposes none rather than trusting a value the caller could pick freely.

#### \[Aztec.nr] `ExecuteUtilityOptions` gains `with_from` to simulate a cross-contract caller in tests

In `TestEnvironment`, use `execute_utility_opts` with the new `ExecuteUtilityOptions::with_from` builder method to set the `msg_sender` a utility observes, simulating a cross-contract caller without routing through an actual nested call (by default it observes no caller):

```rust
let secret = env.execute_utility_opts(
    ExecuteUtilityOptions::new().with_from(caller),
    Registry::at(registry_address).get_app_siloed_secret(sender, recipient),
).map(|secrets| secrets.shared);
```

### \[Prover Node JSON-RPC] Prover API moved to the admin endpoint; `getL2Tips`/`getWorldStateSyncStatus` removed

The prover node's JSON-RPC methods (`prover_*`) have moved off the public node RPC server and onto the admin RPC server. They now require the admin API key and are served on the admin port (8880) instead of the public port (8080).

In addition, `prover_getL2Tips` and `prover_getWorldStateSyncStatus` have been removed from the prover API. They duplicated data already served by the node: use `aztec_getChainTips` (same shape as the old `getL2Tips`) and `aztec_getWorldStateSyncStatus` instead.

If you call the prover RPC directly (e.g. via `curl`), point at the admin endpoint with the API key and use the remaining methods:

* `prover_startProof` — schedule proving for an epoch
* `prover_getJobs` — list proving jobs

A client factory `createProverNodeAdminClient(url, versions?, fetch?, apiKey?)` is now exported from `@aztec/stdlib/interfaces/server`, and the CLI exposes `aztec prover start-proof --epoch <n> --admin-url <url> --api-key <key>` and `aztec prover get-jobs` (the API key defaults to `AZTEC_ADMIN_API_KEY`).

### \[Aztec.nr] `ContractInstance.contract_class_id` renamed to `original_contract_class_id`

The `contract_class_id` field of the `ContractInstance` struct (returned by `get_contract_instance`) has been renamed to `original_contract_class_id`. The struct is the contract's *address preimage*, so this field is the class id the contract was deployed with: for contracts whose class was later updated via the `ContractInstanceRegistry`, it is NOT the class currently executing. The rename makes that explicit.

**Migration:**

```diff
let instance = get_contract_instance(address);
- let class_id = instance.contract_class_id;
+ let class_id = instance.original_contract_class_id;
```

Note that this value is not available during public execution, which only has access to the *current* contract class.

### \[Aztec.nr] `get_contract_instance_class_id_avm` renamed to `get_contract_instance_current_class_id_avm`

The AVM contract-instance class id getter has been renamed to make explicit that it returns the *current* class id, i.e. it reflects updates performed via the `ContractInstanceRegistry`.

**Migration:**

```diff
- use aztec::oracle::get_contract_instance::get_contract_instance_class_id_avm;
+ use aztec::oracle::get_contract_instance::get_contract_instance_current_class_id_avm;

- let class_id = get_contract_instance_class_id_avm(address);
+ let class_id = get_contract_instance_current_class_id_avm(address);
```

### \[Aztec.nr] `for_each` visits elements in order; removing during iteration no longer supported

`CapsuleArray::for_each` and `EphemeralArray::for_each` previously iterated backwards (from the last element to the first) so that the callback could safely remove the current element. They now visit elements in order, from first to last, as is usually expected in other languages. Structurally mutating the array (e.g. via `push` or `remove`) from inside the callback is no longer supported.

For `EphemeralArray`, replace remove-during-iteration with `filter`:

```diff
- array.for_each(|index, value| {
-     if should_remove(value) {
-         array.remove(index);
-     }
- });
+ let kept = array.filter(|value| !should_remove(value));
```

`filter` collects the kept elements into a fresh array at a new slot. If the original slot matters (e.g. a `TransientArray` slot shared with other call frames), rebuild it from the filtered result:

```noir
let kept = array.filter(|value| !should_remove(value));
let _ = array.clear();
kept.for_each(|_index, value| array.push(value));
```

`EphemeralArray`'s are cheap and by nature not persistent though, so in most cases you probably can just work with the new copy instead of going through this hassle.

`CapsuleArray` has no `filter`, so iterate manually, backwards. Removing the current element is safe in a backward loop because it only shifts elements at higher indices:

```noir
let mut i = array.len();
while i > 0 {
    i -= 1;
    if should_remove(array.get(i)) {
        array.remove(i);
    }
}
```

### \[Protocol] `PrivateCircuitPublicInputs` and `PrivateContextInputs` gain a `tx_request_salt` field

The init kernel now always injects the protocol nullifier (`H(tx_request)`) as the transaction's first nullifier and binds the entry-point proof to the tx request's salt. To support this, `PrivateCircuitPublicInputs` and `PrivateContextInputs` each carry a new `tx_request_salt` field (set by the framework), and the `first_nullifier_hint` input to the init kernel is removed. This is handled automatically by the framework and PXE; contracts using the standard entrypoint need no changes beyond recompiling against the new protocol circuits.

The salt serves two purposes. It keeps `H(tx_request)` (the protocol nullifier) unpredictable, preventing a dictionary attack that guesses the tx-request preimage to recompute the nullifier. It also lets the init kernel bind the proof to a specific tx request: the kernel asserts `tx_request_salt` equals `tx_request.salt`, so a third party holding the proof cannot rebind it to a different request (and thus a different protocol nullifier).

Because `tx_request_salt` is now part of the private function's public inputs, an app or account contract can read it. Together with the other public inputs (the call context, `args_hash`, `tx_context`, and the function selector), an entrypoint can reconstruct the full `tx_request` and verify a signature over `tx_request.hash()`, instead of over a separate payload. This lets an account authorize the complete, kernel-checked transaction request rather than just the calls it contains. Building on that, the protocol nullifier can be made to provide a transaction's replay protection and cancellation directly, reducing the number of nullifiers an account needs to emit. This is not done by the standard entrypoint today; see issues #461 (the protocol/design direction) and #462 (the Aztec.nr account-layer work) for the full picture.

### \[Aztec.js] Prefunded local network test accounts are now initializerless

The genesis-funded test accounts in the local network (sandbox), returned by `getInitialTestAccountsData()`, are now initializerless Schnorr accounts (`schnorr_initializerless`). An initializerless account has no onchain deployment transaction: its address commits to the signing public key (through `immutables_hash`) and its contract state is materialized locally in the PXE, so these accounts are usable right away.

Because their address is derived differently from a regular Schnorr account, register them with `createSchnorrInitializerlessAccount` rather than `createSchnorrAccount`.

**Migration:**

```diff
const [alice] = await getInitialTestAccountsData();
- await wallet.createSchnorrAccount(alice.secret, alice.salt);
+ await wallet.createSchnorrInitializerlessAccount(alice.secret, alice.salt);
```

### \[Aztec.js] `AccountContract` interface adds `getImmutablesHash()`

The `AccountContract` interface now declares `getImmutablesHash(): Promise<Fr | undefined>`, which returns the hash of the account's immutable instantiation parameters committed into its address, or `undefined` if the feature is not used. `getAccountContractAddress()` and `AccountManager.create()` call it to derive the address when an `immutablesHash` is not passed explicitly, so the address of an initializerless account is now resolved from the contract itself.

Account contracts that extend `DefaultAccountContract` inherit a default implementation that returns `undefined` and need no changes.

### \[Aztec.js] Wallets validate declared gas limits against the network's per-tx admission limit

Wallets now reject a transaction whose declared `gasLimits` exceed the network's per-tx admission limit (the node-advertised `txsLimits.gas`), throwing before the tx is sent — e.g. `Declared DA gas limit (X) exceeds the maximum this network allows per tx (Y)`. When you declare no gas limits, the wallet fills in the network's admission limits for you. This mirrors the node's inbound `GasLimitsValidator`, surfacing the rejection locally instead of on submission.

**Impact**: Transactions that previously over-declared gas (and were silently skipped by the proposer) now fail fast with a descriptive error. Declare limits at or below `txsLimits.gas`, or declare none and let the wallet fill them in.

### \[Aztec.js] `estimateGas` / `estimatedGasPadding` simulate options and the `estimatedGas` result field removed

The `estimateGas` and `estimatedGasPadding` fee options are gone, and the `estimatedGas` field on simulation results is replaced by `gasUsed` (the raw gas the simulation consumed). Apps that want explicit gas limits read `gasUsed` and pad it themselves; otherwise the wallet fills in the network's admission limits automatically.

**Migration:**

```diff
- const { estimatedGas } = await contract.methods.foo(args).simulate({
-   from,
-   fee: { estimateGas: true, estimatedGasPadding: 0.1 },
- });
- const gasLimits = estimatedGas.gasLimits;
+ const { gasUsed } = await contract.methods.foo(args).simulate({ from, includeMetadata: true });
+ const gasLimits = gasUsed.totalGas.mul(1.1); // pad yourself
```

### \[Aztec.js] `getGasLimits` moved to `@aztec/wallet-sdk` and is no longer exported from `@aztec/aztec.js`

`getGasLimits` is no longer exported from `@aztec/aztec.js`. It now lives in `@aztec/wallet-sdk/base-wallet`, takes the simulated `gasUsed` and the network's per-tx admission limit, and clamps the padded estimates to it. If the simulated usage already exceeds the network limit, it throws immediately rather than returning a limit the node would reject.

**Migration:**

```diff
- import { getGasLimits } from '@aztec/aztec.js';
- const { gasLimits, teardownGasLimits } = getGasLimits(simulationResult, 0.1);
+ import { getGasLimits } from '@aztec/wallet-sdk/base-wallet';
+ const { txsLimits } = await node.getNodeInfo();
+ const { gasLimits, teardownGasLimits } = getGasLimits(simulationResult.gasUsed, Gas.from(txsLimits.gas), 0.1);
```

### \[Aztec.js / PXE] `NodeInfo.txsLimits` is now required

`NodeInfo` now carries a required `txsLimits` field: every node advertises the maximum gas a single tx may declare (`{ gas: { daGas, l2Gas } }`) and wallets rely on it for fallback gas limits. Clients built against this version cannot talk to nodes that predate the field.

### \[Aztec.js] `GasSettings.fallback` requires explicit `gasLimits`

`GasSettings.fallback` no longer supplies a default value for `gasLimits`. Callers must pass the network's per-tx admission limit explicitly — read it from the node's `txsLimits.gas`.

**Migration:**

```diff
- const settings = GasSettings.fallback({ maxFeesPerGas });
+ const { txsLimits } = await node.getNodeInfo();
+ const settings = GasSettings.fallback({ gasLimits: Gas.from(txsLimits.gas), maxFeesPerGas });
```

### \[Aztec.js / stdlib] Removed legacy fallback gas constants

The following exports have been removed from `@aztec/stdlib`:

* `APPROXIMATE_MAX_DA_GAS_PER_BLOCK`
* `FALLBACK_TEARDOWN_L2_GAS_LIMIT`
* `FALLBACK_TEARDOWN_DA_GAS_LIMIT`

**Impact**: Any code that imported these symbols must switch to the live node-advertised limits via the node's `txsLimits.gas`.

### \[Aztec.nr] `messages::message_delivery` module moved to `messages::delivery`

The `message_delivery` module has been renamed to `delivery`. Update imports accordingly:

```diff
- use aztec::messages::message_delivery::MessageDelivery;
+ use aztec::messages::delivery::MessageDelivery;
```

### \[Node JSON-RPC] Method prefixes changed to `aztec_*` and `aztecAdmin_*`

All Aztec node JSON-RPC method prefixes have changed:

* `node_*` → `aztec_*` (public node methods, port 8080)
* `nodeAdmin_*` → `aztecAdmin_*` (admin methods, port 8880)
* `nodeDebug_*` → `aztecDebug_*` (debug methods, port 8080, local-network or `--node-debug` only)
* `p2p_*` namespace removed; P2P queries are on `aztec_*`: `getPeers`, `getCheckpointAttestationsForSlot`, `getProposalsForSlot`
* New archiver sync helpers on `aztec_*`: `getL1Constants`, `getSyncedL2SlotNumber`, `getSyncedL2EpochNumber`, `getSyncedL1Timestamp`

If you call the node RPC directly (e.g. via `curl` or a custom client), update all method names accordingly. Clients created via `createAztecNodeClient`, `createAztecNodeAdminClient`, and `createAztecNodeDebugClient` are updated automatically.

### \[Node RPC] `registerContractFunctionSignatures` moved to the debug API

`registerContractFunctionSignatures` is no longer part of the main node JSON-RPC API (`aztec_` namespace). It is now a debug-only method exposed under the `aztecDebug_` namespace, which is only mounted when the node runs with debug endpoints enabled (`--node-debug`, always on in the in-process sandbox). This removes an unauthenticated write to node memory from prod-like nodes.

Clients that registered public function signatures over `aztec_registerContractFunctionSignatures` should call `aztecDebug_registerContractFunctionSignatures` against a debug-enabled node instead. In the PXE, this is now driven by an optional debug client: pass a `nodeDebug` client (e.g. `createAztecNodeDebugClient(nodeUrl)`) when creating the PXE to keep named public-execution traces; when it is absent, signature registration is skipped. Client-side error enrichment from the contract ABI is unaffected.

### \[Aztec.nr] `get_pending_tagged_logs` oracle interface updated (oracle version 28)

The `aztec_utl_getPendingTaggedLogs` oracle now takes an additional `provided_secrets` parameter of type `EphemeralArray<ProvidedSecret>`. This lets apps pass tagging secrets that PXE cannot derive on its own (e.g. handshake-derived secrets) alongside the secrets PXE manages internally.

### \[Aztec.nr] `set_sender_for_tags` oracle removed

The `set_sender_for_tags` oracle has been removed. Contracts that used it to override the sender for discovery tag derivation should now use the `with_sender` builder method on `MessageDelivery`:

```diff
- use aztec::oracle::notes::set_sender_for_tags;
+ use aztec::messages::delivery::MessageDelivery;

- unsafe { set_sender_for_tags(some_address) };
- note.deliver(MessageDelivery::onchain_constrained());
+ note.deliver(MessageDelivery::onchain_constrained().with_sender(some_address));
```

When `with_sender` is not called, `MessageDelivery` uses the wallet-supplied default sender.

### \[Aztec.nr] `MessageDelivery` API syntax change

`MessageDelivery` variants are now accessed via constructor functions instead of dot notation:

```diff
- MessageDelivery.OFFCHAIN
+ MessageDelivery::offchain()

- MessageDelivery.ONCHAIN_UNCONSTRAINED
+ MessageDelivery::onchain_unconstrained()

- MessageDelivery.ONCHAIN_CONSTRAINED
+ MessageDelivery::onchain_constrained()
```

### \[Aztec.js] `getTxReceipt` returns a lifecycle union and takes `GetTxReceiptOptions`

`AztecNode.getTxReceipt` now takes an optional `GetTxReceiptOptions` argument and returns a `PendingTxReceipt | DroppedTxReceipt | MinedTxReceipt` union instead of a single `TxReceipt` class. Mined-only fields (`transactionFee`, `blockHash`, `blockNumber`, `txIndexInBlock`, and the execution result) are only guaranteed once the transaction is mined, so narrow with `isMined()`, `isPending()`, or `isDropped()` before reading variant-specific fields. The full `TxEffect` is attached only when you request it via `{ includeTxEffect: true }`.

`AztecNode.getTxEffect` is deprecated. Replace direct calls with `getTxReceipt(txHash, { includeTxEffect: true })` and read the `.txEffect` field.

`TxReceipt.empty()` and `TxReceipt.schema` no longer exist, since `TxReceipt` is now a union type rather than a class. Use `DroppedTxReceipt.empty()` and `TxReceiptSchema` instead.

```diff
- const effect = await node.getTxEffect(txHash);
- if (effect) {
-   console.log(effect.data.nullifiers.length);
- }
+ const receipt = await node.getTxReceipt(txHash, { includeTxEffect: true });
+ if (receipt.isMined() && receipt.txEffect) {
+   console.log(receipt.txEffect.nullifiers.length);
+ }

- const empty = TxReceipt.empty();
- const schema = TxReceipt.schema;
+ const empty = DroppedTxReceipt.empty();
+ const schema = TxReceiptSchema;
```

**Impact**: This is a breaking change to the public node RPC with no wire back-compat. Callers that read mined fields off a bare receipt must narrow with the `is*` guards first, callers of `getTxEffect` should migrate to `getTxReceipt`, and references to `TxReceipt.empty()` / `TxReceipt.schema` must move to `DroppedTxReceipt.empty()` / `TxReceiptSchema`.

### \[Aztec.js] `ExtendedDirectionalAppTaggingSecret` renamed to `AppTaggingSecret`

`ExtendedDirectionalAppTaggingSecret` has been renamed to `AppTaggingSecret`.

**Migration:**

```diff
- import { ExtendedDirectionalAppTaggingSecret } from '@aztec/stdlib/logs';
+ import { AppTaggingSecret } from '@aztec/stdlib/logs';

- ExtendedDirectionalAppTaggingSecret.fromString(value)
+ AppTaggingSecret.fromString(value)
```

**Impact**: Code importing or referencing `ExtendedDirectionalAppTaggingSecret` should update to `AppTaggingSecret`.

### \[Protocol] Remaining protocol-contract addresses compacted to 1-3

After the `auth_registry`, `public_checks`, and `multi_call_entrypoint` demotions freed up address slots `1`, `4`, and `6`, the three remaining protocol contracts have been compacted into the lowest slots: `ContractClassRegistry` moves from `3` to `1`, `ContractInstanceRegistry` stays at `2`, and `FeeJuice` moves from `5` to `3`. Code that hardcoded the previous values must be updated. `MAX_PROTOCOL_CONTRACTS` is unchanged (still `11`); only the assigned addresses moved.

### \[Aztec.nr] `multi_call_entrypoint` demoted from protocol contract

`multi_call_entrypoint` is no longer a protocol contract; its address is derived from its artifact rather than hardcoded at `6`, and PXE no longer auto-registers it. It is now a standard contract that PXE *preloads*: both `createPXE` and `EmbeddedWallet` preload the standard MultiCallEntrypoint automatically (and `EmbeddedWallet` additionally preloads `AuthRegistry`). **If you use the standard PXE or `EmbeddedWallet`, no changes are needed** — multicall keeps working out of the box.

To preload a different set of standard contracts (for example to also preload `PublicChecks`, which is not preloaded by default), a wallet or app passes its own `preloadedContractsProvider` through the wallet's PXE options:

```ts
const wallet = await EmbeddedWallet.create(node, {
  pxe: {
    preloadedContractsProvider: {
      // EmbeddedWallet's built-in default preloads only MultiCallEntrypoint + AuthRegistry.
      // A custom provider REPLACES that default (it is not additive), so re-list the ones you
      // still want and add the extras — here, PublicChecks.
      getPreloadedContracts: async () => [
        await getStandardMultiCallEntrypoint(),
        await getStandardAuthRegistry(),
        await getStandardPublicChecks(),
      ],
    },
  },
});
```

The provider *replaces* the default list (it is not additive), so include every standard contract you want available.

### \[Aztec.nr] `public_checks` demoted from protocol contract

`public_checks` is no longer a protocol contract. Its address is now derived from its artifact rather than hardcoded at `6`. The aztec-nr constant has moved and been renamed:

```diff
- use protocol_types::constants::PUBLIC_CHECKS_ADDRESS;
+ use crate::standard_addresses::STANDARD_PUBLIC_CHECKS_ADDRESS;
```

Unlike MultiCallEntrypoint and AuthRegistry, `PublicChecks` is **not** preloaded by `createPXE` or `EmbeddedWallet`. If your contract uses `privately_check_timestamp` or `privately_check_block_number`, `PublicChecks` must be deployed and made available to your PXE — either include it in a custom `preloadedContractsProvider` (see the `multi_call_entrypoint` note above) or register it directly:

```ts
import { getStandardPublicChecks } from "@aztec/standard-contracts/public-checks";

const { instance, artifact } = await getStandardPublicChecks();
await pxe.registerContract({ instance, artifact });
```

For browser bundles, import from `@aztec/standard-contracts/public-checks/lazy` instead.

Deploy `PublicChecks` once per fresh rollup: `aztec-wallet deploy public_checks_contract@PublicChecks --salt 1 --universal -f <fee-paying-account>`.

### \[Aztec.nr] `auth_registry` demoted from protocol contract

`auth_registry` is no longer a protocol contract. Its address is now derived from its artifact rather than hardcoded at `1`. The aztec-nr constant has moved and been renamed:

```diff
- use protocol_types::constants::CANONICAL_AUTH_REGISTRY_ADDRESS;
+ use crate::standard_addresses::STANDARD_AUTH_REGISTRY_ADDRESS;
```

PXE no longer auto-registers `AuthRegistry` on startup. `EmbeddedWallet` preloads it automatically (alongside the MultiCallEntrypoint — see the `multi_call_entrypoint` note above), so apps using the standard wallet need no changes. If you use a PXE setup that doesn't preload it, add it to a custom `preloadedContractsProvider` or register it explicitly:

```ts
import { getStandardAuthRegistry } from "@aztec/standard-contracts/auth-registry";

const { instance, artifact } = await getStandardAuthRegistry();
await pxe.registerContract({ instance, artifact });
```

For browser bundles, import from `@aztec/standard-contracts/auth-registry/lazy` instead.

Deploy `AuthRegistry` once per fresh rollup: `aztec-wallet deploy auth_registry_contract@AuthRegistry --salt 1 --universal -f <fee-paying-account>`.

### \[Aztec.nr] `public_checks` helpers moved to `aztec-nr`

The `privately_check_timestamp`, `privately_check_block_number`, and related caller helpers previously in `noir-contracts/contracts/protocol/public_checks_contract/src/utils.nr` are now in `aztec-nr/aztec/src/public_checks.nr`. Consumer contracts should update their imports:

```diff
- use public_checks::utils::privately_check_timestamp;
+ use aztec::public_checks::privately_check_timestamp;
```

### \[Aztec Node / Aztec.js / CLI] Log retrieval API consolidated to two tag-based methods

The four log-retrieval methods on `AztecNode` have been collapsed into two. `getContractClassLogs` and the `LogFilter`-shaped `getPublicLogs` are removed entirely; the surviving methods are `getPrivateLogsByTags(query)` and `getPublicLogsByTags(query)`, both taking a single query object and returning `LogResult[][]` (one inner array per requested tag, in input order).

**Removed methods on `AztecNode`:**

| Removed                                                                   | Replacement                                           |
| ------------------------------------------------------------------------- | ----------------------------------------------------- |
| `getPublicLogs(filter: LogFilter)`                                        | `getPublicLogsByTags({ contractAddress, tags, ... })` |
| `getContractClassLogs(filter: LogFilter)`                                 | none — RPC removed; no production consumer existed    |
| `getPrivateLogsByTags(tags, page?, referenceBlock?)`                      | `getPrivateLogsByTags({ tags, ... })`                 |
| `getPublicLogsByTagsFromContract(contract, tags, page?, referenceBlock?)` | `getPublicLogsByTags({ contractAddress, tags, ... })` |

**New query and response shapes:**

```ts
// Query
type TagQuery<T> = T | { tag: T; afterLog?: LogCursor };

type LogsQueryBase = {
  fromBlock?: BlockNumber; // inclusive
  toBlock?: BlockNumber; // exclusive
  txHash?: TxHash; // mutually exclusive with fromBlock/toBlock
  referenceBlock?: BlockHash; // reorg-safety anchor; throws if missing
  includeEffects?: boolean; // attach noteHashes + all nullifiers
};

type PrivateLogsQuery = LogsQueryBase & { tags: TagQuery<SiloedTag>[] };
type PublicLogsQuery = LogsQueryBase & {
  contractAddress: AztecAddress;
  tags: TagQuery<Tag>[];
};

// Response (per log)
type LogResult = {
  logData: Fr[];
  blockNumber: BlockNumber;
  blockHash: BlockHash;
  blockTimestamp: UInt64;
  txHash: TxHash;
  logIndexWithinTx: number;
  noteHashes?: Fr[]; // present only when includeEffects is set
  nullifiers?: Fr[]; // all nullifiers of the tx, not just the first
};
```

**Public queries now require a contract address.** Tag-only / contract-less public queries are no longer supported (the public log index is keyed on `(contract, tag)`).

**Per-tag `afterLog` cursors replace the global `page` argument.** Each tag advances independently — pass `{ tag, afterLog: LogCursor.fromLog(lastLog) }` to resume that tag, and omit it for tags that are already exhausted.

**Aztec.js wallet — `PublicEventFilter.contractAddress` is now required, and `afterLog` is a `LogCursor`:**

```diff
- type PublicEventFilter = EventFilterBase & { contractAddress?: AztecAddress };
+ type PublicEventFilter = EventFilterBase & { contractAddress: AztecAddress };

  type EventFilterBase = {
    txHash?: TxHash;
    fromBlock?: BlockNumber;
    toBlock?: BlockNumber;
-   afterLog?: LogId;
+   afterLog?: LogCursor;
  };
```

`LogId`, `LogFilter`, `TxScopedL2Log`, `ExtendedPublicLog`, `ExtendedContractClassLog`, `GetPublicLogsResponse`, and `GetContractClassLogsResponse` are no longer exported from `@aztec/aztec.js`. Build cursors with `LogCursor.fromLog(log)` and decode public-event payloads from `result.logData.slice(1)` (the tag is field 0).

**CLI — `aztec get-logs` now requires `--contract-address` and `--tag`:**

```diff
- aztec get-logs [--tx-hash <tx>] [--from-block <n>] [--to-block <n>] [--after-log <id>]
+ aztec get-logs --contract-address <address> --tag <tag> \
+               [--tx-hash <tx>] [--from-block <n>] [--to-block <n>] [--after-log <cursor>]
```

`--after-log` now takes a `LogCursor` of the form `<blockNumber>-<txIndexWithinBlock>-<logIndexWithinTx>` (formerly a `LogId`).

**Mutual exclusion**: setting both `txHash` and `fromBlock`/`toBlock` is rejected (a `txHash` already pins a block). `txHash` + `afterLog` is allowed and paginates within the tx's logs for a tag.

**Impact**: Any consumer of `getPublicLogs(LogFilter)`, `getContractClassLogs`, the old tag-based methods, `PublicEventFilter` without a `contractAddress`, or `EventFilterBase.afterLog: LogId` must be updated. The CLI rejects calls missing `--contract-address` or `--tag`.

### \[Aztec.js] `AccountManager.create` takes an options bag

`AccountManager.create` no longer takes `salt` as a positional argument. The trailing `salt?: Salt` parameter has been folded into a new `AccountManagerCreateOptions` bag alongside `immutablesHash` and `deployer`:

```diff
- AccountManager.create(wallet, secret, accountContract, salt)
+ AccountManager.create(wallet, secret, accountContract, { salt })
```

`immutablesHash` lets callers commit a non-zero immutables hash on the resulting `ContractInstance` (folded into the salted initialization hash, so it affects the derived address). `deployer` overrides the deployer address recorded on the instance (defaults to `AztecAddress.ZERO`). The same `immutablesHash` field is now also threaded through `DeployMethod` / `DeployAccountMethod` so the address derived at deploy time matches the one on `accountManager.getInstance()`.

### \[Aztec.nr] Defining a custom `sync_state` function now requires `AztecConfig`

Contracts that previously overrode the default `sync_state` by defining their own function with that name will now get a compile error. Use `AztecConfig::custom_sync_state()` instead.

The custom hook receives the same parameters as `do_sync_state` and is responsible for calling it if default behavior is also desired. You can perform work before and/or after the default `do_sync_state` call, or skip it entirely.

```diff
+ unconstrained fn my_custom_sync(
+     contract_address: AztecAddress,
+     compute_note_hash: ComputeNoteHash,
+     compute_note_nullifier: ComputeNoteNullifier,
+     process_custom_message: Option<CustomMessageHandler>,
+     offchain_inbox_sync: Option<OffchainInboxSync>,
+     scope: AztecAddress,
+ ) {
+     // optional: work before default sync
+     do_sync_state(contract_address, compute_note_hash, compute_note_nullifier, process_custom_message, offchain_inbox_sync, scope);
+     // optional: work after default sync
+ }

- #[aztec]
+ #[aztec(::aztec::macros::AztecConfig::new().custom_sync_state(crate::my_custom_sync))]
  contract MyContract {
-     use aztec::macros::functions::external;
-
-     #[external("utility")]
-     unconstrained fn sync_state(scope: AztecAddress) {
-         // custom sync logic
-     }
  }
```

**Impact**: Only contracts that manually defined a `sync_state` function are affected. Contracts using the default macro-generated `sync_state` require no changes.

### \[Aztec.nr] `push_nullifier` renamed to `push_nullifier_unsafe`

`PrivateContext::push_nullifier` and `PublicContext::push_nullifier` have been renamed to `push_nullifier_unsafe` to make it clear that they are low-level functions that require careful domain separation. This is consistent with the `_unsafe` suffix already used by `emit_private_log_unsafe`, `emit_raw_note_log_unsafe`, and `emit_public_log_unsafe`.

```diff
- context.push_nullifier(nullifier);
+ context.push_nullifier_unsafe(nullifier);
```

Prefer higher-level abstractions like `SingleUseClaim` or `destroy_note` which handle domain separation automatically.

### \[Aztec.nr] `LogRetrievalRequest` now includes `source`, `from_block`, and `to_block` fields

`LogRetrievalRequest` has been extended with three new fields to support filtering logs by source and block range. The `get_logs_by_tag` oracle now also returns all matching logs per tag instead of only the first match.

A `LogRetrievalRequest::new(contract_address, tag)` constructor is provided that defaults to querying both public and private logs with no block range filter:

```rust
LogRetrievalRequest::new(contract_address, my_tag)
```

If you need to customize source or block range, construct the struct manually with the new fields:

```diff
  LogRetrievalRequest {
      tag: my_tag,
+     source: LogSource.PUBLIC_AND_PRIVATE,
+     from_block: Option::none(),
+     to_block: Option::none(),
  }
```

`source` controls which RPCs are queried: `LogSource.PRIVATE`, `LogSource.PUBLIC`, or `LogSource.PUBLIC_AND_PRIVATE`. `from_block` and `to_block` define a half-open `[from, to)` block range filter. Both are `Option<Field>` and default to `Option::none()` (no filtering).

### \[Protocol] Public-key hashes replace points in `PublicKeys`

Ships together with immutables hash changes (shown below).

Per [AZIP-8](https://github.com/AztecProtocol/governance/blob/main/AZIPs/azip-8.md), `PublicKeys` no longer carries the four master public keys as elliptic curve points. Three of them (`npk_m`, `ovpk_m`, `tpk_m`) are now exposed only as their poseidon2 hash digests; only `ivpk_m` (the master incoming viewing key) remains a point because address derivation needs it as a curve point.

**This is a hard fork:** every contract address and account address derived from a non-default `PublicKeys` changes.

**Contract author migration.** Read the master nullifier hash directly off `PublicKeys` instead of computing it from a point:

```diff
- let owner_npk_m = get_public_keys(owner).npk_m;
- let secret = context.request_nhk_app(owner_npk_m.hash());
+ let owner_npk_m_hash = get_public_keys(owner).npk_m_hash;
+ let secret = context.request_nhk_app(owner_npk_m_hash);
```

The same field-rename applies to `.ovpk_m` and `.tpk_m`: these are now `.ovpk_m_hash` and `.tpk_m_hash` respectively. Code that needed those keys as points will not compile; the points are no longer accessible to contract code.

**Custom account contracts.** Wallets that ship their own Noir account contracts must recompile. Macro-generated calldata extraction and the `request_nsk_app` / `request_ovsk_app` paths use the hash form natively.

**TS / wallet author migration.** The `PublicKeys` constructor signature changes from four `Point`s to `(npkMHash: Fr, ivpkM: Point, ovpkMHash: Fr, tpkMHash: Fr)`. `KeyValidationRequest` carries `pkMHash: Fr` instead of `pkM: Point`. `KeyStore.getMasterSecretKey` now takes a `pkMHash: Fr` rather than a `Point`. Callers using the auto-generated TS binding pick this up automatically; callers that hand-roll the arg buffer must update.

**Wallet UI.** Any panel that displayed `masterNullifierPublicKey`, `masterOutgoingViewingPublicKey`, or `masterTaggingPublicKey` as Grumpkin points will no longer compile against the new `PublicKeys` class. The points themselves are no longer in `ContractInstancePublished` and cannot be recovered from the onchain record. Switch to displaying the hashes (`npkMHash`, `ovpkMHash`, `tpkMHash`) or drop the display.

**PXE storage migration.** `DatabaseVersionManager` deletes pre-v6 databases on first open: users will see registered accounts, contacts, address aliases, and synced notes wiped. Wallets should surface a "your local state was reset, please re-register accounts and re-sync" path. There is no forward migration because the address derived from a given secret changes (the new `public_keys_hash` is over four single-key digests, not four raw points). Previous addresses are not recoverable from the same secret; assets and notes attached to them are inaccessible at the protocol level.

**Indexer / event-decoder migration.** The `ContractInstancePublished` private log payload is now 13 fields:

```text
[ MAGIC, address, version, salt, class_id, init_hash,
  immutables_hash,
  npk_m_hash,
  ivpk_m.x, ivpk_m.y,
  ovpk_m_hash,
  tpk_m_hash,
  deployer ]
```

`version` is `2`. v1 events should be rejected.

**Security note (PXE side).** The kernel circuit no longer checks that `npk_m`, `ovpk_m`, `tpk_m` are on-curve or non-infinity (those points are no longer in the witness). The PXE / key store relies on `deriveKeys`'s by-construction guarantee that derived points are on-curve and non-infinity. Account-creation flows that bypass `deriveKeys` (e.g. importing pre-derived public keys from an external source) must validate this themselves, or risk producing unspendable notes.

### \[Contracts] `ContractInstance` gains `immutablesHash`, address derivation changes

`ContractInstance` now has a new `immutablesHash: Fr` field that commits to a contract's immutable storage values. The field is folded into the salted initialization hash, so contract addresses are impacted:

```text
salted_initialization_hash = poseidon2(DOM_SEP__SALTED_INITIALIZATION_HASH, [salt, initialization_hash, deployer, immutables_hash])
```

**You may need to act if:**

* You hardcode contract addresses computed from instance fields outside the SDK. Recompute them under the new derivation.

* You parse the `ContractInstancePublished` private log directly. The event payload has an extra field, with `immutables_hash` inserted between `initialization_hash` and the public-keys block:

  ```text
  [tag, address, version, salt, classId, initialization_hash, immutables_hash, ...publicKeys(5), deployer]
  ```

* You call `ContractInstanceRegistry.publish_for_public_execution` directly. The function now takes 6 arguments instead of 5, with `immutables_hash` inserted between `initialization_hash` and `public_keys`:

  ```diff
  - publish_for_public_execution(salt, contract_class_id, initialization_hash,                  public_keys, universal_deploy)
  + publish_for_public_execution(salt, contract_class_id, initialization_hash, immutables_hash, public_keys, universal_deploy)
  ```

* You call the `GetContractInstance` AVM opcode directly or use the per-member helpers in `aztec-nr`. A new enum value `ContractInstanceMember::IMMUTABLES_HASH = 3` selects `immutables_hash`. Use the wrapper helper from `aztec::oracle::get_contract_instance`:

  ```rust
  use aztec::oracle::get_contract_instance::get_contract_instance_immutables_hash_avm;
  let immutables_hash: Option<Field> = get_contract_instance_immutables_hash_avm(address);
  ```

The `aztec.js` `publishInstance` helper handles this automatically.

### \[Aztec.nr] `emit_private_log_unsafe` / `emit_raw_note_log_unsafe` now take `BoundedVec`

The old array-based `emit_private_log_unsafe(tag, log: [Field; N], length)` and `emit_raw_note_log_unsafe(tag, log: [Field; N], length, note_hash_counter)` have been removed. The temporary `_vec_unsafe` variants introduced in a prior release have been renamed to take their place.

```diff
- context.emit_private_log_unsafe(tag, log_array, length);
+ context.emit_private_log_unsafe(tag, bounded_vec_log);

- context.emit_raw_note_log_unsafe(tag, log_array, length, note_hash_counter);
+ context.emit_raw_note_log_unsafe(tag, bounded_vec_log, note_hash_counter);
```

If you were already using `emit_private_log_vec_unsafe` / `emit_raw_note_log_vec_unsafe`, simply drop the `_vec` from the function name:

```diff
- context.emit_private_log_vec_unsafe(tag, log);
+ context.emit_private_log_unsafe(tag, log);

- context.emit_raw_note_log_vec_unsafe(tag, log, note_hash_counter);
+ context.emit_raw_note_log_unsafe(tag, log, note_hash_counter);
```

### \[bb.js / accounts / aztec.nr] Schnorr signatures switched to Poseidon2

The Schnorr challenge hash function changed from `blake2s(pedersen(R.x, pubkey.x, pubkey.y) ‖ message)` to `Poseidon2(DST, R.x, pubkey.x, pubkey.y, message)`, where `DST = poseidon2_hash_bytes("schnorr_grumpkin_poseidon2")` is a domain separation tag binding signatures to this scheme. The change applies end-to-end across the native signer (`bb`), `@aztec/bb.js`, the noir verifier library (`noir-lang/schnorr` v0.2.0 → v0.4.0), and both standard Schnorr account contracts. The auth witness on-wire shape also changes from `[u8; 64]` (the serialized `(s, e)` bytes) to `[Field; 4]` (`[s.lo, s.hi, e.lo, e.hi]`, each scalar split into two 128-bit limbs).

**Impact:** A previously-deployed Schnorr account cannot be controlled by the new TypeScript code. Both the signature scheme and the auth witness format change, so signatures produced by the new code will fail in-circuit verification against the old account contract, and old-style 64-byte auth witnesses will not decode in the new contract. Users with existing Schnorr accounts on testnet must deploy a fresh account contract and migrate funds. ECDSA accounts (`ecdsa_k`, `ecdsa_r`) are unaffected.

**If you maintain a custom Schnorr account contract**, bump the `schnorr` dependency in `Nargo.toml`:

```diff
- schnorr = { tag = "v0.2.0", git = "https://github.com/noir-lang/schnorr" }
+ schnorr = { tag = "v0.4.0", git = "https://github.com/noir-lang/schnorr" }
```

and update `is_valid_impl` to consume the auth witness as four `Field` limbs and pass `outer_hash` directly:

```diff
- let signature: [u8; 64] = unsafe { get_auth_witness_as_bytes(outer_hash) };
- schnorr::verify_signature(pub_key, signature, outer_hash.to_be_bytes::<32>())
+ let limbs: [Field; 4] = unsafe { get_auth_witness(outer_hash) };
+ let signature = (
+     std::embedded_curve_ops::EmbeddedCurveScalar::new(limbs[0], limbs[1]),
+     std::embedded_curve_ops::EmbeddedCurveScalar::new(limbs[2], limbs[3]),
+ );
+ schnorr::verify_signature(pub_key, signature, outer_hash)
```

The `Schnorr` TypeScript API in `@aztec/foundation/crypto/schnorr` keeps the same surface (`constructSignature(msg: Uint8Array, ...)`, `verifySignature(msg, ...)`), but the `msg` parameter is now required to be exactly 32 bytes — a serialized field element (e.g. `Fr.toBuffer()` or `messageHash.toBuffer()`). Passing arbitrary-length byte strings will fail at the bb.js boundary.

### \[Aztec.nr] `attempt_note_discovery` is no longer exposed; use `process_private_note_msg`

`attempt_note_discovery` is now crate-private. Custom message handlers (implementations of `CustomMessageHandler`) that previously called it directly should call `process_private_note_msg` instead, which runs the standard private note message decoding and discovery pipeline.

`process_private_note_msg` takes the raw `msg_metadata` and `msg_content` rather than already-decoded note fields, so it handles decoding (and silently discards undecodable messages) on your behalf:

```diff
- attempt_note_discovery(
-     contract_address,
-     tx_hash,
-     unique_note_hashes_in_tx,
-     first_nullifier_in_tx,
-     compute_note_hash,
-     compute_note_nullifier,
-     owner,
-     storage_slot,
-     randomness,
-     note_type_id,
-     packed_note,
- );
+ process_private_note_msg(
+     contract_address,
+     tx_hash,
+     unique_note_hashes_in_tx,
+     first_nullifier_in_tx,
+     compute_note_hash,
+     compute_note_nullifier,
+     msg_metadata,
+     msg_content,
+ );
```

**Impact**: Custom message handlers that reused the standard note message processing pipeline must switch to `process_private_note_msg`. Contracts using only built-in private note handling are unaffected.

### \[Aztec.nr] TXE `call_public_incognito` no longer takes a `from` parameter

`TestEnvironment::call_public_incognito` previously accepted a `from` address that was silently ignored (the function always uses a null `msg_sender`). The `from` parameter has been removed.

```diff
- env.call_public_incognito(sender, SampleContract::at(addr).some_function());
+ env.call_public_incognito(SampleContract::at(addr).some_function());
```

If you need to call a public function *with* a sender, use `call_public` instead.

### \[Aztec.nr] TXE `view_public_incognito` is deprecated

`TestEnvironment::view_public_incognito` is now deprecated in favor of `view_public`, which has the same behavior (null `msg_sender`, static call).

```diff
- env.view_public_incognito(SampleContract::at(addr).some_view());
+ env.view_public(SampleContract::at(addr).some_view());
```

### \[Aztec.js] `DeployMethod` address-affecting parameters move to construction time

Salt, deployer, and public keys are now passed when the `DeployMethod` is constructed, not on every call to `send` / `simulate` / `request` / `getInstance`. This locks the contract address once it is determined and prevents the silent salt-cache poisoning bug where the address could change between calls.

`contractAddressSalt`, `deployer`, and `universalDeploy` have been removed from `DeployOptions`, `RequestDeployOptions`, and `SimulateDeployOptions`. They now live on a new `DeployInstantiationOptions` argument passed at construction. `deployer` and `universalDeploy` are mutually exclusive; passing both throws. `Contract.deployWithPublicKeys` and the generated `MyContract.deployWithPublicKeys(...)` factories have been removed; pass `publicKeys` via the `instantiation` argument of `deploy(...)` instead. The buggy synchronous `address` and `partialAddress` getters have been removed and replaced with `getAddress()` and `getPartialAddress()` (both `async`).

The compact form keeps working: `MyContract.deploy(wallet, ...args).send({ from: alice })` deploys with `deployer = alice` and `salt = random()`, exactly as before. The deployer is locked the first time `send` / `simulate` / `profile` is called (from `options.from`, with `NO_FROM` or undefined → universal) and cannot change after that:

* Subsequent `send` / `simulate` / `profile` calls with a `from` that would imply a different deployer throw, instead of silently producing a different address.
* A lock to universal (`AztecAddress.ZERO`) is the only one compatible with any sender, since the universal address does not depend on `from`.
* A lock to a concrete address only accepts that exact `from` on subsequent calls.

**Migration:**

Universal deployment with a fixed salt:

```diff
- const deploy = MyContract.deploy(wallet, ...args);
- await deploy.send({
-   from: alice,
-   contractAddressSalt: salt,
-   universalDeploy: true,
- });
+ const deploy = MyContract.deploy(wallet, ...args, { salt, universalDeploy: true });
+ await deploy.send({ from: alice });
```

Non-universal deploy where `from` doubles as the deployer:

```diff
- const deploy = MyContract.deploy(wallet, ...args);
- await deploy.send({ from: alice, contractAddressSalt: salt });
+ const deploy = MyContract.deploy(wallet, ...args, { salt });
+ await deploy.send({ from: alice });
```

If you need to read the address before sending, lock the deployer at construction:

```typescript
const deploy = MyContract.deploy(wallet, ...args, { salt, deployer: alice });
const address = await deploy.getAddress(); // resolves; deployer was locked at construction
await deploy.send({ from: alice }); // deploys at the address `getAddress` returned
```

Universal deploys can be sent by any account, since the universal address does not depend on `from`:

```typescript
const deploy = MyContract.deploy(wallet, ...args, { universalDeploy: true });
await deploy.send({ from: bob }); // OK, universal accepts any sender
```

A lock to a concrete deployer rejects sending from a different account, instead of silently deploying at a different address:

```typescript
const deploy = MyContract.deploy(wallet, ...args, { deployer: alice });
await deploy.send({ from: bob }); // throws: deployer is locked to alice
```

`deployWithPublicKeys` is gone; pass `publicKeys` in the instantiation options instead:

```diff
- const deploy = MyContract.deployWithPublicKeys(publicKeys, wallet, ...args);
+ const deploy = MyContract.deploy(wallet, ...args, { publicKeys });
```

`ContractDeployer.deploy(...)` now takes the instantiation argument as its first parameter (pass `{}` to use defaults and rely on lazy locking from `from`):

```diff
- const cd = new ContractDeployer(artifact, wallet);
- await cd.deploy(...ctorArgs).send({ from: alice, contractAddressSalt: salt });
+ const cd = new ContractDeployer(artifact, wallet);
+ await cd.deploy(ctorArgs, { salt }).send({ from: alice });
```

The synchronous `address` / `partialAddress` getters are gone:

```diff
- const address = deploy.address;                       // sync, possibly undefined
- const partial = await deploy.partialAddress;          // sync getter wrapping async value
+ const address = await deploy.getAddress();            // requires the deployer to be locked
+ const partial = await deploy.getPartialAddress();     // requires the deployer to be locked
```

`getInstance()` no longer takes options; use the construction-time instantiation instead:

```diff
- const instance = await deploy.getInstance({ contractAddressSalt: salt });
+ const deploy = MyContract.deploy(wallet, ...args, { salt, deployer: alice });
+ const instance = await deploy.getInstance();
```

### \[aztec-up] Bundled binaries are no longer exposed under bare names on `PATH`

The Aztec installer previously placed bundled binaries directly into `$HOME/.aztec/current/bin` under bare names (`forge`, `nargo`, `bb`, `pxe`, ...). Anything with the same name in your own `PATH` was silently shadowed in unrelated projects.

Every bundled binary is now exposed only under an `aztec-` prefixed name in `$HOME/.aztec/current/bin`. Bare names are not on `PATH` at all and resolve to your own install (if any).

| Was on `PATH`      | Now                      |
| ------------------ | ------------------------ |
| `forge`            | `aztec-forge`            |
| `cast`             | `aztec-cast`             |
| `anvil`            | `aztec-anvil`            |
| `chisel`           | `aztec-chisel`           |
| `nargo`            | `aztec-nargo`            |
| `noir-profiler`    | `aztec-noir-profiler`    |
| `bb`               | `aztec-bb`               |
| `bb-cli`           | `aztec-bb-cli`           |
| `pxe`              | `aztec-pxe`              |
| `txe`              | `aztec-txe`              |
| `validator-client` | `aztec-validator-client` |
| `blob-client`      | `aztec-blob-client`      |

`aztec`, `aztec-wallet`, and `aztec-up` keep their existing names.

If you relied on a bundled bare-name binary for general use:

* For Aztec contract work, prefer `aztec compile` and `aztec test`.
* For other Noir / Foundry commands, invoke the `aztec-*` symlink directly (e.g. `aztec-nargo fmt`, `aztec-forge build`).
* Or install Foundry / nargo separately via `foundryup` / `noirup`.

If you set `Noir: Nargo Path` in the VS Code Noir extension to `$HOME/.aztec/current/bin/nargo`, change it to `$HOME/.aztec/current/bin/aztec-nargo` (the symlink is a drop-in for `nargo`). See the [Noir VSCode Extension guide](/developers/docs/aztec-nr/installation.md) for details.

### \[Stdlib] `SimulationOverrides.contracts` entries no longer carry an artifact

`ContractOverrides` entries are now `{ instance }` only. To override a contract's artifact, pre-register the target class via `pxe.registerContractClass(artifact)` and set the override instance's `currentContractClassId` to that class id:

```diff
- const instance = await getContractInstanceFromInstantiationParams(stubArtifact, { salt: Fr.random() });
+ const instance = await pxe.getContractInstance(addr);
+ await pxe.registerContractClass(stubArtifact);
+ const stubClassId = (await getContractClassFromArtifact(stubArtifact)).id;
- overrides = { contracts: { [addr.toString()]: { instance, artifact: stubArtifact } } };
+ overrides = { contracts: { [addr.toString()]: { instance: { ...instance, currentContractClassId: stubClassId } } } };
```

### \[Aztec.js] `simulate` accepts `overrides` for testing "what if storage value was X?"

`Contract.methods.foo(...).simulate(...)` now accepts an `overrides` option that injects values into the simulator's (ephemeral) world-state fork and contract DB before the call runs. The supported field is `publicStorage`, which writes a `(contract, slot, value)` into the public-data tree as if a previous tx had set it. Overrides are thrown away after simulation completes.

```typescript
const result = await contract.methods.read_balance(account).simulate({
  overrides: {
    publicStorage: [
      {
        contract: contract.address,
        slot: BALANCE_SLOT,
        value: new Fr(1_000_000n),
      },
    ],
  },
});
```

The same option flows through `wallet.simulateTx` and eventually to `simulatePublicCalls` RPC on `AztecNode`.

Direct callers of the `SimulationOverrides` constructor must switch from a positional `contracts` argument to an options bag:

```diff
- new SimulationOverrides(contracts);
+ new SimulationOverrides({ contracts });
```

`overrides.contracts` swaps contract instances in the simulator's contract DB — useful for simulating a contract being on a different class than the one it was deployed with. To simulate a complete onchain upgrade flow, use the `fastForwardContractUpdate` helper which returns a `SimulationOverrides` covering both registry storage rewrites and the upgraded instance entry:

```typescript
import { fastForwardContractUpdate } from "@aztec/aztec.js";

const overrides = await fastForwardContractUpdate({
  instanceAddress: contract.address,
  newClassId: upgradedClass.id,
  node,
});
const result = await contract.methods.upgraded_method().simulate({ overrides });
```

### \[PXE] `proveTx` takes an options bag

`PXE.proveTx` used to accept `scopes` as a positional argument; it now takes an options bag consistent with `simulateTx` and `profileTx`, and adds an optional `senderForTags` field. Update direct callers:

```diff
- pxe.proveTx(txRequest, scopes);
+ pxe.proveTx(txRequest, { scopes });
```

The new `senderForTags` field sets the address recipients use to find private messages (notes, events, logs) emitted by this tx. Most wallets don't need to set it; the wallet SDK derives it from the tx's `from` address:

```typescript
// Most callers: just migrate scopes
pxe.proveTx(txRequest, { scopes });

// When from === NO_FROM (e.g. self-paid account deploy), supply the tag sender explicitly:
pxe.proveTx(txRequest, { scopes, senderForTags: deployedAddress });
```

### \[Aztec.nr] `set_sender_for_tags` is now scoped to the calling contract

`set_sender_for_tags` previously persisted through nested calls. It is now scoped to the contract that calls it: nested calls, siblings, and parents are unaffected and always start from the initial default. This closes a silent note-discovery DoS vector where any nested callee could overwrite the tag sender for legitimate contracts called below it.

The wallet SDK now supplies the default sender-for-tags from the transaction's `from` address, with an optional `sendMessagesAs` override for flows that don't have a signing account (e.g. self-paid deploys, which `DeployAccountMethod` sets automatically). Account contracts therefore no longer need to call `set_sender_for_tags(self.address)` in their entrypoints; those calls have been removed from the standard schnorr and ECDSA account contracts, and you can drop the equivalent call in any custom account contract.

The save/restore idiom previously used in account-contract constructors (`get` → `set(self.address)` → work → `set(prev)`) is also no longer needed and has been removed: the override never leaks out of the constructor, so there is nothing to restore.

### \[Aztec Node] Unified `getBlock` / `getCheckpoint` RPC API

The Aztec Node JSON-RPC surface for fetching blocks and checkpoints has been consolidated. The unified `getBlock` and `getCheckpoint` methods return uniform `BlockResponse` / `CheckpointResponse` shapes. The extra fields a caller cares about (tx bodies, L1 publish info, committee attestations, nested blocks) are now controlled by an `options` argument rather than by picking the right method. `getBlocks` and `getCheckpoints` retain their names but now return the new response shapes.

**Removed methods:**

| Removed                            | Replacement                                  |
| ---------------------------------- | -------------------------------------------- |
| `getBlockByHash(hash)`             | `getBlock(hash)` or `getBlock({ hash })`     |
| `getBlockByArchive(archive)`       | `getBlock({ archive })`                      |
| `getBlockHeaderByArchive(archive)` | `getBlock({ archive }).then(r => r?.header)` |
| `getProvenBlockNumber()`           | `getBlockNumber('proven')`                   |
| `getCheckpointedBlockNumber()`     | `getBlockNumber('checkpointed')`             |

**Deprecated but still present** (scheduled for removal once internal consumers of the archiver shape are rewired): `getL2Tips` (use `getChainTips`), `getBlockHeader` (use `getBlock(param).then(r => r?.header)`), `getCheckpointedBlocks` (use `getBlocks(from, limit, { includeL1PublishInfo: true, includeAttestations: true })`). Do not adopt these in new code. (`getCheckpointsDataForEpoch` was previously listed here; see the dedicated checkpoint-API entry below for its removal.)

**New response shapes:** `BlockResponse` always carries `header`, `archive`, `hash`, `number`, `checkpointNumber`, and `indexWithinCheckpoint`. `body`, `l1` (an `L1PublishInfo` discriminated union), and `attestations` are present only when the matching include option is set. `CheckpointResponse` mirrors this for checkpoints, with `blocks` gated on `includeBlocks`, and always carries `feeAssetPriceModifier` as a base field. The response types are generic over the options object, so passing a literal `{ includeTransactions: true }` narrows the return type and `response.body` becomes non-optional.

**Nested blocks on `getCheckpoint`:** only `includeTransactions` is forwarded to the blocks embedded by `includeBlocks: true`. `includeL1PublishInfo` and `includeAttestations` on a checkpoint request attach L1 / attestation data to the checkpoint itself, not to its nested blocks.

**Return type changes for `getBlocks` / `getCheckpoints`:** the return type is now `BlockResponse[]` / `CheckpointResponse[]` instead of `L2Block[]` / `PublishedCheckpoint[]`. Callers that previously consumed fields of `L2Block` (e.g. `.body`) must now opt in via `{ includeTransactions: true }`; callers that consumed `PublishedCheckpoint.checkpoint.blocks` must opt in via `{ includeBlocks: true }`.

**Migration for wallet/SDK consumers (`@aztec/aztec.js`, `@aztec/wallet-sdk`):**

```diff
- const block = await node.getBlockByHash(hash);
+ const block = await node.getBlock(hash, { includeTransactions: true });

- const archiveBlock = await node.getBlockByArchive(archive);
+ const archiveBlock = await node.getBlock({ archive }, { includeTransactions: true });

- const provenNumber = await node.getProvenBlockNumber();
+ const provenNumber = await node.getBlockNumber('proven');

- const checkpointedNumber = await node.getCheckpointedBlockNumber();
+ const checkpointedNumber = await node.getBlockNumber('checkpointed');

- const tips = await node.getL2Tips();
+ const tips = await node.getChainTips();
```

`getBlockHeader`, `getCheckpointedBlocks`, `getCheckpointsDataForEpoch`, and `getL2Tips` continue to work in this release but are deprecated; migrate to the replacements above.

**Chain-tip selectors:** `getBlockNumber` and `getCheckpointNumber` now accept an optional `ChainTip` argument (`'proposed' | 'checkpointed' | 'proven' | 'finalized'`). The `'proposed'` semantics are described in the dedicated checkpoint-API entry below — they were tightened in this release to mean "the proposed-tip checkpoint" rather than "the latest L1-confirmed checkpoint."

**Block parameter variants:** `BlockParameter` now also accepts a block hash, an archive root, and chain-tip names. The existing `number | 'latest'` forms continue to work — `'latest'` is an alias for `'proposed'`.

**Impact**: Source changes are required anywhere the removed methods are called. Type changes are required anywhere `L2Block` / `BlockHeader` / `CheckpointedL2Block` were consumed from the RPC — those call sites now receive `BlockResponse` / `CheckpointResponse` and must request the fields they need via `options`. Production nodes will reject JSON-RPC calls to the removed method names.

### \[Aztec Node] Checkpoint RPC: `'proposed'` is now strictly proposed; `'latest'` removed; `getCheckpointsData` takes a query

Follow-up to the unified-RPC change above. Tightens the checkpoint-side API surface: removes the old positional / per-shape entrypoints, drops the wire-level alias that conflated proposed and confirmed checkpoints, and replaces the deprecated epoch-only `getCheckpointsDataForEpoch` method with a unified query-shaped `getCheckpointsData` that mirrors the block-side API.

**`getCheckpointsDataForEpoch(epoch)` removed.** The previously-deprecated method is gone. Use `getCheckpointsData({ epoch })` instead. The new `getCheckpointsData` also accepts a contiguous range:

```diff
- const cps = await node.getCheckpointsDataForEpoch(epoch);
+ const cps = await node.getCheckpointsData({ epoch });

  // New: contiguous range
+ const cps = await node.getCheckpointsData({ from: 1, limit: 5 });
```

**`'latest'` removed from `CheckpointParameter`.** The `'latest'` literal previously accepted by `getCheckpoint('latest', options)` is no longer valid. Use `'checkpointed'` to address the latest confirmed checkpoint, or `'proposed'` for the proposed-tip semantics described below. (Block-side `'latest'` in `BlockParameter` is unaffected.)

```diff
- await node.getCheckpoint('latest');
+ await node.getCheckpoint('checkpointed');
```

**`'proposed'` semantics changed.** Previously `'proposed'` on the checkpoint side aliased to "latest L1-confirmed checkpoint" — a documented foot-gun. After this release:

* `getCheckpoint('proposed')` resolves to the proposed-tip checkpoint number and looks it up confirmed-first, then falls back to the proposed-checkpoint store. When a proposed entry exists at that number it is returned; when none exists, the proposed-tip falls back to the confirmed tip and the call returns the latest confirmed checkpoint. Returns `undefined` only when neither store has the resolved number.
* `getCheckpointNumber('proposed')` returns the proposed-tip checkpoint number, falling back to the latest confirmed checkpoint number when no proposed entry exists. Return type stays `Promise<CheckpointNumber>`.

If you want the latest L1-confirmed checkpoint regardless of proposed state, switch the call to `'checkpointed'`:

```diff
- const cp = await node.getCheckpoint('proposed');
- const n  = await node.getCheckpointNumber('proposed');
+ const cp = await node.getCheckpoint('checkpointed');
+ const n  = await node.getCheckpointNumber('checkpointed');
```

**By-number / by-slot lookups gain a confirmed→proposed fallback.** `getCheckpoint({ number: N })` and `getCheckpoint({ slot: S })` now check the confirmed store first, then fall back to the proposed store. Tag-based lookups (`'checkpointed'`, `'proven'`, `'finalized'`) do not fall back — those tags name confirmed-only positions.

**Throws on a proposed match + L1/attestations.** Proposed checkpoints have no L1 publish info or committee attestations (those data points only exist after L1 confirmation). The throw fires only when the lookup actually lands on a proposed entry — i.e. the confirmed store missed and the proposed store hit. When the proposed-tip falls back to the confirmed tip (no proposed entry exists), `'proposed' + includeAttestations` returns the latest confirmed checkpoint with attestations rather than throwing:

```ts
// Throws BadRequestError when a proposed entry exists at the resolved number:
await node.getCheckpoint("proposed", { includeAttestations: true });
await node.getCheckpoint("proposed", { includeL1PublishInfo: true });

// And when a by-number / by-slot lookup falls back to a proposed entry:
await node.getCheckpoint({ number: N }, { includeAttestations: true });
// → throws if N is matched only in the proposed store
```

If your code asks for `includeAttestations` / `includeL1PublishInfo` and might land on a proposed entry, gate the call on `getCheckpoint(param)` first, then re-issue with the include flags only after confirming the result is from the confirmed store (e.g. by checking that the tag-based equivalent returns the same checkpoint number).

**Impact**: Wallet, indexer, and tooling code that called `node.getCheckpoint('proposed')` or `node.getCheckpoint('latest')` will need to update their tag. Any code relying on the old "proposed = latest confirmed" alias should switch to `'checkpointed'`. Code that combined `'proposed'` (or by-number/by-slot fallbacks) with `includeAttestations` / `includeL1PublishInfo` will now throw at runtime; gate those flags as described above.

### \[Aztec Node] `feeAssetPriceModifier` now correctly populated on confirmed checkpoints

Confirmed checkpoints previously reported `feeAssetPriceModifier = 0n` regardless of the value observed on L1, because the archiver dropped the field on checkpoint confirmation. The field is now persisted and returned correctly on `CheckpointResponse`. Any wallet or indexer logic that special-cased `0n` as a sentinel for "no modifier" will need to be updated; it is now a valid value in its own right.

### \[CLI] `aztec-up` no longer exposes transitive npm bins on PATH

The `aztec-up` installer used to add `$HOME/.aztec/current/node_modules/.bin` to your shell `PATH`, which put \~40 transitive npm bins (`jest`, `tsc`, `tsserver`, `semver`, `uuid`, `json5`, ...) onto your interactive shell and silently shadowed your own installed versions of those tools. Only the seven `@aztec/*`-owned bins (`aztec`, `aztec-wallet`, `bb`, `bb-cli`, `blob-client`, `noir-codegen`, `txe`) are now exposed.

If you had an Aztec version installed before this release, your shell profile (`~/.bashrc` or `~/.zshrc`) still contains the old `PATH` line. Re-run the installer once (replacing `[VERSION]` with whichever toolchain version you're on, e.g. `4.2.0`) to replace it:

```bash
VERSION=[VERSION] bash -i <(curl -sL https://install.aztec.network)
```

Open a fresh shell and confirm the leak is gone:

```bash
echo $PATH
```

`$HOME/.aztec/current/node_modules/.bin` should no longer appear in the output. You'll also see your own `jest`, `tsc`, etc. again instead of the ones bundled with the Aztec toolchain.

### \[Protocol] Domain separators introduced for merkle-node, block-headers, and blob hashes

Several protocol hashes that previously used bare `poseidon2_hash` are now domain-separated via `poseidon2_hash_with_separator`. This is a security hardening change — it prevents a value produced by one hash context from being reinterpreted in another (e.g. a sibling path from one tree being transported to another).

**New domain separators:**

* `DOM_SEP__MERKLE_HASH` — sibling-pair hash for append-only trees (note-hash, L1→L2, archive, VK tree, and the balanced/unbalanced tree hash helpers in `@aztec/foundation/trees`).
* `DOM_SEP__NULLIFIER_MERKLE`, `DOM_SEP__PUBLIC_DATA_MERKLE`, `DOM_SEP__WRITTEN_SLOTS_MERKLE`, `DOM_SEP__RETRIEVED_BYTECODES_MERKLE` — per-tree sibling-pair hash for each indexed tree. Each tree uses its own separator so sibling paths are non-transportable across trees.
* `DOM_SEP__BLOCK_HEADERS_HASH` — used when accumulating block headers into `blockHeadersHash`.
* `DOM_SEP__BLOB_HASHED_Y_LIMBS`, `DOM_SEP__BLOB_CHALLENGE_Z`, `DOM_SEP__BLOB_Z_ACC`, `DOM_SEP__BLOB_GAMMA_ACC`, `DOM_SEP__BLOB_GAMMA_FINAL` — blob accumulator and challenge derivations.

**⚠️<!-- --> Hard-coded test constants will no longer match.** Every value derived from any of the hashes above is new in this release. This includes (non-exhaustively):

* **All merkle tree roots** — note-hash, nullifier, public-data, L1→L2 message, archive, VK, and the AVM-internal written-slots and retrieved-bytecodes (class-ids) trees.
* **Genesis constants** — `GENESIS_BLOCK_HEADER_HASH`, `GENESIS_ARCHIVE_ROOT`, `AVM_WRITTEN_PUBLIC_DATA_SLOTS_TREE_INITIAL_ROOT`, `AVM_RETRIEVED_BYTECODES_TREE_INITIAL_ROOT`.
* **Every block hash and archive root** — they commit to tree roots and the new block-headers-hash.
* **Protocol contract addresses** — their derivation depends on the private-function tree root.
* **Blob commitments / challenges** — any test that pins `z`, `gamma`, or the accumulator outputs.

Regenerate these values from a fresh build of this release — do not copy them from previous release fixtures.

**If you re-implement any of these hashes off-circuit** (e.g. a wallet that computes nullifier low-leaf membership, or an indexer that derives block hashes / tree roots), update the call sites:

```diff
  // Merkle sibling-pair hash (append-only trees)
- poseidon2Hash([left, right])
+ poseidon2HashWithSeparator([left, right], DomainSeparator.MERKLE_HASH)

  // Indexed-tree sibling-pair hash — pick the matching tree separator
- poseidon2Hash([left, right])
+ poseidon2HashWithSeparator([left, right], DomainSeparator.NULLIFIER_MERKLE)
+ // or PUBLIC_DATA_MERKLE, WRITTEN_SLOTS_MERKLE, RETRIEVED_BYTECODES_MERKLE
```

`poseidon2HashWithSeparator` is exported from `@aztec/foundation/crypto/poseidon`; the `DomainSeparator` enum and the matching `DOM_SEP__*` constants are defined in `@aztec/constants`. The new entries listed above are additions — existing separator names are unchanged.

For TypeScript consumers, `@aztec/stdlib/hash` exports ready-made helpers that wrap the right separator: `computeMerkleHash` (append-only), `computeNullifierMerkleHash`, and `computePublicDataMerkleHash`. Prefer these over calling `poseidon2HashWithSeparator` directly so the separator choice stays colocated with the tree.

### \[Aztec.nr] `emit_private_log_unsafe` / `emit_raw_note_log_unsafe` are deprecated

`emit_private_log_unsafe` and `emit_raw_note_log_unsafe` are deprecated and will be removed in a future release. Migrate to the new `emit_private_log_vec_unsafe` / `emit_raw_note_log_vec_unsafe` functions, which take a `BoundedVec<Field, PRIVATE_LOG_CIPHERTEXT_LEN>` instead of the `(log: [Field; PRIVATE_LOG_CIPHERTEXT_LEN], length: u32)` pair.

```diff
- context.emit_private_log_unsafe(tag, log, length);
+ context.emit_private_log_vec_unsafe(tag, bounded_vec_log);
- context.emit_raw_note_log_unsafe(tag, log, length, note_hash_counter);
+ context.emit_raw_note_log_vec_unsafe(tag, bounded_vec_log, note_hash_counter);
```

If you were manually padding an array and passing a shorter length, you can now create a `BoundedVec` from just the meaningful fields:

```diff
- let padded = payload.concat([0; PRIVATE_LOG_CIPHERTEXT_LEN - 2]);
- context.emit_private_log_unsafe(tag, padded, 2);
+ let log = BoundedVec::from_array(payload);
+ context.emit_private_log_vec_unsafe(tag, log);
```

If you were passing the full array, wrap it with `BoundedVec::from_array`:

```diff
- context.emit_private_log_unsafe(tag, ciphertext, ciphertext.len());
+ context.emit_private_log_vec_unsafe(tag, BoundedVec::from_array(ciphertext));
```

### \[aztec-nr] Nullifier membership witness oracle returns split types

`get_nullifier_membership_witness` and `get_low_nullifier_membership_witness` now return `(NullifierLeafPreimage, MembershipWitness<NULLIFIER_TREE_HEIGHT>)` instead of the bundled `NullifierMembershipWitness` struct (which has been removed).

If you were using these oracle functions directly (e.g. in `schnorr_account_contract`'s `lookup_validity`), update your code to destructure the tuple:

```diff
- let witness = get_low_nullifier_membership_witness(block_header, siloed_nullifier);
- let nullifier_value = witness.leaf_preimage.nullifier;
- let index = witness.index;
- let path = witness.path;
+ let (leaf_preimage, witness) = get_low_nullifier_membership_witness(block_header, siloed_nullifier);
+ let nullifier_value = leaf_preimage.nullifier;
+ let index = witness.leaf_index;
+ let path = witness.sibling_path;
```

Note the field renames: `index` is now `leaf_index`, and `path` is now `sibling_path` (matching the protocol circuit's `MembershipWitness` type).

This has been done because this is the format expected by the functionality in protocol circuits and given that this is sensitive security-wise it made sense to reuse that functionality in Aztec.nr.

### \[L1 Contracts] Empire slasher removed, slasher config simplified

The empire slashing model has been removed. Only the tally-based slashing model remains, and it has been renamed from `TallySlashingProposer` to `SlashingProposer`.

**L1 contract changes:**

* `SlasherFlavor` enum removed from `ISlasher.sol`
* `RollupConfigInput.slasherFlavor` (enum) replaced with `slasherEnabled` (bool)
* `TallySlashingProposer` contract renamed to `SlashingProposer`
* `TallySlasherDeploymentExtLib` library renamed to `SlasherDeploymentExtLib`
* `SlashFactory` periphery contract removed
* `SLASHING_PROPOSER_TYPE` constant removed from `SlashingProposer`
* All `TallySlashingProposer__` error prefixes renamed to `SlashingProposer__`

**Environment variable changes:**

```diff
- AZTEC_SLASHER_FLAVOR=tally    # was: "tally" | "empire" | "none"
+ AZTEC_SLASHER_ENABLED=true    # now a boolean
```

**Removed environment variables:** `SLASH_MIN_PENALTY_PERCENTAGE`, `SLASH_MAX_PENALTY_PERCENTAGE`

**Removed from deploy outputs:** `slashFactoryAddress`

**Node admin API:** `getSlashPayloads()` method removed.

**TypeScript config changes:**

```diff
- slasherFlavor: 'tally' | 'none'
+ slasherEnabled: boolean
```

`slashMinPenaltyPercentage` and `slashMaxPenaltyPercentage` removed from `SlasherConfig`.

### \[Aztec Node] `getTxByHash`, `getTxsByHash` and `getPendingTxs` no longer return tx proofs by default

`AztecNode.getTxByHash`, `AztecNode.getTxsByHash` and `AztecNode.getPendingTxs` (also exposed on the P2P API) now take an optional `GetTxByHashOptions` argument with an `includeProof` flag. The proof is stripped from returned txs unless `includeProof: true` is passed, cutting roughly 35-52KB per tx over the wire.

**Migration:**

```diff
- const tx = await node.getTxByHash(txHash);
+ const tx = await node.getTxByHash(txHash, { includeProof: true });

- const txs = await node.getPendingTxs(limit, after);
+ const txs = await node.getPendingTxs(limit, after, { includeProof: true });
```

**Impact**: Callers that read the proof off returned txs (eg to re-broadcast or validate them) must now pass `{ includeProof: true }` explicitly; by default the returned txs carry an empty proof.

### \[aztec.js] `DeployMethod.send()` always returns `{ contract, receipt, instance }`

The `returnReceipt` option in deploy wait options has been removed. `DeployMethod.send()` now always returns an object with `contract`, `receipt`, and `instance` at the top level, provided the user waits for the transaction to be included.

The `DeployTxReceipt` and `DeployWaitOptions` types have been removed.

**Migration:**

```diff
- const {
-   receipt: { contract, instance },
- } = await MyContract.deploy(wallet, ...args).send({
-   from: address,
-   wait: { returnReceipt: true },
- });

+ const { contract, instance } = await MyContract.deploy(wallet, ...args).send({
+   from: address,
+ });
```

### \[CLI] `aztec init` now scaffolds a Counter example template

`aztec init` previously created a blank contract crate. It now scaffolds a runnable **Counter** example contract with a constructor, `increment`, and `get_counter` functions, plus a test suite, so new developers have a working starting point ([#22751](https://github.com/AztecProtocol/aztec-packages/pull/22751)).

* `aztec init` — scaffolds the Counter example (new default).
* `aztec new <NAME>` — still scaffolds a blank contract, either as a new standalone project or as a new crate added to an existing workspace.

**Impact**: any scripts, CI jobs, or onboarding docs that ran `aztec init` expecting an empty contract starting point now get the Counter example. Use `aztec new <NAME>` for the blank scaffold. The existing Counter tutorial under [`docs/tutorials/contract_tutorials`](/developers/docs/tutorials/contract_tutorials/counter_contract.md) is unaffected because it uses `aztec new`.

## 4.3.0

### `aztec new` and `aztec init` now create a 2-crate workspace

`aztec new` and `aztec init` now create a workspace with two crates instead of a single contract crate:

* A `contract` crate (type = "contract") for your smart contract code
* A `test` crate (type = "lib") for Noir tests, which depends on the contract crate

The new project structure looks like:

```text
my_project/
├── Nargo.toml           # [workspace] members = ["contract", "test"]
├── contract/
│   ├── src/main.nr
│   └── Nargo.toml       # type = "contract"
└── test/
    ├── src/lib.nr
    └── Nargo.toml       # type = "lib"
```

**What changed:**

* The `--contract` and `--lib` flags have been removed from `aztec new` and `aztec init`. These commands now always create a contract workspace.
* Contract code is now at `contract/src/main.nr` instead of `src/main.nr`.
* The `Nargo.toml` in the project root is now a workspace file. Contract dependencies go in `contract/Nargo.toml`.
* Tests should be written in the separate `test` crate (`test/src/lib.nr`) and import the contract by package name (e.g., `use my_contract::MyContract;`) instead of using `crate::`.

### `aztec new` crate directories are now named after the contract

`aztec new` and `aztec init` now name the generated crate directories after the contract instead of using generic `contract/` and `test/` names. For example, `aztec new counter` now creates:

```text
counter/
├── Nargo.toml                # [workspace] members = ["counter_contract", "counter_test"]
├── counter_contract/
│   ├── src/main.nr
│   └── Nargo.toml            # type = "contract"
└── counter_test/
    ├── src/lib.nr
    └── Nargo.toml            # type = "lib"
```

This enables adding multiple contracts to a single workspace. Running `aztec new <name>` inside an existing workspace (a directory with a `Nargo.toml` containing `[workspace]`) now adds a new `<name>_contract` and `<name>_test` crate pair to the workspace instead of creating a new directory.

**What changed:**

* Crate directories are now `<name>_contract/` and `<name>_test/` instead of `contract/` and `test/`.
* Contract code is now at `<name>_contract/src/main.nr` instead of `contract/src/main.nr`.
* Contract dependencies go in `<name>_contract/Nargo.toml` instead of `contract/Nargo.toml`.
* Tests import the contract by its new crate name (e.g., `use counter_contract::Main;` instead of `use counter::Main;`).

### \[CLI] `--name` flag removed from `aztec new` and `aztec init`

The `--name` flag has been removed from both `aztec new` and `aztec init`. For `aztec new`, the positional argument now serves as both the contract name and the directory name. For `aztec init`, the directory name is always used as the contract name.

**Migration:**

```diff
- aztec new my_project --name counter
+ aztec new counter
```

```diff
- aztec init --name counter
+ aztec init
```

**Impact**: If you were using `--name` to set a contract name different from the directory name, rename your directory or use `aztec new` with the desired contract name directly.

## 4.2.0

### \[Aztec.js] `GasSettings.default()` renamed to `GasSettings.fallback()`

`GasSettings.default()` has been renamed to `GasSettings.fallback()` to clarify that these gas limits are not protocol defaults — the protocol has no concept of "default" gas settings. `fallback()` is a convenience for cases where gas estimation is not being used, but callers should prefer estimating gas via simulation for accurate limits.

The old `DEFAULT_GAS_LIMIT` and `DEFAULT_TEARDOWN_GAS_LIMIT` constants have been removed. Gas limits are now derived from protocol-level maximums (`MAX_PROCESSABLE_L2_GAS`, `MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT`) rather than arbitrary fixed values.

A new `GasSettings.forEstimation()` method provides intentionally high gas limits for use during simulation. These limits exceed protocol maximums so the simulation doesn't hit gas caps — you must pass `skipTxValidation: true` when simulating with them, then use the results to set accurate gas limits on the actual transaction. `EmbeddedWallet` does this by default.

**Migration:**

```diff
- import { DEFAULT_GAS_LIMIT, DEFAULT_TEARDOWN_GAS_LIMIT } from '@aztec/constants';
- const settings = GasSettings.default({ maxFeesPerGas });
+ const settings = GasSettings.fallback({ maxFeesPerGas });
```

**Impact**: Any code referencing `GasSettings.default()`, `DEFAULT_GAS_LIMIT`, or `DEFAULT_TEARDOWN_GAS_LIMIT` will fail to compile.

### \[PXE] `simulateTx`, `executeUtility`, `profileTx`, and `proveTx` no longer accept `scopes: 'ALL_SCOPES'`

The `AccessScopes` type (`'ALL_SCOPES' | AztecAddress[]`) has been removed. The `scopes` field in `SimulateTxOpts`, `ExecuteUtilityOpts`, and `ProfileTxOpts` now requires an explicit `AztecAddress[]`. Callers that previously passed `'ALL_SCOPES'` must now specify which addresses will be in scope for the call.

**Migration:**

```diff
+ const accounts = await pxe.getRegisteredAccounts();
+ const scopes = accounts.map(a => a.address);

  // simulateTx
- await pxe.simulateTx(txRequest, { simulatePublic: true, scopes: 'ALL_SCOPES' });
+ await pxe.simulateTx(txRequest, { simulatePublic: true, scopes });

  // executeUtility
- await pxe.executeUtility(call, { scopes: 'ALL_SCOPES' });
+ await pxe.executeUtility(call, { scopes });

  // profileTx
- await pxe.profileTx(txRequest, { profileMode: 'full', scopes: 'ALL_SCOPES' });
+ await pxe.profileTx(txRequest, { profileMode: 'full', scopes });

  // proveTx
- await pxe.proveTx(txRequest, 'ALL_SCOPES');
+ await pxe.proveTx(txRequest, scopes);
```

**Impact**: Any code passing `'ALL_SCOPES'` to `simulateTx`, `executeUtility`, `profileTx`, or `proveTx` will fail to compile. Replace with an explicit array of account addresses.

### \[PXE] Capsule operations are now scope-enforced at the PXE level

The PXE now enforces that capsule operations can only access scopes that were authorized for the current execution. If a contract attempts to access a capsule scope that is not in its allowed scopes list, the PXE will throw an error:

```text
Scope 0x1234... is not in the allowed scopes list: [0xabcd...].
```

The zero address (`AztecAddress::zero()`) is always allowed regardless of the scopes list, preserving backwards compatibility for contracts using the global scope.

**Impact**: Contracts that access capsules scoped to addresses not included in the transaction's authorized scopes will now fail at runtime. Ensure the correct scopes are passed when executing transactions.

### \[aztec.js] `EmbeddedWalletOptions` now uses a unified `pxe` field

The `pxeConfig` and `pxeOptions` fields on `EmbeddedWalletOptions` have been deprecated in favor of a single `pxe` field that accepts both PXE configuration and dependency overrides (custom prover, store, simulator):

```diff
const wallet = await EmbeddedWallet.create(nodeUrl, {
-  pxeConfig: { proverEnabled: true },
-  pxeOptions: { proverOrOptions: myCustomProver },
+  pxe: {
+    proverEnabled: true,
+    proverOrOptions: myCustomProver,
+  },
});
```

The old fields still work but will be removed in a future release.

### \[Aztec.nr] Ephemeral arrays replace capsule arrays in PXE oracle interfaces

Oracle interfaces between Aztec.nr and PXE now use a new `EphemeralArray` type (`aztec::ephemeral::EphemeralArray`) instead of `CapsuleArray`. Ephemeral arrays live in memory and are scoped by contract call frame, so they no longer need to be addressed by `(contract_address, scope)`. Several public message-discovery and validation functions lost their `recipient`, `scope`, and `contract_address` parameters as a result.

Most contracts are not affected, as the macro-generated `sync_state` and `process_message` functions handle these APIs automatically. Only contracts that call these functions directly need to update.

**Migration:**

```diff
  attempt_note_discovery(
      contract_address,
      tx_hash,
      unique_note_hashes_in_tx,
      first_nullifier_in_tx,
-     recipient,
      compute_note_hash,
      compute_note_nullifier,
      owner,
      storage_slot,
      randomness,
      note_type_id,
      packed_note,
  );

- enqueue_note_for_validation(contract_address, owner, storage_slot, randomness, note_nonce, packed_note, note_hash, nullifier, tx_hash, scope);
+ enqueue_note_for_validation(contract_address, owner, storage_slot, randomness, note_nonce, packed_note, note_hash, nullifier, tx_hash);

- enqueue_event_for_validation(contract_address, event_type_id, randomness, serialized_event, event_commitment, tx_hash, scope);
+ enqueue_event_for_validation(contract_address, event_type_id, randomness, serialized_event, event_commitment, tx_hash);

- validate_and_store_enqueued_notes_and_events(contract_address, scope);
+ validate_and_store_enqueued_notes_and_events(scope);
```

The `sync_inbox` function and the `OffchainInboxSync` type now return `EphemeralArray<OffchainMessageWithContext>` instead of `CapsuleArray<OffchainMessageWithContext>`. Custom message handlers that bind the returned array to an explicit type must update the type annotation.

**Impact**: Contracts that call the above functions directly (rather than relying on macro-generated code) will fail to compile until the trailing `recipient`, `scope`, and `contract_address` parameters are removed.

## 4.2.0-aztecnr-rc.2

### \[Aztec.js] Removed `SingleKeyAccountContract`

The `SchnorrSingleKeyAccount` contract and its TypeScript wrapper `SingleKeyAccountContract` have been removed. This contract was insecure: it used `ivpk_m` (incoming viewing public key) as its Schnorr signing key, meaning anyone who received a user's viewing key could sign transactions on their behalf.

**Migration:**

```diff
- import { SingleKeyAccountContract } from '@aztec/accounts/single_key';
- const contract = new SingleKeyAccountContract(signingKey);
+ import { SchnorrAccountContract } from '@aztec/accounts/schnorr';
+ const contract = new SchnorrAccountContract(signingKey);
```

**Impact**: If you were using `@aztec/accounts/single_key`, switch to `@aztec/accounts/schnorr` which uses separate keys for encryption and authentication.

### Custom token FPCs removed from default public setup allowlist

Token contract functions (like `transfer_in_public` and `_increase_public_balance`) have been removed from the default public setup allowlist. FPCs that accept custom tokens (like the reference `FPC` contract) will not work on public networks, because their setup-phase calls to these functions will be rejected. Token class IDs change with each aztec-nr release, making it impractical to maintain them in the allowlist.

FPCs that use only Fee Juice still work on all networks, since FeeJuice is a protocol contract with a fixed address in the allowlist. Custom FPCs should only call protocol contract functions (AuthRegistry, FeeJuice) during setup.

`PublicFeePaymentMethod` and `PrivateFeePaymentMethod` in aztec.js are affected, since they use the reference `FPC` contract which calls Token functions during setup. Switch to `FeeJuicePaymentMethodWithClaim` (after [bridging Fee Juice from L1](/developers/docs/aztec-js/how_to_pay_fees.md#bridge-fee-juice-from-l1)) or write an FPC that uses Fee Juice natively.

**Migration:**

```diff
- import { PublicFeePaymentMethod } from '@aztec/aztec.js/fee';
- const paymentMethod = new PublicFeePaymentMethod(fpcAddress, senderAddress, wallet, gasSettings);
+ import { FeeJuicePaymentMethodWithClaim } from '@aztec/aztec.js/fee';
+ const paymentMethod = new FeeJuicePaymentMethodWithClaim(senderAddress, claim);
```

Similarly, the `fpc-public` and `fpc-private` CLI wallet payment methods use the reference Token-based FPC and will not work on public networks. Use `fee_juice` for direct Fee Juice payment, or `fpc-sponsored` on devnet and local network.

### \[Aztec.nr] Domain-separated tags on log emission

All logs emitted through the Aztec.nr framework now include a domain-separated tag at `fields[0]`. Each log category uses its own domain separator via `compute_log_tag(raw_tag, dom_sep)`:

* **Events** (`DOM_SEP__EVENT_LOG_TAG`): the event type ID is the raw tag.
* **Message delivery** (`DOM_SEP__UNCONSTRAINED_MSG_LOG_TAG`): the discovery tag is the raw tag.
* **Partial note completion logs** (`DOM_SEP__NOTE_COMPLETION_LOG_TAG`): the partial note's `commitment` field is the raw tag.

The low-level emit methods now take `tag` as an explicit first parameter and have been renamed with an `_unsafe` suffix. Previously the tag was included as `log[0]` — it has now been extracted into its own parameter, and `log` no longer contains it:

```diff
- context.emit_private_log(log, length);
+ context.emit_private_log_unsafe(tag, log, length);
- context.emit_raw_note_log(log, length, note_hash_counter);
+ context.emit_raw_note_log_unsafe(tag, log, length, note_hash_counter);
- context.emit_public_log(log);
+ context.emit_public_log_unsafe(tag, log);
```

Prefer the higher-level APIs (`emit` for events, `MessageDelivery` for messages) which handle tagging automatically.

### \[Aztec.nr] Public events no longer include the event type selector at the end of the payload

`emit_event_in_public` previously appended the event type selector as the last field. It now prepends a domain-separated tag at `fields[0]` instead. The payload after the tag contains only the serialized event fields.

If you were reading public event directly from node logs (i.e. via `node.getPublicLogs` and not via `wallet.getPublicEvents`), update your parsing:

```diff
- // Old: fields = [serialized_event..., event_type_selector]
- const selector = EventSelector.fromField(fields[fields.length - 1]);
- const event = decodeFromAbi([abiType], fields);
+ // New: fields = [domain_separated_tag, serialized_event...]
+ const eventFields = log.getEmittedFieldsWithoutTag();
+ const event = decodeFromAbi([abiType], eventFields);
```

### \[Aztec.nr] Capsule operations are now addressed by scope

All capsule operations (`store`, `load`, `delete`, `copy`) and `CapsuleArray` now require a `scope: AztecAddress` parameter. This scopes capsule storage by address, providing isolation between different accounts within the same PXE.

Contracts that use `CapsuleArray` directly also need to update.

**Migration:**

```diff
- let array: CapsuleArray<Field> = CapsuleArray::at(contract_address, slot);
+ let array: CapsuleArray<Field> = CapsuleArray::at(contract_address, slot, scope);
```

The low-level capsule functions are similarly affected:

```diff
- capsules::store(contract_address, slot, value);
+ capsules::store(contract_address, slot, value, scope);

- capsules::load(contract_address, slot);
+ capsules::load(contract_address, slot, scope);

- capsules::delete(contract_address, slot);
+ capsules::delete(contract_address, slot, scope);

- capsules::copy(contract_address, src_slot, dst_slot, num_entries);
+ capsules::copy(contract_address, src_slot, dst_slot, num_entries, scope);
```

If you need to stick the old, scope-less behavior, and you are really sure that that's what you need to use, you can use `scope = AztecAddress::zero()`.

### \[Aztec.nr] `process_message` utility function removed

The auto-generated `process_message` utility function has been removed. If you need to deliver offchain messages (messages not broadcast via onchain logs), use the `offchain_receive` utility function instead. This function is automatically injected by the `#[aztec]` macro and accepts messages into a persistent inbox scoped by recipient. These messages are then picked up and processed during `sync_state`.

**Impact**: Contracts that explicitly called `process_message` must switch to delivering messages via `offchain_receive` and letting `sync_state` handle processing.

### \[Aztec.nr] `CustomMessageHandler` type signature changed

The `CustomMessageHandler` function type now receives an additional `scope: AztecAddress` parameter:

```diff
  type CustomMessageHandler = unconstrained fn(
      AztecAddress,    // contract_address
      u64,             // msg_type_id
      u64,             // msg_metadata
      BoundedVec<Field, MAX_MESSAGE_CONTENT_LEN>,  // msg_content
      MessageContext,  // message_context
+     AztecAddress,    // scope
  );
```

**Impact**: Contracts that implement a custom message handler must update the function signature.

### \[aztec.js] `isContractInitialized` is now `initializationStatus` tri-state enum

`ContractMetadata.isContractInitialized` has been renamed to `ContractMetadata.initializationStatus` and changed from `boolean | undefined` to a `ContractInitializationStatus` enum with values `INITIALIZED`, `UNINITIALIZED`, and `UNKNOWN`.

* `INITIALIZED`: the contract has been initialized (initialization nullifier found)
* `UNINITIALIZED`: the contract instance is registered but has not been initialized
* `UNKNOWN`: the instance is not registered and no public initialization nullifier was found

When the instance is not registered, the wallet now attempts to check the public initialization nullifier (computed from address alone) before returning `UNKNOWN`. Previously this case returned `undefined`.

**Migration:**

```diff
+ import { ContractInitializationStatus } from '@aztec/aztec.js/wallet';

  const metadata = await wallet.getContractMetadata(address);
- if (metadata.isContractInitialized) {
+ if (metadata.initializationStatus === ContractInitializationStatus.INITIALIZED) {
    // contract is initialized
  }
```

### \[Aztec.js] Use `NO_FROM` instead of `AztecAddress.ZERO` to bypass account contract entrypoint

When sending transactions that should not be mediated by an account contract (e.g., account contract self-deployments), use the explicit `NO_FROM` sentinel instead of `AztecAddress.ZERO`.

`NO_FROM` signals that the transaction should be executed directly via the `DefaultEntrypoint`. This replaces the brittle convention of passing `AztecAddress.ZERO` as the `from` field.

**Migration:**

```diff
- import { AztecAddress } from '@aztec/aztec.js';
+ import { NO_FROM } from '@aztec/aztec.js/account';

  await contract.methods.my_method().send({
-   from: AztecAddress.ZERO,
+   from: NO_FROM,
  });
```

Note that `DefaultEntrypoint` only accepts a single call. If you need to execute multiple calls without account contract mediation (e.g., deploying an account contract and paying a fee in the same transaction), wrap them through `DefaultMultiCallEntrypoint` on the app side before sending:

```typescript
import { NO_FROM } from "@aztec/aztec.js/account";
import { DefaultMultiCallEntrypoint } from "@aztec/entrypoints/multicall";
import { mergeExecutionPayloads } from "@aztec/stdlib/tx";

// Merge multiple execution payloads into one
const merged = mergeExecutionPayloads([deployPayload, feePayload]);

// Wrap through multicall so it becomes a single call for DefaultEntrypoint
const multicall = new DefaultMultiCallEntrypoint();
const chainInfo = await wallet.getChainInfo();
const wrappedPayload = await multicall.wrapExecutionPayload(merged, chainInfo);

// Send without account contract mediation
await wallet.sendTx(wrappedPayload, { from: NO_FROM });
```

Using other contracts for wrapping (for example, supporting more calls) is also supported, as long as the contract is registered in the wallet. This opens the door to different flows that do not use account entrypoints as the first call in the chain, including app sponsored FPCs.

**Impact**: Any code that passes `AztecAddress.ZERO` as the `from` option in `.send()`, `.simulate()`, or deploy options must switch to `NO_FROM`. Wallets use `DefaultEntrypoint` directly for `NO_FROM` transactions, instead of the `DefaultMultiCallEntrypoint` that was used internally before when specifying `AztecAddress.ZERO`.

### \[Aztec.js] `ExecuteUtilityOptions.scope` renamed to `scopes` and type changed to `AztecAddress[]`

The `scope` field in `ExecuteUtilityOptions` has been renamed to `scopes` and changed from a single `AztecAddress` to `AztecAddress[]`. This aligns the wallet's `executeUtility` API with the PXE API and `sendTx` in `Wallet`, which both accept an array of scopes.

**Migration:**

```diff
  wallet.executeUtility(call, {
-   scope: myAddress,
+   scopes: [myAddress],
  });
```

**Impact**: Any code that calls `wallet.executeUtility` directly must update the options object. Wallets must update to adapt to the new interface

### \[Aztec.nr] `attempt_note_discovery` now takes two separate functions instead of one

The `attempt_note_discovery` function (and related discovery functions like `do_sync_state`, `process_message_ciphertext`) now takes separate `compute_note_hash` and `compute_note_nullifier` arguments instead of a single combined `compute_note_hash_and_nullifier`. The corresponding type aliases are now `ComputeNoteHash` and `ComputeNoteNullifier` (instead of `ComputeNoteHashAndNullifier`).

This split improves performance during nonce discovery: the note hash only needs to be computed once, while the old combined function recomputed it for every candidate nonce.

Most contracts are not affected, as the macro-generated `sync_state` and `process_message` functions handle this automatically. Only contracts that call `attempt_note_discovery` directly need to update.

**Migration:**

```diff
    attempt_note_discovery(
      contract_address,
      tx_hash,
      unique_note_hashes_in_tx,
      first_nullifier_in_tx,
      recipient,
-     _compute_note_hash_and_nullifier,
+     _compute_note_hash,
+     _compute_note_nullifier,
      owner,
      storage_slot,
      randomness,
      note_type_id,
      packed_note,
  );
```

**Impact**: Contracts that call `attempt_note_discovery` or related discovery functions directly with a custom `_compute_note_hash_and_nullifier` argument. The old combined function is still generated (deprecated) but is no longer used by the framework. Additionally, if you had a custom `_compute_note_hash_and_nullifier` function then compilation will now fail as you'll need to also produce the corresponding `_compute_note_hash` and `_compute_note_nullifier` functions.

### Private initialization nullifier now includes `init_hash`

The private initialization nullifier is no longer derived from just the contract address. It is now computed as a Poseidon2 hash of `[address, init_hash]` using a dedicated domain separator. This prevents observers from determining whether a fully private contract has been initialized by simply knowing its address.

Note that `Wallet.getContractMetadata` now returns `initializationStatus: ContractInitializationStatus.UNKNOWN` when the wallet does not have the contract instance registered, since `init_hash` is needed to compute the nullifier and initialization status cannot be determined. Previously, this check worked for any address. Callers should check the enum value before branching on the initialization state.

If you use `assert_contract_was_initialized_by` or `assert_contract_was_not_initialized_by` from `aztec::history::deployment`, these now require an additional `init_hash: Field` parameter:

```diff
+ let instance = get_contract_instance(contract_address);
  assert_contract_was_initialized_by(
      block_header,
      contract_address,
+     instance.initialization_hash,
  );
```

### Two separate init nullifiers for private and public

Contract initialization now emits two separate nullifiers instead of one: a **private init nullifier** and a **public init nullifier**. Each nullifier gates its respective execution domain:

* Private external functions check the private init nullifier.
* Public external functions check the public init nullifier.

**How initializers work:**

* **Private initializers** emit the private init nullifier. If the contract has any external public functions, the protocol auto-enqueues a public call to emit the public init nullifier.
* **Public initializers** emit both nullifiers directly.
* Contracts with no public functions only emit the private init nullifier.

**`only_self` functions no longer have init checks.** They behave as if marked `noinitcheck`.

**External functions called during private initialization must be `#[only_self]`.** Init nullifiers are emitted at the end of the initializer, so any external functions called on the initializing contract (e.g. via `enqueue_self` or `call_self`) during initialization will fail the init check unless they skip it.

**Breaking change for deployment:** If your contract has external public functions and a private initializer, the class must be registered onchain before initialization. You can no longer pass `skipClassPublication: true`, because the auto-enqueued public call requires the class to be available.

```diff
  const deployed = await MyContract.deploy(wallet, ...args).send({
-   skipClassPublication: true,
  }).deployed();
```

### \[Aztec.nr] Made `compute_note_hash_for_nullification` unconstrained

This function shouldn't have been constrained in the first place, as constrained computation of `HintedNote` nullifiers is dangerous (constrained computation of nullifiers can be performed only on the `ConfirmedNote` type). If you were calling this from a constrained function, consider using `compute_confirmed_note_hash_for_nullification` instead. Unconstrained usage is safe.

### \[Aztec.nr] Changes to standard note hash computation

Note hashes used to be computed with the storage slot being the last value of the preimage, it is now the first. This is to make it easier to ensure all note hashes have proper domain separation.

This change requires no input from your side unless you were testing or relying on hardcoded note hashes.

## 4.1.3

### \[Aztec.js] `TxReceipt` now includes `epochNumber`

`TxReceipt` now includes an `epochNumber` field that indicates which epoch the transaction was included in.

### \[Aztec.js] Use `getL2ToL1MembershipWitness` for L2-to-L1 message witnesses

The node now computes L2-to-L1 membership witnesses directly, resolving the epoch and Outbox root internally from the transaction hash. Use `getL2ToL1MembershipWitness` instead of fetching raw epoch messages and computing the witness client-side.

**Migration:**

```diff
- const messages = await aztecNode.getL2ToL1Messages(epochNumber);
- // compute the witness client-side from the epoch messages
+ const witness = await aztecNode.getL2ToL1MembershipWitness(txHash, messageHash);
+ const epoch = witness.epochNumber;
```

The return type `L2ToL1MembershipWitness` includes `epochNumber`. An optional `messageIndexInTx` parameter can be passed as the third argument to disambiguate when a transaction emits multiple identical L2-to-L1 messages.

**Impact**: Call sites that compute L2-to-L1 membership witnesses should use the node method and extract `epochNumber` from the result.

### \[Aztec.js] `getPublicEvents` now returns an object instead of an array

`getPublicEvents` now returns a `GetPublicEventsResult<T>` object with `events` and `maxLogsHit` fields instead of a plain array. This enables pagination through large result sets using the new `afterLog` filter option.

```diff
- const events = await getPublicEvents<MyEvent>(node, MyContract.events.MyEvent, filter);
+ const { events } = await getPublicEvents<MyEvent>(node, MyContract.events.MyEvent, filter);
```

The `maxLogsHit` flag indicates whether the log limit was reached, meaning more results may be available. You can use `afterLog` in the filter to fetch the next page.

### \[Aztec.nr] Removed `get_random_bytes`

The `get_random_bytes` unconstrained function has been removed from `aztec::utils::random`. If you were using it, you can replace it with direct calls to the `random` oracle from `aztec::oracle::random` and convert to bytes yourself.

## 4.1.0-rc.2

### \[Aztec.js] `simulate()`, `send()`, and deploy return types changed to always return objects

All SDK interaction methods now return structured objects that include offchain output alongside the primary result. This affects `.simulate()`, `.send()`, deploy `.send()`, and `Wallet.sendTx()`.

**Impact**: Every call site that uses `.simulate()`, `.send()`, or deploy must destructure the result. This is a mechanical transformation. Custom wallet implementations must update `sendTx()` to return the new object shapes, using `extractOffchainOutput` to decode offchain messages from raw effects.

The offchain output includes two fields:

* `offchainEffects` — raw offchain effects emitted during execution, other than `offchainMessages`
* `offchainMessages` — decoded messages intended for specific recipients

We are making this change now so in the future we can add more fields to the responses of this APIs without breaking backwards compatibility, so this won't ever happen again.

**`simulate()` — always returns `{ result, offchainEffects, offchainMessages }` object:**

```diff
- const value = await contract.methods.foo(args).simulate({ from: sender });
+ const { result: value } = await contract.methods.foo(args).simulate({ from: sender });
```

When using `includeMetadata` or `fee.estimateGas`, `stats` and `estimatedGas` are also available as optional fields on the same object:

```diff
- const { stats, estimatedGas } = await contract.methods.foo(args).simulate({
+ const sim = await contract.methods.foo(args).simulate({
    from: sender,
    includeMetadata: true,
  });
+ const stats = sim.stats!;
+ const estimatedGas = sim.estimatedGas!;
```

`SimulationReturn` is no longer a generic conditional type — it's a single flat type with optional `stats` and `estimatedGas` fields.

**`send()` — returns `{ receipt, offchainEffects, offchainMessages }` object:**

```diff
- const receipt = await contract.methods.foo(args).send({ from: sender });
+ const { receipt } = await contract.methods.foo(args).send({ from: sender });
```

When using `NO_WAIT`, returns `{ txHash, offchainEffects, offchainMessages }` instead of a bare `TxHash`:

```diff
- const txHash = await contract.methods.foo(args).send({ from: sender, wait: NO_WAIT });
+ const { txHash } = await contract.methods.foo(args).send({ from: sender, wait: NO_WAIT });
```

Offchain messages emitted by the transaction are available on the result:

```typescript
const { receipt, offchainMessages } = await contract.methods
  .foo(args)
  .send({ from: sender });
for (const msg of offchainMessages) {
  console.log(
    `Message for ${msg.recipient} from contract ${msg.contractAddress}:`,
    msg.payload,
  );
}
```

**Deploy — returns `{ contract, receipt, offchainEffects, offchainMessages }` object:**

```diff
- const myContract = await MyContract.deploy(wallet, ...args).send({ from: sender });
+ const { contract: myContract } = await MyContract.deploy(wallet, ...args).send({ from: sender });
```

The deploy receipt is also available via `receipt` if needed (e.g. for `receipt.txHash` or `receipt.transactionFee`).

**Custom wallet implementations — `sendTx()` must return objects:**

If you implement the `Wallet` interface (or extend `BaseWallet`), the `sendTx()` method must now return objects that include offchain output. Use `extractOffchainOutput` to split raw effects into decoded messages and remaining effects:

```diff
+ import { extractOffchainOutput } from '@aztec/aztec.js/contracts';

  async sendTx(executionPayload, opts) {
    const provenTx = await this.pxe.proveTx(...);
+   const offchainOutput = extractOffchainOutput(provenTx.getOffchainEffects());
    const tx = await provenTx.toTx();
    const txHash = tx.getTxHash();
    await this.aztecNode.sendTx(tx);

    if (opts.wait === NO_WAIT) {
-     return txHash;
+     return { txHash, ...offchainOutput };
    }
    const receipt = await waitForTx(this.aztecNode, txHash, opts.wait);
-   return receipt;
+   return { receipt, ...offchainOutput };
  }
```

### 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).

````text

**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.

**TypeScript:**

```diff
- import { SimulateUtilityOptions, UtilitySimulationResult } from '@aztec/aztec.js';
+ import { ExecuteUtilityOptions, UtilityExecutionResult } from '@aztec/aztec.js';

- const result: UtilitySimulationResult = await wallet.simulateUtility(functionCall, opts);
+ const result: UtilityExecutionResult = await wallet.executeUtility(functionCall, opts);
```

**Noir (test environment):**

```diff
- let result = env.simulate_utility(my_contract_address, selector);
+ let result = env.execute_utility(my_contract_address, selector);
```

## 4.0.0-devnet.2-patch.0

### \[Protocol] `include_by_timestamp` renamed to `expiration_timestamp`

The `include_by_timestamp` field has been renamed to `expiration_timestamp` across the protocol to better convey its meaning. **Noir:**

```diff
- context.set_tx_include_by_timestamp(123456789);
+ context.set_expiration_timestamp(123456789);
```

### \[CLI] Dockerless CLI Installation

The Aztec CLI is now installed without Docker. The installation command has changed:

**Old installation (deprecated):**

```bash
bash -i <(curl -sL https://install.aztec.network)
aztec-up <version>
```

**New installation:**

```bash
VERSION=<version> bash -i <(curl -sL https://install.aztec.network/<version>)
```

For example, to install version `6.0.0`:

```bash
VERSION=6.0.0 bash -i <(curl -sL https://install.aztec.network/6.0.0)
```

**Key changes:**

* Docker is no longer required to run the Aztec CLI tools
* The `VERSION` environment variable must be set in the installation command
* The version must also be included in the URL path

**aztec-up is now a version manager:**

After installation, `aztec-up` functions as a version manager with the following commands:

| Command                      | Description                                 |
| ---------------------------- | ------------------------------------------- |
| `aztec-up install <version>` | Install a specific version and switch to it |
| `aztec-up use <version>`     | Switch to an already installed version      |
| `aztec-up list`              | List all installed versions                 |
| `aztec-up self-update`       | Update aztec-up itself                      |

### `@aztec/test-wallet` replaced by `@aztec/wallets`

The `@aztec/test-wallet` package has been removed. Use `@aztec/wallets` instead, which provides `EmbeddedWallet` with a `static create()` factory:

```diff
- import { TestWallet, registerInitialLocalNetworkAccountsInWallet } from '@aztec/test-wallet/server';
+ import { EmbeddedWallet } from '@aztec/wallets/embedded';
+ import { registerInitialLocalNetworkAccountsInWallet } from '@aztec/wallets/testing';

- const wallet = await TestWallet.create(node);
+ const wallet = await EmbeddedWallet.create(node);
```

For browser environments, the same import resolves to a browser-specific implementation automatically via conditional exports<!-- -->:X

The `EmbeddedWallet.create()` factory accepts an optional second argument for logger injection and ephemeral storage:

```typescript
const wallet = await EmbeddedWallet.create(node, {
  logger: myLogger, // custom logger; child loggers derived via createChild()
  ephemeral: true, // use in-memory stores (no persistence)
});
```

### \[Aztec.nr] `debug_log` module renamed to `logging`

The `debug_log` module has been renamed to `logging` to avoid naming collisions with per-level logging functions that were introduced in this PR (`warn_log`, `info_log`, `debug_log`... and the "format" versions `warn_log_format`, `debug_log_format`). Update all import paths accordingly:

```diff
- use aztec::oracle::debug_log::debug_log;
- use aztec::oracle::debug_log::debug_log_format;
+ use aztec::oracle::logging::debug_log;
+ use aztec::oracle::logging::debug_log_format;
```

For inline paths:

```diff
- aztec::oracle::debug_log::debug_log_format("msg: {}", [value]);
+ aztec::oracle::logging::debug_log_format("msg: {}", [value]);
```

The function names themselves (`debug_log`, `debug_log_format`, `debug_log_with_level`, `debug_log_format_with_level`) are unchanged.

Additionally, `debug_log_format_slice` has been removed. Use `debug_log_format` instead, which accepts a fixed-size array of fields:

```diff
- debug_log_format_slice("values: {}", &[value1, value2]);
+ debug_log_format("values: {}", [value1, value2]);
```

This has been done as usage of Noir slices is discouraged and the function was unused in the aztec codebase.

### \[AztecNode] Sentinel validator status values renamed

The `ValidatorStatusInSlot` values returned by `getValidatorsStats` and `getValidatorStats` have been updated to reflect the multi-block-per-slot model, where blocks and checkpoints are distinct concepts:

```diff
- 'block-mined'
+ 'checkpoint-mined'

- 'block-proposed'
+ 'checkpoint-proposed'

- 'block-missed'
+ 'checkpoint-missed'  // blocks were proposed but checkpoint was not attested
+ 'blocks-missed'      // no block proposals were sent at all
```

The `attestation-sent` and `attestation-missed` values are unchanged but now explicitly refer to checkpoint attestations.

The `ValidatorStatusType` used for categorizing statuses has also changed from `'block' | 'attestation'` to `'proposer' | 'attestation'`.

### \[aztec.js] `getDecodedPublicEvents` renamed to `getPublicEvents` with new signature

The `getDecodedPublicEvents` function has been renamed to `getPublicEvents` and now uses a filter object instead of positional parameters:

```diff
- import { getDecodedPublicEvents } from '@aztec/aztec.js/events';
+ import { getPublicEvents } from '@aztec/aztec.js/events';

- const events = await getDecodedPublicEvents(node, eventMetadata, fromBlock, limit);
+ const events = await getPublicEvents(node, eventMetadata, {
+   fromBlock,
+   toBlock,
+   contractAddress,  // optional
+   txHash,           // optional
+ });
```

The new function returns richer metadata including `contractAddress`, `txHash`, `l2BlockNumber`, and `l2BlockHash` for each event:

```typescript
import { getPublicEvents } from "@aztec/aztec.js/events";
import { MyContract } from "./artifacts/MyContract.js";

// Query events from a contract
const events = await getPublicEvents<{ amount: bigint; sender: AztecAddress }>(
  aztecNode,
  MyContract.events.Transfer,
  { contractAddress: myContractAddress, fromBlock: BlockNumber(1) },
);

// Each event includes decoded data and metadata
for (const { event, metadata } of events) {
  console.log(`Transfer of ${event.amount} from ${event.sender}`);
  console.log(`  Block: ${metadata.l2BlockNumber}, Tx: ${metadata.txHash}`);
  console.log(`  Contract: ${metadata.contractAddress}`);
}
```

### \[Aztec.nr] `nophasecheck` renamed as `allow_phase_change`

### \[AztecNode] Removed sibling path RPC methods

The following methods have been removed from the `AztecNode` interface:

* `getNullifierSiblingPath`
* `getNoteHashSiblingPath`
* `getArchiveSiblingPath`
* `getPublicDataSiblingPath`

These methods were not used by PXE and returned a subset of the information already available through the corresponding membership witness methods:

| Removed Method             | Use Instead                     |
| -------------------------- | ------------------------------- |
| `getNullifierSiblingPath`  | `getNullifierMembershipWitness` |
| `getNoteHashSiblingPath`   | `getNoteHashMembershipWitness`  |
| `getArchiveSiblingPath`    | `getBlockHashMembershipWitness` |
| `getPublicDataSiblingPath` | `getPublicDataWitness`          |

The membership witness methods return both the sibling path and additional context (leaf index, preimage data) needed for proofs.

### \[Protocol] "Nullifier secret key" renamed to "nullifier hiding key" (nsk → nhk)

The nullifier secret key (`nsk_m` / `nsk_app`) has been renamed to nullifier hiding key (`nhk_m` / `nhk_app`). This is a protocol-breaking change: the domain separator string changes from `"az_nsk_m"` to `"az_nhk_m"`, producing a different constant value.

**Noir changes:**

```diff
- context.request_nsk_app(npk_m_hash)
+ context.request_nhk_app(npk_m_hash)

- get_nsk_app(npk_m_hash)
+ get_nhk_app(npk_m_hash)
```

**TypeScript changes:**

```diff
- import { computeAppNullifierSecretKey, deriveMasterNullifierSecretKey } from '@aztec/stdlib/keys';
+ import { computeAppNullifierHidingKey, deriveMasterNullifierHidingKey } from '@aztec/stdlib/keys';

- const masterNullifierSecretKey = deriveMasterNullifierSecretKey(secret);
+ const masterNullifierHidingKey = deriveMasterNullifierHidingKey(secret);

- const nskApp = await computeAppNullifierSecretKey(masterNullifierSecretKey, contractAddress);
+ const nhkApp = await computeAppNullifierHidingKey(masterNullifierHidingKey, contractAddress);
```

The `GeneratorIndex.NSK_M` enum member is now `GeneratorIndex.NHK_M`.

### \[AztecNode/Aztec.nr] `getArchiveMembershipWitness` renamed to `getBlockHashMembershipWitness`

The `getArchiveMembershipWitness` method has been renamed to `getBlockHashMembershipWitness` to better reflect its purpose. Block hashes are the leaves of the archive tree - each time a new block is added to the chain, its block hash is appended as a new leaf. This rename clarifies that the method finds a membership witness for a block hash in the archive tree.

**TypeScript (AztecNode interface):**

```diff
- const witness = await aztecNode.getArchiveMembershipWitness(blockNumber, archiveLeaf);
+ const witness = await aztecNode.getBlockHashMembershipWitness(blockNumber, blockHash);
```

The second parameter type has also changed from `Fr` to `BlockHash`.

**Noir (aztec-nr):**

```diff
- use dep::aztec::oracle::get_membership_witness::get_archive_membership_witness;
+ use dep::aztec::oracle::get_membership_witness::get_block_hash_membership_witness;

- let witness = get_archive_membership_witness(block_header, leaf_value);
+ let witness = get_block_hash_membership_witness(anchor_block_header, block_hash);
```

### \[Aztec.nr] `protocol_types` renamed to `protocol`

The `protocol_types` re-export from the `aztec` crate has been renamed to `protocol`. Update all imports accordingly:

```diff
- use dep::aztec::protocol_types::address::AztecAddress;
+ use dep::aztec::protocol::address::AztecAddress;
```

### Protocol contract interface separate from protocol contracts

We've stripped protocol contract of `aztec-nr` macros in order for auditors to not need to audit them (protocol contracts are to be audited during the protocol circuits audit). This results in the nice Noir interface no longer being generated.

For context, this is the interface I am talking about:

```noir
let update_delay = self.view(MyContract::at(my_contract_address).my_fn());
```

where the macros generate the `MyContract` struct.

For this reason we've created place holder protocol contracts in `noir-projects/noir-contracts/contracts/protocol_interface` that still have these macros applied and hence you can use them to get the interface.

On your side all you need to do is update the dependency in `Nargo.toml`:

```diff
-instance_contract = { path = "../../protocol/contract_instance_registry" }
+instance_contract = { path = "../../protocol_interface/contract_instance_registry_interface" }
```

### \[aztec-nr] History module refactored to use standalone functions

The `aztec::history` module has been refactored to use standalone functions instead of traits. This changes the calling convention from method syntax to function syntax.

```diff
- use dep::aztec::history::note_inclusion::ProveNoteInclusion;
+ use dep::aztec::history::note::assert_note_existed_by;

let block_header = context.get_anchor_block_header();
- let confirmed_note = block_header.prove_note_inclusion(hinted_note);
+ let confirmed_note = assert_note_existed_by(block_header, hinted_note);
```

**Function name and module mapping:**

| Old (trait method)                                                | New (standalone function)                                            |
| ----------------------------------------------------------------- | -------------------------------------------------------------------- |
| `history::note_inclusion::prove_note_inclusion`                   | `history::note::assert_note_existed_by`                              |
| `history::note_validity::prove_note_validity`                     | `history::note::assert_note_was_valid_by`                            |
| `history::nullifier_inclusion::prove_nullifier_inclusion`         | `history::nullifier::assert_nullifier_existed_by`                    |
| `history::nullifier_inclusion::prove_note_is_nullified`           | `history::note::assert_note_was_nullified_by`                        |
| `history::nullifier_non_inclusion::prove_nullifier_non_inclusion` | `history::nullifier::assert_nullifier_did_not_exist_by`              |
| `history::nullifier_non_inclusion::prove_note_not_nullified`      | `history::note::assert_note_was_not_nullified_by`                    |
| `history::contract_inclusion::prove_contract_deployment`          | `history::deployment::assert_contract_bytecode_was_published_by`     |
| `history::contract_inclusion::prove_contract_non_deployment`      | `history::deployment::assert_contract_bytecode_was_not_published_by` |
| `history::contract_inclusion::prove_contract_initialization`      | `history::deployment::assert_contract_was_initialized_by`            |
| `history::contract_inclusion::prove_contract_non_initialization`  | `history::deployment::assert_contract_was_not_initialized_by`        |
| `history::public_storage::public_storage_historical_read`         | `history::storage::public_storage_historical_read`                   |

### \[Aztec.js] Transaction sending API redesign

The old chained `.send().wait()` pattern has been replaced with a single `.send(options)` call that handles both sending and waiting.

```diff
+ import { Contract, NO_WAIT } from '@aztec/aztec.js/contracts';

- const receipt = await contract.methods.transfer(recipient, amount).send().wait();

// Send now waits by default
+ const receipt = await contract.methods.transfer(recipient, amount).send({ from: sender });

// getTxHash() would confusingly send the transaction too
- const txHash = await contract.methods.transfer(recipient, amount).send().getTxHash();

// NO_WAIT to send the transaction and return TxHash immediately
+ const txHash = await contract.methods.transfer(recipient, amount).send({
+   from: sender,
+   wait: NO_WAIT
+ });
```

#### Deployment changes

The old `.send().deployed()` method has been removed. Deployments now return the contract instance by default, or you can request the full receipt with `returnReceipt: true`:

```diff
- const contract = await MyContract.deploy(wallet, ...args).send().deployed();
- const { contract, instance } = await MyContract.deploy(wallet, ...args).send().wait();

+ const contract = await MyContract.deploy(wallet, ...args).send({ from: deployer });

+ const { contract, instance } = await MyContract.deploy(wallet, ...args).send({
+   from: deployer,
+   wait: { returnReceipt: true },
+ });
```

#### Breaking changes to `Wallet` interface

`getTxReceipt()` has been removed from the interface.

`sendTx` method signature has changed to support the new wait behavior:

```diff
- sendTx(payload: ExecutionPayload, options: SendOptions): Promise<TxReceipt>

+ sendTx<W extends InteractionWaitOptions = undefined>(
+   payload: ExecutionPayload,
+   options: SendOptions<W>
+ ): Promise<SendReturn<W>>
```

#### Manual waiting with `waitForTx`

When using `NO_WAIT` to send transactions, you can manually wait for confirmation using the `waitForTx` utility:

```typescript
import { waitForTx } from "@aztec/aztec.js/node";

const txHash = await contract.methods.transfer(recipient, amount).send({
  from: sender,
  wait: NO_WAIT,
});

const receipt = await waitForTx(node, txHash, {
  timeout: 60000, // Optional: timeout in ms
  interval: 1000, // Optional: polling interval in ms
  dontThrowOnRevert: true, // Optional: return receipt even if tx reverted
});
```

### \[aztec-nr] Removal of intermediate modules

Lots of unnecessary modules have been removed from the API, making imports shorter. These are the modules that contain just a single struct, in which the module has the same name as the struct.

```diff
- use aztec::state_vars::private_mutable::PrivateMutable;
+ use aztec::state_vars::PrivateMutable;
```

Affected structs include all state variables, notes, contexts, messages, etc.

### \[L1 Contracts] Fee asset pricing direction inverted

The fee model now uses `ethPerFeeAsset` instead of the previous `feeAssetPerEth`. This change inverts how the exchange rate is represented: values now express how much ETH one fee asset (AZTEC) is worth, with 1e12 precision.

**Key changes:**

* `FeeHeader.feeAssetPerEth` → `FeeHeader.ethPerFeeAsset`
* `RollupConfigInput` now requires `initialEthPerFeeAsset` parameter at deployment
* Default value: `1e7` (0.00001 ETH per AZTEC)
* Valid range: `100` (1e-10 ETH/AZTEC) to `1e11` (0.1 ETH/AZTEC)

**New environment variable for node operators:**

* `AZTEC_INITIAL_ETH_PER_FEE_ASSET` - Sets the initial ETH per fee asset price with 1e12 precision

### \[L1 Contracts] Fee asset price modifier now in basis points

The `OracleInput.feeAssetPriceModifier` field now expects values in basis points (BPS) instead of the previous representation. The modifier is applied as a percentage change to the ETH/AZTEC price each checkpoint.

**Key changes:**

* Valid range: `-100` to `+100` BPS (±1% max change per checkpoint)
* A value of `+100` increases the price by 1%, `-100` decreases by 1%
* Validated by `MAX_FEE_ASSET_PRICE_MODIFIER_BPS = 100`

### \[Aztec.js] Wallet batching now supports all methods

The `BatchedMethod` type is now a discriminated union that ensures type safety: the `args` must match the specific method `name`. This prevents runtime errors from mismatched arguments.

```diff
- // Before: Only 5 methods could be batched
- const results = await wallet.batch([
-   { name: "registerSender", args: [address, "alias"] },
-   { name: "sendTx", args: [payload, options] },
- ]);
+ // After: All methods can be batched
+ const results = await wallet.batch([
+   { name: "getChainInfo", args: [] },
+   { name: "getContractMetadata", args: [contractAddress] },
+   { name: "registerSender", args: [address, "alias"] },
+   { name: "simulateTx", args: [payload, options] },
+   { name: "sendTx", args: [payload, options] },
+ ]);
```

### \[Aztec.js] Refactored `getContractMetadata` and `getContractClassMetadata` in Wallet

The contract metadata methods in the `Wallet` interface have been refactored to provide more granular information and avoid expensive round-trips.

**`ContractMetadata`:**

```diff
  {
-   contractInstance?: ContractInstanceWithAddress,
+   instance?: ContractInstanceWithAddress;  // Instance registered in the Wallet, if any
    isContractInitialized: boolean;          // Is the init nullifier onchain? (already there)
    isContractPublished: boolean;            // Has the contract been published? (already there)
+   isContractUpdated: boolean;              // Has the contract been updated?
+   updatedContractClassId?: Fr;             // If updated, the new class ID
  }
```

**`ContractClassMetadata`:**

This method loses the ability to request the contract artifact via the `includeArtifact` flag

```diff
  {
-   contractClass?: ContractClassWithId;
-   artifact?: ContractArtifact;
    isContractClassPubliclyRegistered: boolean;  // Is the class registered onchain?
+   isArtifactRegistered: boolean;               // Does the Wallet know about this artifact?
  }
```

* Removes expensive artifact/class transfers between wallet and app
* Separates PXE storage info (`instance`, `isArtifactRegistered`) from public chain info (`isContractPublished`, `isContractClassPubliclyRegistered`)
* Makes it easier to determine if actions like `registerContract` are needed

### \[Aztec.js] Removed `UnsafeContract` and protocol contract helper functions

The `UnsafeContract` class and async helper functions (`getFeeJuice`, `getClassRegistryContract`, `getInstanceRegistryContract`) have been removed. Protocol contracts are now accessed via auto-generated type-safe wrappers with only the ABI (no bytecode). Since PXE always has protocol contract artifacts available, importing and using these contracts from `aztec.js` is very lightweight and follows the same pattern as regular user contracts.

**Migration:**

```diff
- import { getFeeJuice, getClassRegistryContract, getInstanceRegistryContract } from '@aztec/aztec.js/contracts';
+ import { FeeJuiceContract, ContractClassRegistryContract, ContractInstanceRegistryContract } from '@aztec/aztec.js/protocol';

- const feeJuice = await getFeeJuice(wallet);
+ const feeJuice = FeeJuiceContract.at(wallet);
  await feeJuice.methods.check_balance(feeLimit).send().wait();

- const classRegistry = await getClassRegistryContract(wallet);
+ const classRegistry = ContractClassRegistryContract.at(wallet);
  await classRegistry.methods.publish(...).send().wait();

- const instanceRegistry = await getInstanceRegistryContract(wallet);
+ const instanceRegistry = ContractInstanceRegistryContract.at(wallet);
  await instanceRegistry.methods.publish_for_public_execution(...).send().wait();
```

**Note:** The higher-level utilities like `publishInstance`, `publishContractClass`, and `broadcastPrivateFunction` from `@aztec/aztec.js/deployment` are still available and unchanged. These utilities use the new wrappers internally.

### \[Aztec.nr] Renamed Router contract

`Router` contract has been renamed as `PublicChecks` contract. The name of the contract became stale as its use changed from routing public calls through it to simply having public functions that can be called by anyone. Having these "standard checks" on one contract results in a potentially large privacy set for apps that use it.

### \[Aztec Node] `getBlockByHash` and `getBlockHeaderByHash` removed

The `getBlockByHash` and `getBlockHeaderByHash` methods have been removed. Use `getBlock` and `getBlockHeader` with a block hash instead.

**Migration:**

```diff
- const block = await node.getBlockByHash(blockHash);
+ const block = await node.getBlock(blockHash);

- const header = await node.getBlockHeaderByHash(blockHash);
+ const header = await node.getBlockHeader(blockHash);
```

### \[Aztec.nr] Oracle functions now take `BlockHeader` instead of block number

The low-level oracle functions for fetching membership witnesses and storage now take a `BlockHeader` instead of a `block_number: u32`. This change improves type safety and ensures the correct block state is queried.

**Affected functions:**

* `get_note_hash_membership_witness(block_header, leaf_value)` - was `(block_number, leaf_value)`
* `get_archive_membership_witness(block_header, leaf_value)` - was `(block_number, leaf_value)`
* `get_nullifier_membership_witness(block_header, nullifier)` - was `(block_number, nullifier)`
* `get_low_nullifier_membership_witness(block_header, nullifier)` - was `(block_number, nullifier)`
* `get_public_data_witness(block_header, public_data_tree_index)` - was `(block_number, public_data_tree_index)`
* `storage_read(block_header, address, storage_slot)` - was `(address, storage_slot, block_number)`

**Migration:**

If you were calling these oracle functions directly (which is uncommon), update your code to pass a `BlockHeader` instead of a block number:

```diff
- let witness = get_note_hash_membership_witness(self.global_variables.block_number, note_hash);
+ let witness = get_note_hash_membership_witness(self, note_hash);

- let witness = get_nullifier_membership_witness(block_number, nullifier);
+ let witness = get_nullifier_membership_witness(block_header, nullifier);

- let value: T = storage_read(address, slot, block_number);
+ let value: T = storage_read(block_header, address, slot);
```

Note: The high-level history proof functions on `BlockHeader` (such as `prove_note_inclusion`, `prove_nullifier_inclusion`, etc.) are **not affected** by this change. They continue to work the same way.

### \[Toolchain] Node.js upgraded to v24

Node.js minimum version changed from v22 to v24.12.0.

### \[L1 Contracts] Renamed base fee to min fee

The L1 rollup contract functions and types related to fee calculation have been renamed from "base fee" to "min fee" to better reflect their purpose.

**Renamed functions:**

* `getManaBaseFeeAt` → `getManaMinFeeAt`
* `getManaBaseFeeComponentsAt` → `getManaMinFeeComponentsAt`

**Renamed types:**

* `ManaBaseFeeComponents` → `ManaMinFeeComponents`

**Renamed errors:**

* `Rollup__InvalidManaBaseFee` → `Rollup__InvalidManaMinFee`

**Migration:**

```diff
- uint256 fee = rollup.getManaBaseFeeAt(timestamp, true);
+ uint256 fee = rollup.getManaMinFeeAt(timestamp, true);

- ManaBaseFeeComponents memory components = rollup.getManaBaseFeeComponentsAt(timestamp, true);
+ ManaMinFeeComponents memory components = rollup.getManaMinFeeComponentsAt(timestamp, true);
```

### \[Aztec.js] Renamed base fee to min fee

The Aztec Node API method for getting current fees has been renamed:

* `getCurrentBaseFees` → `getCurrentMinFees`

**Migration:**

```diff
- const fees = await node.getCurrentBaseFees();
+ const fees = await node.getCurrentMinFees();
```

### \[Aztec.nr] Renamed fee context methods

The context methods for accessing fee information have been renamed:

* `context.base_fee_per_l2_gas()` → `context.min_fee_per_l2_gas()`
* `context.base_fee_per_da_gas()` → `context.min_fee_per_da_gas()`

**Migration:**

```diff
- let l2_fee = context.base_fee_per_l2_gas();
- let da_fee = context.base_fee_per_da_gas();
+ let l2_fee = context.min_fee_per_l2_gas();
+ let da_fee = context.min_fee_per_da_gas();
```

### \[Aztec.nr] Cleaning up message sender functions

There has been a design decision made to have low-level API exposed on `self.context` and a nicer higher-level API exposed directly on `self`. Currently the `msg_sender` function on `self` was a copy of that same function on `self.context`. The `msg_sender` function on `self` got modified to return the message sender address directly instead of having it be wrapped in an `Option<...>`. In case the underlying message sender is none the function panics.

You need to update your code to no longer trigger the unwrap on the return value:

```diff
- let message_sender: AztecAddress = self.msg_sender().unwrap();
+ let message_sender: AztecAddress = self.msg_sender();
```

If you want to handle the `null` case use the lower level API of context:

```diff
- let maybe_message_sender: Option<AztecAddress> = self.msg_sender();
+ let maybe_message_sender: Option<AztecAddress> = self.context.maybe_msg_sender();
```

The `self.context.msg_sender_unsafe` method has been dropped as its use can be replaced with the standard `self.context.maybe_msg_sender` function.

### \[Aztec.nr] Renamed message delivery options

The following terms have been renamed:

* `MessageDelivery::UNCONSTRAINED_OFFCHAIN` -> `MessageDelivery::OFFCHAIN`
* `MessageDelivery::UNCONSTRAINED_ONCHAIN` -> `MessageDelivery::ONCHAIN_UNCONSTRAINED`
* `MessageDelivery::CONSTRAINED_ONCHAIN` -> `MessageDelivery::ONCHAIN_CONSTRAINED`

We believe these names will better convey the meaning of the concepts.

### \[Aztec Node] changes to `getLogsByTags` endpoint

`getLogsByTags` endpoint has been optimized for our new log sync algorithm and these are the changes:

* The `logsPerTag` pagination argument has been removed. Pagination was unnecessary here, since multiple logs per tag typically only occur if several devices are sending logs from the same sender to a recipient, which is unlikely to generate enough logs to require pagination.
* The structure of `TxScopedL2Log` has been revised to meet the requirements of our new log sync algorithm.
* The endpoint has been separated into two versions: `getPrivateLogsByTags` and `getPublicLogsByTagsFromContract`. This change was made because it was never desirable in PXE to mix public and private logs. The public version requires both a `Tag` and a contract address as input. In contrast to the private version—which uses `SiloedTag` (a tag that hashes the raw tag with the emitting contract's address)—the public version uses the raw `Tag` type, since kernels do not hash the tag with the contract address for public logs.

### \[AVM] Gas cost multipliers for public execution to reach simulation/proving parity

Gas costs for several AVM opcodes have been adjusted with multipliers to better align public simulation costs with actual proving costs.

| Opcode              | Multiplier | Previous Cost | New Cost |
| ------------------- | ---------- | ------------- | -------- |
| FDIV                | 25x        | 9             | 225      |
| SLOAD               | 10x        | 129           | 1,290    |
| SSTORE              | 20x        | 1,657         | 33,140   |
| NOTEHASHEXISTS      | 4x         | 126           | 504      |
| EMITNOTEHASH        | 15x        | 1,285         | 19,275   |
| NULLIFIEREXISTS     | 7x         | 132           | 924      |
| EMITNULLIFIER       | 20x        | 1,540         | 30,800   |
| L1TOL2MSGEXISTS     | 5x         | 108           | 540      |
| SENDL2TOL1MSG       | 2x         | 209           | 418      |
| CALL                | 3x         | 3,312         | 9,936    |
| STATICCALL          | 3x         | 3,312         | 9,936    |
| GETCONTRACTINSTANCE | 4x         | 1,527         | 6,108    |
| POSEIDON2           | 15x        | 24            | 360      |
| ECADD               | 10x        | 27            | 270      |

**Impact**: Contracts with public bytecode performing any of these operations will see increased gas consumption.

### \[PXE] deprecated `getNotes`

This function serves only for debugging purposes so we are taking it out of the main PXE API. If you still need to consume it, you can do so through the new `debug` sub-module.

```diff
- this.pxe.getNotes(filter);
+ this.pxe.debug.getNotes(filter);
```

## 3.0.0-devnet.20251212

### \[Aztec node, archiver] Deprecated `getPrivateLogs`

Aztec node no longer offers a `getPrivateLogs` method. If you need to process the logs of a block, you can instead use `getBlock` and call `getPrivateLogs` on an `L2BlockNew` instance. See the diff below for before/after equivalent code samples.

```diff
-  const logs = await aztecNode.getPrivateLogs(blockNumber, 1);
+  const logs = (await aztecNode.getBlock(blockNumber))?.toL2Block().getPrivateLogs();
```

### \[Aztec.nr] Private event emission API changes

Private events are still emitted via the `emit` function, but this now returns an `EventMessage` type that must have `deliver_to` called on it in order to deliver the event message to the intended recipients. This allows for multiple recipients to receive the same event.

```diff
- self.emit(event, recipient, delivery_method)
+ self.emit(event).delivery(recipient, delivery_method)
```

### \[Aztec.nr] History proof functions no longer require `storage_slot` parameter

The `HintedNote` struct now includes a `storage_slot` field, making it self-contained for proving note inclusion and validity. As a result, the history proof functions in the `aztec::history` module no longer require a separate `storage_slot` parameter.

**Affected functions:**

* `BlockHeader::prove_note_inclusion` - removed `storage_slot: Field` parameter
* `BlockHeader::prove_note_validity` - removed `storage_slot: Field` parameter
* `BlockHeader::prove_note_is_nullified` - removed `storage_slot: Field` parameter
* `BlockHeader::prove_note_not_nullified` - removed `storage_slot: Field` parameter

**Migration:**

The `storage_slot` is now read from `hinted_note.storage_slot` internally. Simply remove the `storage_slot` argument from all calls to these functions:

```diff
  let header = context.get_anchor_block_header();
- header.prove_note_inclusion(hinted_note, storage_slot);
+ header.prove_note_inclusion(hinted_note);

  let header = context.get_anchor_block_header();
- header.prove_note_validity(hinted_note, storage_slot, context);
+ header.prove_note_validity(hinted_note, context);

  let header = context.get_anchor_block_header();
- header.prove_note_is_nullified(hinted_note, storage_slot, context);
+ header.prove_note_is_nullified(hinted_note, context);

  let header = context.get_anchor_block_header();
- header.prove_note_not_nullified(hinted_note, storage_slot, context);
+ header.prove_note_not_nullified(hinted_note, context);
```

### \[Aztec.nr] Note fields are now public

All note struct fields are now public, and the `new()` constructor methods and getter methods have been removed. Notes should be instantiated using struct literal syntax, and fields should be accessed directly.

The motivation for this change has been enshrining of randomness which lead to the `new` method being unnecessary boilerplate.

**Affected notes:**

* `UintNote` - `value` is now public, `new()` and `get_value()` removed
* `AddressNote` - `address` is now public, `new()` and `get_address()` removed
* `FieldNote` - `value` is now public, `new()` and `value()` removed

**Migration:**

```diff
- let note = UintNote::new(100);
+ let note = UintNote { value: 100 };

- let value = note.get_value();
+ let value = note.value;

- let address_note = AddressNote::new(owner);
+ let address_note = AddressNote { address: owner };

- let address = address_note.get_address();
+ let address = address_note.address;

- let field_note = FieldNote::new(42);
+ let field_note = FieldNote { value: 42 };

- let value = field_note.value();
+ let value = field_note.value;
```

### \[Aztec.nr] `emit` renamed to `deliver`

Private state variable functions that created notes and returned their messages no longer return a `NoteEmission` but instead a `NoteMessage`. These messages are delivered to their owner via `deliver` instead of `emit`. The verb 'emit' remains for things like emitting events.

```diff
- self.storage.balances.at(owner).add(5).emit(owner);
+ self.storage.balances.at(owner).add(5).deliver();
```

To deliver a message to a different recipient, use `deliver_to`:

```diff
- self.storage.balances.at(owner).add(5).emit(other);
+ self.storage.balances.at(owner).add(5).deliver_to(other);
```

### \[Aztec.nr] `ValueNote` renamed to `FieldNote` and `value-note` crate renamed to `field-note`

The `ValueNote` struct has been renamed to `FieldNote` to better reflect that it stores a `Field` value. The crate has also been renamed from `value-note` to `field-note`.

**Migration:**

* Update your `Nargo.toml` dependencies: `value_note = { path = "..." }` → `field_note = { path = "..." }`
* Update imports: `use value_note::value_note::ValueNote` → `use field_note::field_note::FieldNote`
* Update type references: `ValueNote` → `FieldNote`
* Update generic parameters: `PrivateSet<ValueNote, ...>` → `PrivateSet<FieldNote, ...>`

### \[Aztec.nr] New `balance-set` library for managing token balances

A new `balance-set` library has been created that provides `BalanceSet<Context>` for managing u128 token balances with `UintNote`. This consolidates balance management functionality that was previously duplicated across contracts.

**Features:**

* `add(amount: u128)` - Add to balance
* `sub(amount: u128)` - Subtract from balance (with change note)
* `try_sub(amount: u128, max_notes: u32)` - Attempt to subtract with configurable note limit
* `balance_of()` - Get total balance (unconstrained)

**Usage:**

```rust
use balance_set::BalanceSet;

#[storage]
struct Storage<Context> {
    balances: Owned<BalanceSet<Context>, Context>,
}

// In a private function:
self.storage.balances.at(owner).add(amount).deliver(owner, MessageDelivery.CONSTRAINED_ONCHAIN);
self.storage.balances.at(owner).sub(amount).deliver(owner, MessageDelivery.CONSTRAINED_ONCHAIN);

// In an unconstrained function:
let balance = self.storage.balances.at(owner).balance_of();
```

### \[Aztec.nr] `EasyPrivateUint` deprecated and removed

The `EasyPrivateUint` type and `easy-private-state` crate have been deprecated and removed. Use `BalanceSet` from the `balance-set` crate instead.

**Migration:**

* Remove `easy_private_state` dependency from `Nargo.toml`

* Add `balance_set = { path = "../../../../aztec-nr/balance-set" }` to `Nargo.toml`

* Update storage: `EasyPrivateUint<Context>` → `Owned<BalanceSet<Context>, Context>`

* Update method calls:

  <!-- -->

  * `add(amount, owner)` → `at(owner).add(amount).deliver(owner, MessageDelivery.CONSTRAINED_ONCHAIN)`
  * `sub(amount, owner)` → `at(owner).sub(amount).deliver(owner, MessageDelivery.CONSTRAINED_ONCHAIN)`
  * `get_value(owner)` → `at(owner).balance_of()` (returns `u128` instead of `Field`)

### \[Aztec.nr] `balance_utils` removed from `value-note` (now `field-note`)

The `balance_utils` module has been removed from the `field-note` crate (formerly `value-note`). If you need similar functionality, implement it locally in your contract or use `BalanceSet` for u128 balances.

### \[Aztec.nr] `filter_notes_min_sum` removed from `value-note` (now `field-note`)

The `filter_notes_min_sum` function has been removed from the `field-note` crate (formerly in `value-note`). If you need this functionality, copy it to your contract locally. This function was only used in specific test contracts and doesn't belong in the general-purpose note library.

### \[Aztec.nr] `derive_ecdh_shared_secret_using_aztec_address` removed

This function made it annoying to deal with invalid addresses in circuits. If you were using it, replace it with `derive_ecdh_shared_secret` instead:

```diff
-let shared_secret = derive_ecdh_shared_secret_using_aztec_address(secret, address).unwrap();
+let shared_secret = derive_ecdh_shared_secret(secret, address.to_address_point().unwrap().inner);
```

### \[Aztec.nr] Note owner is now enshrined

It turns out that in all cases a note always has a logical owner. For this reason we have decided to enshrine the concept of a note owner and you should drop the field from your note:

```diff
#[derive(Deserialize, Eq, Packable, Serialize)]
#[note]
pub struct ValueNote {
    value: Field,
-    owner: AztecAddress,
}
```

The owner being enshrined means that our API explicitly expects it on the input. The `NoteHash` trait got modified as follows:

```diff
pub trait NoteHash {
    fn compute_note_hash(
        self,
+        owner: AztecAddress,
        storage_slot: Field,
        randomness: Field,
    ) -> Field;

    fn compute_nullifier(
        self,
        context: &mut PrivateContext,
+        owner: AztecAddress,
        note_hash_for_nullification: Field,
    ) -> Field;

    unconstrained fn compute_nullifier_unconstrained(
        self,
+        owner: AztecAddress,
        note_hash_for_nullification: Field,
    ) -> Field;
}
```

Our low-level note utilities now also accept owner as a parameter:

```diff
pub fn create_note<Note>(
    context: &mut PrivateContext,
+    owner: AztecAddress,
    storage_slot: Field,
    note: Note,
) -> NoteEmission<Note>
where
    Note: NoteType + NoteHash + Packable,
{
...
}
```

Signature of some functions like `destroy_note_unsafe` is unchanged:

```diff
pub fn destroy_note_unsafe<Note>(
    context: &mut PrivateContext,
    hinted_note: HintedNote<Note>,
    note_hash_read: NoteHashRead,
)
where
    Note: NoteHash,
{
...
}
```

because `HintedNote` now contains owner.

`PrivateImmutable`, `PrivateMutable` and `PrivateSet` got modified to directly contain the owner instead of implicitly "containing it" by including it in the storage slot via a `Map`. These state variables now implement a newly introduced `OwnedStateVariable` trait (see docs of `OwnedStateVariable` for explanation of what it is). These changes make the state variables incompatible with `Map` and now instead these should be wrapped in new `Owned` state variable:

```diff
#[storage]
struct Storage<Context> {
-    private_nfts: Map<AztecAddress, PrivateSet<NFTNote, Context>, Context>,
+    private_nfts: Owned<PrivateSet<NFTNote, Context>, Context>,
}
```

Note that even though the types of your state variables are changing from `Map<AztecAddress, T, Context>` to `Owned<T, Context>`, usage remains unchanged:

```text
let nft_notes = self.storage.private_nfts.at(from).pop_notes(NoteGetterOptions::new().select(NFTNote::properties().token_id, Comparator.EQ, token_id).set_limit(1));
```

With this change the underlying notes will inherit the storage slot of the `Owned` state variable. This is unlike `Map` where the nested state variable got the storage slot computed as `hash([map_storage_slot, key])`.

if you had `PrivateImmutable` or `PrivateMutable` defined out of a `Map`, e.g.:

```rust
#[storage]
struct Storage<Context> {
    signing_public_key: PrivateImmutable<PublicKeyNote, Context>,
}
```

you were most likely dealing with some kind of admin flow where only the admin can modify the state variable. Now, unfortunately, there is a bit of a regression and you will need to wrap the state variable in `Owned` and call `at` on the state var:

```diff
+ use aztec::state_vars::Owned;

#[storage]
struct Storage<Context> {
-   signing_public_key: PrivateImmutable<PublicKeyNote, Context>,
+   signing_public_key: Owned<PrivateImmutable<PublicKeyNote, Context>, Context>,
}

#[external("private")]
fn my_external_function() {
-   self.storage.signing_public_key.initialize(pub_key_note)
+   self.storage.signing_public_key.at(self.address).initialize(pub_key_note)
        .emit(self.address, MessageDelivery.CONSTRAINED_ONCHAIN);
}
```

We are likely to come up with a concept of admin state variables in the future.

None of the reference notes now contain the owner so if you manually construct `AddressNote`, `UintNote` or `ValueNote` you need to update the call to `new` method:

```diff
- let note = UintNote::new(156, owner);
+ let note = UintNote::new(156);
```

### \[Aztec.nr] Note randomness is now handled internally

In order to prevent pre-image attacks, it is necessary to inject randomness to notes. Aztec.nr users were previously expected to add said randomness to their custom note types. From now on, Aztec.nr takes care of handling randomness as built-in note metadata, making it impossible to miss for library users. This change breaks backwards compatibility as we'll discuss below.

#### Changes to Aztec.nr note types

If you're using any of the following note types, please be aware that `randomness` no longer is an explicit attribute in them.

* ValueNote
* UintNote
* NFTNote
* AddressNote

#### Migrating your custom note types: refer to UintNote as an example of how to migrate

We show the changes to `UintNote` below since it serves as a good example of the adjustments you will need to make to your own custom note types, including those that need to support partial notes.

##### Remove `randomness` from note struct

```diff
pub struct UintNote {
    /// The owner of the note, i.e. the account whose nullifier secret key is required to compute the nullifier.
    owner: AztecAddress,
-   /// Random value, protects against note hash preimage attacks.
-   randomness: Field,
    /// The number stored in the note.
    value: u128,
}

impl UintNote {
    pub fn new(value: u128, owner: AztecAddress) -> Self {
-       let randomness = unsafe { random() };
-       Self { value, owner, randomness }
+       Self { value, owner }
    }
```

##### Add `randomness` to `compute_note_hash` implementation

The `NoteHash` trait now requires `compute_note_hash` to receive a `randomness` field. This impacts

```diff
pub trait NoteHash {
    /// ...
-   fn compute_note_hash(self, storage_slot: Field) -> Field;
+   fn compute_note_hash(self, storage_slot: Field, randomness: Field) -> Field;
```

Then in trait implementations:

```diff
impl NoteHash for UintNote {
-   fn compute_note_hash(self, storage_slot: Field) -> Field {
+   fn compute_note_hash(self, storage_slot: Field, randomness: Field) -> Field {
    /// ...
-   let private_content =
-       UintPartialNotePrivateContent { owner: self.owner, randomness: self.randomness };
-   let partial_note = PartialUintNote {
-        commitment: private_content.compute_partial_commitment(storage_slot),
-   };

+   let private_content =
+       UintPartialNotePrivateContent { owner: self.owner };
+   let partial_note = PartialUintNote {
+        commitment: private_content.compute_partial_commitment(storage_slot, randomness),
+   };
```

It's worth noting that this change also affects how partial notes are structured and handled.

```diff
pub fn partial(
        owner: AztecAddress,
        storage_slot: Field,
        randomness: Field,
        context: &mut PrivateContext,
        recipient: AztecAddress,
        completer: AztecAddress,
    ) -> PartialUintNote {
-   let commitment = UintPartialNotePrivateContent { owner, randomness }
-       .compute_partial_commitment(storage_slot);
+   let commitment = UintPartialNotePrivateContent { owner }
+       .compute_partial_commitment(storage_slot, randomness);

    let private_log_content =
-       UintPartialNotePrivateLogContent { owner, randomness, public_log_tag: commitment };
+       UintPartialNotePrivateLogContent { owner, public_log_tag: commitment };
        let encrypted_log = note::compute_partial_note_private_content_log(
            private_log_content,
            storage_slot,
+           randomness,
            recipient,
        );
    /// ...
}

struct UintPartialNotePrivateContent {
    owner: AztecAddress,
-   randomness: Field,
}

impl UintPartialNotePrivateContent {
-   fn compute_partial_commitment(self, storage_slot: Field) -> Field {
+   fn compute_partial_commitment(self, storage_slot: Field, randomness: Field) -> Field {
        poseidon2_hash_with_separator(
-           self.pack().concat([storage_slot]),
+           self.pack().concat([storage_slot, randomness]),
            DOM_SEP__NOTE_HASH,
        )
    }
}

struct UintPartialNotePrivateLogContent {
    public_log_tag: Field,
    owner: AztecAddress,
-   randomness: Field,
}
```

##### Note size

As a result of this change, the maximum packed length of the content of a note is 11 fields, down from 12. This is a direct consequence of moving the randomness field from the note content structure to the note's metadata.

#### HintedNote now includes randomness field

```diff
pub struct HintedNote<Note> {
    pub note: Note,
    pub contract_address: AztecAddress,
+   pub randomness: Field,
    pub metadata: NoteMetadata,
}
```

### \[L1 Contracts] `Block` is now `Checkpoint`

A `checkpoint` is now the primary unit handled by the L1 contracts.

A checkpoint may contain one or more L2 blocks. The protocol circuits already support producing multiple blocks per checkpoint. Updating the L1 contracts to operate on checkpoints allow L2 blockchain to advance faster.

Below are the API and event renames reflecting this change:

```diff
- event L2BlockProposed
+ event CheckpointProposed
```

```diff
- event BlockInvalidated
+ event CheckpointInvalidated
```

```diff
- function getEpochForBlock(uint256 _blockNumber) external view returns (Epoch);
+ function getEpochForCheckpoint(uint256 _checkpointNumber) external view returns (Epoch);
```

```diff
- function getProvenBlockNumber() external view returns (uint256);
+ function getProvenCheckpointNumber() external view returns (uint256);
```

```diff
- function getPendingBlockNumber() external view returns (uint256);
+ function getPendingCheckpointNumber() external view returns (uint256);
```

```diff
- function getBlock(uint256 _blockNumber) external view returns (BlockLog memory);
+ function getCheckpoint(uint256 _checkpointNumber) external view returns (CheckpointLog memory);
```

```diff
- function getBlockReward() external view returns (uint256);
+ function getCheckpointReward() external view returns (uint256);
```

Additionally, any function or struct that previously referenced an L2 block number now uses a checkpoint number instead:

```diff
- function status(uint256 _blockNumber) external view returns (
+ function status(uint256 _checkpointNumber) external view returns (
-    uint256 provenBlockNumber,
+    uint256 provenCheckpointNumber,
     bytes32 provenArchive,
-    uint256 pendingBlockNumber,
+    uint256 pendingCheckpointNumber,
     bytes32 pendingArchive,
     bytes32 archiveOfMyBlock,
     Epoch provenEpochNumber
);
```

Note: current node softwares still produce exactly one L2 block per checkpoint, so for now checkpoint numbers and L2 block numbers remain equal. This may change once multi-block checkpoints are enabled.

### \[L1 Contracts] L2-to-L1 messages are now grouped by epoch.

L2-to-L1 messages are aggregated and organized per epoch rather than per block, but callers should now ask the node to compute the membership witness directly from the emitting transaction hash. The node resolves the epoch and Outbox root internally.

**Note**: This is only an API change. The protocol behavior remains the same - messages can still only be consumed once an epoch is proven as before.

#### What changed

Previously, callers fetched epoch messages and computed the witness client-side. Now, call `getL2ToL1MembershipWitness` on the node:

```typescript
const witness = await node.getL2ToL1MembershipWitness(
  l2TxReceipt.txHash,
  l2ToL1Message,
);
```

### \[Aztec.js] Wallet interface changes

#### `simulateTx` is now batchable

The `simulateTx` method on the `Wallet` interface is now batchable, meaning it can be called as part of a batch operation using `wallet.batch()`. This allows you to batch simulations together with other wallet operations like `registerContract`, `sendTx`, and `registerSender`.

```diff
- // Could not batch simulations
- const simulationResult = await wallet.simulateTx(executionPayload, options);
+ // Can now batch simulations with other operations
+ const results = await wallet.batch([
+   { name: 'registerContract', args: [instance, artifact] },
+   { name: 'simulateTx', args: [executionPayload, options] },
+   { name: 'sendTx', args: [anotherPayload, sendOptions] },
+ ]);
```

#### `ExecutionPayload` moved to `@aztec/stdlib/tx`

The `ExecutionPayload` type has been moved from `@aztec/aztec.js` to `@aztec/stdlib/tx`. Update your imports accordingly.

```diff
- import { ExecutionPayload } from '@aztec/aztec.js';
+ import { ExecutionPayload } from '@aztec/stdlib/tx';
+ // Or import from the re-export in aztec.js/tx:
+ import { ExecutionPayload } from '@aztec/aztec.js/tx';
```

#### `ExecutionPayload` now includes `feePayer` property

The `ExecutionPayload` class now includes an optional `feePayer` property that specifies which address is paying for the fee in the execution payload (if any)

```diff
  const payload = new ExecutionPayload(
    calls,
    authWitnesses,
    capsules,
    extraHashedArgs,
+   feePayer // optional AztecAddress
  );
```

This was previously provided as part of the `SendOptions` (and others) in the wallet interface, which could cause problems if a payload was assembled with a payment method and the parameter was later omitted. This means `SendOptions` now loses `embeddedPaymentMethodFeePayer`

```diff
-wallet.simulateTx(executionPayload, { from: address, embeddedFeePaymentMethodFeePayer: feePayer });
+wallet.simulateTx(executionPayload, { from: address });
```

#### `simulateUtility` signature and return type changed

The `simulateUtility` method signature has changed to accept a `FunctionCall` object instead of separate `functionName`, `args`, and `to` parameters. Additionally, the return type has changed from `AbiDecoded` to `Fr[]`.

```diff
- const result: AbiDecoded = await wallet.simulateUtility(functionName, args, to, authWitnesses);
+ const result: UtilitySimulationResult = await wallet.simulateUtility(functionCall, authWitnesses?);
+ // result.result is now Fr[] instead of AbiDecoded
```

The new signature takes:

* `functionCall`: A `FunctionCall` object containing `name`, `args`, `to`, `selector`, `type`, `isStatic`, `hideMsgSender`, and `returnTypes`
* `authWitnesses` (optional): An array of `AuthWitness` objects

The first argument is exactly the same as what goes into `ExecutionPayload.calls`. As such, the data is already encoded. The return value is now `UtilitySimulationResult` with `result: Fr[]` instead of returning an `AbiDecoded` value directly. You'll need to decode the `Fr[]` array yourself if you need typed results.

#### `Contract.at()` is now synchronous and no longer calls `registerContract`

The `Contract.at()` method (and generated contract `.at()` methods) is now synchronous and no longer automatically registers the contract with the wallet. This reduces unnecessary artifact storage and RPC calls.

```diff
- const contract = await TokenContract.at(address, wallet);
+ const contract = TokenContract.at(address, wallet);
```

**Important:** You now need to explicitly call `registerContract` if you want the wallet to store the contract instance and artifact. This is only necessary when:

* An app first registers a contract
* An app tries to update a contract's artifact

If you need to register the contract, do so explicitly:

```typescript
// Get the instance from deployment
const { contract, instance } = await TokenContract.deploy(wallet, ...args)
  .send({ from: address })
  .wait();

// wallet already has it registered, since the deploy method does it by default
// to avoid it, set skipContractRegistration: true in the send options.

// Register it with another wallet
await otherWallet.registerContract(instance, TokenContract.artifact);

// Now you can use the contract
const otherContract = TokenContract.at(instance.address, otherWallet);
```

Publicly deployed contract instances can be retrieved via `node.getContract(address)`. Otherwise and if deployment parameters are known, an instance can be computed via the `getContractInstanceFromInstantiationParams` from `@aztec/aztec.js/contracts`

#### `registerContract` signature simplified

The `registerContract` method now takes a `ContractInstanceWithAddress` instead of a `Contract` object, and the `artifact` parameter is now optional. If the artifact is not provided, the wallet will attempt to look it up from its contract class storage.

```diff
- await wallet.registerContract(contract);
+ await wallet.registerContract(instance, artifact?);
```

The method now only accepts:

* `instance`: A `ContractInstanceWithAddress` object
* `artifact` (optional): A `ContractArtifact` object
* `secretKey` (optional): A secret key for privacy keys registration

#### Return value of `getNotes` no longer contains a recipient and it contains some other additional info

Return value of `getNotes` used to be defined as `Promise<UniqueNote[]>` and is now defined as `Promise<NoteDao[]>`. `NoteDao` is mostly a super-set of `UniqueNote` but it doesn't contain a `recipient`. Having the recipient in the return value has been redundant as the same outcome can be achieved by populating the `scopes` array in `NoteFilter` with the `recipient` value.

#### Changes to `getPrivateEvents`

The signature of `getPrivateEvents` has changed for two reasons:

1. To align it with how other query methods that include filtering by block range work (for example, `AztecNode#getPublicLogs`)
2. To enrich the returned private events with metadata.

```diff
getPrivateEvents<T>(
-    contractAddress: AztecAddress,
-    eventMetadata: EventMetadataDefinition,
-    from: number,
-    numBlocks: number,
-    recipients: AztecAddress[],
-  ): Promise<T[]>;
+    eventFilter: PrivateEventFilter,
+  ): Promise<PrivateEvent<T>[]>;
```

`PrivateEvent<T>` bundles together an ABI decoded event of type `T`, with `metadata` of type `InTx`:

```ts
export type InBlock = {
  l2BlockNumber: BlockNumber;
  l2BlockHash: L2BlockHash;
};

export type InTx = InBlock & {
  txHash: TxHash;
};

export type PrivateEvent<T> = {
  event: T;
  metadata: InTx;
};
```

You will need to update any calls to `Wallet#getPrivateEvents` accordingly. See below for before/after comparison which conserves semantics.

Pay special attention to the fact that the old method expects a `numBlocks` parameter that instructs it to return `numBlocks` blocks after `fromBlock`, whereas the new version expects an (exclusive) `toBlock` block number.

Also note we're replacing *recipient* terminology with *scope*. While underlying data types are equivalent (they are Aztec addresses), they have different semantics. Messages have a recipient who will be able to receive and process them. As a result of processing messages for a given recipient address, PXE might discover events. Those events are then said to be *in scope* for that address.

```diff
- const events = await context.client.getPrivateEvents(contractAddress, eventMetadata, 42, 10, [recipient]);
- doSomethingWithAnEvent(events[0]);

+ const events = await context.client.getPrivateEvents(eventMetadata, {
+   contractAddress,
+   fromBlock: BlockNumber(42),
+   toBlock: BlockNumber(42 + 10),
+   scopes: [scope],
+ });
+ doSomethingWithAnEvent(events[0].event);
```

Please refer to the wallet interface js-docs for further details.

### \[CLI] Command refactor

The sandbox command has been renamed and remapped to "local network". We believe this conveys better what is actually being spun up when running it.

**REMOVED/RENAMED**:

* `aztec start --sandbox`: now `aztec start --local-network`

### \[Aztec.nr] - Contract API redesign

In this release we decided to largely redesign our contract API. Most of the changes here are not a breaking change (only renaming of original `#[internal]` to `#[only_self]` and `storage` now being available on the newly introduced `self` struct are a breaking change).

#### 1. Renaming of original #\[internal] as #\[only\_self]

We want for internal to mean the same as in Solidity where internal function can be called only from the same contract and is also inlined (EVM JUMP opcode and not EVM CALL). The original implementation of our `#[internal]` macro also results in the function being callable only from the same contract but it results in a different call (hence it doesn't map to EVM JUMP). This is very confusing for people that know Solidity hence we are doing the rename. A true `#[internal]` will be introduced in the future.

To migrate your contracts simply rename all the occurrences of `#[internal]` with `#[only_self]` and update the imports:

```diff
- use aztec::macros::functions::internal;
+ use aztec::macros::functions::only_self;
```

```diff
#[external("public")]
- #[internal]
+ #[only_self]
fn _deduct_public_balance(owner: AztecAddress, amount: u64) {
    ...
}
```

#### 2. Introducing of new #\[internal]

Same as in Solidity internal functions are functions that are callable from inside the contract. Unlike #\[only\_self] functions, internal functions are inlined (e.g. akin to EVM's JUMP and not EVM's CALL).

Internal function can be called using the following API which leverages the new `self` struct (see change 3 below for details):

```noir
self.internal.my_internal_function(...)
```

Private internal functions can only be called from other private external or internal functions. Public internal functions can only be called from other public external or internal functions.

#### 3. Introducing `self` in contracts and a new call interface

Aztec contracts now automatically inject a `self` parameter into every contract function, providing a unified interface for accessing the contract's address, storage, calling of function and an execution context.

##### What is `self`?

`self` is an instance of `ContractSelf<Context, Storage>` that provides:

* `self.address` - The contract's own address
* `self.storage` - Access to your contract's storage
* `self.context` - The execution context (private, public, or utility)
* `self.msg_sender()` - Get the address of the caller
* `self.emit(...)` - Emit events
* `self.call(...)` - Call an external function
* `self.view(...)` - Call an external function statically
* `self.enqueue(...)` - Enqueue a call to an external function
* `self.enqueue_view(...)` - Enqueue a call to an external function
* `self.enqueue_incognito(...)` - Enqueue a call to an external function but hides the `msg_sender`
* `self.enqueue_view_incognito(...)` - Enqueue a static call to an external function but hides the `msg_sender`
* `self.set_as_teardown(...)` - Enqueue a call to an external public function and sets the call as teardown
* `self.set_as_teardown_incognito(...)` - Enqueue a call to an external public function and sets the call as teardown and hides the `msg_sender`
* `self.internal.my_internal_fn(...)` - Call an internal function

`self` also provides you with convenience API to call and enqueue calls to external functions from within the same contract (this is just a convenience API as `self.call(MyContract::at(self.address).my_external_fn(...))` would also work):

* `self.call_self.my_external_fn(...)` - Call external function from within the same contract
* `self.enqueue_self.my_public_external_fn(...)`
* `self.call_self_static.my_static_external_fn(...)`
* `self.enqueue_self_static.my_static_external_public_fn(...)`

##### How it works

The `#[external(...)]` macro automatically injects `self` into your function. When you write:

```noir
#[external("private")]
fn transfer(amount: u128, recipient: AztecAddress) {
    let sender = self.msg_sender().unwrap();
    self.storage.balances.at(sender).sub(amount);
    self.storage.balances.at(recipient).add(amount);
}
```

The macro transforms it to initialize `self` with the context and storage before your code executes.

##### Migration guide

**Before:** Access context and storage as separate parameters

```noir
#[external("private")]
fn old_transfer(amount: u128, recipient: AztecAddress) {
    let storage = Storage::init(context);
    let sender = context.msg_sender().unwrap();
    storage.balances.at(sender).sub(amount);
}
```

**After:** Use `self` to access everything

```noir
#[external("private")]
fn new_transfer(amount: u128, recipient: AztecAddress) {
    let sender = self.msg_sender().unwrap();
    self.storage.balances.at(sender).sub(amount);
}
```

##### Key changes

1. **Storage and context access:**

Storage and context are no longer injected into the function as standalone variables and instead you need to access them via `self`:

```diff
- let balance = storage.balances.at(owner).read();
+ let balance = self.storage.balances.at(owner).read();
```

```diff
- context.push_nullifier(nullifier);
+ self.context.push_nullifier(nullifier);
```

Note that `context` is expected to be use only when needing to access a low-level API (like directly emitting a nullifier).

2. **Getting caller address:** Use `self.msg_sender()` instead of `context.msg_sender()`

   ```diff
   - let caller = context.msg_sender().unwrap();
   + let caller = self.msg_sender().unwrap();
   ```

3. **Getting contract address:** Use `self.address` instead of `context.this_address()`

   ```diff
   - let this_contract = context.this_address();
   + let this_contract = self.address;
   ```

4. **Emitting events:**

   In private functions:

   ```diff
   - emit_event_in_private(event, context, recipient, delivery_mode);
   + self.emit(event, recipient, delivery_mode);
   ```

   In public functions:

   ```diff
   - emit_event_in_public(event, context);
   + self.emit(event);
   ```

5. **Calling functions:**

   In private functions:

   ```diff
   - Token::at(stable_coin).mint_to_public(to, amount).call(&mut context);
   + self.call(Token::at(stable_coin).mint_to_public(to, amount));
   ```

##### Example: Full contract migration

**Before:**

```noir
#[external("private")]
fn withdraw(amount: u128, recipient: AztecAddress) {
    let storage = Storage::init(context);
    let sender = context.msg_sender().unwrap();
    let token = storage.donation_token.get_note().get_address();

    // ... withdrawal logic

    emit_event_in_private(Withdraw { withdrawer, amount }, context, withdrawer, MessageDelivery.UNCONSTRAINED_ONCHAIN);
}
```

**After:**

```noir
#[external("private")]
fn withdraw(amount: u128, recipient: AztecAddress) {
    let sender = self.msg_sender().unwrap();
    let token = self.storage.donation_token.get_note().get_address();

    // ... withdrawal logic

    self.emit(Withdraw { withdrawer, amount }, withdrawer, MessageDelivery.UNCONSTRAINED_ONCHAIN);
}
```

#### No-longer allowing calling of non-view function statically via the old higher-level API

We used to allow calling of non-view function statically as follows:

```noir
MyContract::at(address).my_non_view_function(...).view(context);
MyContract::at(address).my_non_view_function(...).enqueue_view(context);
```

This is no-longer allowed and if you will want to call a function statically you will need to mark the function with `#[view]`.

### Phase checks

Now private external functions check by default that no phase change from non revertible to revertible happens during the execution of the function or any of its nested calls. If you're developing a function that handles phase change (you call `context.end_setup()` or call a function that you expect will change phase) you need to opt out of the phase check using the `#[nophasecheck]` macro. Also, now it's possible to know if you're in the revertible phase of the transaction at any point using `self.context.in_revertible_phase()`.

### \[`aztec` command] Moving functionality of `aztec-nargo` to `aztec` command

`aztec-nargo` has been deprecated and all workflows should now migrate to the `aztec` command that fully replaces `aztec-nargo`:

* **For contract initialization:**

  ```bash
  aztec init
  ```

  (Behaves like `nargo init`, but defaults to a contract project.)

* **For testing:**

  ```bash
  aztec test
  ```

  (Starts the Aztec TXE and runs your tests.)

* **For compiling contracts:**

  ```bash
  aztec compile
  ```

  (Transpiles your contracts and generates verification keys.)

## 3.0.0-devnet.4

## \[aztec.js] Removal of barrel export

`aztec.js` is now divided into granular exports, which improves loading performance in node.js and also makes the job of web bundlers easier:

```diff
-import { AztecAddress, Fr, getContractInstanceFromInstantiationParams, type Wallet } from '@aztec/aztec.js';
+import { AztecAddress } from '@aztec/aztec.js/addresses';
+import { getContractInstanceFromInstantiationParams } from '@aztec/aztec.js/contracts';
+import { Fr } from '@aztec/aztec.js/fields';
+import type { Wallet } from '@aztec/aztec.js/wallet';
```

Additionally, some general utilities reexported from `foundation` have been removed:

```diff
-export { toBigIntBE } from '@aztec/foundation/bigint-buffer';
-export { sha256, Grumpkin, Schnorr } from '@aztec/foundation/crypto';
-export { makeFetch } from '@aztec/foundation/json-rpc/client';
-export { retry, retryUntil } from '@aztec/foundation/retry';
-export { to2Fields, toBigInt } from '@aztec/foundation/serialize';
-export { sleep } from '@aztec/foundation/sleep';
-export { elapsed } from '@aztec/foundation/timer';
-export { type FieldsOf } from '@aztec/foundation/types';
-export { fileURLToPath } from '@aztec/foundation/url';
```

### `getSenders` renamed to `getAddressBook` in wallet interface

An app could request "contacts" from the wallet, which don't necessarily have to be senders in the wallet's PXE. This method has been renamed to reflect that fact:

```diff
-wallet.getSenders();
+wallet.getAddressBook();
```

### Removal of `proveTx` from `Wallet` interface

Exposing this method on the interface opened the door for certain types of attacks, were an app could route proven transactions through malicious nodes (that stored them for later decryption, or collected user IPs for example). It also made transactions difficult to track for the wallet, since they could be sent without their knowledge at any time. This change also affects `ContractFunctionInteraction` and `DeployMethod`, which no longer expose a `prove()` method.

### `msg_sender` is now an `Option<AztecAddress>` type.

Because Aztec has native account abstraction, the very first function call of a tx has no `msg_sender`. (Recall, the first function call of an Aztec transaction is always a *private* function call).

Previously (before this change) we'd been silently setting this first `msg_sender` to be `AztecAddress::from_field(-1);`, and enforcing this value in the protocol's kernel circuits. Now we're passing explicitness to smart contract developers by wrapping `msg_sender` in an `Option` type. We'll explain the syntax shortly.

We've also added a new protocol feature. Previously (before this change) whenever a public function call was enqueued by a private function (a so-called private->public call), the called public function (and hence the whole world) would be able to see `msg_sender`. For some use cases, visibility of `msg_sender` is important, to ensure the caller executed certain checks in private-land. For `#[only_self]` public functions, visibility of `msg_sender` is unavoidable (the caller of an `#[only_self]` function must be the same contract address by definition). But for *some* use cases, a visible `msg_sender` is an unnecessary privacy leakage. We therefore have added a feature where `msg_sender` can be optionally set to `Option<AztecAddress>::none()` for enqueued public function calls (aka private->public calls). We've been colloquially referring to this as "setting msg\_sender to null".

#### Aztec.nr diffs

> Note: we'll be doing another pass at this aztec.nr syntax in the near future.

Given the above, the syntax for accessing `msg_sender` in Aztec.nr is slightly different:

For most public and private functions, to adjust to this change, you can make this change to your code:

```diff
- let sender: AztecAddress = context.msg_sender();
+ let sender: AztecAddress = context.msg_sender().unwrap();
```

Recall that `Option::unwrap()` will throw if the Option is "none".

Indeed, most smart contract functions will require access to a proper contract address (instead of a "null" value), in order to do bookkeeping (allocation of state variables against user addresses), and so in such cases throwing is sensible behaviour.

If you want to output a useful error message when unwrapping fails, you can use `Option::expect`:

```diff
- let sender: AztecAddress = context.msg_sender();
+ let sender: AztecAddress = context.msg_sender().expect(f"Sender must not be none!");
```

For a minority of functions, a "null" msg\_sender will be acceptable:

* A private entrypoint function.
* A public function which doesn't seek to do bookkeeping against `msg_sender`.

Some apps might even want to *assert* that the `msg_sender` is "null" to force their users into strong privacy practices:

```rust
let sender: Option<AztecAddress> = context.msg_sender();
assert(sender.is_none());
```

##### Enqueueing public function calls

###### Auto-generated contract interfaces

When you use the `#[aztec]` macro, it will generate a noir contract interface for your contract, behind the scenes.

This provides pretty syntax when you come to call functions of that contract. E.g.:

```rust
Token::at(context.this_address())._increase_public_balance(to, amount).enqueue(&mut context);
```

In keeping with this new feature of being able to enqueue public function calls with a hidden `msg_sender`, there are some new methods that can be chained instead of `.enqueue(...)`:

* `enqueue_incognito` -- akin to `enqueue`, but `msg_sender` is set "null".
* `enqueue_view_incognito` -- akin to `enqueue_view`, but `msg_sender` is "null".
* `set_as_teardown_incognito` -- akin to `set_as_teardown`, but `msg_sender` is "null".

> The name "incognito" has been chosen to imply "msg\_sender will not be visible to observers".

These new functions enable the *calling* contract to specify that it wants its address to not be visible to the called public function. This is worth re-iterating: it is the *caller's* choice. A smart contract developer who uses these functions must be sure that the target public function will accept a "null" `msg_sender`. It would not be good (for example) if the called public function did `context.msg_sender().unwrap()`, because then a public function that is called via `enqueue_incognito` would *always fail*! Hopefully smart contract developers will write sufficient tests to catch such problems during development!

###### Making lower-level public function calls from the private context

This is discouraged vs using the auto-generated contract interfaces described directly above.

If you do use any of these low-level methods of the `PrivateContext` in your contract:

* `call_public_function`
* `static_call_public_function`
* `call_public_function_no_args`
* `static_call_public_function_no_args`
* `call_public_function_with_calldata_hash`
* `set_public_teardown_function`
* `set_public_teardown_function_with_calldata_hash`

... there is a new `hide_msg_sender: bool` parameter that you will need to specify.

#### Aztec.js diffs

> Note: we'll be doing another pass at this aztec.js syntax in the near future.

When lining up a new tx, the `FunctionCall` struct has been extended to include a `hide_msg_sender: bool` field.

* `is_public & hide_msg_sender` -- will make a public call with `msg_sender` set to "null".
* `is_public & !hide_msg_sender` -- will make a public call with a visible `msg_sender`, as was the case before this new feature.
* `!is_public & hide_msg_sender` -- Incompatible flags.
* `!is_public & !hide_msg_sender` -- will make a private call with a visible `msg_sender` (noting that since it's a private function call, the `msg_sender` will only be visible to the called private function, but not to the rest of the world).

## \[cli-wallet]

The `deploy-account` command now requires the address (or alias) of the account to deploy as an argument, not a parameter

```diff
+aztec-wallet deploy-account main
-aztec-wallet deploy-account -f main
```

This release includes a major architectural change to the system. The PXE JSON RPC Server has been removed, and PXE is now available only as a library to be used by wallets.

## \[Aztec node]

Network config. The node now pulls default configuration from the public repository [AztecProtocol/networks](https://github.com/AztecProtocol/networks) after it applies the configuration it takes from the running environment and the configuration values baked into the source code. See associated [Design document](https://github.com/AztecProtocol/engineering-designs/blob/15415a62a7c8e901acb8e523625e91fc6f71dce4/docs/network-config/dd.md)

## \[Aztec.js]

### Removing Aztec cheatcodes

The Aztec cheatcodes class has been removed. Its functionality can be replaced by using the `getNotes(...)` function directly available on our `TestWallet`, along with the relevant functions available on the Aztec Node interface (note that the cheatcodes were generally just a thin wrapper around the Aztec Node interface).

### CLI Wallet commands dropped from `aztec` command

The following commands used to be exposed by both the `aztec` and the `aztec-wallet` commands:

* import-test-accounts
* create-account
* deploy-account
* deploy
* send
* simulate
* profile
* bridge-fee-juice
* create-authwit
* authorize-action
* get-tx
* cancel-tx
* register-sender
* register-contract

These were dropped from `aztec` and now are exposed only by the `cli-wallet` command exposed by the `@aztec/cli-wallet` package.

### PXE commands dropped from `aztec` command

The following commands were dropped from the `aztec` command:

* `add-contract`: use can be replaced with `register-contract` on our `cli-wallet`
* `get-contract-data`: debug-only and not considered important enough to need a replacement
* `get-accounts`: debug-only and can be replaced by loading aliases from `cli-wallet`
* `get-account`: debug-only and can be replaced by loading aliases from `cli-wallet`
* `get-pxe-info`: debug-only and not considered important enough to need a replacement

## \[Aztec.nr]

### Replacing #\[private], #\[public], #\[utility] with #\[external(...)] macro

The original naming was not great in that it did not sufficiently communicate what the given macro did. We decided to rename `#[private]` as `#[external("private")]`, `#[public]` as `#[external("public")]`, and `#[utility]` as `#[external("utility")]` to better communicate that these functions are externally callable and to specify their execution context. In this sense, `external` now means the exact same thing as in Solidity, i.e. a function that can be called from other contracts, and that can only be invoked via a contract call (i.e. the `CALL` opcode in the EVM, and a kernel call/AVM `CALL` opcode in Aztec).

You have to do the following changes in your contracts:

Update import:

```diff
- use aztec::macros::functions::private;
- use aztec::macros::functions::public;
- use aztec::macros::functions::utility;
+ use aztec::macros::functions::external;
```

Update attributes of your functions:

```diff
-    #[private]
+    #[external("private")]
    fn my_private_func() {
```

```diff
-    #[public]
+    #[external("public")]
    fn my_public_func() {
```

```diff
-    #[utility]
+    #[external("utility")]
    fn my_utility_func() {
```

### Dropping remote mutable references to public context

`PrivateContext` generally needs to be passed as a mutable reference to functions because it does actually hold state we're mutating. This is not the case for `PublicContext`, or `UtilityContext` - these are just marker objects that indicate the current execution mode and make available the correct subset of the API. For this reason we have dropped the mutable reference from the API.

If you've passed the context as an argument to custom functions you will need to do the following migration (example from our token contract):

```diff
#[contract_library_method]
fn _finalize_transfer_to_private(
    from_and_completer: AztecAddress,
    amount: u128,
    partial_note: PartialUintNote,
-    context: &mut PublicContext,
-    storage: Storage<&mut PublicContext>,
+    context: PublicContext,
+    storage: Storage<PublicContext>,
) {
    ...
}
```

### Authwit Test Helper now takes `env`

The `add_private_authwit_from_call_interface` test helper available in `test::helpers::authwit` now takes a `TestEnvironment` parameter, mirroring `add_public_authwit_from_call_interface`. This adds some unfortunate verbosity, but there are bigger plans to improve authwit usage in Noir tests in the near future.

```diff
add_private_authwit_from_call_interface(
+   env,
    on_behalf_of,
    caller,
    call_interface,
);
```

### Historical block renamed as anchor block

A historical block term has been used as a term that denotes the block against which a private part of a tx has been executed. This name is ambiguous and for this reason we've introduced "anchor block". This naming change resulted in quite a few changes and if you've access private context's or utility context's block header you will need to update your code:

```diff
- let header = context.get_block_header();
+ let header = context.get_anchor_block_header();
```

### Removed ValueNote utils

The `value_note::utils` module has been removed because it was incorrect to have those in the value note package.

For the increment function you can easily just insert the note:

```diff
- use value_note::utils;
- utils::increment(storage.notes.at(owner), value, owner, sender);
+ let note = ValueNote::new(value, owner);
+ storage.notes.at(owner).insert(note).emit(&mut context, owner, MessageDelivery.CONSTRAINED_ONCHAIN);
```

### PrivateMutable: replace / initialize\_or\_replace behaviour change

**Motivation:** Updating a note used to require reading it first (via `get_note`, which nullifies and recreates it) and then calling `replace` — effectively proving a note twice. Now, `replace` accepts a callback that transforms the current note directly, and `initialize_or_replace` simply uses this updated `replace` internally. This reduces circuit cost while maintaining exactly one current note.

**Key points:**

1. `replace(self, new_note)` (old) → `replace(self, f)` (new), where `f` takes the current note and returns a transformed note.
2. `initialize_or_replace(self, note)` (old) → `initialize_or_replace(self, f)` (new), where `f` takes an `Option` with the current note, or `none` if uninitialized.
3. Previous note is automatically nullified before the new note is inserted.
4. `NoteEmission<Note>` still requires `.emit()` or `.discard()`.

**Example Migration:**

```diff
- let current_note = storage.my_var.get_note();
- let new_note = f(current_note);
- storage.my_var.replace(new_note);
+ storage.my_var.replace(|current_note| f(current_note));
```

```diff
- storage.my_var.initialize_or_replace(new_note);
+ storage.my_var.initialize_or_replace(|_| new_note);
```

This makes it easy and efficient to handle both initialization and current value mutation via `initialize_or_replace`, e.g. if implementing a note that simply counts how many times it has been read:

```diff
+ storage.my_var.initialize_or_replace(|opt_current: Option<Note>| opt_current.unwrap_or(0 /* initial value */) + 1);
```

* The callback can be a closure (inline) or a named function.
* Any previous assumptions that replace simply inserts a new\_note directly must be updated.

### Unified oracles into single get\_utility\_context oracle

The following oracles:

1. get\_contract\_address,
2. get\_block\_number,
3. get\_timestamp,
4. get\_chain\_id,
5. get\_version

were replaced with a single `get_utility_context` oracle whose return value contains all the values returned from the removed oracles.

If you have used one of these removed oracles before, update the import, e.g.:

```diff
- aztec::oracle::execution::get_chain_id;
+ aztec::oracle::execution::get_utility_context
```

and get the value out of the returned utility context:

```diff
- let chain_id = get_chain_id();
+ let chain_id = get_utility_context().chain_id();
```

### Note emission API changes

The note emission API has been significantly reworked to provide clearer semantics around message delivery guarantees. The key changes are:

1. `encode_and_encrypt_note` has been removed in favor of calling `emit` directly with `MessageDelivery.CONSTRAINED_ONCHAIN`

2. `encode_and_encrypt_note_unconstrained` has been removed in favor of calling `emit` directly with `MessageDelivery.UNCONSTRAINED_ONCHAIN`

3. `encode_and_encrypt_note_and_emit_as_offchain_message` has been removed in favor of using `emit` with `MessageDelivery.UNCONSTRAINED_OFFCHAIN`

4. Note emission now takes a `delivery_mode` parameter with the following values:

   <!-- -->

   * `CONSTRAINED_ONCHAIN`: For onchain delivery with cryptographic guarantees that recipients can discover and decrypt messages. Uses constrained encryption but is slower to prove. Best for critical messages that contracts need to verify.
   * `UNCONSTRAINED_ONCHAIN`: For onchain delivery without encryption constraints. Faster proving but trusts the sender. Good when the sender is incentivized to perform encryption correctly (e.g. they are buying something and will only get it if the recipient sees the note). No guarantees that recipients will be able to find or decrypt messages.
   * `UNCONSTRAINED_OFFCHAIN`: For offchain delivery (e.g. cloud storage) without constraints. Lowest cost since no onchain storage needed. Requires custom infrastructure for delivery. No guarantees that messages will be delivered or that recipients will ever find them.

5. The `context` object no longer needs to be passed to these functions

Example migration:

First you need to update imports in your contract:

```diff
- aztec::messages::logs::note::encode_and_encrypt_note;
- aztec::messages::logs::note::encode_and_encrypt_note_unconstrained;
- aztec::messages::logs::note::encode_and_encrypt_note_and_emit_as_offchain_message;
+ aztec::messages::message_delivery::MessageDelivery;
```

Then update the emissions:

```diff
- storage.balances.at(from).sub(from, amount).emit(encode_and_encrypt_note(&mut context, from));
+ storage.balances.at(from).sub(from, amount).emit(&mut context, from, MessageDelivery.CONSTRAINED_ONCHAIN);
```

```diff
- storage.balances.at(from).add(from, change).emit(encode_and_encrypt_note_unconstrained(&mut context, from));
+ storage.balances.at(from).add(from, change).emit(&mut context, from, MessageDelivery.UNCONSTRAINED_ONCHAIN);
```

```diff
- storage.balances.at(owner).insert(note).emit(encode_and_encrypt_note_and_emit_as_offchain_message(&mut context, context.msg_sender());
+ storage.balances.at(owner).insert(note).emit(&mut context, context.msg_sender(), MessageDelivery.UNCONSTRAINED_OFFCHAIN);
```

## 2.0.2

## \[Public functions]

The L2 gas cost of the different AVM opcodes have been updated to reflect more realistic proving costs. Developers should review the L2 gas costs of executing public functions and reevaluate any hardcoded L2 gas limits.

## \[Aztec Tools]

### Contract compilation now requires two steps

The `aztec-nargo` command is now a direct pass-through to vanilla nargo, without any special compilation flags or postprocessing. Contract compilation for Aztec now requires two explicit steps:

1. Compile your contracts with `aztec-nargo compile`
2. Run postprocessing with the new `aztec-postprocess-contract` command

The postprocessing step includes:

* Transpiling functions for the Aztec VM
* Generating verification keys for private functions
* Caching verification keys for faster subsequent compilations

Update your build scripts accordingly:

```diff
- aztec-nargo compile
+ aztec-nargo compile
+ aztec-postprocess-contract
```

If you're using the `aztec-up` installer, the `aztec-postprocess-contract` command will be automatically installed alongside `aztec-nargo`.

## \[Aztec.js] Mandatory `from`

As we prepare for a bigger `Wallet` interface refactor and the upcoming `WalletSDK`, a new parameter has been added to contract interactions, which now should indicate *explicitly* the address of the entrypoint (usually the account contract) that will be used to authenticate the request. This will be checked in runtime against the current `this.wallet.getAddress()` value, to ensure consistent behavior while the rest of the API is reworked.

```diff
- await contract.methods.my_func(arg).send().wait();
+ await contract.methods.my_func(arg).send({ from: account1Address }).wait();
```

## \[Aztec.nr]

### `emit_event_in_public_log` function renamed as `emit_event_in_public`

This change was done to make the naming consistent with the private counterpart (`emit_event_in_private`).

### Private event emission API changes

The private event emission API has been significantly reworked to provide clearer semantics around message delivery guarantees. The key changes are:

1. `emit_event_in_private_log` has been renamed to `emit_event_in_private` and now takes a `delivery_mode` parameter instead of `constraints`

2. `emit_event_as_offchain_message` has been removed in favor of using `emit_event_in_private` with `MessageDelivery.UNCONSTRAINED_OFFCHAIN`

3. `PrivateLogContent` enum has been replaced with `MessageDelivery` enum with the following values:

   <!-- -->

   * `CONSTRAINED_ONCHAIN`: For onchain delivery with cryptographic guarantees that recipients can discover and decrypt messages. Uses constrained encryption but is slower to prove. Best for critical messages that contracts need to verify.
   * `UNCONSTRAINED_ONCHAIN`: For onchain delivery without encryption constraints. Faster proving but trusts the sender. Good when the sender is incentivized to perform encryption correctly (e.g. they are buying something and will only get it if the recipient sees the note). No guarantees that recipients will be able to find or decrypt messages.
   * `UNCONSTRAINED_OFFCHAIN`: For offchain delivery (e.g. cloud storage) without constraints. Lowest cost since no onchain storage needed. Requires custom infrastructure for delivery. No guarantees that messages will be delivered or that recipients will ever find them.

### Contract functions can no longer be `pub` or `pub(crate)`

With the latest changes to `TestEnvironment`, making contract functions have public visibility is no longer required given the new `call_public` and `simulate_utility` functions. To avoid accidental direct invocation, and to reduce confusion with the autogenerated interfaces, we're forbidding them being public.

```diff
- pub(crate) fn balance_of_private(account: AztecAddress) -> 128 {
+ fn balance_of_private(account: AztecAddress) -> 128 {
```

### Notes require you to manually implement or derive Packable

We have decided to drop auto-derivation of `Packable` from the `#[note]` macro because we want to make the macros less magical. With this change you will be forced to either apply `#[derive(Packable)` on your notes:

```diff
+use aztec::protocol::traits::Packable;

+#[derive(Packable)]
#[note]
pub struct UintNote {
    owner: AztecAddress,
    randomness: Field,
    value: u128,
}
```

or to implement it manually yourself:

```rust
impl Packable for UintNote {
    let N: u32 = 3;

    fn pack(self) -> [Field; Self::N] {
        [self.owner.to_field(), randomness, value as Field]
    }

    fn unpack(fields: [Field; Self::N]) -> Self {
        let owner = AztecAddress::from_field(fields[0]);
        let randomness = fields[1];
        let value = fields[2] as u128;
        UintNote { owner, randomness, value }
    }
}
```

### Tagging sender now managed via oracle functions

Now, instead of manually needing to pass a tagging sender as an argument to log emission functions (e.g. `encode_and_encrypt_note`, `encode_and_encrypt_note_unconstrained`, `emit_event_in_private_log`, ...) we automatically load the sender via the `get_sender_for_tags()` oracle. This value is expected to be populated by account contracts that should call `set_sender_for_tags()` in their entry point functions.

The changes you need to do in your contracts are quite straightforward. You simply need to drop the `sender` arg from the callsites of the log emission functions. E.g. note emission:

```diff
storage.balances.at(from).sub(from, amount).emit(encode_and_encrypt_note(
    &mut context,
    from,
-    tagging_sender,
));
```

E.g. private event emission:

```diff
emit_event_in_private_log(
    Transfer { from, to, amount },
    &mut context,
-    tagging_sender,
    to,
    PrivateLogContent.NO_CONSTRAINTS,
);
```

This change affected arguments `prepare_private_balance_increase` and `mint_to_private` functions on the `Token` contract. Drop the `from` argument when calling these.

Example in TypeScript test:

```diff
- await token.methods.mint_to_private(fundedWallet.getAddress(), alice, mintAmount).send().wait();
+ await token.methods.mint_to_private(alice, mintAmount).send().wait();
```

Example when

```diff
let token_out_partial_note = Token::at(token_out).prepare_private_balance_increase(
    sender,
-    tagging_sender
).call(&mut context);
```

### SharedMutable -> DelayedPublicMutable

The `SharedMutable` state variable has been renamed to `DelayedPublicMutable`. It is a public mutable with a delay before state changes take effect. It can be read in private during the delay period. The name "shared" confuses developers who actually wish to work with so-called "shared private state". Also, we're working on a `DelayedPrivateMutable` which will have similar properties, except writes will be scheduled from private instead. With this new state variable in mind, the new name works nicely.

## \[TXE] - Testing Aztec Contracts using Noir

### Full `TestEnvironment` API overhaul

As part of a broader effort to make Noir tests that leverage TXE easier to use and reason about, large parts of it were changed or adapted, resulting in the API now being quite different. No functionality was lost, so it should be possible to migrate any older Noir test to use the new API.

#### Network State Manipulation

* `committed_timestamp` removed: this function did not work correctly
* `private_at_timestamp`: this function was not really meaningful: private contexts are built from block numbers, not timestamps
* `pending_block_number` was renamed to `next_block_number`. `pending_timestamp` was removed since it was confusing and not useful
* `committed_block_number` was renamed to `last_block_number`
* `advance_timestamp_to` and `advance_timestamp_by` were renamed to `set_next_block_timestamp` and `advance_next_block_timestamp_by` respectively
* `advance_block_to` was renamed to `mine_block_at`, which takes a timestamp instead of a target block number
* `advance_block_by` was renamed to `mine_block`, which now mines a single block

#### Account Management

* `create_account` was renamed to `create_light_account`
* `create_account_contract` was renamed to `create_contract_account`

#### Contract Deployment

* `deploy_self` removed: merged into `deploy`
* `deploy` now accepts both local and external contracts

#### Contract Interactions

The old way of calling contract functions is gone. Contract functions are now invoked via the `call_private`, `view_private`, `call_public`, `view_public` and `simulate_utility` `TestEnvironment` methods. These take a `CallInterface`, like their old counterparts, but now also take an explicit `from` parameter (for the `call` variants - this is left out of the `view` and `simulate` methods for simplicity).

#### Raw Context Access

The `private` and `public` methods are gone. Private, public and utility contexts can now be crated with the `private_context`, `public_context` and `utility_context` functions, all of which takes a callback function that is called with the corresponding context. This functions are expected to be defined in-line as lambdas, and contain the user-defined test logic. This helps delineate where contexts begin and end. Contexts automatically mine blocks on closing, when appropriate.

#### Error-expecting Functions

`assert_public_call_revert` and variants have been removed. Use `#[test(should_fail_with = "message")]` instead.

#### Example Migration

The following are two tests using the older version of `TestEnvironment`:

```rust
#[test]
unconstrained fn initial_empty_value() {
    let mut env = TestEnvironment::new();

    // Setup without account contracts. We are not using authwits here, so dummy accounts are enough
    let admin = env.create_account(1);

    let initializer_call_interface = Auth::interface().constructor(admin);

    let auth_contract =
        env.deploy_self("Auth").with_public_void_initializer(admin, initializer_call_interface);
    let auth_contract_address = auth_contract.to_address();

    env.impersonate(admin);
    let authorized = Auth::at(auth_contract_address).get_authorized().view(&mut env.public());
    assert_eq(authorized, AztecAddress::from_field(0));
}

#[test]
unconstrained fn non_admin_cannot_set_authorized() {
    let mut env = TestEnvironment::new();

    // Setup without account contracts. We are not using authwits here, so dummy accounts are enough
    let admin = env.create_account(1);
    let other = env.create_account(2);

    let initializer_call_interface = Auth::interface().constructor(admin);

    let auth_contract =
        env.deploy_self("Auth").with_public_void_initializer(admin, initializer_call_interface);
    let auth_contract_address = auth_contract.to_address();

    env.impersonate(other);
    env.assert_public_call_fails(Auth::at(auth_contract_address).set_authorized(to_authorize));
}
```

These now look like this:

```rust
#[test]
unconstrained fn authorized_initially_unset() {
    let mut env = TestEnvironment::new();

    let admin = env.create_light_account(); // Manual secret management gone

    let auth_contract_address =
        env.deploy("Auth").with_public_initializer(admin, Auth::interface().constructor(admin)); // deploy_self replaced
    let auth = Auth::at(auth_contract_address);

    assert_eq(env.view_public(auth.get_authorized()), AztecAddress::zero()); // .view_public() instead of .public()
}

#[test(should_fail_with = "caller is not admin")]
unconstrained fn non_admin_cannot_set_unauthorized() {
    let mut env = TestEnvironment::new();

    let admin = env.create_light_account();
    let other = env.create_light_account();

    let auth_contract_address =
        env.deploy("Auth").with_public_initializer(admin, Auth::interface().constructor(admin)); // deploy_self replaced
    let auth = Auth::at(auth_contract_address);

    env.call_public(other, auth.set_authorized(other)); // .call_public(), should_fail_with
}
```

## \[Aztec.js]

### Cheatcodes

Cheatcodes where moved out of the `@aztec/aztec.js` package to `@aztec/ethereum` and `@aztec/aztec` packages. While all of the cheatcodes can be imported from the `@aztec/aztec` package `EthCheatCodes` and `RollupCheatCodes` reside in `@aztec/ethereum` package and if you need only those importing only that package should result in a lighter build.

### Note exports dropped from artifact

Notes are no longer exported in the contract artifact. Exporting notes was technical debt from when we needed to interpret notes in TypeScript.

The following code will no longer work since `notes` is no longer available on the artifact:

```rust
const valueNoteTypeId = StatefulTestContractArtifact.notes['ValueNote'].id;
```

## \[core protocol, Aztec.nr, Aztec.js] Max block number property changed to be seconds based

### `max_block_number` -> `include_by_timestamp`

The transaction expiration mechanism has been updated to use seconds rather than number of blocks. As part of this change, the transaction property `max_block_number` has been renamed to `include_by_timestamp`.

This change significantly impacts the `SharedMutable` state variable in `Aztec.nr`, which now operates on a seconds instead of number of blocks. If your contract uses `SharedMutable`, you'll need to:

1. Update the `INITIAL_DELAY` numeric generic to use seconds instead of blocks
2. Modify any related logic to account for timestamp-based timing
3. Note that timestamps use `u64` values while block numbers use `u32`

### Removed `prelude`, so your `dep::aztec::prelude::...` imports will need to be amended.

Instead of importing common types from `dep::aztec::prelude...`, you'll now need to import them from their lower-level locations. The Noir Language Server vscode extension is now capable of autocompleting imports: just type some of the import and press 'tab' when it pops up with the correct item, and the import will be inserted at the top of the file.

As a quick reference, here are the paths to the types that were previously in the `prelude`. So, for example, if you were previously using `dep::aztec::prelude::AztecAddress`, you'll need to replace it with `dep::aztec::protocol::address::AztecAddress`. Apologies for any pain this brings. The reasoning is that these types were somewhat arbitrary, and it was unclear which types were worthy enough to be included here.

```rust
use dep::aztec::{
    context::{PrivateCallInterface, PrivateContext, PublicContext, UtilityContext, ReturnsHash},
    note::{
        note_getter_options::NoteGetterOptions,
        note_interface::{NoteHash, NoteType},
        note_viewer_options::NoteViewerOptions,
        hinted_note::HintedNote,
    },
    state_vars::{
        map::Map, private_immutable::PrivateImmutable, private_mutable::PrivateMutable,
        private_set::PrivateSet, public_immutable::PublicImmutable, public_mutable::PublicMutable,
        shared_mutable::SharedMutable,
    },
};

use dep::aztec::protocol::{
    abis::function_selector::FunctionSelector,
    address::{AztecAddress, EthAddress},
    point::Point,
    traits::{Deserialize, Serialize},
};
```

### `include_by_timestamp` is now mandatory

Each transaction must now include a valid `include_by_timestamp` that satisfies the following conditions:

* It must be greater than the historical block’s timestamp.
* The duration between the `include_by_timestamp` and the historical block’s timestamp must not exceed the maximum allowed (currently 24 hours).
* It must be greater than or equal to the timestamp of the block in which the transaction is included.

The protocol circuits compute the `include_by_timestamp` for contract updates during each private function iteration. If a contract does not explicitly specify a value, the default will be the maximum allowed duration. This ensures that `include_by_timestamp` is never left unset.

No client-side changes are required. However, please note that transactions now have a maximum lifespan of 24 hours and will be removed from the transaction pool once expired.

## 0.88.0

## \[Aztec.nr] Deprecation of the `authwit` library

It is now included in `aztec-nr`, so imports must be updated:

```diff
-dep::authwit::...
+dep::aztec::authwit...
```

and stale dependencies removed from `Nargo.toml`

```diff
-authwit = { path = "../../../../aztec-nr/authwit" }
```

## 0.87.0

## \[Aztec.js/TS libraries]

We've bumped our minimum supported node version to v20, as v18 is now EOL. As a consequence, the deprecated type assertion syntax has been replaced with modern import attributes whenever contract artifact JSONs are loaded:

```diff
-import ArtifactJson from '../artifacts/contract-Contract.json' assert { type: 'json' };
+import ArtifactJson from '../artifacts/contract-Contract.json' with { type: 'json' };
```

## \[Aztec.js/PXE] `simulateUtility` return type

`pxe.simulateUtility()` now returns a complex object (much like `.simulateTx()`) so extra information can be provided such as simulation timings.

This information can be accessed setting the `includeMetadata` flag in `SimulateMethodOptions` to `true`, but not providing it (which is the default) will NOT change the behavior of the current code.

```diff
-const result = await pxe.simulateUtility(...);
+const { meta, result } = await pxe.simulateUtility(...);

const result = await Contract.methods.myFunction(...).simulate();
const { result, meta} = await Contract.methods.myFunction(...).simulate({ includeMetadata: true });
```

## \[Aztec.js] Removed mandatory simulation before proving in contract interfaces

Previously, our autogenerated contract classes would perform a simulation when calling `.prove` or `.send` on them. This could potentially catch errors earlier, but took away control from the app/wallets on how to handle network interactions. Now this process has to be triggered manually, which means just proving an interaction (or proving and sending it to the network in one go via `.send`) is much faster.

*WARNING:* This means users can incurr in network fees if a transaction that would otherwise be invalid is sent without sanity checks. To ensure this, it is recommended to do:

```diff
+await Contract.method.simulate();
await Contract.method.send().wait();
```

## 0.86.0

### \[PXE] Removed PXE\_L2\_STARTING\_BLOCK environment variable

PXE now fast-syncs by skipping finalized blocks and never downloads all blocks, so there is no longer a need to specify a starting block.

### \[Aztec.nr] Logs and messages renaming

The following renamings have taken place:

* `encrypted_logs` to `messages`: this module now handles much more than just encrypted logs (including unconstrained message delivery, message encoding, etc.)
* `log_assembly_strategies` to `logs`
* `discovery` moved to `messages`: given that what is discovered are messages
* `default_aes128` removed

Most contracts barely used these modules directly. The frequently used `encode_and_encrypt` function imports remain unchanged:

```rust
use dep::aztec::messages::logs::note::encode_and_encrypt_note;
```

### \[noir-contracts] Reference Noir contracts directory structure change

`noir-projects/noir-contracts/contracts` directory became too cluttered so we grouped contracts into `account`, `app`, `docs`, `fees`, `libs`, `protocol` and `test` dirs. If you import contract from the directory make sure to update the paths accordingly. E.g. for a token contract:

```diff
#[dependencies]
-token = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v0.83.0", directory = "noir-projects/noir-contracts/contracts/src/token_contract" }
+token = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v0.83.0", directory = "noir-projects/noir-contracts/contracts/app/src/token_contract" }
```

### \[Aztec.nr] #\[utility] contract functions

Aztec contracts have three kinds of functions: `#[private]`, `#[public]` and what was sometimes called 'top-level unconstrained': an unmarked unconstrained function in the contract module. These are now called `[#utility]` functions, and must be explicitly marked as such:

```diff
+    #[utility]
    unconstrained fn balance_of_private(owner: AztecAddress) -> u128 {
        storage.balances.at(owner).balance_of()
    }
```

Utility functions are standalone unconstrained functions that cannot be called from private or public functions: they are meant to be called by *applications* to perform auxiliary tasks: query contract state (e.g. a token balance), process messages received offchain, etc.

All functions in a `contract` block must now be marked as one of either `#[private]`, `#[public]`, `#[utility]`, `#[contract_library_method]`, or `#[test]`.

Additionally, the `UnconstrainedContext` type has been renamed to `UtilityContext`. This led us to rename the `unkonstrained` method on `TestEnvironment` to `utility`, so any tests using it also need updating:

```diff
-     SharedMutable::new(env.unkonstrained(), storage_slot)
+     SharedMutable::new(env.utility(), storage_slot)
```

### \[AuthRegistry] function name change

As part of the broader transition from "top-level unconstrained" to "utility" name (detailed in the note above), the `unconstrained_is_consumable` function in AuthRegistry has been renamed to `utility_is_consumable`. The function's signature and behavior remain unchanged - only the name has been updated to align with the new convention. If you're currently using this function, a simple rename in your code will suffice.

## 0.83.0

### \[aztec.js] AztecNode.getPrivateEvents API change

The `getPrivateEvents` method signature has changed to require an address of a contract that emitted the event and use recipient addresses instead of viewing public keys:

```diff
- const events = await wallet.getPrivateEvents<Transfer>(TokenContract.events.Transfer, 1, 1, [recipient.getCompleteAddress().publicKeys.masterIncomingViewingPublicKey()]);
+ const events = await wallet.getPrivateEvents<Transfer>(token.address, TokenContract.events.Transfer, 1, 1, [recipient.getAddress()]);
```

### \[portal contracts] Versions and Non-following message boxes

The version number is no longer hard-coded to be `1` across all deployments (it not depends on where it is deployed to and with what genesis and logic). This means that if your portal were hard-coding `1` it will now fail when inserting into the `inbox` or consuming from the `outbox` because of a version mismatch. Instead you can get the real version (which don't change for a deployment) by reading the `VERSION` on inbox and outbox, or using `getVersion()` on the rollup.

New Deployments of the protocol do not preserve former state/across each other. This means that after a new deployment, any "portal" following the registry would try to send messages into this empty rollup to non-existent contracts. To solve, the portal should be linked to a specific deployment, e.g., a specific inbox. This can be done by storing the inbox/outbox/version at the time of deployment or initialize and not update them.

Both of these issues were in the token portal and the uniswap portal, so if you used them as a template it is very likely that you will also have it.

## 0.82.0

### \[aztec.js] AztecNode.findLeavesIndexes returns indexes with block metadata

It's common that we need block metadata of a block in which leaves were inserted when querying indexes of these tree leaves. For this reason we now return that information along with the indexes. This allows us to reduce the number of individual AztecNode queries.

Along with this change, `findNullifiersIndexesWithBlock` and `findBlockNumbersForIndexes` functions were removed as all their uses can now be replaced with the newly modified `findLeavesIndexes` function.

### \[aztec.js] AztecNode.getPublicDataTreeWitness renamed as AztecNode.getPublicDataWitness

This change was done to have consistent naming across codebase.

### \[aztec.js] Wallet interface and Authwit management

The `Wallet` interface in `aztec.js` is undergoing transformations, trying to be friendlier to wallet builders and reducing the surface of its API. This means `Wallet` no longer extends `PXE`, and instead just implements a subset of the methods of the former. This is NOT going to be its final form, but paves the way towards better interfaces and starts to clarify what the responsibilities of the wallet are:

```typescript
/**
 * The wallet interface.
 */
export type Wallet = AccountInterface &
  Pick<
    PXE,
    // Simulation
    | "simulateTx"
    | "simulateUnconstrained"
    | "profileTx"
    // Sending
    | "sendTx"
    // Contract management (will probably be collapsed in the future to avoid instance and class versions)
    | "getContractClassMetadata"
    | "getContractMetadata"
    | "registerContract"
    | "registerContractClass"
    // Likely to be removed
    | "proveTx"
    // Will probably be collapsed
    | "getNodeInfo"
    | "getPXEInfo"
    // Fee info
    | "getCurrentMinFees"
    // Still undecided, kept for the time being
    | "updateContract"
    // Sender management
    | "registerSender"
    | "getSenders"
    | "removeSender"
    // Tx status
    | "getTxReceipt"
    // Events. Kept since events are going to be reworked and changes will come when that's done
    | "getPrivateEvents"
    | "getPublicEvents"
  > & {
    createAuthWit(intent: IntentInnerHash | IntentAction): Promise<AuthWitness>;
  };
```

As a side effect, a few debug only features have been removed

```diff
// Obtain tx effects
const { txHash, debugInfo } = await contract.methods
      .set_constant(value)
      .send()
--      .wait({ interval: 0.1, debug: true });
++      .wait({ interval: 0.1 })

--    // check that 1 note hash was created
--    expect(debugInfo!.noteHashes.length).toBe(1);
++    const txEffect = await aztecNode.getTxEffect(txHash);
++    const noteHashes = txEffect?.data.noteHashes;
++    // check that 1 note hash was created
++    expect(noteHashes?.length).toBe(1);

// Wait for a tx to be proven
--      tx.wait({ timeout: 300, interval: 10, proven: true, provenTimeout: 3000 })));
++      const receipt = await tx.wait({ timeout: 300, interval: 10 });
++      await waitForProven(aztecNode, receipt, { provenTimeout: 3000 });
```

Authwit management has changed, and PXE no longer stores them. This is unnecessary because now they can be externally provided to simulations and transactions, making sure no stale authorizations are kept inside PXE's db.

```diff
const witness = await wallet.createAuthWit({ caller, action });
--await callerWallet.addAuthWitness(witness);
--await action.send().wait();
++await action.send({ authWitnesses: [witness] }).wait();
```

Another side effect of this is that the interface of the `lookupValidity` method has changed, and now the authwitness has to be provided:

```diff
const witness = await wallet.createAuthWit({ caller, action });
--await callerWallet.addAuthWitness(witness);
--await wallet.lookupValidity(wallet.getAddress(), { caller, action });
++await wallet.lookupValidity(wallet.getAddress(), { caller, action }, witness);
```

## 0.80.0

### \[PXE] Concurrent contract function simulation disabled

PXE is no longer be able to execute contract functions concurrently (e.g. by collecting calls to `simulateTx` and then using `await Promise.all`). They will instead be put in a job queue and executed sequentially in order of arrival.

## 0.79.0

### \[aztec.js] Changes to `BatchCall` and `BaseContractInteraction`

The constructor arguments of `BatchCall` have been updated to improve usability. Previously, it accepted an array of `FunctionCall`, requiring users to manually set additional data such as `authwit` and `capsules`. Now, `BatchCall` takes an array of `BaseContractInteraction`, which encapsulates all necessary information.

```diff
class BatchCall extends BaseContractInteraction {
-    constructor(wallet: Wallet, protected calls: FunctionCall[]) {
+    constructor(wallet: Wallet, protected calls: BaseContractInteraction[]) {
        ...
    }
```

The `request` method of `BaseContractInteraction` now returns `ExecutionPayload`. This object includes all the necessary data to execute one or more functions. `BatchCall` invokes this method on all interactions to aggregate the required information. It is also used internally in simulations for fee estimation.

Declaring a `BatchCall`:

```diff
new BatchCall(wallet, [
-    await token.methods.transfer(alice, amount).request(),
-    await token.methods.transfer_to_private(bob, amount).request(),
+    token.methods.transfer(alice, amount),
+    token.methods.transfer_to_private(bob, amount),
])
```

## 0.77.0

### \[aztec-nr] `TestEnvironment::block_number()` refactored

The `block_number` function from `TestEnvironment` has been expanded upon with two extra functions, the first being `pending_block_number`, and the second being `committed_block_number`. `pending_block_number` now returns what `block_number` does. In other words, it returns the block number of the block we are currently building. `committed_block_number` returns the block number of the last committed block, i.e. the block number that gets used to execute the private part of transactions when your PXE is successfully synced to the tip of the chain.

```diff
+    `TestEnvironment::pending_block_number()`
+    `TestEnvironment::committed_block_number()`
```

### \[aztec-nr] `compute_nullifier_without_context` renamed

The `compute_nullifier_without_context` function from `NoteHash` (ex `NoteInterface`) is now called `compute_nullifier_unconstrained`, and instead of taking storage slot, contract address and nonce it takes a note hash for nullification (same as `compute_note_hash`). This makes writing this function simpler:

```diff
-    unconstrained fn compute_nullifier_without_context(self, storage_slot: Field, contract_address: AztecAddress, nonce: Field) -> Field {
-       let note_hash_for_nullify = ...;
+    unconstrained fn compute_nullifier_unconstrained(self, note_hash_for_nullify: Field) -> Field {
        ...
    }
```

### `U128` type replaced with native `u128`

The `U128` type has been replaced with the native `u128` type. This means that you can no longer use the `U128` type in your code. Instead, you should use the `u128` type. Doing the changes is as straightforward as:

```diff
    #[public]
    #[view]
-    fn balance_of_public(owner: AztecAddress) -> U128 {
+    fn balance_of_public(owner: AztecAddress) -> u128 {
        storage.public_balances.at(owner).read()
    }
```

`UintNote` has also been updated to use the native `u128` type.

### \[aztec-nr] Removed `compute_note_hash_and_optionally_a_nullifier`

This function is no longer mandatory for contracts, and the `#[aztec]` macro no longer injects it.

### \[PXE] Removed `addNote` and `addNullifiedNote`

These functions have been removed from PXE and the base `Wallet` interface. If you need to deliver a note manually because its creation is not being broadcast in an encrypted log, then create an unconstrained contract function to process it and simulate execution of it. The `aztec::discovery::private_logs::do_process_log` function can be used to perform note discovery and add to it to PXE.

See an example of how to handle a `TransparentNote`:

```rust
    unconstrained fn deliver_transparent_note(
        contract_address: AztecAddress,
        amount: Field,
        secret_hash: Field,
        tx_hash: Field,
        unique_note_hashes_in_tx: BoundedVec<Field, MAX_NOTE_HASHES_PER_TX>,
        first_nullifier_in_tx: Field,
        recipient: AztecAddress,
    ) {
        // do_process_log expects a standard aztec-nr encoded note, which has the following shape:
        // [ storage_slot, note_type_id, ...packed_note ]
        let note = TransparentNote::new(amount, secret_hash);
        let log_plaintext = BoundedVec::from_array(array_concat(
            [
                MyContract::storage_layout().my_state_variable.slot,
                TransparentNote::get_note_type_id(),
            ],
            note.pack(),
        ));

        do_process_log(
            contract_address,
            log_plaintext,
            tx_hash,
            unique_note_hashes_in_tx,
            first_nullifier_in_tx,
            recipient,
            _compute_note_hash_and_nullifier,
        );
    }
```

The note is then processed by calling this function:

```typescript
const txEffects = await wallet.getTxEffect(txHash);
await contract.methods
  .deliver_transparent_note(
    contract.address,
    new Fr(amount),
    secretHash,
    txHash.hash,
    toBoundedVec(txEffects!.data.noteHashes, MAX_NOTE_HASHES_PER_TX),
    txEffects!.data.nullifiers[0],
    wallet.getAddress(),
  )
  .simulate();
```

### Fee is mandatory

All transactions must now pay fees. Previously, the default payment method was `NoFeePaymentMethod`; It has been changed to `FeeJuicePaymentMethod`, with the wallet owner as the fee payer.

For example, the following code will still work:

```text
await TokenContract.at(address, wallet).methods.transfer(recipient, 100n).send().wait();
```

However, the wallet owner must have enough fee juice to cover the transaction fee. Otherwise, the transaction will be rejected.

The 3 test accounts deployed in the sandbox are pre-funded with 10 ^ 22 fee juice, allowing them to send transactions right away.

In addition to the native fee juice, users can pay the transaction fees using tokens that have a corresponding FPC contract. The sandbox now includes `BananaCoin` and `BananaFPC`. Users can use a funded test account to mint banana coin for a new account. The new account can then start sending transactions and pay fees with banana coin.

```typescript
import { getDeployedTestAccountsWallets } from "@aztec/accounts/testing";
import {
  getDeployedBananaCoinAddress,
  getDeployedBananaFPCAddress,
} from "@aztec/aztec";

// Fetch the funded test accounts.
const [fundedWallet] = await getDeployedTestAccountsWallets(pxe);

// Create a new account.
const secret = Fr.random();
const signingKey = GrumpkinScalar.random();
const alice = await getSchnorrAccount(pxe, secret, signingKey);
const aliceWallet = await alice.getWallet();
const aliceAddress = alice.getAddress();

// Deploy the new account using the pre-funded test account.
await alice.deploy({ deployWallet: fundedWallet }).wait();

// Mint banana coin for the new account.
const bananaCoinAddress = await getDeployedBananaCoinAddress(pxe);
const bananaCoin = await TokenContract.at(bananaCoinAddress, fundedWallet);
const mintAmount = 10n ** 20n;
await bananaCoin.methods
  .mint_to_private(fundedWallet.getAddress(), aliceAddress, mintAmount)
  .send()
  .wait();

// Use the new account to send a tx and pay with banana coin.
const transferAmount = 100n;
const bananaFPCAddress = await getDeployedBananaFPCAddress(pxe);
const paymentMethod = new PrivateFeePaymentMethod(
  bananaFPCAddress,
  aliceWallet,
);
const receipt = await bananaCoin
  .withWallet(aliceWallet)
  .methods.transfer(recipient, transferAmount)
  .send({ fee: { paymentMethod } })
  .wait();
const transactionFee = receipt.transactionFee!;

// Check the new account's balance.
const aliceBalance = await bananaCoin.methods
  .balance_of_private(aliceAddress)
  .simulate();
expect(aliceBalance).toEqual(mintAmount - transferAmount - transactionFee);
```

### The tree of protocol contract addresses is now an indexed tree

This is to allow for non-membership proofs for non-protocol contract addresses. As before, the canonical protocol contract addresses point to the index of the leaf of the 'real' computed protocol address.

For example, the canonical `DEPLOYER_CONTRACT_ADDRESS` is a constant `= 2`. This is used in the kernels as the `contract_address`. We calculate the `computed_address` (currently `0x1665c5fbc1e58ba19c82f64c0402d29e8bbf94b1fde1a056280d081c15b0dac1`) and check that this value exists in the indexed tree at index `2`. This check already existed and ensures that the call cannot do 'special' protocol contract things unless it is a real protocol contract.

The new check an indexed tree allows is non-membership of addresses of non protocol contracts. This ensures that if a call is from a protocol contract, it must use the canonical address. For example, before this check a call could be from the deployer contract and use `0x1665c5fbc1e58ba19c82f64c0402d29e8bbf94b1fde1a056280d081c15b0dac1` as the `contract_address`, but be incorrectly treated as a 'normal' call.

```diff
- let computed_protocol_contract_tree_root = if is_protocol_contract {
-     0
- } else {
-     root_from_sibling_path(
-         computed_address.to_field(),
-         protocol_contract_index,
-         private_call_data.protocol_contract_sibling_path,
-     )
- };

+ conditionally_assert_check_membership(
+     computed_address.to_field(),
+     is_protocol_contract,
+     private_call_data.protocol_contract_leaf,
+     private_call_data.protocol_contract_membership_witness,
+     protocol_contract_tree_root,
+ );
```

### \[Aztec.nr] Changes to note interfaces and note macros

In this releases we decided to do a large refactor of notes which resulted in the following changes:

1. We removed `NoteHeader` and we've introduced a `HintedNote` struct that contains a note and the information originally stored in the `NoteHeader`.
2. We removed the `pack_content` and `unpack_content` functions from the `NoteInterface`and made notes implement the standard `Packable` trait.
3. We renamed the `NullifiableNote` trait to `NoteHash` and we've moved the `compute_note_hash` function to this trait from the `NoteInterface` trait.
4. We renamed `NoteInterface` trait as `NoteType` and `get_note_type_id` function as `get_id`.
5. The `#[note]` and `#[partial_note]` macros now generate both the `NoteType` and `NoteHash` traits.
6. `#[custom_note_interface]` macro has been renamed to `#[custom_note]` and it now implements the `NoteInterface` trait.

This led us to do the following changes to the interfaces:

```diff
-pub trait NoteInterface<let N: u32> {
+pub trait NoteType {
    fn get_id() -> Field;
-    fn pack_content(self) -> [Field; N];
-    fn unpack_content(fields: [Field; N]) -> Self;
-    fn get_header(self) -> NoteHeader;
-    fn set_header(&mut self, header: NoteHeader) -> ();
-    fn compute_note_hash(self) -> Field;
}

pub trait NoteHash {
+    fn compute_note_hash(self, storage_slot: Field) -> Field;

    fn compute_nullifier(self, context: &mut PrivateContext, note_hash_for_nullify: Field) -> Field;

-    unconstrained fn compute_nullifier_without_context(self) -> Field;
+    unconstrained fn fn compute_nullifier_without_context(self, storage_slot: Field, contract_address: AztecAddress, note_nonce: Field) -> Field;
}
```

If you are using `#[note]` or `#[partial_note(...)]` macros you will need to delete the implementations of the `NullifiableNote` (now `NoteHash`) trait as it now gets auto-generated. Your note will also need to have an `owner` (a note struct field called owner) as its used in the auto-generated nullifier functions.

If you need a custom implementation of the `NoteHash` interface use the `#[custom_note]` macro.

If you used `#[note_custom_interface]` macro before you will need to update your notes by using the `#[custom_note]` macro and implementing the `compute_note_hash` function. If you have no need for a custom implementation of the `compute_note_hash` function copy the default one:

```text
fn compute_note_hash(self, storage_slot: Field) -> Field {
    let inputs = aztec::protocol::utils::arrays::array_concat(self.pack(), [storage_slot]);
    aztec::protocol::hash::poseidon2_hash_with_separator(inputs, aztec::protocol::constants::DOM_SEP__NOTE_HASH)
}
```

If you need to keep the custom implementation of the packing functionality, manually implement the `Packable` trait:

```diff
+ use dep::aztec::protocol::traits::Packable;

+impl Packable<N> for YourNote {
+    fn pack(self) -> [Field; N] {
+        ...
+    }
+
+    fn unpack(fields: [Field; N]) -> Self {
+        ...
+    }
+}
```

If you don't provide a custom implementation of the `Packable` trait, a default one will be generated.

### \[Aztec.nr] Changes to state variables

Since we've removed `NoteHeader` from notes we no longer need to modify the header in the notes when working with state variables. This means that we no longer need to be passing a mutable note reference which led to the following changes in the API.

#### PrivateImmutable

For `PrivateImmutable` the changes are fairly straightforward. Instead of passing in a mutable reference `&mut note` just pass in `note`.

```diff
impl<Note> PrivateImmutable<Note, &mut PrivateContext> {
-    pub fn initialize<let N: u32>(self, note: &mut Note) -> NoteEmission<Note>
+    pub fn initialize<let N: u32>(self, note: Note) -> NoteEmission<Note>
    where
        Note: NoteInterface<N> + NullifiableNote,
    {
        ...
    }
}
```

#### PrivateSet

For `PrivateSet` the changes are a bit more involved than the changes in `PrivateImmutable`. Instead of passing in a mutable reference `&mut note` to the `insert` function just pass in `note`. The `remove` function now takes in a `HintedNote<Note>` instead of a `Note` and the `get_notes` function now returns a vector `HintedNote`s instead of a vector `Note`s. Note getters now generally return `HintedNote`s so getting a hold of the `HintedNote` for removal should be straightforward.

```diff
impl<Note, let N: u32> PrivateSet<Note, &mut PrivateContext>
where
    Note: NoteInterface<N> + NullifiableNote + Eq,
{
-    pub fn insert(self, note: &mut Note) -> NoteEmission<Note> {
+    pub fn insert(self, note: Note) -> NoteEmission<Note> {
        ...
    }

-    pub fn remove(self, note: Note) {
+    pub fn remove(self, hinted_note: HintedNote<Note>) {
        ...
    }

    pub fn get_notes<PREPROCESSOR_ARGS, FILTER_ARGS>(
        self,
        options: NoteGetterOptions<Note, N, PREPROCESSOR_ARGS, FILTER_ARGS>,
-    ) -> BoundedVec<Note, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL> {
+    ) -> BoundedVec<HintedNote<Note>, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL> {
        ...
    }
}

- impl<Note, let N: u32> PrivateSet<Note, &mut PublicContext>
- where
-    Note: NoteInterface<N> + NullifiableNote,
- {
-    pub fn insert_from_public(self, note: &mut Note) {
-        create_note_hash_from_public(self.context, self.storage_slot, note);
-    }
- }
```

#### PrivateMutable

For `PrivateMutable` the changes are similar to the changes in `PrivateImmutable`.

```diff
impl<Note, let N: u32> PrivateMutable<Note, &mut PrivateContext>
where
    Note: NoteInterface<N> + NullifiableNote,
{
-    pub fn initialize(self, note: &mut Note) -> NoteEmission<Note> {
+    pub fn initialize(self, note: Note) -> NoteEmission<Note> {
        ...
    }

-    pub fn replace(self, new_note: &mut Note) -> NoteEmission<Note> {
+    pub fn replace(self, new_note: Note) -> NoteEmission<Note> {
        ...
    }

-    pub fn initialize_or_replace(self, note: &mut Note) -> NoteEmission<Note> {
+    pub fn initialize_or_replace(self, note: Note) -> NoteEmission<Note> {
        ...
    }
}
```

## 0.75.0

### Changes to `TokenBridge` interface

`get_token` and `get_portal_address` functions got merged into a single `get_config` function that returns a struct containing both the token and portal addresses.

### \[Aztec.nr] `SharedMutable` can store size of packed length larger than 1

`SharedMutable` has been modified such that now it can store type `T` which packs to a length larger than 1. This is a breaking change because now `SharedMutable` requires `T` to implement `Packable` trait instead of `ToField` and `FromField` traits.

To implement the `Packable` trait for your type you can use the derive macro:

```diff
+ use std::meta::derive;

+ #[derive(Packable)]
pub struct YourType {
    ...
}
```

### \[Aztec.nr] Introduction of `WithHash<T>`

`WithHash<T>` is a struct that allows for efficient reading of value `T` from public storage in private. This is achieved by storing the value with its hash, then obtaining the values via an oracle and verifying them against the hash. This results in in a fewer tree inclusion proofs for values `T` that are packed into more than a single field.

`WithHash<T>` is leveraged by state variables like `PublicImmutable`. This is a breaking change because now we require values stored in `PublicImmutable` and `SharedMutable` to implement the `Eq` trait.

To implement the `Eq` trait you can use the `#[derive(Eq)]` macro:

```diff
+ use std::meta::derive;

+ #[derive(Eq)]
pub struct YourType {
    ...
}
```

## 0.73.0

### \[Token, FPC] Moving fee-related complexity from the Token to the FPC

There was a complexity leak of fee-related functionality in the token contract. We've came up with a way how to achieve the same objective with the general functionality of the Token contract. This lead to the removal of `setup_refund` and `complete_refund` functions from the Token contract and addition of `complete_refund` function to the FPC.

### \[Aztec.nr] Improved storage slot allocation

State variables are no longer assumed to be generic over a type that implements the `Serialize` trait: instead, they must implement the `Storage` trait with an `N` value equal to the number of slots they need to reserve.

For the vast majority of state variables, this simply means binding the serialization length to this trait:

```diff
+ impl<T, let N: u32> Storage<N> for MyStateVar<T> where T: Serialize<N> { };
```

### \[Aztec.nr] Introduction of `Packable` trait

We have introduced a `Packable` trait that allows types to be serialized and deserialized with a focus on minimizing the size of the resulting Field array. This is in contrast to the `Serialize` and `Deserialize` traits, which follows Noir's intrinsic serialization format. This is a breaking change because we now require `Packable` trait implementation for any type that is to be stored in contract storage.

Example implementation of Packable trait for `U128` type from `noir::std`:

```rust
use crate::traits::{Packable, ToField};

let U128_PACKED_LEN: u32 = 1;

impl Packable<U128_PACKED_LEN> for U128 {
    fn pack(self) -> [Field; U128_PACKED_LEN] {
        [self.to_field()]
    }

    fn unpack(fields: [Field; U128_PACKED_LEN]) -> Self {
        U128::from_integer(fields[0])
    }
}
```

### Logs for notes, partial notes, and events have been refactored.

We're preparing to make log assembly more customisable. These paths have changed.

```diff
- use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_note,
+ use dep::aztec::messages::logs::note::encode_and_encrypt_note,
```

And similar paths for `encode_and_encrypt_note_unconstrained`, and for events and partial notes.

The way in which logs are assembled in this "default\_aes128" strategy is has also changed. I repeat: **Encrypted log layouts have changed**. The corresponding typescript for note discovery has also been changed, but if you've rolled your own functions for parsing and decrypting logs, those will be broken by this change.

### `NoteInferface` and `EventInterface` no-longer have a `to_be_bytes` method.

You can remove this method from any custom notes or events that you've implemented.

### \[Aztec.nr] Packing notes resulting in changes in `NoteInterface`

Note interface implementation generated by our macros now packs note content instead of serializing it With this change notes are being less costly DA-wise to emit when some of the note struct members implements the `Packable` trait (this is typically the `UintNote` which represents `value` as `U128` that gets serialized as 2 fields but packed as 1). This results in the following changes in the `NoteInterface`:

```diff
pub trait NoteInterface<let N: u32> {
-    fn serialize_content(self) -> [Field; N];
+    fn pack_content(self) -> [Field; N];

-    fn deserialize_content(fields: [Field; N]) -> Self;
+    fn unpack_content(fields: [Field; N]) -> Self;

    fn get_header(self) -> NoteHeader;
    fn set_header(&mut self, header: NoteHeader) -> ();
    fn get_note_type_id() -> Field;
    fn compute_note_hash(self) -> Field;
}
```

### \[PXE] Cleanup of Contract and ContractClass information getters

```diff
- pxe.isContractInitialized
- pxe.getContractInstance
- pxe.isContractPubliclyDeployed
+ pxe.getContractMetadata
```

have been merged into getContractMetadata

```diff
- pxe.getContractClass
- pxe.isContractClassPubliclyRegistered
- pxe.getContractArtifact
+ pxe.getContractClassMetadata
```

These functions have been merged into `pxe.getContractMetadata` and `pxe.getContractClassMetadata`.

## 0.72.0

### Some functions in `aztec.js` and `@aztec/accounts` are now async

In our efforts to make libraries more browser-friendly and providing with more bundling options for `bb.js` (like a non top-level-await version), some functions are being made async, in particular those that access our cryptographic functions.

```diff
- AztecAddress.random();
+ await AztecAddress.random();

- getSchnorrAccount();
+ await getSchnorrAccount();
```

### Public logs replace unencrypted logs

Any log emitted from public is now known as a public log, rather than an unencrypted log. This means methods relating to these logs have been renamed e.g. in the pxe, archiver, txe:

```diff
- getUnencryptedLogs(filter: LogFilter): Promise<GetUnencryptedLogsResponse>
- getUnencryptedEvents<T>(eventMetadata: EventMetadataDefinition, from: number, limit: number): Promise<T[]>
+ getPublicLogs(filter: LogFilter): Promise<GetPublicLogsResponse>
+ getPublicEvents<T>(eventMetadata: EventMetadataDefinition, from: number, limit: number): Promise<T[]>
```

The context method in aztec.nr is now:

```diff
- context.emit_unencrypted_log(log)
+ context.emit_public_log(log)
```

These logs were treated as bytes in the node and as hashes in the protocol circuits. Now, public logs are treated as fields everywhere:

```diff
- unencryptedLogs: UnencryptedTxL2Logs
- unencrypted_logs_hashes: [ScopedLogHash; MAX_UNENCRYPTED_LOGS_PER_TX]
+ publicLogs: PublicLog[]
+ public_logs: [PublicLog; MAX_PUBLIC_LOGS_PER_TX]
```

A `PublicLog` contains the log (as an array of fields) and the app address.

This PR also renamed encrypted events to private events:

```diff
- getEncryptedEvents<T>(eventMetadata: EventMetadataDefinition, from: number, limit: number, vpks: Point[]): Promise<T[]>
+ getPrivateEvents<T>(eventMetadata: EventMetadataDefinition, from: number, limit: number, vpks: Point[]): Promise<T[]>
```

## 0.70.0

### \[Aztec.nr] Removal of `getSiblingPath` oracle

Use `getMembershipWitness` oracle instead that returns both the sibling path and index.

## 0.68.0

### \[archiver, node, pxe] Remove contract artifacts in node and archiver and store function names instead

Contract artifacts were only in the archiver for debugging purposes. Instead function names are now (optionally) emitted when registering contract classes

Function changes in the Node interface and Contract Data source interface:

```diff
- addContractArtifact(address: AztecAddress, artifact: ContractArtifact): Promise<void>;
+ registerContractFunctionNames(address: AztecAddress, names: Record<string, string>): Promise<void>;
```

So now the PXE registers this when calling `registerContract()`

```text
await this.node.registerContractFunctionNames(instance.address, functionNames);
```

Function changes in the Archiver

```diff
- addContractArtifact(address: AztecAddress, artifact: ContractArtifact)
-  getContractArtifact(address: AztecAddress)
+  registerContractFunctionNames(address: AztecAddress, names: Record<string, string>): Promise<void>
```

### \[fees, fpc] Changes in setting up FPC as fee payer on AztecJS and method names in FPC

On AztecJS, setting up `PrivateFeePaymentMethod` and `PublicFeePaymentMethod` are now the same. The don't need to specify a sequencer address or which coin to pay in. The coins are set up in the FPC contract!

```diff
- paymentMethod: new PrivateFeePaymentMethod(bananaCoin.address,bananaFPC.address,aliceWallet,sequencerAddress),
+ paymentMethod: new PrivateFeePaymentMethod(bananaFPC.address, aliceWallet),

- paymentMethod: new PublicFeePaymentMethod(bananaCoin.address, bananaFPC.address, aliceWallet),
+ paymentMethod: new PublicFeePaymentMethod(bananaFPC.address, aliceWallet),
```

Changes in `FeePaymentMethod` class in AztecJS

```diff
- getAsset(): AztecAddress;
+ getAsset(): Promise<AztecAddress>;
```

Changes in the token contract: FPC specific methods, `setup_refund()` and `complete_refund()` have minor args rename.

Changes in FPC contract: Rename of args in all of FPC functions as FPC now stores the accepted token address and admin and making it clearer the amounts are corresponding to the accepted token and not fee juice. Also created a public function `pull_funds()` for admin to clawback any money in the FPC

Expect more changes in FPC in the coming releases!

### Name change from `contact` to `sender` in PXE API

`contact` has been deemed confusing because the name is too similar to `contract`. For this reason we've decided to rename it:

```diff
- await pxe.registerContact(address);
+ await pxe.registerSender(address);
- await pxe.getContacts();
+ await pxe.getSenders();
- await pxe.removeContact(address);
+ await pxe.removeSender(address);
```

## 0.67.1

### Noir contracts package no longer exposes artifacts as default export

To reduce loading times, the package `@aztec/noir-contracts.js` no longer exposes all artifacts as its default export. Instead, it exposes a `ContractNames` variable with the list of all contract names available. To import a given artifact, use the corresponding export, such as `@aztec/noir-contracts.js/FPC`.

### Blobs

We now publish the majority of DA in L1 blobs rather than calldata, with only contract class logs remaining as calldata. This replaces all code that touched the `txsEffectsHash`. In the rollup circuits, instead of hashing each child circuit's `txsEffectsHash` to form a tree, we track tx effects by absorbing them into a sponge for blob data (hence the name: `spongeBlob`). This sponge is treated like the state trees in that we check each rollup circuit 'follows' the next:

```diff
- let txs_effects_hash = sha256_to_field(left.txs_effects_hash, right.txs_effects_hash);
+ assert(left.end_sponge_blob.eq(right.start_sponge_blob));
+ let start_sponge_blob = left.start_sponge_blob;
+ let end_sponge_blob = right.end_sponge_blob;
```

This sponge is used in the block root circuit to confirm that an injected array of all `txEffects` does match those rolled up so far in the `spongeBlob`. Then, the `txEffects` array is used to construct and prove opening of the polynomial representing the blob commitment on L1 (this is done efficiently thanks to the Barycentric formula). On L1, we publish the array as a blob and verify the above proof of opening. This confirms that the tx effects in the rollup circuit match the data in the blob:

```diff
- bytes32 txsEffectsHash = TxsDecoder.decode(_body);
+ bytes32 blobHash = _validateBlob(blobInput);
```

Where `blobInput` contains the proof of opening and evaluation calculated in the block root rollup circuit. It is then stored and used as a public input to verifying the epoch proof.

## 0.67.0

### L2 Gas limit of 6M enforced for public portion of TX

A 12M limit was previously enforced per-enqueued-public-call. The protocol now enforces a stricter limit that the entire public portion of a transaction consumes at most 6,000,000 L2 gas.

### \[aztec.nr] Renamed `Header` and associated helpers

The `Header` struct has been renamed to `BlockHeader`, and the `get_header()` family of functions have been similarly renamed to `get_block_header()`.

```diff
- let header = context.get_header_at(block_number);
+ let header = context.get_block_header_at(block_number);
```

### Outgoing Events removed

Previously, every event which was emitted included:

* Incoming Header (to convey the app contract address to the recipient)
* Incoming Ciphertext (to convey the note contents to the recipient)
* Outgoing Header (served as a backup, to convey the app contract address to the "outgoing viewer" - most likely the sender)
* Outgoing Ciphertext (served as a backup, encrypting the symmetric key of the incoming ciphertext to the "outgoing viewer" - most likely the sender)

The latter two have been removed from the `.emit()` functions, so now only an Incoming Header and Incoming Ciphertext will be emitted.

The interface for emitting a note has therefore changed, slightly. No more ovpk's need to be derived and passed into `.emit()` functions.

```diff
- nfts.at(to).insert(&mut new_note).emit(encode_and_encrypt_note(&mut context, from_ovpk_m, to, from));
+ nfts.at(to).insert(&mut new_note).emit(encode_and_encrypt_note(&mut context, to, from));
```

The `getOutgoingNotes` function is removed from the PXE interface.

Some aztec.nr library methods' arguments are simplified to remove an `outgoing_viewer` parameter. E.g. `ValueNote::increment`, `ValueNote::decrement`, `ValueNote::decrement_by_at_most`, `EasyPrivateUint::add`, `EasyPrivateUint::sub`.

Further changes are planned, so that:

* Outgoing ciphertexts (or any kind of abstract ciphertext) can be emitted by a contract, and on the other side discovered and then processed by the contract.
* Headers will be removed, due to the new tagging scheme.

## 0.66

### DEBUG env var is removed

The `DEBUG` variable is no longer used. Use `LOG_LEVEL` with one of `silent`, `fatal`, `error`, `warn`, `info`, `verbose`, `debug`, or `trace`. To tweak log levels per module, add a list of module prefixes with their overridden level. For example, LOG\_LEVEL="info; verbose: aztec<!-- -->:sequencer<!-- -->, aztec<!-- -->:archiver<!-- -->; debug: aztec<!-- -->:kv-store<!-- -->" sets `info` as the default log level, `verbose` for the sequencer and archiver, and `debug` for the kv-store. Module name match is done by prefix.

### `tty` resolve fallback required for browser bundling

When bundling `aztec.js` for web, the `tty` package now needs to be specified as an empty fallback:

```diff
resolve: {
  plugins: [new ResolveTypeScriptPlugin()],
  alias: { './node/index.js': false },
  fallback: {
    crypto: false,
    os: false,
    fs: false,
    path: false,
    url: false,
+   tty: false,
    worker_threads: false,
    buffer: require.resolve('buffer/'),
    util: require.resolve('util/'),
    stream: require.resolve('stream-browserify'),
  },
},
```

## 0.65

### \[aztec.nr] Removed SharedImmutable

The `SharedImmutable` state variable has been removed, since it was essentially the exact same as `PublicImmutable`, which now contains functions for reading from private:

```diff
-   foo: SharedImmutable<T, Context>.
+   foo: PublicImmutable<T, Context>.
```

### \[aztec.nr] SharedImmutable renamings

`SharedImmutable::read_private` and `SharedImmutable::read_public` were renamed to simply `read`, since only one of these versions is ever available depending on the current context.

```diff
// In private
- let value = storage.my_var.read_private();
+ let value = storage.my_var.read();

// In public
- let value = storage.my_var.read_public();
+ let value = storage.my_var.read();
```

### \[aztec.nr] SharedMutable renamings

`SharedMutable` getters (`get_current_value_in_public`, etc.) were renamed by dropping the `_in<public|private|unconstrained>` suffix, since only one of these versions is ever available depending on the current context.

```diff
// In private
- let value = storage.my_var.get_current_value_in_private();
+ let value = storage.my_var.get_current_value();

// In public
- let value = storage.my_var.get_current_value_in_public();
+ let value = storage.my_var.get_current_value();
```

### \[aztec.js] Random addresses are now valid

The `AztecAddress.random()` function now returns valid addresses, i.e. addresses that can receive encrypted messages and therefore have notes be sent to them. `AztecAddress.isValid()` was also added to check for validity of an address.

## 0.63.0

### \[PXE] Note tagging and discovery

PXE's trial decryption of notes has been replaced in favor of a tagging and discovery approach. It is much more efficient and should scale a lot better as the network size increases, since notes can now be discovered on-demand. For the time being, this means that accounts residing *on different PXE instances* should add senders to their contact list, so notes can be discovered (accounts created on the same PXE instance will be added as senders for each other by default)

```diff
+pxe.registerContact(senderAddress)
```

The note discovery process is triggered automatically whenever a contract invokes the `get_notes` oracle, meaning no contract changes are expected. Just in case, every contract has now a utility method `sync_notes` that can trigger the process manually if necessary. This can be useful since now the `DebugInfo` object that can be obtained when sending a tx with the `debug` flag set to true no longer contains the notes that were generated in the transaction:

```diff
const receipt = await inclusionsProofsContract.methods.create_note(owner, 5n).send().wait({ debug: true });
-const { visibleIncomingNotes } = receipt.debugInfo!;
-expect(visibleIncomingNotes.length).toEqual(1);
+await inclusionsProofsContract.methods.sync_notes().simulate();
+const incomingNotes = await wallet.getIncomingNotes({ txHash: receipt.txHash });
+expect(incomingNotes.length).toEqual(1);
```

### \[Token contract] Partial notes related refactor

We've decided to replace the old "shield" flow with one leveraging partial notes. This led to a removal of `shield` and `redeem_shield` functions and an introduction of `transfer_to_private`. An advantage of the new approach is that only 1 tx is required and the API of partial notes is generally nicer. For more information on partial notes refer to docs.

### \[Token contract] Function naming changes

There have been a few naming changes done for improved consistency. These are the renamings: `transfer_public` --> `transfer_in_public` `transfer_from` --> `transfer_in_private` `mint_public` --> `mint_to_public` `burn` --> `burn_private`

## 0.62.0

### \[TXE] Single execution environment

Thanks to recent advancements in Brillig TXE performs every single call as if it was a nested call, spawning a new ACVM or AVM simulator without performance loss. This ensures every single test runs in a consistent environment and allows for clearer test syntax:

```diff
-let my_call_interface = MyContract::at(address).my_function(args);
-env.call_private(my_contract_interface)
+MyContract::at(address).my_function(args).call(&mut env.private());
```

This implies every contract has to be deployed before it can be tested (via `env.deploy` or `env.deploy_self`) and of course it has to be recompiled if its code was changed before TXE can use the modified bytecode.

### Uniqueness of L1 to L2 messages

L1 to L2 messages have been updated to guarantee their uniqueness. This means that the hash of an L1 to L2 message cannot be precomputed, and must be obtained from the `MessageSent` event emitted by the `Inbox` contract, found in the L1 transaction receipt that inserted the message:

```solidity
event MessageSent(uint256 indexed l2BlockNumber, uint256 index, bytes32 indexed hash);
```

This event now also includes an `index`. This index was previously required to consume an L1 to L2 message in a public function, and now it is also required for doing so in a private function, since it is part of the message hash preimage. The `PrivateContext` in aztec-nr has been updated to reflect this:

```diff
pub fn consume_l1_to_l2_message(
    &mut self,
    content: Field,
    secret: Field,
    sender: EthAddress,
+   leaf_index: Field,
) {
```

This change has also modified the internal structure of the archiver database, making it incompatible with previous ones. Last, the API for obtaining an L1 to L2 message membership witness has been simplified to leverage message uniqueness:

```diff
getL1ToL2MessageMembershipWitness(
  blockNumber: L2BlockNumber,
  l1ToL2Message: Fr,
- startIndex: bigint,
): Promise<[bigint, SiblingPath<typeof L1_TO_L2_MSG_TREE_HEIGHT>] | undefined>;
```

### Address is now a point

The address now serves as someone's public key to encrypt incoming notes. An address point has a corresponding address secret, which is used to decrypt the notes encrypted with the address point.

### Notes no longer store a hash of the nullifier public keys, and now store addresses

Because of removing key rotation, we can now store addresses as the owner of a note. Because of this and the above change, we can and have removed the process of registering a recipient, because now we do not need any keys of the recipient.

example\_note.nr

```diff
-npk_m_hash: Field
+owner: AztecAddress
```

PXE Interface

```diff
-registerRecipient(completeAddress: CompleteAddress)
```

## 0.58.0

### \[l1-contracts] Inbox's MessageSent event emits global tree index

Earlier `MessageSent` event in Inbox emitted a subtree index (index of the message in the subtree of the l2Block). But the nodes and Aztec.nr expects the index in the global L1\_TO\_L2\_MESSAGES\_TREE. So to make it easier to parse this, Inbox now emits this global index.

## 0.57.0

### Changes to PXE API and \`ContractFunctionInteraction\`\`

PXE APIs have been refactored to better reflect the lifecycle of a Tx (`execute private -> simulate kernels -> simulate public (estimate gas) -> prove -> send`)

* `.simulateTx`: Now returns a `TxSimulationResult`, containing the output of private execution, kernel simulation and public simulation (optional).
* `.proveTx`: Now accepts the result of executing the private part of a transaction, so simulation doesn't have to happen again.

Thanks to this refactor, `ContractFunctionInteraction` has been updated to remove its internal cache and avoid bugs due to its mutable nature. As a result our type-safe interfaces now have to be used as follows:

```diff
-const action = MyContract.at(address).method(args);
-await action.prove();
-await action.send().wait();
+const action = MyContract.at(address).method(args);
+const provenTx = await action.prove();
+await provenTx.send().wait();
```

It's still possible to use `.send()` as before, which will perform proving under the hood.

More changes are coming to these APIs to better support gas estimation mechanisms and advanced features.

### Changes to public calling convention

Contracts that include public functions (that is, marked with `#[public]`), are required to have a function `public_dispatch(selector: Field)` which acts as an entry point. This will be soon the only public function registered/deployed in contracts. The calling convention is updated so that external calls are made to this function.

If you are writing your contracts using Aztec-nr, there is nothing you need to change. The `public_dispatch` function is automatically generated by the `#[aztec]` macro.

### \[Aztec.nr] Renamed `unsafe_rand` to `random`

Since this is an `unconstrained` function, callers are already supposed to include an `unsafe` block, so this function has been renamed for reduced verbosity.

```diff
-use aztec::oracle::unsafe_rand::unsafe_rand;
+use aztec::oracle::random::random;

-let random_value = unsafe { unsafe_rand() };
+let random_value = unsafe { random() };
```

### \[Aztec.js] Removed `L2Block.fromFields`

`L2Block.fromFields` was a syntactic sugar which is causing [issues](https://github.com/AztecProtocol/aztec-packages/issues/8340) so we've removed it.

```diff
-const l2Block = L2Block.fromFields({ header, archive, body });
+const l2Block = new L2Block(archive, header, body);
```

### \[Aztec.nr] Removed `SharedMutablePrivateGetter`

This state variable was deleted due to it being difficult to use safely.

### \[Aztec.nr] Changes to `NullifiableNote`

The `compute_nullifier_without_context` function is now `unconstrained`. It had always been meant to be called in unconstrained contexts (which is why it did not receive the `context` object), but now that Noir supports trait functions being `unconstrained` this can be implemented properly. Users must add the `unconstrained` keyword to their implementations of the trait:

```diff
impl NullifiableNote for MyCustomNote {
-    fn compute_nullifier_without_context(self) -> Field {
+    unconstrained fn compute_nullifier_without_context(self) -> Field {
```

### \[Aztec.nr] Make `TestEnvironment` unconstrained

All of `TestEnvironment`'s functions are now `unconstrained`, preventing accidentally calling them in a constrained circuit, among other kinds of user error. Becuase they work with mutable references, and these are not allowed to cross the constrained/unconstrained barrier, tests that use `TestEnvironment` must also become `unconstrained`. The recommended practice is to make *all* Noir tests and test helper functions be \`unconstrained:

```diff
#[test]
-fn test_my_function() {
+unconstrained fn test_my_function() {
    let env = TestEnvironment::new();
```

### \[Aztec.nr] removed `encode_and_encrypt_note` and renamed `encode_and_encrypt_note_with_keys` to `encode_and_encrypt_note`

```diff
contract XYZ {
-   use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_note_with_keys;
+   use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_note;
...

-    numbers.at(owner).initialize(&mut new_number).emit(encode_and_encrypt_note_with_keys(&mut context, owner_ovpk_m, owner_ivpk_m, owner));
+    numbers.at(owner).initialize(&mut new_number).emit(encode_and_encrypt_note(&mut context, owner_ovpk_m, owner_ivpk_m, owner));
}
```

## 0.56.0

### \[Aztec.nr] Changes to contract definition

We've migrated the Aztec macros to use the newly introduce meta programming Noir feature. Due to being Noir-based, the new macros are less obscure and can be more easily modified.

As part of this transition, some changes need to be applied to Aztec contracts:

* The top level `contract` block needs to have the `#[aztec]` macro applied to it.
* All `#[aztec(name)]` macros are renamed to `#[name]`.
* The storage struct (the one that gets the `#[storage]` macro applied) but be generic over a `Context` type, and all state variables receive this type as their last generic type parameter.

```diff
+ use dep::aztec::macros::aztec;

#[aztec]
contract Token {
+    use dep::aztec::macros::{storage::storage, events::event, functions::{initializer, private, view, public}};

-    #[aztec(storage)]
-    struct Storage {
+    #[storage]
+    struct Storage<Context> {
-        admin: PublicMutable<AztecAddress>,
+        admin: PublicMutable<AztecAddress, Context>,
-        minters: Map<AztecAddress, PublicMutable<bool>>,
+        minters: Map<AztecAddress, PublicMutable<bool, Context>, Context>,
    }

-    #[aztec(public)]
-    #[aztec(initializer)]
+    #[public]
+    #[initializer]
    fn constructor(admin: AztecAddress, name: str<31>, symbol: str<31>, decimals: u8) {
        ...
    }

-    #[aztec(public)]
-    #[aztec(view)]
-    fn public_get_name() -> FieldCompressedString {
+    #[public]
+    #[view]
    fn public_get_name() -> FieldCompressedString {
        ...
    }
```

### \[Aztec.nr] Changes to `NoteInterface`

The new macro model prevents partial trait auto-implementation: they either implement the entire trait or none of it. This means users can no longer implement part of `NoteInterface` and have the rest be auto-implemented.

For this reason we've separated the methods which are auto-implemented and those which needs to be implemented manually into two separate traits: the auto-implemented ones stay in the `NoteInterface` trace and the manually implemented ones were moved to `NullifiableNote` (name likely to change):

```diff
-#[aztec(note)]
+#[note]
struct AddressNote {
    ...
}

-impl NoteInterface<ADDRESS_NOTE_LEN, ADDRESS_NOTE_BYTES_LEN> for AddressNote {
+impl NullifiableNote for AddressNote {
    fn compute_nullifier(self, context: &mut PrivateContext, note_hash_for_nullify: Field) -> Field {
        ...
    }

    fn compute_nullifier_without_context(self) -> Field {
        ...
    }
}
```

### \[Aztec.nr] Changes to contract interface

The `Contract::storage()` static method has been renamed to `Contract::storage_layout()`.

```diff
-    let fee_payer_balances_slot = derive_storage_slot_in_map(Token::storage().balances.slot, fee_payer);
-    let user_balances_slot = derive_storage_slot_in_map(Token::storage().balances.slot, user);
+    let fee_payer_balances_slot = derive_storage_slot_in_map(Token::storage_layout().balances.slot, fee_payer);
+    let user_balances_slot = derive_storage_slot_in_map(Token::storage_layout().balances.slot, user);
```

### Key rotation removed

The ability to rotate incoming, outgoing, nullifying and tagging keys has been removed - this feature was easy to misuse and not worth the complexity and gate count cost. As part of this, the Key Registry contract has also been deleted. The API for fetching public keys has been adjusted accordingly:

```diff
- let keys = get_current_public_keys(&mut context, account);
+ let keys = get_public_keys(account);
```

### \[Aztec.nr] Rework `NoteGetterOptions::select`

The `select` function in both `NoteGetterOptions` and `NoteViewerOptions` no longer takes an `Option` of a comparator, but instead requires an explicit comparator to be passed. Additionally, the order of the parameters has been changed so that they are `(lhs, operator, rhs)`. These two changes should make invocations of the function easier to read:

```diff
- options.select(ValueNote::properties().value, amount, Option::none())
+ options.select(ValueNote::properties().value, Comparator.EQ, amount)
```

## 0.53.0

### \[Aztec.nr] Remove `OwnedNote` and create `UintNote`

`OwnedNote` allowed having a U128 `value` in the custom note while `ValueNote` restricted to just a Field.

We have removed `OwnedNote` but are introducing a more genric `UintNote` within aztec.nr

```text
#[aztec(note)]
struct UintNote {
    // The integer stored by the note
    value: U128,
    // The nullifying public key hash is used with the nsk_app to ensure that the note can be privately spent.
    npk_m_hash: Field,
    // Randomness of the note to hide its contents
    randomness: Field,
}
```

### \[TXE] logging

You can now use `debug_log()` within your contract to print logs when using the TXE

Remember to set the following environment variables to activate debug logging:

```bash
export DEBUG="aztec:*"
export LOG_LEVEL="debug"
```

### \[Account] no assert in is\_valid\_impl

`is_valid_impl` method in account contract asserted if signature was true. Instead now we will return the verification to give flexibility to developers to handle it as they please.

```diff
- let verification = std::ecdsa_secp256k1::verify_signature(public_key.x, public_key.y, signature, hashed_message);
- assert(verification == true);
- true
+ std::ecdsa_secp256k1::verify_signature(public_key.x, public_key.y, signature, hashed_message)
```

## 0.49.0

### Key Rotation API overhaul

Public keys (ivpk, ovpk, npk, tpk) should no longer be fetched using the old `get_[x]pk_m` methods on the `Header` struct, but rather by calling `get_current_public_keys`, which returns a `PublicKeys` struct with all four keys at once:

```diff
+use dep::aztec::keys::getters::get_current_public_keys;

-let header = context.header();
-let owner_ivpk_m = header.get_ivpk_m(&mut context, owner);
-let owner_ovpk_m = header.get_ovpk_m(&mut context, owner);
+let owner_keys = get_current_public_keys(&mut context, owner);
+let owner_ivpk_m = owner_keys.ivpk_m;
+let owner_ovpk_m = owner_keys.ovpk_m;
```

If using more than one key per account, this will result in very large circuit gate count reductions.

Additionally, `get_historical_public_keys` was added to support reading historical keys using a historical header:

```diff
+use dep::aztec::keys::getters::get_historical_public_keys;

let historical_header = context.header_at(some_block_number);
-let owner_ivpk_m = header.get_ivpk_m(&mut context, owner);
-let owner_ovpk_m = header.get_ovpk_m(&mut context, owner);
+let owner_keys = get_historical_public_keys(historical_header, owner);
+let owner_ivpk_m = owner_keys.ivpk_m;
+let owner_ovpk_m = owner_keys.ovpk_m;
```

## 0.48.0

### NoteInterface changes

`compute_note_hash_and_nullifier*` functions were renamed as `compute_nullifier*` and the `compute_nullifier` function now takes `note_hash_for_nullify` as an argument (this allowed us to reduce gate counts and the hash was typically computed before). Also `compute_note_hash_for_consumption` function was renamed as `compute_note_hash_for_nullification`.

```diff
impl NoteInterface<VALUE_NOTE_LEN, VALUE_NOTE_BYTES_LEN> for ValueNote {
-    fn compute_note_hash_and_nullifier(self, context: &mut PrivateContext) -> (Field, Field) {
-        let note_hash_for_nullify = compute_note_hash_for_consumption(self);
-        let secret = context.request_nsk_app(self.npk_m_hash);
-        let nullifier = poseidon2_hash_with_separator([
-            note_hash_for_nullify,
-            secret,
-        ],
-            DOM_SEP__NOTE_NULLIFIER as Field,
-        );
-        (note_hash_for_nullify, nullifier)
-    }
-    fn compute_note_hash_and_nullifier_without_context(self) -> (Field, Field) {
-        let note_hash_for_nullify = compute_note_hash_for_consumption(self);
-        let secret = get_nsk_app(self.npk_m_hash);
-        let nullifier = poseidon2_hash_with_separator([
-            note_hash_for_nullify,
-            secret,
-        ],
-            DOM_SEP__NOTE_NULLIFIER as Field,
-        );
-        (note_hash_for_nullify, nullifier)
-    }

+    fn compute_nullifier(self, context: &mut PrivateContext, note_hash_for_nullify: Field) -> Field {
+        let secret = context.request_nsk_app(self.npk_m_hash);
+        poseidon2_hash_with_separator([
+            note_hash_for_nullify,
+            secret
+        ],
+            DOM_SEP__NOTE_NULLIFIER as Field,
+        )
+    }
+    fn compute_nullifier_without_context(self) -> Field {
+        let note_hash_for_nullify = compute_note_hash_for_nullification(self);
+        let secret = get_nsk_app(self.npk_m_hash);
+        poseidon2_hash_with_separator([
+            note_hash_for_nullify,
+            secret,
+        ],
+            DOM_SEP__NOTE_NULLIFIER as Field,
+        )
+    }
}
```

### Fee Juice rename

The name of the canonical Gas contract has changed to Fee Juice. Update noir code:

```diff
-GasToken::at(contract_address)
+FeeJuice::at(contract_address)
```

Additionally, `NativePaymentMethod` and `NativePaymentMethodWithClaim` have been renamed to `FeeJuicePaymentMethod` and `FeeJuicePaymentMethodWithClaim`.

### PrivateSet::pop\_notes(...)

The most common flow when working with notes is obtaining them from a `PrivateSet` via `get_notes(...)` and then removing them via `PrivateSet::remove(...)`. This is cumbersome and it results in unnecessary constraints due to a redundant note read request checks in the remove function.

For this reason we've implemented `pop_notes(...)` which gets the notes, removes them from the set and returns them. This tight coupling of getting notes and removing them allowed us to safely remove the redundant read request check.

Token contract diff:

```diff
-let options = NoteGetterOptions::with_filter(filter_notes_min_sum, target_amount).set_limit(max_notes);
-let notes = self.map.at(owner).get_notes(options);
-let mut subtracted = U128::from_integer(0);
-for i in 0..options.limit {
-    if i < notes.len() {
-        let note = notes.get_unchecked(i);
-        self.map.at(owner).remove(note);
-        subtracted = subtracted + note.get_amount();
-    }
-}
-assert(minuend >= subtrahend, "Balance too low");
+let options = NoteGetterOptions::with_filter(filter_notes_min_sum, target_amount).set_limit(max_notes);
+let notes = self.map.at(owner).pop_notes(options);
+let mut subtracted = U128::from_integer(0);
+for i in 0..options.limit {
+    if i < notes.len() {
+        let note = notes.get_unchecked(i);
+        subtracted = subtracted + note.get_amount();
+    }
+}
+assert(minuend >= subtrahend, "Balance too low");
```

Note that `pop_notes` may not have obtained and removed any notes! The caller must place checks on the returned notes, e.g. in the example above by checking a sum of balances, or by checking the number of returned notes (`assert_eq(notes.len(), expected_num_notes)`).

## 0.47.0

# \[Aztec sandbox] TXE deployment changes

The way simulated deployments are done in TXE tests has changed to avoid relying on TS interfaces. It is now possible to do it by directly pointing to a Noir standalone contract or workspace:

```diff
-let deployer = env.deploy("path_to_contract_ts_interface");
+let deployer = env.deploy("path_to_contract_root_folder_where_nargo_toml_is", "ContractName");
```

Extended syntax for more use cases:

```rust
// The contract we're testing
env.deploy_self("ContractName"); // We have to provide ContractName since nargo isn't ready to support multi-contract files

// A contract in a workspace
env.deploy("../path/to/workspace@package_name", "ContractName"); // This format allows locating the artifact in the root workspace target folder, regardless of internal code organization
```

The deploy function returns a `Deployer`, which requires performing a subsequent call to `without_initializer()`, `with_private_initializer()` or `with_public_initializer()` just like before in order to **actually** deploy the contract.

### \[CLI] Command refactor and unification + `aztec test`

Sandbox commands have been cleaned up and simplified. Doing `aztec-up` now gets you the following top-level commands:

`aztec`: All the previous commands + all the CLI ones without having to prefix them with cli. Run `aztec` for help! `aztec-nargo`: No changes

**REMOVED/RENAMED**:

* `aztec-sandbox` and `aztec sandbox`: now `aztec start --sandbox`
* `aztec-builder`: now `aztec codegen` and `aztec update`

**ADDED**:

* `aztec test [options]`: runs `aztec start --txe && aztec-nargo test --oracle-resolver http://aztec:8081 --silence-warnings [options]` via docker-compose allowing users to easily run contract tests using TXE

## 0.45.0

### \[Aztec.nr] Remove unencrypted logs from private

They leak privacy so is a footgun!

## 0.44.0

### \[Aztec.nr] Autogenerate Serialize methods for events

```diff
#[aztec(event)]
struct WithdrawalProcessed {
    who: Field,
    amount: Field,
}

-impl Serialize<2> for WithdrawalProcessed {
-    fn serialize(self: Self) -> [Field; 2] {
-        [self.who.to_field(), self.amount as Field]
-    }
}
```

### \[Aztec.nr] rename `encode_and_encrypt_with_keys` to `encode_and_encrypt_note_with_keys`

```diff
contract XYZ {
-   use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_with_keys;
+   use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_note_with_keys;
....

-    numbers.at(owner).initialize(&mut new_number).emit(encode_and_encrypt_with_keys(&mut context, owner_ovpk_m, owner_ivpk_m));
+    numbers.at(owner).initialize(&mut new_number).emit(encode_and_encrypt_note_with_keys(&mut context, owner_ovpk_m, owner_ivpk_m));
}
```

### \[Aztec.nr] changes to `NoteInterface`

`compute_nullifier` function was renamed to `compute_note_hash_and_nullifier` and now the function has to return not only the nullifier but also the note hash used to compute the nullifier. The same change was done to `compute_nullifier_without_context` function. These changes were done because having the note hash exposed allowed us to not having to re-compute it again in `destroy_note` function of Aztec.nr which led to significant decrease in gate counts (see the [optimization PR](https://github.com/AztecProtocol/aztec-packages/pull/7103) for more details).

```diff
- impl NoteInterface<VALUE_NOTE_LEN, VALUE_NOTE_BYTES_LEN> for ValueNote {
-    fn compute_nullifier(self, context: &mut PrivateContext) -> Field {
-        let note_hash_for_nullify = compute_note_hash_for_consumption(self);
-        let secret = context.request_nsk_app(self.npk_m_hash);
-        poseidon2_hash([
-            note_hash_for_nullify,
-            secret,
-            DOM_SEP__NOTE_NULLIFIER as Field,
-        ])
-    }
-
-    fn compute_nullifier_without_context(self) -> Field {
-        let note_hash_for_nullify = compute_note_hash_for_consumption(self);
-        let secret = get_nsk_app(self.npk_m_hash);
-        poseidon2_hash([
-            note_hash_for_nullify,
-            secret,
-            DOM_SEP__NOTE_NULLIFIER as Field,
-        ])
-    }
- }
+ impl NoteInterface<VALUE_NOTE_LEN, VALUE_NOTE_BYTES_LEN> for ValueNote {
+    fn compute_note_hash_and_nullifier(self, context: &mut PrivateContext) -> (Field, Field) {
+        let note_hash_for_nullify = compute_note_hash_for_consumption(self);
+        let secret = context.request_nsk_app(self.npk_m_hash);
+        let nullifier = poseidon2_hash([
+            note_hash_for_nullify,
+            secret,
+            DOM_SEP__NOTE_NULLIFIER as Field,
+        ]);
+        (note_hash_for_nullify, nullifier)
+    }
+
+    fn compute_note_hash_and_nullifier_without_context(self) -> (Field, Field) {
+        let note_hash_for_nullify = compute_note_hash_for_consumption(self);
+        let secret = get_nsk_app(self.npk_m_hash);
+        let nullifier = poseidon2_hash([
+            note_hash_for_nullify,
+            secret,
+            DOM_SEP__NOTE_NULLIFIER as Field,
+        ]);
+        (note_hash_for_nullify, nullifier)
+    }
+ }
```

### \[Aztec.nr] `note_getter` returns `BoundedVec`

The `get_notes` and `view_notes` function no longer return an array of options (i.e. `[Option<Note>, N_NOTES]`) but instead a `BoundedVec<Note, N_NOTES>`. This better conveys the useful property the old array had of having all notes collapsed at the beginning of the array, which allows for powerful optimizations and gate count reduction when setting the `options.limit` value.

A `BoundedVec` has a `max_len()`, which equals the number of elements it can hold, and a `len()`, which equals the number of elements it currently holds. Since `len()` is typically not knwon at compile time, iterating over a `BoundedVec` looks slightly different than iterating over an array of options:

```diff
- let option_notes = get_notes(options);
- for i in 0..option_notes.len() {
-     if option_notes[i].is_some() {
-         let note = option_notes[i].unwrap_unchecked();
-     }
- }
+ let notes = get_notes(options);
+ for i in 0..notes.max_len() {
+     if i < notes.len() {
+         let note = notes.get_unchecked(i);
+     }
+ }
```

To further reduce gate count, you can iterate over `options.limit` instead of `max_len()`, since `options.limit` is guaranteed to be larger or equal to `len()`, and smaller or equal to `max_len()`:

```diff
- for i in 0..notes.max_len() {
+ for i in 0..options.limit {
```

### \[Aztec.nr] static private authwit

The private authwit validation is now making a static call to the account contract instead of passing over control flow. This is to ensure that it cannot be used for re-entry.

To make this change however, we cannot allow emitting a nullifier from the account contract, since that would break the static call. Instead, we will be changing the `spend_private_authwit` to a `verify_private_authwit` and in the `auth` library emit the nullifier. This means that the "calling" contract will now be emitting the nullifier, and not the account. For example, for a token contract, the nullifier is now emitted by the token contract. However, as this is done inside the `auth` library, the token contract doesn't need to change much.

The biggest difference is related to "cancelling" an authwit. Since it is no longer in the account contract, you cannot just emit a nullifier from it anymore. Instead it must rely on the token contract providing functionality for cancelling.

There are also a few general changes to how authwits are generated, namely to more easily support the data required for a validity lookup now. Previously we could lookup the `message_hash` directly at the account contract, now we instead need to use the `inner_hash` and the contract of the consumer to figure out if it have already been emitted.

A minor extension have been made to the authwit creations to make it easier to sign a specific a hash with a specific caller, e.g., the `inner_hash` can be provided as `{consumer, inner_hash}` to the `createAuthWit` where it previously needed to do a couple of manual steps to compute the outer hash. The `computeOuterAuthWitHash` have been made internal and the `computeAuthWitMessageHash` can instead be used to compute the values similarly to other authwit computations.

```diff
const innerHash = computeInnerAuthWitHash([Fr.ZERO, functionSelector.toField(), entrypointPackedArgs.hash]);
-const outerHash = computeOuterAuthWitHash(
-    this.dappEntrypointAddress,
-    new Fr(this.chainId),
-    new Fr(this.version),
-    innerHash,
-);
+const messageHash = computeAuthWitMessageHash(
+    { consumer: this.dappEntrypointAddress, innerHash },
+    { chainId: new Fr(this.chainId), version: new Fr(this.version) },
+);
```

If the wallet is used to compute the authwit, it will populate the chain id and version instead of requiring it to be provided by tha actor.

```diff
const innerHash = computeInnerAuthWitHash([Fr.fromString('0xdead')]);
-const outerHash = computeOuterAuthWitHash(wallets[1].getAddress(), chainId, version, innerHash);
-const witness = await wallets[0].createAuthWit(outerHash);
+ const witness = await wallets[0].createAuthWit({ comsumer: accounts[1].address, inner_hash });
```

## 0.43.0

### \[Aztec.nr] break `token.transfer()` into `transfer` and `transferFrom`

Earlier we had just one function - `transfer()` which used authwits to handle the case where a contract/user wants to transfer funds on behalf of another user. To reduce circuit sizes and proof times, we are breaking up `transfer` and introducing a dedicated `transferFrom()` function like in the ERC20 standard.

### \[Aztec.nr] `options.limit` has to be constant

The `limit` parameter in `NoteGetterOptions` and `NoteViewerOptions` is now required to be a compile-time constant. This allows performing loops over this value, which leads to reduced circuit gate counts when setting a `limit` value.

### \[Aztec.nr] canonical public authwit registry

The public authwits are moved into a shared registry (auth registry) to make it easier for sequencers to approve for their non-revertible (setup phase) whitelist. Previously, it was possible to DOS a sequencer by having a very expensive authwit validation that fails at the end, now the whitelist simply need the registry.

Notable, this means that consuming a public authwit will no longer emit a nullifier in the account contract but instead update STORAGE in the public domain. This means that there is a larger difference between private and public again. However, it also means that if contracts need to approve, and use the approval in the same tx, it is transient and don't need to go to DA (saving 96 bytes).

For the typescript wallets this is handled so the APIs don't change, but account contracts should get rid of their current setup with `approved_actions`.

```diff
- let actions = AccountActions::init(&mut context, ACCOUNT_ACTIONS_STORAGE_SLOT, is_valid_impl);
+ let actions = AccountActions::init(&mut context, is_valid_impl);
```

For contracts we have added a `set_authorized` function in the auth library that can be used to set values in the registry.

```diff
- storage.approved_action.at(message_hash).write(true);
+ set_authorized(&mut context, message_hash, true);
```

### \[Aztec.nr] emit encrypted logs

Emitting or broadcasting encrypted notes are no longer done as part of the note creation, but must explicitly be either emitted or discarded instead.

```diff
+ use dep::aztec::encrypted_logs::encrypted_note_emission::{encode_and_encrypt, encode_and_encrypt_with_keys};

- storage.balances.sub(from, amount);
+ storage.balances.sub(from, amount).emit(encode_and_encrypt_with_keys(&mut context, from, from));
+ storage.balances.sub(from, amount).emit(encode_and_encrypt_with_keys(&mut context, from_ovpk, from_ivpk));
+ storage.balances.sub(from, amount).discard();
```

## 0.42.0

### \[Aztec.nr] Unconstrained Context

Top-level unconstrained execution is now marked by the new `UnconstrainedContext`, which provides access to the block number and contract address being used in the simulation. Any custom state variables that provided unconstrained functions should update their specialization parameter:

```diff
+ use dep::aztec::context::UnconstrainedContext;

- impl MyStateVariable<()> {
+ impl MyStateVariable<UnconstrainedContext> {
```

### \[Aztec.nr] Filtering is now constrained

The `filter` argument of `NoteGetterOptions` (typically passed via the `with_filter()` function) is now applied in a constraining environment, meaning any assertions made during the filtering are guaranteed to hold. This mirrors the behavior of the `select()` function.

### \[Aztec.nr] Emitting encrypted notes and logs

The `emit_encrypted_log` context function is now `encrypt_and_emit_log` or `encrypt_and_emit_note`.

```diff
- context.emit_encrypted_log(log1);
+ context.encrypt_and_emit_log(log1);
+ context.encrypt_and_emit_note(note1);
```

Broadcasting a note will call `encrypt_and_emit_note` in the background. To broadcast a generic event, use `encrypt_and_emit_log` with the same encryption parameters as notes require. Currently, only fields and arrays of fields are supported as events.

By default, logs emitted via `encrypt_and_emit_log` will be siloed with a *masked* contract address. To force the contract address to be revealed, so everyone can check it rather than just the log recipient, provide `randomness = 0`.

## Public execution migrated to the Aztec Virtual Machine

**What does this mean for me?**

It should be mostly transparent, with a few caveats:

* Not all Noir blackbox functions are supported by the AVM. Only `Sha256`, `PedersenHash`, `Poseidon2Permutation`, `Keccak256`, and `ToRadix` are supported.
* For public functions, `context.nullifier_exists(...)` will now also consider pending nullifiers.
* The following methods of `PublicContext` are not supported anymore: `fee_recipient`, `fee_per_da_gas`, `fee_per_l2_gas`, `call_public_function_no_args`, `static_call_public_function_no_args`, `delegate_call_public_function_no_args`, `call_public_function_with_packed_args`, `set_return_hash`, `finish`. However, in terms of functionality, the new context's interface should be equivalent (unless otherwise specified in this list).
* Delegate calls are not yet supported in the AVM.
* If you have types with custom serialization that you use across external contracts calls, you might need to modify its serialization to match how Noir would serialize it. This is a known problem unrelated to the AVM, but triggered more often when using it.
* A few error messages might change format, so you might need to change your test assertions.

**Internal details**

Before this change, public bytecode was executed using the same simulator as in private: the ACIR simulator (and internally, the Brillig VM). On the Aztec.nr side, public functions accessed the context through `PublicContext`.

After this change, public bytecode will be run using the AVM simulator (the simulator for our upcoming zkVM). This bytecode is generated from Noir contracts in two steps: First, `nargo compile` produces an artifact which has Brillig bytecode for public functions, just as it did before. Second: the `avm-transpiler` takes that artifact, and it transpiles Brillig bytecode to AVM bytecode. This final artifact can now be deployed and used with the new public runtime.

On the Aztec.nr side, public functions keep accessing the context using `PublicContext` but the underlying implementation is switch with what formerly was the `AvmContext`.

## 0.41.0

### \[Aztec.nr] State variable rework

Aztec.nr state variables have been reworked so that calling private functions in public and vice versa is detected as an error during compilation instead of at runtime. This affects users in a number of ways:

#### New compile time errors

It used to be that calling a state variable method only available in public from a private function resulted in obscure runtime errors in the form of a failed `_is_some` assertion.

Incorrect usage of the state variable methods now results in compile time errors. For example, given the following function:

```rust
#[aztec(public)]
fn get_decimals() -> pub u8 {
    storage.decimals.read_private()
}
```

The compiler will now error out with

```text
Expected type SharedImmutable<_, &mut PrivateContext>, found type SharedImmutable<u8, &mut PublicContext>
```

The key component is the second generic parameter: the compiler expects a `PrivateContext` (becuse `read_private` is only available during private execution), but a `PublicContext` is being used instead (because of the `#[aztec(public)]` attribute).

#### Generic parameters in `Storage`

The `Storage` struct (the one marked with `#[aztec(storage)]`) should now be generic over a `Context` type, which matches the new generic parameter of all Aztec.nr libraries. This parameter is always the last generic parameter.

This means that, without any additional features, we'd end up with some extra boilerplate when declaring this struct:

```diff
#[aztec(storage)]
- struct Storage {
+ struct Storage<Context> {
-   nonce_for_burn_approval: PublicMutable<Field>,
+   nonce_for_burn_approval: PublicMutable<Field, Context>,
-   portal_address: SharedImmutable<EthAddress>,
+   portal_address: SharedImmutable<EthAddress, Context>,
-   approved_action: Map<Field, PublicMutable<bool>>,
+   approved_action: Map<Field, PublicMutable<bool, Context>, Context>,
}
```

Because of this, the `#[aztec(storage)]` macro has been updated to **automatically inject** this `Context` generic parameter. The storage declaration does not require any changes.

#### Removal of `Context`

The `Context` type no longer exists. End users typically didn't use it, but if imported it needs to be deleted.

### \[Aztec.nr] View functions and interface navigation

It is now possible to explicitly state a function doesn't perform any state alterations (including storage, logs, nullifiers and/or messages from L2 to L1) with the `#[aztec(view)]` attribute, similarly to solidity's `view` function modifier.

```diff
    #[aztec(public)]
+   #[aztec(view)]
    fn get_price(asset_id: Field) -> Asset {
        storage.assets.at(asset_id).read()
    }
```

View functions only generate a `StaticCallInterface` that doesn't include `.call` or `.enqueue` methods. Also, the denomination `static` has been completely removed from the interfaces, in favor of the more familiar `view`

```diff
- let price = PriceFeed::at(asset.oracle).get_price(0).static_call(&mut context).price;
+ let price = PriceFeed::at(asset.oracle).get_price(0).view(&mut context).price;
```

```diff
#[aztec(private)]
fn enqueue_public_get_value_from_child(target_contract: AztecAddress, value: Field) {
-   StaticChild::at(target_contract).pub_get_value(value).static_enqueue(&mut context);
+   StaticChild::at(target_contract).pub_get_value(value).enqueue_view(&mut context);
}
```

Additionally, the Noir LSP will now honor "go to definitions" requests for contract interfaces (Ctrl+click), taking the user to the original function implementation.

### \[Aztec.js] Simulate changes

* `.simulate()` now tracks closer the process performed by `.send().wait()`, specifically going through the account contract entrypoint instead of directly calling the intended function.
* `wallet.viewTx(...)` has been renamed to `wallet.simulateUnconstrained(...)` to better clarify what it does.

### \[Aztec.nr] Keys: Token note now stores an owner master nullifying public key hash instead of an owner address

i.e.

```diff
struct TokenNote {
    amount: U128,
-   owner: AztecAddress,
+   npk_m_hash: Field,
    randomness: Field,
}
```

Creating a token note and adding it to storage now looks like this:

```diff
- let mut note = ValueNote::new(new_value, owner);
- storage.a_private_value.insert(&mut note, true);
+ let owner_npk_m_hash = get_npk_m_hash(&mut context, owner);
+ let owner_ivpk_m = get_ivpk_m(&mut context, owner);
+ let mut note = ValueNote::new(new_value, owner_npk_m_hash);
+ storage.a_private_value.insert(&mut note, true, owner_ivpk_m);
```

Computing the nullifier similarly changes to use this master nullifying public key hash.

## 0.40.0

### \[Aztec.nr] Debug logging

The function `debug_log_array_with_prefix` has been removed. Use `debug_log_format` with `{}` instead. The special sequence `{}` will be replaced with the whole array. You can also use `{0}`, `{1}`, ... as usual with `debug_log_format`.

```diff
- debug_log_array_with_prefix("Prefix", my_array);
+ debug_log_format("Prefix {}", my_array);
```

## 0.39.0

### \[Aztec.nr] Mutable delays in `SharedMutable`

The type signature for `SharedMutable` changed from `SharedMutable<T, DELAY>` to `SharedMutable<T, INITIAL_DELAY>`. The behavior is the same as before, except the delay can now be changed after deployment by calling `schedule_delay_change`.

### \[Aztec.nr] get\_public\_key oracle replaced with get\_ivpk\_m

When implementing changes according to a new key scheme we had to change oracles. What used to be called encryption public key is now master incoming viewing public key.

```diff
- use dep::aztec::oracles::get_public_key::get_public_key;
+ use dep::aztec::keys::getters::get_ivpk_m;

- let encryption_pub_key = get_public_key(self.owner);
+ let ivpk_m = get_ivpk_m(context, self.owner);
```

## 0.38.0

### \[Aztec.nr] Emitting encrypted logs

The `emit_encrypted_log` function is now a context method.

```diff
- use dep::aztec::log::emit_encrypted_log;
- use dep::aztec::logs::emit_encrypted_log;

- emit_encrypted_log(context, log1);
+ context.emit_encrypted_log(log1);
```

## 0.36.0

### `FieldNote` removed

`FieldNote` only existed for testing purposes, and was not a note type that should be used in any real application. Its name unfortunately led users to think that it was a note type suitable to store a `Field` value, which it wasn't.

If using `FieldNote`, you most likely want to use `ValueNote` instead, which has both randomness for privacy and an owner for proper nullification.

### `SlowUpdatesTree` replaced for `SharedMutable`

The old `SlowUpdatesTree` contract and libraries have been removed from the codebase, use the new `SharedMutable` library instead. This will require that you add a global variable specifying a delay in blocks for updates, and replace the slow updates tree state variable with `SharedMutable` variables.

```diff
+ global CHANGE_ROLES_DELAY_BLOCKS = 5;

struct Storage {
-  slow_update: SharedImmutable<AztecAddress>,
+  roles: Map<AztecAddress, SharedMutable<UserFlags, CHANGE_ROLES_DELAY_BLOCKS>>,
}
```

Reading from `SharedMutable` is much simpler, all that's required is to call `get_current_value_in_public` or `get_current_value_in_private`, depending on the domain.

```diff
- let caller_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(context.msg_sender().to_field()).call(&mut context)));
+ let caller_roles = storage.roles.at(context.msg_sender()).get_current_value_in_public();
```

Finally, you can remove all capsule usage on the client code or tests, since those are no longer required when working with `SharedMutable`.

### \[Aztec.nr & js] Portal addresses

Deployments have been modified. No longer are portal addresses treated as a special class, being immutably set on creation of a contract. They are no longer passed in differently compared to the other variables and instead should be implemented using usual storage by those who require it. One should use the storage that matches the usecase - likely shared storage to support private and public.

This means that you will likely add the portal as a constructor argument

```diff
- fn constructor(token: AztecAddress) {
-    storage.token.write(token);
- }
+ struct Storage {
    ...
+   portal_address: SharedImmutable<AztecAddress>,
+ }
+ fn constructor(token: AztecAddress, portal_address: EthAddress) {
+    storage.token.write(token);
+    storage.portal_address.initialize(portal_address);
+ }
```

And read it from storage whenever needed instead of from the context.

```diff
- context.this_portal_address(),
+ storage.portal_address.read_public(),
```

### \[Aztec.nr] Oracles

Oracle `get_nullifier_secret_key` was renamed to `get_app_nullifier_secret_key` and `request_nullifier_secret_key` function on PrivateContext was renamed as `request_app_nullifier_secret_key`.

```diff
- let secret = get_nullifier_secret_key(self.owner);
+ let secret = get_app_nullifier_secret_key(self.owner);
```

```diff
- let secret = context.request_nullifier_secret_key(self.owner);
+ let secret = context.request_app_nullifier_secret_key(self.owner);
```

### \[Aztec.nr] Contract interfaces

It is now possible to import contracts on another contracts and use their automatic interfaces to perform calls. The interfaces have the same name as the contract, and are automatically exported. Parameters are automatically serialized (using the `Serialize<N>` trait) and return values are automatically deserialized (using the `Deserialize<N>` trait). Serialize and Deserialize methods have to conform to the standard ACVM serialization schema for the interface to work!

1. Only fixed length types are supported
2. All numeric types become Fields
3. Strings become arrays of Fields, one per char
4. Arrays become arrays of Fields following rules 2 and 3
5. Structs become arrays of Fields, with every item defined in the same order as they are in Noir code, following rules 2, 3, 4 and 5 (recursive)

```diff
- context.call_public_function(
-   storage.gas_token_address.read_private(),
-   FunctionSelector::from_signature("pay_fee(Field)"),
-   [42]
- );
-
- context.call_public_function(
-   storage.gas_token_address.read_private(),
-   FunctionSelector::from_signature("pay_fee(Field)"),
-   [42]
- );
-
- let _ = context.call_private_function(
-           storage.subscription_token_address.read_private(),
-           FunctionSelector::from_signature("transfer((Field),(Field),Field,Field)"),
-           [
-            context.msg_sender().to_field(),
-            storage.subscription_recipient_address.read_private().to_field(),
-            storage.subscription_price.read_private(),
-            nonce
-           ]
-  );
+ use dep::gas_token::GasToken;
+ use dep::token::Token;
+
+ ...
+ // Public call from public land
+ GasToken::at(storage.gas_token_address.read_private()).pay_fee(42).call(&mut context);
+ // Public call from private land
+ GasToken::at(storage.gas_token_address.read_private()).pay_fee(42).enqueue(&mut context);
+ // Private call from private land
+ Token::at(asset).transfer(context.msg_sender(), storage.subscription_recipient_address.read_private(), amount, nonce).call(&mut context);
```

It is also possible to use these automatic interfaces from the local contract, and thus enqueue public calls from private without having to rely on low level `context` calls.

### \[Aztec.nr] Rename max block number setter

The `request_max_block_number` function has been renamed to `set_tx_max_block_number` to better reflect that it is not a getter, and that the setting is transaction-wide.

```diff
- context.request_max_block_number(value);
+ context.set_tx_max_block_number(value);
```

### \[Aztec.nr] Get portal address

The `get_portal_address` oracle was removed. If you need to get the portal address of SomeContract, add the following methods to it

```text
#[aztec(private)]
fn get_portal_address() -> EthAddress {
    context.this_portal_address()
}

#[aztec(public)]
fn get_portal_address_public() -> EthAddress {
    context.this_portal_address()
}
```

and change the call to `get_portal_address`

```diff
- let portal_address = get_portal_address(contract_address);
+ let portal_address = SomeContract::at(contract_address).get_portal_address().call(&mut context);
```

### \[Aztec.nr] Required gas limits for public-to-public calls

When calling a public function from another public function using the `call_public_function` method, you must now specify how much gas you're allocating to the nested call. This will later allow you to limit the amount of gas consumed by the nested call, and handle any out of gas errors.

Note that gas limits are not yet enforced. For now, it is suggested you use `dep::aztec::context::gas::GasOpts::default()` which will forward all available gas.

```diff
+ use dep::aztec::context::gas::GasOpts;

- context.call_public_function(target_contract, target_selector, args);
+ context.call_public_function(target_contract, target_selector, args, GasOpts::default());
```

Note that this is not required when enqueuing a public function from a private one, since top-level enqueued public functions will always consume all gas available for the transaction, as it is not possible to handle any out-of-gas errors.

### \[Aztec.nr] Emitting unencrypted logs

The `emit_unencrypted_logs` function is now a context method.

```diff
- use dep::aztec::log::emit_unencrypted_log;
- use dep::aztec::log::emit_unencrypted_log_from_private;

- emit_unencrypted_log(context, log1);
- emit_unencrypted_log_from_private(context, log2);
+ context.emit_unencrypted_log(log1);
+ context.emit_unencrypted_log(log2);
```

## 0.33

### \[Aztec.nr] Storage struct annotation

The storage struct now identified by the annotation `#[aztec(storage)]`, instead of having to rely on it being called `Storage`.

```diff
- struct Storage {
-    ...
- }
+ #[aztec(storage)]
+ struct MyStorageStruct {
+    ...
+ }
```

### \[Aztec.js] Storage layout and note info

Storage layout and note information are now exposed in the TS contract artifact

```diff
- const note = new Note([new Fr(mintAmount), secretHash]);
- const pendingShieldStorageSlot = new Fr(5n); // storage slot for pending_shields
- const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // note type id for TransparentNote
- const extendedNote = new ExtendedNote(
-   note,
-   admin.address,
-   token.address,
-   pendingShieldStorageSlot,
-   noteTypeId,
-   receipt.txHash,
- );
- await pxe.addNote(extendedNote);
+ const note = new Note([new Fr(mintAmount), secretHash]);
+ const extendedNote = new ExtendedNote(
+   note,
+   admin.address,
+   token.address,
+   TokenContract.storage.pending_shields.slot,
+   TokenContract.notes.TransparentNote.id,
+   receipt.txHash,
+ );
+ await pxe.addNote(extendedNote);
```

### \[Aztec.nr] rand oracle is now called unsafe\_rand

`oracle::rand::rand` has been renamed to `oracle::unsafe_rand::unsafe_rand`. This change was made to communicate that we do not constrain the value in circuit and instead we just trust our PXE.

```diff
- let random_value = rand();
+ let random_value = unsafe_rand();
```

### \[AztecJS] Simulate and get return values for ANY call and introducing `prove()`

Historically it have been possible to "view" `unconstrained` functions to simulate them and get the return values, but not for `public` nor `private` functions. This has lead to a lot of bad code where we have the same function implemented thrice, once in `private`, once in `public` and once in `unconstrained`. It is not possible to call `simulate` on any call to get the return values! However, beware that it currently always returns a Field array of size 4 for private and public. This will change to become similar to the return values of the `unconstrained` functions with proper return types.

```diff
-    #[aztec(private)]
-    fn get_shared_immutable_constrained_private() -> pub Leader {
-        storage.shared_immutable.read_private()
-    }
-
-    unconstrained fn get_shared_immutable() -> pub Leader {
-        storage.shared_immutable.read_public()
-    }

+    #[aztec(private)]
+    fn get_shared_immutable_private() -> pub Leader {
+        storage.shared_immutable.read_private()
+    }

- const returnValues = await contract.methods.get_shared_immutable().view();
+ const returnValues = await contract.methods.get_shared_immutable_private().simulate();
```

```diff
await expect(
-   asset.withWallet(wallets[1]).methods.update_admin(newAdminAddress).simulate()).rejects.toThrow(
+   asset.withWallet(wallets[1]).methods.update_admin(newAdminAddress).prove()).rejects.toThrow(
        "Assertion failed: caller is not admin 'caller_roles.is_admin'",
);
```

## 0.31.0

### \[Aztec.nr] Public storage historical read API improvement

`history::public_value_inclusion::prove_public_value_inclusion` has been renamed to `history::storage::public_storage_historical_read`, and its API changed slightly. Instead of receiving a `value` parameter it now returns the historical value stored at that slot.

If you were using an oracle to get the value to pass to `prove_public_value_inclusion`, drop the oracle and use the return value from `public_storage_historical_read` instead:

```diff
- let value = read_storage();
- prove_public_value_inclusion(value, storage_slot, contract_address, context);
+ let value = public_storage_historical_read(storage_slot, contract_address, context);
```

If you were proving historical existence of a value you got via some other constrained means, perform an assertion against the return value of `public_storage_historical_read` instead:

```diff
- prove_public_value_inclusion(value, storage_slot, contract_address, context);
+ assert(public_storage_historical_read(storage_slot, contract_address, context) == value);
```

## 0.30.0

### \[AztecJS] Simplify authwit syntax

```diff
- const messageHash = computeAuthWitMessageHash(accounts[1].address, action.request());
- await wallets[0].setPublicAuth(messageHash, true).send().wait();
+ await wallets[0].setPublicAuthWit({ caller: accounts[1].address, action }, true).send().wait();
```

```diff
const action = asset
    .withWallet(wallets[1])
    .methods.unshield(accounts[0].address, accounts[1].address, amount, nonce);
-const messageHash = computeAuthWitMessageHash(accounts[1].address, action.request());
-const witness = await wallets[0].createAuthWitness(messageHash);
+const witness = await wallets[0].createAuthWit({ caller: accounts[1].address, action });
await wallets[1].addAuthWitness(witness);
```

Also note some of the naming changes: `setPublicAuth` -> `setPublicAuthWit` `createAuthWitness` -> `createAuthWit`

### \[Aztec.nr] Automatic NoteInterface implementation and selector changes

Implementing a note required a fair amount of boilerplate code, which has been substituted by the `#[aztec(note)]` attribute.

```diff
+ #[aztec(note)]
struct AddressNote {
    address: AztecAddress,
    owner: AztecAddress,
    randomness: Field,
    header: NoteHeader
}

impl NoteInterface<ADDRESS_NOTE_LEN>  for AddressNote {
-    fn serialize_content(self) -> [Field; ADDRESS_NOTE_LEN]{
-        [self.address.to_field(), self.owner.to_field(), self.randomness]
-    }
-
-    fn deserialize_content(serialized_note: [Field; ADDRESS_NOTE_LEN]) -> Self {
-        AddressNote {
-            address: AztecAddress::from_field(serialized_note[0]),
-            owner: AztecAddress::from_field(serialized_note[1]),
-            randomness: serialized_note[2],
-            header: NoteHeader::empty(),
-        }
-    }
-
-    fn compute_note_content_hash(self) -> Field {
-        pedersen_hash(self.serialize_content(), 0)
-    }
-
    fn compute_nullifier(self, context: &mut PrivateContext) -> Field {
        let note_hash_for_nullify = compute_note_hash_for_consumption(self);
        let secret = context.request_nullifier_secret_key(self.owner);
        pedersen_hash([
            note_hash_for_nullify,
            secret.low,
            secret.high,
        ],0)
    }

    fn compute_nullifier_without_context(self) -> Field {
        let note_hash_for_nullify = compute_note_hash_for_consumption(self);
        let secret = get_nullifier_secret_key(self.owner);
        pedersen_hash([
            note_hash_for_nullify,
            secret.low,
            secret.high,
        ],0)
    }

-    fn set_header(&mut self, header: NoteHeader) {
-        self.header = header;
-    }
-
-    fn get_header(note: Self) -> NoteHeader {
-        note.header
-    }

    fn broadcast(self, context: &mut PrivateContext, slot: Field) {
        let encryption_pub_key = get_public_key(self.owner);
        emit_encrypted_log(
            context,
            (*context).this_address(),
            slot,
            Self::get_note_type_id(),
            encryption_pub_key,
            self.serialize_content(),
        );
    }

-    fn get_note_type_id() -> Field {
-        6510010011410111511578111116101
-    }
}
```

Automatic note (de)serialization implementation also means it is now easier to filter notes using `NoteGetterOptions.select` via the `::properties()` helper:

Before:

```rust
let options = NoteGetterOptions::new().select(0, amount, Option::none()).select(1, owner.to_field(), Option::none()).set_limit(1);
```

After:

```rust
let options = NoteGetterOptions::new().select(ValueNote::properties().value, amount, Option::none()).select(ValueNote::properties().owner, owner.to_field(), Option::none()).set_limit(1);
```

The helper returns a metadata struct that looks like this (if autogenerated)

```rust
ValueNoteProperties {
    value: PropertySelector { index: 0, offset: 0, length: 32 },
    owner: PropertySelector { index: 1, offset: 0, length: 32 },
    randomness: PropertySelector { index: 2, offset: 0, length: 32 },
}
```

It can also be used for the `.sort` method.

## 0.27.0

### `initializer` macro replaces `constructor`

Before this version, every contract was required to have exactly one `constructor` private function, that was used for deployment. We have now removed this requirement, and made `constructor` a function like any other.

To signal that a function can be used to **initialize** a contract, you must now decorate it with the `#[aztec(initializer)]` attribute. Initializers are regular functions that set an "initialized" flag (a nullifier) for the contract. A contract can only be initialized once, and contract functions can only be called after the contract has been initialized, much like a constructor. However, if a contract defines no initializers, it can be called at any time. Additionally, you can define as many initializer functions in a contract as you want, both private and public.

To migrate from current code, simply add an initializer attribute to your constructor functions.

```diff
+ #[aztec(initializer)]
#[aztec(private)]
fn constructor() { ... }
```

If your private constructor was used to just call a public internal initializer, then remove the private constructor and flag the public function as initializer. And if your private constructor was an empty one, just remove it.

## 0.25.0

### \[Aztec.nr] Static calls

It is now possible to perform static calls from both public and private functions. Static calls forbid any modification to the state, including L2->L1 messages or log generation. Once a static context is set through a static all, every subsequent call will also be treated as static via context propagation.

```rust
context.static_call_private_function(targetContractAddress, targetSelector, args);

context.static_call_public_function(targetContractAddress, targetSelector, args);
```

### \[Aztec.nr] Introduction to `prelude`

A new `prelude` module to include common Aztec modules and types. This simplifies dependency syntax. For example:

```rust
use dep::aztec::protocol::address::AztecAddress;
use dep::aztec::{
    context::{PrivateContext, Context}, note::{note_header::NoteHeader, utils as note_utils},
    state_vars::Map
};
```

Becomes:

```rust
use dep::aztec::prelude::{AztecAddress, NoteHeader, PrivateContext, Map};
use dep::aztec::context::Context;
use dep::aztec::notes::utils as note_utils;
```

This will be further simplified in future versions (See [4496](https://github.com/AztecProtocol/aztec-packages/pull/4496) for further details).

The prelude consists of

\[Edit: removed because the prelude no-longer exists]

### `internal` is now a macro

The `internal` keyword is now removed from Noir, and is replaced by an `aztec(internal)` attribute in the function. The resulting behavior is exactly the same: these functions will only be callable from within the same contract.

Before:

```rust
#[aztec(private)]
internal fn double(input: Field) -> Field {
    input * 2
}
```

After:

```rust
#[aztec(private)]
#[aztec(internal)]
fn double(input: Field) -> Field {
    input * 2
}
```

### \[Aztec.nr] No SafeU120 anymore!

Noir now have overflow checks by default. So we don't need SafeU120 like libraries anymore.

You can replace it with `U128` instead

Before:

```text
SafeU120::new(0)
```

Now:

```text
U128::from_integer(0)
```

### \[Aztec.nr] `compute_note_hash_and_nullifier` is now autogenerated

Historically developers have been required to include a `compute_note_hash_and_nullifier` function in each of their contracts. This function is now automatically generated, and all instances of it in contract code can be safely removed.

It is possible to provide a user-defined implementation, in which case auto-generation will be skipped (though there are no known use cases for this).

### \[Aztec.nr] Updated naming of state variable wrappers

We have decided to change the naming of our state variable wrappers because the naming was not clear. The changes are as follows:

1. `Singleton` -> `PrivateMutable`
2. `ImmutableSingleton` -> `PrivateImmutable`
3. `StablePublicState` -> `SharedImmutable`
4. `PublicState` -> `PublicMutable`

This is the meaning of "private", "public" and "shared": Private: read (R) and write (W) from private, not accessible from public Public: not accessible from private, R/W from public Shared: R from private, R/W from public

Note: `SlowUpdates` will be renamed to `SharedMutable` once the implementation is ready.

### \[Aztec.nr] Authwit updates

Authentication Witnesses have been updates such that they are now cancellable and scoped to a specific consumer. This means that the `authwit` nullifier must be emitted from the account contract, which require changes to the interface. Namely, the `assert_current_call_valid_authwit_public` and `assert_current_call_valid_authwit` in `auth.nr` will **NO LONGER** emit a nullifier. Instead it will call a `spend_*_authwit` function in the account contract - which will emit the nullifier and perform a few checks. This means that the `is_valid` functions have been removed to not confuse it for a non-mutating function (static). Furthermore, the `caller` parameter of the "authwits" have been moved "further out" such that the account contract can use it in validation, allowing scoped approvals from the account POV. For most contracts, this won't be changing much, but for the account contract, it will require a few changes.

Before:

```rust
#[aztec(public)]
fn is_valid_public(message_hash: Field) -> Field {
    let actions = AccountActions::public(&mut context, ACCOUNT_ACTIONS_STORAGE_SLOT, is_valid_impl);
    actions.is_valid_public(message_hash)
}

#[aztec(private)]
fn is_valid(message_hash: Field) -> Field {
    let actions = AccountActions::private(&mut context, ACCOUNT_ACTIONS_STORAGE_SLOT, is_valid_impl);
    actions.is_valid(message_hash)
}
```

After:

```rust
#[aztec(private)]
fn verify_private_authwit(inner_hash: Field) -> Field {
    let actions = AccountActions::private(&mut context, ACCOUNT_ACTIONS_STORAGE_SLOT, is_valid_impl);
    actions.verify_private_authwit(inner_hash)
}

#[aztec(public)]
fn spend_public_authwit(inner_hash: Field) -> Field {
    let actions = AccountActions::public(&mut context, ACCOUNT_ACTIONS_STORAGE_SLOT, is_valid_impl);
    actions.spend_public_authwit(inner_hash)
}
```

## 0.24.0

### Introduce Note Type IDs

Note Type IDs are a new feature which enable contracts to have multiple `Map`s with different underlying note types, something that was not possible before. This is done almost without any user intervention, though some minor changes are required.

The mandatory `compute_note_hash_and_nullifier` now has a fifth parameter `note_type_id`. Use this instead of `storage_slot` to determine which deserialization function to use.

Before:

```rust
unconstrained fn compute_note_hash_and_nullifier(
    contract_address: AztecAddress,
    nonce: Field,
    storage_slot: Field,
    preimage: [Field; TOKEN_NOTE_LEN]
) -> pub [Field; 4] {
    let note_header = NoteHeader::new(contract_address, nonce, storage_slot);

    if (storage_slot == storage.pending_shields.get_storage_slot()) {
        note_utils::compute_note_hash_and_nullifier(TransparentNote::deserialize_content, note_header, preimage)
    } else if (note_type_id == storage.slow_update.get_storage_slot()) {
        note_utils::compute_note_hash_and_nullifier(FieldNote::deserialize_content, note_header, preimage)
    } else {
        note_utils::compute_note_hash_and_nullifier(TokenNote::deserialize_content, note_header, preimage)
    }
```

Now:

```rust
unconstrained fn compute_note_hash_and_nullifier(
    contract_address: AztecAddress,
    nonce: Field,
    storage_slot: Field,
    note_type_id: Field,
    preimage: [Field; TOKEN_NOTE_LEN]
) -> pub [Field; 4] {
    let note_header = NoteHeader::new(contract_address, nonce, storage_slot);

    if (note_type_id == TransparentNote::get_note_type_id()) {
        note_utils::compute_note_hash_and_nullifier(TransparentNote::deserialize_content, note_header, preimage)
    } else if (note_type_id == FieldNote::get_note_type_id()) {
        note_utils::compute_note_hash_and_nullifier(FieldNote::deserialize_content, note_header, preimage)
    } else {
        note_utils::compute_note_hash_and_nullifier(TokenNote::deserialize_content, note_header, preimage)
    }
```

The `NoteInterface` trait now has an additional `get_note_type_id()` function. This implementation will be autogenerated in the future, but for now providing any unique ID will suffice. The suggested way to do it is by running the Python command shown in the comment below:

```rust
impl NoteInterface<N> for MyCustomNote {
    fn get_note_type_id() -> Field {
        // python -c "print(int(''.join(str(ord(c)) for c in 'MyCustomNote')))"
       771216711711511611110978111116101
    }
}
```

### \[js] Importing contracts in JS

`@aztec/noir-contracts` is now `@aztec/noir-contracts.js`. You'll need to update your package.json & imports.

Before:

```js
import { TokenContract } from "@aztec/noir-contracts/Token";
```

Now:

```js
import { TokenContract } from "@aztec/noir-contracts.js/Token";
```

### \[Aztec.nr] Aztec.nr contracts location change in Nargo.toml

Aztec contracts are now moved outside of the `yarn-project` folder and into `noir-projects`, so you need to update your imports.

Before:

```rust
easy_private_token_contract = {git = "https://github.com/AztecProtocol/aztec-packages/", tag ="v0.23.0", directory = "yarn-project/noir-contracts/contracts/easy_private_token_contract"}
```

Now, update the `yarn-project` folder for `noir-projects`:

```rust
easy_private_token_contract = {git = "https://github.com/AztecProtocol/aztec-packages/", tag ="v0.24.0", directory = "noir-projects/noir-contracts/contracts/easy_private_token_contract"}
```

## 0.22.0

### `Note::compute_note_hash` renamed to `Note::compute_note_content_hash`

The `compute_note_hash` function in of the `Note` trait has been renamed to `compute_note_content_hash` to avoid being confused with the actual note hash.

Before:

```rust
impl NoteInterface for CardNote {
    fn compute_note_hash(self) -> Field {
        pedersen_hash([
            self.owner.to_field(),
        ], 0)
    }
```

Now:

```rust
impl NoteInterface for CardNote {
    fn compute_note_content_hash(self) -> Field {
        pedersen_hash([
            self.owner.to_field(),
        ], 0)
    }
```

### Introduce `compute_note_hash_for_consumption` and `compute_note_hash_for_insertion`

Makes a split in logic for note hash computation for consumption and insertion. This is to avoid confusion between the two, and to make it clear that the note hash for consumption is different from the note hash for insertion (sometimes).

`compute_note_hash_for_consumption` replaces `compute_note_hash_for_read_or_nullify`. `compute_note_hash_for_insertion` is new, and mainly used in \`lifecycle.nr\`\`

### `Note::serialize_content` and `Note::deserialize_content` added to \`NoteInterface

The `NoteInterface` have been extended to include `serialize_content` and `deserialize_content` functions. This is to convey the difference between serializing the full note, and just the content. This change allows you to also add a `serialize` function to support passing in a complete note to a function.

Before:

```rust
impl Serialize<ADDRESS_NOTE_LEN> for AddressNote {
    fn serialize(self) -> [Field; ADDRESS_NOTE_LEN]{
        [self.address.to_field(), self.owner.to_field(), self.randomness]
    }
}
impl Deserialize<ADDRESS_NOTE_LEN> for AddressNote {
    fn deserialize(serialized_note: [Field; ADDRESS_NOTE_LEN]) -> Self {
        AddressNote {
            address: AztecAddress::from_field(serialized_note[0]),
            owner: AztecAddress::from_field(serialized_note[1]),
            randomness: serialized_note[2],
            header: NoteHeader::empty(),
        }
    }
```

Now

```rust
impl NoteInterface<ADDRESS_NOTE_LEN>  for AddressNote {
    fn serialize_content(self) -> [Field; ADDRESS_NOTE_LEN]{
        [self.address.to_field(), self.owner.to_field(), self.randomness]
    }

    fn deserialize_content(serialized_note: [Field; ADDRESS_NOTE_LEN]) -> Self {
        AddressNote {
            address: AztecAddress::from_field(serialized_note[0]),
            owner: AztecAddress::from_field(serialized_note[1]),
            randomness: serialized_note[2],
            header: NoteHeader::empty(),
        }
    }
    ...
}
```

### \[Aztec.nr] No storage.init() and `Serialize`, `Deserialize`, `NoteInterface` as Traits, removal of SerializationMethods and SERIALIZED\_LEN

Storage definition and initialization has been simplified. Previously:

```rust
struct Storage {
    leader: PublicState<Leader, LEADER_SERIALIZED_LEN>,
    legendary_card: Singleton<CardNote, CARD_NOTE_LEN>,
    profiles: Map<AztecAddress, Singleton<CardNote, CARD_NOTE_LEN>>,
    test: Set<CardNote, CARD_NOTE_LEN>,
    imm_singleton: PrivateImmutable<CardNote, CARD_NOTE_LEN>,
}

impl Storage {
        fn init(context: Context) -> Self {
            Storage {
                leader: PublicMutable::new(
                    context,
                    1,
                    LeaderSerializationMethods,
                ),
                legendary_card: PrivateMutable::new(context, 2, CardNoteMethods),
                profiles: Map::new(
                    context,
                    3,
                    |context, slot| {
                        PrivateMutable::new(context, slot, CardNoteMethods)
                    },
                ),
                test: Set::new(context, 4, CardNoteMethods),
                imm_singleton: PrivateImmutable::new(context, 4, CardNoteMethods),
            }
        }
    }
```

Now:

```rust
struct Storage {
    leader: PublicMutable<Leader>,
    legendary_card: Singleton<CardNote>,
    profiles: Map<AztecAddress, Singleton<CardNote>>,
    test: Set<CardNote>,
    imm_singleton: PrivateImmutable<CardNote>,
}
```

For this to work, Notes must implement Serialize, Deserialize and NoteInterface Traits. Previously:

```rust
use dep::aztec::protocol::address::AztecAddress;
use dep::aztec::{
    note::{
        note_header::NoteHeader,
        note_interface::NoteInterface,
        utils::compute_note_hash_for_read_or_nullify,
    },
    oracle::{
        nullifier_key::get_nullifier_secret_key,
        get_public_key::get_public_key,
    },
    log::emit_encrypted_log,
    hash::pedersen_hash,
    context::PrivateContext,
};

// Shows how to create a custom note

global CARD_NOTE_LEN: Field = 1;

impl CardNote {
    pub fn new(owner: AztecAddress) -> Self {
        CardNote {
            owner,
        }
    }

    pub fn serialize(self) -> [Field; CARD_NOTE_LEN] {
        [self.owner.to_field()]
    }

    pub fn deserialize(serialized_note: [Field; CARD_NOTE_LEN]) -> Self {
        CardNote {
            owner: AztecAddress::from_field(serialized_note[1]),
        }
    }

    pub fn compute_note_hash(self) -> Field {
        pedersen_hash([
            self.owner.to_field(),
        ],0)
    }

    pub fn compute_nullifier(self, context: &mut PrivateContext) -> Field {
        let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(CardNoteMethods, self);
        let secret = context.request_nullifier_secret_key(self.owner);
        pedersen_hash([
            note_hash_for_nullify,
            secret.high,
            secret.low,
        ],0)
    }

    pub fn compute_nullifier_without_context(self) -> Field {
        let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(CardNoteMethods, self);
        let secret = get_nullifier_secret_key(self.owner);
        pedersen_hash([
            note_hash_for_nullify,
            secret.high,
            secret.low,
        ],0)
    }

    pub fn set_header(&mut self, header: NoteHeader) {
        self.header = header;
    }

    // Broadcasts the note as an encrypted log on L1.
    pub fn broadcast(self, context: &mut PrivateContext, slot: Field) {
        let encryption_pub_key = get_public_key(self.owner);
        emit_encrypted_log(
            context,
            (*context).this_address(),
            slot,
            encryption_pub_key,
            self.serialize(),
        );
    }
}

fn deserialize(serialized_note: [Field; CARD_NOTE_LEN]) -> CardNote {
    CardNote::deserialize(serialized_note)
}

fn serialize(note: CardNote) -> [Field; CARD_NOTE_LEN] {
    note.serialize()
}

fn compute_note_hash(note: CardNote) -> Field {
    note.compute_note_hash()
}

fn compute_nullifier(note: CardNote, context: &mut PrivateContext) -> Field {
    note.compute_nullifier(context)
}

fn compute_nullifier_without_context(note: CardNote) -> Field {
    note.compute_nullifier_without_context()
}

fn get_header(note: CardNote) -> NoteHeader {
    note.header
}

fn set_header(note: &mut CardNote, header: NoteHeader) {
    note.set_header(header)
}

// Broadcasts the note as an encrypted log on L1.
fn broadcast(context: &mut PrivateContext, slot: Field, note: CardNote) {
    note.broadcast(context, slot);
}

global CardNoteMethods = NoteInterface {
    deserialize,
    serialize,
    compute_note_hash,
    compute_nullifier,
    compute_nullifier_without_context,
    get_header,
    set_header,
    broadcast,
};
```

Now:

```rust
use dep::aztec::{
    note::{
        note_header::NoteHeader,
        note_interface::NoteInterface,
        utils::compute_note_hash_for_read_or_nullify,
    },
    oracle::{
        nullifier_key::get_nullifier_secret_key,
        get_public_key::get_public_key,
    },
    log::emit_encrypted_log,
    hash::pedersen_hash,
    context::PrivateContext,
    protocol::{
        address::AztecAddress,
        traits::{Serialize, Deserialize, Empty}
    }
};

// Shows how to create a custom note

global CARD_NOTE_LEN: Field = 1;

impl CardNote {
    pub fn new(owner: AztecAddress) -> Self {
        CardNote {
            owner,
        }
    }
}

impl NoteInterface for CardNote {
    fn compute_note_content_hash(self) -> Field {
        pedersen_hash([
            self.owner.to_field(),
        ],0)
    }

    fn compute_nullifier(self, context: &mut PrivateContext) -> Field {
        let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(self);
        let secret = context.request_nullifier_secret_key(self.owner);
        pedersen_hash([
            note_hash_for_nullify,
            secret.high,
            secret.low,
        ],0)
    }

    fn compute_nullifier_without_context(self) -> Field {
        let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(self);
        let secret = get_nullifier_secret_key(self.owner);
        pedersen_hash([
            note_hash_for_nullify,
            secret.high,
            secret.low,
        ],0)
    }

    fn set_header(&mut self, header: NoteHeader) {
        self.header = header;
    }

    fn get_header(note: CardNote) -> NoteHeader {
        note.header
    }

    fn serialize_content(self) -> [Field; CARD_NOTE_LEN]{
        [self.owner.to_field()]
    }

    fn deserialize_content(serialized_note: [Field; CARD_NOTE_LEN]) -> Self {
        AddressNote {
            owner: AztecAddress::from_field(serialized_note[0]),
            header: NoteHeader::empty(),
        }
    }

    // Broadcasts the note as an encrypted log on L1.
    fn broadcast(self, context: &mut PrivateContext, slot: Field) {
        let encryption_pub_key = get_public_key(self.owner);
        emit_encrypted_log(
            context,
            (*context).this_address(),
            slot,
            encryption_pub_key,
            self.serialize(),
        );
    }
}
```

Public state must implement Serialize and Deserialize traits.

It is still possible to manually implement the storage initialization (for custom storage wrappers or internal types that don't implement the required traits). For the above example, the `impl Storage` section would look like this:

```rust
impl Storage {
    fn init(context: Context) -> Self {
        Storage {
            leader: PublicMutable::new(
                context,
                1
            ),
            legendary_card: PrivateMutable::new(context, 2),
            profiles: Map::new(
                context,
                3,
                |context, slot| {
                    PrivateMutable::new(context, slot)
                },
            ),
            test: Set::new(context, 4),
            imm_singleton: PrivateImmutable::new(context, 4),
        }
    }
}
```

## 0.20.0

### \[Aztec.nr] Changes to `NoteInterface`

1. Changing `compute_nullifier()` to `compute_nullifier(private_context: PrivateContext)`

   This API is invoked for nullifier generation within private functions. When using a secret key for nullifier creation, retrieve it through:

   `private_context.request_nullifier_secret_key(account_address)`

   The private context will generate a request for the kernel circuit to validate that the secret key does belong to the account.

   Before:

   ```rust
    pub fn compute_nullifier(self) -> Field {
        let secret = oracle.get_secret_key(self.owner);
        pedersen_hash([
            self.value,
            secret.low,
            secret.high,
        ])
    }
   ```

   Now:

   ```rust
    pub fn compute_nullifier(self, context: &mut PrivateContext) -> Field {
        let secret = context.request_nullifier_secret_key(self.owner);
        pedersen_hash([
            self.value,
            secret.low,
            secret.high,
        ])
    }
   ```

2. New API `compute_nullifier_without_context()`.

   This API is used within unconstrained functions where the private context is not available, and using an unverified nullifier key won't affect the network or other users. For example, it's used in `compute_note_hash_and_nullifier()` to compute values for the user's own notes.

   ```rust
   pub fn compute_nullifier_without_context(self) -> Field {
        let secret = oracle.get_nullifier_secret_key(self.owner);
        pedersen_hash([
            self.value,
            secret.low,
            secret.high,
        ])
    }
   ```

   > Note that the `get_secret_key` oracle API has been renamed to `get_nullifier_secret_key`.

## 0.18.0

### \[Aztec.nr] Remove `protocol` from Nargo.toml

The `protocol` package is now being reexported from `aztec`. It can be accessed through `dep::aztec::protocol`.

```toml
aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="v6.0.0", directory="yarn-project/aztec-nr/aztec" }
```

### \[Aztec.nr] key type definition in Map

The `Map` class now requires defining the key type in its declaration which *must* implement the `ToField` trait.

Before:

```rust
struct Storage {
    balances: Map<PublicMutable<Field, FIELD_SERIALIZED_LEN>>
}

let user_balance = balances.at(owner.to_field())
```

Now:

```rust
struct Storage {
    balances: Map<AztecAddress, PublicState<Field, FIELD_SERIALIZED_LEN>>
}

let user_balance = balances.at(owner)
```

### \[js] Updated function names

* `waitForSandbox` renamed to `waitForPXE` in `@aztec/aztec.js`
* `getSandboxAccountsWallets` renamed to `getInitialTestAccountsWallets` in `@aztec/accounts/testing`

## 0.17.0

### \[js] New `@aztec/accounts` package

Before:

```js
import { getSchnorrAccount } from "@aztec/aztec.js"; // previously you would get the default accounts from the `aztec.js` package:
```

Now, import them from the new package `@aztec/accounts`

```js
import { getSchnorrAccount } from "@aztec/accounts";
```

### Typed Addresses

Address fields in Aztec.nr now is of type `AztecAddress` as opposed to `Field`

Before:

```rust
unconstrained fn compute_note_hash_and_nullifier(contract_address: Field, nonce: Field, storage_slot: Field, serialized_note: [Field; VALUE_NOTE_LEN]) -> [Field; 4] {
        let note_header = NoteHeader::new(_address, nonce, storage_slot);
        ...
```

Now:

```rust
unconstrained fn compute_note_hash_and_nullifier(
        contract_address: AztecAddress,
        nonce: Field,
        storage_slot: Field,
        serialized_note: [Field; VALUE_NOTE_LEN]
    ) -> pub [Field; 4] {
        let note_header = NoteHeader::new(contract_address, nonce, storage_slot);
```

Similarly, there are changes when using aztec.js to call functions.

To parse a `AztecAddress` to BigInt, use `.inner` Before:

```js
const tokenBigInt = await bridge.methods.token().simulate();
```

Now:

```js
const tokenBigInt = (await bridge.methods.token().simulate()).inner;
```

### \[Aztec.nr] Add `protocol` to Nargo.toml

```toml
aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="v6.0.0", directory="yarn-project/aztec-nr/aztec" }
protocol = { git="https://github.com/AztecProtocol/aztec-packages/", tag="v6.0.0", directory="yarn-project/noir-protocol-circuits/crates/types"}
```

### \[Aztec.nr] moving compute\_address func to AztecAddress

Before:

```rust
let calculated_address = compute_address(pub_key_x, pub_key_y, partial_address);
```

Now:

```rust
let calculated_address = AztecAddress::compute(pub_key_x, pub_key_y, partial_address);
```

### \[Aztec.nr] moving `compute_selector` to FunctionSelector

Before:

```rust
let selector = compute_selector("_initialize((Field))");
```

Now:

```rust
let selector = FunctionSelector::from_signature("_initialize((Field))");
```

### \[js] Importing contracts in JS

Contracts are now imported from a file with the type's name.

Before:

```js
import { TokenContract } from "@aztec/noir-contracts/types";
```

Now:

```js
import { TokenContract } from "@aztec/noir-contracts/Token";
```

### \[Aztec.nr] Aztec example contracts location change in Nargo.toml

Aztec contracts are now moved outside of the `src` folder, so you need to update your imports.

Before:

```rust
easy_private_token_contract = {git = "https://github.com/AztecProtocol/aztec-packages/", tag ="v0.16.9", directory = "noir-projects/noir-contracts/contracts/easy_private_token_contract"}
```

Now, just remove the `src` folder,:

```rust
easy_private_token_contract = {git = "https://github.com/AztecProtocol/aztec-packages/", tag ="v0.17.0", directory = "noir-projects/noir-contracts/contracts/easy_private_token_contract"}
```
