12345678910111213141516171819use core::{convert::TryInto, marker::PhantomData};20use evm_coder::{Call, abi::AbiReader};21use pallet_common::{CollectionHandle, eth::map_eth_to_id};22use pallet_evm::account::CrossAccountId;23use pallet_evm_transaction_payment::CallContext;24use pallet_nonfungible::{25 Config as NonfungibleConfig, Pallet as NonfungiblePallet, NonfungibleHandle,26 erc::{27 UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721UniqueMintableCall, ERC721Call,28 TokenPropertiesCall,29 },30};31use pallet_fungible::{32 Config as FungibleConfig,33 erc::{UniqueFungibleCall, ERC20Call},34};35use pallet_refungible::{36 Config as RefungibleConfig,37 erc::UniqueRefungibleCall,38 erc_token::{RefungibleTokenHandle, UniqueRefungibleTokenCall},39 RefungibleHandle,40};41use pallet_unique::Config as UniqueConfig;42use sp_std::prelude::*;43use up_data_structs::{44 CollectionMode, CreateItemData, CreateNftData, mapping::TokenAddressMapping, TokenId,45};46use up_sponsorship::SponsorshipHandler;47use crate::{Runtime, runtime_common::sponsoring::*};4849mod refungible;5051pub type EvmSponsorshipHandler = (52 UniqueEthSponsorshipHandler<Runtime>,53 pallet_evm_contract_helpers::HelpersContractSponsoring<Runtime>,54);5556pub struct UniqueEthSponsorshipHandler<T: UniqueConfig>(PhantomData<*const T>);57impl<T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig>58 SponsorshipHandler<T::CrossAccountId, CallContext> for UniqueEthSponsorshipHandler<T>59where60 T::AccountId: From<[u8; 32]>,61{62 fn get_sponsor(63 who: &T::CrossAccountId,64 call_context: &CallContext,65 ) -> Option<T::CrossAccountId> {66 if let Some(collection_id) = map_eth_to_id(&call_context.contract_address) {67 let collection = <CollectionHandle<T>>::new(collection_id)?;68 let sponsor = collection.sponsorship.sponsor()?.clone();69 let (method_id, mut reader) = AbiReader::new_call(&call_context.input).ok()?;70 Some(T::CrossAccountId::from_sub(match &collection.mode {71 CollectionMode::NFT => {72 let collection = NonfungibleHandle::cast(collection);73 let call = <UniqueNFTCall<T>>::parse(method_id, &mut reader).ok()??;74 match call {75 UniqueNFTCall::TokenProperties(call) => match call {76 TokenPropertiesCall::SetProperty {77 token_id,78 key,79 value,80 ..81 } => {82 let token_id: TokenId = token_id.try_into().ok()?;83 withdraw_set_existing_token_property::<T>(84 &collection,85 who,86 &token_id,87 key.len() + value.len(),88 )89 .map(|()| sponsor)90 }91 TokenPropertiesCall::SetProperties {92 token_id,93 properties,94 ..95 } => {96 let token_id: TokenId = token_id.try_into().ok()?;97 let data_size = properties98 .into_iter()99 .map(|p| p.key().len() + p.value().len())100 .sum();101102 withdraw_set_existing_token_property::<T>(103 &collection,104 who,105 &token_id,106 data_size,107 )108 .map(|()| sponsor)109 }110 _ => None,111 },112 UniqueNFTCall::ERC721UniqueExtensions(call) => match call {113 ERC721UniqueExtensionsCall::Transfer { token_id, .. } => {114 let token_id: TokenId = token_id.try_into().ok()?;115 withdraw_transfer::<T>(&collection, who, &token_id)116 .map(|()| sponsor)117 }118 ERC721UniqueExtensionsCall::MintCross { properties, .. } => {119 withdraw_create_item::<T>(120 &collection,121 who,122 &CreateItemData::NFT(CreateNftData::default()),123 )?;124125 let token_id =126 <NonfungiblePallet<T>>::next_token_id(&collection).ok()?;127 let data_size: usize = properties128 .into_iter()129 .map(|p| p.key().len() + p.value().len())130 .sum();131132 withdraw_set_token_property::<T>(&collection, &token_id, data_size)133 .map(|()| sponsor)134 }135 _ => None,136 },137 UniqueNFTCall::ERC721UniqueMintable(138 ERC721UniqueMintableCall::Mint { .. }139 | ERC721UniqueMintableCall::MintCheckId { .. }140 | ERC721UniqueMintableCall::MintWithTokenUri { .. }141 | ERC721UniqueMintableCall::MintWithTokenUriCheckId { .. },142 ) => withdraw_create_item::<T>(143 &collection,144 who,145 &CreateItemData::NFT(CreateNftData::default()),146 )147 .map(|()| sponsor),148 UniqueNFTCall::ERC721(ERC721Call::TransferFrom {149 token_id, from, ..150 }) => {151 let token_id: TokenId = token_id.try_into().ok()?;152 let from = T::CrossAccountId::from_eth(from);153 withdraw_transfer::<T>(&collection, &from, &token_id).map(|()| sponsor)154 }155 UniqueNFTCall::ERC721(ERC721Call::Approve { token_id, .. }) => {156 let token_id: TokenId = token_id.try_into().ok()?;157 withdraw_approve::<T>(&collection, who.as_sub(), &token_id)158 .map(|()| sponsor)159 }160 _ => None,161 }162 }163 CollectionMode::ReFungible => {164 let call = <UniqueRefungibleCall<T>>::parse(method_id, &mut reader).ok()??;165 refungible::call_sponsor(call, collection, who).map(|()| sponsor)166 }167 CollectionMode::Fungible(_) => {168 let call = <UniqueFungibleCall<T>>::parse(method_id, &mut reader).ok()??;169 match call {170 UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {171 withdraw_transfer::<T>(&collection, who, &TokenId::default())172 .map(|()| sponsor)173 }174 UniqueFungibleCall::ERC20(ERC20Call::TransferFrom { from, .. }) => {175 let from = T::CrossAccountId::from_eth(from);176 withdraw_transfer::<T>(&collection, &from, &TokenId::default())177 .map(|()| sponsor)178 }179 UniqueFungibleCall::ERC20(ERC20Call::Approve { .. }) => {180 withdraw_approve::<T>(&collection, who.as_sub(), &TokenId::default())181 .map(|()| sponsor)182 }183 _ => None,184 }185 }186 }?))187 } else {188 let (collection_id, token_id) =189 T::EvmTokenAddressMapping::address_to_token(&call_context.contract_address)?;190 let collection = <CollectionHandle<T>>::new(collection_id)?;191 if collection.mode != CollectionMode::ReFungible {192 return None;193 }194 let sponsor = collection.sponsorship.sponsor()?.clone();195 let rft_collection = RefungibleHandle::cast(collection);196 197 let token = RefungibleTokenHandle(rft_collection, token_id);198199 let (method_id, mut reader) = AbiReader::new_call(&call_context.input).ok()?;200 let call = <UniqueRefungibleTokenCall<T>>::parse(method_id, &mut reader).ok()??;201 Some(T::CrossAccountId::from_sub(202 refungible::token_call_sponsor(call, token, who).map(|()| sponsor)?,203 ))204 }205 }206}207208mod common {209 use super::*;210211 use pallet_common::erc::{CollectionCall};212213 pub fn collection_call_sponsor<T>(214 call: CollectionCall<T>,215 _collection: CollectionHandle<T>,216 _who: &T::CrossAccountId,217 ) -> Option<()>218 where219 T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,220 {221 use CollectionCall::*;222223 match call {224 225 ERC165Call(_, _)226 | CollectionProperty { .. }227 | CollectionProperties { .. }228 | HasCollectionPendingSponsor229 | CollectionSponsor230 | ContractAddress231 | AllowlistedCross { .. }232 | IsOwnerOrAdminEth { .. }233 | IsOwnerOrAdminCross { .. }234 | CollectionOwner235 | CollectionAdmins236 | CollectionLimits237 | CollectionNestingRestrictedIds238 | CollectionNestingPermissions239 | UniqueCollectionType => None,240241 242 AddToCollectionAllowList { .. }243 | AddToCollectionAllowListCross { .. }244 | RemoveFromCollectionAllowList { .. }245 | RemoveFromCollectionAllowListCross { .. }246 | AddCollectionAdminCross { .. }247 | RemoveCollectionAdminCross { .. }248 | AddCollectionAdmin { .. }249 | RemoveCollectionAdmin { .. }250 | SetNestingBool { .. }251 | SetNesting { .. }252 | SetCollectionAccess { .. }253 | SetCollectionMintMode { .. }254 | SetOwner { .. }255 | ChangeCollectionOwnerCross { .. }256 | SetCollectionProperty { .. }257 | SetCollectionProperties { .. }258 | DeleteCollectionProperty { .. }259 | DeleteCollectionProperties { .. }260 | SetCollectionSponsor { .. }261 | SetCollectionSponsorCross { .. }262 | SetCollectionLimit { .. }263 | ConfirmCollectionSponsorship264 | RemoveCollectionSponsor => None,265 }266 }267}