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(sender: &T::CrossAccountId, contract: H160) -> DispatchResult {223 Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;224 Sponsoring::<T>::insert(225 contract,226 SponsorshipState::<T::CrossAccountId>::Confirmed(T::CrossAccountId::from_eth(227 contract,228 )),229 );230 Ok(())231 }232233 234 235 236 pub fn remove_sponsor(sender: &T::CrossAccountId, contract: H160) -> DispatchResult {237 Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;238 Sponsoring::<T>::remove(contract);239240 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorRemoved(contract));241 <PalletEvm<T>>::deposit_log(242 ContractHelpersEvents::ContractSponsorRemoved { contract }.to_log(contract),243 );244245 Ok(())246 }247248 249 250 251 pub fn confirm_sponsorship(sender: &T::CrossAccountId, contract: H160) -> DispatchResult {252 match Sponsoring::<T>::get(contract) {253 SponsorshipState::Unconfirmed(sponsor) => {254 ensure!(sponsor == *sender, Error::<T>::NoPermission);255 let eth_sponsor = *sponsor.as_eth();256 let sub_sponsor = sponsor.as_sub().clone();257 Sponsoring::<T>::insert(258 contract,259 SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor),260 );261262 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(263 contract,264 sub_sponsor,265 ));266 <PalletEvm<T>>::deposit_log(267 ContractHelpersEvents::ContractSponsorshipConfirmed {268 contract,269 sponsor: eth_sponsor,270 }271 .to_log(contract),272 );273274 Ok(())275 }276 SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => {277 Err(Error::<T>::NoPendingSponsor.into())278 }279 }280 }281282 283 pub fn get_sponsor(contract: H160) -> Option<T::CrossAccountId> {284 match Sponsoring::<T>::get(contract) {285 SponsorshipState::Disabled | SponsorshipState::Unconfirmed(_) => None,286 SponsorshipState::Confirmed(sponsor) => Some(sponsor),287 }288 }289290 291 pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {292 <SponsoringMode<T>>::get(contract)293 .or_else(|| {294 <SelfSponsoring<T>>::get(contract).then(|| SponsoringModeT::Allowlisted)295 })296 .unwrap_or_default()297 }298299 300 pub fn set_sponsoring_mode(contract: H160, mode: SponsoringModeT) {301 if mode == SponsoringModeT::Disabled {302 <SponsoringMode<T>>::remove(contract);303 } else {304 <SponsoringMode<T>>::insert(contract, mode);305 }306 <SelfSponsoring<T>>::remove(contract)307 }308309 310 pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: T::BlockNumber) {311 <SponsoringRateLimit<T>>::insert(contract, rate_limit);312 }313314 315 pub fn allowed(contract: H160, user: H160) -> bool {316 <Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user317 }318319 320 pub fn toggle_allowlist(contract: H160, enabled: bool) {321 <AllowlistEnabled<T>>::insert(contract, enabled)322 }323324 325 pub fn toggle_allowed(contract: H160, user: H160, allowed: bool) {326 <Allowlist<T>>::insert(contract, user, allowed);327 }328329 330 pub fn ensure_owner(contract: H160, user: H160) -> DispatchResult {331 ensure!(<Owner<T>>::get(&contract) == user, Error::<T>::NoPermission);332 Ok(())333 }334 }335}336337338#[derive(Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen, Default)]339pub enum SponsoringModeT {340 341 #[default]342 Disabled,343 344 Allowlisted,345 346 Generous,347}348349impl SponsoringModeT {350 fn from_eth(v: u8) -> Option<Self> {351 Some(match v {352 0 => Self::Disabled,353 1 => Self::Allowlisted,354 2 => Self::Generous,355 _ => return None,356 })357 }358 fn to_eth(self) -> u8 {359 match self {360 SponsoringModeT::Disabled => 0,361 SponsoringModeT::Allowlisted => 1,362 SponsoringModeT::Generous => 2,363 }364 }365}