use alloc::vec::Vec;
use miden_protocol::account::AccountId;
use miden_tx::auth::TransactionAuthenticator;
use crate::store::{InputNoteRecord, NoteFilter, OutputNoteRecord};
use crate::{Client, ClientError, IdPrefixFetchError};
mod import;
mod note_reader;
mod note_screener;
mod note_update_tracker;
pub use miden_protocol::block::BlockNumber;
pub use miden_protocol::errors::NoteError;
pub use miden_protocol::note::{
Note,
NoteAssets,
NoteAttachment,
NoteAttachmentKind,
NoteAttachmentScheme,
NoteDetails,
NoteFile,
NoteHeader,
NoteId,
NoteInclusionProof,
NoteLocation,
NoteMetadata,
NoteMetadataHeader,
NoteRecipient,
NoteScript,
NoteStorage,
NoteTag,
NoteType,
Nullifier,
PartialNote,
};
pub use miden_protocol::transaction::ToInputNoteCommitments;
pub use miden_standards::note::{
NetworkAccountTarget,
NoteConsumptionStatus,
NoteExecutionHint,
P2idNote,
P2idNoteStorage,
StandardNote,
SwapNote,
};
pub use miden_tx::{FailedNote, NoteConsumptionInfo};
pub use note_reader::InputNoteReader;
pub use note_screener::{NoteConsumability, NoteScreener, NoteScreenerError};
pub use note_update_tracker::{
InputNoteUpdate,
NoteUpdateTracker,
NoteUpdateType,
OutputNoteUpdate,
};
impl<AUTH> Client<AUTH>
where
AUTH: TransactionAuthenticator + Sync,
{
pub async fn get_input_notes(
&self,
filter: NoteFilter,
) -> Result<Vec<InputNoteRecord>, ClientError> {
self.store.get_input_notes(filter).await.map_err(Into::into)
}
pub async fn get_consumable_notes(
&self,
account_id: Option<AccountId>,
) -> Result<Vec<(InputNoteRecord, Vec<NoteConsumability>)>, ClientError> {
let committed_notes = self.store.get_input_notes(NoteFilter::Committed).await?;
let notes = committed_notes
.iter()
.cloned()
.map(TryInto::try_into)
.collect::<Result<Vec<Note>, _>>()?;
let note_screener = self.note_screener();
let mut note_relevances = note_screener.can_consume_batch(¬es).await?;
let mut relevant_notes = Vec::new();
for input_note in committed_notes {
let note_id = input_note.id();
let Some(mut account_relevance) = note_relevances.remove(¬e_id) else {
continue;
};
if let Some(account_id) = account_id {
account_relevance.retain(|(id, _)| *id == account_id);
}
if account_relevance.is_empty() {
continue;
}
relevant_notes.push((input_note, account_relevance));
}
Ok(relevant_notes)
}
pub async fn get_note_consumability(
&self,
note: InputNoteRecord,
) -> Result<Vec<NoteConsumability>, ClientError> {
self.note_screener().can_consume(¬e.try_into()?).await.map_err(Into::into)
}
pub async fn get_input_note(
&self,
note_id: NoteId,
) -> Result<Option<InputNoteRecord>, ClientError> {
Ok(self.store.get_input_notes(NoteFilter::Unique(note_id)).await?.pop())
}
pub async fn get_output_notes(
&self,
filter: NoteFilter,
) -> Result<Vec<OutputNoteRecord>, ClientError> {
self.store.get_output_notes(filter).await.map_err(Into::into)
}
pub async fn get_output_note(
&self,
note_id: NoteId,
) -> Result<Option<OutputNoteRecord>, ClientError> {
Ok(self.store.get_output_notes(NoteFilter::Unique(note_id)).await?.pop())
}
pub fn input_note_reader(&self, consumer: AccountId) -> InputNoteReader {
InputNoteReader::new(self.store.clone(), consumer)
}
}
pub async fn get_input_note_with_id_prefix<AUTH>(
client: &Client<AUTH>,
note_id_prefix: &str,
) -> Result<InputNoteRecord, IdPrefixFetchError>
where
AUTH: TransactionAuthenticator + Sync,
{
let mut input_note_records = client
.get_input_notes(NoteFilter::All)
.await
.map_err(|err| {
tracing::error!("Error when fetching all notes from the store: {err}");
IdPrefixFetchError::NoMatch(format!("note ID prefix {note_id_prefix}"))
})?
.into_iter()
.filter(|note_record| note_record.id().to_hex().starts_with(note_id_prefix))
.collect::<Vec<_>>();
if input_note_records.is_empty() {
return Err(IdPrefixFetchError::NoMatch(format!("note ID prefix {note_id_prefix}")));
}
if input_note_records.len() > 1 {
let input_note_record_ids =
input_note_records.iter().map(InputNoteRecord::id).collect::<Vec<_>>();
tracing::error!(
"Multiple notes found for the prefix {}: {:?}",
note_id_prefix,
input_note_record_ids
);
return Err(IdPrefixFetchError::MultipleMatches(format!(
"note ID prefix {note_id_prefix}"
)));
}
Ok(input_note_records
.pop()
.expect("input_note_records should always have one element"))
}