1234567891011121314151617#![doc = include_str!("../README.md")]18#![cfg_attr(not(feature = "std"), no_std)]19#![warn(missing_docs)]2021use codec::{Decode, Encode, MaxEncodedLen};22pub use pallet::*;23pub use eth::*;24use scale_info::TypeInfo;25pub mod eth;2627#[frame_support::pallet]28pub mod pallet {29 pub use super::*;30 use crate::eth::ContractHelpersEvents;31 use frame_support::pallet_prelude::*;32 use pallet_evm_coder_substrate::DispatchResult;33 use sp_core::H160;34 use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};35 use up_data_structs::SponsorshipState;36 use evm_coder::ToLog;3738 #[pallet::config]39 pub trait Config:40 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::account::Config41 {42 43 type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;4445 46 type ContractAddress: Get<H160>;4748 49 50 type DefaultSponsoringRateLimit: Get<Self::BlockNumber>;51 }5253 #[pallet::error]54 pub enum Error<T> {55 56 NoPermission,5758 59 NoPendingSponsor,60 }6162 #[pallet::pallet]63 #[pallet::generate_store(pub(super) trait Store)]64 pub struct Pallet<T>(_);6566 67 68 69 70 #[pallet::storage]71 pub(super) type Owner<T: Config> =72 StorageMap<Hasher = Twox128, Key = H160, Value = H160, QueryKind = ValueQuery>;7374 #[pallet::storage]75 #[deprecated]76 pub(super) type SelfSponsoring<T: Config> =77 StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;7879 80 81 82 83 #[pallet::storage]84 pub(super) type Sponsoring<T: Config> = StorageMap<85 Hasher = Twox64Concat,86 Key = H160,87 Value = SponsorshipState<T::CrossAccountId>,88 QueryKind = ValueQuery,89 >;9091 92 93 94 95 96 97 98 #[pallet::storage]99 pub(super) type SponsoringMode<T: Config> =100 StorageMap<Hasher = Twox128, Key = H160, Value = SponsoringModeT, QueryKind = OptionQuery>;101102 103 104 105 106 #[pallet::storage]107 pub(super) type SponsoringRateLimit<T: Config> = StorageMap<108 Hasher = Twox128,109 Key = H160,110 Value = T::BlockNumber,111 QueryKind = ValueQuery,112 OnEmpty = T::DefaultSponsoringRateLimit,113 >;114115 116 117 118 119 120 #[pallet::storage]121 pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<122 Hasher1 = Twox128,123 Key1 = H160,124 Hasher2 = Twox128,125 Key2 = H160,126 Value = T::BlockNumber,127 QueryKind = OptionQuery,128 >;129130 131 132 133 134 135 136 137 #[pallet::storage]138 pub(super) type AllowlistEnabled<T: Config> =139 StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;140141 142 143 144 145 146 147 148 149 #[pallet::storage]150 pub(super) type Allowlist<T: Config> = StorageDoubleMap<151 Hasher1 = Twox128,152 Key1 = H160,153 Hasher2 = Twox128,154 Key2 = H160,155 Value = bool,156 QueryKind = ValueQuery,157 >;158159 #[pallet::event]160 #[pallet::generate_deposit(pub fn deposit_event)]161 pub enum Event<T: Config> {162 163 ContractSponsorSet(164 165 H160,166 167 T::AccountId,168 ),169170 171 ContractSponsorshipConfirmed(172 173 H160,174 175 T::AccountId,176 ),177178 179 ContractSponsorRemoved(180 181 H160,182 ),183 }184185 impl<T: Config> Pallet<T> {186 187 pub fn contract_owner(contract: H160) -> H160 {188 <Owner<T>>::get(contract)189 }190191 192 193 194 pub fn set_sponsor(195 sender: &T::CrossAccountId,196 contract: H160,197 sponsor: &T::CrossAccountId,198 ) -> DispatchResult {199 Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;200 Sponsoring::<T>::insert(201 contract,202 SponsorshipState::<T::CrossAccountId>::Unconfirmed(sponsor.clone()),203 );204205 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(206 contract,207 sponsor.as_sub().clone(),208 ));209 <PalletEvm<T>>::deposit_log(210 ContractHelpersEvents::ContractSponsorSet {211 contract_address: contract,212 sponsor: *sponsor.as_eth(),213 }214 .to_log(contract),215 );216 Ok(())217 }218219 220 221 222 pub fn force_set_sponsor(223 contract_address: H160,224 sponsor: &T::CrossAccountId,225 ) -> DispatchResult {226 Sponsoring::<T>::insert(227 contract_address,228 SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor.clone()),229 );230231 let eth_sponsor = *sponsor.as_eth();232 let sub_sponsor = sponsor.as_sub().clone();233234 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(235 contract_address,236 sub_sponsor.clone(),237 ));238 <PalletEvm<T>>::deposit_log(239 ContractHelpersEvents::ContractSponsorSet {240 contract_address,241 sponsor: eth_sponsor,242 }243 .to_log(contract_address),244 );245246 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(247 contract_address,248 sub_sponsor,249 ));250 <PalletEvm<T>>::deposit_log(251 ContractHelpersEvents::ContractSponsorshipConfirmed {252 contract_address,253 sponsor: eth_sponsor,254 }255 .to_log(contract_address),256 );257258 Ok(())259 }260261 262 263 264 pub fn remove_sponsor(265 sender: &T::CrossAccountId,266 contract_address: H160,267 ) -> DispatchResult {268 Self::ensure_owner(contract_address, *sender.as_eth())?;269 Self::force_remove_sponsor(contract_address)270 }271272 273 274 275 pub fn force_remove_sponsor(contract_address: H160) -> DispatchResult {276 Sponsoring::<T>::remove(contract_address);277278 Self::deposit_event(Event::<T>::ContractSponsorRemoved(contract_address));279 <PalletEvm<T>>::deposit_log(280 ContractHelpersEvents::ContractSponsorRemoved { contract_address }281 .to_log(contract_address),282 );283284 Ok(())285 }286287 288 289 290 pub fn confirm_sponsorship(291 sender: &T::CrossAccountId,292 contract_address: H160,293 ) -> DispatchResult {294 match Sponsoring::<T>::get(contract_address) {295 SponsorshipState::Unconfirmed(sponsor) => {296 ensure!(sponsor == *sender, Error::<T>::NoPermission);297 let eth_sponsor = *sponsor.as_eth();298 let sub_sponsor = sponsor.as_sub().clone();299 Sponsoring::<T>::insert(300 contract_address,301 SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor),302 );303304 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(305 contract_address,306 sub_sponsor,307 ));308 <PalletEvm<T>>::deposit_log(309 ContractHelpersEvents::ContractSponsorshipConfirmed {310 contract_address,311 sponsor: eth_sponsor,312 }313 .to_log(contract_address),314 );315316 Ok(())317 }318 SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => {319 Err(Error::<T>::NoPendingSponsor.into())320 }321 }322 }323324 325 pub fn get_sponsor(contract: H160) -> Option<T::CrossAccountId> {326 match Sponsoring::<T>::get(contract) {327 SponsorshipState::Disabled | SponsorshipState::Unconfirmed(_) => None,328 SponsorshipState::Confirmed(sponsor) => Some(sponsor),329 }330 }331332 333 pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {334 <SponsoringMode<T>>::get(contract)335 .or_else(|| {336 <SelfSponsoring<T>>::get(contract).then(|| SponsoringModeT::Allowlisted)337 })338 .unwrap_or_default()339 }340341 342 pub fn set_sponsoring_mode(contract: H160, mode: SponsoringModeT) {343 if mode == SponsoringModeT::Disabled {344 <SponsoringMode<T>>::remove(contract);345 } else {346 <SponsoringMode<T>>::insert(contract, mode);347 }348 <SelfSponsoring<T>>::remove(contract)349 }350351 352 pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: T::BlockNumber) {353 <SponsoringRateLimit<T>>::insert(contract, rate_limit);354 }355356 357 pub fn allowed(contract: H160, user: H160) -> bool {358 <Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user359 }360361 362 pub fn toggle_allowlist(contract: H160, enabled: bool) {363 <AllowlistEnabled<T>>::insert(contract, enabled)364 }365366 367 pub fn toggle_allowed(contract: H160, user: H160, allowed: bool) {368 <Allowlist<T>>::insert(contract, user, allowed);369 }370371 372 pub fn ensure_owner(contract: H160, user: H160) -> DispatchResult {373 ensure!(<Owner<T>>::get(&contract) == user, Error::<T>::NoPermission);374 Ok(())375 }376 }377}378379380#[derive(Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen, Default)]381pub enum SponsoringModeT {382 383 #[default]384 Disabled,385 386 Allowlisted,387 388 Generous,389}390391impl SponsoringModeT {392 fn from_eth(v: u8) -> Option<Self> {393 Some(match v {394 0 => Self::Disabled,395 1 => Self::Allowlisted,396 2 => Self::Generous,397 _ => return None,398 })399 }400 fn to_eth(self) -> u8 {401 match self {402 SponsoringModeT::Disabled => 0,403 SponsoringModeT::Allowlisted => 1,404 SponsoringModeT::Generous => 2,405 }406 }407}