miden_lib/note/
utils.rs

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