difftreelog
Merge pull request #426 from UniqueNetwork/feature/token_owners
in: master
31 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5953,7 +5953,7 @@
[[package]]
name = "pallet-fungible"
-version = "0.1.0"
+version = "0.1.1"
dependencies = [
"ethereum",
"evm-coder",
@@ -6198,7 +6198,7 @@
[[package]]
name = "pallet-nonfungible"
-version = "0.1.0"
+version = "0.1.1"
dependencies = [
"ethereum",
"evm-coder",
@@ -12377,7 +12377,7 @@
[[package]]
name = "uc-rpc"
-version = "0.1.0"
+version = "0.1.1"
dependencies = [
"anyhow",
"jsonrpsee",
@@ -12682,7 +12682,7 @@
[[package]]
name = "unique-runtime-common"
-version = "0.9.24"
+version = "0.9.25"
dependencies = [
"evm-coder",
"fp-rpc",
@@ -12754,7 +12754,7 @@
[[package]]
name = "up-rpc"
-version = "0.1.0"
+version = "0.1.1"
dependencies = [
"pallet-common",
"pallet-evm",
client/rpc/CHANGELOG.mddiffbeforeafterboth--- /dev/null
+++ b/client/rpc/CHANGELOG.md
@@ -0,0 +1,11 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+
+## [0.1.1] - 2022-07-14
+
+### Added
+
+ - Implementation of RPC method `token_owners` returning 10 owners in no particular order.
+ This was an internal request to improve the web interface and support fractionalization event.
+
\ No newline at end of file
client/rpc/Cargo.tomldiffbeforeafterboth--- a/client/rpc/Cargo.toml
+++ b/client/rpc/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "uc-rpc"
-version = "0.1.0"
+version = "0.1.1"
license = "GPLv3"
edition = "2021"
client/rpc/src/lib.rsdiffbeforeafterboth--- a/client/rpc/src/lib.rs
+++ b/client/rpc/src/lib.rs
@@ -70,6 +70,16 @@
token: TokenId,
at: Option<BlockHash>,
) -> Result<Option<CrossAccountId>>;
+
+ /// Returns 10 tokens owners in no particular order.
+ #[method(name = "unique_tokenOwners")]
+ fn token_owners(
+ &self,
+ collection: CollectionId,
+ token: TokenId,
+ at: Option<BlockHash>,
+ ) -> Result<Vec<CrossAccountId>>;
+
#[method(name = "unique_topmostTokenOwner")]
fn topmost_token_owner(
&self,
@@ -473,6 +483,7 @@
pass_method!(next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Option<u64>, unique_api);
pass_method!(effective_collection_limits(collection_id: CollectionId) -> Option<CollectionLimits>, unique_api);
pass_method!(total_pieces(collection_id: CollectionId, token_id: TokenId) -> Option<u128>, unique_api);
+ pass_method!(token_owners(collection: CollectionId, token: TokenId) -> Vec<CrossAccountId>, unique_api);
}
#[allow(deprecated)]
pallets/common/CHANGELOG.mddiffbeforeafterboth--- /dev/null
+++ b/pallets/common/CHANGELOG.md
@@ -0,0 +1,12 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+
+## [0.1.1] - 2022-07-14
+
+### Added
+
+ - Implementation of RPC method `token_owners` returning 10 owners in no particular order.
+ This was an internal request to improve the web interface and support fractionalization event.
+
+
\ No newline at end of file
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -1748,6 +1748,11 @@
/// * `token` - The token for which you need to find out the owner.
fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;
+ /// Returns 10 tokens owners in no particular order.
+ ///
+ /// * `token` - The token for which you need to find out the owners.
+ fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId>;
+
/// Get the value of the token property by key.
///
/// * `token` - Token with the property to get.
pallets/fungible/CHANGELOG.mddiffbeforeafterboth--- /dev/null
+++ b/pallets/fungible/CHANGELOG.md
@@ -0,0 +1,13 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+
+## [0.1.1] - 2022-07-14
+
+### Added
+
+ - Implementation of RPC method `token_owners` returning 10 owners in no particular order.
+ This was an internal request to improve the web interface and support fractionalization event.
+
+
+
\ No newline at end of file
pallets/fungible/Cargo.tomldiffbeforeafterboth--- a/pallets/fungible/Cargo.toml
+++ b/pallets/fungible/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "pallet-fungible"
-version = "0.1.0"
+version = "0.1.1"
license = "GPLv3"
edition = "2021"
pallets/fungible/src/common.rsdiffbeforeafterboth--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -362,6 +362,11 @@
None
}
+ /// Returns 10 tokens owners in no particular order.
+ fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {
+ <Pallet<T>>::token_owners(self.id, token).unwrap_or_default()
+ }
+
fn token_property(&self, _token_id: TokenId, _key: &PropertyKey) -> Option<PropertyValue> {
None
}
pallets/fungible/src/lib.rsdiffbeforeafterboth--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -95,7 +95,7 @@
use pallet_evm_coder_substrate::WithRecorder;
use sp_core::H160;
use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};
-use sp_std::{collections::btree_map::BTreeMap};
+use sp_std::{collections::btree_map::BTreeMap, vec::Vec};
pub use pallet::*;
@@ -613,4 +613,26 @@
nesting_budget,
)
}
+
+ /// Returns 10 tokens owners in no particular order
+ ///
+ /// There is no direct way to get token holders in ascending order,
+ /// since `iter_prefix` returns values in no particular order.
+ /// Therefore, getting the 10 largest holders with a large value of holders
+ /// can lead to impact memory allocation + sorting with `n * log (n)`.
+ pub fn token_owners(
+ collection: CollectionId,
+ _token: TokenId,
+ ) -> Option<Vec<T::CrossAccountId>> {
+ let res: Vec<T::CrossAccountId> = <Balance<T>>::iter_prefix((collection,))
+ .map(|(owner, _amount)| owner)
+ .take(10)
+ .collect();
+
+ if res.is_empty() {
+ None
+ } else {
+ Some(res)
+ }
+ }
}
pallets/nonfungible/CHANGELOG.mddiffbeforeafterboth--- /dev/null
+++ b/pallets/nonfungible/CHANGELOG.md
@@ -0,0 +1,11 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+
+## [0.1.1] - 2022-07-14
+
+### Added
+
+- Implementation of RPC method `token_owners`.
+ For reasons of compatibility with this pallet, returns only one owner if token exists.
+ This was an internal request to improve the web interface and support fractionalization event.
\ No newline at end of file
pallets/nonfungible/Cargo.tomldiffbeforeafterboth--- a/pallets/nonfungible/Cargo.toml
+++ b/pallets/nonfungible/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "pallet-nonfungible"
-version = "0.1.0"
+version = "0.1.1"
license = "GPLv3"
edition = "2021"
pallets/nonfungible/src/common.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -26,7 +26,7 @@
weights::WeightInfo as _,
};
use sp_runtime::DispatchError;
-use sp_std::vec::Vec;
+use sp_std::{vec::Vec, vec};
use crate::{
AccountBalance, Allowance, Config, CreateItemData, Error, NonfungibleHandle, Owned, Pallet,
@@ -422,6 +422,11 @@
<TokenData<T>>::get((self.id, token)).map(|t| t.owner)
}
+ /// Returns token owners.
+ fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {
+ self.token_owner(token).map_or_else(|| vec![], |t| vec![t])
+ }
+
fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue> {
<Pallet<T>>::token_properties((self.id, token_id))
.get(key)
pallets/refungible/CHANGELOG.mddiffbeforeafterboth--- a/pallets/refungible/CHANGELOG.md
+++ b/pallets/refungible/CHANGELOG.md
@@ -1,6 +1,18 @@
-## v0.1.2 - 2022-07
+# Change Log
-### Refungible Pallet
+All notable changes to this project will be documented in this file.
+## [v0.1.2] - 2022-07-14
+
+### Other changes
+
feat(refungible-pallet): add ERC-20 EVM API for RFT token pieces ([#413](https://github.com/UniqueNetwork/unique-chain/pull/413))
-test(refungible-pallet): add tests for ERC-20 EVM API for RFT token pieces ([#413](https://github.com/UniqueNetwork/unique-chain/pull/413))
\ No newline at end of file
+test(refungible-pallet): add tests for ERC-20 EVM API for RFT token pieces ([#413](https://github.com/UniqueNetwork/unique-chain/pull/413))
+
+## [v0.1.1] - 2022-07-14
+
+### Other changes
+
+- feat: RPC method `token_owners` returning 10 owners in no particular order.
+
+This was an internal request to improve the web interface and support fractionalization event.
pallets/refungible/src/common.rsdiffbeforeafterboth--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -438,6 +438,11 @@
<Pallet<T>>::token_owner(self.id, token)
}
+ /// Returns 10 token in no particular order.
+ fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {
+ <Pallet<T>>::token_owners(self.id, token).unwrap_or_default()
+ }
+
fn token_property(&self, _token_id: TokenId, _key: &PropertyKey) -> Option<PropertyValue> {
None
}
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -1235,4 +1235,26 @@
) -> DispatchResult {
<PalletCommon<T>>::set_token_property_permissions(collection, sender, property_permissions)
}
+
+ /// Returns 10 token in no particular order.
+ ///
+ /// There is no direct way to get token holders in ascending order,
+ /// since `iter_prefix` returns values in no particular order.
+ /// Therefore, getting the 10 largest holders with a large value of holders
+ /// can lead to impact memory allocation + sorting with `n * log (n)`.
+ pub fn token_owners(
+ collection_id: CollectionId,
+ token: TokenId,
+ ) -> Option<Vec<T::CrossAccountId>> {
+ let res: Vec<T::CrossAccountId> = <Balance<T>>::iter_prefix((collection_id, token))
+ .map(|(owner, _amount)| owner)
+ .take(10)
+ .collect();
+
+ if res.is_empty() {
+ None
+ } else {
+ Some(res)
+ }
+ }
}
primitives/rpc/CHANGELOG.mddiffbeforeafterboth--- /dev/null
+++ b/primitives/rpc/CHANGELOG.md
@@ -0,0 +1,10 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+
+## [0.1.1] - 2022-07-14
+
+### Added
+
+ - Implementation of RPC method `token_owners` returning 10 owners in no particular order.
+ This was an internal request to improve the web interface and support fractionalization event.
\ No newline at end of file
primitives/rpc/Cargo.tomldiffbeforeafterboth--- a/primitives/rpc/Cargo.toml
+++ b/primitives/rpc/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "up-rpc"
-version = "0.1.0"
+version = "0.1.1"
license = "GPLv3"
edition = "2021"
primitives/rpc/src/lib.rsdiffbeforeafterboth--- a/primitives/rpc/src/lib.rs
+++ b/primitives/rpc/src/lib.rs
@@ -81,5 +81,6 @@
fn next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Result<Option<u64>>;
fn effective_collection_limits(collection_id: CollectionId) -> Result<Option<CollectionLimits>>;
fn total_pieces(collection_id: CollectionId, token_id: TokenId) -> Result<Option<u128>>;
+ fn token_owners(collection: CollectionId, token: TokenId) -> Result<Vec<CrossAccountId>>;
}
}
runtime/common/CHANGELOG.mddiffbeforeafterboth--- /dev/null
+++ b/runtime/common/CHANGELOG.md
@@ -0,0 +1,13 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+
+## [0.9.25] - 2022-07-14
+
+### Added
+
+ - Implementation of RPC method `token_owners` returning 10 owners in no particular order.
+ This was an internal request to improve the web interface and support fractionalization event.
+
+
+
\ No newline at end of file
runtime/common/src/runtime_apis.rsdiffbeforeafterboth1// 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#[macro_export]18macro_rules! impl_common_runtime_apis {19 (20 $(21 #![custom_apis]2223 $($custom_apis:tt)+24 )?25 ) => {26 impl_runtime_apis! {27 $($($custom_apis)+)?2829 impl up_rpc::UniqueApi<Block, CrossAccountId, AccountId> for Runtime {30 fn account_tokens(collection: CollectionId, account: CrossAccountId) -> Result<Vec<TokenId>, DispatchError> {31 dispatch_unique_runtime!(collection.account_tokens(account))32 }33 fn collection_tokens(collection: CollectionId) -> Result<Vec<TokenId>, DispatchError> {34 dispatch_unique_runtime!(collection.collection_tokens())35 }36 fn token_exists(collection: CollectionId, token: TokenId) -> Result<bool, DispatchError> {37 dispatch_unique_runtime!(collection.token_exists(token))38 }3940 fn token_owner(collection: CollectionId, token: TokenId) -> Result<Option<CrossAccountId>, DispatchError> {41 dispatch_unique_runtime!(collection.token_owner(token))42 }43 fn topmost_token_owner(collection: CollectionId, token: TokenId) -> Result<Option<CrossAccountId>, DispatchError> {44 let budget = up_data_structs::budget::Value::new(10);4546 Ok(Some(<pallet_structure::Pallet<Runtime>>::find_topmost_owner(collection, token, &budget)?))47 }48 fn token_children(collection: CollectionId, token: TokenId) -> Result<Vec<TokenChild>, DispatchError> {49 Ok(<pallet_nonfungible::Pallet<Runtime>>::token_children_ids(collection, token))50 }51 fn collection_properties(52 collection: CollectionId,53 keys: Option<Vec<Vec<u8>>>54 ) -> Result<Vec<Property>, DispatchError> {55 let keys = keys.map(56 |keys| Common::bytes_keys_to_property_keys(keys)57 ).transpose()?;5859 Common::filter_collection_properties(collection, keys)60 }6162 fn token_properties(63 collection: CollectionId,64 token_id: TokenId,65 keys: Option<Vec<Vec<u8>>>66 ) -> Result<Vec<Property>, DispatchError> {67 let keys = keys.map(68 |keys| Common::bytes_keys_to_property_keys(keys)69 ).transpose()?;7071 dispatch_unique_runtime!(collection.token_properties(token_id, keys))72 }7374 fn property_permissions(75 collection: CollectionId,76 keys: Option<Vec<Vec<u8>>>77 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {78 let keys = keys.map(79 |keys| Common::bytes_keys_to_property_keys(keys)80 ).transpose()?;8182 Common::filter_property_permissions(collection, keys)83 }8485 fn token_data(86 collection: CollectionId,87 token_id: TokenId,88 keys: Option<Vec<Vec<u8>>>89 ) -> Result<TokenData<CrossAccountId>, DispatchError> {90 let token_data = TokenData {91 properties: Self::token_properties(collection, token_id, keys)?,92 owner: Self::token_owner(collection, token_id)?,93 pieces: Self::total_pieces(collection, token_id)?.unwrap_or(0),94 };9596 Ok(token_data)97 }9899 fn total_supply(collection: CollectionId) -> Result<u32, DispatchError> {100 dispatch_unique_runtime!(collection.total_supply())101 }102 fn account_balance(collection: CollectionId, account: CrossAccountId) -> Result<u32, DispatchError> {103 dispatch_unique_runtime!(collection.account_balance(account))104 }105 fn balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Result<u128, DispatchError> {106 dispatch_unique_runtime!(collection.balance(account, token))107 }108 fn allowance(109 collection: CollectionId,110 sender: CrossAccountId,111 spender: CrossAccountId,112 token: TokenId,113 ) -> Result<u128, DispatchError> {114 dispatch_unique_runtime!(collection.allowance(sender, spender, token))115 }116117 fn adminlist(collection: CollectionId) -> Result<Vec<CrossAccountId>, DispatchError> {118 Ok(<pallet_common::Pallet<Runtime>>::adminlist(collection))119 }120 fn allowlist(collection: CollectionId) -> Result<Vec<CrossAccountId>, DispatchError> {121 Ok(<pallet_common::Pallet<Runtime>>::allowlist(collection))122 }123 fn allowed(collection: CollectionId, user: CrossAccountId) -> Result<bool, DispatchError> {124 Ok(<pallet_common::Pallet<Runtime>>::allowed(collection, user))125 }126 fn last_token_id(collection: CollectionId) -> Result<TokenId, DispatchError> {127 dispatch_unique_runtime!(collection.last_token_id())128 }129 fn collection_by_id(collection: CollectionId) -> Result<Option<RpcCollection<AccountId>>, DispatchError> {130 Ok(<pallet_common::Pallet<Runtime>>::rpc_collection(collection))131 }132 fn collection_stats() -> Result<CollectionStats, DispatchError> {133 Ok(<pallet_common::Pallet<Runtime>>::collection_stats())134 }135 fn next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Result<Option<u64>, DispatchError> {136 Ok(<$crate::sponsoring::UniqueSponsorshipPredict<Runtime> as137 $crate::sponsoring::SponsorshipPredict<Runtime>>::predict(138 collection,139 account,140 token))141 }142143 fn effective_collection_limits(collection: CollectionId) -> Result<Option<CollectionLimits>, DispatchError> {144 Ok(<pallet_common::Pallet<Runtime>>::effective_collection_limits(collection))145 }146147 fn total_pieces(collection: CollectionId, token_id: TokenId) -> Result<Option<u128>, DispatchError> {148 dispatch_unique_runtime!(collection.total_pieces(token_id))149 }150 }151152 impl sp_api::Core<Block> for Runtime {153 fn version() -> RuntimeVersion {154 VERSION155 }156157 fn execute_block(block: Block) {158 Executive::execute_block(block)159 }160161 fn initialize_block(header: &<Block as BlockT>::Header) {162 Executive::initialize_block(header)163 }164 }165166 impl sp_api::Metadata<Block> for Runtime {167 fn metadata() -> OpaqueMetadata {168 OpaqueMetadata::new(Runtime::metadata().into())169 }170 }171172 impl sp_block_builder::BlockBuilder<Block> for Runtime {173 fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult {174 Executive::apply_extrinsic(extrinsic)175 }176177 fn finalize_block() -> <Block as BlockT>::Header {178 Executive::finalize_block()179 }180181 fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> {182 data.create_extrinsics()183 }184185 fn check_inherents(186 block: Block,187 data: sp_inherents::InherentData,188 ) -> sp_inherents::CheckInherentsResult {189 data.check_extrinsics(&block)190 }191192 // fn random_seed() -> <Block as BlockT>::Hash {193 // RandomnessCollectiveFlip::random_seed().0194 // }195 }196197 impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {198 fn validate_transaction(199 source: TransactionSource,200 tx: <Block as BlockT>::Extrinsic,201 hash: <Block as BlockT>::Hash,202 ) -> TransactionValidity {203 Executive::validate_transaction(source, tx, hash)204 }205 }206207 impl sp_offchain::OffchainWorkerApi<Block> for Runtime {208 fn offchain_worker(header: &<Block as BlockT>::Header) {209 Executive::offchain_worker(header)210 }211 }212213 impl fp_rpc::EthereumRuntimeRPCApi<Block> for Runtime {214 fn chain_id() -> u64 {215 <Runtime as pallet_evm::Config>::ChainId::get()216 }217218 fn account_basic(address: H160) -> EVMAccount {219 let (account, _) = EVM::account_basic(&address);220 account221 }222223 fn gas_price() -> U256 {224 let (price, _) = <Runtime as pallet_evm::Config>::FeeCalculator::min_gas_price();225 price226 }227228 fn account_code_at(address: H160) -> Vec<u8> {229 EVM::account_codes(address)230 }231232 fn author() -> H160 {233 <pallet_evm::Pallet<Runtime>>::find_author()234 }235236 fn storage_at(address: H160, index: U256) -> H256 {237 let mut tmp = [0u8; 32];238 index.to_big_endian(&mut tmp);239 EVM::account_storages(address, H256::from_slice(&tmp[..]))240 }241242 #[allow(clippy::redundant_closure)]243 fn call(244 from: H160,245 to: H160,246 data: Vec<u8>,247 value: U256,248 gas_limit: U256,249 max_fee_per_gas: Option<U256>,250 max_priority_fee_per_gas: Option<U256>,251 nonce: Option<U256>,252 estimate: bool,253 access_list: Option<Vec<(H160, Vec<H256>)>>,254 ) -> Result<pallet_evm::CallInfo, sp_runtime::DispatchError> {255 let config = if estimate {256 let mut config = <Runtime as pallet_evm::Config>::config().clone();257 config.estimate = true;258 Some(config)259 } else {260 None261 };262263 let is_transactional = false;264 <Runtime as pallet_evm::Config>::Runner::call(265 CrossAccountId::from_eth(from),266 to,267 data,268 value,269 gas_limit.low_u64(),270 max_fee_per_gas,271 max_priority_fee_per_gas,272 nonce,273 access_list.unwrap_or_default(),274 is_transactional,275 config.as_ref().unwrap_or_else(|| <Runtime as pallet_evm::Config>::config()),276 ).map_err(|err| err.error.into())277 }278279 #[allow(clippy::redundant_closure)]280 fn create(281 from: H160,282 data: Vec<u8>,283 value: U256,284 gas_limit: U256,285 max_fee_per_gas: Option<U256>,286 max_priority_fee_per_gas: Option<U256>,287 nonce: Option<U256>,288 estimate: bool,289 access_list: Option<Vec<(H160, Vec<H256>)>>,290 ) -> Result<pallet_evm::CreateInfo, sp_runtime::DispatchError> {291 let config = if estimate {292 let mut config = <Runtime as pallet_evm::Config>::config().clone();293 config.estimate = true;294 Some(config)295 } else {296 None297 };298299 let is_transactional = false;300 <Runtime as pallet_evm::Config>::Runner::create(301 CrossAccountId::from_eth(from),302 data,303 value,304 gas_limit.low_u64(),305 max_fee_per_gas,306 max_priority_fee_per_gas,307 nonce,308 access_list.unwrap_or_default(),309 is_transactional,310 config.as_ref().unwrap_or_else(|| <Runtime as pallet_evm::Config>::config()),311 ).map_err(|err| err.error.into())312 }313314 fn current_transaction_statuses() -> Option<Vec<TransactionStatus>> {315 Ethereum::current_transaction_statuses()316 }317318 fn current_block() -> Option<pallet_ethereum::Block> {319 Ethereum::current_block()320 }321322 fn current_receipts() -> Option<Vec<pallet_ethereum::Receipt>> {323 Ethereum::current_receipts()324 }325326 fn current_all() -> (327 Option<pallet_ethereum::Block>,328 Option<Vec<pallet_ethereum::Receipt>>,329 Option<Vec<TransactionStatus>>330 ) {331 (332 Ethereum::current_block(),333 Ethereum::current_receipts(),334 Ethereum::current_transaction_statuses()335 )336 }337338 fn extrinsic_filter(xts: Vec<<Block as sp_api::BlockT>::Extrinsic>) -> Vec<pallet_ethereum::Transaction> {339 xts.into_iter().filter_map(|xt| match xt.0.function {340 Call::Ethereum(pallet_ethereum::Call::transact { transaction }) => Some(transaction),341 _ => None342 }).collect()343 }344345 fn elasticity() -> Option<Permill> {346 None347 }348 }349350 impl fp_rpc::ConvertTransactionRuntimeApi<Block> for Runtime {351 fn convert_transaction(transaction: pallet_ethereum::Transaction) -> <Block as BlockT>::Extrinsic {352 UncheckedExtrinsic::new_unsigned(353 pallet_ethereum::Call::<Runtime>::transact { transaction }.into(),354 )355 }356 }357358 impl sp_session::SessionKeys<Block> for Runtime {359 fn decode_session_keys(360 encoded: Vec<u8>,361 ) -> Option<Vec<(Vec<u8>, KeyTypeId)>> {362 SessionKeys::decode_into_raw_public_keys(&encoded)363 }364365 fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {366 SessionKeys::generate(seed)367 }368 }369370 impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {371 fn slot_duration() -> sp_consensus_aura::SlotDuration {372 sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration())373 }374375 fn authorities() -> Vec<AuraId> {376 Aura::authorities().to_vec()377 }378 }379380 impl cumulus_primitives_core::CollectCollationInfo<Block> for Runtime {381 fn collect_collation_info(header: &<Block as BlockT>::Header) -> cumulus_primitives_core::CollationInfo {382 ParachainSystem::collect_collation_info(header)383 }384 }385386 impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime {387 fn account_nonce(account: AccountId) -> Index {388 System::account_nonce(account)389 }390 }391392 impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance> for Runtime {393 fn query_info(uxt: <Block as BlockT>::Extrinsic, len: u32) -> RuntimeDispatchInfo<Balance> {394 TransactionPayment::query_info(uxt, len)395 }396 fn query_fee_details(uxt: <Block as BlockT>::Extrinsic, len: u32) -> FeeDetails<Balance> {397 TransactionPayment::query_fee_details(uxt, len)398 }399 }400401 /*402 impl pallet_contracts_rpc_runtime_api::ContractsApi<Block, AccountId, Balance, BlockNumber, Hash>403 for Runtime404 {405 fn call(406 origin: AccountId,407 dest: AccountId,408 value: Balance,409 gas_limit: u64,410 input_data: Vec<u8>,411 ) -> pallet_contracts_primitives::ContractExecResult {412 Contracts::bare_call(origin, dest, value, gas_limit, input_data, false)413 }414415 fn instantiate(416 origin: AccountId,417 endowment: Balance,418 gas_limit: u64,419 code: pallet_contracts_primitives::Code<Hash>,420 data: Vec<u8>,421 salt: Vec<u8>,422 ) -> pallet_contracts_primitives::ContractInstantiateResult<AccountId, BlockNumber>423 {424 Contracts::bare_instantiate(origin, endowment, gas_limit, code, data, salt, true, false)425 }426427 fn get_storage(428 address: AccountId,429 key: [u8; 32],430 ) -> pallet_contracts_primitives::GetStorageResult {431 Contracts::get_storage(address, key)432 }433434 fn rent_projection(435 address: AccountId,436 ) -> pallet_contracts_primitives::RentProjectionResult<BlockNumber> {437 Contracts::rent_projection(address)438 }439 }440 */441442 #[cfg(feature = "runtime-benchmarks")]443 impl frame_benchmarking::Benchmark<Block> for Runtime {444 fn benchmark_metadata(extra: bool) -> (445 Vec<frame_benchmarking::BenchmarkList>,446 Vec<frame_support::traits::StorageInfo>,447 ) {448 use frame_benchmarking::{list_benchmark, Benchmarking, BenchmarkList};449 use frame_support::traits::StorageInfoTrait;450451 let mut list = Vec::<BenchmarkList>::new();452453 list_benchmark!(list, extra, pallet_evm_migration, EvmMigration);454 list_benchmark!(list, extra, pallet_common, Common);455 list_benchmark!(list, extra, pallet_unique, Unique);456 list_benchmark!(list, extra, pallet_structure, Structure);457 list_benchmark!(list, extra, pallet_inflation, Inflation);458 list_benchmark!(list, extra, pallet_fungible, Fungible);459 list_benchmark!(list, extra, pallet_refungible, Refungible);460 list_benchmark!(list, extra, pallet_nonfungible, Nonfungible);461 list_benchmark!(list, extra, pallet_unique_scheduler, Scheduler);462463 #[cfg(not(feature = "unique-runtime"))]464 list_benchmark!(list, extra, pallet_proxy_rmrk_core, RmrkCore);465466 #[cfg(not(feature = "unique-runtime"))]467 list_benchmark!(list, extra, pallet_proxy_rmrk_equip, RmrkEquip);468469 // list_benchmark!(list, extra, pallet_evm_coder_substrate, EvmCoderSubstrate);470471 let storage_info = AllPalletsReversedWithSystemFirst::storage_info();472473 return (list, storage_info)474 }475476 fn dispatch_benchmark(477 config: frame_benchmarking::BenchmarkConfig478 ) -> Result<Vec<frame_benchmarking::BenchmarkBatch>, sp_runtime::RuntimeString> {479 use frame_benchmarking::{Benchmarking, BenchmarkBatch, add_benchmark, TrackedStorageKey};480481 let allowlist: Vec<TrackedStorageKey> = vec![482 // Total Issuance483 hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80").to_vec().into(),484485 // Block Number486 hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(),487 // Execution Phase488 hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(),489 // Event Count490 hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(),491 // System Events492 hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(),493494 // Evm CurrentLogs495 hex_literal::hex!("1da53b775b270400e7e61ed5cbc5a146547f210cec367e9af919603343b9cb56").to_vec().into(),496497 // Transactional depth498 hex_literal::hex!("3a7472616e73616374696f6e5f6c6576656c3a").to_vec().into(),499 ];500501 let mut batches = Vec::<BenchmarkBatch>::new();502 let params = (&config, &allowlist);503504 add_benchmark!(params, batches, pallet_evm_migration, EvmMigration);505 add_benchmark!(params, batches, pallet_common, Common);506 add_benchmark!(params, batches, pallet_unique, Unique);507 add_benchmark!(params, batches, pallet_structure, Structure);508 add_benchmark!(params, batches, pallet_inflation, Inflation);509 add_benchmark!(params, batches, pallet_fungible, Fungible);510 add_benchmark!(params, batches, pallet_refungible, Refungible);511 add_benchmark!(params, batches, pallet_nonfungible, Nonfungible);512 add_benchmark!(params, batches, pallet_unique_scheduler, Scheduler);513514 #[cfg(not(feature = "unique-runtime"))]515 add_benchmark!(params, batches, pallet_proxy_rmrk_core, RmrkCore);516517 #[cfg(not(feature = "unique-runtime"))]518 add_benchmark!(params, batches, pallet_proxy_rmrk_equip, RmrkEquip);519520 // add_benchmark!(params, batches, pallet_evm_coder_substrate, EvmCoderSubstrate);521522 if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) }523 Ok(batches)524 }525 }526527 #[cfg(feature = "try-runtime")]528 impl frame_try_runtime::TryRuntime<Block> for Runtime {529 fn on_runtime_upgrade() -> (Weight, Weight) {530 log::info!("try-runtime::on_runtime_upgrade unique-chain.");531 let weight = Executive::try_runtime_upgrade().unwrap();532 (weight, RuntimeBlockWeights::get().max_block)533 }534535 fn execute_block_no_check(block: Block) -> Weight {536 Executive::execute_block_no_check(block)537 }538 }539 }540 }541}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#[macro_export]18macro_rules! impl_common_runtime_apis {19 (20 $(21 #![custom_apis]2223 $($custom_apis:tt)+24 )?25 ) => {26 impl_runtime_apis! {27 $($($custom_apis)+)?2829 impl up_rpc::UniqueApi<Block, CrossAccountId, AccountId> for Runtime {30 fn account_tokens(collection: CollectionId, account: CrossAccountId) -> Result<Vec<TokenId>, DispatchError> {31 dispatch_unique_runtime!(collection.account_tokens(account))32 }33 fn collection_tokens(collection: CollectionId) -> Result<Vec<TokenId>, DispatchError> {34 dispatch_unique_runtime!(collection.collection_tokens())35 }36 fn token_exists(collection: CollectionId, token: TokenId) -> Result<bool, DispatchError> {37 dispatch_unique_runtime!(collection.token_exists(token))38 }3940 fn token_owner(collection: CollectionId, token: TokenId) -> Result<Option<CrossAccountId>, DispatchError> {41 dispatch_unique_runtime!(collection.token_owner(token))42 }4344 fn token_owners(collection: CollectionId, token: TokenId) -> Result<Vec::<CrossAccountId>, DispatchError> {45 dispatch_unique_runtime!(collection.token_owners(token))46 }4748 fn topmost_token_owner(collection: CollectionId, token: TokenId) -> Result<Option<CrossAccountId>, DispatchError> {49 let budget = up_data_structs::budget::Value::new(10);5051 Ok(Some(<pallet_structure::Pallet<Runtime>>::find_topmost_owner(collection, token, &budget)?))52 }53 fn token_children(collection: CollectionId, token: TokenId) -> Result<Vec<TokenChild>, DispatchError> {54 Ok(<pallet_nonfungible::Pallet<Runtime>>::token_children_ids(collection, token))55 }56 fn collection_properties(57 collection: CollectionId,58 keys: Option<Vec<Vec<u8>>>59 ) -> Result<Vec<Property>, DispatchError> {60 let keys = keys.map(61 |keys| Common::bytes_keys_to_property_keys(keys)62 ).transpose()?;6364 Common::filter_collection_properties(collection, keys)65 }6667 fn token_properties(68 collection: CollectionId,69 token_id: TokenId,70 keys: Option<Vec<Vec<u8>>>71 ) -> Result<Vec<Property>, DispatchError> {72 let keys = keys.map(73 |keys| Common::bytes_keys_to_property_keys(keys)74 ).transpose()?;7576 dispatch_unique_runtime!(collection.token_properties(token_id, keys))77 }7879 fn property_permissions(80 collection: CollectionId,81 keys: Option<Vec<Vec<u8>>>82 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {83 let keys = keys.map(84 |keys| Common::bytes_keys_to_property_keys(keys)85 ).transpose()?;8687 Common::filter_property_permissions(collection, keys)88 }8990 fn token_data(91 collection: CollectionId,92 token_id: TokenId,93 keys: Option<Vec<Vec<u8>>>94 ) -> Result<TokenData<CrossAccountId>, DispatchError> {95 let token_data = TokenData {96 properties: Self::token_properties(collection, token_id, keys)?,97 owner: Self::token_owner(collection, token_id)?,98 pieces: Self::total_pieces(collection, token_id)?.unwrap_or(0),99 };100101 Ok(token_data)102 }103104 fn total_supply(collection: CollectionId) -> Result<u32, DispatchError> {105 dispatch_unique_runtime!(collection.total_supply())106 }107 fn account_balance(collection: CollectionId, account: CrossAccountId) -> Result<u32, DispatchError> {108 dispatch_unique_runtime!(collection.account_balance(account))109 }110 fn balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Result<u128, DispatchError> {111 dispatch_unique_runtime!(collection.balance(account, token))112 }113 fn allowance(114 collection: CollectionId,115 sender: CrossAccountId,116 spender: CrossAccountId,117 token: TokenId,118 ) -> Result<u128, DispatchError> {119 dispatch_unique_runtime!(collection.allowance(sender, spender, token))120 }121122 fn adminlist(collection: CollectionId) -> Result<Vec<CrossAccountId>, DispatchError> {123 Ok(<pallet_common::Pallet<Runtime>>::adminlist(collection))124 }125 fn allowlist(collection: CollectionId) -> Result<Vec<CrossAccountId>, DispatchError> {126 Ok(<pallet_common::Pallet<Runtime>>::allowlist(collection))127 }128 fn allowed(collection: CollectionId, user: CrossAccountId) -> Result<bool, DispatchError> {129 Ok(<pallet_common::Pallet<Runtime>>::allowed(collection, user))130 }131 fn last_token_id(collection: CollectionId) -> Result<TokenId, DispatchError> {132 dispatch_unique_runtime!(collection.last_token_id())133 }134 fn collection_by_id(collection: CollectionId) -> Result<Option<RpcCollection<AccountId>>, DispatchError> {135 Ok(<pallet_common::Pallet<Runtime>>::rpc_collection(collection))136 }137 fn collection_stats() -> Result<CollectionStats, DispatchError> {138 Ok(<pallet_common::Pallet<Runtime>>::collection_stats())139 }140 fn next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Result<Option<u64>, DispatchError> {141 Ok(<$crate::sponsoring::UniqueSponsorshipPredict<Runtime> as142 $crate::sponsoring::SponsorshipPredict<Runtime>>::predict(143 collection,144 account,145 token))146 }147148 fn effective_collection_limits(collection: CollectionId) -> Result<Option<CollectionLimits>, DispatchError> {149 Ok(<pallet_common::Pallet<Runtime>>::effective_collection_limits(collection))150 }151152 fn total_pieces(collection: CollectionId, token_id: TokenId) -> Result<Option<u128>, DispatchError> {153 dispatch_unique_runtime!(collection.total_pieces(token_id))154 }155 }156157 impl sp_api::Core<Block> for Runtime {158 fn version() -> RuntimeVersion {159 VERSION160 }161162 fn execute_block(block: Block) {163 Executive::execute_block(block)164 }165166 fn initialize_block(header: &<Block as BlockT>::Header) {167 Executive::initialize_block(header)168 }169 }170171 impl sp_api::Metadata<Block> for Runtime {172 fn metadata() -> OpaqueMetadata {173 OpaqueMetadata::new(Runtime::metadata().into())174 }175 }176177 impl sp_block_builder::BlockBuilder<Block> for Runtime {178 fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult {179 Executive::apply_extrinsic(extrinsic)180 }181182 fn finalize_block() -> <Block as BlockT>::Header {183 Executive::finalize_block()184 }185186 fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> {187 data.create_extrinsics()188 }189190 fn check_inherents(191 block: Block,192 data: sp_inherents::InherentData,193 ) -> sp_inherents::CheckInherentsResult {194 data.check_extrinsics(&block)195 }196197 // fn random_seed() -> <Block as BlockT>::Hash {198 // RandomnessCollectiveFlip::random_seed().0199 // }200 }201202 impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {203 fn validate_transaction(204 source: TransactionSource,205 tx: <Block as BlockT>::Extrinsic,206 hash: <Block as BlockT>::Hash,207 ) -> TransactionValidity {208 Executive::validate_transaction(source, tx, hash)209 }210 }211212 impl sp_offchain::OffchainWorkerApi<Block> for Runtime {213 fn offchain_worker(header: &<Block as BlockT>::Header) {214 Executive::offchain_worker(header)215 }216 }217218 impl fp_rpc::EthereumRuntimeRPCApi<Block> for Runtime {219 fn chain_id() -> u64 {220 <Runtime as pallet_evm::Config>::ChainId::get()221 }222223 fn account_basic(address: H160) -> EVMAccount {224 let (account, _) = EVM::account_basic(&address);225 account226 }227228 fn gas_price() -> U256 {229 let (price, _) = <Runtime as pallet_evm::Config>::FeeCalculator::min_gas_price();230 price231 }232233 fn account_code_at(address: H160) -> Vec<u8> {234 EVM::account_codes(address)235 }236237 fn author() -> H160 {238 <pallet_evm::Pallet<Runtime>>::find_author()239 }240241 fn storage_at(address: H160, index: U256) -> H256 {242 let mut tmp = [0u8; 32];243 index.to_big_endian(&mut tmp);244 EVM::account_storages(address, H256::from_slice(&tmp[..]))245 }246247 #[allow(clippy::redundant_closure)]248 fn call(249 from: H160,250 to: H160,251 data: Vec<u8>,252 value: U256,253 gas_limit: U256,254 max_fee_per_gas: Option<U256>,255 max_priority_fee_per_gas: Option<U256>,256 nonce: Option<U256>,257 estimate: bool,258 access_list: Option<Vec<(H160, Vec<H256>)>>,259 ) -> Result<pallet_evm::CallInfo, sp_runtime::DispatchError> {260 let config = if estimate {261 let mut config = <Runtime as pallet_evm::Config>::config().clone();262 config.estimate = true;263 Some(config)264 } else {265 None266 };267268 let is_transactional = false;269 <Runtime as pallet_evm::Config>::Runner::call(270 CrossAccountId::from_eth(from),271 to,272 data,273 value,274 gas_limit.low_u64(),275 max_fee_per_gas,276 max_priority_fee_per_gas,277 nonce,278 access_list.unwrap_or_default(),279 is_transactional,280 config.as_ref().unwrap_or_else(|| <Runtime as pallet_evm::Config>::config()),281 ).map_err(|err| err.error.into())282 }283284 #[allow(clippy::redundant_closure)]285 fn create(286 from: H160,287 data: Vec<u8>,288 value: U256,289 gas_limit: U256,290 max_fee_per_gas: Option<U256>,291 max_priority_fee_per_gas: Option<U256>,292 nonce: Option<U256>,293 estimate: bool,294 access_list: Option<Vec<(H160, Vec<H256>)>>,295 ) -> Result<pallet_evm::CreateInfo, sp_runtime::DispatchError> {296 let config = if estimate {297 let mut config = <Runtime as pallet_evm::Config>::config().clone();298 config.estimate = true;299 Some(config)300 } else {301 None302 };303304 let is_transactional = false;305 <Runtime as pallet_evm::Config>::Runner::create(306 CrossAccountId::from_eth(from),307 data,308 value,309 gas_limit.low_u64(),310 max_fee_per_gas,311 max_priority_fee_per_gas,312 nonce,313 access_list.unwrap_or_default(),314 is_transactional,315 config.as_ref().unwrap_or_else(|| <Runtime as pallet_evm::Config>::config()),316 ).map_err(|err| err.error.into())317 }318319 fn current_transaction_statuses() -> Option<Vec<TransactionStatus>> {320 Ethereum::current_transaction_statuses()321 }322323 fn current_block() -> Option<pallet_ethereum::Block> {324 Ethereum::current_block()325 }326327 fn current_receipts() -> Option<Vec<pallet_ethereum::Receipt>> {328 Ethereum::current_receipts()329 }330331 fn current_all() -> (332 Option<pallet_ethereum::Block>,333 Option<Vec<pallet_ethereum::Receipt>>,334 Option<Vec<TransactionStatus>>335 ) {336 (337 Ethereum::current_block(),338 Ethereum::current_receipts(),339 Ethereum::current_transaction_statuses()340 )341 }342343 fn extrinsic_filter(xts: Vec<<Block as sp_api::BlockT>::Extrinsic>) -> Vec<pallet_ethereum::Transaction> {344 xts.into_iter().filter_map(|xt| match xt.0.function {345 Call::Ethereum(pallet_ethereum::Call::transact { transaction }) => Some(transaction),346 _ => None347 }).collect()348 }349350 fn elasticity() -> Option<Permill> {351 None352 }353 }354355 impl fp_rpc::ConvertTransactionRuntimeApi<Block> for Runtime {356 fn convert_transaction(transaction: pallet_ethereum::Transaction) -> <Block as BlockT>::Extrinsic {357 UncheckedExtrinsic::new_unsigned(358 pallet_ethereum::Call::<Runtime>::transact { transaction }.into(),359 )360 }361 }362363 impl sp_session::SessionKeys<Block> for Runtime {364 fn decode_session_keys(365 encoded: Vec<u8>,366 ) -> Option<Vec<(Vec<u8>, KeyTypeId)>> {367 SessionKeys::decode_into_raw_public_keys(&encoded)368 }369370 fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {371 SessionKeys::generate(seed)372 }373 }374375 impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {376 fn slot_duration() -> sp_consensus_aura::SlotDuration {377 sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration())378 }379380 fn authorities() -> Vec<AuraId> {381 Aura::authorities().to_vec()382 }383 }384385 impl cumulus_primitives_core::CollectCollationInfo<Block> for Runtime {386 fn collect_collation_info(header: &<Block as BlockT>::Header) -> cumulus_primitives_core::CollationInfo {387 ParachainSystem::collect_collation_info(header)388 }389 }390391 impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime {392 fn account_nonce(account: AccountId) -> Index {393 System::account_nonce(account)394 }395 }396397 impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance> for Runtime {398 fn query_info(uxt: <Block as BlockT>::Extrinsic, len: u32) -> RuntimeDispatchInfo<Balance> {399 TransactionPayment::query_info(uxt, len)400 }401 fn query_fee_details(uxt: <Block as BlockT>::Extrinsic, len: u32) -> FeeDetails<Balance> {402 TransactionPayment::query_fee_details(uxt, len)403 }404 }405406 /*407 impl pallet_contracts_rpc_runtime_api::ContractsApi<Block, AccountId, Balance, BlockNumber, Hash>408 for Runtime409 {410 fn call(411 origin: AccountId,412 dest: AccountId,413 value: Balance,414 gas_limit: u64,415 input_data: Vec<u8>,416 ) -> pallet_contracts_primitives::ContractExecResult {417 Contracts::bare_call(origin, dest, value, gas_limit, input_data, false)418 }419420 fn instantiate(421 origin: AccountId,422 endowment: Balance,423 gas_limit: u64,424 code: pallet_contracts_primitives::Code<Hash>,425 data: Vec<u8>,426 salt: Vec<u8>,427 ) -> pallet_contracts_primitives::ContractInstantiateResult<AccountId, BlockNumber>428 {429 Contracts::bare_instantiate(origin, endowment, gas_limit, code, data, salt, true, false)430 }431432 fn get_storage(433 address: AccountId,434 key: [u8; 32],435 ) -> pallet_contracts_primitives::GetStorageResult {436 Contracts::get_storage(address, key)437 }438439 fn rent_projection(440 address: AccountId,441 ) -> pallet_contracts_primitives::RentProjectionResult<BlockNumber> {442 Contracts::rent_projection(address)443 }444 }445 */446447 #[cfg(feature = "runtime-benchmarks")]448 impl frame_benchmarking::Benchmark<Block> for Runtime {449 fn benchmark_metadata(extra: bool) -> (450 Vec<frame_benchmarking::BenchmarkList>,451 Vec<frame_support::traits::StorageInfo>,452 ) {453 use frame_benchmarking::{list_benchmark, Benchmarking, BenchmarkList};454 use frame_support::traits::StorageInfoTrait;455456 let mut list = Vec::<BenchmarkList>::new();457458 list_benchmark!(list, extra, pallet_evm_migration, EvmMigration);459 list_benchmark!(list, extra, pallet_common, Common);460 list_benchmark!(list, extra, pallet_unique, Unique);461 list_benchmark!(list, extra, pallet_structure, Structure);462 list_benchmark!(list, extra, pallet_inflation, Inflation);463 list_benchmark!(list, extra, pallet_fungible, Fungible);464 list_benchmark!(list, extra, pallet_refungible, Refungible);465 list_benchmark!(list, extra, pallet_nonfungible, Nonfungible);466 list_benchmark!(list, extra, pallet_unique_scheduler, Scheduler);467468 #[cfg(not(feature = "unique-runtime"))]469 list_benchmark!(list, extra, pallet_proxy_rmrk_core, RmrkCore);470471 #[cfg(not(feature = "unique-runtime"))]472 list_benchmark!(list, extra, pallet_proxy_rmrk_equip, RmrkEquip);473474 // list_benchmark!(list, extra, pallet_evm_coder_substrate, EvmCoderSubstrate);475476 let storage_info = AllPalletsReversedWithSystemFirst::storage_info();477478 return (list, storage_info)479 }480481 fn dispatch_benchmark(482 config: frame_benchmarking::BenchmarkConfig483 ) -> Result<Vec<frame_benchmarking::BenchmarkBatch>, sp_runtime::RuntimeString> {484 use frame_benchmarking::{Benchmarking, BenchmarkBatch, add_benchmark, TrackedStorageKey};485486 let allowlist: Vec<TrackedStorageKey> = vec![487 // Total Issuance488 hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80").to_vec().into(),489490 // Block Number491 hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(),492 // Execution Phase493 hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(),494 // Event Count495 hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(),496 // System Events497 hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(),498499 // Evm CurrentLogs500 hex_literal::hex!("1da53b775b270400e7e61ed5cbc5a146547f210cec367e9af919603343b9cb56").to_vec().into(),501502 // Transactional depth503 hex_literal::hex!("3a7472616e73616374696f6e5f6c6576656c3a").to_vec().into(),504 ];505506 let mut batches = Vec::<BenchmarkBatch>::new();507 let params = (&config, &allowlist);508509 add_benchmark!(params, batches, pallet_evm_migration, EvmMigration);510 add_benchmark!(params, batches, pallet_common, Common);511 add_benchmark!(params, batches, pallet_unique, Unique);512 add_benchmark!(params, batches, pallet_structure, Structure);513 add_benchmark!(params, batches, pallet_inflation, Inflation);514 add_benchmark!(params, batches, pallet_fungible, Fungible);515 add_benchmark!(params, batches, pallet_refungible, Refungible);516 add_benchmark!(params, batches, pallet_nonfungible, Nonfungible);517 add_benchmark!(params, batches, pallet_unique_scheduler, Scheduler);518519 #[cfg(not(feature = "unique-runtime"))]520 add_benchmark!(params, batches, pallet_proxy_rmrk_core, RmrkCore);521522 #[cfg(not(feature = "unique-runtime"))]523 add_benchmark!(params, batches, pallet_proxy_rmrk_equip, RmrkEquip);524525 // add_benchmark!(params, batches, pallet_evm_coder_substrate, EvmCoderSubstrate);526527 if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) }528 Ok(batches)529 }530 }531532 #[cfg(feature = "try-runtime")]533 impl frame_try_runtime::TryRuntime<Block> for Runtime {534 fn on_runtime_upgrade() -> (Weight, Weight) {535 log::info!("try-runtime::on_runtime_upgrade unique-chain.");536 let weight = Executive::try_runtime_upgrade().unwrap();537 (weight, RuntimeBlockWeights::get().max_block)538 }539540 fn execute_block_no_check(block: Block) -> Weight {541 Executive::execute_block_no_check(block)542 }543 }544 }545 }546}tests/CHANGELOG.mddiffbeforeafterboth--- /dev/null
+++ b/tests/CHANGELOG.md
@@ -0,0 +1,11 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+
+## 2022-07-14
+
+### Added
+ - Integrintegration tests of RPC method `token_owners`.
+ - Integrintegration tests of Fungible Pallet.
+
+
\ No newline at end of file
tests/package.jsondiffbeforeafterboth--- a/tests/package.json
+++ b/tests/package.json
@@ -80,6 +80,8 @@
"testLimits": "mocha --timeout 9999999 -r ts-node/register ./**/limits.test.ts",
"testEthCreateCollection": "mocha --timeout 9999999 -r ts-node/register ./**/eth/createCollection.test.ts",
"testRFT": "mocha --timeout 9999999 -r ts-node/register ./**/refungible.test.ts",
+ "testFT": "mocha --timeout 9999999 -r ts-node/register ./**/fungible.test.ts",
+ "testRPC": "mocha --timeout 9999999 -r ts-node/register ./**/rpc.test.ts",
"polkadot-types-fetch-metadata": "curl -H 'Content-Type: application/json' -d '{\"id\":\"1\", \"jsonrpc\":\"2.0\", \"method\": \"state_getMetadata\", \"params\":[]}' http://localhost:9933 > src/interfaces/metadata.json",
"polkadot-types-from-defs": "ts-node ./node_modules/.bin/polkadot-types-from-defs --endpoint src/interfaces/metadata.json --input src/interfaces/ --package .",
"polkadot-types-from-chain": "ts-node ./node_modules/.bin/polkadot-types-from-chain --endpoint src/interfaces/metadata.json --output src/interfaces/ --package .",
tests/src/fungible.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/fungible.test.ts
@@ -0,0 +1,184 @@
+// 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/>.
+
+import {default as usingApi} from './substrate/substrate-api';
+import {IKeyringPair} from '@polkadot/types/types';
+import {
+ getBalance,
+ createMultipleItemsExpectSuccess,
+ isTokenExists,
+ getLastTokenId,
+ getAllowance,
+ approve,
+ transferFrom,
+ createCollection,
+ transfer,
+ burnItem,
+ normalizeAccountId,
+ CrossAccountId,
+ createFungibleItemExpectSuccess,
+ U128_MAX,
+ burnFromExpectSuccess,
+} from './util/helpers';
+
+import chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+chai.use(chaiAsPromised);
+const expect = chai.expect;
+
+let alice: IKeyringPair;
+let bob: IKeyringPair;
+
+describe('integration test: Fungible functionality:', () => {
+ before(async () => {
+ await usingApi(async (api, privateKeyWrapper) => {
+ alice = privateKeyWrapper('//Alice');
+ bob = privateKeyWrapper('//Bob');
+ });
+ });
+
+ it('Create fungible collection and token', async () => {
+ await usingApi(async api => {
+ const createCollectionResult = await createCollection(api, alice, {mode: {type: 'Fungible', decimalPoints: 0}});
+ expect(createCollectionResult.success).to.be.true;
+ const collectionId = createCollectionResult.collectionId;
+ const defaultTokenId = await getLastTokenId(api, collectionId);
+ const aliceTokenId = await createFungibleItemExpectSuccess(alice, collectionId, {Value: U128_MAX}, alice.address);
+ const aliceBalance = await getBalance(api, collectionId, alice, aliceTokenId);
+ const itemCountAfter = await getLastTokenId(api, collectionId);
+
+ // What to expect
+ // tslint:disable-next-line:no-unused-expression
+ expect(itemCountAfter).to.be.equal(defaultTokenId);
+ expect(aliceBalance).to.be.equal(U128_MAX);
+ });
+ });
+
+ it('RPC method tokenOnewrs for fungible collection and token', async () => {
+ await usingApi(async (api, privateKeyWrapper) => {
+ const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'};
+ const facelessCrowd = Array.from(Array(7).keys()).map(i => normalizeAccountId(privateKeyWrapper(i.toString())));
+
+ const createCollectionResult = await createCollection(api, alice, {mode: {type: 'Fungible', decimalPoints: 0}});
+ const collectionId = createCollectionResult.collectionId;
+ const aliceTokenId = await createFungibleItemExpectSuccess(alice, collectionId, {Value: U128_MAX}, alice.address);
+
+ await transfer(api, collectionId, aliceTokenId, alice, bob, 1000n);
+ await transfer(api, collectionId, aliceTokenId, alice, ethAcc, 900n);
+
+ for (let i = 0; i < 7; i++) {
+ await transfer(api, collectionId, aliceTokenId, alice, facelessCrowd[i], 1);
+ }
+
+ const owners = await api.rpc.unique.tokenOwners(collectionId, aliceTokenId);
+ const ids = (owners.toJSON() as CrossAccountId[]).map(s => normalizeAccountId(s));
+ const aliceID = normalizeAccountId(alice);
+ const bobId = normalizeAccountId(bob);
+
+ // What to expect
+ // tslint:disable-next-line:no-unused-expression
+ expect(ids).to.deep.include.members([aliceID, ethAcc, bobId, ...facelessCrowd]);
+ expect(owners.length == 10).to.be.true;
+
+ const eleven = privateKeyWrapper('11');
+ expect(await transfer(api, collectionId, aliceTokenId, alice, eleven, 10n)).to.be.true;
+ expect((await api.rpc.unique.tokenOwners(collectionId, aliceTokenId)).length).to.be.equal(10);
+ });
+ });
+
+ it('Transfer token', async () => {
+ await usingApi(async api => {
+ const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'};
+ const collectionId = (await createCollection(api, alice, {mode: {type: 'Fungible', decimalPoints: 0}})).collectionId;
+ const tokenId = await createFungibleItemExpectSuccess(alice, collectionId, {Value: 500n}, alice.address);
+
+ expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(500n);
+ expect(await transfer(api, collectionId, tokenId, alice, bob, 60n)).to.be.true;
+ expect(await transfer(api, collectionId, tokenId, alice, ethAcc, 140n)).to.be.true;
+
+ expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(300n);
+ expect(await getBalance(api, collectionId, bob, tokenId)).to.be.equal(60n);
+ expect(await getBalance(api, collectionId, ethAcc, tokenId)).to.be.equal(140n);
+ await expect(transfer(api, collectionId, tokenId, alice, bob, 350n)).to.eventually.be.rejected;
+ });
+ });
+
+ it('Tokens multiple creation', async () => {
+ await usingApi(async api => {
+ const collectionId = (await createCollection(api, alice, {mode: {type: 'Fungible', decimalPoints: 0}})).collectionId;
+
+ const args = [
+ {Fungible: {Value: 500n}},
+ {Fungible: {Value: 400n}},
+ {Fungible: {Value: 300n}},
+ ];
+
+ await createMultipleItemsExpectSuccess(alice, collectionId, args);
+ expect(await getBalance(api, collectionId, alice, 0)).to.be.equal(1200n);
+ });
+ });
+
+ it('Burn some tokens ', async () => {
+ await usingApi(async api => {
+ const collectionId = (await createCollection(api, alice, {mode: {type: 'Fungible', decimalPoints: 0}})).collectionId;
+ const tokenId = (await createFungibleItemExpectSuccess(alice, collectionId, {Value: 500n}, alice.address));
+ expect(await isTokenExists(api, collectionId, tokenId)).to.be.true;
+ expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(500n);
+ expect(await burnItem(api, alice, collectionId, tokenId, 499n)).to.be.true;
+ expect(await isTokenExists(api, collectionId, tokenId)).to.be.true;
+ expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(1n);
+ });
+ });
+
+ it('Burn all tokens ', async () => {
+ await usingApi(async api => {
+ const collectionId = (await createCollection(api, alice, {mode: {type: 'Fungible', decimalPoints: 0}})).collectionId;
+ const tokenId = (await createFungibleItemExpectSuccess(alice, collectionId, {Value: 500n}, alice.address));
+ expect(await isTokenExists(api, collectionId, tokenId)).to.be.true;
+ expect(await burnItem(api, alice, collectionId, tokenId, 500n)).to.be.true;
+ expect(await isTokenExists(api, collectionId, tokenId)).to.be.true;
+
+ expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(0n);
+ expect((await api.rpc.unique.totalPieces(collectionId, tokenId)).value.toBigInt()).to.be.equal(0n);
+ });
+ });
+
+ it('Set allowance for token', async () => {
+ await usingApi(async api => {
+ const collectionId = (await createCollection(api, alice, {mode: {type: 'Fungible', decimalPoints: 0}})).collectionId;
+ const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'};
+
+ const tokenId = (await createFungibleItemExpectSuccess(alice, collectionId, {Value: 100n}, alice.address));
+ expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(100n);
+
+ expect(await approve(api, collectionId, tokenId, alice, bob, 60n)).to.be.true;
+ expect(await getAllowance(api, collectionId, alice, bob, tokenId)).to.be.equal(60n);
+ expect(await getBalance(api, collectionId, bob, tokenId)).to.be.equal(0n);
+
+ expect(await transferFrom(api, collectionId, tokenId, bob, alice, bob, 20n)).to.be.true;
+ expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(80n);
+ expect(await getBalance(api, collectionId, bob, tokenId)).to.be.equal(20n);
+ expect(await getAllowance(api, collectionId, alice, bob, tokenId)).to.be.equal(40n);
+
+ await burnFromExpectSuccess(bob, alice, collectionId, tokenId, 10n);
+
+ expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(70n);
+ expect(await getAllowance(api, collectionId, alice, bob, tokenId)).to.be.equal(30n);
+ expect(await transferFrom(api, collectionId, tokenId, bob, alice, ethAcc, 10n)).to.be.true;
+ expect(await getBalance(api, collectionId, ethAcc, tokenId)).to.be.equal(10n);
+ });
+ });
+});
tests/src/interfaces/augment-api-consts.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-consts.ts
+++ b/tests/src/interfaces/augment-api-consts.ts
@@ -29,7 +29,13 @@
[key: string]: Codec;
};
common: {
+ /**
+ * Maximum admins per collection.
+ **/
collectionAdminsLimit: u32 & AugmentedConst<ApiType>;
+ /**
+ * Set price to create a collection.
+ **/
collectionCreationPrice: u128 & AugmentedConst<ApiType>;
/**
* Generic const
tests/src/interfaces/augment-api-events.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-events.ts
+++ b/tests/src/interfaces/augment-api-events.ts
@@ -59,82 +59,47 @@
};
common: {
/**
- * * collection_id
- *
- * * item_id
- *
- * * sender
- *
- * * spender
- *
- * * amount
+ * Amount pieces of token owned by `sender` was approved for `spender`.
**/
Approved: AugmentedEvent<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
/**
* New collection was created
- *
- * # Arguments
- *
- * * collection_id: Globally unique identifier of newly created collection.
- *
- * * mode: [CollectionMode] converted into u8.
- *
- * * account_id: Collection owner.
**/
CollectionCreated: AugmentedEvent<ApiType, [u32, u8, AccountId32]>;
/**
* New collection was destroyed
- *
- * # Arguments
- *
- * * collection_id: Globally unique identifier of collection.
**/
CollectionDestroyed: AugmentedEvent<ApiType, [u32]>;
+ /**
+ * The property has been deleted.
+ **/
CollectionPropertyDeleted: AugmentedEvent<ApiType, [u32, Bytes]>;
+ /**
+ * The colletion property has been set.
+ **/
CollectionPropertySet: AugmentedEvent<ApiType, [u32, Bytes]>;
/**
* New item was created.
- *
- * # Arguments
- *
- * * collection_id: Id of the collection where item was created.
- *
- * * item_id: Id of an item. Unique within the collection.
- *
- * * recipient: Owner of newly created item
- *
- * * amount: Always 1 for NFT
**/
ItemCreated: AugmentedEvent<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
/**
* Collection item was burned.
- *
- * # Arguments
- *
- * * collection_id.
- *
- * * item_id: Identifier of burned NFT.
- *
- * * owner: which user has destroyed its tokens
- *
- * * amount: Always 1 for NFT
**/
ItemDestroyed: AugmentedEvent<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
+ /**
+ * The colletion property permission has been set.
+ **/
PropertyPermissionSet: AugmentedEvent<ApiType, [u32, Bytes]>;
+ /**
+ * The token property has been deleted.
+ **/
TokenPropertyDeleted: AugmentedEvent<ApiType, [u32, u32, Bytes]>;
+ /**
+ * The token property has been set.
+ **/
TokenPropertySet: AugmentedEvent<ApiType, [u32, u32, Bytes]>;
/**
* Item was transferred
- *
- * * collection_id: Id of collection to which item is belong
- *
- * * item_id: Id of an item
- *
- * * sender: Original owner of item
- *
- * * recipient: New owner of item
- *
- * * amount: Always 1 for NFT
**/
Transfer: AugmentedEvent<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
/**
tests/src/interfaces/augment-api-query.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-query.ts
+++ b/tests/src/interfaces/augment-api-query.ts
@@ -69,24 +69,36 @@
[key: string]: QueryableStorageEntry<ApiType>;
};
common: {
+ /**
+ * Storage of collection admins count.
+ **/
adminAmount: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u32>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
/**
* Allowlisted collection users
**/
allowlist: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
/**
- * Collection info
+ * Storage of collection info.
**/
collectionById: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Option<UpDataStructsCollection>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
/**
- * Collection properties
+ * Storage of collection properties.
**/
collectionProperties: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<UpDataStructsProperties>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+ /**
+ * Storage of collection properties permissions.
+ **/
collectionPropertyPermissions: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<BTreeMap<Bytes, UpDataStructsPropertyPermission>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+ /**
+ * Storage of the count of created collections.
+ **/
createdCollectionCount: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+ /**
+ * Storage of the count of deleted collections.
+ **/
destroyedCollectionCount: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
/**
- * Not used by code, exists only to provide some types to metadata
+ * Not used by code, exists only to provide some types to metadata.
**/
dummyStorageValue: AugmentedQuery<ApiType, () => Observable<Option<ITuple<[UpDataStructsCollectionStats, u32, u32, UpDataStructsTokenChild, PhantomTypeUpDataStructs]>>>, []> & QueryableStorageEntry<ApiType, []>;
/**
@@ -231,20 +243,42 @@
[key: string]: QueryableStorageEntry<ApiType>;
};
nonfungible: {
+ /**
+ * Amount of tokens owned by account.
+ **/
accountBalance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<u32>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+ /**
+ * Allowance set by an owner for a spender for a token.
+ **/
allowance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<PalletEvmAccountBasicCrossAccountIdRepr>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
/**
- * Used to enumerate tokens owned by account
+ * Used to enumerate tokens owned by account.
**/
owned: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg3: u32 | AnyNumber | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]>;
+ /**
+ * Custom data that is serialized to bytes and attached to a token property.
+ * Currently used to store RMRK data.
+ **/
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
+ * Used to enumerate token's children.
**/
tokenChildren: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: ITuple<[u32, u32]> | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array]) => Observable<bool>, [u32, u32, ITuple<[u32, u32]>]> & QueryableStorageEntry<ApiType, [u32, u32, ITuple<[u32, u32]>]>;
+ /**
+ * Custom data serialized to bytes for token.
+ **/
tokenData: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<PalletNonfungibleItemData>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
+ /**
+ * Key-Value map stored for token.
+ **/
tokenProperties: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<UpDataStructsProperties>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
+ /**
+ * Amount of burnt tokens for collection.
+ **/
tokensBurnt: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u32>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+ /**
+ * Amount of tokens minted for collection.
+ **/
tokensMinted: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u32>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
/**
* Generic query
@@ -430,6 +464,9 @@
**/
tokenData: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<PalletRefungibleItemData>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
tokenProperties: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<UpDataStructsProperties>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
+ /**
+ * Amount of burnt tokens for collection
+ **/
tokensBurnt: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u32>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
/**
* Amount of tokens minted for collection
tests/src/interfaces/augment-api-rpc.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-rpc.ts
+++ b/tests/src/interfaces/augment-api-rpc.ts
@@ -709,6 +709,10 @@
**/
tokenOwner: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Option<PalletEvmAccountBasicCrossAccountIdRepr>>>;
/**
+ * Returns 10 tokens owners in no particular order
+ **/
+ tokenOwners: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Vec<PalletEvmAccountBasicCrossAccountIdRepr>>>;
+ /**
* Get token properties
**/
tokenProperties: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, propertyKeys?: Vec<Text> | (Text | string)[], at?: Hash | string | Uint8Array) => Observable<Vec<UpDataStructsProperty>>>;
tests/src/interfaces/unique/definitions.tsdiffbeforeafterboth--- a/tests/src/interfaces/unique/definitions.ts
+++ b/tests/src/interfaces/unique/definitions.ts
@@ -49,6 +49,7 @@
balance: fun('Get amount of specific account token', [collectionParam, crossAccountParam(), tokenParam], 'u128'),
allowance: fun('Get allowed amount', [collectionParam, crossAccountParam('sender'), crossAccountParam('spender'), tokenParam], 'u128'),
tokenOwner: fun('Get token owner', [collectionParam, tokenParam], `Option<${CROSS_ACCOUNT_ID_TYPE}>`),
+ tokenOwners: fun('Returns 10 tokens owners in no particular order', [collectionParam, tokenParam], `Vec<${CROSS_ACCOUNT_ID_TYPE}>`),
topmostTokenOwner: fun('Get token owner, in case of nested token - find parent recursive', [collectionParam, tokenParam], `Option<${CROSS_ACCOUNT_ID_TYPE}>`),
tokenChildren: fun('Get tokens nested directly into the token', [collectionParam, tokenParam], 'Vec<UpDataStructsTokenChild>'),
constMetadata: fun('Get token constant metadata', [collectionParam, tokenParam], 'Vec<u8>'),
tests/src/refungible.test.tsdiffbeforeafterboth--- a/tests/src/refungible.test.ts
+++ b/tests/src/refungible.test.ts
@@ -32,6 +32,8 @@
repartitionRFT,
createCollectionWithPropsExpectSuccess,
getDetailedCollectionInfo,
+ normalizeAccountId,
+ CrossAccountId,
getCreateItemsResult,
getDestroyItemsResult,
} from './util/helpers';
@@ -55,14 +57,14 @@
it('Create refungible collection and token', async () => {
await usingApi(async api => {
const createCollectionResult = await createCollection(api, alice, {mode: {type: 'ReFungible'}});
- expect(createCollectionResult.success).to.be.true;
+ expect(createCollectionResult.success).to.be.true;
const collectionId = createCollectionResult.collectionId;
-
+
const itemCountBefore = await getLastTokenId(api, collectionId);
const result = await createRefungibleToken(api, alice, collectionId, 100n);
-
+
const itemCountAfter = await getLastTokenId(api, collectionId);
-
+
// What to expect
// tslint:disable-next-line:no-unused-expression
expect(result.success).to.be.true;
@@ -71,7 +73,43 @@
expect(itemCountAfter.toString()).to.be.equal(result.itemId.toString());
});
});
-
+
+ it('RPC method tokenOnewrs for refungible collection and token', async () => {
+ await usingApi(async (api, privateKeyWrapper) => {
+ const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'};
+ const facelessCrowd = Array.from(Array(7).keys()).map(i => normalizeAccountId(privateKeyWrapper(i.toString())));
+
+ const createCollectionResult = await createCollection(api, alice, {mode: {type: 'ReFungible'}});
+ const collectionId = createCollectionResult.collectionId;
+
+ const result = await createRefungibleToken(api, alice, collectionId, 10_000n);
+ const aliceTokenId = result.itemId;
+
+
+ await transfer(api, collectionId, aliceTokenId, alice, bob, 1000n);
+ await transfer(api, collectionId, aliceTokenId, alice, ethAcc, 900n);
+
+ for (let i = 0; i < 7; i++) {
+ await transfer(api, collectionId, aliceTokenId, alice, facelessCrowd[i], 50*(i+1));
+ }
+
+ const owners = await api.rpc.unique.tokenOwners(collectionId, aliceTokenId);
+ const ids = (owners.toJSON() as CrossAccountId[]).map(s => normalizeAccountId(s));
+
+ const aliceID = normalizeAccountId(alice);
+ const bobId = normalizeAccountId(bob);
+
+ // What to expect
+ // tslint:disable-next-line:no-unused-expression
+ expect(ids).to.deep.include.members([aliceID, ethAcc, bobId, ...facelessCrowd]);
+ expect(owners.length).to.be.equal(10);
+
+ const eleven = privateKeyWrapper('11');
+ expect(await transfer(api, collectionId, aliceTokenId, alice, eleven, 10n)).to.be.true;
+ expect((await api.rpc.unique.tokenOwners(collectionId, aliceTokenId)).length).to.be.equal(10);
+ });
+ });
+
it('Transfer token pieces', async () => {
await usingApi(async api => {
const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;
tests/src/rpc.test.tsdiffbeforeafterboth--- a/tests/src/rpc.test.ts
+++ b/tests/src/rpc.test.ts
@@ -1,12 +1,57 @@
+import {IKeyringPair} from '@polkadot/types/types';
import {expect} from 'chai';
import usingApi from './substrate/substrate-api';
-import {createCollectionExpectSuccess, getTokenOwner} from './util/helpers';
+import {createCollection, createCollectionExpectSuccess, createFungibleItemExpectSuccess, CrossAccountId, getTokenOwner, normalizeAccountId, transfer, U128_MAX} from './util/helpers';
+
+let alice: IKeyringPair;
+let bob: IKeyringPair;
-describe('getTokenOwner', () => {
+
+describe('integration test: RPC methods', () => {
+ before(async () => {
+ await usingApi(async (api, privateKeyWrapper) => {
+ alice = privateKeyWrapper('//Alice');
+ bob = privateKeyWrapper('//Bob');
+ });
+ });
+
+
it('returns None for fungible collection', async () => {
await usingApi(async api => {
const collection = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
await expect(getTokenOwner(api, collection, 0)).to.be.rejectedWith(/^owner == null$/);
});
});
+
+ it('RPC method tokenOwners for fungible collection and token', async () => {
+ await usingApi(async (api, privateKeyWrapper) => {
+ const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'};
+ const facelessCrowd = Array.from(Array(7).keys()).map(i => normalizeAccountId(privateKeyWrapper(i.toString())));
+
+ const createCollectionResult = await createCollection(api, alice, {mode: {type: 'Fungible', decimalPoints: 0}});
+ const collectionId = createCollectionResult.collectionId;
+ const aliceTokenId = await createFungibleItemExpectSuccess(alice, collectionId, {Value: U128_MAX}, alice.address);
+
+ await transfer(api, collectionId, aliceTokenId, alice, bob, 1000n);
+ await transfer(api, collectionId, aliceTokenId, alice, ethAcc, 900n);
+
+ for (let i = 0; i < 7; i++) {
+ await transfer(api, collectionId, aliceTokenId, alice, facelessCrowd[i], 1);
+ }
+
+ const owners = await api.rpc.unique.tokenOwners(collectionId, aliceTokenId);
+ const ids = (owners.toJSON() as CrossAccountId[]).map(s => normalizeAccountId(s));
+ const aliceID = normalizeAccountId(alice);
+ const bobId = normalizeAccountId(bob);
+
+ // What to expect
+ // tslint:disable-next-line:no-unused-expression
+ expect(ids).to.deep.include.members([aliceID, ethAcc, bobId, ...facelessCrowd]);
+ expect(owners.length == 10).to.be.true;
+
+ const eleven = privateKeyWrapper('11');
+ expect(await transfer(api, collectionId, aliceTokenId, alice, eleven, 10n)).to.be.true;
+ expect((await api.rpc.unique.tokenOwners(collectionId, aliceTokenId)).length).to.be.equal(10);
+ });
+ });
});
\ No newline at end of file