multiversx-chain-vm 0.1.4

MultiversX VM implementation and tooling
Documentation
use crate::{
    tx_execution::BuiltinFunctionMap,
    tx_mock::{TxInput, TxResult},
};
use multiversx_sc::{
    codec::*,
    types::heap::{Address, H256},
};

use crate::num_bigint::BigUint;

use alloc::vec::Vec;

use super::{CallbackPayments, Promise, TxFunctionName};

#[derive(Debug, Clone)]
pub struct AsyncCallTxData {
    pub from: Address,
    pub to: Address,
    pub call_value: BigUint,
    pub endpoint_name: TxFunctionName,
    pub arguments: Vec<Vec<u8>>,
    pub tx_hash: H256,
}

pub fn async_call_tx_input(async_call: &AsyncCallTxData) -> TxInput {
    TxInput {
        from: async_call.from.clone(),
        to: async_call.to.clone(),
        egld_value: async_call.call_value.clone(),
        esdt_values: Vec::new(),
        func_name: async_call.endpoint_name.clone(),
        args: async_call.arguments.clone(),
        gas_limit: 1000,
        gas_price: 0,
        tx_hash: async_call.tx_hash.clone(),
        ..Default::default()
    }
}

fn result_status_bytes(result_status: u64) -> Vec<u8> {
    if result_status == 0 {
        vec![0x00]
    } else {
        top_encode_to_vec_u8(&result_status).unwrap()
    }
}

pub fn async_callback_tx_input(
    async_data: &AsyncCallTxData,
    async_result: &TxResult,
    builtin_functions: &BuiltinFunctionMap,
) -> TxInput {
    let mut args: Vec<Vec<u8>> = vec![result_status_bytes(async_result.result_status)];
    if async_result.result_status == 0 {
        args.extend_from_slice(async_result.result_values.as_slice());
    } else {
        args.push(async_result.result_message.clone().into_bytes());
    }
    let callback_payments =
        extract_callback_payments(&async_data.from, async_result, builtin_functions);
    TxInput {
        from: async_data.to.clone(),
        to: async_data.from.clone(),
        egld_value: 0u32.into(),
        esdt_values: Vec::new(),
        func_name: TxFunctionName::CALLBACK,
        args,
        gas_limit: 1000,
        gas_price: 0,
        tx_hash: async_data.tx_hash.clone(),
        callback_payments,
        ..Default::default()
    }
}

fn extract_callback_payments(
    callback_contract_address: &Address,
    async_result: &TxResult,
    builtin_functions: &BuiltinFunctionMap,
) -> CallbackPayments {
    let mut callback_payments = CallbackPayments::default();
    for async_call in &async_result.all_calls {
        let tx_input = async_call_tx_input(async_call);
        let token_transfers = builtin_functions.extract_token_transfers(&tx_input);
        if &token_transfers.real_recipient == callback_contract_address {
            if !token_transfers.is_empty() {
                callback_payments.esdt_values = token_transfers.transfers;
            } else {
                callback_payments.egld_value = async_call.call_value.clone();
            }
            break;
        }
    }
    callback_payments
}

pub fn async_promise_tx_input(
    address: &Address,
    promise: &Promise,
    async_result: &TxResult,
) -> TxInput {
    let mut args: Vec<Vec<u8>> = Vec::new();
    let serialized_bytes = top_encode_to_vec_u8(&async_result.result_status).unwrap();
    args.push(serialized_bytes);
    let callback_name = if async_result.result_status == 0 {
        args.extend_from_slice(async_result.result_values.as_slice());
        promise.success_callback.clone()
    } else {
        args.push(async_result.result_message.clone().into_bytes());
        promise.error_callback.clone()
    };

    TxInput {
        from: promise.call.from.clone(),
        to: address.clone(),
        egld_value: 0u32.into(),
        esdt_values: Vec::new(),
        func_name: callback_name,
        args,
        gas_limit: 1000,
        gas_price: 0,
        tx_hash: promise.call.tx_hash.clone(),
        promise_callback_closure_data: promise.callback_closure_data.clone(),
        ..Default::default()
    }
}

pub fn merge_results(mut original: TxResult, mut new: TxResult) -> TxResult {
    if original.result_status == 0 {
        original.result_values.append(&mut new.result_values);
        original.result_logs.append(&mut new.result_logs);
        original.result_message = new.result_message;
        original
    } else {
        new
    }
}