use core::marker::PhantomData;
use frame_support::{
dispatch::{GetDispatchInfo, PostDispatchInfo},
traits::Get,
weights::Weight,
};
use sp_runtime::{traits::Dispatchable, DispatchError};
use fp_evm::{ExitError, PrecompileFailure, PrecompileHandle};
use pallet_evm::GasWeightMapping;
use crate::{evm::handle::using_precompile_handle, solidity::revert::revert};
#[derive(Debug)]
pub enum TryDispatchError {
Evm(ExitError),
Substrate(DispatchError),
}
impl From<TryDispatchError> for PrecompileFailure {
fn from(f: TryDispatchError) -> PrecompileFailure {
match f {
TryDispatchError::Evm(e) => PrecompileFailure::Error { exit_status: e },
TryDispatchError::Substrate(e) => {
revert(alloc::format!("Dispatched call failed with error: {e:?}"))
}
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct RuntimeHelper<Runtime>(PhantomData<Runtime>);
impl<Runtime> RuntimeHelper<Runtime>
where
Runtime: pallet_evm::Config,
Runtime::RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
{
#[inline(always)]
pub fn record_weight_v2_cost(
handle: &mut impl PrecompileHandle,
weight: Weight,
) -> Result<(), ExitError> {
let remaining_gas = handle.remaining_gas();
let required_gas = Runtime::GasWeightMapping::weight_to_gas(weight);
if required_gas > remaining_gas {
return Err(ExitError::OutOfGas);
}
handle.record_external_cost(None, Some(weight.proof_size()), None)
}
#[inline(always)]
pub fn refund_weight_v2_cost(
handle: &mut impl PrecompileHandle,
weight: Weight,
maybe_actual_weight: Option<Weight>,
) -> Result<u64, ExitError> {
let used_weight = if let Some(actual_weight) = maybe_actual_weight {
let refund_weight = weight.checked_sub(&actual_weight).unwrap_or_default();
handle.refund_external_cost(None, Some(refund_weight.proof_size()));
actual_weight
} else {
weight
};
let used_gas = Runtime::GasWeightMapping::weight_to_gas(used_weight);
handle.record_cost(used_gas)?;
Ok(used_gas)
}
pub fn try_dispatch<Call>(
handle: &mut impl PrecompileHandle,
origin: <Runtime::RuntimeCall as Dispatchable>::RuntimeOrigin,
call: Call,
) -> Result<PostDispatchInfo, TryDispatchError>
where
Runtime::RuntimeCall: From<Call>,
{
let call = Runtime::RuntimeCall::from(call);
let dispatch_info = call.get_dispatch_info();
Self::record_weight_v2_cost(handle, dispatch_info.weight).map_err(TryDispatchError::Evm)?;
let post_dispatch_info = using_precompile_handle(handle, || call.dispatch(origin))
.map_err(|e| TryDispatchError::Substrate(e.error))?;
Self::refund_weight_v2_cost(
handle,
dispatch_info.weight,
post_dispatch_info.actual_weight,
)
.map_err(TryDispatchError::Evm)?;
Ok(post_dispatch_info)
}
}
impl<Runtime> RuntimeHelper<Runtime>
where
Runtime: pallet_evm::Config,
{
pub fn db_write_gas_cost() -> u64 {
<Runtime as pallet_evm::Config>::GasWeightMapping::weight_to_gas(
<Runtime as frame_system::Config>::DbWeight::get().writes(1),
)
}
pub fn db_read_gas_cost() -> u64 {
<Runtime as pallet_evm::Config>::GasWeightMapping::weight_to_gas(
<Runtime as frame_system::Config>::DbWeight::get().reads(1),
)
}
}