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 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,212 sponsor: *sponsor.as_eth(),213 }214 .to_log(contract),215 );216 Ok(())217 }218219 220 221 222 pub fn force_set_sponsor(223 sender: &T::CrossAccountId,224 contract: H160,225 sponsor: &T::CrossAccountId,226 ) -> DispatchResult {227 Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;228 Sponsoring::<T>::insert(229 contract,230 SponsorshipState::<T::CrossAccountId>::Confirmed(T::CrossAccountId::from_eth(231 contract,232 )),233 );234235 let eth_sponsor = *sponsor.as_eth();236 let sub_sponsor = sponsor.as_sub().clone();237238 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(239 contract,240 sub_sponsor.clone(),241 ));242 <PalletEvm<T>>::deposit_log(243 ContractHelpersEvents::ContractSponsorSet {244 contract,245 sponsor: eth_sponsor,246 }247 .to_log(contract),248 );249250 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(251 contract,252 sub_sponsor,253 ));254 <PalletEvm<T>>::deposit_log(255 ContractHelpersEvents::ContractSponsorshipConfirmed {256 contract,257 sponsor: eth_sponsor,258 }259 .to_log(contract),260 );261262 Ok(())263 }264265 266 267 268 pub fn remove_sponsor(sender: &T::CrossAccountId, contract: H160) -> DispatchResult {269 Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;270 Sponsoring::<T>::remove(contract);271272 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorRemoved(contract));273 <PalletEvm<T>>::deposit_log(274 ContractHelpersEvents::ContractSponsorRemoved { contract }.to_log(contract),275 );276277 Ok(())278 }279280 281 282 283 pub fn confirm_sponsorship(sender: &T::CrossAccountId, contract: H160) -> DispatchResult {284 match Sponsoring::<T>::get(contract) {285 SponsorshipState::Unconfirmed(sponsor) => {286 ensure!(sponsor == *sender, Error::<T>::NoPermission);287 let eth_sponsor = *sponsor.as_eth();288 let sub_sponsor = sponsor.as_sub().clone();289 Sponsoring::<T>::insert(290 contract,291 SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor),292 );293294 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(295 contract,296 sub_sponsor,297 ));298 <PalletEvm<T>>::deposit_log(299 ContractHelpersEvents::ContractSponsorshipConfirmed {300 contract,301 sponsor: eth_sponsor,302 }303 .to_log(contract),304 );305306 Ok(())307 }308 SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => {309 Err(Error::<T>::NoPendingSponsor.into())310 }311 }312 }313314 315 pub fn get_sponsor(contract: H160) -> Option<T::CrossAccountId> {316 match Sponsoring::<T>::get(contract) {317 SponsorshipState::Disabled | SponsorshipState::Unconfirmed(_) => None,318 SponsorshipState::Confirmed(sponsor) => Some(sponsor),319 }320 }321322 323 pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {324 <SponsoringMode<T>>::get(contract)325 .or_else(|| {326 <SelfSponsoring<T>>::get(contract).then(|| SponsoringModeT::Allowlisted)327 })328 .unwrap_or_default()329 }330331 332 pub fn set_sponsoring_mode(contract: H160, mode: SponsoringModeT) {333 if mode == SponsoringModeT::Disabled {334 <SponsoringMode<T>>::remove(contract);335 } else {336 <SponsoringMode<T>>::insert(contract, mode);337 }338 <SelfSponsoring<T>>::remove(contract)339 }340341 342 pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: T::BlockNumber) {343 <SponsoringRateLimit<T>>::insert(contract, rate_limit);344 }345346 347 pub fn allowed(contract: H160, user: H160) -> bool {348 <Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user349 }350351 352 pub fn toggle_allowlist(contract: H160, enabled: bool) {353 <AllowlistEnabled<T>>::insert(contract, enabled)354 }355356 357 pub fn toggle_allowed(contract: H160, user: H160, allowed: bool) {358 <Allowlist<T>>::insert(contract, user, allowed);359 }360361 362 pub fn ensure_owner(contract: H160, user: H160) -> DispatchResult {363 ensure!(<Owner<T>>::get(&contract) == user, Error::<T>::NoPermission);364 Ok(())365 }366 }367}368369370#[derive(Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen, Default)]371pub enum SponsoringModeT {372 373 #[default]374 Disabled,375 376 Allowlisted,377 378 Generous,379}380381impl SponsoringModeT {382 fn from_eth(v: u8) -> Option<Self> {383 Some(match v {384 0 => Self::Disabled,385 1 => Self::Allowlisted,386 2 => Self::Generous,387 _ => return None,388 })389 }390 fn to_eth(self) -> u8 {391 match self {392 SponsoringModeT::Disabled => 0,393 SponsoringModeT::Allowlisted => 1,394 SponsoringModeT::Generous => 2,395 }396 }397}