12345678910111213141516171819extern crate alloc;20use alloc::{format, string::ToString};21use core::marker::PhantomData;22use evm_coder::{23 abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*, ToLog,24};25use pallet_evm::{26 ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, PrecompileHandle,27 account::CrossAccountId,28};29use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder, dispatch_to_evm};30use pallet_evm_transaction_payment::CallContext;31use sp_core::{H160, U256};32use up_data_structs::SponsorshipState;33use crate::{34 AllowlistEnabled, Config, Owner, Pallet, SponsorBasket, SponsoringFeeLimit,35 SponsoringRateLimit, SponsoringModeT, Sponsoring,36};37use frame_support::traits::Get;38use up_sponsorship::SponsorshipHandler;39use sp_std::vec::Vec;404142#[derive(ToLog)]43pub enum ContractHelpersEvents {44 45 ContractSponsorSet {46 47 #[indexed]48 contract_address: address,49 50 sponsor: address,51 },5253 54 ContractSponsorshipConfirmed {55 56 #[indexed]57 contract_address: address,58 59 sponsor: address,60 },6162 63 ContractSponsorRemoved {64 65 #[indexed]66 contract_address: address,67 },68}697071pub struct ContractHelpers<T: Config>(SubstrateRecorder<T>);72impl<T: Config> WithRecorder<T> for ContractHelpers<T> {73 fn recorder(&self) -> &SubstrateRecorder<T> {74 &self.075 }7677 fn into_recorder(self) -> SubstrateRecorder<T> {78 self.079 }80}818283#[solidity_interface(name = ContractHelpers, events(ContractHelpersEvents))]84impl<T: Config> ContractHelpers<T>85where86 T::AccountId: AsRef<[u8; 32]>,87{88 89 90 91 92 93 94 95 fn contract_owner(&self, contract_address: address) -> Result<address> {96 Ok(<Owner<T>>::get(contract_address))97 }9899 100 101 102 fn set_sponsor(103 &mut self,104 caller: caller,105 contract_address: address,106 sponsor: address,107 ) -> Result<void> {108 self.recorder().consume_sload()?;109 self.recorder().consume_sstore()?;110111 Pallet::<T>::set_sponsor(112 &T::CrossAccountId::from_eth(caller),113 contract_address,114 &T::CrossAccountId::from_eth(sponsor),115 )116 .map_err(dispatch_to_evm::<T>)?;117118 Ok(())119 }120121 122 123 124 fn self_sponsored_enable(&mut self, caller: caller, contract_address: address) -> Result<void> {125 self.recorder().consume_sload()?;126 self.recorder().consume_sstore()?;127128 let caller = T::CrossAccountId::from_eth(caller);129130 Pallet::<T>::ensure_owner(contract_address, *caller.as_eth())131 .map_err(dispatch_to_evm::<T>)?;132133 Pallet::<T>::force_set_sponsor(134 contract_address,135 &T::CrossAccountId::from_eth(contract_address),136 )137 .map_err(dispatch_to_evm::<T>)?;138139 Ok(())140 }141142 143 144 145 fn remove_sponsor(&mut self, caller: caller, contract_address: address) -> Result<void> {146 self.recorder().consume_sload()?;147 self.recorder().consume_sstore()?;148149 Pallet::<T>::remove_sponsor(&T::CrossAccountId::from_eth(caller), contract_address)150 .map_err(dispatch_to_evm::<T>)?;151152 Ok(())153 }154155 156 157 158 159 160 fn confirm_sponsorship(&mut self, caller: caller, contract_address: address) -> Result<void> {161 self.recorder().consume_sload()?;162 self.recorder().consume_sstore()?;163164 Pallet::<T>::confirm_sponsorship(&T::CrossAccountId::from_eth(caller), contract_address)165 .map_err(dispatch_to_evm::<T>)?;166167 Ok(())168 }169170 171 172 173 174 fn sponsor(&self, contract_address: address) -> Result<(address, uint256)> {175 let sponsor =176 Pallet::<T>::get_sponsor(contract_address).ok_or("Contract has no sponsor")?;177 Ok(pallet_common::eth::convert_cross_account_to_tuple::<T>(178 &sponsor,179 ))180 }181182 183 184 185 186 fn has_sponsor(&self, contract_address: address) -> Result<bool> {187 Ok(Pallet::<T>::get_sponsor(contract_address).is_some())188 }189190 191 192 193 194 fn has_pending_sponsor(&self, contract_address: address) -> Result<bool> {195 Ok(match Sponsoring::<T>::get(contract_address) {196 SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => false,197 SponsorshipState::Unconfirmed(_) => true,198 })199 }200201 fn sponsoring_enabled(&self, contract_address: address) -> Result<bool> {202 Ok(<Pallet<T>>::sponsoring_mode(contract_address) != SponsoringModeT::Disabled)203 }204205 fn set_sponsoring_mode(206 &mut self,207 caller: caller,208 contract_address: address,209 210 mode: uint8,211 ) -> Result<void> {212 self.recorder().consume_sload()?;213 self.recorder().consume_sstore()?;214215 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;216 let mode = SponsoringModeT::from_eth(mode).ok_or("unknown mode")?;217 <Pallet<T>>::set_sponsoring_mode(contract_address, mode);218219 Ok(())220 }221222 223 224 225 fn sponsoring_rate_limit(&self, contract_address: address) -> Result<uint32> {226 self.recorder().consume_sload()?;227228 Ok(<SponsoringRateLimit<T>>::get(contract_address)229 .try_into()230 .map_err(|_| "rate limit > u32::MAX")?)231 }232233 234 235 236 237 238 239 fn set_sponsoring_rate_limit(240 &mut self,241 caller: caller,242 contract_address: address,243 rate_limit: uint32,244 ) -> Result<void> {245 self.recorder().consume_sload()?;246 self.recorder().consume_sstore()?;247248 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;249 <Pallet<T>>::set_sponsoring_rate_limit(contract_address, rate_limit.into());250 Ok(())251 }252253 254 255 256 257 258 259 fn set_sponsoring_fee_limit(260 &mut self,261 caller: caller,262 contract_address: address,263 fee_limit: uint256,264 ) -> Result<void> {265 self.recorder().consume_sload()?;266 self.recorder().consume_sstore()?;267268 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;269 <Pallet<T>>::set_sponsoring_fee_limit(contract_address, fee_limit.into())270 .map_err(dispatch_to_evm::<T>)?;271 Ok(())272 }273274 275 276 277 278 fn sponsoring_fee_limit(&self, contract_address: address) -> Result<uint256> {279 self.recorder().consume_sload()?;280281 Ok(get_sponsoring_fee_limit::<T>(contract_address))282 }283284 285 286 287 288 289 fn allowed(&self, contract_address: address, user: address) -> Result<bool> {290 self.0.consume_sload()?;291 Ok(<Pallet<T>>::allowed(contract_address, user))292 }293294 295 296 297 298 299 300 fn toggle_allowed(301 &mut self,302 caller: caller,303 contract_address: address,304 user: address,305 is_allowed: bool,306 ) -> Result<void> {307 self.recorder().consume_sload()?;308 self.recorder().consume_sstore()?;309310 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;311 <Pallet<T>>::toggle_allowed(contract_address, user, is_allowed);312313 Ok(())314 }315316 317 318 319 320 321 322 fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {323 Ok(<AllowlistEnabled<T>>::get(contract_address))324 }325326 327 328 329 fn toggle_allowlist(330 &mut self,331 caller: caller,332 contract_address: address,333 enabled: bool,334 ) -> Result<void> {335 self.recorder().consume_sload()?;336 self.recorder().consume_sstore()?;337338 <Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;339 <Pallet<T>>::toggle_allowlist(contract_address, enabled);340 Ok(())341 }342}343344345pub struct HelpersOnMethodCall<T: Config>(PhantomData<*const T>);346impl<T: Config> OnMethodCall<T> for HelpersOnMethodCall<T>347where348 T::AccountId: AsRef<[u8; 32]>,349{350 fn is_reserved(contract: &sp_core::H160) -> bool {351 contract == &T::ContractAddress::get()352 }353354 fn is_used(contract: &sp_core::H160) -> bool {355 contract == &T::ContractAddress::get()356 }357358 fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {359 360 if <AllowlistEnabled<T>>::get(handle.code_address())361 && !<Pallet<T>>::allowed(handle.code_address(), handle.context().caller)362 {363 return Some(Err(PrecompileFailure::Revert {364 exit_status: ExitRevert::Reverted,365 output: {366 let mut writer = AbiWriter::new_call(evm_coder::fn_selector!(Error(string)));367 writer.string("Target contract is allowlisted");368 writer.finish()369 },370 }));371 }372373 if handle.code_address() != T::ContractAddress::get() {374 return None;375 }376377 let helpers = ContractHelpers::<T>(SubstrateRecorder::<T>::new(handle.remaining_gas()));378 pallet_evm_coder_substrate::call(handle, helpers)379 }380381 fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {382 (contract == &T::ContractAddress::get())383 .then(|| include_bytes!("./stubs/ContractHelpers.raw").to_vec())384 }385}386387388pub struct HelpersOnCreate<T: Config>(PhantomData<*const T>);389impl<T: Config> OnCreate<T> for HelpersOnCreate<T> {390 fn on_create(owner: H160, contract: H160) {391 <Owner<T>>::insert(contract, owner);392 }393}394395396pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);397impl<T: Config> SponsorshipHandler<T::CrossAccountId, CallContext>398 for HelpersContractSponsoring<T>399{400 fn get_sponsor(401 who: &T::CrossAccountId,402 call_context: &CallContext,403 ) -> Option<T::CrossAccountId> {404 let contract_address = call_context.contract_address;405 let mode = <Pallet<T>>::sponsoring_mode(contract_address);406 if mode == SponsoringModeT::Disabled {407 return None;408 }409410 let sponsor = match <Pallet<T>>::get_sponsor(contract_address) {411 Some(sponsor) => sponsor,412 None => return None,413 };414415 if mode == SponsoringModeT::Allowlisted416 && !<Pallet<T>>::allowed(contract_address, *who.as_eth())417 {418 return None;419 }420 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;421422 if let Some(last_tx_block) = <SponsorBasket<T>>::get(contract_address, who.as_eth()) {423 let limit = <SponsoringRateLimit<T>>::get(contract_address);424425 let timeout = last_tx_block + limit;426 if block_number < timeout {427 return None;428 }429 }430431 let sponsored_fee_limit = get_sponsoring_fee_limit::<T>(contract_address);432433 if call_context.max_fee > sponsored_fee_limit {434 return None;435 }436437 <SponsorBasket<T>>::insert(contract_address, who.as_eth(), block_number);438439 Some(sponsor)440 }441}442443fn get_sponsoring_fee_limit<T: Config>(contract_address: address) -> uint256 {444 <SponsoringFeeLimit<T>>::get(contract_address)445 .get(&0xffffffff)446 .cloned()447 .unwrap_or(U256::MAX)448}449450generate_stubgen!(contract_helpers_impl, ContractHelpersCall<()>, true);451generate_stubgen!(contract_helpers_iface, ContractHelpersCall<()>, false);