use cml_core::ArithmeticError;
use crate::{
address::Address,
assets::{Coin, MultiAsset, Value},
crypto::hash::hash_plutus_data,
min_ada::min_ada_required,
plutus::PlutusData,
transaction::{DatumOption, ScriptRef, TransactionOutput},
};
#[derive(Debug, thiserror::Error)]
pub enum OutputBuilderError {
#[error("Address missing")]
AddressMissing,
#[error("Value missing")]
AmountMissing,
#[error("Min ADA error: {0:?}")]
MinAdaError(#[from] ArithmeticError),
}
#[derive(Clone, Debug, Default)]
pub struct TransactionOutputBuilder {
pub address: Option<Address>,
pub datum: Option<DatumOption>,
pub communication_datum: Option<PlutusData>,
pub script_ref: Option<ScriptRef>,
}
impl TransactionOutputBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn with_address(mut self, address: Address) -> Self {
self.address = Some(address);
self
}
pub fn with_communication_data(mut self, datum: PlutusData) -> Self {
self.datum = Some(DatumOption::new_hash(hash_plutus_data(&datum)));
self.communication_datum = Some(datum);
self
}
pub fn with_data(mut self, datum: DatumOption) -> Self {
self.datum = Some(datum);
self.communication_datum = None;
self
}
pub fn with_reference_script(mut self, script_ref: ScriptRef) -> Self {
self.script_ref = Some(script_ref);
self
}
pub fn next(self) -> Result<TransactionOutputAmountBuilder, OutputBuilderError> {
Ok(TransactionOutputAmountBuilder {
address: self.address.ok_or(OutputBuilderError::AddressMissing)?,
amount: None,
datum: self.datum,
script_ref: self.script_ref,
communication_datum: self.communication_datum,
})
}
}
#[derive(Clone, Debug)]
pub struct TransactionOutputAmountBuilder {
address: Address,
amount: Option<Value>,
datum: Option<DatumOption>,
script_ref: Option<ScriptRef>,
communication_datum: Option<PlutusData>,
}
impl TransactionOutputAmountBuilder {
pub fn with_value<T: Into<Value>>(mut self, amount: T) -> Self {
self.amount = Some(amount.into());
self
}
pub fn with_asset_and_min_required_coin(
self,
multiasset: MultiAsset,
coins_per_utxo_byte: Coin,
) -> Result<Self, OutputBuilderError> {
let mut min_output = TransactionOutput::new(
self.address.clone(),
self.amount.clone().unwrap_or_else(|| Value::from(0)),
self.datum.clone(),
self.script_ref.clone(),
);
let min_possible_coin = min_ada_required(&min_output, coins_per_utxo_byte)?;
let check_output = &mut min_output;
check_output.set_amount(Value::new(min_possible_coin, multiasset.clone()));
let required_coin = min_ada_required(check_output, coins_per_utxo_byte)?;
Ok(self.with_value(Value::new(required_coin, multiasset)))
}
pub fn build(self) -> Result<SingleOutputBuilderResult, OutputBuilderError> {
let output = TransactionOutput::new(
self.address,
self.amount.ok_or(OutputBuilderError::AmountMissing)?,
self.datum,
self.script_ref,
);
Ok(SingleOutputBuilderResult {
output,
communication_datum: self.communication_datum,
})
}
}
#[derive(Clone, Debug)]
pub struct SingleOutputBuilderResult {
pub output: TransactionOutput,
pub communication_datum: Option<PlutusData>,
}
impl SingleOutputBuilderResult {
pub fn new(output: TransactionOutput) -> SingleOutputBuilderResult {
Self {
output,
communication_datum: None,
}
}
}