cardano_sdk/cardano/
macros.rs

1//  This Source Code Form is subject to the terms of the Mozilla Public
2//  License, v. 2.0. If a copy of the MPL was not distributed with this
3//  file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5//! Useful macros for testing and quickly constructing objects.
6
7/// Construct variable-length [`Hash`](crate::Hash) from base16-encoded text strings.
8///
9/// # examples
10///
11/// ```rust
12/// # use cardano_sdk::{hash};
13/// assert_eq!(
14///     <[u8; 28]>::from(hash!("00000000000000000000000000000000000000000000000000000000")),
15///     [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
16/// )
17/// ```
18///
19/// ```rust
20/// # use cardano_sdk::{hash};
21/// assert_eq!(
22///     <[u8; 32]>::from(hash!("702206530b2e1566e90b3aec753bd0abbf397842bd5421e0c3d23ed10167b3ce")),
23///     [112, 34, 6, 83, 11, 46, 21, 102, 233, 11, 58, 236, 117, 59, 208, 171, 191, 57, 120, 66, 189, 84, 33, 224, 195, 210, 62, 209, 1, 103, 179, 206],
24/// )
25/// ```
26#[macro_export]
27macro_rules! hash {
28    ($txt:literal $(,)?) => {
29        <$crate::Hash<_>>::try_from($txt).unwrap()
30    };
31}
32
33#[macro_export]
34/// Construct [`Input`](crate::Input) from base16-encoded text strings & plain numbers.
35///
36/// The macro is variadic. It always requires a [`Hash<32>`](crate::Hash) for transaction id, and a
37/// [`u64`] index. It may also take in an optional [`PlutusData`](crate::PlutusData) redeemer or
38/// '_' to indicate none, in cases where the input is spending from a script.
39macro_rules! input {
40    ($id:literal, $index:expr $(,)?) => {
41        $crate::Input::new(<$crate::Hash<32>>::try_from($id).unwrap(), $index)
42    };
43
44    ($id:literal, $index:expr, _ $(,)?) => {
45        (
46            $crate::Input::new(<$crate::Hash<32>>::try_from($id).unwrap(), $index),
47            None,
48        )
49    };
50
51    ($id:literal, $index:expr, $redeemer:expr $(,)?) => {
52        (
53            $crate::Input::new(<$crate::Hash<32>>::try_from($id).unwrap(), $index),
54            Some($redeemer),
55        )
56    };
57}
58
59/// Construct a [`Mainnet`](crate::NetworkId::mainnet) address from a literal or from its
60/// constituents.
61///
62/// Panics when given anything invalid.
63///
64/// Note that the first variation returns an [`Address<Any>`](crate::Address), whereas the two
65/// other returns a [`Address<Shelley>`](crate::Address).
66///
67/// # examples
68///
69/// ```rust
70/// # use cardano_sdk::{address, address::{Address, kind}};
71/// // From a string literal:
72/// let my_address: Address<kind::Any> =
73///   address!("addr1v83gkkw3nqzakg5xynlurqcfqhgd65vkfvf5xv8tx25ufds2yvy2h");
74/// ```
75///
76/// ```rust
77/// # use cardano_sdk::{address, address::{Address, kind}, script_credential};
78/// // From a script credential, using yet another macro:
79/// assert_eq!(
80///   address!(script_credential!("bd3ae991b5aafccafe5ca70758bd36a9b2f872f57f6d3a1ffa0eb777")).to_string(),
81///   "addr1wx7n46v3kk40ejh7tjnswk9ax65m97rj74lk6wsllg8twac57dez7",
82/// );
83/// ```
84///
85/// ```rust
86/// # use cardano_sdk::{address, address::{Address, kind}, key_credential};
87/// // From key credentials, with delegation:
88/// assert_eq!(
89///   address!(
90///     key_credential!("bd3ae991b5aafccafe5ca70758bd36a9b2f872f57f6d3a1ffa0eb777"),
91///     key_credential!("bd3ae991b5aafccafe5ca70758bd36a9b2f872f57f6d3a1ffa0eb777"),
92///   ).to_string(),
93///   "addr1qx7n46v3kk40ejh7tjnswk9ax65m97rj74lk6wsllg8twaaa8t5erdd2ln90uh98qavt6d4fktu89atld5apl7swkamst576s8",
94/// );
95/// ```
96#[macro_export]
97macro_rules! address {
98    ($text:literal $(,)?) => {{
99        let address = $crate::Address::<$crate::address::kind::Any>::try_from($text).unwrap();
100        if address
101            .as_shelley()
102            .is_some_and(|shelley| shelley.network_id() != $crate::NetworkId::MAINNET)
103        {
104            panic!("network mismatch for address {}", $text);
105        }
106        address
107    }};
108
109    ($payment:expr $(,)?) => {
110        $crate::Address::new($crate::NetworkId::MAINNET, $payment)
111    };
112
113    ($payment:expr, $delegation: expr $(,)?) => {
114        $crate::Address::new($crate::NetworkId::MAINNET, $payment).with_delegation($delegation)
115    };
116}
117
118/// Construct a [`Testnet`](crate::NetworkId::testnet) address from a literal or from its
119/// constituents.
120///
121/// Panics when given anything invalid.
122///
123/// Note that the first variation returns an [`Address<Any>`](crate::Address), whereas the two
124/// other returns a [`Address<Shelley>`](crate::Address).
125///
126/// # examples
127///
128/// ```rust
129/// # use cardano_sdk::{address_test, address::{Address, kind}};
130/// // From a string literal:
131/// let my_address: Address<kind::Any> =
132///   address_test!("addr_test1vr3gkkw3nqzakg5xynlurqcfqhgd65vkfvf5xv8tx25ufds3vcc9j");
133/// ```
134///
135/// ```rust
136/// # use cardano_sdk::{address_test, address::{Address, kind}, script_credential};
137/// // From a script credential, using yet another macro:
138/// assert_eq!(
139///   address_test!(script_credential!("bd3ae991b5aafccafe5ca70758bd36a9b2f872f57f6d3a1ffa0eb777")).to_string(),
140///   "addr_test1wz7n46v3kk40ejh7tjnswk9ax65m97rj74lk6wsllg8twac0ke9dm",
141/// );
142/// ```
143///
144/// ```rust
145/// # use cardano_sdk::{address_test, address::{Address, kind}, key_credential};
146/// // From key credentials, with delegation:
147/// assert_eq!(
148///   address_test!(
149///     key_credential!("bd3ae991b5aafccafe5ca70758bd36a9b2f872f57f6d3a1ffa0eb777"),
150///     key_credential!("bd3ae991b5aafccafe5ca70758bd36a9b2f872f57f6d3a1ffa0eb777"),
151///   ).to_string(),
152///   "addr_test1qz7n46v3kk40ejh7tjnswk9ax65m97rj74lk6wsllg8twaaa8t5erdd2ln90uh98qavt6d4fktu89atld5apl7swkamsgzr6uc",
153/// );
154/// ```
155#[macro_export]
156macro_rules! address_test {
157    ($text:literal $(,)?) => {{
158        let address = $crate::Address::<$crate::address::kind::Any>::try_from($text).unwrap();
159        if address
160            .as_shelley()
161            .is_some_and(|shelley| shelley.network_id() != $crate::NetworkId::TESTNET)
162        {
163            panic!("network mismatch for address {}", $text);
164        }
165        address
166    }};
167
168    ($payment:expr $(,)?) => {
169        $crate::Address::new($crate::NetworkId::TESTNET, $payment)
170    };
171
172    ($payment:expr, $delegation: expr $(,)?) => {
173        $crate::Address::new($crate::NetworkId::TESTNET, $payment).with_delegation($delegation)
174    };
175}
176
177/// Construct a script [`Credential`](crate::Credential) from base16-encoded text strings.
178///
179/// See also:
180///
181/// - [`Credential::from_script`](crate::Credential::from_script)
182///
183/// # examples
184///
185/// ```rust
186/// # use cardano_sdk::{script_credential};
187/// assert!(
188///     script_credential!("00000000000000000000000000000000000000000000000000000000")
189///         .as_key()
190///         .is_none()
191/// )
192/// ```
193///
194/// ```rust
195/// # use cardano_sdk::{script_credential};
196/// assert!(
197///     script_credential!("00000000000000000000000000000000000000000000000000000000")
198///         .as_script()
199///         .is_some()
200/// )
201/// ```
202#[macro_export]
203macro_rules! script_credential {
204    ($hash:literal $(,)?) => {
205        $crate::Credential::from_script(<$crate::Hash<28>>::try_from($hash).unwrap())
206    };
207}
208
209/// Construct a key [`Credential`](crate::Credential) from base16-encoded text strings.
210///
211/// See also:
212///
213/// - [`Credential::from_key`](crate::Credential::from_key)
214///
215/// # examples
216///
217/// ```rust
218/// # use cardano_sdk::{key_credential};
219/// assert!(
220///     key_credential!("00000000000000000000000000000000000000000000000000000000")
221///         .as_key()
222///         .is_some()
223/// )
224/// ```
225///
226/// ```rust
227/// # use cardano_sdk::{key_credential};
228/// assert!(
229///     key_credential!("00000000000000000000000000000000000000000000000000000000")
230///         .as_script()
231///         .is_none()
232/// )
233/// ```
234#[macro_export]
235macro_rules! key_credential {
236    ($hash:literal $(,)?) => {
237        $crate::Credential::from_key(<$crate::Hash<28>>::try_from($hash).unwrap())
238    };
239}
240
241/// Construct an [`Output`](crate::Output) from an [`Address`](crate::Address) and an optional [`Value`](crate::Value).
242///
243/// See also:
244///
245/// - [`address!`](crate::address!)/[`address_test!`](crate::address_test!)
246/// - [`value!`](crate::value!)
247/// - [`Output::new`](crate::Output::new)
248/// - [`Output::to`](crate::Output::to)
249///
250/// # examples
251///
252/// ```rust
253/// # use cardano_sdk::{address, output};
254/// # use indoc::indoc;
255/// assert_eq!(
256///   output!("addr1v83gkkw3nqzakg5xynlurqcfqhgd65vkfvf5xv8tx25ufds2yvy2h").to_string(),
257///   indoc!{"
258///     Output {
259///         address: addr1v83gkkw3nqzakg5xynlurqcfqhgd65vkfvf5xv8tx25ufds2yvy2h,
260///         value: Value {
261///             lovelace: 857690,
262///         },
263///     }"
264///   },
265/// );
266/// ```
267///
268/// ```rust
269/// # use cardano_sdk::{address, output, value};
270/// # use indoc::indoc;
271/// assert_eq!(
272///   output!(
273///     "addr1v83gkkw3nqzakg5xynlurqcfqhgd65vkfvf5xv8tx25ufds2yvy2h",
274///     value!(
275///         123456789,
276///         (
277///             "279c909f348e533da5808898f87f9a14bb2c3dfbbacccd631d927a3f",
278///             "534e454b",
279///             1,
280///         ),
281///     ),
282///   ).to_string(),
283///   indoc!{"
284///     Output {
285///         address: addr1v83gkkw3nqzakg5xynlurqcfqhgd65vkfvf5xv8tx25ufds2yvy2h,
286///         value: Value {
287///             lovelace: 123456789,
288///             assets: {
289///                 279c909f348e533da5808898f87f9a14bb2c3dfbbacccd631d927a3f: {
290///                     SNEK: 1,
291///                 },
292///             },
293///         },
294///     }"
295///   },
296/// );
297/// ```
298#[macro_export]
299macro_rules! output {
300    ($addr:literal $(,)?) => {
301        $crate::Output::to($crate::Address::try_from($addr).unwrap())
302    };
303
304    ($addr:literal, $value:expr $(,)?) => {
305        $crate::Output::new($crate::Address::try_from($addr).unwrap(), $value)
306    };
307}
308
309/// Construct a [`PlutusScript`](crate::PlutusScript) from a [`PlutusVersion`](crate::PlutusVersion) and a base16-encoded flat-serialised program.
310///
311/// See also:
312///
313/// - [`PlutusScript::new`](crate::PlutusScript::new)
314///
315/// # example
316///
317/// ```rust
318/// # use cardano_sdk::{PlutusVersion, plutus_script};
319/// let always_succeed = plutus_script!(PlutusVersion::V3, "5101010023259800a518a4d136564004ae69");
320/// ```
321#[macro_export]
322macro_rules! plutus_script {
323    ($lang:expr, $bytes:literal $(,)?) => {
324        $crate::PlutusScript::new($lang, hex::decode($bytes).unwrap())
325    };
326}
327
328/// Construct a [`PlutusData`](crate::PlutusData) from a serialised CBOR hex-encoded string.
329///
330/// See also:
331///
332/// - [`PlutusData::integer`](crate::PlutusData::integer)
333/// - [`PlutusData::bytes`](crate::PlutusData::bytes)
334/// - [`PlutusData::list`](crate::PlutusData::list)
335/// - [`PlutusData::map`](crate::PlutusData::map)
336/// - [`PlutusData::constr`](crate::PlutusData::constr)
337#[macro_export]
338macro_rules! plutus_data {
339    ($bytes:literal $(,)?) => {
340        $crate::cbor::decode::<$crate::PlutusData>(hex::decode($bytes).unwrap().as_slice()).unwrap()
341    };
342}
343
344/// Construct a [`Value<u64>`](crate::Value) from a lovelace amount and a list of assets.
345///
346/// The macro is variadic. In its first form, it only accepts a lovelace quantity. In its second
347/// second form, it accepts one or more asset as a triple.
348///
349/// The script hash and the asset name are expectd to be plain base16-encoded literals.
350///
351/// See also:
352///
353/// - [`assets!`](crate::assets) if you only want to create assets, without lovelace;
354/// - [`Value::new`](crate::Value::new)
355/// - [`Value::with_assets`](crate::Value::with_assets)
356///
357/// # examples
358///
359/// ```rust
360/// # use cardano_sdk::{Value, hash, value};
361/// assert_eq!(value!(123_456_789), Value::<u64>::new(123456789));
362/// ```
363///
364/// ```rust
365/// # use cardano_sdk::{Value, hash, value};
366/// assert_eq!(
367///     value!(
368///         123_456_789,
369///         (
370///             "279c909f348e533da5808898f87f9a14bb2c3dfbbacccd631d927a3f",
371///             "534e454b",
372///             1_000_000,
373///         ),
374///     ),
375///     Value::new(123456789)
376///         .with_assets([
377///             (
378///                 hash!("279c909f348e533da5808898f87f9a14bb2c3dfbbacccd631d927a3f"),
379///                 [( b"SNEK", 1_000_000)]
380///             ),
381///         ]),
382/// );
383/// ```
384///
385/// ```rust
386/// # use cardano_sdk::{Value, hash, value};
387/// assert_eq!(
388///     value!(
389///         0,
390///         (
391///             "b558ea5ecfa2a6e9701dab150248e94104402f789c090426eb60eb60",
392///             "536e656b6b696530393033",
393///             1,
394///         ),
395///         (
396///             "b558ea5ecfa2a6e9701dab150248e94104402f789c090426eb60eb60",
397///             "536e656b6b696533353536",
398///             1,
399///         ),
400///         (
401///             "a0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235",
402///             "484f534b59",
403///             42_000_000,
404///         )
405///     ),
406///     Value::default()
407///         .with_assets([
408///             (
409///                 hash!("b558ea5ecfa2a6e9701dab150248e94104402f789c090426eb60eb60"),
410///                 vec![( Vec::from(b"Snekkie0903"), 1), ( Vec::from(b"Snekkie3556"), 1)],
411///             ),
412///             (
413///                 hash!("a0028f350aaabe0545fdcb56b039bfb08e4bb4d8c4d7c3c7d481c235"),
414///                 vec![( Vec::from(b"HOSKY"), 42_000_000)],
415///             ),
416///         ]),
417/// );
418/// ```
419#[macro_export]
420macro_rules! value {
421    ($lovelace:expr $(,)?) => {
422        $crate::Value::new($lovelace)
423    };
424
425    ($lovelace:expr, $( ($script_hash:literal, $asset_name:literal, $amount:expr $(,)?) ),+ $(,)? ) => {{
426        $crate::Value::new($lovelace)
427            .with_assets(vec![
428                $(
429                    (
430                        <$crate::Hash<28>>::try_from($script_hash).unwrap(),
431                        vec![ (hex::decode($asset_name).unwrap(), $amount) ],
432                    )
433                ),+
434            ])
435    }};
436}
437
438/// Construct a multi-asset object; akin to a [`Value<u64>`](crate::Value) but without lovelace.
439///
440/// The second variant of this macro takes an extra [`PlutusData`](crate::PlutusData) redeemer.
441/// This is handy to create mint assets, which always come with a redeemer.
442///
443/// See also:
444///
445/// - [`value!`](crate::value)
446/// - [`Transaction::with_mint`](crate::Transaction::with_mint)
447#[macro_export]
448macro_rules! assets {
449    ($( ($script_hash:literal, $asset_name:literal, $amount:expr $(,)?) ),+ $(,)? ) => {{
450        std::collections::BTreeMap::from([
451            $(
452                (
453                    <$crate::Hash<28>>::try_from($script_hash).unwrap(),
454                    std::collections::BTreeMap::from([ (hex::decode($asset_name).unwrap(), $amount) ]),
455                )
456            ),+
457        ])
458    }};
459
460    ($( ($script_hash:literal, $asset_name:literal, $amount:expr, $redeemer:expr $(,)?) ),+ $(,)? ) => {{
461        std::collections::BTreeMap::from([
462            $(
463                (
464                    (<$crate::Hash<28>>::try_from($script_hash).unwrap(), $redeemer),
465                    std::collections::BTreeMap::from([ (hex::decode($asset_name).unwrap(), $amount) ]),
466                )
467            ),+
468        ])
469    }};
470}