12345678910111213141516171819extern crate alloc;20use core::marker::PhantomData;21use evm_coder::{22 abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*, ToLog,23};24use pallet_evm::{25 ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, PrecompileHandle,26 account::CrossAccountId,27};28use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder, dispatch_to_evm};29use pallet_evm_transaction_payment::CallContext;30use sp_core::{H160, U256};31use up_data_structs::SponsorshipState;32use crate::{33 AllowlistEnabled, Config, Owner, Pallet, SponsorBasket, SponsoringFeeLimit,34 SponsoringRateLimit, SponsoringModeT, Sponsoring,35};36use frame_support::traits::Get;37use up_sponsorship::SponsorshipHandler;38use sp_std::vec::Vec;394041#[derive(ToLog)]42pub enum ContractHelpersEvents {43 44 ContractSponsorSet {45 46 #[indexed]47 contract_address: address,48 49 sponsor: address,50 },5152 53 ContractSponsorshipConfirmed {54 55 #[indexed]56 contract_address: address,57 58 sponsor: address,59 },6061 62 ContractSponsorRemoved {63 64 #[indexed]65 contract_address: address,66 },67}686970pub struct ContractHelpers<T: Config>(SubstrateRecorder<T>);71impl<T: Config> WithRecorder<T> for ContractHelpers<T> {72 fn recorder(&self) -> &SubstrateRecorder<T> {73 &self.074 }7576 fn into_recorder(self) -> SubstrateRecorder<T> {77 self.078 }79}808182#[solidity_interface(name = ContractHelpers, events(ContractHelpersEvents))]83impl<T: Config> ContractHelpers<T>84where85 T::AccountId: AsRef<[u8; 32]>,86{87 88 89 90 91 92 93 94 fn contract_owner(&self, contract_address: address) -> Result<address> {95 Ok(<Owner<T>>::get(contract_address))96 }9798 99 100 101 fn set_sponsor(102 &mut self,103 caller: caller,104 contract_address: address,105 sponsor: address,106 ) -> Result<void> {107 self.recorder().consume_sload()?;108 self.recorder().consume_sstore()?;109110 Pallet::<T>::set_sponsor(111 &T::CrossAccountId::from_eth(caller),112 contract_address,113 &T::CrossAccountId::from_eth(sponsor),114 )115 .map_err(dispatch_to_evm::<T>)?;116117 Ok(())118 }119120 121 122 123 fn self_sponsored_enable(&mut self, caller: caller, contract_address: address) -> Result<void> {124 self.recorder().consume_sload()?;125 self.recorder().consume_sstore()?;126127 let caller = T::CrossAccountId::from_eth(caller);128129 Pallet::<T>::ensure_owner(contract_address, *caller.as_eth())130 .map_err(dispatch_to_evm::<T>)?;131132 Pallet::<T>::force_set_sponsor(133 contract_address,134 &T::CrossAccountId::from_eth(contract_address),135 )136 .map_err(dispatch_to_evm::<T>)?;137138 Ok(())139 }140141 142 143 144 fn remove_sponsor(&mut self, caller: caller, contract_address: address) -> Result<void> {145 self.recorder().consume_sload()?;146 self.recorder().consume_sstore()?;147148 Pallet::<T>::remove_sponsor(&T::CrossAccountId::from_eth(caller), contract_address)149 .map_err(dispatch_to_evm::<T>)?;150151 Ok(())152 }153154 155 156 157 158 159 fn confirm_sponsorship(&mut self, caller: caller, contract_address: address) -> Result<void> {160 self.recorder().consume_sload()?;161 self.recorder().consume_sstore()?;162163 Pallet::<T>::confirm_sponsorship(&T::CrossAccountId::from_eth(caller), contract_address)164 .map_err(dispatch_to_evm::<T>)?;165166 Ok(())167 }168169 170 171 172 173 fn sponsor(&self, contract_address: address) -> Result<(address, uint256)> {174 let sponsor =175 Pallet::<T>::get_sponsor(contract_address).ok_or("Contract has no sponsor")?;176 Ok(pallet_common::eth::convert_cross_account_to_tuple::<T>(177 &sponsor,178 ))179 }180181 182 183 184 185 fn has_sponsor(&self, contract_address: address) -> Result<bool> {186 Ok(Pallet::<T>::get_sponsor(contract_address).is_some())187 }188189 190 191 192 193 fn has_pending_sponsor(&self, contract_address: address) -> Result<bool> {194 Ok(match Sponsoring::<T>::get(contract_address) {195 SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => false,196 SponsorshipState::Unconfirmed(_) => true,197 })198 }199200 fn sponsoring_enabled(&self, contract_address: address) -> Result<bool> {201 Ok(<Pallet<T>>::sponsoring_mode(contract_address) != SponsoringModeT::Disabled)202 }203204 fn set_sponsoring_mode(205 &mut self,206 caller: caller,207 contract_address: address,208 209 mode: uint8,210 ) -> Result<void> {211 self.recorder().consume_sload()?;212 self.recorder().consume_sstore()?;213214 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;215 let mode = SponsoringModeT::from_eth(mode).ok_or("unknown mode")?;216 <Pallet<T>>::set_sponsoring_mode(contract_address, mode);217218 Ok(())219 }220221 222 223 224 fn sponsoring_rate_limit(&self, contract_address: address) -> Result<uint32> {225 self.recorder().consume_sload()?;226227 Ok(<SponsoringRateLimit<T>>::get(contract_address)228 .try_into()229 .map_err(|_| "rate limit > u32::MAX")?)230 }231232 233 234 235 236 237 238 fn set_sponsoring_rate_limit(239 &mut self,240 caller: caller,241 contract_address: address,242 rate_limit: uint32,243 ) -> Result<void> {244 self.recorder().consume_sload()?;245 self.recorder().consume_sstore()?;246247 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;248 <Pallet<T>>::set_sponsoring_rate_limit(contract_address, rate_limit.into());249 Ok(())250 }251252 253 254 255 256 257 258 fn set_sponsoring_fee_limit(259 &mut self,260 caller: caller,261 contract_address: address,262 fee_limit: uint256,263 ) -> Result<void> {264 self.recorder().consume_sload()?;265 self.recorder().consume_sstore()?;266267 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;268 <Pallet<T>>::set_sponsoring_fee_limit(contract_address, fee_limit.into())269 .map_err(dispatch_to_evm::<T>)?;270 Ok(())271 }272273 274 275 276 277 fn sponsoring_fee_limit(&self, contract_address: address) -> Result<uint256> {278 self.recorder().consume_sload()?;279280 Ok(get_sponsoring_fee_limit::<T>(contract_address))281 }282283 284 285 286 287 288 fn allowed(&self, contract_address: address, user: address) -> Result<bool> {289 self.0.consume_sload()?;290 Ok(<Pallet<T>>::allowed(contract_address, user))291 }292293 294 295 296 297 298 299 fn toggle_allowed(300 &mut self,301 caller: caller,302 contract_address: address,303 user: address,304 is_allowed: bool,305 ) -> Result<void> {306 self.recorder().consume_sload()?;307 self.recorder().consume_sstore()?;308309 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;310 <Pallet<T>>::toggle_allowed(contract_address, user, is_allowed);311312 Ok(())313 }314315 316 317 318 319 320 321 fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {322 Ok(<AllowlistEnabled<T>>::get(contract_address))323 }324325 326 327 328 fn toggle_allowlist(329 &mut self,330 caller: caller,331 contract_address: address,332 enabled: bool,333 ) -> Result<void> {334 self.recorder().consume_sload()?;335 self.recorder().consume_sstore()?;336337 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;338 <Pallet<T>>::toggle_allowlist(contract_address, enabled);339 Ok(())340 }341}342343344pub struct HelpersOnMethodCall<T: Config>(PhantomData<*const T>);345impl<T: Config> OnMethodCall<T> for HelpersOnMethodCall<T>346where347 T::AccountId: AsRef<[u8; 32]>,348{349 fn is_reserved(contract: &sp_core::H160) -> bool {350 contract == &T::ContractAddress::get()351 }352353 fn is_used(contract: &sp_core::H160) -> bool {354 contract == &T::ContractAddress::get()355 }356357 fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {358 359 if <AllowlistEnabled<T>>::get(handle.code_address())360 && !<Pallet<T>>::allowed(handle.code_address(), handle.context().caller)361 {362 return Some(Err(PrecompileFailure::Revert {363 exit_status: ExitRevert::Reverted,364 output: {365 let mut writer = AbiWriter::new_call(evm_coder::fn_selector!(Error(string)));366 writer.string("Target contract is allowlisted");367 writer.finish()368 },369 }));370 }371372 if handle.code_address() != T::ContractAddress::get() {373 return None;374 }375376 let helpers = ContractHelpers::<T>(SubstrateRecorder::<T>::new(handle.remaining_gas()));377 pallet_evm_coder_substrate::call(handle, helpers)378 }379380 fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {381 (contract == &T::ContractAddress::get())382 .then(|| include_bytes!("./stubs/ContractHelpers.raw").to_vec())383 }384}385386387pub struct HelpersOnCreate<T: Config>(PhantomData<*const T>);388impl<T: Config> OnCreate<T> for HelpersOnCreate<T> {389 fn on_create(owner: H160, contract: H160) {390 <Owner<T>>::insert(contract, owner);391 }392}393394395pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);396impl<T: Config> SponsorshipHandler<T::CrossAccountId, CallContext>397 for HelpersContractSponsoring<T>398{399 fn get_sponsor(400 who: &T::CrossAccountId,401 call_context: &CallContext,402 ) -> Option<T::CrossAccountId> {403 let contract_address = call_context.contract_address;404 let mode = <Pallet<T>>::sponsoring_mode(contract_address);405 if mode == SponsoringModeT::Disabled {406 return None;407 }408409 let sponsor = match <Pallet<T>>::get_sponsor(contract_address) {410 Some(sponsor) => sponsor,411 None => return None,412 };413414 if mode == SponsoringModeT::Allowlisted415 && !<Pallet<T>>::allowed(contract_address, *who.as_eth())416 {417 return None;418 }419 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;420421 if let Some(last_tx_block) = <SponsorBasket<T>>::get(contract_address, who.as_eth()) {422 let limit = <SponsoringRateLimit<T>>::get(contract_address);423424 let timeout = last_tx_block + limit;425 if block_number < timeout {426 return None;427 }428 }429430 let sponsored_fee_limit = get_sponsoring_fee_limit::<T>(contract_address);431432 if call_context.max_fee > sponsored_fee_limit {433 return None;434 }435436 <SponsorBasket<T>>::insert(contract_address, who.as_eth(), block_number);437438 Some(sponsor)439 }440}441442fn get_sponsoring_fee_limit<T: Config>(contract_address: address) -> uint256 {443 <SponsoringFeeLimit<T>>::get(contract_address)444 .get(&0xffffffff)445 .cloned()446 .unwrap_or(U256::MAX)447}448449generate_stubgen!(contract_helpers_impl, ContractHelpersCall<()>, true);450generate_stubgen!(contract_helpers_iface, ContractHelpersCall<()>, false);