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, U256};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 52 53 type DefaultSponsoringFeeLimit: Get<U256>;54 }5556 #[pallet::error]57 pub enum Error<T> {58 59 NoPermission,6061 62 NoPendingSponsor,63 }6465 #[pallet::pallet]66 #[pallet::generate_store(pub(super) trait Store)]67 pub struct Pallet<T>(_);6869 70 71 72 73 #[pallet::storage]74 pub(super) type Owner<T: Config> =75 StorageMap<Hasher = Twox128, Key = H160, Value = H160, QueryKind = ValueQuery>;7677 #[pallet::storage]78 #[deprecated]79 pub(super) type SelfSponsoring<T: Config> =80 StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;8182 83 84 85 86 #[pallet::storage]87 pub(super) type Sponsoring<T: Config> = StorageMap<88 Hasher = Twox64Concat,89 Key = H160,90 Value = SponsorshipState<T::CrossAccountId>,91 QueryKind = ValueQuery,92 >;9394 95 96 97 98 99 100 101 #[pallet::storage]102 pub(super) type SponsoringMode<T: Config> =103 StorageMap<Hasher = Twox128, Key = H160, Value = SponsoringModeT, QueryKind = OptionQuery>;104105 106 107 108 109 #[pallet::storage]110 pub(super) type SponsoringRateLimit<T: Config> = StorageMap<111 Hasher = Twox128,112 Key = H160,113 Value = T::BlockNumber,114 QueryKind = ValueQuery,115 OnEmpty = T::DefaultSponsoringRateLimit,116 >;117118 119 120 121 122 123 #[pallet::storage]124 pub(super) type SponsoringFeeLimit<T: Config> = StorageDoubleMap<125 Hasher1 = Twox128,126 Key1 = H160,127 Hasher2 = Blake2_128Concat,128 Key2 = u32,129 Value = U256,130 QueryKind = ValueQuery,131 OnEmpty = T::DefaultSponsoringFeeLimit,132 >;133134 #[pallet::storage]135 pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<136 Hasher1 = Twox128,137 Key1 = H160,138 Hasher2 = Twox128,139 Key2 = H160,140 Value = T::BlockNumber,141 QueryKind = OptionQuery,142 >;143144 145 146 147 148 149 150 151 #[pallet::storage]152 pub(super) type AllowlistEnabled<T: Config> =153 StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;154155 156 157 158 159 160 161 162 163 #[pallet::storage]164 pub(super) type Allowlist<T: Config> = StorageDoubleMap<165 Hasher1 = Twox128,166 Key1 = H160,167 Hasher2 = Twox128,168 Key2 = H160,169 Value = bool,170 QueryKind = ValueQuery,171 >;172173 #[pallet::event]174 #[pallet::generate_deposit(pub fn deposit_event)]175 pub enum Event<T: Config> {176 177 ContractSponsorSet(178 179 H160,180 181 T::AccountId,182 ),183184 185 ContractSponsorshipConfirmed(186 187 H160,188 189 T::AccountId,190 ),191192 193 ContractSponsorRemoved(194 195 H160,196 ),197 }198199 impl<T: Config> Pallet<T> {200 201 pub fn contract_owner(contract: H160) -> H160 {202 <Owner<T>>::get(contract)203 }204205 206 207 208 pub fn set_sponsor(209 sender: &T::CrossAccountId,210 contract: H160,211 sponsor: &T::CrossAccountId,212 ) -> DispatchResult {213 Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;214 Sponsoring::<T>::insert(215 contract,216 SponsorshipState::<T::CrossAccountId>::Unconfirmed(sponsor.clone()),217 );218219 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(220 contract,221 sponsor.as_sub().clone(),222 ));223 <PalletEvm<T>>::deposit_log(224 ContractHelpersEvents::ContractSponsorSet {225 contract_address: contract,226 sponsor: *sponsor.as_eth(),227 }228 .to_log(contract),229 );230 Ok(())231 }232233 234 235 236 pub fn force_set_sponsor(237 contract_address: H160,238 sponsor: &T::CrossAccountId,239 ) -> DispatchResult {240 Sponsoring::<T>::insert(241 contract_address,242 SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor.clone()),243 );244245 let eth_sponsor = *sponsor.as_eth();246 let sub_sponsor = sponsor.as_sub().clone();247248 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(249 contract_address,250 sub_sponsor.clone(),251 ));252 <PalletEvm<T>>::deposit_log(253 ContractHelpersEvents::ContractSponsorSet {254 contract_address,255 sponsor: eth_sponsor,256 }257 .to_log(contract_address),258 );259260 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(261 contract_address,262 sub_sponsor,263 ));264 <PalletEvm<T>>::deposit_log(265 ContractHelpersEvents::ContractSponsorshipConfirmed {266 contract_address,267 sponsor: eth_sponsor,268 }269 .to_log(contract_address),270 );271272 Ok(())273 }274275 276 277 278 pub fn remove_sponsor(279 sender: &T::CrossAccountId,280 contract_address: H160,281 ) -> DispatchResult {282 Self::ensure_owner(contract_address, *sender.as_eth())?;283 Self::force_remove_sponsor(contract_address)284 }285286 287 288 289 pub fn force_remove_sponsor(contract_address: H160) -> DispatchResult {290 Sponsoring::<T>::remove(contract_address);291292 Self::deposit_event(Event::<T>::ContractSponsorRemoved(contract_address));293 <PalletEvm<T>>::deposit_log(294 ContractHelpersEvents::ContractSponsorRemoved { contract_address }295 .to_log(contract_address),296 );297298 Ok(())299 }300301 302 303 304 pub fn confirm_sponsorship(305 sender: &T::CrossAccountId,306 contract_address: H160,307 ) -> DispatchResult {308 match Sponsoring::<T>::get(contract_address) {309 SponsorshipState::Unconfirmed(sponsor) => {310 ensure!(sponsor == *sender, Error::<T>::NoPermission);311 let eth_sponsor = *sponsor.as_eth();312 let sub_sponsor = sponsor.as_sub().clone();313 Sponsoring::<T>::insert(314 contract_address,315 SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor),316 );317318 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(319 contract_address,320 sub_sponsor,321 ));322 <PalletEvm<T>>::deposit_log(323 ContractHelpersEvents::ContractSponsorshipConfirmed {324 contract_address,325 sponsor: eth_sponsor,326 }327 .to_log(contract_address),328 );329330 Ok(())331 }332 SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => {333 Err(Error::<T>::NoPendingSponsor.into())334 }335 }336 }337338 339 pub fn get_sponsor(contract: H160) -> Option<T::CrossAccountId> {340 match Sponsoring::<T>::get(contract) {341 SponsorshipState::Disabled | SponsorshipState::Unconfirmed(_) => None,342 SponsorshipState::Confirmed(sponsor) => Some(sponsor),343 }344 }345346 347 pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {348 <SponsoringMode<T>>::get(contract)349 .or_else(|| {350 <SelfSponsoring<T>>::get(contract).then(|| SponsoringModeT::Allowlisted)351 })352 .unwrap_or_default()353 }354355 356 pub fn set_sponsoring_mode(contract: H160, mode: SponsoringModeT) {357 if mode == SponsoringModeT::Disabled {358 <SponsoringMode<T>>::remove(contract);359 } else {360 <SponsoringMode<T>>::insert(contract, mode);361 }362 <SelfSponsoring<T>>::remove(contract)363 }364365 366 pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: T::BlockNumber) {367 <SponsoringRateLimit<T>>::insert(contract, rate_limit);368 }369370 371 pub fn set_sponsoring_fee_limit(contract: H160, fee_limit: U256) {372 <SponsoringFeeLimit<T>>::insert(contract, 0xffffffff, fee_limit);373 }374375 376 pub fn allowed(contract: H160, user: H160) -> bool {377 <Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user378 }379380 381 pub fn toggle_allowlist(contract: H160, enabled: bool) {382 <AllowlistEnabled<T>>::insert(contract, enabled)383 }384385 386 pub fn toggle_allowed(contract: H160, user: H160, allowed: bool) {387 <Allowlist<T>>::insert(contract, user, allowed);388 }389390 391 pub fn ensure_owner(contract: H160, user: H160) -> DispatchResult {392 ensure!(<Owner<T>>::get(&contract) == user, Error::<T>::NoPermission);393 Ok(())394 }395 }396}397398399#[derive(Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen, Default)]400pub enum SponsoringModeT {401 402 #[default]403 Disabled,404 405 Allowlisted,406 407 Generous,408}409410impl SponsoringModeT {411 fn from_eth(v: u8) -> Option<Self> {412 Some(match v {413 0 => Self::Disabled,414 1 => Self::Allowlisted,415 2 => Self::Generous,416 _ => return None,417 })418 }419 fn to_eth(self) -> u8 {420 match self {421 SponsoringModeT::Disabled => 0,422 SponsoringModeT::Allowlisted => 1,423 SponsoringModeT::Generous => 2,424 }425 }426}