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}