1234567891011121314151617#![doc = include_str!("../README.md")]18#![cfg_attr(not(feature = "std"), no_std)]19#![warn(missing_docs)]2021use codec::{Decode, Encode, MaxEncodedLen};22use evm_coder::AbiCoder;23pub use pallet::*;24pub use eth::*;25use scale_info::TypeInfo;26use frame_support::storage::bounded_btree_map::BoundedBTreeMap;27pub mod eth;282930pub const MAX_FEE_LIMITED_METHODS: u32 = 5;3132#[frame_support::pallet]33pub mod pallet {34 pub use super::*;35 use crate::eth::ContractHelpersEvents;36 use frame_support::{pallet_prelude::*, sp_runtime::DispatchResult};37 use frame_system::{pallet_prelude::OriginFor, ensure_root};38 use sp_core::{H160, U256};39 use sp_std::vec::Vec;40 use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};41 use up_data_structs::SponsorshipState;42 use evm_coder::ToLog;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<Self::BlockNumber>;58 }5960 #[pallet::error]61 pub enum Error<T> {62 63 NoPermission,6465 66 NoPendingSponsor,6768 69 TooManyMethodsHaveSponsoredLimit,70 }7172 #[pallet::pallet]73 #[pallet::generate_store(trait Store)]74 pub struct Pallet<T>(_);7576 77 78 79 80 #[pallet::storage]81 pub(super) type Owner<T: Config> =82 StorageMap<Hasher = Twox128, Key = H160, Value = H160, QueryKind = ValueQuery>;8384 85 #[pallet::storage]86 type SelfSponsoring<T: Config> =87 StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;8889 90 91 92 93 #[pallet::storage]94 pub(super) type Sponsoring<T: Config> = StorageMap<95 Hasher = Twox64Concat,96 Key = H160,97 Value = SponsorshipState<T::CrossAccountId>,98 QueryKind = ValueQuery,99 >;100101 102 103 104 105 106 107 108 #[pallet::storage]109 pub(super) type SponsoringMode<T: Config> =110 StorageMap<Hasher = Twox128, Key = H160, Value = SponsoringModeT, QueryKind = OptionQuery>;111112 113 114 115 116 #[pallet::storage]117 pub(super) type SponsoringRateLimit<T: Config> = StorageMap<118 Hasher = Twox128,119 Key = H160,120 Value = T::BlockNumber,121 QueryKind = ValueQuery,122 OnEmpty = T::DefaultSponsoringRateLimit,123 >;124125 126 127 128 129 130 #[pallet::storage]131 pub(super) type SponsoringFeeLimit<T: Config> = StorageMap<132 Hasher = Twox128,133 Key = H160,134 Value = BoundedBTreeMap<u32, U256, ConstU32<MAX_FEE_LIMITED_METHODS>>,135 QueryKind = ValueQuery,136 >;137138 #[pallet::storage]139 pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<140 Hasher1 = Twox128,141 Key1 = H160,142 Hasher2 = Twox128,143 Key2 = H160,144 Value = T::BlockNumber,145 QueryKind = OptionQuery,146 >;147148 149 150 151 152 153 154 155 #[pallet::storage]156 pub(super) type AllowlistEnabled<T: Config> =157 StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;158159 160 161 162 163 164 165 166 167 #[pallet::storage]168 pub(super) type Allowlist<T: Config> = StorageDoubleMap<169 Hasher1 = Twox128,170 Key1 = H160,171 Hasher2 = Twox128,172 Key2 = H160,173 Value = bool,174 QueryKind = ValueQuery,175 >;176177 #[pallet::event]178 #[pallet::generate_deposit(fn deposit_event)]179 pub enum Event<T: Config> {180 181 ContractSponsorSet(182 183 H160,184 185 T::AccountId,186 ),187188 189 ContractSponsorshipConfirmed(190 191 H160,192 193 T::AccountId,194 ),195196 197 ContractSponsorRemoved(198 199 H160,200 ),201 }202203 #[pallet::call]204 impl<T: Config> Pallet<T> {205 206 #[pallet::call_index(0)]207 #[pallet::weight(<T as frame_system::Config>::DbWeight::get().reads_writes(208 addresses.len() as u64, addresses.len() as u64 * 2209 ))]210 pub fn migrate_from_self_sponsoring(211 origin: OriginFor<T>,212 addresses: Vec<H160>,213 ) -> DispatchResult {214 ensure_root(origin)?;215 for address in addresses {216 if <SelfSponsoring<T>>::get(address) {217 <SponsoringMode<T>>::set(address, Some(SponsoringModeT::Allowlisted));218 <SelfSponsoring<T>>::remove(address);219 }220 }221 Ok(())222 }223 }224225 impl<T: Config> Pallet<T> {226 227 pub fn contract_owner(contract: H160) -> H160 {228 <Owner<T>>::get(contract)229 }230231 232 233 234 pub fn set_sponsor(235 sender: &T::CrossAccountId,236 contract: H160,237 sponsor: &T::CrossAccountId,238 ) -> DispatchResult {239 Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;240 Sponsoring::<T>::insert(241 contract,242 SponsorshipState::<T::CrossAccountId>::Unconfirmed(sponsor.clone()),243 );244245 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(246 contract,247 sponsor.as_sub().clone(),248 ));249 <PalletEvm<T>>::deposit_log(250 ContractHelpersEvents::ContractSponsorSet {251 contract_address: contract,252 sponsor: *sponsor.as_eth(),253 }254 .to_log(contract),255 );256 Ok(())257 }258259 260 261 262 263 pub fn force_set_sponsor(264 contract_address: H160,265 sponsor: &T::CrossAccountId,266 ) -> DispatchResult {267 Sponsoring::<T>::insert(268 contract_address,269 SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor.clone()),270 );271272 let eth_sponsor = *sponsor.as_eth();273 let sub_sponsor = sponsor.as_sub().clone();274275 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(276 contract_address,277 sub_sponsor.clone(),278 ));279 <PalletEvm<T>>::deposit_log(280 ContractHelpersEvents::ContractSponsorSet {281 contract_address,282 sponsor: eth_sponsor,283 }284 .to_log(contract_address),285 );286287 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(288 contract_address,289 sub_sponsor,290 ));291 <PalletEvm<T>>::deposit_log(292 ContractHelpersEvents::ContractSponsorshipConfirmed {293 contract_address,294 sponsor: eth_sponsor,295 }296 .to_log(contract_address),297 );298299 Ok(())300 }301302 303 304 305 pub fn remove_sponsor(306 sender: &T::CrossAccountId,307 contract_address: H160,308 ) -> DispatchResult {309 Self::ensure_owner(contract_address, *sender.as_eth())?;310 Self::force_remove_sponsor(contract_address)311 }312313 314 315 316 317 pub fn force_remove_sponsor(contract_address: H160) -> DispatchResult {318 Sponsoring::<T>::remove(contract_address);319320 Self::deposit_event(Event::<T>::ContractSponsorRemoved(contract_address));321 <PalletEvm<T>>::deposit_log(322 ContractHelpersEvents::ContractSponsorRemoved { contract_address }323 .to_log(contract_address),324 );325326 Ok(())327 }328329 330 331 332 pub fn confirm_sponsorship(333 sender: &T::CrossAccountId,334 contract_address: H160,335 ) -> DispatchResult {336 match Sponsoring::<T>::get(contract_address) {337 SponsorshipState::Unconfirmed(sponsor) => {338 ensure!(sponsor == *sender, Error::<T>::NoPermission);339 let eth_sponsor = *sponsor.as_eth();340 let sub_sponsor = sponsor.as_sub().clone();341 Sponsoring::<T>::insert(342 contract_address,343 SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor),344 );345346 <Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(347 contract_address,348 sub_sponsor,349 ));350 <PalletEvm<T>>::deposit_log(351 ContractHelpersEvents::ContractSponsorshipConfirmed {352 contract_address,353 sponsor: eth_sponsor,354 }355 .to_log(contract_address),356 );357358 Ok(())359 }360 SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => {361 Err(Error::<T>::NoPendingSponsor.into())362 }363 }364 }365366 367 pub fn get_sponsor(contract: H160) -> Option<T::CrossAccountId> {368 match Sponsoring::<T>::get(contract) {369 SponsorshipState::Disabled | SponsorshipState::Unconfirmed(_) => None,370 SponsorshipState::Confirmed(sponsor) => Some(sponsor),371 }372 }373374 375 376 pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {377 <SponsoringMode<T>>::get(contract)378 .or_else(|| {379 #[allow(deprecated)]380 <SelfSponsoring<T>>::get(contract).then(|| SponsoringModeT::Allowlisted)381 })382 .unwrap_or_default()383 }384385 386 387 pub fn set_sponsoring_mode(contract: H160, mode: SponsoringModeT) {388 if mode == SponsoringModeT::Disabled {389 <SponsoringMode<T>>::remove(contract);390 } else {391 <SponsoringMode<T>>::insert(contract, mode);392 }393 #[allow(deprecated)]394 <SelfSponsoring<T>>::remove(contract)395 }396397 398 pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: T::BlockNumber) {399 <SponsoringRateLimit<T>>::insert(contract, rate_limit);400 }401402 403 pub fn set_sponsoring_fee_limit(contract: H160, fee_limit: U256) -> DispatchResult {404 <SponsoringFeeLimit<T>>::try_mutate(contract, |limits_map| {405 limits_map406 .try_insert(0xffffffff, fee_limit)407 .map_err(|_| <Error<T>>::TooManyMethodsHaveSponsoredLimit)408 })?;409 Ok(())410 }411412 413 pub fn allowed(contract: H160, user: H160) -> bool {414 <Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user415 }416417 418 pub fn toggle_allowlist(contract: H160, enabled: bool) {419 <AllowlistEnabled<T>>::insert(contract, enabled)420 }421422 423 pub fn toggle_allowed(contract: H160, user: H160, allowed: bool) {424 <Allowlist<T>>::insert(contract, user, allowed);425 }426427 428 pub fn ensure_owner(contract: H160, user: H160) -> DispatchResult {429 ensure!(<Owner<T>>::get(&contract) == user, Error::<T>::NoPermission);430 Ok(())431 }432 }433}434435436#[derive(437 Encode, Decode, Debug, PartialEq, TypeInfo, MaxEncodedLen, Default, AbiCoder, Clone, Copy,438)]439#[repr(u8)]440pub enum SponsoringModeT {441 442 #[default]443 Disabled,444 445 Allowlisted,446 447 Generous,448}