use alloc::vec::Vec;
use alloy_primitives::Address;
pub(crate) use raw::CachePolicy;
pub use raw::RawCall;
use stylus_core::{
calls::{errors::Error, MutatingCallContext, StaticCallContext},
Host,
};
mod raw;
pub mod transfer;
pub fn static_call<H: Host + ?Sized>(
host: &H,
context: impl StaticCallContext,
to: Address,
data: &[u8],
) -> Result<Vec<u8>, Error> {
host.flush_cache(false); unsafe {
RawCall::new_static(host)
.gas(context.gas())
.call(to, data)
.map_err(Error::Revert)
}
}
pub unsafe fn delegate_call<H: Host + ?Sized>(
host: &H,
context: impl MutatingCallContext,
to: Address,
data: &[u8],
) -> Result<Vec<u8>, Error> {
host.flush_cache(true);
RawCall::new_delegate(host)
.gas(context.gas())
.call(to, data)
.map_err(Error::Revert)
}
pub fn call<H: Host + ?Sized>(
host: &H,
context: impl MutatingCallContext,
to: Address,
data: &[u8],
) -> Result<Vec<u8>, Error> {
host.flush_cache(true);
unsafe {
RawCall::new_with_value(host, context.value())
.gas(context.gas())
.call(to, data)
.map_err(Error::Revert)
}
}
#[cfg(test)]
mod test {
use alloy_primitives::{Address, U256};
use stylus_core::CallContext;
use stylus_test::TestVM;
use super::*;
#[derive(Clone)]
pub struct MyContract;
impl CallContext for MyContract {
fn gas(&self) -> u64 {
0
}
}
unsafe impl MutatingCallContext for MyContract {
fn value(&self) -> U256 {
U256::from(0)
}
}
impl StaticCallContext for MyContract {}
stylus_proc::sol_interface! {
interface IMultiReturn {
function mixed() external view returns (string, uint256);
}
}
#[test]
fn test_sol_interface_multi_return() {
use alloy_sol_types::SolType;
let vm = TestVM::new();
let target = Address::from([3u8; 20]);
let iface = IMultiReturn::new(target);
let ctx = MyContract {};
let return_data = <(
alloy_sol_types::sol_data::String,
alloy_sol_types::sol_data::Uint<256>,
) as SolType>::abi_encode_params(&(
alloc::string::String::from("hello"),
U256::from(42),
));
let selector = &alloy_primitives::keccak256("mixed()")[..4];
vm.mock_static_call(target, selector.to_vec(), Ok(return_data));
let (s, n) = iface.mixed(&vm, ctx).unwrap();
assert_eq!(s, "hello");
assert_eq!(n, U256::from(42));
}
#[test]
fn test_calls() {
let vm = TestVM::new();
let contract = MyContract {};
let target = Address::from([2u8; 20]);
let data = vec![1, 2, 3, 4];
let expected_return = vec![5, 6, 7, 8];
vm.mock_call(
target,
data.clone(),
U256::ZERO,
Ok(expected_return.clone()),
);
let response = call(&vm, contract.clone(), target, &data).unwrap();
assert_eq!(response, expected_return);
vm.clear_mocks();
vm.mock_delegate_call(target, data.clone(), Ok(expected_return.clone()));
let response = unsafe { delegate_call(&vm, contract.clone(), target, &data).unwrap() };
assert_eq!(response, expected_return);
vm.clear_mocks();
vm.mock_static_call(target, data.clone(), Ok(expected_return.clone()));
let response = static_call(&vm, contract.clone(), target, &data).unwrap();
assert_eq!(response, expected_return);
}
}