miden_lib/note/
utils.rs

1use miden_objects::{
2    Felt, NoteError, Word,
3    account::AccountId,
4    asset::Asset,
5    block::BlockNumber,
6    note::{NoteExecutionMode, NoteInputs, NoteRecipient, NoteTag, NoteType},
7};
8
9use super::well_known_note::WellKnownNote;
10
11/// Creates a [NoteRecipient] for the P2ID note.
12///
13/// Notes created with this recipient will be P2ID notes consumable by the specified target
14/// account.
15pub fn build_p2id_recipient(
16    target: AccountId,
17    serial_num: Word,
18) -> Result<NoteRecipient, NoteError> {
19    let note_script = WellKnownNote::P2ID.script();
20    let note_inputs = NoteInputs::new(vec![target.suffix(), target.prefix().as_felt()])?;
21
22    Ok(NoteRecipient::new(serial_num, note_script, note_inputs))
23}
24
25/// Creates a [NoteRecipient] for the P2IDE note.
26///
27/// Notes created with this recipient will be P2IDE notes consumable by the specified target
28/// account.
29pub fn build_p2ide_recipient(
30    target: AccountId,
31    reclaim_block_height: Option<BlockNumber>,
32    timelock_block_height: Option<BlockNumber>,
33    serial_num: Word,
34) -> Result<NoteRecipient, NoteError> {
35    let note_script = WellKnownNote::P2IDE.script();
36
37    let reclaim_height_u32 = reclaim_block_height.map_or(0, |bn| bn.as_u32());
38    let timelock_height_u32 = timelock_block_height.map_or(0, |bn| bn.as_u32());
39
40    let note_inputs = NoteInputs::new(vec![
41        target.suffix(),
42        target.prefix().into(),
43        Felt::new(reclaim_height_u32 as u64),
44        Felt::new(timelock_height_u32 as u64),
45    ])?;
46
47    Ok(NoteRecipient::new(serial_num, note_script, note_inputs))
48}
49
50/// Returns a note tag for a swap note with the specified parameters.
51///
52/// Use case ID for the returned tag is set to 0.
53///
54/// Tag payload is constructed by taking asset tags (8 bits of each faucet ID) and concatenating
55/// them together as offered_asset_tag + requested_asset tag.
56///
57/// Network execution hint for the returned tag is set to `Local`.
58pub fn build_swap_tag(
59    note_type: NoteType,
60    offered_asset: &Asset,
61    requested_asset: &Asset,
62) -> Result<NoteTag, NoteError> {
63    const SWAP_USE_CASE_ID: u16 = 0;
64
65    // Get bits 0..8 from the faucet IDs of both assets which will form the tag payload.
66    let offered_asset_id: u64 = offered_asset.faucet_id_prefix().into();
67    let offered_asset_tag = (offered_asset_id >> 56) as u8;
68
69    let requested_asset_id: u64 = requested_asset.faucet_id_prefix().into();
70    let requested_asset_tag = (requested_asset_id >> 56) as u8;
71
72    let payload = ((offered_asset_tag as u16) << 8) | (requested_asset_tag as u16);
73
74    let execution = NoteExecutionMode::Local;
75    match note_type {
76        NoteType::Public => NoteTag::for_public_use_case(SWAP_USE_CASE_ID, payload, execution),
77        _ => NoteTag::for_local_use_case(SWAP_USE_CASE_ID, payload),
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use miden_objects::{
84        self,
85        account::{AccountIdVersion, AccountStorageMode, AccountType},
86        asset::{FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails},
87    };
88
89    use super::*;
90
91    #[test]
92    fn swap_tag() {
93        // Construct an ID that starts with 0xcdb1.
94        let mut fungible_faucet_id_bytes = [0; 15];
95        fungible_faucet_id_bytes[0] = 0xcd;
96        fungible_faucet_id_bytes[1] = 0xb1;
97
98        // Construct an ID that starts with 0xabec.
99        let mut non_fungible_faucet_id_bytes = [0; 15];
100        non_fungible_faucet_id_bytes[0] = 0xab;
101        non_fungible_faucet_id_bytes[1] = 0xec;
102
103        let offered_asset = Asset::Fungible(
104            FungibleAsset::new(
105                AccountId::dummy(
106                    fungible_faucet_id_bytes,
107                    AccountIdVersion::Version0,
108                    AccountType::FungibleFaucet,
109                    AccountStorageMode::Public,
110                ),
111                2500,
112            )
113            .unwrap(),
114        );
115
116        let requested_asset = Asset::NonFungible(
117            NonFungibleAsset::new(
118                &NonFungibleAssetDetails::new(
119                    AccountId::dummy(
120                        non_fungible_faucet_id_bytes,
121                        AccountIdVersion::Version0,
122                        AccountType::NonFungibleFaucet,
123                        AccountStorageMode::Public,
124                    )
125                    .prefix(),
126                    vec![0xaa, 0xbb, 0xcc, 0xdd],
127                )
128                .unwrap(),
129            )
130            .unwrap(),
131        );
132
133        // The fungible ID starts with 0xcdb1.
134        // The non fungible ID starts with 0xabec.
135        // The expected tag payload is thus 0xcdab.
136        let expected_tag_payload = 0xcdab;
137
138        let actual_tag =
139            build_swap_tag(NoteType::Public, &offered_asset, &requested_asset).unwrap();
140
141        // 0 is the SWAP use case ID.
142        let expected_tag =
143            NoteTag::for_public_use_case(0, expected_tag_payload, NoteExecutionMode::Local)
144                .unwrap();
145
146        assert_eq!(actual_tag, expected_tag);
147    }
148}