use std::marker::PhantomData;
use async_trait::async_trait;
use multiversx_sc::codec::TopDecodeMulti;
use num_bigint::BigUint;
use novax_data::{NativeConvertible, parse_query_return_string_data};
use crate::{BlockchainProxy, ExecutorError, NetworkQueryError, QueryExecutor, TokenTransfer, VmValuesQueryRequest};
use crate::network::query::proxy::NetworkBlockchainProxy;
use crate::utils::transaction::normalization::NormalizationInOut;
pub type ProxyQueryExecutor = QueryNetworkExecutor<NetworkBlockchainProxy>;
#[derive(Clone, Debug)]
pub struct QueryNetworkExecutor<Proxy: BlockchainProxy> {
pub gateway_url: String,
_data: PhantomData<Proxy>
}
impl<Proxy: BlockchainProxy> QueryNetworkExecutor<Proxy> {
pub fn new(gateway_url: String) -> Self {
QueryNetworkExecutor {
gateway_url,
_data: PhantomData
}
}
}
#[async_trait]
impl<Proxy: BlockchainProxy> QueryExecutor for QueryNetworkExecutor<Proxy> {
async fn execute<OutputManaged>(
&self,
to: &novax_data::Address,
function: String,
arguments: Vec<Vec<u8>>,
egld_value: BigUint,
esdt_transfers: Vec<TokenTransfer>
) -> Result<OutputManaged::Native, ExecutorError>
where
OutputManaged: TopDecodeMulti + NativeConvertible + Send + Sync
{
let sc_address = to.to_bech32_string()?;
let normalized = NormalizationInOut {
sender: sc_address.clone(),
receiver: sc_address,
function_name: Some(function),
arguments,
egld_value,
esdt_transfers,
}.normalize()?;
let vm_request = VmValuesQueryRequest {
sc_address: normalized.receiver,
func_name: normalized.function_name.unwrap_or_default(),
caller: Some(normalized.sender),
value: Some(normalized.egld_value.to_string()),
args: encode_arguments(&normalized.arguments),
};
let blockchain = Proxy::new(self.gateway_url.clone());
let result = blockchain.execute_vmquery(&vm_request).await?;
let Some(return_data) = result.data.return_data else {
return Err(NetworkQueryError::ErrorInResponse { message: result.data.return_message }.into())
};
let data: Vec<&str> = return_data.iter().map(AsRef::as_ref).collect();
Ok(parse_query_return_string_data::<OutputManaged>(data.as_slice())?.to_native())
}
}
#[async_trait]
impl QueryExecutor for &str {
async fn execute<OutputManaged>(
&self,
to: &novax_data::Address,
function: String,
arguments: Vec<Vec<u8>>,
egld_value: BigUint,
esdt_transfers: Vec<TokenTransfer>
) -> Result<OutputManaged::Native, ExecutorError>
where
OutputManaged: TopDecodeMulti + NativeConvertible + Send + Sync
{
self.to_string()
.execute::<OutputManaged>(
to,
function,
arguments,
egld_value,
esdt_transfers
)
.await
}
}
#[async_trait]
impl QueryExecutor for String {
async fn execute<OutputManaged>(
&self,
to: &novax_data::Address,
function: String,
arguments: Vec<Vec<u8>>,
egld_value: BigUint,
esdt_transfers: Vec<TokenTransfer>
) -> Result<OutputManaged::Native, ExecutorError>
where
OutputManaged: TopDecodeMulti + NativeConvertible + Send + Sync
{
ProxyQueryExecutor::new(self.to_string())
.execute::<OutputManaged>(
to,
function,
arguments,
egld_value,
esdt_transfers
)
.await
}
}
fn encode_arguments(arguments: &[Vec<u8>]) -> Vec<String> {
arguments.iter()
.map(hex::encode)
.collect()
}
#[cfg(test)]
mod tests {
use multiversx_sc::codec::TopEncode;
use multiversx_sc::imports::ManagedVec;
use multiversx_sc::types::ManagedBuffer;
use multiversx_sc_scenario::imports::StaticApi;
use crate::network::query::executor::encode_arguments;
#[test]
fn test_encode_arguments_empty() {
let result = encode_arguments(&[]);
let expected: Vec<String> = vec![];
assert_eq!(result, expected);
}
#[test]
fn test_encode_one_type() {
let vec: ManagedVec<StaticApi, ManagedBuffer<StaticApi>> = ManagedVec::from_single_item(ManagedBuffer::from("Hey!"));
let mut arguments: Vec<Vec<u8>> = vec![];
for item in vec.into_iter() {
let mut encoded_buffer: ManagedBuffer<StaticApi> = ManagedBuffer::new();
_ = item.top_encode(&mut encoded_buffer);
arguments.push(encoded_buffer.to_boxed_bytes().into_vec());
}
let result = encode_arguments(&arguments);
let expected = vec!["48657921".to_string()];
assert_eq!(result, expected)
}
#[test]
fn test_encode_two_type() {
let mut vec: ManagedVec<StaticApi, ManagedBuffer<StaticApi>> = ManagedVec::new();
vec.push(ManagedBuffer::from("Hey!"));
vec.push(ManagedBuffer::from("Hi!"));
let mut arguments: Vec<Vec<u8>> = vec![];
for item in vec.into_iter() {
let mut encoded_buffer: ManagedBuffer<StaticApi> = ManagedBuffer::new();
_ = item.top_encode(&mut encoded_buffer);
arguments.push(encoded_buffer.to_boxed_bytes().into_vec());
}
let result = encode_arguments(&arguments);
let expected = vec!["48657921".to_string(), "486921".to_string()];
assert_eq!(result, expected)
}
}