1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
use std::mem;
use async_trait::async_trait;
use multiversx_sc::codec::TopEncodeMulti;
use multiversx_sc_scenario::scenario_model::{ScCallStep, ScDeployStep, TypedScCall, TypedScDeploy};
use novax_data::Address;
use crate::base::deploy::DeployExecutor;
use crate::base::transaction::TransactionExecutor;
use crate::error::executor::ExecutorError;
use crate::utils::transaction::data::{SendableTransaction, SendableTransactionConvertible};

/// A type alias for `DummyExecutor` handling `ScCallStep`.
pub type DummyTransactionExecutor = DummyExecutor<ScCallStep>;

/// A type alias for `DummyExecutor` handling `ScDeployStep`.
pub type DummyDeployExecutor = DummyExecutor<ScDeployStep>;

/// A structure for capturing transaction details without performing actual blockchain transactions.
/// It is designed for testing scenarios, especially to fetch `SendableTransaction` details from interactions.
pub struct DummyExecutor<Tx: SendableTransactionConvertible> {
    /// Holds the transaction details.
    pub tx: Tx,
    /// Optionally holds the caller address.
    pub caller: Option<Address>
}

impl<Tx: SendableTransactionConvertible> DummyExecutor<Tx> {
    /// Retrieves the transaction details encapsulated into a `SendableTransaction`.
    pub fn get_transaction_details(&self) -> SendableTransaction {
        self.tx.to_sendable_transaction()
    }
}

impl<Tx: SendableTransactionConvertible + Default> DummyExecutor<Tx> {
    /// Constructs a new `DummyExecutor` instance.
    ///
    /// # Arguments
    ///
    /// * `caller` - An optional reference to the caller address.
    pub fn new(caller: &Option<Address>) -> DummyExecutor<Tx> {
        DummyExecutor {
            tx: Tx::default(),
            caller: caller.clone()
        }
    }
}

#[async_trait]
impl TransactionExecutor for DummyExecutor<ScCallStep> {
    /// Captures the smart contract call details.
    async fn sc_call<OriginalResult: Send>(&mut self, sc_call_step: &mut TypedScCall<OriginalResult>) -> Result<(), ExecutorError> {
        let mut owned_sc_call_step = mem::replace(sc_call_step, ScCallStep::new().into());

        if let Some(caller) = &self.caller {
            owned_sc_call_step = owned_sc_call_step.from(&multiversx_sc::types::Address::from(caller.to_bytes()));
        }

        self.tx = owned_sc_call_step.sc_call_step;

        Ok(())
    }

    /// Indicates that deserialization should be skipped as there is no actual execution.
    async fn should_skip_deserialization(&self) -> bool {
        true
    }
}

#[async_trait]
impl DeployExecutor for DummyExecutor<ScDeployStep> {
    /// Captures the smart contract deployment details.
    async fn sc_deploy<OriginalResult>(&mut self, sc_deploy_step: &mut TypedScDeploy<OriginalResult>) -> Result<(), ExecutorError>
        where
            OriginalResult: TopEncodeMulti + Send + Sync,
    {
        let mut owned_sc_deploy_step = mem::replace(sc_deploy_step, ScDeployStep::new().into());

        if let Some(caller) = &self.caller {
            owned_sc_deploy_step = owned_sc_deploy_step.from(&multiversx_sc::types::Address::from(caller.to_bytes()));
        }

        self.tx = owned_sc_deploy_step.sc_deploy_step;

        Ok(())
    }

    /// Indicates that deserialization should be skipped as there is no actual execution.
    async fn should_skip_deserialization(&self) -> bool {
        true
    }
}