use miden_protocol::block::BlockNumber;
use miden_protocol::errors::NoteError;
use miden_protocol::note::{Note, NoteId, Nullifier};
use miden_standards::note::{FeeSponsorshipNote, FeeSponsorshipNoteStorage};
#[derive(Debug, Clone)]
pub struct SponsorshipNote {
note: Note,
}
impl SponsorshipNote {
fn storage(&self) -> FeeSponsorshipNoteStorage {
FeeSponsorshipNoteStorage::try_from(self.note.storage().items())
.expect("SponsorshipNote guarantees valid FEE_SPONSORSHIP storage")
}
pub fn feature_note_id(&self) -> NoteId {
self.storage().feature_note_id()
}
pub fn reclaim_height(&self) -> Option<BlockNumber> {
self.storage().reclaim_height()
}
pub fn id(&self) -> NoteId {
self.note.id()
}
pub fn nullifier(&self) -> Nullifier {
self.note.nullifier()
}
pub fn as_note(&self) -> &Note {
&self.note
}
}
impl TryFrom<Note> for SponsorshipNote {
type Error = NoteError;
fn try_from(note: Note) -> Result<Self, Self::Error> {
if note.script().root() != FeeSponsorshipNote::script_root() {
return Err(NoteError::other(
"note script root does not match the FEE_SPONSORSHIP script root",
));
}
FeeSponsorshipNoteStorage::try_from(note.storage().items())?;
if note.assets().num_assets() != 1 {
return Err(NoteError::other("fee sponsorship note must carry exactly one asset"));
}
Ok(Self { note })
}
}
#[cfg(test)]
mod tests {
use miden_protocol::Word;
use miden_protocol::note::NoteId;
use super::*;
use crate::test_utils::{
mock_network_account_id,
mock_single_target_note,
mock_sponsorship_note,
};
fn feature_note_id() -> NoteId {
NoteId::from_raw(Word::from([7, 8, 9, 10u32]))
}
#[test]
fn try_from_accepts_sponsorship_note() {
let note = mock_sponsorship_note(mock_network_account_id(), feature_note_id(), 1);
let detected = SponsorshipNote::try_from(note.clone())
.expect("a FEE_SPONSORSHIP note must be detected");
assert_eq!(detected.feature_note_id(), feature_note_id());
assert_eq!(detected.id(), note.id());
assert_eq!(detected.nullifier(), note.nullifier());
assert_eq!(detected.reclaim_height(), None);
}
#[test]
fn try_from_rejects_other_scripts() {
let network_note = mock_single_target_note(mock_network_account_id(), 1);
assert!(SponsorshipNote::try_from(network_note.as_note().clone()).is_err());
}
}