1234567891011121314151617use core::marker::PhantomData;18use evm_coder::{abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*};19use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder, dispatch_to_evm};20use pallet_evm::{21 ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, PrecompileHandle,22 account::CrossAccountId,23};24use sp_core::H160;25use up_data_structs::SponsorshipState;26use crate::{27 AllowlistEnabled, Config, Owner, Pallet, SponsorBasket, SponsoringRateLimit, SponsoringModeT,28 Sponsoring,29};30use frame_support::traits::Get;31use up_sponsorship::SponsorshipHandler;32use sp_std::vec::Vec;3334struct ContractHelpers<T: Config>(SubstrateRecorder<T>);35impl<T: Config> WithRecorder<T> for ContractHelpers<T> {36 fn recorder(&self) -> &SubstrateRecorder<T> {37 &self.038 }3940 fn into_recorder(self) -> SubstrateRecorder<T> {41 self.042 }43}4445#[solidity_interface(name = "ContractHelpers")]46impl<T: Config> ContractHelpers<T>47where48 T::AccountId: AsRef<[u8; 32]>,49{50 51 52 53 54 fn contract_owner(&self, contract_address: address) -> Result<address> {55 Ok(<Owner<T>>::get(contract_address))56 }5758 59 60 61 62 fn set_sponsor(63 &mut self,64 caller: caller,65 contract_address: address,66 sponsor: address,67 ) -> Result<void> {68 self.recorder().consume_sload()?;69 self.recorder().consume_sstore()?;7071 Pallet::<T>::set_sponsor(72 &T::CrossAccountId::from_eth(caller),73 contract_address,74 &T::CrossAccountId::from_eth(sponsor),75 )76 .map_err(dispatch_to_evm::<T>)?;7778 Ok(())79 }8081 82 83 84 fn self_sponsored_enable(&mut self, caller: caller, contract_address: address) -> Result<void> {85 self.recorder().consume_sload()?;86 self.recorder().consume_sstore()?;8788 Pallet::<T>::self_sponsored_enable(&T::CrossAccountId::from_eth(caller), contract_address)89 .map_err(dispatch_to_evm::<T>)?;9091 Ok(())92 }9394 95 96 97 fn remove_sponsor(&mut self, caller: caller, contract_address: address) -> Result<void> {98 self.recorder().consume_sload()?;99 self.recorder().consume_sstore()?;100101 Pallet::<T>::remove_sponsor(&T::CrossAccountId::from_eth(caller), contract_address)102 .map_err(dispatch_to_evm::<T>)?;103104 Ok(())105 }106107 108 109 110 111 112 fn confirm_sponsorship(&mut self, caller: caller, contract_address: address) -> Result<void> {113 self.recorder().consume_sload()?;114 self.recorder().consume_sstore()?;115116 Pallet::<T>::confirm_sponsorship(&T::CrossAccountId::from_eth(caller), contract_address)117 .map_err(dispatch_to_evm::<T>)?;118119 Ok(())120 }121122 123 124 125 126 fn get_sponsor(&self, contract_address: address) -> Result<(address, uint256)> {127 let sponsor =128 Pallet::<T>::get_sponsor(contract_address).ok_or("Contract has no sponsor")?;129 let result: (address, uint256) = if sponsor.is_canonical_substrate() {130 let sponsor = pallet_common::eth::convert_cross_account_to_uint256::<T>(&sponsor);131 (Default::default(), sponsor)132 } else {133 let sponsor = *sponsor.as_eth();134 (sponsor, Default::default())135 };136 Ok(result)137 }138139 140 141 142 143 fn has_sponsor(&self, contract_address: address) -> Result<bool> {144 Ok(Pallet::<T>::get_sponsor(contract_address).is_some())145 }146147 148 149 150 151 fn has_pending_sponsor(&self, contract_address: address) -> Result<bool> {152 Ok(match Sponsoring::<T>::get(contract_address) {153 SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => false,154 SponsorshipState::Unconfirmed(_) => true,155 })156 }157158 fn sponsoring_enabled(&self, contract_address: address) -> Result<bool> {159 Ok(<Pallet<T>>::sponsoring_mode(contract_address) != SponsoringModeT::Disabled)160 }161162 fn set_sponsoring_mode(163 &mut self,164 caller: caller,165 contract_address: address,166 mode: uint8,167 ) -> Result<void> {168 self.recorder().consume_sload()?;169 self.recorder().consume_sstore()?;170171 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;172 let mode = SponsoringModeT::from_eth(mode).ok_or("unknown mode")?;173 <Pallet<T>>::set_sponsoring_mode(contract_address, mode);174175 Ok(())176 }177178 fn sponsoring_mode(&self, contract_address: address) -> Result<uint8> {179 Ok(<Pallet<T>>::sponsoring_mode(contract_address).to_eth())180 }181182 fn set_sponsoring_rate_limit(183 &mut self,184 caller: caller,185 contract_address: address,186 rate_limit: uint32,187 ) -> Result<void> {188 self.recorder().consume_sload()?;189 self.recorder().consume_sstore()?;190191 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;192 <Pallet<T>>::set_sponsoring_rate_limit(contract_address, rate_limit.into());193194 Ok(())195 }196197 fn get_sponsoring_rate_limit(&self, contract_address: address) -> Result<uint32> {198 Ok(<SponsoringRateLimit<T>>::get(contract_address)199 .try_into()200 .map_err(|_| "rate limit > u32::MAX")?)201 }202203 fn allowed(&self, contract_address: address, user: address) -> Result<bool> {204 self.0.consume_sload()?;205 Ok(<Pallet<T>>::allowed(contract_address, user))206 }207208 fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {209 Ok(<AllowlistEnabled<T>>::get(contract_address))210 }211212 fn toggle_allowlist(213 &mut self,214 caller: caller,215 contract_address: address,216 enabled: bool,217 ) -> Result<void> {218 self.recorder().consume_sload()?;219 self.recorder().consume_sstore()?;220221 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;222 <Pallet<T>>::toggle_allowlist(contract_address, enabled);223224 Ok(())225 }226227 fn toggle_allowed(228 &mut self,229 caller: caller,230 contract_address: address,231 user: address,232 allowed: bool,233 ) -> Result<void> {234 self.recorder().consume_sload()?;235 self.recorder().consume_sstore()?;236237 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;238 <Pallet<T>>::toggle_allowed(contract_address, user, allowed);239240 Ok(())241 }242}243244pub struct HelpersOnMethodCall<T: Config>(PhantomData<*const T>);245impl<T: Config> OnMethodCall<T> for HelpersOnMethodCall<T>246where247 T::AccountId: AsRef<[u8; 32]>,248{249 fn is_reserved(contract: &sp_core::H160) -> bool {250 contract == &T::ContractAddress::get()251 }252253 fn is_used(contract: &sp_core::H160) -> bool {254 contract == &T::ContractAddress::get()255 }256257 fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {258 259 if <AllowlistEnabled<T>>::get(handle.code_address())260 && !<Pallet<T>>::allowed(handle.code_address(), handle.context().caller)261 {262 return Some(Err(PrecompileFailure::Revert {263 exit_status: ExitRevert::Reverted,264 output: {265 let mut writer = AbiWriter::new_call(evm_coder::fn_selector!(Error(string)));266 writer.string("Target contract is allowlisted");267 writer.finish()268 },269 }));270 }271272 if handle.code_address() != T::ContractAddress::get() {273 return None;274 }275276 let helpers = ContractHelpers::<T>(SubstrateRecorder::<T>::new(handle.remaining_gas()));277 pallet_evm_coder_substrate::call(handle, helpers)278 }279280 fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {281 (contract == &T::ContractAddress::get())282 .then(|| include_bytes!("./stubs/ContractHelpers.raw").to_vec())283 }284}285286pub struct HelpersOnCreate<T: Config>(PhantomData<*const T>);287impl<T: Config> OnCreate<T> for HelpersOnCreate<T> {288 fn on_create(owner: H160, contract: H160) {289 <Owner<T>>::insert(contract, owner);290 }291}292293pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);294impl<T: Config> SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)>295 for HelpersContractSponsoring<T>296{297 fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec<u8>)) -> Option<T::CrossAccountId> {298 let (contract_address, _) = call;299 let mode = <Pallet<T>>::sponsoring_mode(*contract_address);300 if mode == SponsoringModeT::Disabled {301 return None;302 }303304 let sponsor = match <Pallet<T>>::get_sponsor(*contract_address) {305 Some(sponsor) => sponsor,306 None => return None,307 };308309 if mode == SponsoringModeT::Allowlisted310 && !<Pallet<T>>::allowed(*contract_address, *who.as_eth())311 {312 return None;313 }314 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;315316 if let Some(last_tx_block) = <SponsorBasket<T>>::get(contract_address, who.as_eth()) {317 let limit = <SponsoringRateLimit<T>>::get(contract_address);318319 let timeout = last_tx_block + limit;320 if block_number < timeout {321 return None;322 }323 }324325 <SponsorBasket<T>>::insert(contract_address, who.as_eth(), block_number);326327 Some(sponsor)328 }329}330331generate_stubgen!(contract_helpers_impl, ContractHelpersCall<()>, true);332generate_stubgen!(contract_helpers_iface, ContractHelpersCall<()>, false);