1234567891011121314151617#![doc = include_str!("../README.md")]18#![cfg_attr(not(feature = "std"), no_std)]19#![warn(missing_docs)]2021pub use eth::*;22use evm_coder::AbiCoder;23use frame_support::storage::bounded_btree_map::BoundedBTreeMap;24pub use pallet::*;25use parity_scale_codec::{Decode, Encode, MaxEncodedLen};26use scale_info::TypeInfo;27pub mod eth;282930pub const MAX_FEE_LIMITED_METHODS: u32 = 5;3132#[frame_support::pallet]33pub mod pallet {34 use evm_coder::ToLog;35 use frame_support::{pallet_prelude::*, sp_runtime::DispatchResult};36 use frame_system::{ensure_root, pallet_prelude::*};37 use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};38 use sp_core::{H160, U256};39 use sp_std::vec::Vec;40 use up_data_structs::SponsorshipState;4142 pub use super::*;4344 #[pallet::config]45 pub trait Config:46 frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config47 {48 49 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;5051 52 #[pallet::constant]53 type ContractAddress: Get<H160>;5455 56 57 type DefaultSponsoringRateLimit: Get<BlockNumberFor<Self>>;58 }5960 #[pallet::error]61 pub enum Error<T> {62 63 NoPermission,6465 66 NoPendingSponsor,6768 69 TooManyMethodsHaveSponsoredLimit,70 }7172 #[pallet::pallet]73 pub struct Pallet<T>(_);7475 76 77 78 79 #[pallet::storage]80 pub(super) type Owner<T: Config> =81 StorageMap<Hasher = Twox128, Key = H160, Value = H160, QueryKind = ValueQuery>;8283 84 #[pallet::storage]85 type SelfSponsoring<T: Config> =86 StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;8788 89 90 91 92 #[pallet::storage]93 pub(super) type Sponsoring<T: Config> = StorageMap<94 Hasher = Twox64Concat,95 Key = H160,96 Value = SponsorshipState<T::CrossAccountId>,97 QueryKind = ValueQuery,98 >;99100 101 102 103 104 105 106 107 #[pallet::storage]108 pub(super) type SponsoringMode<T: Config> =109 StorageMap<Hasher = Twox128, Key = H160, Value = SponsoringModeT, QueryKind = OptionQuery>;110111 112 113 114 115 #[pallet::storage]116 pub(super) type SponsoringRateLimit<T: Config> = StorageMap<117 Hasher = Twox128,118 Key = H160,119 Value = BlockNumberFor<T>,120 QueryKind = ValueQuery,121 OnEmpty = T::DefaultSponsoringRateLimit,122 >;123124 125 126 127 128 129 #[pallet::storage]130 pub(super) type SponsoringFeeLimit<T: Config> = StorageMap<131 Hasher = Twox128,132 Key = H160,133 Value = BoundedBTreeMap<u32, U256, ConstU32<MAX_FEE_LIMITED_METHODS>>,134 QueryKind = ValueQuery,135 >;136137 #[pallet::storage]138 pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<139 Hasher1 = Twox128,140 Key1 = H160,141 Hasher2 = Twox128,142 Key2 = H160,143 Value = BlockNumberFor<T>,144 QueryKind = OptionQuery,145 >;146147 148 149 150 151 152 153 154 #[pallet::storage]155 pub(super) type AllowlistEnabled<T: Config> =156 StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;157158 159 160 161 162 163 164 165 166 #[pallet::storage]167 pub(super) type Allowlist<T: Config> = StorageDoubleMap<168 Hasher1 = Twox128,169 Key1 = H160,170 Hasher2 = Twox128,171 Key2 = H160,172 Value = bool,173 QueryKind = ValueQuery,174 >;175176 #[pallet::event]177 #[pallet::generate_deposit(fn deposit_event)]178 pub enum Event<T: Config> {179 180 ContractSponsorSet(181 182 H160,183 184 T::AccountId,185 ),186187 188 ContractSponsorshipConfirmed(189 190 H160,191 192 T::AccountId,193 ),194195 196 ContractSponsorRemoved(197 198 H160,199 ),200 }201202 #[pallet::call]203 impl<T: Config> Pallet<T> {204 205 #[pallet::call_index(0)]206 #[pallet::weight(<T as frame_system::Config>::DbWeight::get().reads_writes(207 addresses.len() as u64, addresses.len() as u64 * 2208 ))]209 pub fn migrate_from_self_sponsoring(210 origin: OriginFor<T>,211 addresses: Vec<H160>,212 ) -> DispatchResult {213 ensure_root(origin)?;214 for address in addresses {215 if <SelfSponsoring<T>>::get(address) {216 <SponsoringMode<T>>::set(address, Some(SponsoringModeT::Allowlisted));217 <SelfSponsoring<T>>::remove(address);218 }219 }220 Ok(())221 }222 }223224 impl<T: Config> Pallet<T> {225 226 pub fn contract_owner(contract: H160) -> H160 {227 <Owner<T>>::get(contract)228 }229230 231 232 233 pub fn set_sponsor(234 sender: &T::CrossAccountId,235 contract: H160,236 sponsor: &T::CrossAccountId,237 ) -> DispatchResult {238 Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;239 Sponsoring::<T>::insert(240 contract,241 SponsorshipState::<T::CrossAccountId>::Unconfirmed(sponsor.clone()),242 );243244 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(245 contract,246 sponsor.as_sub().clone(),247 ));248 <PalletEvm<T>>::deposit_log(249 ContractHelpersEvents::ContractSponsorSet {250 contract_address: contract,251 sponsor: *sponsor.as_eth(),252 }253 .to_log(contract),254 );255 Ok(())256 }257258 259 260 261 262 pub fn force_set_sponsor(263 contract_address: H160,264 sponsor: &T::CrossAccountId,265 ) -> DispatchResult {266 Sponsoring::<T>::insert(267 contract_address,268 SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor.clone()),269 );270271 let eth_sponsor = *sponsor.as_eth();272 let sub_sponsor = sponsor.as_sub().clone();273274 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(275 contract_address,276 sub_sponsor.clone(),277 ));278 <PalletEvm<T>>::deposit_log(279 ContractHelpersEvents::ContractSponsorSet {280 contract_address,281 sponsor: eth_sponsor,282 }283 .to_log(contract_address),284 );285286 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(287 contract_address,288 sub_sponsor,289 ));290 <PalletEvm<T>>::deposit_log(291 ContractHelpersEvents::ContractSponsorshipConfirmed {292 contract_address,293 sponsor: eth_sponsor,294 }295 .to_log(contract_address),296 );297298 Ok(())299 }300301 302 303 304 pub fn remove_sponsor(305 sender: &T::CrossAccountId,306 contract_address: H160,307 ) -> DispatchResult {308 Self::ensure_owner(contract_address, *sender.as_eth())?;309 Self::force_remove_sponsor(contract_address)310 }311312 313 314 315 316 pub fn force_remove_sponsor(contract_address: H160) -> DispatchResult {317 Sponsoring::<T>::remove(contract_address);318319 Self::deposit_event(Event::<T>::ContractSponsorRemoved(contract_address));320 <PalletEvm<T>>::deposit_log(321 ContractHelpersEvents::ContractSponsorRemoved { contract_address }322 .to_log(contract_address),323 );324325 Ok(())326 }327328 329 330 331 pub fn confirm_sponsorship(332 sender: &T::CrossAccountId,333 contract_address: H160,334 ) -> DispatchResult {335 match Sponsoring::<T>::get(contract_address) {336 SponsorshipState::Unconfirmed(sponsor) => {337 ensure!(sponsor == *sender, Error::<T>::NoPermission);338 let eth_sponsor = *sponsor.as_eth();339 let sub_sponsor = sponsor.as_sub().clone();340 Sponsoring::<T>::insert(341 contract_address,342 SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor),343 );344345 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(346 contract_address,347 sub_sponsor,348 ));349 <PalletEvm<T>>::deposit_log(350 ContractHelpersEvents::ContractSponsorshipConfirmed {351 contract_address,352 sponsor: eth_sponsor,353 }354 .to_log(contract_address),355 );356357 Ok(())358 }359 SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => {360 Err(Error::<T>::NoPendingSponsor.into())361 }362 }363 }364365 366 pub fn get_sponsor(contract: H160) -> Option<T::CrossAccountId> {367 match Sponsoring::<T>::get(contract) {368 SponsorshipState::Disabled | SponsorshipState::Unconfirmed(_) => None,369 SponsorshipState::Confirmed(sponsor) => Some(sponsor),370 }371 }372373 374 375 pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {376 <SponsoringMode<T>>::get(contract)377 .or_else(|| {378 #[allow(deprecated)]379 <SelfSponsoring<T>>::get(contract).then_some(SponsoringModeT::Allowlisted)380 })381 .unwrap_or_default()382 }383384 385 386 pub fn set_sponsoring_mode(contract: H160, mode: SponsoringModeT) {387 if mode == SponsoringModeT::Disabled {388 <SponsoringMode<T>>::remove(contract);389 } else {390 <SponsoringMode<T>>::insert(contract, mode);391 }392 #[allow(deprecated)]393 <SelfSponsoring<T>>::remove(contract)394 }395396 397 pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: BlockNumberFor<T>) {398 <SponsoringRateLimit<T>>::insert(contract, rate_limit);399 }400401 402 pub fn set_sponsoring_fee_limit(contract: H160, fee_limit: U256) -> DispatchResult {403 <SponsoringFeeLimit<T>>::try_mutate(contract, |limits_map| {404 limits_map405 .try_insert(0xffffffff, fee_limit)406 .map_err(|_| <Error<T>>::TooManyMethodsHaveSponsoredLimit)407 })?;408 Ok(())409 }410411 412 pub fn allowed(contract: H160, user: H160) -> bool {413 <Allowlist<T>>::get(contract, user) || <Owner<T>>::get(contract) == user414 }415416 417 pub fn toggle_allowlist(contract: H160, enabled: bool) {418 <AllowlistEnabled<T>>::insert(contract, enabled)419 }420421 422 pub fn toggle_allowed(contract: H160, user: H160, allowed: bool) {423 <Allowlist<T>>::insert(contract, user, allowed);424 }425426 427 pub fn ensure_owner(contract: H160, user: H160) -> DispatchResult {428 ensure!(<Owner<T>>::get(contract) == user, Error::<T>::NoPermission);429 Ok(())430 }431 }432}433434435#[derive(436 Encode, Decode, Debug, PartialEq, TypeInfo, MaxEncodedLen, Default, AbiCoder, Clone, Copy,437)]438#[repr(u8)]439pub enum SponsoringModeT {440 441 #[default]442 Disabled,443 444 Allowlisted,445 446 Generous,447}