miden_lib/note/
mod.rs

1use alloc::vec::Vec;
2
3use miden_objects::account::AccountId;
4use miden_objects::asset::Asset;
5use miden_objects::block::BlockNumber;
6use miden_objects::crypto::rand::FeltRng;
7use miden_objects::note::{
8    Note,
9    NoteAssets,
10    NoteDetails,
11    NoteExecutionHint,
12    NoteInputs,
13    NoteMetadata,
14    NoteRecipient,
15    NoteTag,
16    NoteType,
17};
18use miden_objects::{Felt, NoteError, Word};
19use utils::build_swap_tag;
20use well_known_note::WellKnownNote;
21
22pub mod utils;
23pub mod well_known_note;
24
25// STANDARDIZED SCRIPTS
26// ================================================================================================
27
28/// Generates a P2ID note - Pay-to-ID note.
29///
30/// This script enables the transfer of assets from the `sender` account to the `target` account
31/// by specifying the target's account ID.
32///
33/// The passed-in `rng` is used to generate a serial number for the note. The returned note's tag
34/// is set to the target's account ID.
35///
36/// # Errors
37/// Returns an error if deserialization or compilation of the `P2ID` script fails.
38pub fn create_p2id_note<R: FeltRng>(
39    sender: AccountId,
40    target: AccountId,
41    assets: Vec<Asset>,
42    note_type: NoteType,
43    aux: Felt,
44    rng: &mut R,
45) -> Result<Note, NoteError> {
46    let serial_num = rng.draw_word();
47    let recipient = utils::build_p2id_recipient(target, serial_num)?;
48
49    let tag = NoteTag::from_account_id(target);
50
51    let metadata = NoteMetadata::new(sender, note_type, tag, NoteExecutionHint::always(), aux)?;
52    let vault = NoteAssets::new(assets)?;
53
54    Ok(Note::new(vault, metadata, recipient))
55}
56
57/// Generates a P2IDE note - Pay-to-ID note with optional reclaim after a certain block height and
58/// optional timelock.
59///
60/// This script enables the transfer of assets from the `sender` account to the `target`
61/// account by specifying the target's account ID. It adds the optional possibility for the
62/// sender to reclaiming the assets if the note has not been consumed by the target within the
63/// specified timeframe and the optional possibility to add a timelock to the asset transfer.
64///
65/// The passed-in `rng` is used to generate a serial number for the note. The returned note's tag
66/// is set to the target's account ID.
67///
68/// # Errors
69/// Returns an error if deserialization or compilation of the `P2ID` script fails.
70pub fn create_p2ide_note<R: FeltRng>(
71    sender: AccountId,
72    target: AccountId,
73    assets: Vec<Asset>,
74    reclaim_height: Option<BlockNumber>,
75    timelock_height: Option<BlockNumber>,
76    note_type: NoteType,
77    aux: Felt,
78    rng: &mut R,
79) -> Result<Note, NoteError> {
80    let serial_num = rng.draw_word();
81    let recipient =
82        utils::build_p2ide_recipient(target, reclaim_height, timelock_height, serial_num)?;
83    let tag = NoteTag::from_account_id(target);
84
85    let execution_hint = match timelock_height {
86        Some(height) => NoteExecutionHint::after_block(height)?,
87        None => NoteExecutionHint::always(),
88    };
89
90    let metadata = NoteMetadata::new(sender, note_type, tag, execution_hint, aux)?;
91    let vault = NoteAssets::new(assets)?;
92
93    Ok(Note::new(vault, metadata, recipient))
94}
95
96/// Generates a SWAP note - swap of assets between two accounts - and returns the note as well as
97/// [NoteDetails] for the payback note.
98///
99/// This script enables a swap of 2 assets between the `sender` account and any other account that
100/// is willing to consume the note. The consumer will receive the `offered_asset` and will create a
101/// new P2ID note with `sender` as target, containing the `requested_asset`.
102///
103/// # Errors
104/// Returns an error if deserialization or compilation of the `SWAP` script fails.
105pub fn create_swap_note<R: FeltRng>(
106    sender: AccountId,
107    offered_asset: Asset,
108    requested_asset: Asset,
109    swap_note_type: NoteType,
110    swap_note_aux: Felt,
111    payback_note_type: NoteType,
112    payback_note_aux: Felt,
113    rng: &mut R,
114) -> Result<(Note, NoteDetails), NoteError> {
115    if requested_asset == offered_asset {
116        return Err(NoteError::other("requested asset same as offered asset"));
117    }
118
119    let note_script = WellKnownNote::SWAP.script();
120
121    let payback_serial_num = rng.draw_word();
122    let payback_recipient = utils::build_p2id_recipient(sender, payback_serial_num)?;
123
124    let payback_recipient_word: Word = payback_recipient.digest();
125    let requested_asset_word: Word = requested_asset.into();
126    let payback_tag = NoteTag::from_account_id(sender);
127
128    let inputs = NoteInputs::new(vec![
129        requested_asset_word[0],
130        requested_asset_word[1],
131        requested_asset_word[2],
132        requested_asset_word[3],
133        payback_recipient_word[0],
134        payback_recipient_word[1],
135        payback_recipient_word[2],
136        payback_recipient_word[3],
137        NoteExecutionHint::always().into(),
138        payback_note_type.into(),
139        payback_note_aux,
140        payback_tag.into(),
141    ])?;
142
143    // build the tag for the SWAP use case
144    let tag = build_swap_tag(swap_note_type, &offered_asset, &requested_asset)?;
145    let serial_num = rng.draw_word();
146
147    // build the outgoing note
148    let metadata =
149        NoteMetadata::new(sender, swap_note_type, tag, NoteExecutionHint::always(), swap_note_aux)?;
150    let assets = NoteAssets::new(vec![offered_asset])?;
151    let recipient = NoteRecipient::new(serial_num, note_script, inputs);
152    let note = Note::new(assets, metadata, recipient);
153
154    // build the payback note details
155    let payback_assets = NoteAssets::new(vec![requested_asset])?;
156    let payback_note = NoteDetails::new(payback_assets, payback_recipient);
157
158    Ok((note, payback_note))
159}