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;25use frame_support::storage::bounded_btree_map::BoundedBTreeMap;26pub mod eth;272829pub const MAX_FEE_LIMITED_METHODS: u32 = 5;3031#[frame_support::pallet]32pub mod pallet {33 pub use super::*;34 use crate::eth::ContractHelpersEvents;35 use frame_support::pallet_prelude::*;36 use pallet_evm_coder_substrate::DispatchResult;37 use sp_core::{H160, U256};38 use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};39 use up_data_structs::SponsorshipState;40 use evm_coder::ToLog;4142 #[pallet::config]43 pub trait Config:44 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config45 {46 47 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;4849 50 #[pallet::constant]51 type ContractAddress: Get<H160>;5253 54 55 type DefaultSponsoringRateLimit: Get<Self::BlockNumber>;56 }5758 #[pallet::error]59 pub enum Error<T> {60 61 NoPermission,6263 64 NoPendingSponsor,6566 67 TooManyMethodsHaveSponsoredLimit,68 }6970 #[pallet::pallet]71 #[pallet::generate_store(pub(super) trait Store)]72 pub struct Pallet<T>(_);7374 75 76 77 78 #[pallet::storage]79 pub(super) type Owner<T: Config> =80 StorageMap<Hasher = Twox128, Key = H160, Value = H160, QueryKind = ValueQuery>;8182 #[pallet::storage]83 #[deprecated]84 pub(super) type SelfSponsoring<T: Config> =85 StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;8687 88 89 90 91 #[pallet::storage]92 pub(super) type Sponsoring<T: Config> = StorageMap<93 Hasher = Twox64Concat,94 Key = H160,95 Value = SponsorshipState<T::CrossAccountId>,96 QueryKind = ValueQuery,97 >;9899 100 101 102 103 104 105 106 #[pallet::storage]107 pub(super) type SponsoringMode<T: Config> =108 StorageMap<Hasher = Twox128, Key = H160, Value = SponsoringModeT, QueryKind = OptionQuery>;109110 111 112 113 114 #[pallet::storage]115 pub(super) type SponsoringRateLimit<T: Config> = StorageMap<116 Hasher = Twox128,117 Key = H160,118 Value = T::BlockNumber,119 QueryKind = ValueQuery,120 OnEmpty = T::DefaultSponsoringRateLimit,121 >;122123 124 125 126 127 128 #[pallet::storage]129 pub(super) type SponsoringFeeLimit<T: Config> = StorageMap<130 Hasher = Twox128,131 Key = H160,132 Value = BoundedBTreeMap<u32, U256, ConstU32<MAX_FEE_LIMITED_METHODS>>,133 QueryKind = ValueQuery,134 >;135136 #[pallet::storage]137 pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<138 Hasher1 = Twox128,139 Key1 = H160,140 Hasher2 = Twox128,141 Key2 = H160,142 Value = T::BlockNumber,143 QueryKind = OptionQuery,144 >;145146 147 148 149 150 151 152 153 #[pallet::storage]154 pub(super) type AllowlistEnabled<T: Config> =155 StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;156157 158 159 160 161 162 163 164 165 #[pallet::storage]166 pub(super) type Allowlist<T: Config> = StorageDoubleMap<167 Hasher1 = Twox128,168 Key1 = H160,169 Hasher2 = Twox128,170 Key2 = H160,171 Value = bool,172 QueryKind = ValueQuery,173 >;174175 #[pallet::event]176 #[pallet::generate_deposit(fn deposit_event)]177 pub enum Event<T: Config> {178 179 ContractSponsorSet(180 181 H160,182 183 T::AccountId,184 ),185186 187 ContractSponsorshipConfirmed(188 189 H160,190 191 T::AccountId,192 ),193194 195 ContractSponsorRemoved(196 197 H160,198 ),199 }200201 impl<T: Config> Pallet<T> {202 203 pub fn contract_owner(contract: H160) -> H160 {204 <Owner<T>>::get(contract)205 }206207 208 209 210 pub fn set_sponsor(211 sender: &T::CrossAccountId,212 contract: H160,213 sponsor: &T::CrossAccountId,214 ) -> DispatchResult {215 Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;216 Sponsoring::<T>::insert(217 contract,218 SponsorshipState::<T::CrossAccountId>::Unconfirmed(sponsor.clone()),219 );220221 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(222 contract,223 sponsor.as_sub().clone(),224 ));225 <PalletEvm<T>>::deposit_log(226 ContractHelpersEvents::ContractSponsorSet {227 contract_address: contract,228 sponsor: *sponsor.as_eth(),229 }230 .to_log(contract),231 );232 Ok(())233 }234235 236 237 238 239 pub fn force_set_sponsor(240 contract_address: H160,241 sponsor: &T::CrossAccountId,242 ) -> DispatchResult {243 Sponsoring::<T>::insert(244 contract_address,245 SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor.clone()),246 );247248 let eth_sponsor = *sponsor.as_eth();249 let sub_sponsor = sponsor.as_sub().clone();250251 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(252 contract_address,253 sub_sponsor.clone(),254 ));255 <PalletEvm<T>>::deposit_log(256 ContractHelpersEvents::ContractSponsorSet {257 contract_address,258 sponsor: eth_sponsor,259 }260 .to_log(contract_address),261 );262263 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(264 contract_address,265 sub_sponsor,266 ));267 <PalletEvm<T>>::deposit_log(268 ContractHelpersEvents::ContractSponsorshipConfirmed {269 contract_address,270 sponsor: eth_sponsor,271 }272 .to_log(contract_address),273 );274275 Ok(())276 }277278 279 280 281 pub fn remove_sponsor(282 sender: &T::CrossAccountId,283 contract_address: H160,284 ) -> DispatchResult {285 Self::ensure_owner(contract_address, *sender.as_eth())?;286 Self::force_remove_sponsor(contract_address)287 }288289 290 291 292 293 pub fn force_remove_sponsor(contract_address: H160) -> DispatchResult {294 Sponsoring::<T>::remove(contract_address);295296 Self::deposit_event(Event::<T>::ContractSponsorRemoved(contract_address));297 <PalletEvm<T>>::deposit_log(298 ContractHelpersEvents::ContractSponsorRemoved { contract_address }299 .to_log(contract_address),300 );301302 Ok(())303 }304305 306 307 308 pub fn confirm_sponsorship(309 sender: &T::CrossAccountId,310 contract_address: H160,311 ) -> DispatchResult {312 match Sponsoring::<T>::get(contract_address) {313 SponsorshipState::Unconfirmed(sponsor) => {314 ensure!(sponsor == *sender, Error::<T>::NoPermission);315 let eth_sponsor = *sponsor.as_eth();316 let sub_sponsor = sponsor.as_sub().clone();317 Sponsoring::<T>::insert(318 contract_address,319 SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor),320 );321322 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(323 contract_address,324 sub_sponsor,325 ));326 <PalletEvm<T>>::deposit_log(327 ContractHelpersEvents::ContractSponsorshipConfirmed {328 contract_address,329 sponsor: eth_sponsor,330 }331 .to_log(contract_address),332 );333334 Ok(())335 }336 SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => {337 Err(Error::<T>::NoPendingSponsor.into())338 }339 }340 }341342 343 pub fn get_sponsor(contract: H160) -> Option<T::CrossAccountId> {344 match Sponsoring::<T>::get(contract) {345 SponsorshipState::Disabled | SponsorshipState::Unconfirmed(_) => None,346 SponsorshipState::Confirmed(sponsor) => Some(sponsor),347 }348 }349350 351 pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {352 <SponsoringMode<T>>::get(contract)353 .or_else(|| {354 #[allow(deprecated)]355 <SelfSponsoring<T>>::get(contract).then(|| SponsoringModeT::Allowlisted)356 })357 .unwrap_or_default()358 }359360 361 pub fn set_sponsoring_mode(contract: H160, mode: SponsoringModeT) {362 if mode == SponsoringModeT::Disabled {363 <SponsoringMode<T>>::remove(contract);364 } else {365 <SponsoringMode<T>>::insert(contract, mode);366 }367 #[allow(deprecated)]368 <SelfSponsoring<T>>::remove(contract)369 }370371 372 pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: T::BlockNumber) {373 <SponsoringRateLimit<T>>::insert(contract, rate_limit);374 }375376 377 pub fn set_sponsoring_fee_limit(contract: H160, fee_limit: U256) -> DispatchResult {378 <SponsoringFeeLimit<T>>::try_mutate(contract, |limits_map| {379 limits_map380 .try_insert(0xffffffff, fee_limit)381 .map_err(|_| <Error<T>>::TooManyMethodsHaveSponsoredLimit)382 })?;383 Ok(())384 }385386 387 pub fn allowed(contract: H160, user: H160) -> bool {388 <Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user389 }390391 392 pub fn toggle_allowlist(contract: H160, enabled: bool) {393 <AllowlistEnabled<T>>::insert(contract, enabled)394 }395396 397 pub fn toggle_allowed(contract: H160, user: H160, allowed: bool) {398 <Allowlist<T>>::insert(contract, user, allowed);399 }400401 402 pub fn ensure_owner(contract: H160, user: H160) -> DispatchResult {403 ensure!(<Owner<T>>::get(&contract) == user, Error::<T>::NoPermission);404 Ok(())405 }406 }407}408409410#[derive(Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen, Default)]411pub enum SponsoringModeT {412 413 #[default]414 Disabled,415 416 Allowlisted,417 418 Generous,419}420421impl SponsoringModeT {422 fn from_eth(v: u8) -> Option<Self> {423 Some(match v {424 0 => Self::Disabled,425 1 => Self::Allowlisted,426 2 => Self::Generous,427 _ => return None,428 })429 }430 #[allow(dead_code)]431 fn to_eth(self) -> u8 {432 match self {433 SponsoringModeT::Disabled => 0,434 SponsoringModeT::Allowlisted => 1,435 SponsoringModeT::Generous => 2,436 }437 }438}