1234567891011121314151617#![doc = include_str!("../README.md")]18#![cfg_attr(not(feature = "std"), no_std)]19#![warn(missing_docs)]2021use codec::{Decode, Encode, MaxEncodedLen};22use evm_coder::AbiCoder;23pub use pallet::*;24pub use eth::*;25use scale_info::TypeInfo;26use frame_support::storage::bounded_btree_map::BoundedBTreeMap;27pub mod eth;282930pub const MAX_FEE_LIMITED_METHODS: u32 = 5;3132#[frame_support::pallet]33pub mod pallet {34 pub use super::*;35 use crate::eth::ContractHelpersEvents;36 use frame_support::pallet_prelude::*;37 use pallet_evm_coder_substrate::DispatchResult;38 use sp_core::{H160, U256};39 use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};40 use up_data_structs::SponsorshipState;41 use evm_coder::ToLog;4243 #[pallet::config]44 pub trait Config:45 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config46 {47 48 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;4950 51 #[pallet::constant]52 type ContractAddress: Get<H160>;5354 55 56 type DefaultSponsoringRateLimit: Get<Self::BlockNumber>;57 }5859 #[pallet::error]60 pub enum Error<T> {61 62 NoPermission,6364 65 NoPendingSponsor,6667 68 TooManyMethodsHaveSponsoredLimit,69 }7071 #[pallet::pallet]72 #[pallet::generate_store(trait Store)]73 pub struct Pallet<T>(_);7475 76 77 78 79 #[pallet::storage]80 pub(super) type Owner<T: Config> =81 StorageMap<Hasher = Twox128, Key = H160, Value = H160, QueryKind = ValueQuery>;8283 84 #[pallet::storage]85 type SelfSponsoring<T: Config> =86 StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;8788 89 90 91 92 #[pallet::storage]93 pub(super) type Sponsoring<T: Config> = StorageMap<94 Hasher = Twox64Concat,95 Key = H160,96 Value = SponsorshipState<T::CrossAccountId>,97 QueryKind = ValueQuery,98 >;99100 101 102 103 104 105 106 107 #[pallet::storage]108 pub(super) type SponsoringMode<T: Config> =109 StorageMap<Hasher = Twox128, Key = H160, Value = SponsoringModeT, QueryKind = OptionQuery>;110111 112 113 114 115 #[pallet::storage]116 pub(super) type SponsoringRateLimit<T: Config> = StorageMap<117 Hasher = Twox128,118 Key = H160,119 Value = T::BlockNumber,120 QueryKind = ValueQuery,121 OnEmpty = T::DefaultSponsoringRateLimit,122 >;123124 125 126 127 128 129 #[pallet::storage]130 pub(super) type SponsoringFeeLimit<T: Config> = StorageMap<131 Hasher = Twox128,132 Key = H160,133 Value = BoundedBTreeMap<u32, U256, ConstU32<MAX_FEE_LIMITED_METHODS>>,134 QueryKind = ValueQuery,135 >;136137 #[pallet::storage]138 pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<139 Hasher1 = Twox128,140 Key1 = H160,141 Hasher2 = Twox128,142 Key2 = H160,143 Value = T::BlockNumber,144 QueryKind = OptionQuery,145 >;146147 148 149 150 151 152 153 154 #[pallet::storage]155 pub(super) type AllowlistEnabled<T: Config> =156 StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;157158 159 160 161 162 163 164 165 166 #[pallet::storage]167 pub(super) type Allowlist<T: Config> = StorageDoubleMap<168 Hasher1 = Twox128,169 Key1 = H160,170 Hasher2 = Twox128,171 Key2 = H160,172 Value = bool,173 QueryKind = ValueQuery,174 >;175176 #[pallet::event]177 #[pallet::generate_deposit(fn deposit_event)]178 pub enum Event<T: Config> {179 180 ContractSponsorSet(181 182 H160,183 184 T::AccountId,185 ),186187 188 ContractSponsorshipConfirmed(189 190 H160,191 192 T::AccountId,193 ),194195 196 ContractSponsorRemoved(197 198 H160,199 ),200 }201202 impl<T: Config> Pallet<T> {203 204 pub fn contract_owner(contract: H160) -> H160 {205 <Owner<T>>::get(contract)206 }207208 209 210 211 pub fn set_sponsor(212 sender: &T::CrossAccountId,213 contract: H160,214 sponsor: &T::CrossAccountId,215 ) -> DispatchResult {216 Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;217 Sponsoring::<T>::insert(218 contract,219 SponsorshipState::<T::CrossAccountId>::Unconfirmed(sponsor.clone()),220 );221222 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(223 contract,224 sponsor.as_sub().clone(),225 ));226 <PalletEvm<T>>::deposit_log(227 ContractHelpersEvents::ContractSponsorSet {228 contract_address: contract,229 sponsor: *sponsor.as_eth(),230 }231 .to_log(contract),232 );233 Ok(())234 }235236 237 238 239 240 pub fn force_set_sponsor(241 contract_address: H160,242 sponsor: &T::CrossAccountId,243 ) -> DispatchResult {244 Sponsoring::<T>::insert(245 contract_address,246 SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor.clone()),247 );248249 let eth_sponsor = *sponsor.as_eth();250 let sub_sponsor = sponsor.as_sub().clone();251252 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(253 contract_address,254 sub_sponsor.clone(),255 ));256 <PalletEvm<T>>::deposit_log(257 ContractHelpersEvents::ContractSponsorSet {258 contract_address,259 sponsor: eth_sponsor,260 }261 .to_log(contract_address),262 );263264 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(265 contract_address,266 sub_sponsor,267 ));268 <PalletEvm<T>>::deposit_log(269 ContractHelpersEvents::ContractSponsorshipConfirmed {270 contract_address,271 sponsor: eth_sponsor,272 }273 .to_log(contract_address),274 );275276 Ok(())277 }278279 280 281 282 pub fn remove_sponsor(283 sender: &T::CrossAccountId,284 contract_address: H160,285 ) -> DispatchResult {286 Self::ensure_owner(contract_address, *sender.as_eth())?;287 Self::force_remove_sponsor(contract_address)288 }289290 291 292 293 294 pub fn force_remove_sponsor(contract_address: H160) -> DispatchResult {295 Sponsoring::<T>::remove(contract_address);296297 Self::deposit_event(Event::<T>::ContractSponsorRemoved(contract_address));298 <PalletEvm<T>>::deposit_log(299 ContractHelpersEvents::ContractSponsorRemoved { contract_address }300 .to_log(contract_address),301 );302303 Ok(())304 }305306 307 308 309 pub fn confirm_sponsorship(310 sender: &T::CrossAccountId,311 contract_address: H160,312 ) -> DispatchResult {313 match Sponsoring::<T>::get(contract_address) {314 SponsorshipState::Unconfirmed(sponsor) => {315 ensure!(sponsor == *sender, Error::<T>::NoPermission);316 let eth_sponsor = *sponsor.as_eth();317 let sub_sponsor = sponsor.as_sub().clone();318 Sponsoring::<T>::insert(319 contract_address,320 SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor),321 );322323 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(324 contract_address,325 sub_sponsor,326 ));327 <PalletEvm<T>>::deposit_log(328 ContractHelpersEvents::ContractSponsorshipConfirmed {329 contract_address,330 sponsor: eth_sponsor,331 }332 .to_log(contract_address),333 );334335 Ok(())336 }337 SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => {338 Err(Error::<T>::NoPendingSponsor.into())339 }340 }341 }342343 344 pub fn get_sponsor(contract: H160) -> Option<T::CrossAccountId> {345 match Sponsoring::<T>::get(contract) {346 SponsorshipState::Disabled | SponsorshipState::Unconfirmed(_) => None,347 SponsorshipState::Confirmed(sponsor) => Some(sponsor),348 }349 }350351 352 353 pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {354 <SponsoringMode<T>>::get(contract)355 .or_else(|| {356 #[allow(deprecated)]357 <SelfSponsoring<T>>::get(contract).then(|| SponsoringModeT::Allowlisted)358 })359 .unwrap_or_default()360 }361362 363 364 pub fn set_sponsoring_mode(contract: H160, mode: SponsoringModeT) {365 if mode == SponsoringModeT::Disabled {366 <SponsoringMode<T>>::remove(contract);367 } else {368 <SponsoringMode<T>>::insert(contract, mode);369 }370 #[allow(deprecated)]371 <SelfSponsoring<T>>::remove(contract)372 }373374 375 pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: T::BlockNumber) {376 <SponsoringRateLimit<T>>::insert(contract, rate_limit);377 }378379 380 pub fn set_sponsoring_fee_limit(contract: H160, fee_limit: U256) -> DispatchResult {381 <SponsoringFeeLimit<T>>::try_mutate(contract, |limits_map| {382 limits_map383 .try_insert(0xffffffff, fee_limit)384 .map_err(|_| <Error<T>>::TooManyMethodsHaveSponsoredLimit)385 })?;386 Ok(())387 }388389 390 pub fn allowed(contract: H160, user: H160) -> bool {391 <Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user392 }393394 395 pub fn toggle_allowlist(contract: H160, enabled: bool) {396 <AllowlistEnabled<T>>::insert(contract, enabled)397 }398399 400 pub fn toggle_allowed(contract: H160, user: H160, allowed: bool) {401 <Allowlist<T>>::insert(contract, user, allowed);402 }403404 405 pub fn ensure_owner(contract: H160, user: H160) -> DispatchResult {406 ensure!(<Owner<T>>::get(&contract) == user, Error::<T>::NoPermission);407 Ok(())408 }409 }410}411412413#[derive(414 Encode, Decode, Debug, PartialEq, TypeInfo, MaxEncodedLen, Default, AbiCoder, Clone, Copy,415)]416#[repr(u8)]417pub enum SponsoringModeT {418 419 #[default]420 Disabled,421 422 Allowlisted,423 424 Generous,425}