use num_traits::Zero;
use crate::{
tx_execution::execute_system_sc,
tx_mock::{
BlockchainUpdate, TxCache, TxContext, TxContextStack, TxFunctionName, TxInput, TxLog,
TxResult,
},
types::VMAddress,
with_shared::Shareable,
};
use super::{is_system_sc_address, BlockchainVMRef};
fn should_execute_sc_call(tx_input: &TxInput) -> bool {
if tx_input.func_name == TxFunctionName::WHITEBOX_CALL {
return true;
}
if !tx_input.to.is_smart_contract_address() {
return false;
}
!tx_input.func_name.is_empty()
}
impl BlockchainVMRef {
pub fn default_execution<F>(
&self,
tx_input: TxInput,
tx_cache: TxCache,
f: F,
) -> (TxResult, BlockchainUpdate)
where
F: FnOnce(),
{
if let Err(err) =
tx_cache.transfer_egld_balance(&tx_input.from, &tx_input.to, &tx_input.egld_value)
{
return (TxResult::from_panic_obj(&err), BlockchainUpdate::empty());
}
let add_transfer_log =
tx_input.from.is_smart_contract_address() && !tx_input.egld_value.is_zero();
let transfer_value_log = if add_transfer_log {
Some(TxLog {
address: VMAddress::zero(), endpoint: "transferValueOnly".into(),
topics: vec![
tx_input.from.to_vec(),
tx_input.to.to_vec(),
tx_input.egld_value.to_bytes_be(),
],
data: Vec::new(),
})
} else {
None
};
for esdt_transfer in tx_input.esdt_values.iter() {
let transfer_result = tx_cache.transfer_esdt_balance(
&tx_input.from,
&tx_input.to,
&esdt_transfer.token_identifier,
esdt_transfer.nonce,
&esdt_transfer.value,
);
if let Err(err) = transfer_result {
return (TxResult::from_panic_obj(&err), BlockchainUpdate::empty());
}
}
let (mut tx_result, blockchain_updates) = if is_system_sc_address(&tx_input.to) {
execute_system_sc(tx_input, tx_cache)
} else if should_execute_sc_call(&tx_input) {
let tx_context = TxContext::new(self.clone(), tx_input, tx_cache);
let mut tx_context_sh = Shareable::new(tx_context);
TxContextStack::execute_on_vm_stack(&mut tx_context_sh, f);
tx_context_sh.into_inner().into_results()
} else {
(TxResult::empty(), tx_cache.into_blockchain_updates())
};
if let Some(tv_log) = transfer_value_log {
tx_result.result_logs.insert(0, tv_log);
}
(tx_result, blockchain_updates)
}
pub fn deploy_contract<F>(
&self,
mut tx_input: TxInput,
contract_path: Vec<u8>,
tx_cache: TxCache,
f: F,
) -> (TxResult, VMAddress, BlockchainUpdate)
where
F: FnOnce(),
{
let new_address = tx_cache.get_new_address(&tx_input.from);
tx_input.to = new_address.clone();
tx_input.func_name = TxFunctionName::INIT;
let tx_context = TxContext::new(self.clone(), tx_input, tx_cache);
let mut tx_context_sh = Shareable::new(tx_context);
let tx_input_ref = tx_context_sh.input_ref();
if let Err(err) = tx_context_sh
.tx_cache
.subtract_egld_balance(&tx_input_ref.from, &tx_input_ref.egld_value)
{
return (
TxResult::from_panic_obj(&err),
VMAddress::zero(),
BlockchainUpdate::empty(),
);
}
tx_context_sh.create_new_contract(&new_address, contract_path, tx_input_ref.from.clone());
tx_context_sh
.tx_cache
.increase_egld_balance(&new_address, &tx_input_ref.egld_value);
TxContextStack::execute_on_vm_stack(&mut tx_context_sh, f);
let (tx_result, blockchain_updates) = tx_context_sh.into_inner().into_results();
(tx_result, new_address, blockchain_updates)
}
}