use crate::transaction::nested_value_map::{add_amount_to_nested_map, nested_map_to_vecs};
use crate::{
address::{Address, PolicyId},
backend::RedemptionDetails,
error::*,
output::{Output, UnbuiltOutput},
scripts::{MintingPolicy, ValidatorCode},
values::Values,
};
use std::cell::RefCell;
use std::collections::HashMap;
pub(crate) mod nested_value_map;
pub enum Action<Datum, Redeemer> {
Transfer {
amount: u64,
recipient: Address,
policy_id: PolicyId,
},
Mint {
amount: u64,
asset_name: Option<String>,
redeemer: Redeemer,
policy: Box<dyn MintingPolicy<Redeemer>>,
recipient: Address,
},
InitScript {
datum: Datum,
values: Values,
address: Address,
},
RedeemScriptOutput {
output: Output<Datum>,
redeemer: Redeemer,
script: Box<dyn ValidatorCode<Datum, Redeemer>>, },
}
pub struct TxActions<Datum, Redeemer> {
pub actions: Vec<Action<Datum, Redeemer>>,
}
impl<Datum, Redeemer> Default for TxActions<Datum, Redeemer> {
fn default() -> Self {
TxActions {
actions: Vec::new(),
}
}
}
impl<Datum: Clone, Redeemer> TxActions<Datum, Redeemer> {
pub fn with_transfer(mut self, amount: u64, recipient: Address, policy_id: PolicyId) -> Self {
let action = Action::Transfer {
amount,
recipient,
policy_id,
};
self.actions.push(action);
self
}
pub fn with_mint(
mut self,
amount: u64,
asset_name: Option<String>,
recipient: &Address,
redeemer: Redeemer,
policy: Box<dyn MintingPolicy<Redeemer>>,
) -> Self {
let action = Action::Mint {
amount,
asset_name,
redeemer,
policy,
recipient: recipient.clone(),
};
self.actions.push(action);
self
}
pub fn with_script_init(mut self, datum: Datum, values: Values, address: Address) -> Self {
let action = Action::InitScript {
datum,
values,
address,
};
self.actions.push(action);
self
}
pub fn with_script_redeem(
mut self,
output: Output<Datum>,
redeemer: Redeemer,
script: Box<dyn ValidatorCode<Datum, Redeemer>>,
) -> Self {
let action = Action::RedeemScriptOutput {
output,
redeemer,
script,
};
self.actions.push(action);
self
}
pub fn to_unbuilt_tx(self) -> Result<UnbuiltTransaction<Datum, Redeemer>> {
let TxActions { actions } = self;
let mut min_output_values: HashMap<Address, RefCell<Values>> = HashMap::new();
let mut minting = Vec::new();
let mut script_inputs: Vec<RedemptionDetails<Datum, Redeemer>> = Vec::new();
let mut specific_outputs: Vec<UnbuiltOutput<Datum>> = Vec::new();
for action in actions {
match action {
Action::Transfer {
amount,
recipient,
policy_id: policy,
} => {
add_amount_to_nested_map(&mut min_output_values, amount, &recipient, &policy);
}
Action::Mint {
amount,
asset_name,
redeemer,
policy,
recipient,
} => {
let id = policy.id();
let policy_id = PolicyId::native_token(&id, &asset_name);
minting.push((amount, asset_name, redeemer, policy));
add_amount_to_nested_map(
&mut min_output_values,
amount,
&recipient,
&policy_id,
);
}
Action::InitScript {
datum,
values,
address,
} => {
let owner = address;
let output = UnbuiltOutput::Validator {
script_address: owner,
values,
datum,
};
specific_outputs.push(output);
}
Action::RedeemScriptOutput {
output,
redeemer,
script,
} => {
script_inputs.push((output.clone(), redeemer, script));
}
}
}
let out_vecs = nested_map_to_vecs(min_output_values);
let mut outputs = create_outputs_for(out_vecs)?;
outputs.extend(specific_outputs);
let tx = UnbuiltTransaction {
script_inputs,
unbuilt_outputs: outputs,
minting,
};
Ok(tx)
}
}
fn create_outputs_for<Datum>(
values: Vec<(Address, Vec<(PolicyId, u64)>)>,
) -> Result<Vec<UnbuiltOutput<Datum>>> {
let outputs = values
.into_iter()
.map(|(owner, val_vec)| {
let values = val_vec
.iter()
.fold(Values::default(), |mut acc, (policy, amt)| {
acc.add_one_value(policy, *amt);
acc
});
UnbuiltOutput::new_wallet(owner, values)
})
.collect();
Ok(outputs)
}
pub struct UnbuiltTransaction<Datum, Redeemer> {
pub script_inputs: Vec<RedemptionDetails<Datum, Redeemer>>,
pub unbuilt_outputs: Vec<UnbuiltOutput<Datum>>,
#[allow(clippy::type_complexity)]
pub minting: Vec<(
u64,
Option<String>,
Redeemer,
Box<dyn MintingPolicy<Redeemer>>,
)>,
}
impl<Datum, Redeemer> UnbuiltTransaction<Datum, Redeemer> {
pub fn unbuilt_outputs(&self) -> &Vec<UnbuiltOutput<Datum>> {
&self.unbuilt_outputs
}
pub fn script_inputs(&self) -> &Vec<RedemptionDetails<Datum, Redeemer>> {
&self.script_inputs
}
}
#[derive(Debug)]
pub struct TxId(String);
impl TxId {
pub fn new(id_str: &str) -> Self {
TxId(id_str.to_string())
}
pub fn as_str(&self) -> String {
self.0.clone()
}
}