git.delta.rocks / unique-network / refs/commits / 081dbb6ae4fb

difftreelog

feat Separate rpc calls to own group

Trubnikov Sergey2022-09-01parent: #33ed679.patch.diff
in: master

31 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -113,6 +113,20 @@
 checksum = "508b352bb5c066aac251f6daf6b36eccd03e8a88e8081cd44959ea277a3af9a8"
 
 [[package]]
+name = "app-promotion-rpc"
+version = "0.1.0"
+dependencies = [
+ "pallet-common",
+ "pallet-evm",
+ "parity-scale-codec 3.1.5",
+ "sp-api",
+ "sp-core",
+ "sp-runtime",
+ "sp-std",
+ "up-data-structs",
+]
+
+[[package]]
 name = "approx"
 version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5127,6 +5141,7 @@
 name = "opal-runtime"
 version = "0.9.27"
 dependencies = [
+ "app-promotion-rpc",
  "cumulus-pallet-aura-ext",
  "cumulus-pallet-dmp-queue",
  "cumulus-pallet-parachain-system",
@@ -8358,6 +8373,7 @@
 name = "quartz-runtime"
 version = "0.9.27"
 dependencies = [
+ "app-promotion-rpc",
  "cumulus-pallet-aura-ext",
  "cumulus-pallet-dmp-queue",
  "cumulus-pallet-parachain-system",
@@ -8381,6 +8397,7 @@
  "hex-literal",
  "log",
  "orml-vesting",
+ "pallet-app-promotion",
  "pallet-aura",
  "pallet-balances",
  "pallet-base-fee",
@@ -12132,6 +12149,7 @@
 version = "0.1.3"
 dependencies = [
  "anyhow",
+ "app-promotion-rpc",
  "jsonrpsee",
  "pallet-common",
  "pallet-evm",
@@ -12210,6 +12228,7 @@
 name = "unique-node"
 version = "0.9.27"
 dependencies = [
+ "app-promotion-rpc",
  "clap",
  "cumulus-client-cli",
  "cumulus-client-collator",
@@ -12298,6 +12317,7 @@
 name = "unique-rpc"
 version = "0.1.1"
 dependencies = [
+ "app-promotion-rpc",
  "fc-db",
  "fc-mapping-sync",
  "fc-rpc",
@@ -12347,6 +12367,7 @@
 name = "unique-runtime"
 version = "0.9.27"
 dependencies = [
+ "app-promotion-rpc",
  "cumulus-pallet-aura-ext",
  "cumulus-pallet-dmp-queue",
  "cumulus-pallet-parachain-system",
@@ -12370,6 +12391,7 @@
  "hex-literal",
  "log",
  "orml-vesting",
+ "pallet-app-promotion",
  "pallet-aura",
  "pallet-balances",
  "pallet-base-fee",
modifiedclient/rpc/Cargo.tomldiffbeforeafterboth
--- a/client/rpc/Cargo.toml
+++ b/client/rpc/Cargo.toml
@@ -8,6 +8,7 @@
 pallet-common = { default-features = false, path = '../../pallets/common' }
 up-data-structs = { default-features = false, path = '../../primitives/data-structs' }
 up-rpc = { path = "../../primitives/rpc" }
+app-promotion-rpc = { path = "../../primitives/app_promotion_rpc"}
 rmrk-rpc = { path = "../../primitives/rmrk-rpc" }
 codec = { package = "parity-scale-codec", version = "3.1.2" }
 jsonrpsee = { version = "0.14.0", features = ["server", "macros"] }
modifiedclient/rpc/src/lib.rsdiffbeforeafterboth
before · client/rpc/src/lib.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617// Original License18use std::sync::Arc;1920use codec::{Decode, Encode};21use jsonrpsee::{22	core::{RpcResult as Result},23	proc_macros::rpc,24};25use anyhow::anyhow;26use sp_runtime::traits::{AtLeast32BitUnsigned, Member};27use up_data_structs::{28	RpcCollection, CollectionId, CollectionStats, CollectionLimits, TokenId, Property,29	PropertyKeyPermission, TokenData, TokenChild,30};31use sp_api::{BlockId, BlockT, ProvideRuntimeApi, ApiExt};32use sp_blockchain::HeaderBackend;33use up_rpc::UniqueApi as UniqueRuntimeApi;3435// RMRK36use rmrk_rpc::RmrkApi as RmrkRuntimeApi;37use up_data_structs::{38	RmrkCollectionId, RmrkNftId, RmrkBaseId, RmrkNftChild, RmrkThemeName, RmrkResourceId,39};4041pub use rmrk_unique_rpc::RmrkApiServer;4243#[rpc(server)]44#[async_trait]45pub trait UniqueApi<BlockHash, BlockNumber, CrossAccountId, AccountId> {46	/// Get tokens owned by account.47	#[method(name = "unique_accountTokens")]48	fn account_tokens(49		&self,50		collection: CollectionId,51		account: CrossAccountId,52		at: Option<BlockHash>,53	) -> Result<Vec<TokenId>>;5455	/// Get tokens contained within a collection.56	#[method(name = "unique_collectionTokens")]57	fn collection_tokens(58		&self,59		collection: CollectionId,60		at: Option<BlockHash>,61	) -> Result<Vec<TokenId>>;6263	/// Check if the token exists.64	#[method(name = "unique_tokenExists")]65	fn token_exists(66		&self,67		collection: CollectionId,68		token: TokenId,69		at: Option<BlockHash>,70	) -> Result<bool>;7172	/// Get the token owner.73	#[method(name = "unique_tokenOwner")]74	fn token_owner(75		&self,76		collection: CollectionId,77		token: TokenId,78		at: Option<BlockHash>,79	) -> Result<Option<CrossAccountId>>;8081	/// Returns 10 tokens owners in no particular order.82	#[method(name = "unique_tokenOwners")]83	fn token_owners(84		&self,85		collection: CollectionId,86		token: TokenId,87		at: Option<BlockHash>,88	) -> Result<Vec<CrossAccountId>>;8990	/// Get the topmost token owner in the hierarchy of a possibly nested token.91	#[method(name = "unique_topmostTokenOwner")]92	fn topmost_token_owner(93		&self,94		collection: CollectionId,95		token: TokenId,96		at: Option<BlockHash>,97	) -> Result<Option<CrossAccountId>>;9899	/// Get tokens nested directly into the token.100	#[method(name = "unique_tokenChildren")]101	fn token_children(102		&self,103		collection: CollectionId,104		token: TokenId,105		at: Option<BlockHash>,106	) -> Result<Vec<TokenChild>>;107108	/// Get collection properties, optionally limited to the provided keys.109	#[method(name = "unique_collectionProperties")]110	fn collection_properties(111		&self,112		collection: CollectionId,113		keys: Option<Vec<String>>,114		at: Option<BlockHash>,115	) -> Result<Vec<Property>>;116117	/// Get token properties, optionally limited to the provided keys.118	#[method(name = "unique_tokenProperties")]119	fn token_properties(120		&self,121		collection: CollectionId,122		token_id: TokenId,123		keys: Option<Vec<String>>,124		at: Option<BlockHash>,125	) -> Result<Vec<Property>>;126127	/// Get property permissions, optionally limited to the provided keys.128	#[method(name = "unique_propertyPermissions")]129	fn property_permissions(130		&self,131		collection: CollectionId,132		keys: Option<Vec<String>>,133		at: Option<BlockHash>,134	) -> Result<Vec<PropertyKeyPermission>>;135136	/// Get token data, including properties, optionally limited to the provided keys, and total pieces for an RFT.137	#[method(name = "unique_tokenData")]138	fn token_data(139		&self,140		collection: CollectionId,141		token_id: TokenId,142		keys: Option<Vec<String>>,143		at: Option<BlockHash>,144	) -> Result<TokenData<CrossAccountId>>;145146	/// Get the amount of distinctive tokens present in a collection.147	#[method(name = "unique_totalSupply")]148	fn total_supply(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<u32>;149150	/// Get the amount of any user tokens owned by an account.151	#[method(name = "unique_accountBalance")]152	fn account_balance(153		&self,154		collection: CollectionId,155		account: CrossAccountId,156		at: Option<BlockHash>,157	) -> Result<u32>;158159	/// Get the amount of a specific token owned by an account.160	#[method(name = "unique_balance")]161	fn balance(162		&self,163		collection: CollectionId,164		account: CrossAccountId,165		token: TokenId,166		at: Option<BlockHash>,167	) -> Result<String>;168169	/// Get the amount of currently possible sponsored transactions on a token for the fee to be taken off a sponsor.170	#[method(name = "unique_allowance")]171	fn allowance(172		&self,173		collection: CollectionId,174		sender: CrossAccountId,175		spender: CrossAccountId,176		token: TokenId,177		at: Option<BlockHash>,178	) -> Result<String>;179180	/// Get the list of admin accounts of a collection.181	#[method(name = "unique_adminlist")]182	fn adminlist(183		&self,184		collection: CollectionId,185		at: Option<BlockHash>,186	) -> Result<Vec<CrossAccountId>>;187188	/// Get the list of accounts allowed to operate within a collection.189	#[method(name = "unique_allowlist")]190	fn allowlist(191		&self,192		collection: CollectionId,193		at: Option<BlockHash>,194	) -> Result<Vec<CrossAccountId>>;195196	/// Check if a user is allowed to operate within a collection.197	#[method(name = "unique_allowed")]198	fn allowed(199		&self,200		collection: CollectionId,201		user: CrossAccountId,202		at: Option<BlockHash>,203	) -> Result<bool>;204205	/// Get the last token ID created in a collection.206	#[method(name = "unique_lastTokenId")]207	fn last_token_id(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<TokenId>;208209	/// Get collection info by the specified ID.210	#[method(name = "unique_collectionById")]211	fn collection_by_id(212		&self,213		collection: CollectionId,214		at: Option<BlockHash>,215	) -> Result<Option<RpcCollection<AccountId>>>;216217	/// Get chain stats about collections.218	#[method(name = "unique_collectionStats")]219	fn collection_stats(&self, at: Option<BlockHash>) -> Result<CollectionStats>;220221	/// Get the number of blocks until sponsoring a transaction is available.222	#[method(name = "unique_nextSponsored")]223	fn next_sponsored(224		&self,225		collection: CollectionId,226		account: CrossAccountId,227		token: TokenId,228		at: Option<BlockHash>,229	) -> Result<Option<u64>>;230231	/// Get effective collection limits. If not explicitly set, get the chain defaults.232	#[method(name = "unique_effectiveCollectionLimits")]233	fn effective_collection_limits(234		&self,235		collection_id: CollectionId,236		at: Option<BlockHash>,237	) -> Result<Option<CollectionLimits>>;238239	/// Get the total amount of pieces of an RFT.240	#[method(name = "unique_totalPieces")]241	fn total_pieces(242		&self,243		collection_id: CollectionId,244		token_id: TokenId,245		at: Option<BlockHash>,246	) -> Result<Option<String>>;247248	/// Returns the total amount of staked tokens.249	#[method(name = "unique_totalStaked")]250	fn total_staked(&self, staker: Option<CrossAccountId>, at: Option<BlockHash>)251		-> Result<String>;252253	///Returns the total amount of staked tokens per block when staked.254	#[method(name = "unique_totalStakedPerBlock")]255	fn total_staked_per_block(256		&self,257		staker: CrossAccountId,258		at: Option<BlockHash>,259	) -> Result<Vec<(BlockNumber, String)>>;260261	/// Returns the total amount locked by staking tokens.262	#[method(name = "unique_totalStakingLocked")]263	fn total_staking_locked(&self, staker: CrossAccountId, at: Option<BlockHash>)264		-> Result<String>;265266	/// Returns the total amount of tokens pending withdrawal from staking.267	#[method(name = "unique_pendingUnstake")]268	fn pending_unstake(269		&self,270		staker: Option<CrossAccountId>,271		at: Option<BlockHash>,272	) -> Result<String>;273	/// Returns the total amount of tokens pending withdrawal from staking per block.274	#[method(name = "unique_pendingUnstakePerBlock")]275	fn pending_unstake_per_block(276		&self,277		staker: CrossAccountId,278		at: Option<BlockHash>,279	) -> Result<Vec<(BlockNumber, String)>>;280}281282mod rmrk_unique_rpc {283	use super::*;284285	#[rpc(server)]286	#[async_trait]287	pub trait RmrkApi<288		BlockHash,289		AccountId,290		CollectionInfo,291		NftInfo,292		ResourceInfo,293		PropertyInfo,294		BaseInfo,295		PartType,296		Theme,297	>298	{299		/// Get the latest created collection ID.300		#[method(name = "rmrk_lastCollectionIdx")]301		fn last_collection_idx(&self, at: Option<BlockHash>) -> Result<RmrkCollectionId>;302303		/// Get collection info by ID.304		#[method(name = "rmrk_collectionById")]305		fn collection_by_id(306			&self,307			id: RmrkCollectionId,308			at: Option<BlockHash>,309		) -> Result<Option<CollectionInfo>>;310311		/// Get NFT info by collection and NFT IDs.312		#[method(name = "rmrk_nftById")]313		fn nft_by_id(314			&self,315			collection_id: RmrkCollectionId,316			nft_id: RmrkNftId,317			at: Option<BlockHash>,318		) -> Result<Option<NftInfo>>;319320		/// Get tokens owned by an account in a collection.321		#[method(name = "rmrk_accountTokens")]322		fn account_tokens(323			&self,324			account_id: AccountId,325			collection_id: RmrkCollectionId,326			at: Option<BlockHash>,327		) -> Result<Vec<RmrkNftId>>;328329		/// Get tokens nested in an NFT - its direct children (not the children's children).330		#[method(name = "rmrk_nftChildren")]331		fn nft_children(332			&self,333			collection_id: RmrkCollectionId,334			nft_id: RmrkNftId,335			at: Option<BlockHash>,336		) -> Result<Vec<RmrkNftChild>>;337338		/// Get collection properties, created by the user - not the proxy-specific properties.339		#[method(name = "rmrk_collectionProperties")]340		fn collection_properties(341			&self,342			collection_id: RmrkCollectionId,343			filter_keys: Option<Vec<String>>,344			at: Option<BlockHash>,345		) -> Result<Vec<PropertyInfo>>;346347		/// Get NFT properties, created by the user - not the proxy-specific properties.348		#[method(name = "rmrk_nftProperties")]349		fn nft_properties(350			&self,351			collection_id: RmrkCollectionId,352			nft_id: RmrkNftId,353			filter_keys: Option<Vec<String>>,354			at: Option<BlockHash>,355		) -> Result<Vec<PropertyInfo>>;356357		/// Get data of resources of an NFT.358		#[method(name = "rmrk_nftResources")]359		fn nft_resources(360			&self,361			collection_id: RmrkCollectionId,362			nft_id: RmrkNftId,363			at: Option<BlockHash>,364		) -> Result<Vec<ResourceInfo>>;365366		/// Get the priority of a resource in an NFT.367		#[method(name = "rmrk_nftResourcePriority")]368		fn nft_resource_priority(369			&self,370			collection_id: RmrkCollectionId,371			nft_id: RmrkNftId,372			resource_id: RmrkResourceId,373			at: Option<BlockHash>,374		) -> Result<Option<u32>>;375376		/// Get base info by its ID.377		#[method(name = "rmrk_base")]378		fn base(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Option<BaseInfo>>;379380		/// Get all parts of a base.381		#[method(name = "rmrk_baseParts")]382		fn base_parts(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Vec<PartType>>;383384		/// Get the theme names belonging to a base.385		#[method(name = "rmrk_themeNames")]386		fn theme_names(387			&self,388			base_id: RmrkBaseId,389			at: Option<BlockHash>,390		) -> Result<Vec<RmrkThemeName>>;391392		/// Get theme info, including properties, optionally limited to the provided keys.393		#[method(name = "rmrk_themes")]394		fn theme(395			&self,396			base_id: RmrkBaseId,397			theme_name: String,398			filter_keys: Option<Vec<String>>,399			at: Option<BlockHash>,400		) -> Result<Option<Theme>>;401	}402}403404pub struct Unique<C, P> {405	client: Arc<C>,406	_marker: std::marker::PhantomData<P>,407}408409impl<C, P> Unique<C, P> {410	pub fn new(client: Arc<C>) -> Self {411		Self {412			client,413			_marker: Default::default(),414		}415	}416}417418pub struct Rmrk<C, P> {419	client: Arc<C>,420	_marker: std::marker::PhantomData<P>,421}422423impl<C, P> Rmrk<C, P> {424	pub fn new(client: Arc<C>) -> Self {425		Self {426			client,427			_marker: Default::default(),428		}429	}430}431432macro_rules! pass_method {433	(434		$method_name:ident(435			$($(#[map(|$map_arg:ident| $map:expr)])? $name:ident: $ty:ty),* $(,)?436		) -> $result:ty $(=> $mapper:expr)?,437		//$runtime_name:ident $(<$($lt: tt),+>)*438		$runtime_api_macro:ident439		$(; changed_in $ver:expr, $changed_method_name:ident ($($changed_name:expr), * $(,)?) => $fixer:expr)*440	) => {441		fn $method_name(442			&self,443			$(444				$name: $ty,445			)*446			at: Option<<Block as BlockT>::Hash>,447		) -> Result<$result> {448			let api = self.client.runtime_api();449			let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));450			let _api_version = if let Ok(Some(api_version)) =451				api.api_version::<$runtime_api_macro!()>(&at)452			{453				api_version454			} else {455				// unreachable for our runtime456				return Err(anyhow!("api is not available").into())457			};458459			let result = $(if _api_version < $ver {460				api.$changed_method_name(&at, $($changed_name),*).map(|r| r.map($fixer))461			} else)*462			{ api.$method_name(&at, $($((|$map_arg: $ty| $map))? ($name)),*) };463464			Ok(result465				.map_err(|e| anyhow!("unable to query: {e}"))?466				.map_err(|e| anyhow!("runtime error: {e:?}"))$(.map($mapper))??)467		}468	};469}470471macro_rules! unique_api {472	() => {473		dyn UniqueRuntimeApi<Block, BlockNumber, CrossAccountId, AccountId>474	};475}476477macro_rules! rmrk_api {478	() => {479		dyn RmrkRuntimeApi<Block, AccountId, CollectionInfo, NftInfo, ResourceInfo, PropertyInfo, BaseInfo, PartType, Theme>480	};481}482483#[allow(deprecated)]484impl<C, Block, BlockNumber, CrossAccountId, AccountId>485	UniqueApiServer<<Block as BlockT>::Hash, BlockNumber, CrossAccountId, AccountId>486	for Unique<C, Block>487where488	Block: BlockT,489	BlockNumber: Decode + Member + AtLeast32BitUnsigned,490	AccountId: Decode,491	C: 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,492	C::Api: UniqueRuntimeApi<Block, BlockNumber, CrossAccountId, AccountId>,493	CrossAccountId: pallet_evm::account::CrossAccountId<AccountId>,494{495	pass_method!(496		account_tokens(collection: CollectionId, account: CrossAccountId) -> Vec<TokenId>, unique_api497	);498	pass_method!(499		collection_tokens(collection: CollectionId) -> Vec<TokenId>, unique_api500	);501	pass_method!(502		token_exists(collection: CollectionId, token: TokenId) -> bool, unique_api503	);504	pass_method!(505		token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api506	);507	pass_method!(508		topmost_token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api509	);510	pass_method!(token_children(collection: CollectionId, token: TokenId) -> Vec<TokenChild>, unique_api);511	pass_method!(total_supply(collection: CollectionId) -> u32, unique_api);512	pass_method!(account_balance(collection: CollectionId, account: CrossAccountId) -> u32, unique_api);513	pass_method!(balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> String => |v| v.to_string(), unique_api);514	pass_method!(515		allowance(collection: CollectionId, sender: CrossAccountId, spender: CrossAccountId, token: TokenId) -> String => |v| v.to_string(),516		unique_api517	);518519	pass_method!(collection_properties(520		collection: CollectionId,521522		#[map(|keys| string_keys_to_bytes_keys(keys))]523		keys: Option<Vec<String>>524	) -> Vec<Property>, unique_api);525526	pass_method!(token_properties(527		collection: CollectionId,528		token_id: TokenId,529530		#[map(|keys| string_keys_to_bytes_keys(keys))]531		keys: Option<Vec<String>>532	) -> Vec<Property>, unique_api);533534	pass_method!(property_permissions(535		collection: CollectionId,536537		#[map(|keys| string_keys_to_bytes_keys(keys))]538		keys: Option<Vec<String>>539	) -> Vec<PropertyKeyPermission>, unique_api);540541	pass_method!(token_data(542		collection: CollectionId,543		token_id: TokenId,544545		#[map(|keys| string_keys_to_bytes_keys(keys))]546		keys: Option<Vec<String>>,547	) -> TokenData<CrossAccountId>, unique_api);548549	pass_method!(adminlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);550	pass_method!(allowlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);551	pass_method!(allowed(collection: CollectionId, user: CrossAccountId) -> bool, unique_api);552	pass_method!(last_token_id(collection: CollectionId) -> TokenId, unique_api);553	pass_method!(collection_by_id(collection: CollectionId) -> Option<RpcCollection<AccountId>>, unique_api);554	pass_method!(collection_stats() -> CollectionStats, unique_api);555	pass_method!(next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Option<u64>, unique_api);556	pass_method!(effective_collection_limits(collection_id: CollectionId) -> Option<CollectionLimits>, unique_api);557	pass_method!(total_pieces(collection_id: CollectionId, token_id: TokenId) -> Option<String> => |o| o.map(|number| number.to_string()) , unique_api);558	pass_method!(token_owners(collection: CollectionId, token: TokenId) -> Vec<CrossAccountId>, unique_api);559	pass_method!(total_staked(staker: Option<CrossAccountId>) -> String => |v| v.to_string(), unique_api);560	pass_method!(total_staked_per_block(staker: CrossAccountId) -> Vec<(BlockNumber, String)> =>561		|v| v562		.into_iter()563		.map(|(b, a)| (b, a.to_string()))564		.collect::<Vec<_>>(), unique_api);565	pass_method!(total_staking_locked(staker: CrossAccountId) -> String => |v| v.to_string(), unique_api);566	pass_method!(pending_unstake(staker: Option<CrossAccountId>) -> String => |v| v.to_string(), unique_api);567	pass_method!(pending_unstake_per_block(staker: CrossAccountId) -> Vec<(BlockNumber, String)> =>568		|v| v569		.into_iter()570		.map(|(b, a)| (b, a.to_string()))571		.collect::<Vec<_>>(), unique_api);572}573574#[allow(deprecated)]575impl<576		C,577		Block,578		AccountId,579		CollectionInfo,580		NftInfo,581		ResourceInfo,582		PropertyInfo,583		BaseInfo,584		PartType,585		Theme,586	>587	rmrk_unique_rpc::RmrkApiServer<588		<Block as BlockT>::Hash,589		AccountId,590		CollectionInfo,591		NftInfo,592		ResourceInfo,593		PropertyInfo,594		BaseInfo,595		PartType,596		Theme,597	> for Rmrk<C, Block>598where599	C: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,600	C::Api: RmrkRuntimeApi<601		Block,602		AccountId,603		CollectionInfo,604		NftInfo,605		ResourceInfo,606		PropertyInfo,607		BaseInfo,608		PartType,609		Theme,610	>,611	AccountId: Decode + Encode,612	CollectionInfo: Decode,613	NftInfo: Decode,614	ResourceInfo: Decode,615	PropertyInfo: Decode,616	BaseInfo: Decode,617	PartType: Decode,618	Theme: Decode,619	Block: BlockT,620{621	pass_method!(last_collection_idx() -> RmrkCollectionId, rmrk_api);622	pass_method!(collection_by_id(id: RmrkCollectionId) -> Option<CollectionInfo>, rmrk_api);623	pass_method!(nft_by_id(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Option<NftInfo>, rmrk_api);624	pass_method!(account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Vec<RmrkNftId>, rmrk_api);625	pass_method!(nft_children(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<RmrkNftChild>, rmrk_api);626	pass_method!(627		collection_properties(628			collection_id: RmrkCollectionId,629630			#[map(|keys| string_keys_to_bytes_keys(keys))]631			filter_keys: Option<Vec<String>>632		) -> Vec<PropertyInfo>,633		rmrk_api634	);635	pass_method!(636		nft_properties(637			collection_id: RmrkCollectionId,638			nft_id: RmrkNftId,639640			#[map(|keys| string_keys_to_bytes_keys(keys))]641			filter_keys: Option<Vec<String>>642		) -> Vec<PropertyInfo>,643		rmrk_api644	);645	pass_method!(nft_resources(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<ResourceInfo>, rmrk_api);646	pass_method!(nft_resource_priority(collection_id: RmrkCollectionId, nft_id: RmrkNftId, resource_id: RmrkResourceId) -> Option<u32>, rmrk_api);647	pass_method!(base(base_id: RmrkBaseId) -> Option<BaseInfo>, rmrk_api);648	pass_method!(base_parts(base_id: RmrkBaseId) -> Vec<PartType>, rmrk_api);649	pass_method!(theme_names(base_id: RmrkBaseId) -> Vec<RmrkThemeName>, rmrk_api);650	pass_method!(651		theme(652			base_id: RmrkBaseId,653654			#[map(|n| n.into_bytes())]655			theme_name: String,656657			#[map(|keys| string_keys_to_bytes_keys(keys))]658			filter_keys: Option<Vec<String>>659		) -> Option<Theme>, rmrk_api);660}661662fn string_keys_to_bytes_keys(keys: Option<Vec<String>>) -> Option<Vec<Vec<u8>>> {663	keys.map(|keys| keys.into_iter().map(|key| key.into_bytes()).collect())664}
modifiednode/cli/Cargo.tomldiffbeforeafterboth
--- a/node/cli/Cargo.toml
+++ b/node/cli/Cargo.toml
@@ -318,6 +318,7 @@
 pallet-ethereum = { git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" }
 
 unique-rpc = { default-features = false, path = "../rpc" }
+app-promotion-rpc = { path = "../../primitives/app_promotion_rpc", default-features = false}
 rmrk-rpc = { path = "../../primitives/rmrk-rpc" }
 
 [features]
modifiednode/cli/src/service.rsdiffbeforeafterboth
--- a/node/cli/src/service.rs
+++ b/node/cli/src/service.rs
@@ -364,6 +364,7 @@
 		+ pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance>
 		+ sp_api::ApiExt<Block, StateBackend = sc_client_api::StateBackendFor<FullBackend, Block>>
 		+ up_rpc::UniqueApi<Block, BlockNumber, Runtime::CrossAccountId, AccountId>
+		+ app_promotion_rpc::AppPromotionApi<Block, BlockNumber, Runtime::CrossAccountId, AccountId>
 		+ rmrk_rpc::RmrkApi<
 			Block,
 			AccountId,
@@ -665,6 +666,7 @@
 		+ pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance>
 		+ sp_api::ApiExt<Block, StateBackend = sc_client_api::StateBackendFor<FullBackend, Block>>
 		+ up_rpc::UniqueApi<Block, BlockNumber, Runtime::CrossAccountId, AccountId>
+		+ app_promotion_rpc::AppPromotionApi<Block, BlockNumber, Runtime::CrossAccountId, AccountId>
 		+ rmrk_rpc::RmrkApi<
 			Block,
 			AccountId,
@@ -809,6 +811,7 @@
 		+ pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance>
 		+ sp_api::ApiExt<Block, StateBackend = sc_client_api::StateBackendFor<FullBackend, Block>>
 		+ up_rpc::UniqueApi<Block, BlockNumber, Runtime::CrossAccountId, AccountId>
+		+ app_promotion_rpc::AppPromotionApi<Block, BlockNumber, Runtime::CrossAccountId, AccountId>
 		+ rmrk_rpc::RmrkApi<
 			Block,
 			AccountId,
modifiednode/rpc/Cargo.tomldiffbeforeafterboth
--- a/node/rpc/Cargo.toml
+++ b/node/rpc/Cargo.toml
@@ -53,6 +53,7 @@
 pallet-unique = { path = "../../pallets/unique" }
 uc-rpc = { path = "../../client/rpc" }
 up-rpc = { path = "../../primitives/rpc" }
+app-promotion-rpc = { path = "../../primitives/app_promotion_rpc"}
 rmrk-rpc = { path = "../../primitives/rmrk-rpc" }
 up-data-structs = { default-features = false, path = "../../primitives/data-structs" }
 
modifiednode/rpc/src/lib.rsdiffbeforeafterboth
--- a/node/rpc/src/lib.rs
+++ b/node/rpc/src/lib.rs
@@ -38,6 +38,7 @@
 use sp_block_builder::BlockBuilder;
 use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata};
 use sc_service::TransactionPool;
+use uc_rpc::AppPromotion;
 use std::{collections::BTreeMap, sync::Arc};
 
 use up_common::types::opaque::{Hash, AccountId, RuntimeInstance, Index, Block, BlockNumber, Balance};
@@ -147,6 +148,7 @@
 	C::Api: fp_rpc::ConvertTransactionRuntimeApi<Block>,
 	C::Api:
 		up_rpc::UniqueApi<Block, BlockNumber, <R as RuntimeInstance>::CrossAccountId, AccountId>,
+	C::Api: app_promotion_rpc::AppPromotionApi<Block, BlockNumber, <R as RuntimeInstance>::CrossAccountId, AccountId>,
 	C::Api: rmrk_rpc::RmrkApi<
 		Block,
 		AccountId,
@@ -171,6 +173,7 @@
 		EthPubSubApiServer, EthSigner, Net, NetApiServer, Web3, Web3ApiServer,
 	};
 	use uc_rpc::{UniqueApiServer, Unique};
+	use uc_rpc::{AppPromotionApiServer, AppPromotion};
 
 	#[cfg(not(feature = "unique-runtime"))]
 	use uc_rpc::{RmrkApiServer, Rmrk};
@@ -229,6 +232,9 @@
 
 	io.merge(Unique::new(client.clone()).into_rpc())?;
 
+	// #[cfg(not(feature = "unique-runtime"))]
+	io.merge(AppPromotion::new(client.clone()).into_rpc())?;
+
 	#[cfg(not(feature = "unique-runtime"))]
 	io.merge(Rmrk::new(client.clone()).into_rpc())?;
 
addedprimitives/app_promotion_rpc/CHANGELOG.mddiffbeforeafterboth
--- /dev/null
+++ b/primitives/app_promotion_rpc/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+
+<!-- bureaucrate goes here -->
addedprimitives/app_promotion_rpc/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/primitives/app_promotion_rpc/Cargo.toml
@@ -0,0 +1,29 @@
+[package]
+name = "app-promotion-rpc"
+version = "0.1.0"
+license = "GPLv3"
+edition = "2021"
+
+[dependencies]
+pallet-common = { default-features = false, path = '../../pallets/common' }
+up-data-structs = { default-features = false, path = '../data-structs' }
+codec = { package = "parity-scale-codec", version = "3.1.2", default-features = false, features = [
+	"derive",
+] }
+sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" }
+sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" }
+sp-api = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" }
+sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.27" }
+pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" }
+
+[features]
+default = ["std"]
+std = [
+	"codec/std",
+	"sp-core/std",
+	"sp-std/std",
+	"sp-api/std",
+	"sp-runtime/std",
+	"pallet-common/std",
+	"up-data-structs/std",
+]
addedprimitives/app_promotion_rpc/src/lib.rsdiffbeforeafterboth
--- /dev/null
+++ b/primitives/app_promotion_rpc/src/lib.rs
@@ -0,0 +1,47 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+use up_data_structs::{
+	CollectionId, TokenId, RpcCollection, CollectionStats, CollectionLimits, Property,
+	PropertyKeyPermission, TokenData, TokenChild,
+};
+
+use sp_std::vec::Vec;
+use codec::Decode;
+use sp_runtime::{
+	DispatchError,
+	traits::{AtLeast32BitUnsigned, Member},
+};
+
+type Result<T> = core::result::Result<T, DispatchError>;
+
+sp_api::decl_runtime_apis! {
+	#[api_version(2)]
+	/// Trait for generate rpc.
+	pub trait AppPromotionApi<BlockNumber ,CrossAccountId, AccountId> where
+		BlockNumber: Decode + Member + AtLeast32BitUnsigned,
+		AccountId: Decode,
+		CrossAccountId: pallet_evm::account::CrossAccountId<AccountId>,
+	{
+		fn total_staked(staker: Option<CrossAccountId>) -> Result<u128>;
+		fn total_staked_per_block(staker: CrossAccountId) -> Result<Vec<(BlockNumber, u128)>>;
+		fn total_staking_locked(staker: CrossAccountId) -> Result<u128>;
+		fn pending_unstake(staker: Option<CrossAccountId>) -> Result<u128>;
+		fn pending_unstake_per_block(staker: CrossAccountId) -> Result<Vec<(BlockNumber, u128)>>;
+	}
+}
modifiedprimitives/rpc/src/lib.rsdiffbeforeafterboth
--- a/primitives/rpc/src/lib.rs
+++ b/primitives/rpc/src/lib.rs
@@ -127,11 +127,5 @@
 		fn total_pieces(collection_id: CollectionId, token_id: TokenId) -> Result<Option<u128>>;
 
 		fn token_owners(collection: CollectionId, token: TokenId) -> Result<Vec<CrossAccountId>>;
-		fn total_staked(staker: Option<CrossAccountId>) -> Result<u128>;
-		fn total_staked_per_block(staker: CrossAccountId) -> Result<Vec<(BlockNumber, u128)>>;
-		fn total_staking_locked(staker: CrossAccountId) -> Result<u128>;
-		fn pending_unstake(staker: Option<CrossAccountId>) -> Result<u128>;
-		fn pending_unstake_per_block(staker: CrossAccountId) -> Result<Vec<(BlockNumber, u128)>>;
-
 	}
 }
modifiedruntime/common/runtime_apis.rsdiffbeforeafterboth
--- a/runtime/common/runtime_apis.rs
+++ b/runtime/common/runtime_apis.rs
@@ -187,25 +187,47 @@
                 fn total_pieces(collection: CollectionId, token_id: TokenId) -> Result<Option<u128>, DispatchError> {
                     dispatch_unique_runtime!(collection.total_pieces(token_id))
                 }
+            }
 
+            impl app_promotion_rpc::AppPromotionApi<Block, BlockNumber, CrossAccountId, AccountId> for Runtime {
                 fn total_staked(staker: Option<CrossAccountId>) -> Result<u128, DispatchError> {
-                    Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_total_staked(staker).unwrap_or_default())
-                }
+                    #[cfg(not(feature = "app-promotion"))]
+                    return unsupported!();
 
+                    #[cfg(feature = "app-promotion")]
+                    return Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_total_staked(staker).unwrap_or_default());
+                }
+                
                 fn total_staked_per_block(staker: CrossAccountId) -> Result<Vec<(BlockNumber, u128)>, DispatchError> {
-                    Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_total_staked_per_block(staker))
+                    #[cfg(not(feature = "app-promotion"))]
+                    return unsupported!();
+                    
+                    #[cfg(feature = "app-promotion")]
+                    return Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_total_staked_per_block(staker));
                 }
 
                 fn total_staking_locked(staker: CrossAccountId) -> Result<u128, DispatchError> {
-                    Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_locked_balance(staker))
+                    #[cfg(not(feature = "app-promotion"))]
+                    return unsupported!();
+                    
+                    #[cfg(feature = "app-promotion")]
+                    return Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_locked_balance(staker));
                 }
 
                 fn pending_unstake(staker: Option<CrossAccountId>) -> Result<u128, DispatchError> {
-                    Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_pending_unstake(staker))
+                    #[cfg(not(feature = "app-promotion"))]
+                    return unsupported!();
+                    
+                    #[cfg(feature = "app-promotion")]
+                    return Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_pending_unstake(staker));
                 }
 
                 fn pending_unstake_per_block(staker: CrossAccountId) -> Result<Vec<(BlockNumber, u128)>, DispatchError> {
-                    Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_pending_unstake_per_block(staker))
+                    #[cfg(not(feature = "app-promotion"))]
+                    return unsupported!();
+                    
+                    #[cfg(feature = "app-promotion")]
+                    return Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_pending_unstake_per_block(staker))
                 }
             }
 
modifiedruntime/opal/Cargo.tomldiffbeforeafterboth
--- a/runtime/opal/Cargo.toml
+++ b/runtime/opal/Cargo.toml
@@ -83,6 +83,7 @@
     'pallet-base-fee/std',
     'fp-rpc/std',
     'up-rpc/std',
+    'app-promotion-rpc/std',
     'fp-evm-mapping/std',
     'fp-self-contained/std',
     'parachain-info/std',
@@ -414,6 +415,7 @@
 derivative = "2.2.0"
 pallet-unique = { path = '../../pallets/unique', default-features = false }
 up-rpc = { path = "../../primitives/rpc", default-features = false }
+app-promotion-rpc = { path = "../../primitives/app_promotion_rpc", default-features = false}
 rmrk-rpc = { path = "../../primitives/rmrk-rpc", default-features = false }
 fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" }
 pallet-inflation = { path = '../../pallets/inflation', default-features = false }
modifiedruntime/quartz/Cargo.tomldiffbeforeafterboth
--- a/runtime/quartz/Cargo.toml
+++ b/runtime/quartz/Cargo.toml
@@ -82,6 +82,7 @@
     'pallet-base-fee/std',
     'fp-rpc/std',
     'up-rpc/std',
+    'app-promotion-rpc/std',
     'fp-evm-mapping/std',
     'fp-self-contained/std',
     'parachain-info/std',
@@ -416,8 +417,10 @@
 derivative = "2.2.0"
 pallet-unique = { path = '../../pallets/unique', default-features = false }
 up-rpc = { path = "../../primitives/rpc", default-features = false }
+app-promotion-rpc = { path = "../../primitives/app_promotion_rpc", default-features = false}
 fp-evm-mapping = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.27" }
 pallet-inflation = { path = '../../pallets/inflation', default-features = false }
+pallet-app-promotion = { path = '../../pallets/app-promotion', default-features = false }
 up-data-structs = { path = '../../primitives/data-structs', default-features = false }
 pallet-configuration = { default-features = false, path = "../../pallets/configuration" }
 pallet-common = { default-features = false, path = "../../pallets/common" }
modifiedruntime/unique/Cargo.tomldiffbeforeafterboth
--- a/runtime/unique/Cargo.toml
+++ b/runtime/unique/Cargo.toml
@@ -83,6 +83,7 @@
     'pallet-base-fee/std',
     'fp-rpc/std',
     'up-rpc/std',
+    'app-promotion-rpc/std',
     'fp-evm-mapping/std',
     'fp-self-contained/std',
     'parachain-info/std',
@@ -409,8 +410,10 @@
 derivative = "2.2.0"
 pallet-unique = { path = '../../pallets/unique', default-features = false }
 up-rpc = { path = "../../primitives/rpc", default-features = false }
+app-promotion-rpc = { path = "../../primitives/app_promotion_rpc", default-features = false}
 rmrk-rpc = { path = "../../primitives/rmrk-rpc", default-features = false }
 pallet-inflation = { path = '../../pallets/inflation', default-features = false }
+pallet-app-promotion = { path = '../../pallets/app-promotion', default-features = false }
 up-data-structs = { path = '../../primitives/data-structs', default-features = false }
 pallet-configuration = { default-features = false, path = "../../pallets/configuration" }
 pallet-common = { default-features = false, path = "../../pallets/common" }
addedtests/src/interfaces/appPromotion/definitions.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/interfaces/appPromotion/definitions.ts
@@ -0,0 +1,66 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+type RpcParam = {
+  name: string;
+  type: string;
+  isOptional?: true;
+};
+
+const CROSS_ACCOUNT_ID_TYPE = 'PalletEvmAccountBasicCrossAccountIdRepr';
+
+const collectionParam = {name: 'collection', type: 'u32'};
+const tokenParam = {name: 'tokenId', type: 'u32'};
+const propertyKeysParam = {name: 'propertyKeys', type: 'Vec<String>', isOptional: true};
+const crossAccountParam = (name = 'account') => ({name, type: CROSS_ACCOUNT_ID_TYPE});
+const atParam = {name: 'at', type: 'Hash', isOptional: true};
+
+const fun = (description: string, params: RpcParam[], type: string) => ({
+  description,
+  params: [...params, atParam],
+  type,
+});
+
+export default {
+  types: {},
+  rpc: {
+    totalStaked: fun(
+      'Returns the total amount of staked tokens',
+      [{name: 'staker', type: CROSS_ACCOUNT_ID_TYPE, isOptional: true}],
+      'u128',
+    ),
+    totalStakedPerBlock: fun(
+      'Returns the total amount of staked tokens per block when staked',
+      [crossAccountParam('staker')],
+      'Vec<(u32, u128)>',
+    ),
+    totalStakingLocked: fun(
+      'Return the total amount locked by staking tokens',
+      [crossAccountParam('staker')],
+      'u128',
+    ),
+    pendingUnstake: fun(
+      'Returns the total amount of unstaked tokens',
+      [{name: 'staker', type: CROSS_ACCOUNT_ID_TYPE, isOptional: true}],
+      'u128',
+    ),
+    pendingUnstakePerBlock: fun(
+      'Returns the total amount of unstaked tokens per block',
+      [crossAccountParam('staker')],
+      'Vec<(u32, u128)>',
+    ),
+  },
+};
addedtests/src/interfaces/appPromotion/index.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/interfaces/appPromotion/index.ts
@@ -0,0 +1,4 @@
+// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
+/* eslint-disable */
+
+export * from './types';
addedtests/src/interfaces/appPromotion/types.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/interfaces/appPromotion/types.ts
@@ -0,0 +1,4 @@
+// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
+/* eslint-disable */
+
+export type PHANTOM_APPPROMOTION = 'appPromotion';
modifiedtests/src/interfaces/augment-api-errors.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-errors.ts
+++ b/tests/src/interfaces/augment-api-errors.ts
@@ -269,7 +269,7 @@
        **/
       NoPendingSponsor: AugmentedError<ApiType>;
       /**
-       * This method is only executable by owner.
+       * This method is only executable by contract owner
        **/
       NoPermission: AugmentedError<ApiType>;
       /**
@@ -278,7 +278,13 @@
       [key: string]: AugmentedError<ApiType>;
     };
     evmMigration: {
+      /**
+       * Migration of this account is not yet started, or already finished.
+       **/
       AccountIsNotMigrating: AugmentedError<ApiType>;
+      /**
+       * Can only migrate to empty address.
+       **/
       AccountNotEmpty: AugmentedError<ApiType>;
       /**
        * Generic error
modifiedtests/src/interfaces/augment-api-query.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-query.ts
+++ b/tests/src/interfaces/augment-api-query.ts
@@ -335,7 +335,7 @@
        * 
        * Currently used to store RMRK data.
        **/
-      tokenAuxProperties: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: UpDataStructsPropertyScope | 'None' | 'Rmrk' | 'Eth' | number | Uint8Array, arg4: Bytes | string | Uint8Array) => Observable<Option<Bytes>>, [u32, u32, UpDataStructsPropertyScope, Bytes]> & QueryableStorageEntry<ApiType, [u32, u32, UpDataStructsPropertyScope, Bytes]>;
+      tokenAuxProperties: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: UpDataStructsPropertyScope | 'None' | 'Rmrk' | number | Uint8Array, arg4: Bytes | string | Uint8Array) => Observable<Option<Bytes>>, [u32, u32, UpDataStructsPropertyScope, Bytes]> & QueryableStorageEntry<ApiType, [u32, u32, UpDataStructsPropertyScope, Bytes]>;
       /**
        * Used to enumerate token's children.
        **/
modifiedtests/src/interfaces/augment-api-rpc.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-rpc.ts
+++ b/tests/src/interfaces/augment-api-rpc.ts
@@ -35,6 +35,28 @@
 
 declare module '@polkadot/rpc-core/types/jsonrpc' {
   interface RpcInterface {
+    appPromotion: {
+      /**
+       * Returns the total amount of unstaked tokens
+       **/
+      pendingUnstake: AugmentedRpc<(staker?: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u128>>;
+      /**
+       * Returns the total amount of unstaked tokens per block
+       **/
+      pendingUnstakePerBlock: AugmentedRpc<(staker: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Vec<ITuple<[u32, u128]>>>>;
+      /**
+       * Returns the total amount of staked tokens
+       **/
+      totalStaked: AugmentedRpc<(staker?: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u128>>;
+      /**
+       * Returns the total amount of staked tokens per block when staked
+       **/
+      totalStakedPerBlock: AugmentedRpc<(staker: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Vec<ITuple<[u32, u128]>>>>;
+      /**
+       * Return the total amount locked by staking tokens
+       **/
+      totalStakingLocked: AugmentedRpc<(staker: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u128>>;
+    };
     author: {
       /**
        * Returns true if the keystore has private keys for the given public key and key type.
@@ -702,15 +724,7 @@
        * Get the number of blocks until sponsoring a transaction is available
        **/
       nextSponsored: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, account: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Option<u64>>>;
-      /**
-       * Returns the total amount of unstaked tokens
-       **/
-      pendingUnstake: AugmentedRpc<(staker?: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u128>>;
       /**
-       * Returns the total amount of unstaked tokens per block
-       **/
-      pendingUnstakePerBlock: AugmentedRpc<(staker: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Vec<ITuple<[u32, u128]>>>>;
-      /**
        * Get property permissions, optionally limited to the provided keys
        **/
       propertyPermissions: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, propertyKeys?: Vec<Text> | (Text | string)[], at?: Hash | string | Uint8Array) => Observable<Vec<UpDataStructsPropertyKeyPermission>>>;
@@ -746,18 +760,6 @@
        * Get the total amount of pieces of an RFT
        **/
       totalPieces: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Option<u128>>>;
-      /**
-       * Returns the total amount of staked tokens
-       **/
-      totalStaked: AugmentedRpc<(staker?: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u128>>;
-      /**
-       * Returns the total amount of staked tokens per block when staked
-       **/
-      totalStakedPerBlock: AugmentedRpc<(staker: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Vec<ITuple<[u32, u128]>>>>;
-      /**
-       * Return the total amount locked by staking tokens
-       **/
-      totalStakingLocked: AugmentedRpc<(staker: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u128>>;
       /**
        * Get the amount of distinctive tokens present in a collection
        **/
modifiedtests/src/interfaces/augment-api-tx.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-tx.ts
+++ b/tests/src/interfaces/augment-api-tx.ts
@@ -181,8 +181,21 @@
       [key: string]: SubmittableExtrinsicFunction<ApiType>;
     };
     evmMigration: {
+      /**
+       * Start contract migration, inserts contract stub at target address,
+       * and marks account as pending, allowing to insert storage
+       **/
       begin: AugmentedSubmittable<(address: H160 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H160]>;
+      /**
+       * Finish contract migration, allows it to be called.
+       * It is not possible to alter contract storage via [`Self::set_data`]
+       * after this call.
+       **/
       finish: AugmentedSubmittable<(address: H160 | string | Uint8Array, code: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H160, Bytes]>;
+      /**
+       * Insert items into contract storage, this method can be called
+       * multiple times
+       **/
       setData: AugmentedSubmittable<(address: H160 | string | Uint8Array, data: Vec<ITuple<[H256, H256]>> | ([H256 | string | Uint8Array, H256 | string | Uint8Array])[]) => SubmittableExtrinsic<ApiType>, [H160, Vec<ITuple<[H256, H256]>>]>;
       /**
        * Generic tx
@@ -372,7 +385,7 @@
       stopAppPromotion: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
       stopSponsoringCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
       stopSponsoringContract: AugmentedSubmittable<(contractId: H160 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H160]>;
-      unstake: AugmentedSubmittable<(amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u128]>;
+      unstake: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
       /**
        * Generic tx
        **/
modifiedtests/src/interfaces/default/types.tsdiffbeforeafterboth
--- a/tests/src/interfaces/default/types.ts
+++ b/tests/src/interfaces/default/types.ts
@@ -822,9 +822,6 @@
     readonly amount: u128;
   } & Struct;
   readonly isUnstake: boolean;
-  readonly asUnstake: {
-    readonly amount: u128;
-  } & Struct;
   readonly isSponsorCollection: boolean;
   readonly asSponsorCollection: {
     readonly collectionId: u32;
@@ -2639,8 +2636,7 @@
 export interface UpDataStructsPropertyScope extends Enum {
   readonly isNone: boolean;
   readonly isRmrk: boolean;
-  readonly isEth: boolean;
-  readonly type: 'None' | 'Rmrk' | 'Eth';
+  readonly type: 'None' | 'Rmrk';
 }
 
 /** @name UpDataStructsRpcCollection */
modifiedtests/src/interfaces/definitions.tsdiffbeforeafterboth
--- a/tests/src/interfaces/definitions.ts
+++ b/tests/src/interfaces/definitions.ts
@@ -15,5 +15,6 @@
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
 export {default as unique} from './unique/definitions';
+export {default as appPromotion} from './appPromotion/definitions';
 export {default as rmrk} from './rmrk/definitions';
 export {default as default} from './default/definitions';
\ No newline at end of file
modifiedtests/src/interfaces/lookup.tsdiffbeforeafterboth
--- a/tests/src/interfaces/lookup.ts
+++ b/tests/src/interfaces/lookup.ts
@@ -2467,9 +2467,7 @@
       stake: {
         amount: 'u128',
       },
-      unstake: {
-        amount: 'u128',
-      },
+      unstake: 'Null',
       sponsor_collection: {
         collectionId: 'u32',
       },
@@ -3078,7 +3076,7 @@
    * Lookup403: up_data_structs::PropertyScope
    **/
   UpDataStructsPropertyScope: {
-    _enum: ['None', 'Rmrk', 'Eth']
+    _enum: ['None', 'Rmrk']
   },
   /**
    * Lookup405: pallet_nonfungible::pallet::Error<T>
modifiedtests/src/interfaces/types-lookup.tsdiffbeforeafterboth
--- a/tests/src/interfaces/types-lookup.ts
+++ b/tests/src/interfaces/types-lookup.ts
@@ -2675,9 +2675,6 @@
       readonly amount: u128;
     } & Struct;
     readonly isUnstake: boolean;
-    readonly asUnstake: {
-      readonly amount: u128;
-    } & Struct;
     readonly isSponsorCollection: boolean;
     readonly asSponsorCollection: {
       readonly collectionId: u32;
@@ -3238,8 +3235,7 @@
   interface UpDataStructsPropertyScope extends Enum {
     readonly isNone: boolean;
     readonly isRmrk: boolean;
-    readonly isEth: boolean;
-    readonly type: 'None' | 'Rmrk' | 'Eth';
+    readonly type: 'None' | 'Rmrk';
   }
 
   /** @name PalletNonfungibleError (405) */
modifiedtests/src/interfaces/types.tsdiffbeforeafterboth
--- a/tests/src/interfaces/types.ts
+++ b/tests/src/interfaces/types.ts
@@ -2,5 +2,6 @@
 /* eslint-disable */
 
 export * from './unique/types';
+export * from './appPromotion/types';
 export * from './rmrk/types';
 export * from './default/types';
modifiedtests/src/interfaces/unique/definitions.tsdiffbeforeafterboth
--- a/tests/src/interfaces/unique/definitions.ts
+++ b/tests/src/interfaces/unique/definitions.ts
@@ -175,30 +175,5 @@
       [collectionParam, tokenParam], 
       'Option<u128>',
     ),
-    totalStaked: fun(
-      'Returns the total amount of staked tokens',
-      [{name: 'staker', type: CROSS_ACCOUNT_ID_TYPE, isOptional: true}],
-      'u128',
-    ),
-    totalStakedPerBlock: fun(
-      'Returns the total amount of staked tokens per block when staked',
-      [crossAccountParam('staker')],
-      'Vec<(u32, u128)>',
-    ),
-    totalStakingLocked: fun(
-      'Return the total amount locked by staking tokens',
-      [crossAccountParam('staker')],
-      'u128',
-    ),
-    pendingUnstake: fun(
-      'Returns the total amount of unstaked tokens',
-      [{name: 'staker', type: CROSS_ACCOUNT_ID_TYPE, isOptional: true}],
-      'u128',
-    ),
-    pendingUnstakePerBlock: fun(
-      'Returns the total amount of unstaked tokens per block',
-      [crossAccountParam('staker')],
-      'Vec<(u32, u128)>',
-    ),
   },
 };
modifiedtests/src/substrate/substrate-api.tsdiffbeforeafterboth
--- a/tests/src/substrate/substrate-api.ts
+++ b/tests/src/substrate/substrate-api.ts
@@ -42,6 +42,7 @@
     },
     rpc: {
       unique: defs.unique.rpc,
+      appPromotion: defs.appPromotion.rpc,
       rmrk: defs.rmrk.rpc,
       eth: {
         feeHistory: {
modifiedtests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/unique.dev.ts
+++ b/tests/src/util/playgrounds/unique.dev.ts
@@ -35,6 +35,7 @@
       },
       rpc: {
         unique: defs.unique.rpc,
+        appPromotion: defs.appPromotion.rpc,
         rmrk: defs.rmrk.rpc,
         eth: {
           feeHistory: {
modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -2027,24 +2027,24 @@
   }
 
   async getTotalStaked(address?: ICrossAccountId): Promise<bigint> {
-    if (address) return (await this.helper.callRpc('api.rpc.unique.totalStaked', [address])).toBigInt();
-    return (await this.helper.callRpc('api.rpc.unique.totalStaked')).toBigInt();
+    if (address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt();
+    return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt();
   }
 
   async getTotalStakingLocked(address: ICrossAccountId): Promise<bigint> {
-    return (await this.helper.callRpc('api.rpc.unique.totalStakingLocked', [address])).toBigInt();
+    return (await this.helper.callRpc('api.rpc.appPromotion.totalStakingLocked', [address])).toBigInt();
   }
 
   async getTotalStakedPerBlock(address: ICrossAccountId): Promise<bigint[][]> {
-    return (await this.helper.callRpc('api.rpc.unique.totalStakedPerBlock', [address])).map(([block, amount]: any[]) => [block.toBigInt(), amount.toBigInt()]);
+    return (await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address])).map(([block, amount]: any[]) => [block.toBigInt(), amount.toBigInt()]);
   }
 
   async getPendingUnstake(address: ICrossAccountId): Promise<bigint> {
-    return (await this.helper.callRpc('api.rpc.unique.pendingUnstake', [address])).toBigInt();
+    return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt();
   }
   
   async getPendingUnstakePerBlock(address: ICrossAccountId): Promise<bigint[][]> {
-    return (await this.helper.callRpc('api.rpc.unique.pendingUnstakePerBlock', [address])).map(([block, amount]: any[]) => [block.toBigInt(), amount.toBigInt()]);
+    return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address])).map(([block, amount]: any[]) => [block.toBigInt(), amount.toBigInt()]);
   }
 }