12345678910111213141516171819extern crate alloc;20use core::marker::PhantomData;2122use evm_coder::{23 abi::{AbiEncode, AbiType},24 generate_stubgen, solidity_interface,25 types::*,26 ToLog,27};28use frame_support::traits::Get;29use frame_system::pallet_prelude::*;30use pallet_common::eth;31use pallet_evm::{32 account::CrossAccountId, ExitRevert, OnCreate, OnMethodCall, PrecompileFailure,33 PrecompileHandle, PrecompileResult,34};35use pallet_evm_coder_substrate::{36 dispatch_to_evm,37 execution::{PreDispatch, Result},38 frontier_contract, SubstrateRecorder, WithRecorder,39};40use pallet_evm_transaction_payment::CallContext;41use sp_core::{H160, U256};42use sp_std::vec::Vec;43use up_data_structs::SponsorshipState;44use up_sponsorship::SponsorshipHandler;4546use crate::{47 AllowlistEnabled, Config, Owner, Pallet, SponsorBasket, Sponsoring, SponsoringFeeLimit,48 SponsoringModeT, SponsoringRateLimit,49};5051frontier_contract! {52 macro_rules! ContractHelpers_result {...}53 impl<T: Config> Contract for ContractHelpers<T> {...}54}555657#[derive(ToLog)]58pub enum ContractHelpersEvents {59 60 ContractSponsorSet {61 62 #[indexed]63 contract_address: Address,64 65 sponsor: Address,66 },6768 69 ContractSponsorshipConfirmed {70 71 #[indexed]72 contract_address: Address,73 74 sponsor: Address,75 },7677 78 ContractSponsorRemoved {79 80 #[indexed]81 contract_address: Address,82 },83}848586pub struct ContractHelpers<T: Config>(SubstrateRecorder<T>);87impl<T: Config> WithRecorder<T> for ContractHelpers<T> {88 fn recorder(&self) -> &SubstrateRecorder<T> {89 &self.090 }9192 fn into_recorder(self) -> SubstrateRecorder<T> {93 self.094 }95}969798#[solidity_interface(name = ContractHelpers, events(ContractHelpersEvents), enum(derive(PreDispatch)))]99impl<T: Config> ContractHelpers<T>100where101 T::AccountId: AsRef<[u8; 32]>,102{103 104 105 106 107 108 109 110 fn contract_owner(&self, contract_address: Address) -> Result<Address> {111 Ok(<Owner<T>>::get(contract_address))112 }113114 115 116 117 fn set_sponsor(118 &mut self,119 caller: Caller,120 contract_address: Address,121 sponsor: Address,122 ) -> Result<()> {123 self.recorder().consume_sload()?;124 self.recorder().consume_sstore()?;125126 Pallet::<T>::set_sponsor(127 &T::CrossAccountId::from_eth(caller),128 contract_address,129 &T::CrossAccountId::from_eth(sponsor),130 )131 .map_err(dispatch_to_evm::<T>)?;132133 Ok(())134 }135136 137 138 139 fn self_sponsored_enable(&mut self, caller: Caller, contract_address: Address) -> Result<()> {140 self.recorder().consume_sload()?;141 self.recorder().consume_sstore()?;142143 let caller = T::CrossAccountId::from_eth(caller);144145 Pallet::<T>::ensure_owner(contract_address, *caller.as_eth())146 .map_err(dispatch_to_evm::<T>)?;147148 Pallet::<T>::force_set_sponsor(149 contract_address,150 &T::CrossAccountId::from_eth(contract_address),151 )152 .map_err(dispatch_to_evm::<T>)?;153154 Ok(())155 }156157 158 159 160 fn remove_sponsor(&mut self, caller: Caller, contract_address: Address) -> Result<()> {161 self.recorder().consume_sload()?;162 self.recorder().consume_sstore()?;163164 Pallet::<T>::remove_sponsor(&T::CrossAccountId::from_eth(caller), contract_address)165 .map_err(dispatch_to_evm::<T>)?;166167 Ok(())168 }169170 171 172 173 174 175 fn confirm_sponsorship(&mut self, caller: Caller, contract_address: Address) -> Result<()> {176 self.recorder().consume_sload()?;177 self.recorder().consume_sstore()?;178179 Pallet::<T>::confirm_sponsorship(&T::CrossAccountId::from_eth(caller), contract_address)180 .map_err(dispatch_to_evm::<T>)?;181182 Ok(())183 }184185 186 187 188 189 fn sponsor(&self, contract_address: Address) -> Result<Option<eth::CrossAddress>> {190 Ok(Pallet::<T>::get_sponsor(contract_address)191 .as_ref()192 .map(eth::CrossAddress::from_sub_cross_account::<T>))193 }194195 196 197 198 199 fn has_sponsor(&self, contract_address: Address) -> Result<bool> {200 Ok(Pallet::<T>::get_sponsor(contract_address).is_some())201 }202203 204 205 206 207 fn has_pending_sponsor(&self, contract_address: Address) -> Result<bool> {208 Ok(match Sponsoring::<T>::get(contract_address) {209 SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => false,210 SponsorshipState::Unconfirmed(_) => true,211 })212 }213214 fn sponsoring_enabled(&self, contract_address: Address) -> Result<bool> {215 Ok(<Pallet<T>>::sponsoring_mode(contract_address) != SponsoringModeT::Disabled)216 }217218 fn set_sponsoring_mode(219 &mut self,220 caller: Caller,221 contract_address: Address,222 mode: SponsoringModeT,223 ) -> Result<()> {224 self.recorder().consume_sload()?;225 self.recorder().consume_sstore()?;226227 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;228 <Pallet<T>>::set_sponsoring_mode(contract_address, mode);229230 Ok(())231 }232233 234 235 236 fn sponsoring_rate_limit(&self, contract_address: Address) -> Result<u32> {237 self.recorder().consume_sload()?;238239 Ok(<SponsoringRateLimit<T>>::get(contract_address)240 .try_into()241 .map_err(|_| "rate limit > u32::MAX")?)242 }243244 245 246 247 248 249 250 fn set_sponsoring_rate_limit(251 &mut self,252 caller: Caller,253 contract_address: Address,254 rate_limit: u32,255 ) -> Result<()> {256 self.recorder().consume_sload()?;257 self.recorder().consume_sstore()?;258259 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;260 <Pallet<T>>::set_sponsoring_rate_limit(contract_address, rate_limit.into());261 Ok(())262 }263264 265 266 267 268 269 270 fn set_sponsoring_fee_limit(271 &mut self,272 caller: Caller,273 contract_address: Address,274 fee_limit: U256,275 ) -> Result<()> {276 self.recorder().consume_sload()?;277 self.recorder().consume_sstore()?;278279 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;280 <Pallet<T>>::set_sponsoring_fee_limit(contract_address, fee_limit)281 .map_err(dispatch_to_evm::<T>)?;282 Ok(())283 }284285 286 287 288 289 fn sponsoring_fee_limit(&self, contract_address: Address) -> Result<U256> {290 self.recorder().consume_sload()?;291292 Ok(get_sponsoring_fee_limit::<T>(contract_address))293 }294295 296 297 298 299 300 fn allowed(&self, contract_address: Address, user: Address) -> Result<bool> {301 self.0.consume_sload()?;302 Ok(<Pallet<T>>::allowed(contract_address, user))303 }304305 306 307 308 309 310 311 fn toggle_allowed(312 &mut self,313 caller: Caller,314 contract_address: Address,315 user: Address,316 is_allowed: bool,317 ) -> Result<()> {318 self.recorder().consume_sload()?;319 self.recorder().consume_sstore()?;320321 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;322 <Pallet<T>>::toggle_allowed(contract_address, user, is_allowed);323324 Ok(())325 }326327 328 329 330 331 332 333 fn allowlist_enabled(&self, contract_address: Address) -> Result<bool> {334 Ok(<AllowlistEnabled<T>>::get(contract_address))335 }336337 338 339 340 fn toggle_allowlist(341 &mut self,342 caller: Caller,343 contract_address: Address,344 enabled: bool,345 ) -> Result<()> {346 self.recorder().consume_sload()?;347 self.recorder().consume_sstore()?;348349 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;350 <Pallet<T>>::toggle_allowlist(contract_address, enabled);351 Ok(())352 }353}354355356pub struct HelpersOnMethodCall<T: Config>(PhantomData<*const T>);357impl<T: Config> OnMethodCall<T> for HelpersOnMethodCall<T>358where359 T::AccountId: AsRef<[u8; 32]>,360{361 fn is_reserved(contract: &sp_core::H160) -> bool {362 contract == &T::ContractAddress::get()363 }364365 fn is_used(contract: &sp_core::H160) -> bool {366 contract == &T::ContractAddress::get()367 }368369 fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {370 371 if <AllowlistEnabled<T>>::get(handle.code_address())372 && !<Pallet<T>>::allowed(handle.code_address(), handle.context().caller)373 {374 return Some(Err(PrecompileFailure::Revert {375 exit_status: ExitRevert::Reverted,376 output: ("target contract is allowlisted",)377 .abi_encode_call(evm_coder::fn_selector!(Error(string))),378 }));379 }380381 if handle.code_address() != T::ContractAddress::get() {382 return None;383 }384385 let helpers = ContractHelpers::<T>(SubstrateRecorder::<T>::new(handle.remaining_gas()));386 pallet_evm_coder_substrate::call(handle, helpers)387 }388389 fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {390 (contract == &T::ContractAddress::get())391 .then(|| include_bytes!("./stubs/ContractHelpers.raw").to_vec())392 }393}394395396pub struct HelpersOnCreate<T: Config>(PhantomData<*const T>);397impl<T: Config> OnCreate<T> for HelpersOnCreate<T> {398 fn on_create(owner: H160, contract: H160) {399 <Owner<T>>::insert(contract, owner);400 }401}402403404pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);405impl<T: Config> SponsorshipHandler<T::CrossAccountId, CallContext>406 for HelpersContractSponsoring<T>407{408 fn get_sponsor(409 who: &T::CrossAccountId,410 call_context: &CallContext,411 ) -> Option<T::CrossAccountId> {412 let contract_address = call_context.contract_address;413 let mode = <Pallet<T>>::sponsoring_mode(contract_address);414 if mode == SponsoringModeT::Disabled {415 return None;416 }417418 let sponsor = match <Pallet<T>>::get_sponsor(contract_address) {419 Some(sponsor) => sponsor,420 None => return None,421 };422423 if mode == SponsoringModeT::Allowlisted424 && !<Pallet<T>>::allowed(contract_address, *who.as_eth())425 {426 return None;427 }428 let block_number = <frame_system::Pallet<T>>::block_number() as BlockNumberFor<T>;429430 if let Some(last_tx_block) = <SponsorBasket<T>>::get(contract_address, who.as_eth()) {431 let limit = <SponsoringRateLimit<T>>::get(contract_address);432433 let timeout = last_tx_block + limit;434 if block_number < timeout {435 return None;436 }437 }438439 let sponsored_fee_limit = get_sponsoring_fee_limit::<T>(contract_address);440441 if call_context.max_fee > sponsored_fee_limit {442 return None;443 }444445 <SponsorBasket<T>>::insert(contract_address, who.as_eth(), block_number);446447 Some(sponsor)448 }449}450451fn get_sponsoring_fee_limit<T: Config>(contract_address: Address) -> U256 {452 <SponsoringFeeLimit<T>>::get(contract_address)453 .get(&0xffffffff)454 .cloned()455 .unwrap_or(U256::MAX)456}457458generate_stubgen!(contract_helpers_impl, ContractHelpersCall<()>, true);459generate_stubgen!(contract_helpers_iface, ContractHelpersCall<()>, false);