difftreelog
initial commit
in: master
14 files changed
client/rpc/src/lib.rsdiffbeforeafterboth--- a/client/rpc/src/lib.rs
+++ b/client/rpc/src/lib.rs
@@ -70,6 +70,15 @@
token: TokenId,
at: Option<BlockHash>,
) -> Result<Option<CrossAccountId>>;
+
+ #[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 +482,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/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -1748,6 +1748,9 @@
/// * `token` - The token for which you need to find out the owner.
fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;
+ /// Token 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/src/common.rsdiffbeforeafterboth--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -362,6 +362,10 @@
None
}
+ fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {
+ <Pallet<T>>::token_owners(self.id, token).unwrap_or_else(|| vec![])
+ }
+
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,20 @@
nesting_budget,
)
}
+
+ pub fn token_owners(
+ collection: CollectionId,
+ _token: TokenId,
+ ) -> Option<Vec<T::CrossAccountId>> {
+ let res: Vec<T::CrossAccountId> = <Balance<T>>::iter_prefix((collection,))
+ .map(|r| r.0)
+ .take(10)
+ .collect();
+
+ if res.len() == 0 {
+ None
+ } else {
+ Some(res)
+ }
+ }
}
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,10 @@
<TokenData<T>>::get((self.id, token)).map(|t| t.owner)
}
+ 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/src/common.rsdiffbeforeafterboth--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -438,6 +438,10 @@
<Pallet<T>>::token_owner(self.id, token)
}
+ fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {
+ <Pallet<T>>::token_owners(self.id, token).unwrap_or_else(|| vec![])
+ }
+
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
@@ -1134,4 +1134,20 @@
) -> DispatchResult {
<PalletCommon<T>>::set_token_property_permissions(collection, sender, property_permissions)
}
+
+ 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(|r| r.0)
+ .take(10)
+ .collect();
+
+ if res.len() == 0 {
+ None
+ } else {
+ Some(res)
+ }
+ }
}
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/src/runtime_apis.rsdiffbeforeafterboth--- a/runtime/common/src/runtime_apis.rs
+++ b/runtime/common/src/runtime_apis.rs
@@ -40,6 +40,11 @@
fn token_owner(collection: CollectionId, token: TokenId) -> Result<Option<CrossAccountId>, DispatchError> {
dispatch_unique_runtime!(collection.token_owner(token))
}
+
+ fn token_owners(collection: CollectionId, token: TokenId) -> Result<Vec::<CrossAccountId>, DispatchError> {
+ dispatch_unique_runtime!(collection.token_owners(token))
+ }
+
fn topmost_token_owner(collection: CollectionId, token: TokenId) -> Result<Option<CrossAccountId>, DispatchError> {
let budget = up_data_structs::budget::Value::new(10);
tests/package.jsondiffbeforeafterboth--- a/tests/package.json
+++ b/tests/package.json
@@ -80,6 +80,7 @@
"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",
+ "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/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>>>;
/**
+ * Get token owners
+ **/
+ 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('Get token owners', [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.tsdiffbeforeafterboth1// 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/>.1617import {default as usingApi, executeTransaction} from './substrate/substrate-api';18import {IKeyringPair} from '@polkadot/types/types';19import {20 createCollectionExpectSuccess,21 getBalance,22 createMultipleItemsExpectSuccess,23 isTokenExists,24 getLastTokenId,25 getAllowance,26 approve,27 transferFrom,28 createCollection,29 createRefungibleToken,30 transfer,31 burnItem,32 repartitionRFT,33 createCollectionWithPropsExpectSuccess,34 getDetailedCollectionInfo,35} from './util/helpers';3637import chai from 'chai';38import chaiAsPromised from 'chai-as-promised';39chai.use(chaiAsPromised);40const expect = chai.expect;4142let alice: IKeyringPair;43let bob: IKeyringPair;4445describe('integration test: Refungible functionality:', () => {46 before(async () => {47 await usingApi(async (api, privateKeyWrapper) => {48 alice = privateKeyWrapper('//Alice');49 bob = privateKeyWrapper('//Bob');50 });51 });5253 it('Create refungible collection and token', async () => {54 await usingApi(async api => {55 const createCollectionResult = await createCollection(api, alice, {mode: {type: 'ReFungible'}});56 expect(createCollectionResult.success).to.be.true; 57 const collectionId = createCollectionResult.collectionId;5859 const itemCountBefore = await getLastTokenId(api, collectionId);60 const result = await createRefungibleToken(api, alice, collectionId, 100n);6162 const itemCountAfter = await getLastTokenId(api, collectionId);6364 // What to expect65 // tslint:disable-next-line:no-unused-expression66 expect(result.success).to.be.true;67 expect(itemCountAfter).to.be.equal(itemCountBefore + 1);68 expect(collectionId).to.be.equal(result.collectionId);69 expect(itemCountAfter.toString()).to.be.equal(result.itemId.toString());70 });71 });7273 it('Transfer token pieces', async () => {74 await usingApi(async api => {75 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;76 const tokenId = (await createRefungibleToken(api, alice, collectionId, 100n)).itemId;7778 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(100n);79 expect(await transfer(api, collectionId, tokenId, alice, bob, 60n)).to.be.true;8081 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(40n);82 expect(await getBalance(api, collectionId, bob, tokenId)).to.be.equal(60n);83 await expect(transfer(api, collectionId, tokenId, alice, bob, 41n)).to.eventually.be.rejected;84 });85 });8687 it('Create multiple tokens', async () => {88 const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});89 const args = [90 {ReFungible: {pieces: 1}},91 {ReFungible: {pieces: 2}},92 {ReFungible: {pieces: 100}},93 ];94 await createMultipleItemsExpectSuccess(alice, collectionId, args);9596 await usingApi(async api => { 97 const tokenId = await getLastTokenId(api, collectionId);98 expect(tokenId).to.be.equal(3);99 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(100n);100 });101 });102103 it('Burn some pieces', async () => {104 await usingApi(async api => { 105 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;106 const tokenId = (await createRefungibleToken(api, alice, collectionId, 100n)).itemId;107 expect(await isTokenExists(api, collectionId, tokenId)).to.be.true;108 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(100n);109 expect(await burnItem(api, alice, collectionId, tokenId, 99n)).to.be.true;110 expect(await isTokenExists(api, collectionId, tokenId)).to.be.true;111 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(1n);112 });113 });114115 it('Burn all pieces', async () => {116 await usingApi(async api => { 117 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;118 const tokenId = (await createRefungibleToken(api, alice, collectionId, 100n)).itemId;119 expect(await isTokenExists(api, collectionId, tokenId)).to.be.true;120 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(100n);121 expect(await burnItem(api, alice, collectionId, tokenId, 100n)).to.be.true;122 expect(await isTokenExists(api, collectionId, tokenId)).to.be.false;123 });124 });125126 it('Burn some pieces for multiple users', async () => {127 await usingApi(async api => { 128 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;129 const tokenId = (await createRefungibleToken(api, alice, collectionId, 100n)).itemId;130 expect(await isTokenExists(api, collectionId, tokenId)).to.be.true;131132 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(100n);133 expect(await transfer(api, collectionId, tokenId, alice, bob, 60n)).to.be.true;134135 136 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(40n);137 expect(await getBalance(api, collectionId, bob, tokenId)).to.be.equal(60n);138 expect(await burnItem(api, alice, collectionId, tokenId, 40n)).to.be.true;139140 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(0n);141 expect(await isTokenExists(api, collectionId, tokenId)).to.be.true;142 expect(await burnItem(api, bob, collectionId, tokenId, 59n)).to.be.true;143144 expect(await getBalance(api, collectionId, bob, tokenId)).to.be.equal(1n);145 expect(await isTokenExists(api, collectionId, tokenId)).to.be.true;146 expect(await burnItem(api, bob, collectionId, tokenId, 1n)).to.be.true;147 148 expect(await isTokenExists(api, collectionId, tokenId)).to.be.false;149 });150 });151152 it('Set allowance for token', async () => {153 await usingApi(async api => {154 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;155 const tokenId = (await createRefungibleToken(api, alice, collectionId, 100n)).itemId;156157 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(100n);158159 expect(await approve(api, collectionId, tokenId, alice, bob, 60n)).to.be.true;160 expect(await getAllowance(api, collectionId, alice, bob, tokenId)).to.be.equal(60n);161162 expect(await transferFrom(api, collectionId, tokenId, bob, alice, bob, 20n)).to.be.true;163 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(80n);164 expect(await getBalance(api, collectionId, bob, tokenId)).to.be.equal(20n);165 expect(await getAllowance(api, collectionId, alice, bob, tokenId)).to.be.equal(40n);166 });167 });168169 it('Repartition', async () => {170 await usingApi(async api => {171 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;172 const tokenId = (await createRefungibleToken(api, alice, collectionId, 100n)).itemId;173174 expect(await repartitionRFT(api, collectionId, alice, tokenId, 200n)).to.be.true;175 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(200n);176177 expect(await transfer(api, collectionId, tokenId, alice, bob, 110n)).to.be.true;178 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(90n);179 expect(await getBalance(api, collectionId, bob, tokenId)).to.be.equal(110n);180181 await expect(repartitionRFT(api, collectionId, alice, tokenId, 80n)).to.eventually.be.rejected;182183 expect(await transfer(api, collectionId, tokenId, alice, bob, 90n)).to.be.true;184 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(0n);185 expect(await getBalance(api, collectionId, bob, tokenId)).to.be.equal(200n);186187 expect(await repartitionRFT(api, collectionId, bob, tokenId, 150n)).to.be.true;188 await expect(transfer(api, collectionId, tokenId, bob, alice, 160n)).to.eventually.be.rejected;189 });190 });191});192193describe('Test Refungible properties:', () => {194 before(async () => {195 await usingApi(async (api, privateKeyWrapper) => {196 alice = privateKeyWrapper('//Alice');197 bob = privateKeyWrapper('//Bob');198 });199 });200 201 it('Сreate new collection with properties', async () => {202 await usingApi(async api => {203 const properties = [{key: 'key1', value: 'val1'}];204 const propertyPermissions = [{key: 'key1', permission: {tokenOwner: true, mutable: false, collectionAdmin: true}}];205 const collectionId = await createCollectionWithPropsExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'ReFungible'},206 properties: properties,207 propPerm: propertyPermissions, 208 });209 const collection = (await getDetailedCollectionInfo(api, collectionId))!;210 expect(collection.properties.toHuman()).to.be.deep.equal(properties);211 expect(collection.tokenPropertyPermissions.toHuman()).to.be.deep.equal(propertyPermissions);212 });213 });214});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/>.1617import {default as usingApi, executeTransaction} from './substrate/substrate-api';18import {IKeyringPair} from '@polkadot/types/types';19import {20 createCollectionExpectSuccess,21 getBalance,22 createMultipleItemsExpectSuccess,23 isTokenExists,24 getLastTokenId,25 getAllowance,26 approve,27 transferFrom,28 createCollection,29 createRefungibleToken,30 transfer,31 burnItem,32 repartitionRFT,33 createCollectionWithPropsExpectSuccess,34 getDetailedCollectionInfo,35} from './util/helpers';3637import chai from 'chai';38import chaiAsPromised from 'chai-as-promised';39chai.use(chaiAsPromised);40const expect = chai.expect;4142let alice: IKeyringPair;43let bob: IKeyringPair;4445describe('integration test: Refungible functionality:', () => {46 before(async () => {47 await usingApi(async (api, privateKeyWrapper) => {48 alice = privateKeyWrapper('//Alice');49 bob = privateKeyWrapper('//Bob');50 });51 });5253 it('Create refungible collection and token', async () => {54 await usingApi(async api => {55 const createCollectionResult = await createCollection(api, alice, {mode: {type: 'ReFungible'}});56 expect(createCollectionResult.success).to.be.true;57 const collectionId = createCollectionResult.collectionId;58 59 const itemCountBefore = await getLastTokenId(api, collectionId);60 const result = await createRefungibleToken(api, alice, collectionId, 100n);61 62 const itemCountAfter = await getLastTokenId(api, collectionId);63 64 // What to expect65 // tslint:disable-next-line:no-unused-expression66 expect(result.success).to.be.true;67 expect(itemCountAfter).to.be.equal(itemCountBefore + 1);68 expect(collectionId).to.be.equal(result.collectionId);69 expect(itemCountAfter.toString()).to.be.equal(result.itemId.toString());70 });71 });72 73 it('RPC method tokenOnewrs for refungible collection and token', async () => {74 await usingApi(async (api, privateKeyWrapper) => {75 const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'};76 const facelessCrowd = Array.from(Array(7).keys()).map(i => normalizeAccountId(privateKeyWrapper(i.toString())));77 78 const createCollectionResult = await createCollection(api, alice, {mode: {type: 'ReFungible'}});79 const collectionId = createCollectionResult.collectionId;80 81 const result = await createRefungibleToken(api, alice, collectionId, 10_000n);82 const aliceTokenId = result.itemId;83 84 85 await transfer(api, collectionId, aliceTokenId, alice, bob, 1000n);86 await transfer(api, collectionId, aliceTokenId, alice, ethAcc, 900n);87 88 for (let i = 0; i < 7; i++) {89 await transfer(api, collectionId, aliceTokenId, alice, facelessCrowd[i], 50*(i+1));90 } 91 92 const owners = await api.rpc.unique.tokenOwners(collectionId, aliceTokenId);93 const ids = (owners.toJSON() as CrossAccountId[]).map(s => normalizeAccountId(s));94 95 const aliceID = normalizeAccountId(alice);96 const bobId = normalizeAccountId(bob);97 98 // What to expect99 // tslint:disable-next-line:no-unused-expression100 expect(ids).to.deep.include.members([aliceID, ethAcc, bobId, ...facelessCrowd]);101 expect(owners.length == 10).to.be.true;102 });103 });104 105 it('Transfer token pieces', async () => {106 await usingApi(async api => {107 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;108 const tokenId = (await createRefungibleToken(api, alice, collectionId, 100n)).itemId;109110 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(100n);111 expect(await transfer(api, collectionId, tokenId, alice, bob, 60n)).to.be.true;112113 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(40n);114 expect(await getBalance(api, collectionId, bob, tokenId)).to.be.equal(60n);115 await expect(transfer(api, collectionId, tokenId, alice, bob, 41n)).to.eventually.be.rejected;116 });117 });118119 it('Create multiple tokens', async () => {120 const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});121 const args = [122 {ReFungible: {pieces: 1}},123 {ReFungible: {pieces: 2}},124 {ReFungible: {pieces: 100}},125 ];126 await createMultipleItemsExpectSuccess(alice, collectionId, args);127128 await usingApi(async api => { 129 const tokenId = await getLastTokenId(api, collectionId);130 expect(tokenId).to.be.equal(3);131 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(100n);132 });133 });134135 it('Burn some pieces', async () => {136 await usingApi(async api => { 137 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;138 const tokenId = (await createRefungibleToken(api, alice, collectionId, 100n)).itemId;139 expect(await isTokenExists(api, collectionId, tokenId)).to.be.true;140 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(100n);141 expect(await burnItem(api, alice, collectionId, tokenId, 99n)).to.be.true;142 expect(await isTokenExists(api, collectionId, tokenId)).to.be.true;143 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(1n);144 });145 });146147 it('Burn all pieces', async () => {148 await usingApi(async api => { 149 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;150 const tokenId = (await createRefungibleToken(api, alice, collectionId, 100n)).itemId;151 expect(await isTokenExists(api, collectionId, tokenId)).to.be.true;152 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(100n);153 expect(await burnItem(api, alice, collectionId, tokenId, 100n)).to.be.true;154 expect(await isTokenExists(api, collectionId, tokenId)).to.be.false;155 });156 });157158 it('Burn some pieces for multiple users', async () => {159 await usingApi(async api => { 160 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;161 const tokenId = (await createRefungibleToken(api, alice, collectionId, 100n)).itemId;162 expect(await isTokenExists(api, collectionId, tokenId)).to.be.true;163164 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(100n);165 expect(await transfer(api, collectionId, tokenId, alice, bob, 60n)).to.be.true;166167 168 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(40n);169 expect(await getBalance(api, collectionId, bob, tokenId)).to.be.equal(60n);170 expect(await burnItem(api, alice, collectionId, tokenId, 40n)).to.be.true;171172 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(0n);173 expect(await isTokenExists(api, collectionId, tokenId)).to.be.true;174 expect(await burnItem(api, bob, collectionId, tokenId, 59n)).to.be.true;175176 expect(await getBalance(api, collectionId, bob, tokenId)).to.be.equal(1n);177 expect(await isTokenExists(api, collectionId, tokenId)).to.be.true;178 expect(await burnItem(api, bob, collectionId, tokenId, 1n)).to.be.true;179 180 expect(await isTokenExists(api, collectionId, tokenId)).to.be.false;181 });182 });183184 it('Set allowance for token', async () => {185 await usingApi(async api => {186 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;187 const tokenId = (await createRefungibleToken(api, alice, collectionId, 100n)).itemId;188189 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(100n);190191 expect(await approve(api, collectionId, tokenId, alice, bob, 60n)).to.be.true;192 expect(await getAllowance(api, collectionId, alice, bob, tokenId)).to.be.equal(60n);193194 expect(await transferFrom(api, collectionId, tokenId, bob, alice, bob, 20n)).to.be.true;195 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(80n);196 expect(await getBalance(api, collectionId, bob, tokenId)).to.be.equal(20n);197 expect(await getAllowance(api, collectionId, alice, bob, tokenId)).to.be.equal(40n);198 });199 });200201 it('Repartition', async () => {202 await usingApi(async api => {203 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;204 const tokenId = (await createRefungibleToken(api, alice, collectionId, 100n)).itemId;205206 expect(await repartitionRFT(api, collectionId, alice, tokenId, 200n)).to.be.true;207 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(200n);208209 expect(await transfer(api, collectionId, tokenId, alice, bob, 110n)).to.be.true;210 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(90n);211 expect(await getBalance(api, collectionId, bob, tokenId)).to.be.equal(110n);212213 await expect(repartitionRFT(api, collectionId, alice, tokenId, 80n)).to.eventually.be.rejected;214215 expect(await transfer(api, collectionId, tokenId, alice, bob, 90n)).to.be.true;216 expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(0n);217 expect(await getBalance(api, collectionId, bob, tokenId)).to.be.equal(200n);218219 expect(await repartitionRFT(api, collectionId, bob, tokenId, 150n)).to.be.true;220 await expect(transfer(api, collectionId, tokenId, bob, alice, 160n)).to.eventually.be.rejected;221 });222 });223});224225describe('Test Refungible properties:', () => {226 before(async () => {227 await usingApi(async (api, privateKeyWrapper) => {228 alice = privateKeyWrapper('//Alice');229 bob = privateKeyWrapper('//Bob');230 });231 });232 233 it('Сreate new collection with properties', async () => {234 await usingApi(async api => {235 const properties = [{key: 'key1', value: 'val1'}];236 const propertyPermissions = [{key: 'key1', permission: {tokenOwner: true, mutable: false, collectionAdmin: true}}];237 const collectionId = await createCollectionWithPropsExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'ReFungible'},238 properties: properties,239 propPerm: propertyPermissions, 240 });241 const collection = (await getDetailedCollectionInfo(api, collectionId))!;242 expect(collection.properties.toHuman()).to.be.deep.equal(properties);243 expect(collection.tokenPropertyPermissions.toHuman()).to.be.deep.equal(propertyPermissions);244 });245 });246});tests/src/rpc.test.tsdiffbeforeafterboth--- a/tests/src/rpc.test.ts
+++ b/tests/src/rpc.test.ts
@@ -1,12 +1,53 @@
+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 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;
+ });
+ });
});
\ No newline at end of file