whisky 1.0.25

The Cardano Rust SDK, inspired by MeshJS
Documentation
use crate::*;
use async_trait::async_trait;
use uplc::tx::SlotConfig;
use whisky_common::Evaluator;

use super::TxBuilder;

pub trait TxEvaluation {
    fn update_redeemer(&mut self, tx_evaluation: Vec<Action>) -> &mut Self;
}

impl TxEvaluation for TxBuilder {
    fn update_redeemer(&mut self, tx_evaluation: Vec<Action>) -> &mut Self {
        let multiplier = self.serializer.tx_evaluation_multiplier_percentage();
        for redeemer_evaluation in tx_evaluation {
            match redeemer_evaluation.tag {
                RedeemerTag::Spend => {
                    let input =
                        &mut self.tx_builder_body.inputs[redeemer_evaluation.index as usize];
                    if let TxIn::ScriptTxIn(ScriptTxIn { script_tx_in, .. }) = input {
                        let redeemer: &mut Redeemer = script_tx_in.redeemer.as_mut().unwrap();
                        redeemer.ex_units.mem = redeemer_evaluation.budget.mem * multiplier / 100;
                        redeemer.ex_units.steps =
                            redeemer_evaluation.budget.steps * multiplier / 100;
                    }
                }
                RedeemerTag::Mint => {
                    let mint_item =
                        &mut self.tx_builder_body.mints[redeemer_evaluation.index as usize];
                    if let MintItem::ScriptMint(mint) = mint_item {
                        let redeemer: &mut Redeemer = mint.redeemer.as_mut().unwrap();
                        redeemer.ex_units.mem = redeemer_evaluation.budget.mem * multiplier / 100;
                        redeemer.ex_units.steps =
                            redeemer_evaluation.budget.steps * multiplier / 100;
                    }
                }
                RedeemerTag::Cert => {
                    let cert_item =
                        &mut self.tx_builder_body.certificates[redeemer_evaluation.index as usize];
                    if let Certificate::ScriptCertificate(cert) = cert_item {
                        let redeemer: &mut Redeemer = cert.redeemer.as_mut().unwrap();
                        redeemer.ex_units.mem = redeemer_evaluation.budget.mem * multiplier / 100;
                        redeemer.ex_units.steps =
                            redeemer_evaluation.budget.steps * multiplier / 100;
                    }
                }
                RedeemerTag::Reward => {
                    let withdrawal_item =
                        &mut self.tx_builder_body.withdrawals[redeemer_evaluation.index as usize];
                    if let Withdrawal::PlutusScriptWithdrawal(withdrawal) = withdrawal_item {
                        let redeemer: &mut Redeemer = withdrawal.redeemer.as_mut().unwrap();
                        redeemer.ex_units.mem = redeemer_evaluation.budget.mem * multiplier / 100;
                        redeemer.ex_units.steps =
                            redeemer_evaluation.budget.steps * multiplier / 100;
                    }
                }
                RedeemerTag::Propose => todo!(),
                RedeemerTag::Vote => todo!(),
            }
        }
        self
    }
}

#[derive(Clone, Debug)]
pub struct OfflineTxEvaluator {}

impl OfflineTxEvaluator {
    pub fn new() -> Self {
        OfflineTxEvaluator {}
    }
}

impl Default for OfflineTxEvaluator {
    fn default() -> Self {
        OfflineTxEvaluator::new()
    }
}

impl OfflineTxEvaluator {
    fn evaluate_tx_sync(
        &self,
        tx_hex: &str,
        inputs: &[UTxO],
        additional_txs: &[String],
        network: &Network,
        slot_config: &SlotConfig,
    ) -> Result<Vec<Action>, WError> {
        consolidate_errors(evaluate_tx_scripts(
            tx_hex,
            inputs,
            additional_txs,
            network,
            slot_config,
        )?)
    }
}

fn consolidate_errors(eval_results: Vec<EvalResult>) -> Result<Vec<Action>, WError> {
    let mut actions = Vec::new();
    let mut errors_texts = Vec::new();
    for eval_result in eval_results {
        match eval_result {
            EvalResult::Success(action) => actions.push(action),
            EvalResult::Error(error) => {
                errors_texts.push(format!("Error at index: [ {} ] - Budget: [ {:?} ] - Tag: [ {:?} ] - Error message: [ {} ] - Logs: [ {:?} ]",
                                          error.index, error.budget, error.tag, error.error_message, error.logs));
            }
        }
    }
    if errors_texts.is_empty() {
        Ok(actions)
    } else {
        Err(WError::new(
            "consolidate_errors",
            &format!("Errors found during evaluation: [ {:?} ]", errors_texts),
        ))
    }
}

#[async_trait]
impl Evaluator for OfflineTxEvaluator {
    async fn evaluate_tx(
        &self,
        tx_hex: &str,
        inputs: &[UTxO],
        additional_txs: &[String],
        network: &Network,
        slot_config: &SlotConfig,
    ) -> Result<Vec<Action>, WError> {
        self.evaluate_tx_sync(tx_hex, inputs, additional_txs, network, slot_config)
    }
}