From d7795cb3d0ff936796034dcd639cad5ed5eabb86 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Tue, 23 Dec 2025 15:38:40 -0300 Subject: [PATCH 1/8] refactor: remove note screener API --- bin/miden-cli/src/commands/notes.rs | 20 ++++- crates/rust-client/src/note/mod.rs | 3 +- crates/rust-client/src/note/note_screener.rs | 41 ++------- .../testing/miden-client-tests/src/tests.rs | 31 ++++--- crates/web-client/js/types/index.d.ts | 1 + .../src/models/consumable_note_record.rs | 90 +++++++++++++++---- crates/web-client/test/notes.test.ts | 21 +++-- docs/external/src/web-client/notes.md | 8 +- 8 files changed, 137 insertions(+), 78 deletions(-) diff --git a/bin/miden-cli/src/commands/notes.rs b/bin/miden-cli/src/commands/notes.rs index 1ecc4a3809..a62cb712eb 100644 --- a/bin/miden-cli/src/commands/notes.rs +++ b/bin/miden-cli/src/commands/notes.rs @@ -6,6 +6,7 @@ use miden_client::auth::TransactionAuthenticator; use miden_client::note::{ Note, NoteConsumability, + NoteConsumptionStatus, NoteInputs, NoteMetadata, WellKnownNote, @@ -395,7 +396,7 @@ where table.add_row(vec![ note.id().to_hex(), relevance.0.to_string(), - relevance.1.to_string(), + note_consumption_status_type(&relevance.1), ]); } } @@ -403,6 +404,23 @@ where println!("{table}"); } +fn note_consumption_status_type(note_consumption_status: &NoteConsumptionStatus) -> String { + match note_consumption_status { + NoteConsumptionStatus::Consumable => "Consumable".to_string(), + NoteConsumptionStatus::ConsumableAfter(block_number) => { + format!("Consumable after block {block_number}") + }, + NoteConsumptionStatus::ConsumableWithAuthorization => { + "Consumable with authorization".to_string() + }, + NoteConsumptionStatus::UnconsumableConditions => { + "Unconsumable due to conditions".to_string() + }, + NoteConsumptionStatus::NeverConsumable(error) => format!("Never consumable: {error}"), + } + .to_string() +} + fn note_record_type(note_record_metadata: Option<&NoteMetadata>) -> String { match note_record_metadata { Some(metadata) => match metadata.note_type() { diff --git a/crates/rust-client/src/note/mod.rs b/crates/rust-client/src/note/mod.rs index 42a0023422..09713feef3 100644 --- a/crates/rust-client/src/note/mod.rs +++ b/crates/rust-client/src/note/mod.rs @@ -98,12 +98,13 @@ pub use miden_protocol::note::{ pub use miden_protocol::transaction::ToInputNoteCommitments; pub use miden_standards::note::utils::{build_p2id_recipient, build_swap_tag}; pub use miden_standards::note::{ + NoteConsumptionStatus, WellKnownNote, create_p2id_note, create_p2ide_note, create_swap_note, }; -pub use note_screener::{NoteConsumability, NoteRelevance, NoteScreener, NoteScreenerError}; +pub use note_screener::{NoteConsumability, NoteScreener, NoteScreenerError}; pub use note_update_tracker::{ InputNoteUpdate, NoteUpdateTracker, diff --git a/crates/rust-client/src/note/note_screener.rs b/crates/rust-client/src/note/note_screener.rs index 79ab9adf3f..b9063b5c5a 100644 --- a/crates/rust-client/src/note/note_screener.rs +++ b/crates/rust-client/src/note/note_screener.rs @@ -1,7 +1,6 @@ use alloc::boxed::Box; use alloc::sync::Arc; use alloc::vec::Vec; -use core::fmt; use async_trait::async_trait; use miden_protocol::account::{Account, AccountId}; @@ -20,29 +19,11 @@ use crate::store::{InputNoteRecord, NoteFilter, Store, StoreError}; use crate::sync::{NoteUpdateAction, OnNoteReceived}; use crate::transaction::{InputNote, TransactionRequestBuilder, TransactionRequestError}; -/// Describes the relevance of a note based on the screening. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub enum NoteRelevance { - /// The note can be consumed in the client's current block. - Now, - /// The note can be consumed after the block with the specified number. - After(u32), -} - /// Represents the consumability of a note by a specific account. /// /// The tuple contains the account ID that may consume the note and the moment it will become /// relevant. -pub type NoteConsumability = (AccountId, NoteRelevance); - -impl fmt::Display for NoteRelevance { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - NoteRelevance::Now => write!(f, "Now"), - NoteRelevance::After(height) => write!(f, "After block {height}"), - } - } -} +pub type NoteConsumability = (AccountId, NoteConsumptionStatus); /// Provides functionality for testing whether a note is relevant to the client or not. /// @@ -115,7 +96,7 @@ where &self, account: &Account, note: &Note, - ) -> Result, NoteScreenerError> { + ) -> Result, NoteScreenerError> { let transaction_request = TransactionRequestBuilder::new().build_consume_notes(vec![note.id()])?; @@ -142,19 +123,7 @@ where ) .await?; - let result = match note_consumption_check { - NoteConsumptionStatus::ConsumableAfter(block_number) => { - Some(NoteRelevance::After(block_number.as_u32())) - }, - NoteConsumptionStatus::Consumable - | NoteConsumptionStatus::ConsumableWithAuthorization => Some(NoteRelevance::Now), - // NOTE: NoteConsumptionStatus::UnconsumableConditions means that state-related context - // does not allow for consumption, so don't keep for now. In the next - // version, we should be more careful about this - NoteConsumptionStatus::UnconsumableConditions - | NoteConsumptionStatus::NeverConsumable(_) => None, - }; - Ok(result) + Ok(Some(note_consumption_check)) } /// Special relevance check for P2IDE notes. It checks if the sender account can consume and @@ -162,7 +131,7 @@ where fn check_p2ide_recall_consumability( note: &Note, account_id: &AccountId, - ) -> Result, NoteScreenerError> { + ) -> Result, NoteScreenerError> { let note_inputs = note.inputs().values(); // TODO: this needs to be removed (see note screener refactor issue) @@ -178,7 +147,7 @@ where })?; if sender == *account_id { - Ok(Some(NoteRelevance::After(recall_height))) + Ok(Some(NoteConsumptionStatus::ConsumableAfter(recall_height.into()))) } else { Ok(None) } diff --git a/crates/testing/miden-client-tests/src/tests.rs b/crates/testing/miden-client-tests/src/tests.rs index bad62b0fe6..d70952b3a9 100644 --- a/crates/testing/miden-client-tests/src/tests.rs +++ b/crates/testing/miden-client-tests/src/tests.rs @@ -25,7 +25,7 @@ use miden_client::auth::{ }; use miden_client::builder::ClientBuilder; use miden_client::keystore::FilesystemKeyStore; -use miden_client::note::{BlockNumber, NoteId, NoteRelevance}; +use miden_client::note::{BlockNumber, NoteId}; use miden_client::rpc::{ACCOUNT_ID_LIMIT, NOTE_TAG_LIMIT, NodeRpcClient}; use miden_client::store::input_note_states::ConsumedAuthenticatedLocalNoteState; use miden_client::store::{ @@ -108,7 +108,7 @@ use miden_protocol::{EMPTY_WORD, Felt, ONE, Word, ZERO}; use miden_standards::account::faucets::BasicFungibleFaucet; use miden_standards::account::interface::AccountInterfaceError; use miden_standards::account::wallets::BasicWallet; -use miden_standards::note::{WellKnownNote, utils}; +use miden_standards::note::{NoteConsumptionStatus, WellKnownNote, utils}; use miden_standards::testing::mock_account::MockAccountExt; use miden_standards::testing::note::NoteBuilder; use miden_testing::{MockChain, MockChainBuilder, TxContextInput}; @@ -1488,20 +1488,29 @@ async fn get_consumable_notes() { // Check that the note is only consumable after block 100 for the account that sent the // transaction - let from_account_relevance = relevant_accounts + let from_account_relevance = &relevant_accounts .iter() .find(|relevance| relevance.0 == from_account_id) .unwrap() .1; - assert_eq!(from_account_relevance, NoteRelevance::After(100)); + match from_account_relevance { + NoteConsumptionStatus::ConsumableAfter(value) => { + assert_eq!(value, &(100u32.into())); + }, + _ => panic!("Unexpected NoteConsumptionStatus"), + } // Check that the note is always consumable for the account that received the transaction - let to_account_relevance = relevant_accounts + let to_account_relevance = &relevant_accounts .iter() .find(|relevance| relevance.0 == to_account_id) .unwrap() .1; - assert_eq!(to_account_relevance, NoteRelevance::Now); + + match to_account_relevance { + NoteConsumptionStatus::Consumable => {}, + _ => panic!("Unexpected NoteConsumptionStatus"), + } } #[tokio::test] @@ -2120,25 +2129,25 @@ const BUMP_MAP_CODE: &str = r#" pub proc bump_map_item # map key push.{map_key} - + # push slot_id_prefix, slot_id_suffix for the map slot push.MAP_SLOT[0..2] exec.::miden::protocol::active_account::get_map_item add.1 push.{map_key} - + # push slot_id_prefix, slot_id_suffix for the map slot push.MAP_SLOT[0..2] exec.::miden::protocol::native_account::set_map_item dropw # => [OLD_VALUE] - + dupw - + # push slot_id_prefix, slot_id_suffix for the map slot push.MAP_SLOT[0..2] - + # Set a new item each time as the value keeps changing exec.::miden::protocol::native_account::set_map_item dropw dropw diff --git a/crates/web-client/js/types/index.d.ts b/crates/web-client/js/types/index.d.ts index d24dd92c1d..c7690fd4e0 100644 --- a/crates/web-client/js/types/index.d.ts +++ b/crates/web-client/js/types/index.d.ts @@ -67,6 +67,7 @@ export { NoteAndArgsArray, NoteAssets, NoteConsumability, + NoteConsumptionStatus, NoteDetails, NoteDetailsAndTag, NoteDetailsAndTagArray, diff --git a/crates/web-client/src/models/consumable_note_record.rs b/crates/web-client/src/models/consumable_note_record.rs index f1dcafb773..cacf6fae52 100644 --- a/crates/web-client/src/models/consumable_note_record.rs +++ b/crates/web-client/src/models/consumable_note_record.rs @@ -1,10 +1,70 @@ -use miden_client::note::{NoteConsumability as NativeNoteConsumability, NoteRelevance}; +use miden_client::note::{ + NoteConsumability as NativeNoteConsumability, + NoteConsumptionStatus as NativeNoteConsumptionStatus, +}; use miden_client::store::InputNoteRecord as NativeInputNoteRecord; use wasm_bindgen::prelude::*; use super::account_id::AccountId; use super::input_note_record::InputNoteRecord; +/// Describes if a note could be consumed under a specific conditions: target account state and +/// block height. +#[derive(Clone)] +#[wasm_bindgen] +pub struct NoteConsumptionStatus(NativeNoteConsumptionStatus); + +#[wasm_bindgen] +impl NoteConsumptionStatus { + /// Constructs a `NoteConsumptionStatus` that is consumable. + #[wasm_bindgen(js_name = "consumable")] + pub fn consumable() -> Self { + Self(NativeNoteConsumptionStatus::Consumable) + } + + /// Constructs a `NoteConsumptionStatus` that is consumable with authorization. + #[wasm_bindgen(js_name = "consumableWithAuthorization")] + pub fn consumable_with_authorization() -> Self { + Self(NativeNoteConsumptionStatus::ConsumableWithAuthorization) + } + + /// Constructs a `NoteConsumptionStatus` that is consumable after a specific block height. + #[wasm_bindgen(js_name = "consumableAfter")] + pub fn consumable_after(block_height: u32) -> Self { + Self(NativeNoteConsumptionStatus::ConsumableAfter(block_height.into())) + } + + /// Constructs a `NoteConsumptionStatus` that is never consumable. + #[wasm_bindgen(js_name = "neverConsumable")] + pub fn never_consumable(err: String) -> Self { + Self(NativeNoteConsumptionStatus::NeverConsumable(err.into())) + } + + /// Constructs a `NoteConsumptionStatus` that is unconsumable due to conditions. + #[wasm_bindgen(js_name = "unconsumableConditions")] + pub fn unconsumable_conditions() -> Self { + Self(NativeNoteConsumptionStatus::UnconsumableConditions) + } + + /// Returns the block number at which the note can be consumed. + /// Returns None if the note is already consumable or never possible + #[wasm_bindgen(js_name = "consumableAfterBlock")] + pub fn consumable_after_block(&self) -> Option { + match self.0 { + NativeNoteConsumptionStatus::ConsumableAfter(block_height) => { + Some(block_height.as_u32()) + }, + _ => None, + } + } +} + +impl From for NoteConsumptionStatus { + fn from(native_note_consumption_status: NativeNoteConsumptionStatus) -> Self { + NoteConsumptionStatus(native_note_consumption_status) + } +} + /// Input note record annotated with consumption conditions. #[derive(Clone)] #[wasm_bindgen] @@ -13,23 +73,23 @@ pub struct ConsumableNoteRecord { note_consumability: Vec, } -#[derive(Clone, Copy)] +#[derive(Clone)] #[wasm_bindgen] pub struct NoteConsumability { account_id: AccountId, - // The block number after which the note can be consumed, - // if None then the note can be consumed immediately - consumable_after_block: Option, + // The status of the note, consumable immediately, + // after a certain block number, etc. + consumption_status: NoteConsumptionStatus, } #[wasm_bindgen] impl NoteConsumability { pub(crate) fn new( account_id: AccountId, - consumable_after_block: Option, + consumption_status: NoteConsumptionStatus, ) -> NoteConsumability { - NoteConsumability { account_id, consumable_after_block } + NoteConsumability { account_id, consumption_status } } /// Returns the account that can consume the note. @@ -38,10 +98,10 @@ impl NoteConsumability { self.account_id } - /// Returns the block number after which the note becomes consumable (if any). - #[wasm_bindgen(js_name = "consumableAfterBlock")] - pub fn consumable_after_block(&self) -> Option { - self.consumable_after_block + /// Returns the consumption status of the note. + #[wasm_bindgen(js_name = "consumptionStatus")] + pub fn consumption_status(&self) -> NoteConsumptionStatus { + self.consumption_status.clone() } } @@ -87,12 +147,6 @@ impl From<(NativeInputNoteRecord, Vec)> for ConsumableN impl From for NoteConsumability { fn from(note_consumability: NativeNoteConsumability) -> Self { - NoteConsumability::new( - note_consumability.0.into(), - match note_consumability.1 { - NoteRelevance::After(block) => Some(block), - NoteRelevance::Now => None, - }, - ) + NoteConsumability::new(note_consumability.0.into(), note_consumability.1.into()) } } diff --git a/crates/web-client/test/notes.test.ts b/crates/web-client/test/notes.test.ts index 1c815490c8..5b1b643bcc 100644 --- a/crates/web-client/test/notes.test.ts +++ b/crates/web-client/test/notes.test.ts @@ -16,6 +16,7 @@ import { Page, expect } from "@playwright/test"; import { ConsumableNoteRecord, NoteConsumability, + NoteConsumptionStatus, } from "../dist/crates/miden_client_web"; const getConsumableNotes = async ( @@ -26,7 +27,7 @@ const getConsumableNotes = async ( noteId: string; consumability: { accountId: string; - consumableAfterBlock: number | undefined; + consumptionStatus: NoteConsumptionStatus | undefined; }[]; }[] > => { @@ -45,7 +46,7 @@ const getConsumableNotes = async ( noteId: record.inputNoteRecord().id().toString(), consumability: record.noteConsumability().map((c) => ({ accountId: c.accountId().toString(), - consumableAfterBlock: c.consumableAfterBlock(), + consumptionStatus: c.consumptionStatus(), })), })); }, accountId); @@ -155,7 +156,9 @@ test.describe("get_consumable_notes", () => { expect(record.consumability).toHaveLength(1); expect(record.consumability[0].accountId).toBe(accountId1); expect(record.noteId).toBe(noteId1); - expect(record.consumability[0].consumableAfterBlock).toBeUndefined(); + expect( + record.consumability[0].consumptionStatus().consumableAfterBlock() + ).toBeUndefined(); }); }); @@ -201,12 +204,16 @@ test.describe("get_consumable_notes", () => { const consumableRecipient = await getConsumableNotes(page, targetAccountId); const consumableSender = await getConsumableNotes(page, senderAccountId); expect(consumableSender.length).toBe(1); - expect(consumableSender[0].consumability[0].consumableAfterBlock).toBe( - recallHeight - ); + expect( + consumableSender[0].consumability[0] + .consumptionStatus() + .consumableAfterBlock() + ).toBe(recallHeight); expect(consumableRecipient.length).toBe(1); expect( - consumableRecipient[0].consumability[0].consumableAfterBlock + consumableRecipient[0].consumability[0] + .consumptionStatus() + .consumableAfterBlock() ).toBeUndefined(); }); }); diff --git a/docs/external/src/web-client/notes.md b/docs/external/src/web-client/notes.md index 5dbdfa78d2..1160d069b5 100644 --- a/docs/external/src/web-client/notes.md +++ b/docs/external/src/web-client/notes.md @@ -75,12 +75,12 @@ try { // Get consumable notes for a specific account const accountId = AccountId.fromHex(accountIdHex); const records = await webClient.getConsumableNotes(accountId); - + records.forEach(record => { console.log("Note ID:", record.inputNoteRecord().id().toString()); record.noteConsumability().forEach(consumability => { console.log("Account ID:", consumability.accountId().toString()); - console.log("Consumable after block:", consumability.consumableAfterBlock()); + console.log("Consumable after block:", consumability.consumptionStatus().consumableAfterBlock()); }); }); @@ -111,7 +111,7 @@ try { // Script builder can be instanced in either debug or normal mode using one of: // - CodeBuilderMode.Debug // - CodeBuilderMode.Normal - + const noteScript = new CodeBuilder(CodeBuilderMode.Debug); // Use the compiled script in your transaction } catch (error) { @@ -158,4 +158,4 @@ const consumedNotes = await webClient.getInputNotes(consumedFilter); const noteIds = [noteId1, noteId2]; const listFilter = new NoteFilter(NoteFilterTypes.List, noteIds); const specificNotes = await webClient.getInputNotes(listFilter); -``` +``` From ee54ab85f11a4105b7d83fa4899e03ff2d4a8d54 Mon Sep 17 00:00:00 2001 From: SantiagoPittella Date: Fri, 9 Jan 2026 15:46:02 -0300 Subject: [PATCH 2/8] docs: add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a35c0930f..cf848d46ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ * [BREAKING] Introduced named storage slots, changed `FilesystemKeystore` to not be generic over RNG ([#1626](https://github.com/0xMiden/miden-client/pull/1626)). * Added `submit_new_transaction_with_prover` to the Rust client and `submitNewTransactionWithProver` to the WebClient([#1622](https://github.com/0xMiden/miden-client/pull/1622)). * [BREAKING] Modified JS binding for `AccountComponent::compile` which now takes an `AccountComponentCode` built with the newly added binding `CodeBuilder::compile_account_component_code` ([#1627](https://github.com/0xMiden/miden-client/pull/1627)). +* [BREAKING] Refactored the `NoteScreener` API ([#1630](https://github.com/0xMiden/miden-client/pull/1630)). ## 0.12.5 (2025-12-01) From 2b6c03448a3947e86c4944d9f5810e1f620c0fa8 Mon Sep 17 00:00:00 2001 From: SantiagoPittella Date: Fri, 9 Jan 2026 16:02:47 -0300 Subject: [PATCH 3/8] chore: refresh docs --- crates/web-client/yarn.lock | 77 ++++++++---- docs/typedoc/web-client/README.md | 1 + .../web-client/classes/NoteConsumability.md | 8 +- .../classes/NoteConsumptionStatus.md | 115 ++++++++++++++++++ 4 files changed, 170 insertions(+), 31 deletions(-) create mode 100644 docs/typedoc/web-client/classes/NoteConsumptionStatus.md diff --git a/crates/web-client/yarn.lock b/crates/web-client/yarn.lock index 54514e86f7..7ed42a49c1 100644 --- a/crates/web-client/yarn.lock +++ b/crates/web-client/yarn.lock @@ -93,7 +93,7 @@ "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": +"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": version "2.0.5" resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== @@ -195,7 +195,7 @@ dependencies: "@shikijs/types" "3.13.0" -"@shikijs/types@3.13.0", "@shikijs/types@^3.13.0": +"@shikijs/types@^3.13.0", "@shikijs/types@3.13.0": version "3.13.0" resolved "https://registry.npmjs.org/@shikijs/types/-/types-3.13.0.tgz" integrity sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw== @@ -463,6 +463,11 @@ binary-extensions@^2.0.0: resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz" integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== +binaryen@^121.0.0: + version "121.0.0" + resolved "https://registry.npmjs.org/binaryen/-/binaryen-121.0.0.tgz" + integrity sha512-St5LX+CmVdDQMf+DDHWdne7eDK+8tH9TE4Kc+Xk3s5+CzVYIKeJbWuXgsKVbkdLJXGUc2eflFqjThQy555mBag== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" @@ -628,16 +633,16 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - color-name@~1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + colorette@^1.1.0: version "1.4.0" resolved "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz" @@ -709,13 +714,6 @@ data-uri-to-buffer@^6.0.2: resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz" integrity sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw== -debug@4, debug@^4.1.1, debug@^4.3.4, debug@^4.3.5, debug@^4.3.6: - version "4.4.3" - resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz" - integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== - dependencies: - ms "^2.1.3" - debug@^3.2.7: version "3.2.7" resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" @@ -723,6 +721,13 @@ debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.1.1, debug@^4.3.4, debug@^4.3.5, debug@^4.3.6, debug@4: + version "4.4.3" + resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + decamelize@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz" @@ -756,7 +761,7 @@ degenerator@^5.0.0: escodegen "^2.1.0" esprima "^4.0.1" -devtools-protocol@0.0.1330662: +devtools-protocol@*, devtools-protocol@0.0.1330662: version "0.0.1330662" resolved "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1330662.tgz" integrity sha512-pzh6YQ8zZfz3iKlCvgzVCu22NdpZ8hNmwU6WnQjNVquh0A9iVosPtNLWDwaWVGyrntQlltPFztTMK5Cg6lfCuw== @@ -1002,16 +1007,16 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@2.3.2: - version "2.3.2" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - fsevents@~2.3.2: version "2.3.3" resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== +fsevents@2.3.2: + version "2.3.2" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" @@ -1098,7 +1103,18 @@ glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.0.3, glob@^8.1.0: +glob@^8.0.3: + version "8.1.0" + resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + +glob@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz" integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== @@ -1534,7 +1550,14 @@ minimatch@^5.0.1, minimatch@^5.1.6: dependencies: brace-expansion "^2.0.1" -minimatch@^9.0.4, minimatch@^9.0.5: +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.5: version "9.0.5" resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz" integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== @@ -1949,7 +1972,7 @@ rollup-plugin-copy@^3.5.0: globby "10.0.1" is-plain-object "^3.0.0" -rollup@^3.27.2: +rollup@^1.20.0||^2.0.0||^3.0.0||^4.0.0, rollup@^2.14.0||^3.0.0||^4.0.0, rollup@^2.68.0||^3.0.0||^4.0.0, rollup@^2.78.0||^3.0.0||^4.0.0, rollup@^3.27.2: version "3.29.4" resolved "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz" integrity sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw== @@ -1963,7 +1986,7 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -safe-buffer@5.1.2, safe-buffer@^5.1.0: +safe-buffer@^5.1.0, safe-buffer@5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== @@ -2226,7 +2249,7 @@ ts-node@^10.9.2: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -tslib@^2.0.1: +tslib@*, tslib@^2.0.1: version "2.8.1" resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== @@ -2241,7 +2264,7 @@ typedoc-plugin-markdown@^4.8.1: resolved "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.9.0.tgz" integrity sha512-9Uu4WR9L7ZBgAl60N/h+jqmPxxvnC9nQAlnnO/OujtG2ubjnKTVUFY1XDhcMY+pCqlX3N2HsQM2QTYZIU9tJuw== -typedoc@^0.28.1: +typedoc@^0.28.1, typedoc@0.28.x: version "0.28.13" resolved "https://registry.npmjs.org/typedoc/-/typedoc-0.28.13.tgz" integrity sha512-dNWY8msnYB2a+7Audha+aTF1Pu3euiE7ySp53w8kEsXoYw7dMouV5A1UsTUY345aB152RHnmRMDiovuBi7BD+w== @@ -2252,7 +2275,7 @@ typedoc@^0.28.1: minimatch "^9.0.5" yaml "^2.8.1" -typescript@^5.5.4: +typescript@^5.5.4, typescript@>=2.7, typescript@>=3.7.0, typescript@>=4.9.5, "typescript@5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x": version "5.5.4" resolved "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz" integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== diff --git a/docs/typedoc/web-client/README.md b/docs/typedoc/web-client/README.md index 7f2ab019db..90f4eaebec 100644 --- a/docs/typedoc/web-client/README.md +++ b/docs/typedoc/web-client/README.md @@ -74,6 +74,7 @@ - [NoteAndArgsArray](classes/NoteAndArgsArray.md) - [NoteAssets](classes/NoteAssets.md) - [NoteConsumability](classes/NoteConsumability.md) +- [NoteConsumptionStatus](classes/NoteConsumptionStatus.md) - [NoteDetails](classes/NoteDetails.md) - [NoteDetailsAndTag](classes/NoteDetailsAndTag.md) - [NoteDetailsAndTagArray](classes/NoteDetailsAndTagArray.md) diff --git a/docs/typedoc/web-client/classes/NoteConsumability.md b/docs/typedoc/web-client/classes/NoteConsumability.md index 02b415a0a9..eb562841d3 100644 --- a/docs/typedoc/web-client/classes/NoteConsumability.md +++ b/docs/typedoc/web-client/classes/NoteConsumability.md @@ -30,15 +30,15 @@ Returns the account that can consume the note. *** -### consumableAfterBlock() +### consumptionStatus() -> **consumableAfterBlock**(): `number` +> **consumptionStatus**(): [`NoteConsumptionStatus`](NoteConsumptionStatus.md) -Returns the block number after which the note becomes consumable (if any). +Returns the consumption status of the note. #### Returns -`number` +[`NoteConsumptionStatus`](NoteConsumptionStatus.md) *** diff --git a/docs/typedoc/web-client/classes/NoteConsumptionStatus.md b/docs/typedoc/web-client/classes/NoteConsumptionStatus.md new file mode 100644 index 0000000000..a43926870d --- /dev/null +++ b/docs/typedoc/web-client/classes/NoteConsumptionStatus.md @@ -0,0 +1,115 @@ +[**@demox-labs/miden-sdk**](../README.md) + +*** + +[@demox-labs/miden-sdk](../README.md) / NoteConsumptionStatus + +# Class: NoteConsumptionStatus + +Describes if a note could be consumed under a specific conditions: target account state and +block height. + +## Methods + +### \[dispose\]() + +> **\[dispose\]**(): `void` + +#### Returns + +`void` + +*** + +### consumableAfterBlock() + +> **consumableAfterBlock**(): `number` + +Returns the block number at which the note can be consumed. +Returns None if the note is already consumable or never possible + +#### Returns + +`number` + +*** + +### free() + +> **free**(): `void` + +#### Returns + +`void` + +*** + +### consumable() + +> `static` **consumable**(): `NoteConsumptionStatus` + +Constructs a `NoteConsumptionStatus` that is consumable. + +#### Returns + +`NoteConsumptionStatus` + +*** + +### consumableAfter() + +> `static` **consumableAfter**(`block_height`): `NoteConsumptionStatus` + +Constructs a `NoteConsumptionStatus` that is consumable after a specific block height. + +#### Parameters + +##### block\_height + +`number` + +#### Returns + +`NoteConsumptionStatus` + +*** + +### consumableWithAuthorization() + +> `static` **consumableWithAuthorization**(): `NoteConsumptionStatus` + +Constructs a `NoteConsumptionStatus` that is consumable with authorization. + +#### Returns + +`NoteConsumptionStatus` + +*** + +### neverConsumable() + +> `static` **neverConsumable**(`err`): `NoteConsumptionStatus` + +Constructs a `NoteConsumptionStatus` that is never consumable. + +#### Parameters + +##### err + +`string` + +#### Returns + +`NoteConsumptionStatus` + +*** + +### unconsumableConditions() + +> `static` **unconsumableConditions**(): `NoteConsumptionStatus` + +Constructs a `NoteConsumptionStatus` that is unconsumable due to conditions. + +#### Returns + +`NoteConsumptionStatus` From cda943d4584a48cceaf877d6853eba9ca16c020d Mon Sep 17 00:00:00 2001 From: SantiagoPittella Date: Fri, 9 Jan 2026 19:17:20 -0300 Subject: [PATCH 4/8] fix: remove unconsumable notes --- crates/rust-client/src/note/note_screener.rs | 8 ++++++++ crates/testing/miden-client-tests/src/tests.rs | 4 +++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/crates/rust-client/src/note/note_screener.rs b/crates/rust-client/src/note/note_screener.rs index b9063b5c5a..5c7cb88da1 100644 --- a/crates/rust-client/src/note/note_screener.rs +++ b/crates/rust-client/src/note/note_screener.rs @@ -123,6 +123,14 @@ where ) .await?; + if matches!( + note_consumption_check, + NoteConsumptionStatus::NeverConsumable(..) + | NoteConsumptionStatus::UnconsumableConditions + ) { + return Ok(None); + } + Ok(Some(note_consumption_check)) } diff --git a/crates/testing/miden-client-tests/src/tests.rs b/crates/testing/miden-client-tests/src/tests.rs index d70952b3a9..38bd0ab1a5 100644 --- a/crates/testing/miden-client-tests/src/tests.rs +++ b/crates/testing/miden-client-tests/src/tests.rs @@ -1508,7 +1508,9 @@ async fn get_consumable_notes() { .1; match to_account_relevance { - NoteConsumptionStatus::Consumable => {}, + NoteConsumptionStatus::Consumable + | NoteConsumptionStatus::ConsumableAfter(..) + | NoteConsumptionStatus::ConsumableWithAuthorization => {}, _ => panic!("Unexpected NoteConsumptionStatus"), } } From e86e26331f263ed9dcae49f2b535208c55f2e652 Mon Sep 17 00:00:00 2001 From: SantiagoPittella Date: Mon, 12 Jan 2026 13:52:57 -0300 Subject: [PATCH 5/8] fix: integration tests for web client --- crates/web-client/test/notes.test.ts | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/crates/web-client/test/notes.test.ts b/crates/web-client/test/notes.test.ts index 5b1b643bcc..793e91a625 100644 --- a/crates/web-client/test/notes.test.ts +++ b/crates/web-client/test/notes.test.ts @@ -13,11 +13,7 @@ import { setupPublicConsumedNote, } from "./webClientTestUtils"; import { Page, expect } from "@playwright/test"; -import { - ConsumableNoteRecord, - NoteConsumability, - NoteConsumptionStatus, -} from "../dist/crates/miden_client_web"; +import { ConsumableNoteRecord } from "../dist/crates/miden_client_web"; const getConsumableNotes = async ( testingPage: Page, @@ -27,7 +23,7 @@ const getConsumableNotes = async ( noteId: string; consumability: { accountId: string; - consumptionStatus: NoteConsumptionStatus | undefined; + consumableAfterBlock: number | undefined; }[]; }[] > => { @@ -46,7 +42,7 @@ const getConsumableNotes = async ( noteId: record.inputNoteRecord().id().toString(), consumability: record.noteConsumability().map((c) => ({ accountId: c.accountId().toString(), - consumptionStatus: c.consumptionStatus(), + consumableAfterBlock: c.consumptionStatus()?.consumableAfterBlock(), })), })); }, accountId); @@ -156,9 +152,7 @@ test.describe("get_consumable_notes", () => { expect(record.consumability).toHaveLength(1); expect(record.consumability[0].accountId).toBe(accountId1); expect(record.noteId).toBe(noteId1); - expect( - record.consumability[0].consumptionStatus().consumableAfterBlock() - ).toBeUndefined(); + expect(record.consumability[0].consumableAfterBlock).toBeUndefined(); }); }); @@ -204,16 +198,12 @@ test.describe("get_consumable_notes", () => { const consumableRecipient = await getConsumableNotes(page, targetAccountId); const consumableSender = await getConsumableNotes(page, senderAccountId); expect(consumableSender.length).toBe(1); - expect( - consumableSender[0].consumability[0] - .consumptionStatus() - .consumableAfterBlock() - ).toBe(recallHeight); + expect(consumableSender[0].consumability[0].consumableAfterBlock).toBe( + recallHeight + ); expect(consumableRecipient.length).toBe(1); expect( - consumableRecipient[0].consumability[0] - .consumptionStatus() - .consumableAfterBlock() + consumableRecipient[0].consumability[0].consumableAfterBlock ).toBeUndefined(); }); }); From 1a7018343941b96f85267693e9a7672ad0a9f1ba Mon Sep 17 00:00:00 2001 From: Ignacio Amigo Date: Wed, 14 Jan 2026 00:42:58 -0300 Subject: [PATCH 6/8] refactor: simplify and remove the standard checks --- crates/rust-client/src/note/note_screener.rs | 66 +++----------------- 1 file changed, 10 insertions(+), 56 deletions(-) diff --git a/crates/rust-client/src/note/note_screener.rs b/crates/rust-client/src/note/note_screener.rs index 5c7cb88da1..66c35e709a 100644 --- a/crates/rust-client/src/note/note_screener.rs +++ b/crates/rust-client/src/note/note_screener.rs @@ -7,7 +7,7 @@ use miden_protocol::account::{Account, AccountId}; use miden_protocol::note::{Note, NoteId}; use miden_protocol::{AccountError, AssetError}; use miden_standards::account::interface::{AccountInterface, AccountInterfaceExt}; -use miden_standards::note::{NoteConsumptionStatus, WellKnownNote}; +use miden_standards::note::NoteConsumptionStatus; use miden_tx::auth::TransactionAuthenticator; use miden_tx::{NoteCheckerError, NoteConsumptionChecker, TransactionExecutor}; use thiserror::Error; @@ -50,7 +50,8 @@ where /// Returns a vector of tuples describing the relevance of the provided note to the /// accounts monitored by this screener. /// - /// If relevance can't be determined, the screener defaults to setting the note as consumable. + /// The relevance is determined by [`NoteConsumptionChecker::can_consume`] and is based on + /// current conditions (for example, it takes the latest block in the client as reference). pub async fn check_relevance( &self, note: &Note, @@ -66,24 +67,12 @@ where .try_into() .map_err(|_| NoteScreenerError::AccountDataNotFound(id))?; - match self.check_standard_consumability(&account, note).await { - Ok(Some(relevance)) => { + match self.check_standard_consumability(&account, note).await? { + NoteConsumptionStatus::NeverConsumable(_) + | NoteConsumptionStatus::UnconsumableConditions => {}, + relevance => { note_relevances.push((id, relevance)); }, - Ok(None) => { - // The note might be consumable after a certain block height if the note is - // p2ide - let script_root = note.script().root(); - - if script_root == WellKnownNote::P2IDE.script_root() - && let Some(relevance) = Self::check_p2ide_recall_consumability(note, &id)? - { - note_relevances.push((id, relevance)); - } - }, - // If an error occurs while checking consumability, we count it as not relevant for - // that account - Err(_) => {}, } } @@ -92,11 +81,11 @@ where /// Tries to execute a standard consume transaction to check if the note is consumable by the /// account. - async fn check_standard_consumability( + pub async fn check_standard_consumability( &self, account: &Account, note: &Note, - ) -> Result, NoteScreenerError> { + ) -> Result { let transaction_request = TransactionRequestBuilder::new().build_consume_notes(vec![note.id()])?; @@ -123,42 +112,7 @@ where ) .await?; - if matches!( - note_consumption_check, - NoteConsumptionStatus::NeverConsumable(..) - | NoteConsumptionStatus::UnconsumableConditions - ) { - return Ok(None); - } - - Ok(Some(note_consumption_check)) - } - - /// Special relevance check for P2IDE notes. It checks if the sender account can consume and - /// recall the note. - fn check_p2ide_recall_consumability( - note: &Note, - account_id: &AccountId, - ) -> Result, NoteScreenerError> { - let note_inputs = note.inputs().values(); - // TODO: this needs to be removed (see note screener refactor issue) - - if note_inputs.len() != 4 { - return Err(InvalidNoteInputsError::WrongNumInputs(note.id(), 4).into()); - } - - let recall_height_felt = note_inputs[2]; - - let sender = note.metadata().sender(); - let recall_height: u32 = recall_height_felt.as_int().try_into().map_err(|_err| { - InvalidNoteInputsError::BlockNumberError(note.id(), recall_height_felt.as_int()) - })?; - - if sender == *account_id { - Ok(Some(NoteConsumptionStatus::ConsumableAfter(recall_height.into()))) - } else { - Ok(None) - } + Ok(note_consumption_check) } } From 2fcb324bb8ff04890a3af0961eb0dcfcf929ecda Mon Sep 17 00:00:00 2001 From: SantiagoPittella Date: Wed, 14 Jan 2026 15:24:05 -0300 Subject: [PATCH 7/8] chore: refresh web client docs --- docs/typedoc/web-client/classes/NoteConsumptionStatus.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/typedoc/web-client/classes/NoteConsumptionStatus.md b/docs/typedoc/web-client/classes/NoteConsumptionStatus.md index a43926870d..6e18a6ac34 100644 --- a/docs/typedoc/web-client/classes/NoteConsumptionStatus.md +++ b/docs/typedoc/web-client/classes/NoteConsumptionStatus.md @@ -1,8 +1,8 @@ -[**@demox-labs/miden-sdk**](../README.md) +[**@miden-sdk/miden-sdk**](../README.md) *** -[@demox-labs/miden-sdk](../README.md) / NoteConsumptionStatus +[@miden-sdk/miden-sdk](../README.md) / NoteConsumptionStatus # Class: NoteConsumptionStatus From 4ebfcb11b029365dee60d7ce0eacd030e58099b6 Mon Sep 17 00:00:00 2001 From: igamigo Date: Wed, 14 Jan 2026 16:35:59 -0300 Subject: [PATCH 8/8] chore: changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8acaa0de17..8fc3b4dbff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ * Fixed MMR reconstruction code and fixed how block authentication paths are adjusted ([#1633](https://github.com/0xMiden/miden-client/pull/1633)). * Added WebClient bindings and RPC helpers for additional account, note, and validation workflows ([#1638](https://github.com/0xMiden/miden-client/pull/1638)). * [BREAKING] Modified JS binding for `AccountComponent::compile` which now takes an `AccountComponentCode` built with the newly added binding `CodeBuilder::compile_account_component_code` ([#1627](https://github.com/0xMiden/miden-client/pull/1627)). -* [BREAKING] Refactored the `NoteScreener` API ([#1630](https://github.com/0xMiden/miden-client/pull/1630)). +* [BREAKING] Simplified the `NoteScreener` API, removing `NoteRelevance` in favor of `NoteConsumptionStatus`; exposed JS bindings for consumption check results ([#1630](https://github.com/0xMiden/miden-client/pull/1630)). * [BREAKING] Replaced `TransactionRequestBuilder::unauthenticated_input_notes` & `TransactionRequestBuilder::authenticated_input_notes` for `TransactionRequestBuilder::input_notes`, now the user passes a list of notes which the `Client` itself determines the authentication status of ([#1624](https://github.com/0xMiden/miden-client/issues/1624)). ## 0.12.5 (2025-12-01)