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 frame_support::{pallet_prelude::*, sp_runtime::DispatchResult};36 use frame_system::{pallet_prelude::OriginFor, ensure_root};37 use sp_core::{H160, U256};38 use sp_std::vec::Vec;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 pub struct Pallet<T>(_);7374 75 76 77 78 #[pallet::storage]79 pub(super) type Owner<T: Config> =80 StorageMap<Hasher = Twox128, Key = H160, Value = H160, QueryKind = ValueQuery>;8182 83 #[pallet::storage]84 type SelfSponsoring<T: Config> =85 StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;8687 88 89 90 91 #[pallet::storage]92 pub(super) type Sponsoring<T: Config> = StorageMap<93 Hasher = Twox64Concat,94 Key = H160,95 Value = SponsorshipState<T::CrossAccountId>,96 QueryKind = ValueQuery,97 >;9899 100 101 102 103 104 105 106 #[pallet::storage]107 pub(super) type SponsoringMode<T: Config> =108 StorageMap<Hasher = Twox128, Key = H160, Value = SponsoringModeT, QueryKind = OptionQuery>;109110 111 112 113 114 #[pallet::storage]115 pub(super) type SponsoringRateLimit<T: Config> = StorageMap<116 Hasher = Twox128,117 Key = H160,118 Value = T::BlockNumber,119 QueryKind = ValueQuery,120 OnEmpty = T::DefaultSponsoringRateLimit,121 >;122123 124 125 126 127 128 #[pallet::storage]129 pub(super) type SponsoringFeeLimit<T: Config> = StorageMap<130 Hasher = Twox128,131 Key = H160,132 Value = BoundedBTreeMap<u32, U256, ConstU32<MAX_FEE_LIMITED_METHODS>>,133 QueryKind = ValueQuery,134 >;135136 #[pallet::storage]137 pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<138 Hasher1 = Twox128,139 Key1 = H160,140 Hasher2 = Twox128,141 Key2 = H160,142 Value = T::BlockNumber,143 QueryKind = OptionQuery,144 >;145146 147 148 149 150 151 152 153 #[pallet::storage]154 pub(super) type AllowlistEnabled<T: Config> =155 StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;156157 158 159 160 161 162 163 164 165 #[pallet::storage]166 pub(super) type Allowlist<T: Config> = StorageDoubleMap<167 Hasher1 = Twox128,168 Key1 = H160,169 Hasher2 = Twox128,170 Key2 = H160,171 Value = bool,172 QueryKind = ValueQuery,173 >;174175 #[pallet::event]176 #[pallet::generate_deposit(fn deposit_event)]177 pub enum Event<T: Config> {178 179 ContractSponsorSet(180 181 H160,182 183 T::AccountId,184 ),185186 187 ContractSponsorshipConfirmed(188 189 H160,190 191 T::AccountId,192 ),193194 195 ContractSponsorRemoved(196 197 H160,198 ),199 }200201 #[pallet::call]202 impl<T: Config> Pallet<T> {203 204 #[pallet::call_index(0)]205 #[pallet::weight(<T as frame_system::Config>::DbWeight::get().reads_writes(206 addresses.len() as u64, addresses.len() as u64 * 2207 ))]208 pub fn migrate_from_self_sponsoring(209 origin: OriginFor<T>,210 addresses: Vec<H160>,211 ) -> DispatchResult {212 ensure_root(origin)?;213 for address in addresses {214 if <SelfSponsoring<T>>::get(address) {215 <SponsoringMode<T>>::set(address, Some(SponsoringModeT::Allowlisted));216 <SelfSponsoring<T>>::remove(address);217 }218 }219 Ok(())220 }221 }222223 impl<T: Config> Pallet<T> {224 225 pub fn contract_owner(contract: H160) -> H160 {226 <Owner<T>>::get(contract)227 }228229 230 231 232 pub fn set_sponsor(233 sender: &T::CrossAccountId,234 contract: H160,235 sponsor: &T::CrossAccountId,236 ) -> DispatchResult {237 Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;238 Sponsoring::<T>::insert(239 contract,240 SponsorshipState::<T::CrossAccountId>::Unconfirmed(sponsor.clone()),241 );242243 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(244 contract,245 sponsor.as_sub().clone(),246 ));247 <PalletEvm<T>>::deposit_log(248 ContractHelpersEvents::ContractSponsorSet {249 contract_address: contract,250 sponsor: *sponsor.as_eth(),251 }252 .to_log(contract),253 );254 Ok(())255 }256257 258 259 260 261 pub fn force_set_sponsor(262 contract_address: H160,263 sponsor: &T::CrossAccountId,264 ) -> DispatchResult {265 Sponsoring::<T>::insert(266 contract_address,267 SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor.clone()),268 );269270 let eth_sponsor = *sponsor.as_eth();271 let sub_sponsor = sponsor.as_sub().clone();272273 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(274 contract_address,275 sub_sponsor.clone(),276 ));277 <PalletEvm<T>>::deposit_log(278 ContractHelpersEvents::ContractSponsorSet {279 contract_address,280 sponsor: eth_sponsor,281 }282 .to_log(contract_address),283 );284285 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(286 contract_address,287 sub_sponsor,288 ));289 <PalletEvm<T>>::deposit_log(290 ContractHelpersEvents::ContractSponsorshipConfirmed {291 contract_address,292 sponsor: eth_sponsor,293 }294 .to_log(contract_address),295 );296297 Ok(())298 }299300 301 302 303 pub fn remove_sponsor(304 sender: &T::CrossAccountId,305 contract_address: H160,306 ) -> DispatchResult {307 Self::ensure_owner(contract_address, *sender.as_eth())?;308 Self::force_remove_sponsor(contract_address)309 }310311 312 313 314 315 pub fn force_remove_sponsor(contract_address: H160) -> DispatchResult {316 Sponsoring::<T>::remove(contract_address);317318 Self::deposit_event(Event::<T>::ContractSponsorRemoved(contract_address));319 <PalletEvm<T>>::deposit_log(320 ContractHelpersEvents::ContractSponsorRemoved { contract_address }321 .to_log(contract_address),322 );323324 Ok(())325 }326327 328 329 330 pub fn confirm_sponsorship(331 sender: &T::CrossAccountId,332 contract_address: H160,333 ) -> DispatchResult {334 match Sponsoring::<T>::get(contract_address) {335 SponsorshipState::Unconfirmed(sponsor) => {336 ensure!(sponsor == *sender, Error::<T>::NoPermission);337 let eth_sponsor = *sponsor.as_eth();338 let sub_sponsor = sponsor.as_sub().clone();339 Sponsoring::<T>::insert(340 contract_address,341 SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor),342 );343344 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(345 contract_address,346 sub_sponsor,347 ));348 <PalletEvm<T>>::deposit_log(349 ContractHelpersEvents::ContractSponsorshipConfirmed {350 contract_address,351 sponsor: eth_sponsor,352 }353 .to_log(contract_address),354 );355356 Ok(())357 }358 SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => {359 Err(Error::<T>::NoPendingSponsor.into())360 }361 }362 }363364 365 pub fn get_sponsor(contract: H160) -> Option<T::CrossAccountId> {366 match Sponsoring::<T>::get(contract) {367 SponsorshipState::Disabled | SponsorshipState::Unconfirmed(_) => None,368 SponsorshipState::Confirmed(sponsor) => Some(sponsor),369 }370 }371372 373 374 pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {375 <SponsoringMode<T>>::get(contract)376 .or_else(|| {377 #[allow(deprecated)]378 <SelfSponsoring<T>>::get(contract).then_some(SponsoringModeT::Allowlisted)379 })380 .unwrap_or_default()381 }382383 384 385 pub fn set_sponsoring_mode(contract: H160, mode: SponsoringModeT) {386 if mode == SponsoringModeT::Disabled {387 <SponsoringMode<T>>::remove(contract);388 } else {389 <SponsoringMode<T>>::insert(contract, mode);390 }391 #[allow(deprecated)]392 <SelfSponsoring<T>>::remove(contract)393 }394395 396 pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: T::BlockNumber) {397 <SponsoringRateLimit<T>>::insert(contract, rate_limit);398 }399400 401 pub fn set_sponsoring_fee_limit(contract: H160, fee_limit: U256) -> DispatchResult {402 <SponsoringFeeLimit<T>>::try_mutate(contract, |limits_map| {403 limits_map404 .try_insert(0xffffffff, fee_limit)405 .map_err(|_| <Error<T>>::TooManyMethodsHaveSponsoredLimit)406 })?;407 Ok(())408 }409410 411 pub fn allowed(contract: H160, user: H160) -> bool {412 <Allowlist<T>>::get(contract, user) || <Owner<T>>::get(contract) == user413 }414415 416 pub fn toggle_allowlist(contract: H160, enabled: bool) {417 <AllowlistEnabled<T>>::insert(contract, enabled)418 }419420 421 pub fn toggle_allowed(contract: H160, user: H160, allowed: bool) {422 <Allowlist<T>>::insert(contract, user, allowed);423 }424425 426 pub fn ensure_owner(contract: H160, user: H160) -> DispatchResult {427 ensure!(<Owner<T>>::get(contract) == user, Error::<T>::NoPermission);428 Ok(())429 }430 }431}432433434#[derive(435 Encode, Decode, Debug, PartialEq, TypeInfo, MaxEncodedLen, Default, AbiCoder, Clone, Copy,436)]437#[repr(u8)]438pub enum SponsoringModeT {439 440 #[default]441 Disabled,442 443 Allowlisted,444 445 Generous,446}