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 223 pub fn force_set_sponsor(224 contract_address: H160,225 sponsor: &T::CrossAccountId,226 ) -> DispatchResult {227 Sponsoring::<T>::insert(228 contract_address,229 SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor.clone()),230 );231232 let eth_sponsor = *sponsor.as_eth();233 let sub_sponsor = sponsor.as_sub().clone();234235 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(236 contract_address,237 sub_sponsor.clone(),238 ));239 <PalletEvm<T>>::deposit_log(240 ContractHelpersEvents::ContractSponsorSet {241 contract_address,242 sponsor: eth_sponsor,243 }244 .to_log(contract_address),245 );246247 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(248 contract_address,249 sub_sponsor,250 ));251 <PalletEvm<T>>::deposit_log(252 ContractHelpersEvents::ContractSponsorshipConfirmed {253 contract_address,254 sponsor: eth_sponsor,255 }256 .to_log(contract_address),257 );258259 Ok(())260 }261262 263 264 265 pub fn remove_sponsor(266 sender: &T::CrossAccountId,267 contract_address: H160,268 ) -> DispatchResult {269 Self::ensure_owner(contract_address, *sender.as_eth())?;270 Self::force_remove_sponsor(contract_address)271 }272273 274 275 276 277 pub fn force_remove_sponsor(contract_address: H160) -> DispatchResult {278 Sponsoring::<T>::remove(contract_address);279280 Self::deposit_event(Event::<T>::ContractSponsorRemoved(contract_address));281 <PalletEvm<T>>::deposit_log(282 ContractHelpersEvents::ContractSponsorRemoved { contract_address }283 .to_log(contract_address),284 );285286 Ok(())287 }288289 290 291 292 pub fn confirm_sponsorship(293 sender: &T::CrossAccountId,294 contract_address: H160,295 ) -> DispatchResult {296 match Sponsoring::<T>::get(contract_address) {297 SponsorshipState::Unconfirmed(sponsor) => {298 ensure!(sponsor == *sender, Error::<T>::NoPermission);299 let eth_sponsor = *sponsor.as_eth();300 let sub_sponsor = sponsor.as_sub().clone();301 Sponsoring::<T>::insert(302 contract_address,303 SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor),304 );305306 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(307 contract_address,308 sub_sponsor,309 ));310 <PalletEvm<T>>::deposit_log(311 ContractHelpersEvents::ContractSponsorshipConfirmed {312 contract_address,313 sponsor: eth_sponsor,314 }315 .to_log(contract_address),316 );317318 Ok(())319 }320 SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => {321 Err(Error::<T>::NoPendingSponsor.into())322 }323 }324 }325326 327 pub fn get_sponsor(contract: H160) -> Option<T::CrossAccountId> {328 match Sponsoring::<T>::get(contract) {329 SponsorshipState::Disabled | SponsorshipState::Unconfirmed(_) => None,330 SponsorshipState::Confirmed(sponsor) => Some(sponsor),331 }332 }333334 335 pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {336 <SponsoringMode<T>>::get(contract)337 .or_else(|| {338 <SelfSponsoring<T>>::get(contract).then(|| SponsoringModeT::Allowlisted)339 })340 .unwrap_or_default()341 }342343 344 pub fn set_sponsoring_mode(contract: H160, mode: SponsoringModeT) {345 if mode == SponsoringModeT::Disabled {346 <SponsoringMode<T>>::remove(contract);347 } else {348 <SponsoringMode<T>>::insert(contract, mode);349 }350 <SelfSponsoring<T>>::remove(contract)351 }352353 354 pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: T::BlockNumber) {355 <SponsoringRateLimit<T>>::insert(contract, rate_limit);356 }357358 359 pub fn allowed(contract: H160, user: H160) -> bool {360 <Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user361 }362363 364 pub fn toggle_allowlist(contract: H160, enabled: bool) {365 <AllowlistEnabled<T>>::insert(contract, enabled)366 }367368 369 pub fn toggle_allowed(contract: H160, user: H160, allowed: bool) {370 <Allowlist<T>>::insert(contract, user, allowed);371 }372373 374 pub fn ensure_owner(contract: H160, user: H160) -> DispatchResult {375 ensure!(<Owner<T>>::get(&contract) == user, Error::<T>::NoPermission);376 Ok(())377 }378 }379}380381382#[derive(Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen, Default)]383pub enum SponsoringModeT {384 385 #[default]386 Disabled,387 388 Allowlisted,389 390 Generous,391}392393impl SponsoringModeT {394 fn from_eth(v: u8) -> Option<Self> {395 Some(match v {396 0 => Self::Disabled,397 1 => Self::Allowlisted,398 2 => Self::Generous,399 _ => return None,400 })401 }402 fn to_eth(self) -> u8 {403 match self {404 SponsoringModeT::Disabled => 0,405 SponsoringModeT::Allowlisted => 1,406 SponsoringModeT::Generous => 2,407 }408 }409}