precompile_utils/
substrate.rs1use core::marker::PhantomData;
24
25use frame_support::{
27 dispatch::{GetDispatchInfo, PostDispatchInfo},
28 traits::Get,
29 weights::Weight,
30};
31use sp_runtime::{traits::Dispatchable, DispatchError};
32use fp_evm::{ExitError, PrecompileFailure, PrecompileHandle};
34use pallet_evm::GasWeightMapping;
35
36use crate::{evm::handle::using_precompile_handle, solidity::revert::revert};
37
38#[derive(Debug)]
39pub enum TryDispatchError {
40 Evm(ExitError),
41 Substrate(DispatchError),
42}
43
44impl From<TryDispatchError> for PrecompileFailure {
45 fn from(f: TryDispatchError) -> PrecompileFailure {
46 match f {
47 TryDispatchError::Evm(e) => PrecompileFailure::Error { exit_status: e },
48 TryDispatchError::Substrate(e) => {
49 revert(alloc::format!("Dispatched call failed with error: {e:?}"))
50 }
51 }
52 }
53}
54
55#[derive(Clone, Copy, Debug)]
58pub struct RuntimeHelper<Runtime>(PhantomData<Runtime>);
59
60impl<Runtime> RuntimeHelper<Runtime>
61where
62 Runtime: pallet_evm::Config,
63 Runtime::RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
64{
65 #[inline(always)]
66 pub fn record_external_cost(
67 handle: &mut impl PrecompileHandle,
68 weight: Weight,
69 storage_growth: u64,
70 ) -> Result<(), ExitError> {
71 let remaining_gas = handle.remaining_gas();
73 let required_gas = Runtime::GasWeightMapping::weight_to_gas(weight);
74 if required_gas > remaining_gas {
75 return Err(ExitError::OutOfGas);
76 }
77
78 handle.record_external_cost(None, Some(weight.proof_size()), Some(storage_growth))
81 }
82
83 #[inline(always)]
84 pub fn refund_weight_v2_cost(
85 handle: &mut impl PrecompileHandle,
86 weight: Weight,
87 maybe_actual_weight: Option<Weight>,
88 ) -> Result<u64, ExitError> {
89 let used_weight = if let Some(actual_weight) = maybe_actual_weight {
92 let refund_weight = weight.checked_sub(&actual_weight).unwrap_or_default();
93 handle.refund_external_cost(None, Some(refund_weight.proof_size()));
94 actual_weight
95 } else {
96 weight
97 };
98 let used_gas = Runtime::GasWeightMapping::weight_to_gas(used_weight);
99 handle.record_cost(used_gas)?;
100 Ok(used_gas)
101 }
102
103 pub fn try_dispatch<Call>(
107 handle: &mut impl PrecompileHandle,
108 origin: <Runtime::RuntimeCall as Dispatchable>::RuntimeOrigin,
109 call: Call,
110 storage_growth: u64,
111 ) -> Result<PostDispatchInfo, TryDispatchError>
112 where
113 Runtime::RuntimeCall: From<Call>,
114 {
115 let call = Runtime::RuntimeCall::from(call);
116 let dispatch_info = call.get_dispatch_info();
117
118 Self::record_external_cost(handle, dispatch_info.total_weight(), storage_growth)
119 .map_err(TryDispatchError::Evm)?;
120
121 let post_dispatch_info = using_precompile_handle(handle, || call.dispatch(origin))
127 .map_err(|e| TryDispatchError::Substrate(e.error))?;
128
129 Self::refund_weight_v2_cost(
130 handle,
131 dispatch_info.total_weight(),
132 post_dispatch_info.actual_weight,
133 )
134 .map_err(TryDispatchError::Evm)?;
135
136 Ok(post_dispatch_info)
137 }
138}
139
140impl<Runtime> RuntimeHelper<Runtime>
141where
142 Runtime: pallet_evm::Config,
143{
144 pub fn db_write_gas_cost() -> u64 {
146 <Runtime as pallet_evm::Config>::GasWeightMapping::weight_to_gas(
147 <Runtime as frame_system::Config>::DbWeight::get().writes(1),
148 )
149 }
150
151 pub fn db_read_gas_cost() -> u64 {
153 <Runtime as pallet_evm::Config>::GasWeightMapping::weight_to_gas(
154 <Runtime as frame_system::Config>::DbWeight::get().reads(1),
155 )
156 }
157}