difftreelog
refactor share unq sponsoring code with evm
in: master
4 files changed
pallets/nft/src/eth/sponsoring.rsdiffbeforeafterboth--- a/pallets/nft/src/eth/sponsoring.rs
+++ b/pallets/nft/src/eth/sponsoring.rs
@@ -1,123 +1,64 @@
//! Implements EVM sponsoring logic via OnChargeEVMTransaction
-use crate::{Collection, Config, FungibleTransferBasket, NftTransferBasket};
+use crate::{Config, sponsorship::*};
use evm_coder::{Call, abi::AbiReader};
-use frame_support::{
- storage::{StorageDoubleMap},
-};
-use pallet_common::eth::map_eth_to_id;
+use pallet_common::{CollectionHandle, eth::map_eth_to_id};
use sp_core::H160;
use sp_std::prelude::*;
use up_sponsorship::SponsorshipHandler;
use core::marker::PhantomData;
use core::convert::TryInto;
-use nft_data_structs::{CollectionId, NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT};
-use pallet_common::{
- CollectionById,
- account::{CrossAccountId, EvmBackwardsAddressMapping},
-};
+use nft_data_structs::TokenId;
+use up_evm_mapping::EvmBackwardsAddressMapping;
+use pallet_evm::AddressMapping;
use pallet_nonfungible::erc::{UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721Call};
use pallet_fungible::erc::{UniqueFungibleCall, ERC20Call};
-struct AnyError;
-
-fn try_sponsor<T: Config>(
- caller: &H160,
- collection_id: CollectionId,
- collection: &Collection<T>,
- call: &[u8],
-) -> Result<(), AnyError> {
- let (method_id, mut reader) = AbiReader::new_call(call).map_err(|_| AnyError)?;
- match &collection.mode {
- crate::CollectionMode::NFT => {
- let call: UniqueNFTCall = UniqueNFTCall::parse(method_id, &mut reader)
- .map_err(|_| AnyError)?
- .ok_or(AnyError)?;
- match call {
- UniqueNFTCall::ERC721UniqueExtensions(ERC721UniqueExtensionsCall::Transfer {
- token_id,
- ..
- })
- | UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, .. }) => {
- let token_id: u32 = token_id.try_into().map_err(|_| AnyError)?;
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
- let collection_limits = &collection.limits;
- let limit =
- collection_limits.sponsor_transfer_timeout(NFT_SPONSOR_TRANSFER_TIMEOUT);
-
- let mut sponsor = true;
- if <NftTransferBasket<T>>::contains_key(collection_id, token_id) {
- let last_tx_block = <NftTransferBasket<T>>::get(collection_id, token_id);
- let limit_time = last_tx_block + limit.into();
- if block_number <= limit_time {
- sponsor = false;
- }
+pub struct NftEthSponsorshipHandler<T: Config>(PhantomData<*const T>);
+impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for NftEthSponsorshipHandler<T> {
+ fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {
+ let collection_id = map_eth_to_id(&call.0)?;
+ let collection = <CollectionHandle<T>>::new(collection_id)?;
+ let sponsor = collection.sponsorship.sponsor()?.clone();
+ let sponsor =
+ <T as pallet_common::Config>::EvmBackwardsAddressMapping::from_account_id(sponsor);
+ let who = <T as pallet_common::Config>::EvmAddressMapping::into_account_id(*who);
+ let (method_id, mut reader) = AbiReader::new_call(&call.1).ok()?;
+ match &collection.mode {
+ crate::CollectionMode::NFT => {
+ let call = UniqueNFTCall::parse(method_id, &mut reader).ok()??;
+ match call {
+ UniqueNFTCall::ERC721UniqueExtensions(
+ ERC721UniqueExtensionsCall::Transfer { token_id, .. },
+ )
+ | UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, .. }) => {
+ let token_id: TokenId = token_id.try_into().ok()?;
+ withdraw_transfer::<T>(&collection, &who, &token_id).map(|()| sponsor)
}
- if sponsor {
- <NftTransferBasket<T>>::insert(collection_id, token_id, block_number);
- return Ok(());
+ UniqueNFTCall::ERC721(ERC721Call::Approve { token_id, .. }) => {
+ let token_id: TokenId = token_id.try_into().ok()?;
+ withdraw_approve::<T>(&collection, &who, &token_id).map(|()| sponsor)
}
+ _ => None,
}
- _ => {}
}
- }
- crate::CollectionMode::Fungible(_) => {
- let call: UniqueFungibleCall = UniqueFungibleCall::parse(method_id, &mut reader)
- .map_err(|_| AnyError)?
- .ok_or(AnyError)?;
- #[allow(clippy::single_match)]
- match call {
- UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {
- let who = T::CrossAccountId::from_eth(*caller);
- let collection_limits = &collection.limits;
- let limit = collection_limits
- .sponsor_transfer_timeout(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);
-
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
- let mut sponsored = true;
- if <FungibleTransferBasket<T>>::contains_key(collection_id, who.as_sub()) {
- let last_tx_block =
- <FungibleTransferBasket<T>>::get(collection_id, who.as_sub());
- let limit_time = last_tx_block + limit.into();
- if block_number <= limit_time {
- sponsored = false;
- }
- }
- if sponsored {
- <FungibleTransferBasket<T>>::insert(
- collection_id,
- who.as_sub(),
- block_number,
- );
- return Ok(());
+ crate::CollectionMode::Fungible(_) => {
+ let call = UniqueFungibleCall::parse(method_id, &mut reader).ok()??;
+ #[allow(clippy::single_match)]
+ match call {
+ UniqueFungibleCall::ERC20(
+ ERC20Call::Transfer { .. } | ERC20Call::TransferFrom { .. },
+ ) => withdraw_transfer::<T>(&collection, &who, &TokenId::default())
+ .map(|()| sponsor),
+ UniqueFungibleCall::ERC20(ERC20Call::Approve { .. }) => {
+ withdraw_approve::<T>(&collection, &who, &TokenId::default())
+ .map(|()| sponsor)
}
- }
- _ => {}
- }
- }
- _ => {}
- }
- Err(AnyError)
-}
-
-pub struct NftEthSponsorshipHandler<T: Config>(PhantomData<*const T>);
-impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for NftEthSponsorshipHandler<T> {
- fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {
- if let Some(collection_id) = map_eth_to_id(&call.0) {
- if let Some(collection) = <CollectionById<T>>::get(collection_id) {
- if !collection.sponsorship.confirmed() {
- return None;
- }
- if try_sponsor(who, collection_id, &collection, &call.1).is_ok() {
- return collection
- .sponsorship
- .sponsor()
- .cloned()
- .map(T::EvmBackwardsAddressMapping::from_account_id);
+ _ => None,
}
}
+ _ => None,
}
- None
}
}
pallets/nft/src/lib.rsdiffbeforeafterboth--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -136,13 +136,13 @@
//#region Tokens transfer rate limit baskets
/// (Collection id (controlled?2), who created (real))
/// TODO: Off chain worker should remove from this map when collection gets removed
- pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;
+ pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;
/// Collection id (controlled?2), token id (controlled?2)
- pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;
+ pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;
/// Collection id (controlled?2), owning user (real)
- pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;
+ pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;
/// Collection id (controlled?2), token id (controlled?2)
- pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;
+ pub ReFungibleTransferBasket get(fn refungible_transfer_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;
//#endregion
/// Variable metadata sponsoring
@@ -253,7 +253,7 @@
<NftTransferBasket<T>>::remove_prefix(collection_id, None);
<FungibleTransferBasket<T>>::remove_prefix(collection_id, None);
- <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);
+ <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);
<VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);
<NftApproveBasket<T>>::remove_prefix(collection_id, None);
pallets/nft/src/sponsorship.rsdiffbeforeafterboth1use crate::{1use crate::{2 Config, Call, CreateItemBasket, VariableMetaDataBasket, ReFungibleTransferBasket,2 Config, Call, CreateItemBasket, VariableMetaDataBasket, ReFungibleTransferBasket,3 FungibleTransferBasket, NftTransferBasket, CreateItemData, CollectionMode,3 FungibleTransferBasket, NftTransferBasket, CreateItemData, CollectionMode, NftApproveBasket,4 FungibleApproveBasket, RefungibleApproveBasket,4};5};5use core::marker::PhantomData;6use core::marker::PhantomData;6use up_sponsorship::SponsorshipHandler;7use up_sponsorship::SponsorshipHandler;7use frame_support::{8use frame_support::{8 traits::{IsSubType},9 traits::{IsSubType},9 storage::{StorageMap, StorageDoubleMap},10 storage::{StorageMap, StorageDoubleMap, StorageNMap},10};11};11use nft_data_structs::{12use nft_data_structs::{12 TokenId, CollectionId, NFT_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,13 CollectionId, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, NFT_SPONSOR_TRANSFER_TIMEOUT,13 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,14 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, TokenId,14};15};15use pallet_common::{CollectionById};16use pallet_common::{CollectionHandle};1718pub fn withdraw_transfer<T: Config>(19 collection: &CollectionHandle<T>,20 who: &T::AccountId,21 item_id: &TokenId,22) -> Option<()> {23 // sponsor timeout24 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;25 let limit = collection26 .limits27 .sponsor_transfer_timeout(match collection.mode {28 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,29 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,30 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,31 });3233 let last_tx_block = match collection.mode {34 CollectionMode::NFT => <NftTransferBasket<T>>::get(collection.id, item_id),35 CollectionMode::Fungible(_) => <FungibleTransferBasket<T>>::get(collection.id, who),36 CollectionMode::ReFungible => {37 <ReFungibleTransferBasket<T>>::get((collection.id, item_id, who))38 }39 };4041 if let Some(last_tx_block) = last_tx_block {42 let timeout = last_tx_block + limit.into();43 if block_number < timeout {44 return None;45 }46 }4748 match collection.mode {49 CollectionMode::NFT => <NftTransferBasket<T>>::insert(collection.id, item_id, block_number),50 CollectionMode::Fungible(_) => {51 <FungibleTransferBasket<T>>::insert(collection.id, who, block_number)52 }53 CollectionMode::ReFungible => {54 <ReFungibleTransferBasket<T>>::insert((collection.id, item_id, who), block_number)55 }56 };5758 Some(())59}6061pub fn withdraw_create_item<T: Config>(62 collection: &CollectionHandle<T>,63 who: &T::AccountId,64 _properties: &CreateItemData,65) -> Option<()> {66 if _properties.data_size() as u32 > collection.limits.sponsored_data_size() {67 return None;68 }6970 // sponsor timeout71 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;72 let limit = collection73 .limits74 .sponsor_transfer_timeout(match _properties {75 CreateItemData::NFT(_) => NFT_SPONSOR_TRANSFER_TIMEOUT,76 CreateItemData::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,77 CreateItemData::ReFungible(_) => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,78 });7980 if let Some(last_tx_block) = <CreateItemBasket<T>>::get((collection.id, &who)) {81 let timeout = last_tx_block + limit.into();82 if block_number < timeout {83 return None;84 }85 }8687 CreateItemBasket::<T>::insert((collection.id, who.clone()), block_number);8889 Some(())90}9192pub fn withdraw_set_variable_meta_data<T: Config>(93 collection: &CollectionHandle<T>,94 item_id: &TokenId,95 data: &[u8],96) -> Option<()> {97 // Can't sponsor fungible collection, this tx will be rejected98 // as invalid99 if matches!(collection.mode, CollectionMode::Fungible(_)) {100 return None;101 }102 if data.len() > collection.limits.sponsored_data_size() as usize {103 return None;104 }105106 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;107 let limit = collection.limits.sponsored_data_rate_limit()?;108109 if let Some(last_tx_block) = VariableMetaDataBasket::<T>::get(collection.id, item_id) {110 let timeout = last_tx_block + limit.into();111 if block_number < timeout {112 return None;113 }114 }115116 <VariableMetaDataBasket<T>>::insert(collection.id, item_id, block_number);117118 Some(())119}120121pub fn withdraw_approve<T: Config>(122 collection: &CollectionHandle<T>,123 who: &T::AccountId,124 item_id: &TokenId,125) -> Option<()> {126 // sponsor timeout127 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;128 let limit = collection.limits.sponsor_approve_timeout();129130 let last_tx_block = match collection.mode {131 CollectionMode::NFT => <NftApproveBasket<T>>::get(collection.id, item_id),132 CollectionMode::Fungible(_) => <FungibleApproveBasket<T>>::get(collection.id, who),133 CollectionMode::ReFungible => {134 <RefungibleApproveBasket<T>>::get((collection.id, item_id, who))135 }136 };137138 if let Some(last_tx_block) = last_tx_block {139 let timeout = last_tx_block + limit.into();140 if block_number < timeout {141 return None;142 }143 }144145 match collection.mode {146 CollectionMode::NFT => <NftApproveBasket<T>>::insert(collection.id, item_id, block_number),147 CollectionMode::Fungible(_) => {148 <FungibleApproveBasket<T>>::insert(collection.id, who, block_number)149 }150 CollectionMode::ReFungible => {151 <RefungibleApproveBasket<T>>::insert((collection.id, item_id, who), block_number)152 }153 };154155 Some(())156}157158fn load<T: Config>(id: CollectionId) -> Option<(T::AccountId, CollectionHandle<T>)> {159 let collection = CollectionHandle::new(id)?;160 let sponsor = collection.sponsorship.sponsor().cloned()?;161 Some((sponsor, collection))162}1616317pub struct NftSponsorshipHandler<T>(PhantomData<T>);164pub struct NftSponsorshipHandler<T>(PhantomData<T>);18impl<T: Config> NftSponsorshipHandler<T> {19 pub fn withdraw_create_item(20 who: &T::AccountId,21 collection_id: &CollectionId,22 _properties: &CreateItemData,23 ) -> Option<T::AccountId> {24 let collection = CollectionById::<T>::get(collection_id)?;2526 // sponsor timeout27 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;2829 let limit = collection30 .limits31 .sponsor_transfer_timeout(match _properties {32 CreateItemData::NFT(_) => NFT_SPONSOR_TRANSFER_TIMEOUT,33 CreateItemData::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,34 CreateItemData::ReFungible(_) => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,35 });36 if CreateItemBasket::<T>::contains_key((collection_id, &who)) {37 let last_tx_block = CreateItemBasket::<T>::get((collection_id, &who));38 let limit_time = last_tx_block + limit.into();39 if block_number <= limit_time {40 return None;41 }42 }43 CreateItemBasket::<T>::insert((collection_id, who.clone()), block_number);4445 // check free create limit46 if collection.limits.sponsored_data_size() >= (_properties.data_size() as u32) {47 collection.sponsorship.sponsor().cloned()48 } else {49 None50 }51 }5253 pub fn withdraw_transfer(54 who: &T::AccountId,55 collection_id: &CollectionId,56 item_id: &TokenId,57 ) -> Option<T::AccountId> {58 let collection = CollectionById::<T>::get(collection_id)?;5960 let mut sponsor_transfer = false;61 if collection.sponsorship.confirmed() {62 let collection_limits = collection.limits.clone();63 let collection_mode = collection.mode.clone();6465 // sponsor timeout66 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;67 sponsor_transfer = match collection_mode {68 CollectionMode::NFT => {69 // get correct limit70 let limit =71 collection_limits.sponsor_transfer_timeout(NFT_SPONSOR_TRANSFER_TIMEOUT);7273 let mut sponsored = true;74 if NftTransferBasket::<T>::contains_key(collection_id, item_id) {75 let last_tx_block = NftTransferBasket::<T>::get(collection_id, item_id);76 let limit_time = last_tx_block + limit.into();77 if block_number <= limit_time {78 sponsored = false;79 }80 }81 if sponsored {82 NftTransferBasket::<T>::insert(collection_id, item_id, block_number);83 }8485 sponsored86 }87 CollectionMode::Fungible(_) => {88 // get correct limit89 let limit = collection_limits90 .sponsor_transfer_timeout(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);9192 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;93 let mut sponsored = true;94 if FungibleTransferBasket::<T>::contains_key(collection_id, who) {95 let last_tx_block = FungibleTransferBasket::<T>::get(collection_id, who);96 let limit_time = last_tx_block + limit.into();97 if block_number <= limit_time {98 sponsored = false;99 }100 }101 if sponsored {102 FungibleTransferBasket::<T>::insert(collection_id, who, block_number);103 }104105 sponsored106 }107 CollectionMode::ReFungible => {108 // get correct limit109 let limit = collection_limits110 .sponsor_transfer_timeout(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);111112 let mut sponsored = true;113 if ReFungibleTransferBasket::<T>::contains_key(collection_id, item_id) {114 let last_tx_block =115 ReFungibleTransferBasket::<T>::get(collection_id, item_id);116 let limit_time = last_tx_block + limit.into();117 if block_number <= limit_time {118 sponsored = false;119 }120 }121 if sponsored {122 ReFungibleTransferBasket::<T>::insert(collection_id, item_id, block_number);123 }124125 sponsored126 }127 };128 }129130 if !sponsor_transfer {131 None132 } else {133 collection.sponsorship.sponsor().cloned()134 }135 }136137 pub fn withdraw_set_variable_meta_data(138 collection_id: &CollectionId,139 item_id: &TokenId,140 data: &[u8],141 ) -> Option<T::AccountId> {142 let mut sponsor_metadata_changes = false;143144 let collection = CollectionById::<T>::get(collection_id)?;145146 if collection.sponsorship.confirmed() &&147 // Can't sponsor fungible collection, this tx will be rejected148 // as invalid149 !matches!(collection.mode, CollectionMode::Fungible(_)) &&150 data.len() <= collection.limits.sponsored_data_size() as usize151 {152 if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit() {153 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;154155 if VariableMetaDataBasket::<T>::get(collection_id, item_id)156 .map(|last_block| block_number - last_block > rate_limit.into())157 .unwrap_or(true)158 {159 sponsor_metadata_changes = true;160 VariableMetaDataBasket::<T>::insert(collection_id, item_id, block_number);161 }162 }163 }164165 if !sponsor_metadata_changes {166 None167 } else {168 collection.sponsorship.sponsor().cloned()169 }170 }171}172173impl<T, C> SponsorshipHandler<T::AccountId, C> for NftSponsorshipHandler<T>165impl<T, C> SponsorshipHandler<T::AccountId, C> for NftSponsorshipHandler<T>174where166where181 collection_id,173 collection_id,182 data,174 data,183 ..175 ..184 } => Self::withdraw_create_item(who, collection_id, data),176 } => {177 let (sponsor, collection) = load(*collection_id)?;178 withdraw_create_item::<T>(&collection, who, data).map(|()| sponsor)179 }185 Call::transfer {180 Call::transfer {186 collection_id,181 collection_id,187 item_id,182 item_id,188 ..183 ..189 } => Self::withdraw_transfer(who, collection_id, item_id),184 }185 | Call::transfer_from {186 collection_id,187 item_id,188 ..189 } => {190 let (sponsor, collection) = load(*collection_id)?;191 withdraw_transfer::<T>(&collection, who, item_id).map(|()| sponsor)192 }193 Call::approve {194 collection_id,195 item_id,196 ..197 } => {198 let (sponsor, collection) = load(*collection_id)?;199 withdraw_approve::<T>(&collection, who, item_id).map(|()| sponsor)200 }190 Call::set_variable_meta_data {201 Call::set_variable_meta_data {191 collection_id,202 collection_id,192 item_id,203 item_id,193 data,204 data,194 } => Self::withdraw_set_variable_meta_data(collection_id, item_id, data),205 } => {206 let (sponsor, collection) = load(*collection_id)?;207 withdraw_set_variable_meta_data::<T>(&collection, item_id, data).map(|()| sponsor)208 }195 _ => None,209 _ => None,196 }210 }197 }211 }primitives/evm-mapping/src/lib.rsdiffbeforeafterboth--- a/primitives/evm-mapping/src/lib.rs
+++ b/primitives/evm-mapping/src/lib.rs
@@ -4,9 +4,9 @@
use sp_core::H160;
/// Transforms substrate addresses to ethereum (Reverse of `EvmAddressMapping`)
-/// pallet_evm doesn't have this, as it only checks if eth address
+/// pallet_evm doesn't have this, as it only checks if eth address
/// is owned by substrate via `EnsureAddressOrigin` trait
-///
+///
/// This trait implementations shouldn't conflict with used `EnsureAddressOrigin`
pub trait EvmBackwardsAddressMapping<AccountId> {
fn from_account_id(account_id: AccountId) -> H160;