--- a/tests/src/eth/tokenProperties.test.ts
+++ b/tests/src/eth/tokenProperties.test.ts
@@ -14,11 +14,12 @@
// You should have received a copy of the GNU General Public License
// along with Unique Network. If not, see .
+import {IKeyringPair} from '@polkadot/types/types';
+import {Contract} from 'web3-eth-contract';
import {itEth, usingEthPlaygrounds, expect, EthUniqueHelper} from './util';
-import {IKeyringPair} from '@polkadot/types/types';
import {ITokenPropertyPermission, TCollectionMode} from '../util/playgrounds/types';
import {Pallets} from '../util';
-import {UniqueNFTCollection, UniqueRFTCollection} from '../util/playgrounds/unique';
+import {UniqueNFTCollection, UniqueNFToken, UniqueRFTCollection} from '../util/playgrounds/unique';
describe('EVM token properties', () => {
let donor: IKeyringPair;
@@ -88,84 +89,85 @@
expect(properties).to.deep.equal(testCase.expectedProps);
}));
- async function checkProps(helper: EthUniqueHelper, mode: TCollectionMode) {
- const caller = await helper.eth.createAccountWithBalance(donor);
-
- const properties = Array(5).fill(0).map((_, i) => { return {key: `key_${i}`, value: Buffer.from(`value_${i}`)}; });
- const permissions: ITokenPropertyPermission[] = properties.map(p => { return {key: p.key, permission: {tokenOwner: true,
- collectionAdmin: true,
- mutable: true}}; });
-
- const collection = await helper[mode].mintCollection(alice, {
- tokenPrefix: 'ethp',
- tokenPropertyPermissions: permissions,
- }) as UniqueNFTCollection | UniqueRFTCollection;
-
- const token = await collection.mintToken(alice);
-
- const valuesBefore = await token.getProperties(properties.map(p => p.key));
- expect(valuesBefore).to.be.deep.equal([]);
-
-
- await collection.addAdmin(alice, {Ethereum: caller});
-
- const address = helper.ethAddress.fromCollectionId(collection.collectionId);
- const contract = helper.ethNativeContract.collection(address, mode, caller);
-
- expect(await contract.methods.properties(token.tokenId, []).call()).to.be.deep.equal([]);
-
- await contract.methods.setProperties(token.tokenId, properties).send({from: caller});
-
- const values = await token.getProperties(properties.map(p => p.key));
- expect(values).to.be.deep.equal(properties.map(p => { return {key: p.key, value: p.value.toString()}; }));
-
- expect(await contract.methods.properties(token.tokenId, []).call()).to.be.like(properties
- .map(p => { return helper.ethProperty.property(p.key, p.value.toString()); }));
-
- expect(await contract.methods.properties(token.tokenId, [properties[0].key]).call())
- .to.be.like([helper.ethProperty.property(properties[0].key, properties[0].value.toString())]);
- }
+ [
+ {mode: 'nft' as const, requiredPallets: [Pallets.ReFungible]},
+ {mode: 'rft' as const, requiredPallets: []},
+ ].map(testCase =>
+ itEth.ifWithPallets(`Can be multiple set/read for ${testCase.mode}`, testCase.requiredPallets, async({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+
+ const properties = Array(5).fill(0).map((_, i) => { return {key: `key_${i}`, value: Buffer.from(`value_${i}`)}; });
+ const permissions: ITokenPropertyPermission[] = properties.map(p => { return {key: p.key, permission: {tokenOwner: true,
+ collectionAdmin: true,
+ mutable: true}}; });
+
+ const collection = await helper[testCase.mode].mintCollection(alice, {
+ tokenPrefix: 'ethp',
+ tokenPropertyPermissions: permissions,
+ }) as UniqueNFTCollection | UniqueRFTCollection;
+
+ const token = await collection.mintToken(alice);
+
+ const valuesBefore = await token.getProperties(properties.map(p => p.key));
+ expect(valuesBefore).to.be.deep.equal([]);
+
+
+ await collection.addAdmin(alice, {Ethereum: caller});
+
+ const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(address, testCase.mode, caller);
+
+ expect(await contract.methods.properties(token.tokenId, []).call()).to.be.deep.equal([]);
- itEth('Can be multiple set/read for NFT ', async({helper}) => {
- await checkProps(helper, 'nft');
- });
+ await contract.methods.setProperties(token.tokenId, properties).send({from: caller});
- itEth.ifWithPallets('Can be multiple set/read for RFT ', [Pallets.ReFungible], async({helper}) => {
- await checkProps(helper, 'rft');
- });
+ const values = await token.getProperties(properties.map(p => p.key));
+ expect(values).to.be.deep.equal(properties.map(p => { return {key: p.key, value: p.value.toString()}; }));
+
+ expect(await contract.methods.properties(token.tokenId, []).call()).to.be.like(properties
+ .map(p => { return helper.ethProperty.property(p.key, p.value.toString()); }));
+
+ expect(await contract.methods.properties(token.tokenId, [properties[0].key]).call())
+ .to.be.like([helper.ethProperty.property(properties[0].key, properties[0].value.toString())]);
+ }));
- itEth('Can be deleted', async({helper}) => {
- const caller = await helper.eth.createAccountWithBalance(donor);
- const collection = await helper.nft.mintCollection(alice, {
- tokenPropertyPermissions: [{
- key: 'testKey',
- permission: {
- mutable: true,
- collectionAdmin: true,
+ [
+ {mode: 'nft' as const, requiredPallets: [Pallets.ReFungible]},
+ {mode: 'rft' as const, requiredPallets: []},
+ ].map(testCase =>
+ itEth.ifWithPallets(`Can be deleted fro ${testCase.mode}`, testCase.requiredPallets, async({helper}) => {
+ const caller = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper[testCase.mode].mintCollection(alice, {
+ tokenPropertyPermissions: [{
+ key: 'testKey',
+ permission: {
+ mutable: true,
+ collectionAdmin: true,
+ },
},
- },
- {
- key: 'testKey_1',
- permission: {
- mutable: true,
- collectionAdmin: true,
- },
- }],
- });
+ {
+ key: 'testKey_1',
+ permission: {
+ mutable: true,
+ collectionAdmin: true,
+ },
+ }],
+ });
- const token = await collection.mintToken(alice);
- await token.setProperties(alice, [{key: 'testKey', value: 'testValue'}, {key: 'testKey_1', value: 'testValue_1'}]);
+ const token = await collection.mintToken(alice);
+ await token.setProperties(alice, [{key: 'testKey', value: 'testValue'}, {key: 'testKey_1', value: 'testValue_1'}]);
+ expect(await token.getProperties()).to.has.length(2);
- await collection.addAdmin(alice, {Ethereum: caller});
+ await collection.addAdmin(alice, {Ethereum: caller});
- const address = helper.ethAddress.fromCollectionId(collection.collectionId);
- const contract = helper.ethNativeContract.collection(address, 'nft', caller);
+ const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(address, testCase.mode, caller);
- await contract.methods.deleteProperties(token.tokenId, ['testKey', 'testKey_1']).send({from: caller});
+ await contract.methods.deleteProperties(token.tokenId, ['testKey', 'testKey_1']).send({from: caller});
- const result = await token.getProperties(['testKey']);
- expect(result.length).to.equal(0);
- });
+ const result = await token.getProperties(['testKey', 'testKey_1']);
+ expect(result.length).to.equal(0);
+ }));
itEth('Can be read', async({helper}) => {
const caller = helper.eth.createAccount();
@@ -189,6 +191,94 @@
});
});
+describe('EVM token properties negative', () => {
+ let donor: IKeyringPair;
+ let alice: IKeyringPair;
+ let caller: string;
+ let aliceCollection: UniqueNFTCollection;
+ let token: UniqueNFToken;
+ const tokenProps = [{key: 'testKey_1', value: 'testValue_1'}, {key: 'testKey_2', value: 'testValue_2'}];
+ let collectionEvm: Contract;
+
+ before(async function() {
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ donor = await privateKey({filename: __filename});
+ [alice] = await helper.arrange.createAccounts([100n], donor);
+ });
+ });
+
+ beforeEach(async () => {
+ // 1. create collection with props: testKey_1, testKey_2
+ // 2. create token and set props testKey_1, testKey_2
+ await usingEthPlaygrounds(async (helper) => {
+ aliceCollection = await helper.nft.mintCollection(alice, {
+ tokenPropertyPermissions: [{
+ key: 'testKey_1',
+ permission: {
+ mutable: true,
+ collectionAdmin: true,
+ },
+ },
+ {
+ key: 'testKey_2',
+ permission: {
+ mutable: true,
+ collectionAdmin: true,
+ },
+ }],
+ });
+ token = await aliceCollection.mintToken(alice);
+ await token.setProperties(alice, tokenProps);
+ collectionEvm = helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, true);
+ });
+ });
+
+ [
+ {method: 'setProperty', methodParams: [tokenProps[1].key, Buffer.from('newValue')]},
+ {method: 'setProperties', methodParams: [[{key: tokenProps[1].key, value: Buffer.from('newValue')}]]},
+ ].map(testCase =>
+ itEth(`[${testCase.method}] Cannot set properties of non-owned collection`, async ({helper}) => {
+ caller = await helper.eth.createAccountWithBalance(donor);
+ collectionEvm = helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, true);
+ // Caller an owner and not an admin, so he cannot set properties:
+ // FIXME: Can setProperties as non owner and non admin
+ await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).call({from: caller})).to.be.rejectedWith('NoPermission');
+ await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller})).to.be.rejected;
+
+ // Props have not changed:
+ const expectedProps = tokenProps.map(p => helper.ethProperty.property(p.key, p.value.toString()));
+ const actualProps = await collectionEvm.methods.properties(token.tokenId, []).call();
+ expect(actualProps).to.deep.eq(expectedProps);
+ }));
+
+ [
+ {method: 'setProperty', methodParams: ['testKey_3', Buffer.from('testValue3')]},
+ {method: 'setProperties', methodParams: [[{key: 'testKey_3', value: Buffer.from('testValue3')}]]},
+ ].map(testCase =>
+ itEth(`[${testCase.method}] Cannot set non-existing properties`, async ({helper}) => {
+ caller = await helper.eth.createAccountWithBalance(donor);
+ collectionEvm = helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, true);
+ await helper.collection.addAdmin(alice, aliceCollection.collectionId, {Ethereum: caller});
+
+ await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).call({from: caller})).to.be.rejectedWith('NoPermission');
+ await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller})).to.be.rejected;
+
+ // Props have not changed:
+ const actualProps = await collectionEvm.methods.properties(token.tokenId, ['testKey2']).call();
+ expect(actualProps).to.deep.eq(tokenProps
+ .map(p => { return helper.ethProperty.property(p.key, p.value.toString());
+ }));
+ }));
+
+ itEth('Cannot delete properties of non-owned collection', async () => {
+
+ });
+
+ itEth('Cannot delete non-existing properties', async () => {
+
+ });
+});
+
type ElementOf = A extends readonly (infer T)[] ? T : never;
function* cartesian>, R extends Array>(internalRest: [...R], ...args: [...T]): Generator<[...R, ...{[K in keyof T]: ElementOf}]> {