pallet_revive/evm/
call.rs1use crate::{
21 evm::{
22 fees::{compute_max_integer_quotient, InfoT},
23 runtime::SetWeightLimit,
24 },
25 extract_code_and_data, BalanceOf, CallOf, Config, GenericTransaction, Pallet, Weight, Zero,
26 LOG_TARGET, RUNTIME_PALLETS_ADDR,
27};
28use alloc::{boxed::Box, vec::Vec};
29use codec::DecodeLimit;
30use frame_support::MAX_EXTRINSIC_DEPTH;
31use sp_core::{Get, U256};
32use sp_runtime::{transaction_validity::InvalidTransaction, SaturatedConversion};
33
34pub struct CallInfo<T: Config> {
36 pub call: CallOf<T>,
40 pub weight_limit: Weight,
42 pub encoded_len: u32,
44 pub tx_fee: BalanceOf<T>,
46 pub storage_deposit: BalanceOf<T>,
48 pub eth_gas_limit: U256,
50}
51
52#[derive(Debug, PartialEq, Eq, Clone)]
54pub enum CreateCallMode {
55 ExtrinsicExecution(u32, Vec<u8>),
58 DryRun,
60}
61
62impl GenericTransaction {
63 pub fn into_call<T>(self, mode: CreateCallMode) -> Result<CallInfo<T>, InvalidTransaction>
65 where
66 T: Config,
67 CallOf<T>: SetWeightLimit,
68 {
69 let is_dry_run = matches!(mode, CreateCallMode::DryRun);
70 let base_fee = <Pallet<T>>::evm_base_fee();
71
72 let Some(gas) = self.gas else {
73 log::debug!(target: LOG_TARGET, "No gas provided");
74 return Err(InvalidTransaction::Call);
75 };
76
77 let Some(effective_gas_price) = self.gas_price else {
81 log::debug!(target: LOG_TARGET, "No gas_price provided.");
82 return Err(InvalidTransaction::Payment);
83 };
84
85 let chain_id = self.chain_id.unwrap_or_default();
86
87 if chain_id != <T as Config>::ChainId::get().into() {
88 log::debug!(target: LOG_TARGET, "Invalid chain_id {chain_id:?}");
89 return Err(InvalidTransaction::Call);
90 }
91
92 if effective_gas_price < base_fee {
93 log::debug!(
94 target: LOG_TARGET,
95 "Specified gas_price is too low. effective_gas_price={effective_gas_price} base_fee={base_fee}"
96 );
97 return Err(InvalidTransaction::Payment);
98 }
99
100 let (encoded_len, transaction_encoded) =
101 if let CreateCallMode::ExtrinsicExecution(encoded_len, transaction_encoded) = mode {
102 (encoded_len, transaction_encoded)
103 } else {
104 let mut maximized_tx = self.clone();
107 let maximized_base_fee = base_fee.saturating_mul(256.into());
108 maximized_tx.gas = Some(u64::MAX.into());
109 maximized_tx.gas_price = Some(maximized_base_fee);
110 maximized_tx.max_priority_fee_per_gas = Some(maximized_base_fee);
111
112 let unsigned_tx = maximized_tx.try_into_unsigned().map_err(|_| {
113 log::debug!(target: LOG_TARGET, "Invalid transaction type.");
114 InvalidTransaction::Call
115 })?;
116 let transaction_encoded = unsigned_tx.dummy_signed_payload();
117
118 let eth_transact_call =
119 crate::Call::<T>::eth_transact { payload: transaction_encoded.clone() };
120 (<T as Config>::FeeInfo::encoded_len(eth_transact_call.into()), transaction_encoded)
121 };
122
123 let value = self.value.unwrap_or_default();
124 let data = self.input.to_vec();
125
126 let mut call = if let Some(dest) = self.to {
127 if dest == RUNTIME_PALLETS_ADDR {
128 let call =
129 CallOf::<T>::decode_all_with_depth_limit(MAX_EXTRINSIC_DEPTH, &mut &data[..])
130 .map_err(|_| {
131 log::debug!(target: LOG_TARGET, "Failed to decode data as Call");
132 InvalidTransaction::Call
133 })?;
134
135 if !value.is_zero() {
136 log::debug!(target: LOG_TARGET, "Runtime pallets address cannot be called with value");
137 return Err(InvalidTransaction::Call)
138 }
139
140 crate::Call::eth_substrate_call::<T> { call: Box::new(call), transaction_encoded }
141 .into()
142 } else {
143 let call = crate::Call::eth_call::<T> {
144 dest,
145 value,
146 weight_limit: Zero::zero(),
147 eth_gas_limit: gas,
148 data,
149 transaction_encoded,
150 effective_gas_price,
151 encoded_len,
152 }
153 .into();
154 call
155 }
156 } else {
157 let (code, data) = if data.starts_with(&polkavm_common::program::BLOB_MAGIC) {
158 let Some((code, data)) = extract_code_and_data(&data) else {
159 log::debug!(target: LOG_TARGET, "Failed to extract polkavm code & data");
160 return Err(InvalidTransaction::Call);
161 };
162 (code, data)
163 } else {
164 (data, Default::default())
165 };
166
167 let call = crate::Call::eth_instantiate_with_code::<T> {
168 value,
169 weight_limit: Zero::zero(),
170 eth_gas_limit: gas,
171 code,
172 data,
173 transaction_encoded,
174 effective_gas_price,
175 encoded_len,
176 }
177 .into();
178
179 call
180 };
181
182 let eth_fee =
184 effective_gas_price.saturating_mul(gas) / <T as Config>::NativeToEthRatio::get();
185
186 let weight_limit = {
187 let fixed_fee = <T as Config>::FeeInfo::fixed_fee(encoded_len as u32);
188 let info = <T as Config>::FeeInfo::dispatch_info(&call);
189
190 let remaining_fee = {
191 let adjusted = eth_fee.checked_sub(fixed_fee.into()).ok_or_else(|| {
192 log::debug!(target: LOG_TARGET, "Not enough gas supplied to cover base and len fee. eth_fee={eth_fee:?} fixed_fee={fixed_fee:?}");
193 InvalidTransaction::Payment
194 })?;
195
196 let unadjusted = compute_max_integer_quotient(
197 <T as Config>::FeeInfo::next_fee_multiplier(),
198 <BalanceOf<T>>::saturated_from(adjusted),
199 );
200
201 unadjusted
202 };
203 let remaining_fee_weight = <T as Config>::FeeInfo::fee_to_weight(remaining_fee);
204 let weight_limit = remaining_fee_weight
205 .checked_sub(&info.total_weight()).ok_or_else(|| {
206 log::debug!(target: LOG_TARGET, "Not enough gas supplied to cover the weight ({:?}) of the extrinsic. remaining_fee_weight: {remaining_fee_weight:?}", info.total_weight(),);
207 InvalidTransaction::Payment
208 })?;
209
210 call.set_weight_limit(weight_limit);
211
212 if !is_dry_run {
213 let max_weight = <Pallet<T>>::evm_max_extrinsic_weight();
214 let info = <T as Config>::FeeInfo::dispatch_info(&call);
215 let overweight_by = info.total_weight().saturating_sub(max_weight);
216 let capped_weight = weight_limit.saturating_sub(overweight_by);
217 call.set_weight_limit(capped_weight);
218 capped_weight
219 } else {
220 weight_limit
221 }
222 };
223
224 let tx_fee = <T as Config>::FeeInfo::tx_fee(encoded_len, &call);
226
227 let storage_deposit = eth_fee.checked_sub(tx_fee.into()).ok_or_else(|| {
229 log::error!(target: LOG_TARGET, "The eth_fee={eth_fee:?} is smaller than the tx_fee={tx_fee:?}. This is a bug.");
230 InvalidTransaction::Payment
231 })?.saturated_into();
232
233 Ok(CallInfo {
234 call,
235 weight_limit,
236 encoded_len,
237 tx_fee,
238 storage_deposit,
239 eth_gas_limit: gas,
240 })
241 }
242}