git.delta.rocks / unique-network / refs/commits / e9291bd41a30

difftreelog

Add economic model POC

Greg Zaitsev2020-07-27parent: #31df977.patch.diff
in: master

5 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3085,10 +3085,12 @@
 dependencies = [
  "frame-support",
  "frame-system",
+ "pallet-transaction-payment",
  "parity-scale-codec",
  "sp-core",
  "sp-io",
  "sp-runtime",
+ "sp-std",
 ]
 
 [[package]]
modifieddoc/application_development.mddiffbeforeafterboth
before · doc/application_development.md
1# Building an NFT Application23## Architecture45Both centralized and serverless architectures are supported and application creator can decide which one to use depending on how much logic and data they intend to manage off-chain and off-line.67### Server-based architecture89In server-based architecture the application creator manually creates NFT collections and authorizes server to perform operations on each collection. See NFT palette methods that have admin permission level to understand better what server (or administrators) can do.1011![](server_architecture.png)1213### Serverless architecture1415In serverless architecture the application creator does all initialization and token distribution manually. Alternatively, smart contracts may be used in order to implement some distribution mechanics such as token sales or claiming. The dApp has access to all properties of an NFT token, but it does not have any elevated permissions, since it resides on user site. All operations happen on user's behalf signed by user's private key.1617![](serverless_architecture.png)1819## NFT Palette Methods2021### Collection Management2223#### CreateCollection2425##### Description26This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.2728##### Permissions29Anyone3031##### Parameters32customDataSz: Size of NFT properties data.3334##### Events35CollectionCreated36CollectionID: Globally unique identifier of newly created collection.37Owner: Collection owner3839#### ChangeCollectionOwner4041##### Description42Change the owner of the collection4344##### Permissions45Collection Owner4647##### Parameters48CollectionId4950#### DestroyCollection5152##### Description53DANGEROUS: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.5455##### Permissions56Collection Owner5758##### Parameters59CollectionId6061#### CreateItem6263##### Description64This method creates a concrete instance of NFT Collection created with CreateCollection method.6566##### Permissions67Collection Owner68Collection Admin6970##### Parameters71CollectionID: ID of the collection72Properties: Array of bytes that contains NFT properties. Since NFT Module is agnostic of properties’ meaning, it is treated purely as an array of bytes73Owner: Address, initial owner of the NFT7475##### Events76ItemCreated77ItemId: Identifier of newly created NFT, which is unique within the Collection, so the NFT is uniquely identified with a pair of values: CollectionId and ItemId.7879#### BurnItem8081##### Description82This method destroys a concrete instance of NFT.8384##### Permissions85Collection Owner86Collection Admin87Current NFT Owner8889##### Parameters90CollectionID: ID of the collection91ItemID: ID of NFT to burn9293##### Events94ItemDestroyed95CollectionID96ItemId: Identifier of burned NFT979899#### AddCollectionAdmin100101##### Description102NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership.103104This method adds an admin of the Collection.105106##### Permissions107Collection Owner108Collection Admin109110##### Parameters111CollectionID: ID of the Collection to add admin for112Admin: Address of new admin to add113114#### RemoveCollectionAdmin115116##### Description117Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.118119##### Permissions120Collection Owner121Collection Admin122123##### Parameters124CollectionID: ID of the Collection to remove admin for125Admin: Address of admin to remove126127### Item Ownership and Transfers128This group of methods allows managing NFT ownership.129130#### GetOwner131132##### Description133Return the address of the NFT owner. 134135##### Permissions136Anyone137138##### Parameters139CollectionId140ItemId: ID of the NFT141142##### Returns143Owner address144145#### BalanceOf146147##### Description148This method is included for compatibility with ERC-721. Return the total count of NFTs of a given Collection that belong to a given address. 149150##### Permissions151Anyone152153##### Parameters154CollectionId155Address to count NFTs for156157##### Returns158Total count of NFTs for Address159160161#### Transfer162163##### Description164Change ownership of the NFT.165166##### Permissions167Collection Owner168Collection Admin169Current NFT owner170171##### Parameters172Recipient: Address of token recipient173ClassId: ID of item class174ItemId: ID of the item175176#### TransferFrom177178##### Description179Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.180181##### Permissions182Collection Owner183Collection Admin184Current NFT owner185Address approved by current NFT owner186187##### Parameters188Recipient: Address of token recipient189ClassId: ID of item class190ItemId: ID of the item191192193#### Approve194195##### Description196Set, change, or remove approved address to transfer the ownership of the NFT.197198##### Permissions199Collection Owner200Collection Admin201Current NFT owner202203##### Parameters204Approved: Address that is approved to transfer this NFT or zero (if needed to remove approval)205ClassId: ID of item class206ItemId: ID of the item207208#### GetApproved209210##### Description211Get the approved address for a single NFT.212213##### Permissions214Anyone215216##### Parameters217ClassId: ID of item class218ItemId: ID of the item219220##### Returns221Approved address
after · doc/application_development.md
1# Building an NFT Application23## Architecture45Both centralized and serverless architectures are supported and application creator can decide which one to use depending on how much logic and data they intend to manage off-chain and off-line.67### Server-based architecture89In server-based architecture the application creator manually creates NFT collections and authorizes server to perform operations on each collection. See NFT palette methods that have admin permission level to understand better what server (or administrators) can do.1011![](server_architecture.png)1213### Serverless architecture1415In serverless architecture the application creator does all initialization and token distribution manually. Alternatively, smart contracts may be used in order to implement some distribution mechanics such as token sales or claiming. The dApp has access to all properties of an NFT token, but it does not have any elevated permissions, since it resides on user site. All operations happen on user's behalf signed by user's private key.1617![](serverless_architecture.png)1819## Custom Types for JS API2021```22{23  "Schedule": {24    "version": "u32",25    "put_code_per_byte_cost": "Gas",26    "grow_mem_cost": "Gas",27    "regular_op_cost": "Gas",28    "return_data_per_byte_cost": "Gas",29    "event_data_per_byte_cost": "Gas",30    "event_per_topic_cost": "Gas",31    "event_base_cost": "Gas",32    "call_base_cost": "Gas",33    "instantiate_base_cost": "Gas",34    "dispatch_base_cost": "Gas",35    "sandbox_data_read_cost": "Gas",36    "sandbox_data_write_cost": "Gas",37    "transfer_cost": "Gas",38    "instantiate_cost": "Gas",39    "max_event_topics": "u32",40    "max_stack_height": "u32",41    "max_memory_pages": "u32",42    "max_table_size": "u32",43    "enable_println": "bool",44    "max_subject_len": "u32"45  },46  "NftItemType": {47    "Collection": "u64",48    "Owner": "AccountId",49    "Data": "Vec<u8>"50  },51  "CollectionType": {52    "Owner": "AccountId",53    "NextItemId": "u64",54    "Name": "Vec<u16>",55    "Description": "Vec<u16>",56    "TokenPrefix": "Vec<u8>",57    "CustomDataSize": "u32",58    "Sponsor": "AccountId",59    "UnconfirmedSponsor": "AccountId"60  },61  "Address": "AccountId",62  "LookupSource": "AccountId",63  "Weight": "u64"64}65```6667## NFT Palette Methods6869### Collection Management7071#### CreateCollection7273##### Description74This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.7576##### Permissions77Anyone7879##### Parameters80customDataSz: Size of NFT properties data.8182##### Events83CollectionCreated84CollectionID: Globally unique identifier of newly created collection.85Owner: Collection owner8687#### ChangeCollectionOwner8889##### Description90Change the owner of the collection9192##### Permissions93Collection Owner9495##### Parameters96CollectionId9798#### DestroyCollection99100##### Description101DANGEROUS: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.102103##### Permissions104Collection Owner105106##### Parameters107CollectionId108109#### CreateItem110111##### Description112This method creates a concrete instance of NFT Collection created with CreateCollection method.113114##### Permissions115Collection Owner116Collection Admin117118##### Parameters119CollectionID: ID of the collection120Properties: Array of bytes that contains NFT properties. Since NFT Module is agnostic of properties’ meaning, it is treated purely as an array of bytes121Owner: Address, initial owner of the NFT122123##### Events124ItemCreated125ItemId: Identifier of newly created NFT, which is unique within the Collection, so the NFT is uniquely identified with a pair of values: CollectionId and ItemId.126127#### BurnItem128129##### Description130This method destroys a concrete instance of NFT.131132##### Permissions133Collection Owner134Collection Admin135Current NFT Owner136137##### Parameters138CollectionID: ID of the collection139ItemID: ID of NFT to burn140141##### Events142ItemDestroyed143CollectionID144ItemId: Identifier of burned NFT145146147#### AddCollectionAdmin148149##### Description150NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership.151152This method adds an admin of the Collection.153154##### Permissions155Collection Owner156Collection Admin157158##### Parameters159CollectionID: ID of the Collection to add admin for160Admin: Address of new admin to add161162#### RemoveCollectionAdmin163164##### Description165Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.166167##### Permissions168Collection Owner169Collection Admin170171##### Parameters172CollectionID: ID of the Collection to remove admin for173Admin: Address of admin to remove174175### Item Ownership and Transfers176This group of methods allows managing NFT ownership.177178#### GetOwner179180##### Description181Return the address of the NFT owner. 182183##### Permissions184Anyone185186##### Parameters187CollectionId188ItemId: ID of the NFT189190##### Returns191Owner address192193#### BalanceOf194195##### Description196This method is included for compatibility with ERC-721. Return the total count of NFTs of a given Collection that belong to a given address. 197198##### Permissions199Anyone200201##### Parameters202CollectionId203Address to count NFTs for204205##### Returns206Total count of NFTs for Address207208209#### Transfer210211##### Description212Change ownership of the NFT.213214##### Permissions215Collection Owner216Collection Admin217Current NFT owner218219##### Parameters220Recipient: Address of token recipient221ClassId: ID of item class222ItemId: ID of the item223224#### TransferFrom225226##### Description227Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.228229##### Permissions230Collection Owner231Collection Admin232Current NFT owner233Address approved by current NFT owner234235##### Parameters236Recipient: Address of token recipient237ClassId: ID of item class238ItemId: ID of the item239240241#### Approve242243##### Description244Set, change, or remove approved address to transfer the ownership of the NFT.245246##### Permissions247Collection Owner248Collection Admin249Current NFT owner250251##### Parameters252Approved: Address that is approved to transfer this NFT or zero (if needed to remove approval)253ClassId: ID of item class254ItemId: ID of the item255256#### GetApproved257258##### Description259Get the approved address for a single NFT.260261##### Permissions262Anyone263264##### Parameters265ClassId: ID of item class266ItemId: ID of the item267268##### Returns269Approved address
modifiedpallets/nft/Cargo.tomldiffbeforeafterboth
--- a/pallets/nft/Cargo.toml
+++ b/pallets/nft/Cargo.toml
@@ -33,6 +33,19 @@
 branch = 'rc4_ext_dispatch_reenabled'
 version = '2.0.0-rc4'
 
+[dependencies.sp-std]
+default-features = false
+git = 'https://github.com/usetech-llc/substrate.git'
+branch = 'rc4_ext_dispatch_reenabled'
+version = '2.0.0-rc4'
+
+[dependencies.transaction-payment]
+default-features = false
+git = 'https://github.com/usetech-llc/substrate.git'
+package = 'pallet-transaction-payment'
+branch = 'rc4_ext_dispatch_reenabled'
+version = '2.0.0-rc4'
+
 [package]
 authors = ['Substrate DevHub <https://github.com/substrate-developer-hub>']
 description = 'FRAME pallet nft'
@@ -52,4 +65,5 @@
     'frame-support/std',
     'frame-system/std',
     'sp-runtime/std',
+    'sp-std/std',
 ]
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -9,9 +9,32 @@
 
 /// For more guidance on Substrate FRAME, see the example pallet
 /// https://github.com/paritytech/substrate/blob/master/frame/example/src/lib.rs
-use frame_support::{decl_event, decl_module, decl_storage, dispatch::DispatchResult, ensure};
+pub use frame_support::{
+    decl_event, decl_module, decl_storage,
+    construct_runtime, parameter_types,
+    traits::{Currency, Get, ExistenceRequirement, KeyOwnerProofSystem, OnUnbalanced, Randomness, WithdrawReason, Imbalance},
+    weights::{
+        DispatchInfo, PostDispatchInfo, constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
+        IdentityFee, Weight, WeightToFeePolynomial, GetDispatchInfo, Pays,
+    },
+    StorageValue,
+    dispatch::DispatchResult, 
+    IsSubType,
+    ensure
+};
+
 use frame_system::{self as system, ensure_signed};
 use sp_runtime::sp_std::prelude::Vec;
+use sp_std::prelude::*;
+use sp_runtime::{
+	FixedU128, FixedPointOperand, 
+	transaction_validity::{
+		TransactionPriority, ValidTransaction, InvalidTransaction, TransactionValidityError, TransactionValidity
+	},
+	traits::{
+        Saturating, Dispatchable, DispatchInfoOf, PostDispatchInfoOf, SignedExtension, Zero, SaturatedConversion,
+	},
+};
 
 #[cfg(test)]
 mod mock;
@@ -28,6 +51,8 @@
     pub description: Vec<u16>, // 256 include null escape char
     pub token_prefix: Vec<u8>, // 16 include null escape char
     pub custom_data_size: u32,
+    pub sponsor: AccountId,    // Who pays fees. If set to default address, the fees are applied to the transaction sender
+    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship
 }
 
 #[derive(Encode, Decode, Default, Clone, PartialEq)]
@@ -51,6 +76,7 @@
 
     /// The overarching event type.
     type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
+
 }
 
 // This pallet's storage items.
@@ -138,6 +164,8 @@
                 token_prefix: prefix,
                 next_item_id: next_id,
                 custom_data_size: custom_data_sz,
+                sponsor: T::AccountId::default(),
+                unconfirmed_sponsor: T::AccountId::default(),
             };
 
             // Add new collection to map
@@ -237,6 +265,54 @@
         }
 
         #[weight = 0]
+        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {
+
+            let sender = ensure_signed(origin)?;
+            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");
+
+            let mut target_collection = <Collection<T>>::get(collection_id);
+            ensure!(sender == target_collection.owner, "You do not own this collection");
+
+            target_collection.unconfirmed_sponsor = new_sponsor;
+            <Collection<T>>::insert(collection_id, target_collection);
+
+            Ok(())
+        }
+
+        #[weight = 0]
+        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {
+
+            let sender = ensure_signed(origin)?;
+            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");
+
+            let mut target_collection = <Collection<T>>::get(collection_id);
+            ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");
+
+            target_collection.sponsor = target_collection.unconfirmed_sponsor;
+            target_collection.unconfirmed_sponsor = T::AccountId::default();
+            <Collection<T>>::insert(collection_id, target_collection);
+
+            Ok(())
+        }
+
+        #[weight = 0]
+        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {
+
+            let sender = ensure_signed(origin)?;
+            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");
+
+            let mut target_collection = <Collection<T>>::get(collection_id);
+            ensure!(sender == target_collection.owner, "You do not own this collection");
+
+            target_collection.sponsor = T::AccountId::default();
+            <Collection<T>>::insert(collection_id, target_collection);
+
+            Ok(())
+        }
+        
+
+
+        #[weight = 0]
         pub fn create_item(origin, collection_id: u64, properties: Vec<u8>) -> DispatchResult {
 
             let sender = ensure_signed(origin)?;
@@ -490,3 +566,185 @@
         Ok(())
     }
 }
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Economic models
+
+/// Fee multiplier.
+pub type Multiplier = FixedU128;
+
+type BalanceOf<T> =
+	<<T as transaction_payment::Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;
+type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<
+	<T as system::Trait>::AccountId,>>::NegativeImbalance;
+
+
+
+/// Require the transactor pay for themselves and maybe include a tip to gain additional priority
+/// in the queue.
+#[derive(Encode, Decode, Clone, Eq, PartialEq)]
+pub struct ChargeTransactionPayment<T: transaction_payment::Trait + Send + Sync>(#[codec(compact)] BalanceOf<T>);
+
+impl<T:Trait + transaction_payment::Trait + Send + Sync> sp_std::fmt::Debug for ChargeTransactionPayment<T> {
+	#[cfg(feature = "std")]
+	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
+		write!(f, "ChargeTransactionPayment<{:?}>", self.0)
+	}
+	#[cfg(not(feature = "std"))]
+	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
+		Ok(())
+	}
+}
+
+impl<T:Trait + transaction_payment::Trait + Send + Sync> ChargeTransactionPayment<T> where
+	T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Module<T>, T>,
+	BalanceOf<T>: Send + Sync + FixedPointOperand,
+{
+	/// utility constructor. Used only in client/factory code.
+	pub fn from(fee: BalanceOf<T>) -> Self {
+		Self(fee)
+	}
+
+    pub fn traditional_fee(
+        len: usize,
+        info: &DispatchInfoOf<T::Call>,
+        tip: BalanceOf<T>,
+    ) -> BalanceOf<T> where
+        T::Call: Dispatchable<Info=DispatchInfo>,
+    {
+        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)
+    }
+
+	fn withdraw_fee(
+		&self,
+        who: &T::AccountId,
+        call: &T::Call,
+		info: &DispatchInfoOf<T::Call>,
+		len: usize,
+	) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {
+        let tip = self.0;
+
+        // Set fee based on call type. Creating collection costs 1 Unique.
+        // All other transactions have traditional fees so far
+        let fee = match call.is_sub_type() {
+            Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),
+            _ => Self::traditional_fee(len, info, tip)
+
+            // Flat fee model, use only for testing purposes
+            // _ => <BalanceOf<T>>::from(100)
+        };
+
+        // Determine who is paying transaction fee based on ecnomic model
+        // Parse call to extract collection ID and access collection sponsor
+        let sponsor: T::AccountId = match call.is_sub_type() {
+            Some(Call::create_item(collection_id, _properties)) => {
+                <Collection<T>>::get(collection_id).sponsor
+            },
+            Some(Call::transfer(collection_id, _item_id, _new_owner)) => {
+                <Collection<T>>::get(collection_id).sponsor
+            },
+
+            _ => T::AccountId::default()
+        };
+
+        let mut who_pays_fee: T::AccountId = sponsor.clone();
+        if sponsor == T::AccountId::default() {
+            who_pays_fee = who.clone();
+        }
+
+		// Only mess with balances if fee is not zero.
+		if fee.is_zero() {
+			return Ok((fee, None));
+		}
+
+		match <T as transaction_payment::Trait>::Currency::withdraw(
+			&who_pays_fee,
+			fee,
+			if tip.is_zero() {
+				WithdrawReason::TransactionPayment.into()
+			} else {
+				WithdrawReason::TransactionPayment | WithdrawReason::Tip
+			},
+			ExistenceRequirement::KeepAlive,
+		) {
+			Ok(imbalance) => Ok((fee, Some(imbalance))),
+			Err(_) => Err(InvalidTransaction::Payment.into()),
+		}
+	}
+}
+
+impl<T:Trait + transaction_payment::Trait + Send + Sync> SignedExtension for ChargeTransactionPayment<T> where
+    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,
+    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Module<T>, T>,
+{
+	const IDENTIFIER: &'static str = "ChargeTransactionPayment";
+	type AccountId = T::AccountId;
+	type Call = T::Call;
+	type AdditionalSigned = ();
+	type Pre = (BalanceOf<T>, Self::AccountId, Option<NegativeImbalanceOf<T>>, BalanceOf<T>);
+	fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { Ok(()) }
+
+	fn validate(
+		&self,
+		who: &Self::AccountId,
+		call: &Self::Call,
+		info: &DispatchInfoOf<Self::Call>,
+		len: usize,
+	) -> TransactionValidity {
+		let (fee, _) = self.withdraw_fee(who, call, info, len)?;
+
+		let mut r = ValidTransaction::default();
+		// NOTE: we probably want to maximize the _fee (of any type) per weight unit_ here, which
+		// will be a bit more than setting the priority to tip. For now, this is enough.
+		r.priority = fee.saturated_into::<TransactionPriority>();
+		Ok(r)
+	}
+
+	fn pre_dispatch(
+		self,
+		who: &Self::AccountId,
+		call: &Self::Call,
+		info: &DispatchInfoOf<Self::Call>,
+		len: usize
+	) -> Result<Self::Pre, TransactionValidityError> {
+		let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;
+		Ok((self.0, who.clone(), imbalance, fee))
+	}
+
+	fn post_dispatch(
+		pre: Self::Pre,
+		info: &DispatchInfoOf<Self::Call>,
+		post_info: &PostDispatchInfoOf<Self::Call>,
+		len: usize,
+		_result: &DispatchResult,
+	) -> Result<(), TransactionValidityError> {
+		let (tip, who, imbalance, fee) = pre;
+		if let Some(payed) = imbalance {
+			let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(
+				len as u32,
+				info,
+				post_info,
+				tip,
+			);
+			let refund = fee.saturating_sub(actual_fee);
+			let actual_payment = match <T as transaction_payment::Trait>::Currency::deposit_into_existing(&who, refund) {
+				Ok(refund_imbalance) => {
+					// The refund cannot be larger than the up front payed max weight.
+					// `PostDispatchInfo::calc_unspent` guards against such a case.
+					match payed.offset(refund_imbalance) {
+						Ok(actual_payment) => actual_payment,
+						Err(_) => return Err(InvalidTransaction::Payment.into()),
+					}
+				}
+				// We do not recreate the account using the refund. The up front payment
+				// is gone in that case.
+				Err(_) => payed,
+			};
+			let imbalances = actual_payment.split(tip);
+			<T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(Some(imbalances.0).into_iter()
+				.chain(Some(imbalances.1)));
+		}
+		Ok(())
+	}
+}
modifiedruntime/src/lib.rsdiffbeforeafterboth
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -14,13 +14,13 @@
 use sp_api::impl_runtime_apis;
 use sp_consensus_aura::sr25519::AuthorityId as AuraId;
 use sp_core::{crypto::KeyTypeId, OpaqueMetadata};
-use sp_runtime::traits::{
-    BlakeTwo256, Block as BlockT, IdentifyAccount, IdentityLookup, NumberFor, Saturating, Verify,
-};
 use sp_runtime::{
     create_runtime_str, generic, impl_opaque_keys,
     transaction_validity::{TransactionSource, TransactionValidity},
     ApplyExtrinsicResult, MultiSignature,
+	traits::{
+        BlakeTwo256, Block as BlockT, IdentifyAccount, IdentityLookup, NumberFor, Saturating, Verify,
+	},
 };
 use sp_std::prelude::*;
 #[cfg(feature = "std")]
@@ -32,16 +32,22 @@
 pub use contracts::Schedule as ContractsSchedule;
 pub use frame_support::{
     construct_runtime, parameter_types,
-    traits::{KeyOwnerProofSystem, Randomness},
+    traits::{Currency, Get, ExistenceRequirement, KeyOwnerProofSystem, OnUnbalanced, Randomness, WithdrawReason},
     weights::{
-        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
-        IdentityFee, Weight,
+        DispatchInfo, PostDispatchInfo, constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
+        IdentityFee, Weight, WeightToFeePolynomial, GetDispatchInfo, Pays,
     },
     StorageValue,
+	dispatch::DispatchResult,
 };
+use system::{self as system};
 #[cfg(any(feature = "std", test))]
 pub use sp_runtime::BuildStorage;
-pub use sp_runtime::{Perbill, Permill};
+use sp_runtime::{
+	Perbill,
+};
+
+
 pub use timestamp::Call as TimestampCall;
 
 /// Importing a nft pallet
@@ -228,7 +234,8 @@
 }
 
 parameter_types! {
-    pub const ExistentialDeposit: u128 = 500;
+    // pub const ExistentialDeposit: u128 = 500;
+    pub const ExistentialDeposit: u128 = 0;
 }
 
 impl balances::Trait for Runtime {
@@ -331,7 +338,7 @@
     system::CheckEra<Runtime>,
     system::CheckNonce<Runtime>,
     system::CheckWeight<Runtime>,
-    transaction_payment::ChargeTransactionPayment<Runtime>,
+    nft::ChargeTransactionPayment<Runtime>,
 );
 /// Unchecked extrinsic type as expected by this runtime.
 pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;
@@ -486,3 +493,4 @@
         }
     }
 }
+