use neo_syscalls::NeoVMSyscall;
use neo_types::{
ContractCaller, NeoArray, NeoByteString, NeoError, NeoInteger, NeoResult, NeoString, NeoValue,
};
pub struct DefaultContractCaller;
impl ContractCaller for DefaultContractCaller {
fn call_raw(
&self,
script_hash: &NeoByteString,
method: &str,
args: &[NeoValue],
call_flags: &NeoInteger,
) -> NeoResult<NeoValue> {
let method_str = NeoString::from_str(method);
let args_array: NeoArray<NeoValue> = args.iter().cloned().collect();
NeoVMSyscall::contract_call(script_hash, &method_str, call_flags, &args_array)
}
}
pub fn call_typed<T: neo_types::FromNeoValue>(
script_hash: &NeoByteString,
method: &str,
args: &[NeoValue],
call_flags: &NeoInteger,
) -> NeoResult<T> {
DefaultContractCaller.call_typed(script_hash, method, args, call_flags)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ContractCallError {
NoReturn,
TypeMismatch(String),
Panicked(String),
Wasm32CrossCallUnavailable { syscall: &'static str },
Other(NeoError),
}
impl std::fmt::Display for ContractCallError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ContractCallError::NoReturn => write!(f, "contract call returned no value"),
ContractCallError::TypeMismatch(s) => write!(f, "type mismatch: {s}"),
ContractCallError::Panicked(s) => write!(f, "contract call panicked: {s}"),
ContractCallError::Wasm32CrossCallUnavailable { syscall } => {
write!(f, "wasm32 cross-call unavailable: {syscall}")
}
ContractCallError::Other(e) => write!(f, "{e}"),
}
}
}
impl std::error::Error for ContractCallError {}
impl From<NeoError> for ContractCallError {
fn from(e: NeoError) -> Self {
match e {
NeoError::Wasm32CrossCallUnavailable { syscall } => {
ContractCallError::Wasm32CrossCallUnavailable { syscall }
}
other => ContractCallError::Other(other),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use neo_types::{NeoByteString, NeoInteger, NeoValue};
#[test]
fn default_caller_returns_null_for_known_contract() {
let script_hash = NeoByteString::from_slice(&[1u8; 20]);
let call_flags = NeoInteger::new(0x0F);
let result = DefaultContractCaller.call_raw(&script_hash, "method", &[], &call_flags);
let _ = result;
}
#[test]
fn call_typed_returns_null_for_non_typed_target() {
let script_hash = NeoByteString::from_slice(&[1u8; 20]);
let call_flags = NeoInteger::new(0x0F);
let _result: Result<NeoValue, _> = call_typed(&script_hash, "method", &[], &call_flags);
}
}