use multiversx_chain_core::types::ReturnCode;
use multiversx_chain_vm_executor::{BreakpointValue, InstanceCallResult, VMHooksEarlyExit};
use crate::{
host::{
context::{GasUsed, TxFunctionName, TxResult},
vm_hooks::vh_early_exit::ASYNC_CALL_EARLY_EXIT_CODE,
},
vm_err_msg,
};
use super::{RuntimeInstanceCall, RuntimeInstanceCallLambda};
pub struct RuntimeInstanceCallLambdaDefault;
impl RuntimeInstanceCallLambda for RuntimeInstanceCallLambdaDefault {
fn call(self, instance_call: RuntimeInstanceCall<'_>) {
default_instance_call(instance_call);
}
fn override_function_name(&self) -> Option<TxFunctionName> {
None
}
}
fn default_instance_call(instance_call: RuntimeInstanceCall<'_>) {
if !instance_call.instance.has_function(instance_call.func_name) {
*instance_call.tx_context_ref.result_lock() = TxResult::from_function_not_found();
return;
}
let result = instance_call
.instance
.call(instance_call.func_name, instance_call.gas_limit);
let mut tx_result_ref = instance_call.tx_context_ref.result_lock();
if let Some(error_tx_result) = instance_call_error_result(result) {
tx_result_ref.merge_error(error_tx_result);
}
if tx_result_ref.result_status.is_success() {
let gas_used = instance_call
.instance
.get_points_used()
.expect("error retrieving gas used");
tx_result_ref.gas_used = GasUsed::SomeGas(gas_used);
} else {
tx_result_ref.gas_used =
GasUsed::AllGas(instance_call.tx_context_ref.tx_input_box.gas_limit);
}
}
fn instance_call_error_result(call_result: InstanceCallResult) -> Option<TxResult> {
match call_result {
InstanceCallResult::Ok => None,
InstanceCallResult::FunctionNotFound => Some(TxResult::from_function_not_found()),
InstanceCallResult::RuntimeError(error) => Some(TxResult::from_vm_error(error.to_string())),
InstanceCallResult::VMHooksEarlyExit(vm_hooks_early_exit) => {
vm_hooks_early_exit_result(vm_hooks_early_exit)
}
InstanceCallResult::Breakpoint(BreakpointValue::None) => {
Some(TxResult::from_vm_error("invalid breakpoint".to_string()))
}
InstanceCallResult::Breakpoint(BreakpointValue::OutOfGas) => Some(TxResult::from_error(
ReturnCode::OutOfGas,
vm_err_msg::NOT_ENOUGH_GAS,
)),
InstanceCallResult::Breakpoint(BreakpointValue::MemoryLimit) => {
Some(TxResult::from_vm_error("memory limit".to_string()))
}
}
}
fn vm_hooks_early_exit_result(vm_hooks_early_exit: VMHooksEarlyExit) -> Option<TxResult> {
if vm_hooks_early_exit.code == ASYNC_CALL_EARLY_EXIT_CODE {
None
} else {
Some(TxResult::from_error(
ReturnCode::from_u64(vm_hooks_early_exit.code).expect("invalid return code"),
vm_hooks_early_exit.message.into_owned(),
))
}
}