cardano_sdk/cardano/output/change_strategy.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
5use crate::{Address, Output, Value, address::kind::*};
6use anyhow::anyhow;
7use std::collections::VecDeque;
8
9/// Defines the behaviour of the transaction builder towards change outputs.
10///
11/// A _strategy_ is nothing more than a (faillible) function of the change and a mutable reference
12/// to the outputs. It can be defined explicitly using [`Self::new`]. Alternatively, we provide a
13/// handful of pre-defined common strategies:
14///
15/// - [`Self::as_first_output`]
16/// - [`Self::as_last_output`]
17pub struct ChangeStrategy(
18 #[allow(clippy::type_complexity)]
19 Box<dyn FnOnce(Value<u64>, &mut VecDeque<Output>) -> anyhow::Result<()>>,
20);
21
22// -------------------------------------------------------------------- Building
23
24impl Default for ChangeStrategy {
25 fn default() -> Self {
26 Self::new(|_change, _outputs| {
27 Err(anyhow!(
28 "no explicit change strategy defined; use 'with_change_strategy' to define one."
29 ))
30 })
31 }
32}
33
34impl ChangeStrategy {
35 /// A change strategy that creates a new output with all change sent at the given address, and
36 /// prepend it to the existing list of outputs.
37 pub fn as_first_output(change_address: Address<Any>) -> Self {
38 Self::new(move |change, outputs| {
39 outputs.push_front(Output::new(change_address, change));
40 Ok(())
41 })
42 }
43
44 /// A change strategy that creates a new output with all change sent at the given address, and
45 /// append it to the existing list of outputs.
46 pub fn as_last_output(change_address: Address<Any>) -> Self {
47 Self::new(move |change, outputs| {
48 outputs.push_back(Output::new(change_address, change));
49 Ok(())
50 })
51 }
52
53 /// Define a custom strategy manually. For example, we have:
54 ///
55 /// ```ignore
56 /// pub fn as_last_output(change_address: Address<Any>) -> Self {
57 /// Self::new(move |change, outputs| {
58 /// outputs.push_back(Output::new(change_address, change));
59 /// Ok(())
60 /// })
61 /// }
62 /// ```
63 ///
64 /// ```ignore
65 /// pub fn as_first_output(change_address: Address<Any>) -> Self {
66 /// Self::new(move |change, outputs| {
67 /// outputs.push_front(Output::new(change_address, change));
68 /// Ok(())
69 /// })
70 /// }
71 /// ```
72 pub fn new(
73 strategy: impl FnOnce(Value<u64>, &mut VecDeque<Output>) -> anyhow::Result<()> + 'static,
74 ) -> Self {
75 Self(Box::new(strategy))
76 }
77}
78
79// --------------------------------------------------------------------- Running
80
81impl ChangeStrategy {
82 /// Run the encapsulated (faillible) strategy on a mutable set of outputs. This is called
83 /// internally by the [`Transaction::build`](crate::Transaction::build) when necessary to
84 /// distribute change to the outputs according to the given strategy.
85 pub fn apply(self, change: Value<u64>, outputs: &mut VecDeque<Output>) -> anyhow::Result<()> {
86 self.0(change, outputs)
87 }
88}