difftreelog
refactor share unq sponsoring code with evm
in: master
4 files changed
pallets/nft/src/eth/sponsoring.rsdiffbeforeafterboth1//! Implements EVM sponsoring logic via OnChargeEVMTransaction1//! Implements EVM sponsoring logic via OnChargeEVMTransaction223use crate::{Collection, Config, FungibleTransferBasket, NftTransferBasket};3use crate::{Config, sponsorship::*};4use evm_coder::{Call, abi::AbiReader};4use evm_coder::{Call, abi::AbiReader};5use frame_support::{6 storage::{StorageDoubleMap},7};8use pallet_common::eth::map_eth_to_id;5use pallet_common::{CollectionHandle, eth::map_eth_to_id};9use sp_core::H160;6use sp_core::H160;10use sp_std::prelude::*;7use sp_std::prelude::*;11use up_sponsorship::SponsorshipHandler;8use up_sponsorship::SponsorshipHandler;12use core::marker::PhantomData;9use core::marker::PhantomData;13use core::convert::TryInto;10use core::convert::TryInto;14use nft_data_structs::{CollectionId, NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT};11use nft_data_structs::TokenId;15use pallet_common::{12use up_evm_mapping::EvmBackwardsAddressMapping;16 CollectionById,13use pallet_evm::AddressMapping;17 account::{CrossAccountId, EvmBackwardsAddressMapping},18};191420use pallet_nonfungible::erc::{UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721Call};15use pallet_nonfungible::erc::{UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721Call};21use pallet_fungible::erc::{UniqueFungibleCall, ERC20Call};16use pallet_fungible::erc::{UniqueFungibleCall, ERC20Call};2223struct AnyError;2425fn try_sponsor<T: Config>(26 caller: &H160,27 collection_id: CollectionId,28 collection: &Collection<T>,29 call: &[u8],30) -> Result<(), AnyError> {31 let (method_id, mut reader) = AbiReader::new_call(call).map_err(|_| AnyError)?;32 match &collection.mode {33 crate::CollectionMode::NFT => {34 let call: UniqueNFTCall = UniqueNFTCall::parse(method_id, &mut reader)35 .map_err(|_| AnyError)?36 .ok_or(AnyError)?;37 match call {38 UniqueNFTCall::ERC721UniqueExtensions(ERC721UniqueExtensionsCall::Transfer {39 token_id,40 ..41 })42 | UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, .. }) => {43 let token_id: u32 = token_id.try_into().map_err(|_| AnyError)?;44 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;45 let collection_limits = &collection.limits;46 let limit =47 collection_limits.sponsor_transfer_timeout(NFT_SPONSOR_TRANSFER_TIMEOUT);4849 let mut sponsor = true;50 if <NftTransferBasket<T>>::contains_key(collection_id, token_id) {51 let last_tx_block = <NftTransferBasket<T>>::get(collection_id, token_id);52 let limit_time = last_tx_block + limit.into();53 if block_number <= limit_time {54 sponsor = false;55 }56 }57 if sponsor {58 <NftTransferBasket<T>>::insert(collection_id, token_id, block_number);59 return Ok(());60 }61 }62 _ => {}63 }64 }65 crate::CollectionMode::Fungible(_) => {66 let call: UniqueFungibleCall = UniqueFungibleCall::parse(method_id, &mut reader)67 .map_err(|_| AnyError)?68 .ok_or(AnyError)?;69 #[allow(clippy::single_match)]70 match call {71 UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {72 let who = T::CrossAccountId::from_eth(*caller);73 let collection_limits = &collection.limits;74 let limit = collection_limits75 .sponsor_transfer_timeout(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);7677 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;78 let mut sponsored = true;79 if <FungibleTransferBasket<T>>::contains_key(collection_id, who.as_sub()) {80 let last_tx_block =81 <FungibleTransferBasket<T>>::get(collection_id, who.as_sub());82 let limit_time = last_tx_block + limit.into();83 if block_number <= limit_time {84 sponsored = false;85 }86 }87 if sponsored {88 <FungibleTransferBasket<T>>::insert(89 collection_id,90 who.as_sub(),91 block_number,92 );93 return Ok(());94 }95 }96 _ => {}97 }98 }99 _ => {}100 }101 Err(AnyError)102}10317104pub struct NftEthSponsorshipHandler<T: Config>(PhantomData<*const T>);18pub struct NftEthSponsorshipHandler<T: Config>(PhantomData<*const T>);105impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for NftEthSponsorshipHandler<T> {19impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for NftEthSponsorshipHandler<T> {106 fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {20 fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {107 if let Some(collection_id) = map_eth_to_id(&call.0) {21 let collection_id = map_eth_to_id(&call.0)?;108 if let Some(collection) = <CollectionById<T>>::get(collection_id) {22 let collection = <CollectionHandle<T>>::new(collection_id)?;109 if !collection.sponsorship.confirmed() {23 let sponsor = collection.sponsorship.sponsor()?.clone();110 return None;24 let sponsor =111 }25 <T as pallet_common::Config>::EvmBackwardsAddressMapping::from_account_id(sponsor);26 let who = <T as pallet_common::Config>::EvmAddressMapping::into_account_id(*who);27 let (method_id, mut reader) = AbiReader::new_call(&call.1).ok()?;28 match &collection.mode {29 crate::CollectionMode::NFT => {30 let call = UniqueNFTCall::parse(method_id, &mut reader).ok()??;31 match call {32 UniqueNFTCall::ERC721UniqueExtensions(33 ERC721UniqueExtensionsCall::Transfer { token_id, .. },34 )35 | UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, .. }) => {36 let token_id: TokenId = token_id.try_into().ok()?;112 if try_sponsor(who, collection_id, &collection, &call.1).is_ok() {37 withdraw_transfer::<T>(&collection, &who, &token_id).map(|()| sponsor)113 return collection38 }114 .sponsorship39 UniqueNFTCall::ERC721(ERC721Call::Approve { token_id, .. }) => {115 .sponsor()40 let token_id: TokenId = token_id.try_into().ok()?;116 .cloned()41 withdraw_approve::<T>(&collection, &who, &token_id).map(|()| sponsor)117 .map(T::EvmBackwardsAddressMapping::from_account_id);42 }118 }43 _ => None,119 }44 }120 }45 }46 crate::CollectionMode::Fungible(_) => {47 let call = UniqueFungibleCall::parse(method_id, &mut reader).ok()??;48 #[allow(clippy::single_match)]49 match call {50 UniqueFungibleCall::ERC20(51 ERC20Call::Transfer { .. } | ERC20Call::TransferFrom { .. },52 ) => withdraw_transfer::<T>(&collection, &who, &TokenId::default())53 .map(|()| sponsor),54 UniqueFungibleCall::ERC20(ERC20Call::Approve { .. }) => {55 withdraw_approve::<T>(&collection, &who, &TokenId::default())56 .map(|()| sponsor)57 }58 _ => None,59 }60 }121 None61 _ => None,62 }122 }63 }123}64}12465pallets/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.rsdiffbeforeafterboth--- a/pallets/nft/src/sponsorship.rs
+++ b/pallets/nft/src/sponsorship.rs
@@ -1,175 +1,167 @@
use crate::{
Config, Call, CreateItemBasket, VariableMetaDataBasket, ReFungibleTransferBasket,
- FungibleTransferBasket, NftTransferBasket, CreateItemData, CollectionMode,
+ FungibleTransferBasket, NftTransferBasket, CreateItemData, CollectionMode, NftApproveBasket,
+ FungibleApproveBasket, RefungibleApproveBasket,
};
use core::marker::PhantomData;
use up_sponsorship::SponsorshipHandler;
use frame_support::{
traits::{IsSubType},
- storage::{StorageMap, StorageDoubleMap},
+ storage::{StorageMap, StorageDoubleMap, StorageNMap},
};
use nft_data_structs::{
- TokenId, CollectionId, NFT_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
- FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ CollectionId, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, NFT_SPONSOR_TRANSFER_TIMEOUT,
+ REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, TokenId,
};
-use pallet_common::{CollectionById};
+use pallet_common::{CollectionHandle};
-pub struct NftSponsorshipHandler<T>(PhantomData<T>);
-impl<T: Config> NftSponsorshipHandler<T> {
- pub fn withdraw_create_item(
- who: &T::AccountId,
- collection_id: &CollectionId,
- _properties: &CreateItemData,
- ) -> Option<T::AccountId> {
- let collection = CollectionById::<T>::get(collection_id)?;
+pub fn withdraw_transfer<T: Config>(
+ collection: &CollectionHandle<T>,
+ who: &T::AccountId,
+ item_id: &TokenId,
+) -> Option<()> {
+ // sponsor timeout
+ let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+ let limit = collection
+ .limits
+ .sponsor_transfer_timeout(match collection.mode {
+ CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,
+ CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ });
- // sponsor timeout
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
-
- let limit = collection
- .limits
- .sponsor_transfer_timeout(match _properties {
- CreateItemData::NFT(_) => NFT_SPONSOR_TRANSFER_TIMEOUT,
- CreateItemData::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
- CreateItemData::ReFungible(_) => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
- });
- if CreateItemBasket::<T>::contains_key((collection_id, &who)) {
- let last_tx_block = CreateItemBasket::<T>::get((collection_id, &who));
- let limit_time = last_tx_block + limit.into();
- if block_number <= limit_time {
- return None;
- }
+ let last_tx_block = match collection.mode {
+ CollectionMode::NFT => <NftTransferBasket<T>>::get(collection.id, item_id),
+ CollectionMode::Fungible(_) => <FungibleTransferBasket<T>>::get(collection.id, who),
+ CollectionMode::ReFungible => {
+ <ReFungibleTransferBasket<T>>::get((collection.id, item_id, who))
}
- CreateItemBasket::<T>::insert((collection_id, who.clone()), block_number);
+ };
- // check free create limit
- if collection.limits.sponsored_data_size() >= (_properties.data_size() as u32) {
- collection.sponsorship.sponsor().cloned()
- } else {
- None
+ if let Some(last_tx_block) = last_tx_block {
+ let timeout = last_tx_block + limit.into();
+ if block_number < timeout {
+ return None;
}
}
- pub fn withdraw_transfer(
- who: &T::AccountId,
- collection_id: &CollectionId,
- item_id: &TokenId,
- ) -> Option<T::AccountId> {
- let collection = CollectionById::<T>::get(collection_id)?;
+ match collection.mode {
+ CollectionMode::NFT => <NftTransferBasket<T>>::insert(collection.id, item_id, block_number),
+ CollectionMode::Fungible(_) => {
+ <FungibleTransferBasket<T>>::insert(collection.id, who, block_number)
+ }
+ CollectionMode::ReFungible => {
+ <ReFungibleTransferBasket<T>>::insert((collection.id, item_id, who), block_number)
+ }
+ };
- let mut sponsor_transfer = false;
- if collection.sponsorship.confirmed() {
- let collection_limits = collection.limits.clone();
- let collection_mode = collection.mode.clone();
+ Some(())
+}
- // sponsor timeout
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
- sponsor_transfer = match collection_mode {
- CollectionMode::NFT => {
- // get correct limit
- let limit =
- collection_limits.sponsor_transfer_timeout(NFT_SPONSOR_TRANSFER_TIMEOUT);
+pub fn withdraw_create_item<T: Config>(
+ collection: &CollectionHandle<T>,
+ who: &T::AccountId,
+ _properties: &CreateItemData,
+) -> Option<()> {
+ if _properties.data_size() as u32 > collection.limits.sponsored_data_size() {
+ return None;
+ }
- let mut sponsored = true;
- if NftTransferBasket::<T>::contains_key(collection_id, item_id) {
- let last_tx_block = NftTransferBasket::<T>::get(collection_id, item_id);
- let limit_time = last_tx_block + limit.into();
- if block_number <= limit_time {
- sponsored = false;
- }
- }
- if sponsored {
- NftTransferBasket::<T>::insert(collection_id, item_id, block_number);
- }
+ // sponsor timeout
+ let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+ let limit = collection
+ .limits
+ .sponsor_transfer_timeout(match _properties {
+ CreateItemData::NFT(_) => NFT_SPONSOR_TRANSFER_TIMEOUT,
+ CreateItemData::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ CreateItemData::ReFungible(_) => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ });
- sponsored
- }
- CollectionMode::Fungible(_) => {
- // get correct limit
- let limit = collection_limits
- .sponsor_transfer_timeout(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);
+ if let Some(last_tx_block) = <CreateItemBasket<T>>::get((collection.id, &who)) {
+ let timeout = last_tx_block + limit.into();
+ if block_number < timeout {
+ return None;
+ }
+ }
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
- let mut sponsored = true;
- if FungibleTransferBasket::<T>::contains_key(collection_id, who) {
- let last_tx_block = FungibleTransferBasket::<T>::get(collection_id, who);
- let limit_time = last_tx_block + limit.into();
- if block_number <= limit_time {
- sponsored = false;
- }
- }
- if sponsored {
- FungibleTransferBasket::<T>::insert(collection_id, who, block_number);
- }
+ CreateItemBasket::<T>::insert((collection.id, who.clone()), block_number);
- sponsored
- }
- CollectionMode::ReFungible => {
- // get correct limit
- let limit = collection_limits
- .sponsor_transfer_timeout(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);
+ Some(())
+}
- let mut sponsored = true;
- if ReFungibleTransferBasket::<T>::contains_key(collection_id, item_id) {
- let last_tx_block =
- ReFungibleTransferBasket::<T>::get(collection_id, item_id);
- let limit_time = last_tx_block + limit.into();
- if block_number <= limit_time {
- sponsored = false;
- }
- }
- if sponsored {
- ReFungibleTransferBasket::<T>::insert(collection_id, item_id, block_number);
- }
+pub fn withdraw_set_variable_meta_data<T: Config>(
+ collection: &CollectionHandle<T>,
+ item_id: &TokenId,
+ data: &[u8],
+) -> Option<()> {
+ // Can't sponsor fungible collection, this tx will be rejected
+ // as invalid
+ if matches!(collection.mode, CollectionMode::Fungible(_)) {
+ return None;
+ }
+ if data.len() > collection.limits.sponsored_data_size() as usize {
+ return None;
+ }
- sponsored
- }
- };
- }
+ let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+ let limit = collection.limits.sponsored_data_rate_limit()?;
- if !sponsor_transfer {
- None
- } else {
- collection.sponsorship.sponsor().cloned()
+ if let Some(last_tx_block) = VariableMetaDataBasket::<T>::get(collection.id, item_id) {
+ let timeout = last_tx_block + limit.into();
+ if block_number < timeout {
+ return None;
}
}
- pub fn withdraw_set_variable_meta_data(
- collection_id: &CollectionId,
- item_id: &TokenId,
- data: &[u8],
- ) -> Option<T::AccountId> {
- let mut sponsor_metadata_changes = false;
+ <VariableMetaDataBasket<T>>::insert(collection.id, item_id, block_number);
- let collection = CollectionById::<T>::get(collection_id)?;
+ Some(())
+}
- if collection.sponsorship.confirmed() &&
- // Can't sponsor fungible collection, this tx will be rejected
- // as invalid
- !matches!(collection.mode, CollectionMode::Fungible(_)) &&
- data.len() <= collection.limits.sponsored_data_size() as usize
- {
- if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit() {
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+pub fn withdraw_approve<T: Config>(
+ collection: &CollectionHandle<T>,
+ who: &T::AccountId,
+ item_id: &TokenId,
+) -> Option<()> {
+ // sponsor timeout
+ let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+ let limit = collection.limits.sponsor_approve_timeout();
- if VariableMetaDataBasket::<T>::get(collection_id, item_id)
- .map(|last_block| block_number - last_block > rate_limit.into())
- .unwrap_or(true)
- {
- sponsor_metadata_changes = true;
- VariableMetaDataBasket::<T>::insert(collection_id, item_id, block_number);
- }
- }
+ let last_tx_block = match collection.mode {
+ CollectionMode::NFT => <NftApproveBasket<T>>::get(collection.id, item_id),
+ CollectionMode::Fungible(_) => <FungibleApproveBasket<T>>::get(collection.id, who),
+ CollectionMode::ReFungible => {
+ <RefungibleApproveBasket<T>>::get((collection.id, item_id, who))
}
+ };
- if !sponsor_metadata_changes {
- None
- } else {
- collection.sponsorship.sponsor().cloned()
+ if let Some(last_tx_block) = last_tx_block {
+ let timeout = last_tx_block + limit.into();
+ if block_number < timeout {
+ return None;
}
}
+
+ match collection.mode {
+ CollectionMode::NFT => <NftApproveBasket<T>>::insert(collection.id, item_id, block_number),
+ CollectionMode::Fungible(_) => {
+ <FungibleApproveBasket<T>>::insert(collection.id, who, block_number)
+ }
+ CollectionMode::ReFungible => {
+ <RefungibleApproveBasket<T>>::insert((collection.id, item_id, who), block_number)
+ }
+ };
+
+ Some(())
}
+fn load<T: Config>(id: CollectionId) -> Option<(T::AccountId, CollectionHandle<T>)> {
+ let collection = CollectionHandle::new(id)?;
+ let sponsor = collection.sponsorship.sponsor().cloned()?;
+ Some((sponsor, collection))
+}
+
+pub struct NftSponsorshipHandler<T>(PhantomData<T>);
impl<T, C> SponsorshipHandler<T::AccountId, C> for NftSponsorshipHandler<T>
where
T: Config,
@@ -181,17 +173,39 @@
collection_id,
data,
..
- } => Self::withdraw_create_item(who, collection_id, data),
+ } => {
+ let (sponsor, collection) = load(*collection_id)?;
+ withdraw_create_item::<T>(&collection, who, data).map(|()| sponsor)
+ }
Call::transfer {
collection_id,
item_id,
..
- } => Self::withdraw_transfer(who, collection_id, item_id),
+ }
+ | Call::transfer_from {
+ collection_id,
+ item_id,
+ ..
+ } => {
+ let (sponsor, collection) = load(*collection_id)?;
+ withdraw_transfer::<T>(&collection, who, item_id).map(|()| sponsor)
+ }
+ Call::approve {
+ collection_id,
+ item_id,
+ ..
+ } => {
+ let (sponsor, collection) = load(*collection_id)?;
+ withdraw_approve::<T>(&collection, who, item_id).map(|()| sponsor)
+ }
Call::set_variable_meta_data {
collection_id,
item_id,
data,
- } => Self::withdraw_set_variable_meta_data(collection_id, item_id, data),
+ } => {
+ let (sponsor, collection) = load(*collection_id)?;
+ withdraw_set_variable_meta_data::<T>(&collection, item_id, data).map(|()| sponsor)
+ }
_ => None,
}
}
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;