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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
use async_trait::async_trait;
use multiversx_sc::codec::TopDecodeMulti;
use multiversx_sc::imports::CodeMetadata;
use num_bigint::BigUint;

use novax_data::{Address, NativeConvertible};

use crate::base::deploy::DeployExecutor;
use crate::base::transaction::TransactionExecutor;
use crate::call_result::CallResult;
use crate::error::dummy::DummyExecutorError;
use crate::error::executor::ExecutorError;
use crate::utils::transaction::data::{SendableTransaction, SendableTransactionConvertible};
use crate::utils::transaction::deploy::get_deploy_call_input;
use crate::utils::transaction::normalization::NormalizationInOut;
use crate::utils::transaction::token_transfer::TokenTransfer;

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

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

/// 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: Option<Tx>,
    /// Optionally holds the caller address.
    pub caller: Option<Address>
}

impl<Tx: SendableTransactionConvertible + Clone> DummyExecutor<Tx> {
    /// Retrieves the transaction details encapsulated into a `SendableTransaction`.
    pub fn get_transaction_details(&self) -> Result<SendableTransaction, ExecutorError> {
        if let Some(tx) = self.tx.clone() {
            Ok(tx.to_sendable_transaction())
        } else {
            Err(DummyExecutorError::NoTransactionSent.into())
        }
    }
}

impl<Tx: SendableTransactionConvertible> 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: None,
            caller: caller.clone()
        }
    }
}

#[async_trait]
impl TransactionExecutor for DummyExecutor<SendableTransaction> {
    /// Captures the smart contract call details.
    async fn sc_call<OutputManaged>(
        &mut self,
        to: &Address,
        function: String,
        arguments: Vec<Vec<u8>>,
        gas_limit: u64,
        egld_value: BigUint,
        esdt_transfers: Vec<TokenTransfer>
    ) -> Result<CallResult<OutputManaged::Native>, ExecutorError>
        where
            OutputManaged: TopDecodeMulti + NativeConvertible + Send + Sync
    {
        let function_name = if function.is_empty() {
            None
        } else {
            Some(function)
        };

        let to = to.to_bech32_string()?;

        let normalized = NormalizationInOut {
            sender: self.caller.clone().map(|address| address.to_bech32_string()).unwrap_or_else(|| Ok(to.clone()))?,
            receiver: to,
            function_name,
            arguments,
            egld_value,
            esdt_transfers,
        }.normalize()?;

        self.tx = Some(normalized.into_sendable_transaction(gas_limit));

        let dummy_result = CallResult {
            response: Default::default(),
            result: None,
        };

        Ok(dummy_result)
    }
}

#[async_trait]
impl DeployExecutor for DummyExecutor<SendableTransaction> {
    /// Captures the smart contract deployment details.
    async fn sc_deploy<
        OutputManaged
    >(
        &mut self,
        bytes: Vec<u8>,
        code_metadata: CodeMetadata,
        egld_value: BigUint,
        arguments: Vec<Vec<u8>>,
        gas_limit: u64
    ) -> Result<(Address, CallResult<OutputManaged::Native>), ExecutorError>
        where
            OutputManaged: TopDecodeMulti + NativeConvertible + Send + Sync
    {
        let deploy_call_input = get_deploy_call_input(
            bytes,
            code_metadata,
            egld_value,
            arguments,
            gas_limit
        );

        let deploy_result = self.sc_call::<OutputManaged>(
            &deploy_call_input.to,
            deploy_call_input.function,
            deploy_call_input.arguments,
            deploy_call_input.gas_limit,
            deploy_call_input.egld_value,
            deploy_call_input.esdt_transfers
        )
            .await?;

        Ok((Address::default(), deploy_result))
    }
}