12345678910111213141516171819extern crate alloc;20use core::marker::PhantomData;21use evm_coder::{22 abi::{AbiWriter, AbiType},23 generate_stubgen, solidity_interface,24 types::*,25 ToLog,26};27use pallet_common::eth;28use pallet_evm::{29 ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, PrecompileHandle,30 account::CrossAccountId,31};32use pallet_evm_coder_substrate::{33 SubstrateRecorder, WithRecorder, dispatch_to_evm,34 execution::{Result, PreDispatch},35 frontier_contract,36};37use pallet_evm_transaction_payment::CallContext;38use sp_core::{H160, U256};39use up_data_structs::SponsorshipState;40use crate::{41 AllowlistEnabled, Config, Owner, Pallet, SponsorBasket, SponsoringFeeLimit,42 SponsoringRateLimit, SponsoringModeT, Sponsoring,43};44use frame_support::traits::Get;45use up_sponsorship::SponsorshipHandler;46use sp_std::vec::Vec;4748frontier_contract! {49 macro_rules! ContractHelpers_result {...}50 impl<T: Config> Contract for ContractHelpers<T> {...}51}525354#[derive(ToLog)]55pub enum ContractHelpersEvents {56 57 ContractSponsorSet {58 59 #[indexed]60 contract_address: Address,61 62 sponsor: Address,63 },6465 66 ContractSponsorshipConfirmed {67 68 #[indexed]69 contract_address: Address,70 71 sponsor: Address,72 },7374 75 ContractSponsorRemoved {76 77 #[indexed]78 contract_address: Address,79 },80}818283pub struct ContractHelpers<T: Config>(SubstrateRecorder<T>);84impl<T: Config> WithRecorder<T> for ContractHelpers<T> {85 fn recorder(&self) -> &SubstrateRecorder<T> {86 &self.087 }8889 fn into_recorder(self) -> SubstrateRecorder<T> {90 self.091 }92}939495#[solidity_interface(name = ContractHelpers, events(ContractHelpersEvents), enum(derive(PreDispatch)))]96impl<T: Config> ContractHelpers<T>97where98 T::AccountId: AsRef<[u8; 32]>,99{100 101 102 103 104 105 106 107 fn contract_owner(&self, contract_address: Address) -> Result<Address> {108 Ok(<Owner<T>>::get(contract_address))109 }110111 112 113 114 fn set_sponsor(115 &mut self,116 caller: Caller,117 contract_address: Address,118 sponsor: Address,119 ) -> Result<()> {120 self.recorder().consume_sload()?;121 self.recorder().consume_sstore()?;122123 Pallet::<T>::set_sponsor(124 &T::CrossAccountId::from_eth(caller),125 contract_address,126 &T::CrossAccountId::from_eth(sponsor),127 )128 .map_err(dispatch_to_evm::<T>)?;129130 Ok(())131 }132133 134 135 136 fn self_sponsored_enable(&mut self, caller: Caller, contract_address: Address) -> Result<()> {137 self.recorder().consume_sload()?;138 self.recorder().consume_sstore()?;139140 let caller = T::CrossAccountId::from_eth(caller);141142 Pallet::<T>::ensure_owner(contract_address, *caller.as_eth())143 .map_err(dispatch_to_evm::<T>)?;144145 Pallet::<T>::force_set_sponsor(146 contract_address,147 &T::CrossAccountId::from_eth(contract_address),148 )149 .map_err(dispatch_to_evm::<T>)?;150151 Ok(())152 }153154 155 156 157 fn remove_sponsor(&mut self, caller: Caller, contract_address: Address) -> Result<()> {158 self.recorder().consume_sload()?;159 self.recorder().consume_sstore()?;160161 Pallet::<T>::remove_sponsor(&T::CrossAccountId::from_eth(caller), contract_address)162 .map_err(dispatch_to_evm::<T>)?;163164 Ok(())165 }166167 168 169 170 171 172 fn confirm_sponsorship(&mut self, caller: Caller, contract_address: Address) -> Result<()> {173 self.recorder().consume_sload()?;174 self.recorder().consume_sstore()?;175176 Pallet::<T>::confirm_sponsorship(&T::CrossAccountId::from_eth(caller), contract_address)177 .map_err(dispatch_to_evm::<T>)?;178179 Ok(())180 }181182 183 184 185 186 fn sponsor(&self, contract_address: Address) -> Result<Option<eth::CrossAddress>> {187 Ok(Pallet::<T>::get_sponsor(contract_address)188 .as_ref()189 .map(eth::CrossAddress::from_sub_cross_account::<T>))190 }191192 193 194 195 196 fn has_sponsor(&self, contract_address: Address) -> Result<bool> {197 Ok(Pallet::<T>::get_sponsor(contract_address).is_some())198 }199200 201 202 203 204 fn has_pending_sponsor(&self, contract_address: Address) -> Result<bool> {205 Ok(match Sponsoring::<T>::get(contract_address) {206 SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => false,207 SponsorshipState::Unconfirmed(_) => true,208 })209 }210211 fn sponsoring_enabled(&self, contract_address: Address) -> Result<bool> {212 Ok(<Pallet<T>>::sponsoring_mode(contract_address) != SponsoringModeT::Disabled)213 }214215 fn set_sponsoring_mode(216 &mut self,217 caller: Caller,218 contract_address: Address,219 mode: SponsoringModeT,220 ) -> Result<()> {221 self.recorder().consume_sload()?;222 self.recorder().consume_sstore()?;223224 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;225 <Pallet<T>>::set_sponsoring_mode(contract_address, mode);226227 Ok(())228 }229230 231 232 233 fn sponsoring_rate_limit(&self, contract_address: Address) -> Result<u32> {234 self.recorder().consume_sload()?;235236 Ok(<SponsoringRateLimit<T>>::get(contract_address)237 .try_into()238 .map_err(|_| "rate limit > u32::MAX")?)239 }240241 242 243 244 245 246 247 fn set_sponsoring_rate_limit(248 &mut self,249 caller: Caller,250 contract_address: Address,251 rate_limit: u32,252 ) -> Result<()> {253 self.recorder().consume_sload()?;254 self.recorder().consume_sstore()?;255256 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;257 <Pallet<T>>::set_sponsoring_rate_limit(contract_address, rate_limit.into());258 Ok(())259 }260261 262 263 264 265 266 267 fn set_sponsoring_fee_limit(268 &mut self,269 caller: Caller,270 contract_address: Address,271 fee_limit: U256,272 ) -> Result<()> {273 self.recorder().consume_sload()?;274 self.recorder().consume_sstore()?;275276 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;277 <Pallet<T>>::set_sponsoring_fee_limit(contract_address, fee_limit)278 .map_err(dispatch_to_evm::<T>)?;279 Ok(())280 }281282 283 284 285 286 fn sponsoring_fee_limit(&self, contract_address: Address) -> Result<U256> {287 self.recorder().consume_sload()?;288289 Ok(get_sponsoring_fee_limit::<T>(contract_address))290 }291292 293 294 295 296 297 fn allowed(&self, contract_address: Address, user: Address) -> Result<bool> {298 self.0.consume_sload()?;299 Ok(<Pallet<T>>::allowed(contract_address, user))300 }301302 303 304 305 306 307 308 fn toggle_allowed(309 &mut self,310 caller: Caller,311 contract_address: Address,312 user: Address,313 is_allowed: bool,314 ) -> Result<()> {315 self.recorder().consume_sload()?;316 self.recorder().consume_sstore()?;317318 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;319 <Pallet<T>>::toggle_allowed(contract_address, user, is_allowed);320321 Ok(())322 }323324 325 326 327 328 329 330 fn allowlist_enabled(&self, contract_address: Address) -> Result<bool> {331 Ok(<AllowlistEnabled<T>>::get(contract_address))332 }333334 335 336 337 fn toggle_allowlist(338 &mut self,339 caller: Caller,340 contract_address: Address,341 enabled: bool,342 ) -> Result<()> {343 self.recorder().consume_sload()?;344 self.recorder().consume_sstore()?;345346 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;347 <Pallet<T>>::toggle_allowlist(contract_address, enabled);348 Ok(())349 }350}351352353pub struct HelpersOnMethodCall<T: Config>(PhantomData<*const T>);354impl<T: Config> OnMethodCall<T> for HelpersOnMethodCall<T>355where356 T::AccountId: AsRef<[u8; 32]>,357{358 fn is_reserved(contract: &sp_core::H160) -> bool {359 contract == &T::ContractAddress::get()360 }361362 fn is_used(contract: &sp_core::H160) -> bool {363 contract == &T::ContractAddress::get()364 }365366 fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {367 368 if <AllowlistEnabled<T>>::get(handle.code_address())369 && !<Pallet<T>>::allowed(handle.code_address(), handle.context().caller)370 {371 return Some(Err(PrecompileFailure::Revert {372 exit_status: ExitRevert::Reverted,373 output: {374 let mut writer = AbiWriter::new_call(evm_coder::fn_selector!(Error(string)));375 writer.string("Target contract is allowlisted");376 writer.finish()377 },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 T::BlockNumber;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);