1234567891011121314151617#![doc = include_str!("../README.md")]18#![cfg_attr(not(feature = "std"), no_std)]19#![deny(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 frame_support::pallet_prelude::*;31 use pallet_evm_coder_substrate::DispatchResult;32 use sp_core::H160;33 use pallet_evm::account::CrossAccountId;34 use up_data_structs::SponsorshipState;3536 #[pallet::config]37 pub trait Config:38 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::account::Config39 {40 41 type ContractAddress: Get<H160>;42 43 44 type DefaultSponsoringRateLimit: Get<Self::BlockNumber>;45 }4647 #[pallet::error]48 pub enum Error<T> {49 50 NoPermission,5152 53 NoPendingSponsor,54 }5556 #[pallet::pallet]57 #[pallet::generate_store(pub(super) trait Store)]58 pub struct Pallet<T>(_);5960 61 62 63 64 #[pallet::storage]65 pub(super) type Owner<T: Config> =66 StorageMap<Hasher = Twox128, Key = H160, Value = H160, QueryKind = ValueQuery>;6768 #[pallet::storage]69 #[deprecated]70 pub(super) type SelfSponsoring<T: Config> =71 StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;7273 74 75 76 77 #[pallet::storage]78 pub(super) type Sponsoring<T: Config> = StorageMap<79 Hasher = Twox64Concat,80 Key = H160,81 Value = SponsorshipState<T::CrossAccountId>,82 QueryKind = ValueQuery,83 >;8485 86 87 88 89 90 91 92 #[pallet::storage]93 pub(super) type SponsoringMode<T: Config> =94 StorageMap<Hasher = Twox128, Key = H160, Value = SponsoringModeT, QueryKind = OptionQuery>;9596 97 98 99 100 #[pallet::storage]101 pub(super) type SponsoringRateLimit<T: Config> = StorageMap<102 Hasher = Twox128,103 Key = H160,104 Value = T::BlockNumber,105 QueryKind = ValueQuery,106 OnEmpty = T::DefaultSponsoringRateLimit,107 >;108109 110 111 112 113 114 #[pallet::storage]115 pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<116 Hasher1 = Twox128,117 Key1 = H160,118 Hasher2 = Twox128,119 Key2 = H160,120 Value = T::BlockNumber,121 QueryKind = OptionQuery,122 >;123124 125 126 127 128 129 130 131 #[pallet::storage]132 pub(super) type AllowlistEnabled<T: Config> =133 StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;134135 136 137 138 139 140 141 142 143 #[pallet::storage]144 pub(super) type Allowlist<T: Config> = StorageDoubleMap<145 Hasher1 = Twox128,146 Key1 = H160,147 Hasher2 = Twox128,148 Key2 = H160,149 Value = bool,150 QueryKind = ValueQuery,151 >;152153 impl<T: Config> Pallet<T> {154 155 pub fn contract_owner(contract: H160) -> H160 {156 <Owner<T>>::get(contract)157 }158159 160 161 162 pub fn set_sponsor(163 sender: &T::CrossAccountId,164 contract: H160,165 sponsor: &T::CrossAccountId,166 ) -> DispatchResult {167 Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;168 Sponsoring::<T>::insert(169 contract,170 SponsorshipState::<T::CrossAccountId>::Unconfirmed(sponsor.clone()),171 );172 Ok(())173 }174175 176 177 178 pub fn self_sponsored_enable(sender: &T::CrossAccountId, contract: H160) -> DispatchResult {179 Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;180 Sponsoring::<T>::insert(181 contract,182 SponsorshipState::<T::CrossAccountId>::Confirmed(T::CrossAccountId::from_eth(183 contract,184 )),185 );186 Ok(())187 }188189 190 191 192 pub fn remove_sponsor(sender: &T::CrossAccountId, contract: H160) -> DispatchResult {193 Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;194 Sponsoring::<T>::remove(contract);195 Ok(())196 }197198 199 200 201 pub fn confirm_sponsorship(sender: &T::CrossAccountId, contract: H160) -> DispatchResult {202 match Sponsoring::<T>::get(contract) {203 SponsorshipState::Unconfirmed(sponsor) => {204 ensure!(sponsor == *sender, Error::<T>::NoPermission);205 Sponsoring::<T>::insert(206 contract,207 SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor),208 );209 Ok(())210 }211 SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => {212 Err(Error::<T>::NoPendingSponsor.into())213 }214 }215 }216217 218 pub fn get_sponsor(contract: H160) -> Option<T::CrossAccountId> {219 match Sponsoring::<T>::get(contract) {220 SponsorshipState::Disabled | SponsorshipState::Unconfirmed(_) => None,221 SponsorshipState::Confirmed(sponsor) => Some(sponsor),222 }223 }224225 226 pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {227 <SponsoringMode<T>>::get(contract)228 .or_else(|| {229 <SelfSponsoring<T>>::get(contract).then(|| SponsoringModeT::Allowlisted)230 })231 .unwrap_or_default()232 }233234 235 pub fn set_sponsoring_mode(contract: H160, mode: SponsoringModeT) {236 if mode == SponsoringModeT::Disabled {237 <SponsoringMode<T>>::remove(contract);238 } else {239 <SponsoringMode<T>>::insert(contract, mode);240 }241 <SelfSponsoring<T>>::remove(contract)242 }243244 245 pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: T::BlockNumber) {246 <SponsoringRateLimit<T>>::insert(contract, rate_limit);247 }248249 250 pub fn allowed(contract: H160, user: H160) -> bool {251 <Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user252 }253254 255 pub fn toggle_allowlist(contract: H160, enabled: bool) {256 <AllowlistEnabled<T>>::insert(contract, enabled)257 }258259 260 pub fn toggle_allowed(contract: H160, user: H160, allowed: bool) {261 <Allowlist<T>>::insert(contract, user, allowed);262 }263264 265 pub fn ensure_owner(contract: H160, user: H160) -> DispatchResult {266 ensure!(<Owner<T>>::get(&contract) == user, Error::<T>::NoPermission);267 Ok(())268 }269 }270}271272273#[derive(Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen, Default)]274pub enum SponsoringModeT {275 276 #[default]277 Disabled,278 279 Allowlisted,280 281 Generous,282}283284impl SponsoringModeT {285 fn from_eth(v: u8) -> Option<Self> {286 Some(match v {287 0 => Self::Disabled,288 1 => Self::Allowlisted,289 2 => Self::Generous,290 _ => return None,291 })292 }293 fn to_eth(self) -> u8 {294 match self {295 SponsoringModeT::Disabled => 0,296 SponsoringModeT::Allowlisted => 1,297 SponsoringModeT::Generous => 2,298 }299 }300}