difftreelog
feat implement sponsoring primitive for nft
in: master
3 files changed
pallets/nft/Cargo.tomldiffbeforeafterboth--- a/pallets/nft/Cargo.toml
+++ b/pallets/nft/Cargo.toml
@@ -30,6 +30,7 @@
'pallet-transaction-payment/std',
'fp-evm/std',
'nft-data-structs/std',
+ 'up-sponsorship/std',
'sp-std/std',
'sp-api/std',
'sp-runtime/std',
@@ -135,9 +136,14 @@
[dependencies.nft-data-structs]
default-features = false
-path = '../../primitives'
+path = '../../primitives/nft'
version = '0.9.0'
+[dependencies.up-sponsorship]
+default-features = false
+path = '../../primitives/sponsorship'
+version = '0.1.0'
+
[dependencies]
ethereum-tx-sign = { version = "3.0.4", optional = true }
pallets/nft/src/lib.rsdiffbeforeafterboth505051mod default_weights;51mod default_weights;52mod eth;52mod eth;53mod sponsorship;54pub use sponsorship::NftSponsorshipHandler;535554pub use eth::NftErcSupport;56pub use eth::NftErcSupport;55pub use eth::account::*;57pub use eth::account::*;2236 Ok(())2238 Ok(())2237 }2239 }2238 2239 fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2240 ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);22412242 Ok(())2243 }2244}2240}224522412246sp_api::decl_runtime_apis! {2242sp_api::decl_runtime_apis! {pallets/nft/src/sponsorship.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/nft/src/sponsorship.rs
@@ -0,0 +1,203 @@
+use crate::{Config, Call, CollectionById, CreateItemBasket, VariableMetaDataBasket, ReFungibleTransferBasket, FungibleTransferBasket, NftTransferBasket, ChainLimit, CreateItemData, CollectionMode};
+use core::marker::PhantomData;
+use up_sponsorship::SponsorshipHandler;
+use frame_support::{
+ traits::IsSubType,
+ storage::{StorageMap, StorageDoubleMap, StorageValue},
+};
+use nft_data_structs::{TokenId, CollectionId};
+use alloc::vec::Vec;
+
+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)?;
+
+ // sponsor timeout
+ let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+
+ let limit = collection.limits.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;
+ }
+ }
+ CreateItemBasket::<T>::insert((collection_id, who.clone()), block_number);
+
+ // check free create limit
+ if collection.limits.sponsored_data_size >= (_properties.len() as u32) {
+ collection.sponsorship.sponsor()
+ .cloned()
+ } else {
+ None
+ }
+ }
+
+ pub fn withdraw_transfer(
+ who: &T::AccountId,
+ collection_id: &CollectionId,
+ item_id: &TokenId,
+ ) -> Option<T::AccountId> {
+
+ let collection = CollectionById::<T>::get(collection_id)?;
+ let limits = ChainLimit::get();
+
+ let mut sponsor_transfer = false;
+ if collection.sponsorship.confirmed() {
+
+ let collection_limits = collection.limits.clone();
+ let collection_mode = collection.mode.clone();
+
+ // 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: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
+ collection_limits.sponsor_transfer_timeout
+ } else {
+ limits.nft_sponsor_transfer_timeout
+ };
+
+ 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);
+ }
+
+ sponsored
+ }
+ CollectionMode::Fungible(_) => {
+
+ // get correct limit
+ let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
+ collection_limits.sponsor_transfer_timeout
+ } else {
+ limits.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) {
+ 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);
+ }
+
+ sponsored
+ }
+ CollectionMode::ReFungible => {
+
+ // get correct limit
+ let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
+ collection_limits.sponsor_transfer_timeout
+ } else {
+ limits.refungible_sponsor_transfer_timeout
+ };
+
+ 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);
+ }
+
+ sponsored
+ }
+ _ => {
+ false
+ },
+ };
+ }
+
+ if !sponsor_transfer {
+ None
+ } else {
+ collection.sponsorship.sponsor()
+ .cloned()
+ }
+ }
+
+ pub fn withdraw_set_variable_meta_data(
+ collection_id: &CollectionId,
+ item_id: &TokenId,
+ data: &Vec<u8>,
+ ) -> Option<T::AccountId> {
+
+ let mut sponsor_metadata_changes = false;
+
+ let collection = CollectionById::<T>::get(collection_id)?;
+
+ 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;
+
+ if VariableMetaDataBasket::<T>::get(collection_id, item_id)
+ .map(|last_block| block_number - last_block > rate_limit)
+ .unwrap_or(true)
+ {
+ sponsor_metadata_changes = true;
+ VariableMetaDataBasket::<T>::insert(collection_id, item_id, block_number);
+ }
+ }
+ }
+
+ if !sponsor_metadata_changes {
+ None
+ } else {
+ collection.sponsorship.sponsor().cloned()
+ }
+
+ }
+}
+
+impl<T, C> SponsorshipHandler<T::AccountId, C> for NftSponsorshipHandler<T>
+where
+ T: Config,
+ C: IsSubType<Call<T>>
+{
+ fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {
+ match IsSubType::<Call<T>>::is_sub_type(call)? {
+ Call::create_item(collection_id, _owner, _properties) => {
+ Self::withdraw_create_item(who, collection_id, &_properties)
+ },
+ Call::transfer(_new_owner, collection_id, item_id, _value) => {
+ Self::withdraw_transfer(who, collection_id, item_id)
+ },
+ Call::set_variable_meta_data(collection_id, item_id, data) => {
+ Self::withdraw_set_variable_meta_data(collection_id, item_id, &data)
+ },
+ _ => None,
+ }
+ }
+}
\ No newline at end of file