use alloy_primitives::{Address, B256, U256};
use stylus_core::Host;
use crate::ArbResult;
#[derive(Clone)]
#[must_use]
pub struct RawCall<'a, H: Host + ?Sized> {
kind: CallKind,
callvalue: U256,
gas: Option<u64>,
offset: usize,
size: Option<usize>,
#[allow(unused)]
cache_policy: CachePolicy,
host: &'a H,
}
#[derive(Clone, Default, PartialEq)]
enum CallKind {
#[default]
Basic,
Delegate,
Static,
}
#[allow(unused)]
#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum CachePolicy {
#[default]
DoNothing,
Flush,
Clear,
}
impl<'a, H: Host + ?Sized> RawCall<'a, H> {
pub fn new(host: &'a H) -> Self {
Self {
host,
cache_policy: CachePolicy::default(),
kind: CallKind::default(),
callvalue: U256::ZERO,
gas: None,
offset: 0,
size: None,
}
}
pub fn new_with_value(host: &'a H, callvalue: U256) -> Self {
Self {
host,
callvalue,
cache_policy: CachePolicy::default(),
kind: CallKind::default(),
gas: None,
offset: 0,
size: None,
}
}
pub fn new_delegate(host: &'a H) -> Self {
Self {
host,
cache_policy: CachePolicy::default(),
kind: CallKind::Delegate,
callvalue: U256::ZERO,
gas: None,
offset: 0,
size: None,
}
}
pub fn new_static(host: &'a H) -> Self {
Self {
host,
cache_policy: CachePolicy::default(),
kind: CallKind::Static,
callvalue: U256::ZERO,
gas: None,
offset: 0,
size: None,
}
}
pub fn gas(mut self, gas: u64) -> Self {
self.gas = Some(gas);
self
}
#[allow(dead_code)]
pub fn ink(mut self, ink: u64) -> Self {
self.gas = Some(self.host.ink_to_gas(ink));
self
}
pub fn limit_return_data(mut self, offset: usize, size: usize) -> Self {
self.offset = offset;
self.size = Some(size);
self
}
pub fn skip_return_data(self) -> Self {
self.limit_return_data(0, 0)
}
pub fn flush_storage_cache(mut self) -> Self {
self.cache_policy = self.cache_policy.max(CachePolicy::Flush);
self
}
pub fn clear_storage_cache(mut self) -> Self {
self.cache_policy = CachePolicy::Clear;
self
}
pub unsafe fn call(self, contract: Address, calldata: &[u8]) -> ArbResult {
let mut outs_len: usize = 0;
let gas = self.gas.unwrap_or(u64::MAX); let value = B256::from(self.callvalue);
let status = unsafe {
match self.cache_policy {
CachePolicy::Clear => self.host.flush_cache(true),
CachePolicy::Flush => self.host.flush_cache(false),
CachePolicy::DoNothing => {}
}
match self.kind {
CallKind::Basic => self.host.call_contract(
contract.as_ptr(),
calldata.as_ptr(),
calldata.len(),
value.as_ptr(),
gas,
&mut outs_len,
),
CallKind::Delegate => self.host.delegate_call_contract(
contract.as_ptr(),
calldata.as_ptr(),
calldata.len(),
gas,
&mut outs_len,
),
CallKind::Static => self.host.static_call_contract(
contract.as_ptr(),
calldata.as_ptr(),
calldata.len(),
gas,
&mut outs_len,
),
}
};
let outs = self.host.read_return_data(self.offset, self.size);
match status {
0 => Ok(outs),
_ => Err(outs),
}
}
}