git.delta.rocks / unique-network / refs/commits / 3e8db543c726

difftreelog

Merge pull request #799 from UniqueNetwork/tests/eth-helpers

ut-akuznetsov2022-12-22parents: #3739509 #7d30844.patch.diff
in: master

14 files changed

modified.envdiffbeforeafterboth
33
4POLKADOT_MAINNET_BRANCH=release-v0.9.304POLKADOT_MAINNET_BRANCH=release-v0.9.30
5STATEMINT_BUILD_BRANCH=release-parachains-v93205STATEMINT_BUILD_BRANCH=release-parachains-v9320
6ACALA_BUILD_BRANCH=2.10.16ACALA_BUILD_BRANCH=2.11.0
7MOONBEAM_BUILD_BRANCH=runtime-19017MOONBEAM_BUILD_BRANCH=runtime-1901
8UNIQUE_MAINNET_BRANCH=v9300338UNIQUE_MAINNET_BRANCH=release-v930033
9UNIQUE_REPLICA_FROM=wss://eu-ws.unique.network:4439UNIQUE_REPLICA_FROM=wss://eu-ws.unique.network:443
1010
11KUSAMA_MAINNET_BRANCH=release-v0.9.3411KUSAMA_MAINNET_BRANCH=release-v0.9.35
12STATEMINE_BUILD_BRANCH=release-parachains-v932012STATEMINE_BUILD_BRANCH=release-parachains-v9330
13KARURA_BUILD_BRANCH=release-karura-2.10.013KARURA_BUILD_BRANCH=release-karura-2.11.0
14MOONRIVER_BUILD_BRANCH=runtime-190114MOONRIVER_BUILD_BRANCH=runtime-2000
15QUARTZ_MAINNET_BRANCH=v93003315QUARTZ_MAINNET_BRANCH=release-v930034
16QUARTZ_REPLICA_FROM=wss://eu-ws-quartz.unique.network:44316QUARTZ_REPLICA_FROM=wss://eu-ws-quartz.unique.network:443
1717
18UNQWND_MAINNET_BRANCH=release-v0.9.3018UNQWND_MAINNET_BRANCH=release-v0.9.30
19WESTMINT_BUILD_BRANCH=parachains-v933019WESTMINT_BUILD_BRANCH=parachains-v9330
20OPAL_MAINNET_BRANCH=v93003220OPAL_MAINNET_BRANCH=release-v930034
21OPAL_REPLICA_FROM=wss://eu-ws-opal.unique.network:44321OPAL_REPLICA_FROM=wss://eu-ws-opal.unique.network:443
2222
23POLKADOT_LAUNCH_BRANCH=unique-network23POLKADOT_LAUNCH_BRANCH=unique-network
modifiedtests/package.jsondiffbeforeafterboth
8 "@types/chai": "^4.3.3",8 "@types/chai": "^4.3.3",
9 "@types/chai-as-promised": "^7.1.5",9 "@types/chai-as-promised": "^7.1.5",
10 "@types/chai-like": "^1.1.1",10 "@types/chai-like": "^1.1.1",
11 "@types/chai-subset": "^1.3.3",
11 "@types/mocha": "^10.0.0",12 "@types/mocha": "^10.0.0",
12 "@types/node": "^18.11.2",13 "@types/node": "^18.11.2",
13 "@typescript-eslint/eslint-plugin": "^5.40.1",14 "@typescript-eslint/eslint-plugin": "^5.40.1",
14 "@typescript-eslint/parser": "^5.40.1",15 "@typescript-eslint/parser": "^5.40.1",
15 "chai": "^4.3.6",16 "chai": "^4.3.6",
17 "chai-subset": "^1.6.0",
16 "eslint": "^8.25.0",18 "eslint": "^8.25.0",
17 "eslint-plugin-mocha": "^10.1.0",19 "eslint-plugin-mocha": "^10.1.0",
18 "mocha": "^10.1.0",20 "mocha": "^10.1.0",
modifiedtests/src/eth/collectionAdmin.test.tsdiffbeforeafterboth
1515
16import {IKeyringPair} from '@polkadot/types/types';16import {IKeyringPair} from '@polkadot/types/types';
17import {expect} from 'chai';17import {expect} from 'chai';
18import {Pallets} from '../util';
18import {IEthCrossAccountId} from '../util/playgrounds/types';19import {IEthCrossAccountId} from '../util/playgrounds/types';
19import {usingEthPlaygrounds, itEth} from './util';20import {usingEthPlaygrounds, itEth} from './util';
20import {EthUniqueHelper} from './util/playgrounds/unique.dev';21import {EthUniqueHelper} from './util/playgrounds/unique.dev';
39 });40 });
40 });41 });
4142
43 [
44 {mode: 'nft' as const, requiredPallets: []},
45 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
46 {mode: 'ft' as const, requiredPallets: []},
47 ].map(testCase => {
42 itEth('can add account admin by owner', async ({helper, privateKey}) => {48 itEth.ifWithPallets(`can add account admin by owner for ${testCase.mode}`, testCase.requiredPallets, async ({helper, privateKey}) => {
43 // arrange49 // arrange
44 const owner = await helper.eth.createAccountWithBalance(donor);50 const owner = await helper.eth.createAccountWithBalance(donor);
45 const adminSub = await privateKey('//admin2');51 const adminSub = await privateKey('//admin2');
49 const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);55 const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);
50 const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);56 const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);
51 57
52 const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');58 const {collectionAddress, collectionId} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');
53 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);59 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, true);
5460
61 // Check isOwnerOrAdminCross returns false:
62 expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossSub).call()).to.be.false;
63 expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossEth).call()).to.be.false;
64 expect(await collectionEvm.methods.isOwnerOrAdminCross(helper.ethCrossAccount.fromAddress(adminDeprecated)).call()).to.be.false;
65
55 // Soft-deprecated: can addCollectionAdmin 66 // Soft-deprecated: can addCollectionAdmin
56 await collectionEvm.methods.addCollectionAdmin(adminDeprecated).send();67 await collectionEvm.methods.addCollectionAdmin(adminDeprecated).send();
70 });81 });
71 expect(adminListRpc).to.be.like(adminListEth);82 expect(adminListRpc).to.be.like(adminListEth);
83
84 // 3. check isOwnerOrAdminCross returns true:
85 expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossSub).call()).to.be.true;
86 expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossEth).call()).to.be.true;
87 expect(await collectionEvm.methods.isOwnerOrAdminCross(helper.ethCrossAccount.fromAddress(adminDeprecated)).call()).to.be.true;
72 });88 });
89 });
7390
74 itEth('cross account admin can mint', async ({helper}) => {91 itEth('cross account admin can mint', async ({helper}) => {
75 // arrange: create collection and accounts92 // arrange: create collection and accounts
modifiedtests/src/eth/collectionProperties.test.tsdiffbeforeafterboth
26 before(async function() {26 before(async function() {
27 await usingEthPlaygrounds(async (_helper, privateKey) => {27 await usingEthPlaygrounds(async (_helper, privateKey) => {
28 donor = await privateKey({filename: __filename});28 donor = await privateKey({filename: __filename});
29 [alice] = await _helper.arrange.createAccounts([20n], donor);29 [alice] = await _helper.arrange.createAccounts([50n], donor);
30 });30 });
31 });31 });
3232
33 // Soft-deprecated: setCollectionProperty33 // Soft-deprecated: setCollectionProperty
34 [34 [
35 {method: 'setCollectionProperties', methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]}, 35 {method: 'setCollectionProperties', mode: 'nft' as const, methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},
36 {method: 'setCollectionProperties', mode: 'rft' as const, methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},
37 {method: 'setCollectionProperties', mode: 'ft' as const, methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},
36 {method: 'setCollectionProperty', methodParams: ['testKey', Buffer.from('testValue')], expectedProps: [{key: 'testKey', value: 'testValue'}]},38 {method: 'setCollectionProperty', mode: 'nft' as const, methodParams: ['testKey', Buffer.from('testValue')], expectedProps: [{key: 'testKey', value: 'testValue'}]},
37 ].map(testCase => 39 ].map(testCase =>
38 itEth(`Collection properties can be set: ${testCase.method}`, async({helper}) => {40 itEth.ifWithPallets(`Collection properties can be set: ${testCase.method}() for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => {
39 const caller = await helper.eth.createAccountWithBalance(donor);41 const caller = await helper.eth.createAccountWithBalance(donor);
40 const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties: []});42 const collection = await helper[testCase.mode].mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties: []});
41 await collection.addAdmin(alice, {Ethereum: caller});43 await collection.addAdmin(alice, {Ethereum: caller});
4244
43 const address = helper.ethAddress.fromCollectionId(collection.collectionId);45 const address = helper.ethAddress.fromCollectionId(collection.collectionId);
44 const contract = helper.ethNativeContract.collection(address, 'nft', caller, testCase.method === 'setCollectionProperty');46 const collectionEvm = helper.ethNativeContract.collection(address, 'nft', caller, testCase.method === 'setCollectionProperty');
47
48 // collectionProperties returns an empty array if no properties:
49 expect(await collectionEvm.methods.collectionProperties([]).call()).to.be.like([]);
50 expect(await collectionEvm.methods.collectionProperties(['NonExistingKey']).call()).to.be.like([]);
4551
46 await contract.methods[testCase.method](...testCase.methodParams).send({from: caller});52 await collectionEvm.methods[testCase.method](...testCase.methodParams).send({from: caller});
4753
48 const raw = (await collection.getData())?.raw;54 const raw = (await collection.getData())?.raw;
49 expect(raw.properties).to.deep.equal(testCase.expectedProps);55 expect(raw.properties).to.deep.equal(testCase.expectedProps);
56
57 // collectionProperties returns properties:
58 expect(await collectionEvm.methods.collectionProperties([]).call()).to.be.like(testCase.expectedProps.map(prop => helper.ethProperty.property(prop.key, prop.value)));
50 }));59 }));
5160
52 itEth('Cannot set invalid properties', async({helper}) => {61 itEth('Cannot set invalid properties', async({helper}) => {
6877
69 // Soft-deprecated: deleteCollectionProperty78 // Soft-deprecated: deleteCollectionProperty
70 [79 [
71 {method: 'deleteCollectionProperties', methodParams: [['testKey1', 'testKey2']], expectedProps: [{key: 'testKey3', value: 'testValue3'}]},80 {method: 'deleteCollectionProperties', mode: 'nft' as const, methodParams: [['testKey1', 'testKey2']], expectedProps: [{key: 'testKey3', value: 'testValue3'}]},
81 {method: 'deleteCollectionProperties', mode: 'rft' as const, methodParams: [['testKey1', 'testKey2']], expectedProps: [{key: 'testKey3', value: 'testValue3'}]},
82 {method: 'deleteCollectionProperties', mode: 'ft' as const, methodParams: [['testKey1', 'testKey2']], expectedProps: [{key: 'testKey3', value: 'testValue3'}]},
72 {method: 'deleteCollectionProperty', methodParams: ['testKey1'], expectedProps: [{key: 'testKey2', value: 'testValue2'}, {key: 'testKey3', value: 'testValue3'}]}, 83 {method: 'deleteCollectionProperty', mode: 'nft' as const, methodParams: ['testKey1'], expectedProps: [{key: 'testKey2', value: 'testValue2'}, {key: 'testKey3', value: 'testValue3'}]},
73 ].map(testCase => 84 ].map(testCase =>
74 itEth(`Collection properties can be deleted: ${testCase.method}()`, async({helper}) => {85 itEth.ifWithPallets(`Collection properties can be deleted: ${testCase.method}() for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => {
75 const properties = [86 const properties = [
76 {key: 'testKey1', value: 'testValue1'},87 {key: 'testKey1', value: 'testValue1'},
77 {key: 'testKey2', value: 'testValue2'},88 {key: 'testKey2', value: 'testValue2'},
78 {key: 'testKey3', value: 'testValue3'}];89 {key: 'testKey3', value: 'testValue3'}];
79 const caller = await helper.eth.createAccountWithBalance(donor);90 const caller = await helper.eth.createAccountWithBalance(donor);
80 const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties});91 const collection = await helper[testCase.mode].mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties});
81 92
82 await collection.addAdmin(alice, {Ethereum: caller});93 await collection.addAdmin(alice, {Ethereum: caller});
83 94
210 }));220 }));
211});221});
212
213describe('EVM collection property', () => {
214 let donor: IKeyringPair;
215
216 before(async function() {
217 await usingEthPlaygrounds(async (_helper, privateKey) => {
218 donor = await privateKey({filename: __filename});
219 });
220 });
221
222 [
223 {case: 'nft' as const},
224 {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
225 {case: 'ft' as const},
226 ].map(testCase =>
227 itEth.ifWithPallets(`can set/read properties ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => {
228 const collection = await helper[testCase.case].mintCollection(donor, {name: 'A', description: 'B', tokenPrefix: 'C'});
229
230 const sender = await helper.eth.createAccountWithBalance(donor, 100n);
231 await collection.addAdmin(donor, {Ethereum: sender});
232
233 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
234 const contract = helper.ethNativeContract.collection(collectionAddress, testCase.case, sender);
235
236 const keys = ['key0', 'key1'];
237
238 const writeProperties = [
239 helper.ethProperty.property(keys[0], 'value0'),
240 helper.ethProperty.property(keys[1], 'value1'),
241 ];
242
243 await contract.methods.setCollectionProperties(writeProperties).send();
244 const readProperties = await contract.methods.collectionProperties([keys[0], keys[1]]).call();
245 expect(readProperties).to.be.like(writeProperties);
246 }));
247
248 [
249 {case: 'nft' as const},
250 {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
251 {case: 'ft' as const},
252 ].map(testCase =>
253 itEth.ifWithPallets(`can delete properties ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => {
254 const collection = await helper[testCase.case].mintCollection(donor, {name: 'A', description: 'B', tokenPrefix: 'C'});
255
256 const sender = await helper.eth.createAccountWithBalance(donor, 100n);
257 await collection.addAdmin(donor, {Ethereum: sender});
258
259 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
260 const contract = helper.ethNativeContract.collection(collectionAddress, testCase.case, sender);
261
262 const keys = ['key0', 'key1', 'key2', 'key3'];
263
264 {
265 const writeProperties = [
266 helper.ethProperty.property(keys[0], 'value0'),
267 helper.ethProperty.property(keys[1], 'value1'),
268 helper.ethProperty.property(keys[2], 'value2'),
269 helper.ethProperty.property(keys[3], 'value3'),
270 ];
271
272 await contract.methods.setCollectionProperties(writeProperties).send();
273 const readProperties = await contract.methods.collectionProperties([keys[0], keys[1], keys[2], keys[3]]).call();
274 expect(readProperties).to.be.like(writeProperties);
275 }
276
277 {
278 const expectProperties = [
279 helper.ethProperty.property(keys[0], 'value0'),
280 helper.ethProperty.property(keys[1], 'value1'),
281 ];
282
283 await contract.methods.deleteCollectionProperties([keys[2], keys[3]]).send();
284 const readProperties = await contract.methods.collectionProperties([]).call();
285 expect(readProperties).to.be.like(expectProperties);
286 }
287 }));
288});
289222
modifiedtests/src/eth/contractSponsoring.test.tsdiffbeforeafterboth
2222
23describe('Sponsoring EVM contracts', () => {23describe('Sponsoring EVM contracts', () => {
24 let donor: IKeyringPair;24 let donor: IKeyringPair;
25 let nominal: bigint;
2526
26 before(async () => {27 before(async () => {
27 await usingPlaygrounds(async (_helper, privateKey) => {28 await usingPlaygrounds(async (helper, privateKey) => {
28 donor = await privateKey({filename: __filename});29 donor = await privateKey({filename: __filename});
30 nominal = helper.balance.getOneTokenNominal();
29 });31 });
30 });32 });
31
32 itEth('Self sponsored can be set by the address that deployed the contract', async ({helper}) => {
33 const owner = await helper.eth.createAccountWithBalance(donor);
34 const flipper = await helper.eth.deployFlipper(owner);
35 const helpers = helper.ethNativeContract.contractHelpers(owner);
36
37 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
38 await expect(helpers.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.not.rejected;
39 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
40 });
4133
42 itEth('Set self sponsored events', async ({helper}) => {34 itEth('Self sponsoring can be set by the address that deployed the contract', async ({helper}) => {
43 const owner = await helper.eth.createAccountWithBalance(donor);35 const owner = await helper.eth.createAccountWithBalance(donor);
44 const flipper = await helper.eth.deployFlipper(owner);36 const flipper = await helper.eth.deployFlipper(owner);
45 const helpers = helper.ethNativeContract.contractHelpers(owner);37 const helpers = helper.ethNativeContract.contractHelpers(owner);
46 38
39 // 1. owner can set selfSponsoring:
40 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
47 const result = await helpers.methods.selfSponsoredEnable(flipper.options.address).send();41 const result = await helpers.methods.selfSponsoredEnable(flipper.options.address).send({from: owner});
42 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
43
44 // 1.1 Can get sponsor using methods.sponsor:
45 const actualSponsor = await helpers.methods.sponsor(flipper.options.address).call();
46 expect(actualSponsor.eth).to.eq(flipper.options.address);
47 expect(actualSponsor.sub).to.eq('0');
48
49 // 2. Events should be:
48 const ethEvents = helper.eth.helper.eth.normalizeEvents(result.events);50 const ethEvents = helper.eth.helper.eth.normalizeEvents(result.events);
49 expect(ethEvents).to.be.deep.equal([51 expect(ethEvents).to.be.deep.equal([
50 {52 {
66 ]);68 ]);
67 });69 });
6870
69 itEth('Self sponsored can not be set by the address that did not deployed the contract', async ({helper}) => {71 itEth('Self sponsoring cannot be set by the address that did not deployed the contract', async ({helper}) => {
70 const owner = await helper.eth.createAccountWithBalance(donor);72 const owner = await helper.eth.createAccountWithBalance(donor);
71 const notOwner = await helper.eth.createAccountWithBalance(donor);73 const notOwner = await helper.eth.createAccountWithBalance(donor);
72 const helpers = helper.ethNativeContract.contractHelpers(owner);74 const helpers = helper.ethNativeContract.contractHelpers(owner);
83 const flipper = await helper.eth.deployFlipper(owner);85 const flipper = await helper.eth.deployFlipper(owner);
8486
85 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;87 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
86 await expect(helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner})).to.be.not.rejected;88 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
87 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;89 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;
88 });90 });
8991
98 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;100 expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
99 });101 });
100 102
101 itEth('Sponsor can be set by the address that deployed the contract', async ({helper}) => {
102 const owner = await helper.eth.createAccountWithBalance(donor);
103 const sponsor = await helper.eth.createAccountWithBalance(donor);
104 const helpers = helper.ethNativeContract.contractHelpers(owner);
105 const flipper = await helper.eth.deployFlipper(owner);
106
107 expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;
108 await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).send()).to.be.not.rejected;
109 expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.true;
110 });
111
112 itEth('Set sponsor event', async ({helper}) => {103 itEth('Sponsor can be set by the address that deployed the contract', async ({helper}) => {
113 const owner = await helper.eth.createAccountWithBalance(donor);104 const owner = await helper.eth.createAccountWithBalance(donor);
114 const sponsor = await helper.eth.createAccountWithBalance(donor);105 const sponsor = await helper.eth.createAccountWithBalance(donor);
115 const helpers = helper.ethNativeContract.contractHelpers(owner);106 const helpers = helper.ethNativeContract.contractHelpers(owner);
116 const flipper = await helper.eth.deployFlipper(owner);107 const flipper = await helper.eth.deployFlipper(owner);
117 108
109 // 1. owner can set a sponsor:
110 expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;
118 const result = await helpers.methods.setSponsor(flipper.options.address, sponsor).send();111 const result = await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
112 expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.true;
113
114 // 2. Events should be:
119 const events = helper.eth.normalizeEvents(result.events);115 const events = helper.eth.normalizeEvents(result.events);
120 expect(events).to.be.deep.equal([116 expect(events).to.be.deep.equal([
121 {117 {
129 ]);125 ]);
130 });126 });
131 127
132 itEth('Sponsor can not be set by the address that did not deployed the contract', async ({helper}) => {128 itEth('Sponsor cannot be set by the address that did not deployed the contract', async ({helper}) => {
133 const owner = await helper.eth.createAccountWithBalance(donor);129 const owner = await helper.eth.createAccountWithBalance(donor);
134 const sponsor = await helper.eth.createAccountWithBalance(donor);130 const sponsor = await helper.eth.createAccountWithBalance(donor);
135 const notOwner = await helper.eth.createAccountWithBalance(donor);131 const notOwner = await helper.eth.createAccountWithBalance(donor);
141 expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;137 expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;
142 });138 });
143
144 itEth('Sponsorship can be confirmed by the address that pending as sponsor', async ({helper}) => {
145 const owner = await helper.eth.createAccountWithBalance(donor);
146 const sponsor = await helper.eth.createAccountWithBalance(donor);
147 const helpers = helper.ethNativeContract.contractHelpers(owner);
148 const flipper = await helper.eth.deployFlipper(owner);
149
150 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
151 await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).send()).to.be.not.rejected;
152 await expect(helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor})).to.be.not.rejected;
153 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
154 });
155139
156 itEth('Confirm sponsorship event', async ({helper}) => {140 itEth('Sponsorship can be confirmed by the address that pending as sponsor', async ({helper}) => {
157 const owner = await helper.eth.createAccountWithBalance(donor);141 const owner = await helper.eth.createAccountWithBalance(donor);
158 const sponsor = await helper.eth.createAccountWithBalance(donor);142 const sponsor = await helper.eth.createAccountWithBalance(donor);
159 const helpers = helper.ethNativeContract.contractHelpers(owner);143 const helpers = helper.ethNativeContract.contractHelpers(owner);
160 const flipper = await helper.eth.deployFlipper(owner);144 const flipper = await helper.eth.deployFlipper(owner);
161145
146 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
162 await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).send()).to.be.not.rejected;147 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
148
149 // 1. sponsor can confirm sponsorship:
163 const result = await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});150 const result = await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
151 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
152
153 // 1.1 Can get sponsor using methods.sponsor:
154 const actualSponsor = await helpers.methods.sponsor(flipper.options.address).call();
155 expect(actualSponsor.eth).to.eq(sponsor);
156 expect(actualSponsor.sub).to.eq('0');
157
158 // 2. Events should be:
164 const events = helper.eth.normalizeEvents(result.events);159 const events = helper.eth.normalizeEvents(result.events);
165 expect(events).to.be.deep.equal([160 expect(events).to.be.deep.equal([
166 {161 {
198 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;193 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
199 });194 });
200
201 itEth('Get self sponsored sponsor', async ({helper}) => {
202 const owner = await helper.eth.createAccountWithBalance(donor);
203 const helpers = helper.ethNativeContract.contractHelpers(owner);
204 const flipper = await helper.eth.deployFlipper(owner);
205
206 await helpers.methods.selfSponsoredEnable(flipper.options.address).send();
207
208 const result = await helpers.methods.sponsor(flipper.options.address).call();
209
210 expect(result[0]).to.be.eq(flipper.options.address);
211 expect(result[1]).to.be.eq('0');
212 });
213
214 itEth('Get confirmed sponsor', async ({helper}) => {
215 const owner = await helper.eth.createAccountWithBalance(donor);
216 const sponsor = await helper.eth.createAccountWithBalance(donor);
217 const helpers = helper.ethNativeContract.contractHelpers(owner);
218 const flipper = await helper.eth.deployFlipper(owner);
219
220 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
221 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
222
223 const result = await helpers.methods.sponsor(flipper.options.address).call();
224
225 expect(result[0]).to.be.eq(sponsor);
226 expect(result[1]).to.be.eq('0');
227 });
228195
229 itEth('Sponsor can be removed by the address that deployed the contract', async ({helper}) => {196 itEth('Sponsor can be removed by the address that deployed the contract', async ({helper}) => {
230 const owner = await helper.eth.createAccountWithBalance(donor);197 const owner = await helper.eth.createAccountWithBalance(donor);
236 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();203 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
237 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});204 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
238 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;205 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
239 206 // 1. Can remove sponsor:
240 await helpers.methods.removeSponsor(flipper.options.address).send();207 const result = await helpers.methods.removeSponsor(flipper.options.address).send();
241 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;208 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
209
210 // 2. Events should be:
211 const events = helper.eth.normalizeEvents(result.events);
212 expect(events).to.be.deep.equal([
213 {
214 address: flipper.options.address,
215 event: 'ContractSponsorRemoved',
216 args: {
217 contractAddress: flipper.options.address,
218 },
219 },
220 ]);
221
222 // TODO: why call method reverts?
223 // const actualSponsor = await helpers.methods.sponsor(flipper.options.address).call();
224 // expect(actualSponsor.eth).to.eq(sponsor);
225 // expect(actualSponsor.sub).to.eq('0');
242 });226 });
243
244 itEth('Remove sponsor event', async ({helper}) => {
245 const owner = await helper.eth.createAccountWithBalance(donor);
246 const sponsor = await helper.eth.createAccountWithBalance(donor);
247 const helpers = helper.ethNativeContract.contractHelpers(owner);
248 const flipper = await helper.eth.deployFlipper(owner);
249
250 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
251 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
252
253 const result = await helpers.methods.removeSponsor(flipper.options.address).send();
254 const events = helper.eth.normalizeEvents(result.events);
255 expect(events).to.be.deep.equal([
256 {
257 address: flipper.options.address,
258 event: 'ContractSponsorRemoved',
259 args: {
260 contractAddress: flipper.options.address,
261 },
262 },
263 ]);
264 });
265227
266 itEth('Sponsor can not be removed by the address that did not deployed the contract', async ({helper}) => {228 itEth('Sponsor can not be removed by the address that did not deployed the contract', async ({helper}) => {
267 const owner = await helper.eth.createAccountWithBalance(donor);229 const owner = await helper.eth.createAccountWithBalance(donor);
276 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;238 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
277 239
278 await expect(helpers.methods.removeSponsor(flipper.options.address).call({from: notOwner})).to.be.rejectedWith('NoPermission');240 await expect(helpers.methods.removeSponsor(flipper.options.address).call({from: notOwner})).to.be.rejectedWith('NoPermission');
241 await expect(helpers.methods.removeSponsor(flipper.options.address).send({from: notOwner})).to.be.rejected;
279 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;242 expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
280 });243 });
281244
292 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner});255 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner});
293 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});256 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
294257
295 const sponsorBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(sponsor));258 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
296 const callerBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(caller));259 const callerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));
297260
298 await flipper.methods.flip().send({from: caller});261 await flipper.methods.flip().send({from: caller});
299 expect(await flipper.methods.getValue().call()).to.be.true;262 expect(await flipper.methods.getValue().call()).to.be.true;
300263
301 // Balance should be taken from sponsor instead of caller264 // Balance should be taken from sponsor instead of caller
302 const sponsorBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(sponsor));265 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
303 const callerBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(caller));266 const callerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));
304 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;267 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
305 expect(callerBalanceAfter).to.be.eq(callerBalanceBefore);268 expect(callerBalanceAfter).to.be.eq(callerBalanceBefore);
306 });269 });
331 expect(callerBalanceAfter).to.be.eq(callerBalanceBefore);294 expect(callerBalanceAfter).to.be.eq(callerBalanceBefore);
332 });295 });
333296
297 [
298 {balance: 0n, label: '0'},
299 {balance: 10n, label: '10'},
300 ].map(testCase => {
334 itEth('Sponsoring is set, an address that has no UNQ can send a transaction and it works. Sponsor balance should decrease (allowlisted)', async ({helper}) => {301 itEth(`Allow-listed address that has ${testCase.label} UNQ can call a contract. Sponsor balance should decrease`, async ({helper}) => {
335 const owner = await helper.eth.createAccountWithBalance(donor);302 const owner = await helper.eth.createAccountWithBalance(donor);
336 const sponsor = await helper.eth.createAccountWithBalance(donor);303 const sponsor = await helper.eth.createAccountWithBalance(donor);
337 const caller = helper.eth.createAccount();304 const caller = helper.eth.createAccount();
305 await helper.eth.transferBalanceFromSubstrate(donor, caller, testCase.balance);
338 const helpers = helper.ethNativeContract.contractHelpers(owner);306 const helpers = helper.ethNativeContract.contractHelpers(owner);
339 const flipper = await helper.eth.deployFlipper(owner);307 const flipper = await helper.eth.deployFlipper(owner);
340308
347 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();315 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
348 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});316 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
349317
350 const sponsorBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(sponsor));318 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
351 expect(sponsorBalanceBefore).to.be.not.equal('0');319 expect(sponsorBalanceBefore > 0n).to.be.true;
352320
353 await flipper.methods.flip().send({from: caller});321 await flipper.methods.flip().send({from: caller});
354 expect(await flipper.methods.getValue().call()).to.be.true;322 expect(await flipper.methods.getValue().call()).to.be.true;
355323
356 // Balance should be taken from flipper instead of caller324 // Balance should be taken from flipper instead of caller
357 const sponsorBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(sponsor));325 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
358 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;326 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
327 // Caller's balance does not change:
328 const callerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));
329 expect(callerBalanceAfter).to.eq(testCase.balance * nominal);
359 });330 });
360331 });
361 itEth('Sponsoring is set, an address that has no UNQ can send a transaction and it works. Sponsor balance should not decrease (non-allowlisted)', async ({helper}) => {
362 const owner = await helper.eth.createAccountWithBalance(donor);
363 const caller = await helper.eth.createAccount();
364 const helpers = helper.ethNativeContract.contractHelpers(owner);
365 const flipper = await helper.eth.deployFlipper(owner);
366
367 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
368 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
369
370 await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address);
371
372 const originalFlipperBalance = await helper.balance.getEthereum(flipper.options.address);
373 expect(originalFlipperBalance).to.be.not.equal('0');
374
375 await expect(flipper.methods.flip().send({from: caller})).to.be.rejectedWith(/Returned error: insufficient funds for gas \* price \+ value/);
376 expect(await flipper.methods.getValue().call()).to.be.false;
377
378 // Balance should be taken from flipper instead of caller
379 // FIXME the comment is wrong! What check should be here?
380 const balanceAfter = await helper.balance.getEthereum(flipper.options.address);
381 expect(balanceAfter).to.be.equal(originalFlipperBalance);
382 });
383332
384 itEth('Sponsoring is set, an address that has UNQ can send a transaction and it works. User balance should not change', async ({helper}) => {333 itEth('Non-allow-listed address can call a contract. Sponsor balance should not decrease', async ({helper}) => {
385 const owner = await helper.eth.createAccountWithBalance(donor);334 const owner = await helper.eth.createAccountWithBalance(donor);
386 const sponsor = await helper.eth.createAccountWithBalance(donor);
387 const caller = await helper.eth.createAccountWithBalance(donor);335 const caller = helper.eth.createAccount();
388 const helpers = helper.ethNativeContract.contractHelpers(owner);336 const contractHelpers = helper.ethNativeContract.contractHelpers(owner);
337
338 // Deploy flipper and send some tokens:
389 const flipper = await helper.eth.deployFlipper(owner);339 const flipper = await helper.eth.deployFlipper(owner);
390
391 await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});340 await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address);
392 await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});341 expect(await flipper.methods.getValue().call()).to.be.false;
393342 // flipper address has some tokens:
394 await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});343 const originalFlipperBalance = await helper.balance.getEthereum(flipper.options.address);
344 expect(originalFlipperBalance > 0n).to.be.true;
345
346 // Set Allowlisted sponsoring mode. caller is not in allow list:
395 await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});347 await contractHelpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});
396
397 await helpers.methods.setSponsor(flipper.options.address, sponsor).send();348 await contractHelpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
398 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});349 await contractHelpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
399350
400 const sponsorBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(sponsor));351 // 1. Caller has no UNQ and is not in allow list. So he cannot flip:
401 const callerBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(caller));
402
403 await flipper.methods.flip().send({from: caller});352 await expect(flipper.methods.flip().send({from: caller})).to.be.rejectedWith(/Returned error: insufficient funds for gas \* price \+ value/);
404 expect(await flipper.methods.getValue().call()).to.be.true;353 expect(await flipper.methods.getValue().call()).to.be.false;
405354
355 // Flipper's balance does not change:
406 const sponsorBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(sponsor));356 const balanceAfter = await helper.balance.getEthereum(flipper.options.address);
407 const callerBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(caller));
408 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
409 expect(callerBalanceAfter).to.be.equal(callerBalanceBefore);357 expect(balanceAfter).to.be.equal(originalFlipperBalance);
410 });358 });
411359
412 itEth('Sponsoring is limited, with setContractRateLimit. The limitation is working if transactions are sent more often, the sender pays the commission.', async ({helper}) => {360 itEth('Sponsoring is limited, with setContractRateLimit. The limitation is working if transactions are sent more often, the sender pays the commission.', async ({helper}) => {
427 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});375 await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
428376
429 const originalFlipperBalance = await helper.balance.getEthereum(sponsor);377 const originalFlipperBalance = await helper.balance.getEthereum(sponsor);
430 expect(originalFlipperBalance).to.be.not.equal('0');378 expect(originalFlipperBalance > 0n).to.be.true;
431379
432 await flipper.methods.flip().send({from: caller});380 await flipper.methods.flip().send({from: caller});
433 expect(await flipper.methods.getValue().call()).to.be.true;381 expect(await flipper.methods.getValue().call()).to.be.true;
modifiedtests/src/eth/events.test.tsdiffbeforeafterboth
40 const {collectionAddress, events: ethEvents} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');40 const {collectionAddress, events: ethEvents} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');
41 await helper.wait.newBlocks(1);41 await helper.wait.newBlocks(1);
42 {42 {
43 expect(ethEvents).to.be.like([43 expect(ethEvents).to.containSubset([
44 {44 {
45 event: 'CollectionCreated',45 event: 'CollectionCreated',
46 args: {46 args: {
49 },49 },
50 },50 },
51 ]);51 ]);
52 expect(subEvents).to.be.like([{method: 'CollectionCreated'}]);52 expect(subEvents).to.containSubset([{method: 'CollectionCreated'}]);
53 clearEvents(ethEvents, subEvents);53 clearEvents(ethEvents, subEvents);
54 }54 }
55 {55 {
56 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);56 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
57 const result = await collectionHelper.methods.destroyCollection(collectionAddress).send({from:owner});57 const result = await collectionHelper.methods.destroyCollection(collectionAddress).send({from:owner});
58 await helper.wait.newBlocks(1);58 await helper.wait.newBlocks(1);
59 expect(result.events).to.be.like({59 expect(result.events).to.containSubset({
60 CollectionDestroyed: {60 CollectionDestroyed: {
61 returnValues: {61 returnValues: {
62 collectionId: collectionAddress,62 collectionId: collectionAddress,
63 },63 },
64 },64 },
65 });65 });
66 expect(subEvents).to.be.like([{method: 'CollectionDestroyed'}]);66 expect(subEvents).to.containSubset([{method: 'CollectionDestroyed'}]);
67 }67 }
68 unsubscribe();68 unsubscribe();
69}69}
7070
71async function testCollectionPropertySetAndDeleted(helper: EthUniqueHelper, mode: TCollectionMode) {71async function testCollectionPropertySetAndDeleted(helper: EthUniqueHelper, mode: TCollectionMode) {
72 const owner = await helper.eth.createAccountWithBalance(donor);72 const owner = await helper.eth.createAccountWithBalance(donor);
73 const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');73 const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');
74 const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);74 const collection = helper.ethNativeContract.collection(collectionAddress, mode, owner);
75 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);75 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
76 76
77 const ethEvents: any = [];77 const ethEvents: any = [];
82 {82 {
83 await collection.methods.setCollectionProperties([{key: 'A', value: [0,1,2,3]}]).send({from:owner});83 await collection.methods.setCollectionProperties([{key: 'A', value: [0,1,2,3]}]).send({from:owner});
84 await helper.wait.newBlocks(1);84 await helper.wait.newBlocks(1);
85 expect(ethEvents).to.be.like([85 expect(ethEvents).to.containSubset([
86 {86 {
87 event: 'CollectionChanged',87 event: 'CollectionChanged',
88 returnValues: {88 returnValues: {
89 collectionId: collectionAddress,89 collectionId: collectionAddress,
90 },90 },
91 },91 },
92 ]);92 ]);
93 expect(subEvents).to.be.like([{method: 'CollectionPropertySet'}]);93 expect(subEvents).to.containSubset([{method: 'CollectionPropertySet'}]);
94 clearEvents(ethEvents, subEvents);94 clearEvents(ethEvents, subEvents);
95 }95 }
96 {96 {
97 await collection.methods.deleteCollectionProperties(['A']).send({from:owner});97 await collection.methods.deleteCollectionProperties(['A']).send({from:owner});
98 await helper.wait.newBlocks(1);98 await helper.wait.newBlocks(1);
99 expect(ethEvents).to.be.like([99 expect(ethEvents).to.containSubset([
100 {100 {
101 event: 'CollectionChanged',101 event: 'CollectionChanged',
102 returnValues: {102 returnValues: {
103 collectionId: collectionAddress,103 collectionId: collectionAddress,
104 },104 },
105 },105 },
106 ]);106 ]);
107 expect(subEvents).to.be.like([{method: 'CollectionPropertyDeleted'}]);107 expect(subEvents).to.containSubset([{method: 'CollectionPropertyDeleted'}]);
108 }108 }
109 unsubscribe();109 unsubscribe();
110}110}
111111
112async function testPropertyPermissionSet(helper: EthUniqueHelper, mode: TCollectionMode) {112async function testPropertyPermissionSet(helper: EthUniqueHelper, mode: TCollectionMode) {
113 const owner = await helper.eth.createAccountWithBalance(donor);113 const owner = await helper.eth.createAccountWithBalance(donor);
114 const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');114 const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');
115 const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);115 const collection = helper.ethNativeContract.collection(collectionAddress, mode, owner);
116 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);116 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
117 const ethEvents: any = [];117 const ethEvents: any = [];
118 collectionHelper.events.allEvents((_: any, event: any) => {118 collectionHelper.events.allEvents((_: any, event: any) => {
127 ],127 ],
128 ]).send({from: owner});128 ]).send({from: owner});
129 await helper.wait.newBlocks(1);129 await helper.wait.newBlocks(1);
130 expect(ethEvents).to.be.like([130 expect(ethEvents).to.containSubset([
131 {131 {
132 event: 'CollectionChanged',132 event: 'CollectionChanged',
133 returnValues: {133 returnValues: {
134 collectionId: collectionAddress,134 collectionId: collectionAddress,
135 },135 },
136 },136 },
137 ]);137 ]);
138 expect(subEvents).to.be.like([{method: 'PropertyPermissionSet'}]);138 expect(subEvents).to.containSubset([{method: 'PropertyPermissionSet'}]);
139 unsubscribe();139 unsubscribe();
140}140}
141141
142async function testAllowListAddressAddedAndRemoved(helper: EthUniqueHelper, mode: TCollectionMode) {142async function testAllowListAddressAddedAndRemoved(helper: EthUniqueHelper, mode: TCollectionMode) {
143 const owner = await helper.eth.createAccountWithBalance(donor);143 const owner = await helper.eth.createAccountWithBalance(donor);
144 const user = helper.ethCrossAccount.createAccount();144 const user = helper.ethCrossAccount.createAccount();
145 const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');145 const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');
146 const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);146 const collection = helper.ethNativeContract.collection(collectionAddress, mode, owner);
147 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);147 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
148 const ethEvents: any[] = [];148 const ethEvents: any[] = [];
149 collectionHelper.events.allEvents((_: any, event: any) => {149 collectionHelper.events.allEvents((_: any, event: any) => {
154 {154 {
155 await collection.methods.addToCollectionAllowListCross(user).send({from: owner});155 await collection.methods.addToCollectionAllowListCross(user).send({from: owner});
156 await helper.wait.newBlocks(1);156 await helper.wait.newBlocks(1);
157 expect(ethEvents).to.be.like([157 expect(ethEvents).to.containSubset([
158 {158 {
159 event: 'CollectionChanged',159 event: 'CollectionChanged',
160 returnValues: {160 returnValues: {
161 collectionId: collectionAddress,161 collectionId: collectionAddress,
162 },162 },
163 },163 },
164 ]);164 ]);
165 expect(subEvents).to.be.like([{method: 'AllowListAddressAdded'}]);165 expect(subEvents).to.containSubset([{method: 'AllowListAddressAdded'}]);
166 clearEvents(ethEvents, subEvents);166 clearEvents(ethEvents, subEvents);
167 }167 }
168 {168 {
169 await collection.methods.removeFromCollectionAllowListCross(user).send({from: owner});169 await collection.methods.removeFromCollectionAllowListCross(user).send({from: owner});
170 await helper.wait.newBlocks(1);170 await helper.wait.newBlocks(1);
171 expect(ethEvents.length).to.be.eq(1);
172 expect(ethEvents).to.be.like([171 expect(ethEvents).to.containSubset([
173 {172 {
174 event: 'CollectionChanged',173 event: 'CollectionChanged',
175 returnValues: {174 returnValues: {
176 collectionId: collectionAddress,175 collectionId: collectionAddress,
177 },176 },
178 },177 },
179 ]);178 ]);
180 expect(subEvents).to.be.like([{method: 'AllowListAddressRemoved'}]);179 expect(subEvents).to.containSubset([{method: 'AllowListAddressRemoved'}]);
181 }180 }
182 unsubscribe();181 unsubscribe();
183}182}
186 const owner = await helper.eth.createAccountWithBalance(donor);185 const owner = await helper.eth.createAccountWithBalance(donor);
187 const user = helper.ethCrossAccount.createAccount();186 const user = helper.ethCrossAccount.createAccount();
188 const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');187 const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');
189 const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);188 const collection = helper.ethNativeContract.collection(collectionAddress, mode, owner);
190 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);189 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
191 const ethEvents: any = [];190 const ethEvents: any = [];
192 collectionHelper.events.allEvents((_: any, event: any) => {191 collectionHelper.events.allEvents((_: any, event: any) => {
196 {195 {
197 await collection.methods.addCollectionAdminCross(user).send({from: owner});196 await collection.methods.addCollectionAdminCross(user).send({from: owner});
198 await helper.wait.newBlocks(1);197 await helper.wait.newBlocks(1);
199 expect(ethEvents).to.be.like([198 expect(ethEvents).to.containSubset([
200 {199 {
201 event: 'CollectionChanged',200 event: 'CollectionChanged',
202 returnValues: {201 returnValues: {
203 collectionId: collectionAddress,202 collectionId: collectionAddress,
204 },203 },
205 },204 },
206 ]);205 ]);
207 expect(subEvents).to.be.like([{method: 'CollectionAdminAdded'}]);206 expect(subEvents).to.containSubset([{method: 'CollectionAdminAdded'}]);
208 clearEvents(ethEvents, subEvents);207 clearEvents(ethEvents, subEvents);
209 }208 }
210 {209 {
211 await collection.methods.removeCollectionAdminCross(user).send({from: owner});210 await collection.methods.removeCollectionAdminCross(user).send({from: owner});
212 await helper.wait.newBlocks(1);211 await helper.wait.newBlocks(1);
213 expect(ethEvents).to.be.like([212 expect(ethEvents).to.containSubset([
214 {213 {
215 event: 'CollectionChanged',214 event: 'CollectionChanged',
216 returnValues: {215 returnValues: {
217 collectionId: collectionAddress,216 collectionId: collectionAddress,
218 },217 },
219 },218 },
220 ]);219 ]);
221 expect(subEvents).to.be.like([{method: 'CollectionAdminRemoved'}]);220 expect(subEvents).to.containSubset([{method: 'CollectionAdminRemoved'}]);
222 }221 }
223 unsubscribe();222 unsubscribe();
224}223}
225224
226async function testCollectionLimitSet(helper: EthUniqueHelper, mode: TCollectionMode) {225async function testCollectionLimitSet(helper: EthUniqueHelper, mode: TCollectionMode) {
227 const owner = await helper.eth.createAccountWithBalance(donor);226 const owner = await helper.eth.createAccountWithBalance(donor);
228 const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');227 const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');
229 const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);228 const collection = helper.ethNativeContract.collection(collectionAddress, mode, owner);
230 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);229 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
231 const ethEvents: any = [];230 const ethEvents: any = [];
232 collectionHelper.events.allEvents((_: any, event: any) => {231 collectionHelper.events.allEvents((_: any, event: any) => {
236 {235 {
237 await collection.methods.setCollectionLimit(CollectionLimits.OwnerCanTransfer, true, 0).send({from: owner});236 await collection.methods.setCollectionLimit(CollectionLimits.OwnerCanTransfer, true, 0).send({from: owner});
238 await helper.wait.newBlocks(1);237 await helper.wait.newBlocks(1);
239 expect(ethEvents).to.be.like([238 expect(ethEvents).to.containSubset([
240 {239 {
241 event: 'CollectionChanged',240 event: 'CollectionChanged',
242 returnValues: {241 returnValues: {
243 collectionId: collectionAddress,242 collectionId: collectionAddress,
244 },243 },
245 },244 },
246 ]);245 ]);
247 expect(subEvents).to.be.like([{method: 'CollectionLimitSet'}]);246 expect(subEvents).to.containSubset([{method: 'CollectionLimitSet'}]);
248 }247 }
249 unsubscribe();248 unsubscribe();
250}249}
253 const owner = await helper.eth.createAccountWithBalance(donor);252 const owner = await helper.eth.createAccountWithBalance(donor);
254 const newOwner = helper.ethCrossAccount.createAccount();253 const newOwner = helper.ethCrossAccount.createAccount();
255 const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');254 const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');
256 const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);255 const collection = helper.ethNativeContract.collection(collectionAddress, mode, owner);
257 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);256 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
258 const ethEvents: any = [];257 const ethEvents: any = [];
259 collectionHelper.events.allEvents((_: any, event: any) => {258 collectionHelper.events.allEvents((_: any, event: any) => {
263 {262 {
264 await collection.methods.changeCollectionOwnerCross(newOwner).send({from: owner});263 await collection.methods.changeCollectionOwnerCross(newOwner).send({from: owner});
265 await helper.wait.newBlocks(1);264 await helper.wait.newBlocks(1);
266 expect(ethEvents).to.be.like([265 expect(ethEvents).to.containSubset([
267 {266 {
268 event: 'CollectionChanged',267 event: 'CollectionChanged',
269 returnValues: {268 returnValues: {
270 collectionId: collectionAddress,269 collectionId: collectionAddress,
271 },270 },
272 },271 },
273 ]);272 ]);
274 expect(subEvents).to.be.like([{method: 'CollectionOwnerChanged'}]);273 expect(subEvents).to.containSubset([{method: 'CollectionOwnerChanged'}]);
275 }274 }
276 unsubscribe();275 unsubscribe();
277}276}
278277
279async function testCollectionPermissionSet(helper: EthUniqueHelper, mode: TCollectionMode) {278async function testCollectionPermissionSet(helper: EthUniqueHelper, mode: TCollectionMode) {
280 const owner = await helper.eth.createAccountWithBalance(donor);279 const owner = await helper.eth.createAccountWithBalance(donor);
281 const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');280 const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');
282 const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);281 const collection = helper.ethNativeContract.collection(collectionAddress, mode, owner);
283 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);282 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
284 const ethEvents: any = [];283 const ethEvents: any = [];
285 collectionHelper.events.allEvents((_: any, event: any) => {284 collectionHelper.events.allEvents((_: any, event: any) => {
289 {288 {
290 await collection.methods.setCollectionMintMode(true).send({from: owner});289 await collection.methods.setCollectionMintMode(true).send({from: owner});
291 await helper.wait.newBlocks(1);290 await helper.wait.newBlocks(1);
292 expect(ethEvents).to.be.like([291 expect(ethEvents).to.containSubset([
293 {292 {
294 event: 'CollectionChanged',293 event: 'CollectionChanged',
295 returnValues: {294 returnValues: {
296 collectionId: collectionAddress,295 collectionId: collectionAddress,
297 },296 },
298 },297 },
299 ]);298 ]);
300 expect(subEvents).to.be.like([{method: 'CollectionPermissionSet'}]);299 expect(subEvents).to.containSubset([{method: 'CollectionPermissionSet'}]);
301 clearEvents(ethEvents, subEvents);300 clearEvents(ethEvents, subEvents);
302 }301 }
303 {302 {
304 await collection.methods.setCollectionAccess(1).send({from: owner});303 await collection.methods.setCollectionAccess(1).send({from: owner});
305 await helper.wait.newBlocks(1);304 await helper.wait.newBlocks(1);
306 expect(ethEvents).to.be.like([305 expect(ethEvents).to.containSubset([
307 {306 {
308 event: 'CollectionChanged',307 event: 'CollectionChanged',
309 returnValues: {308 returnValues: {
310 collectionId: collectionAddress,309 collectionId: collectionAddress,
311 },310 },
312 },311 },
313 ]);312 ]);
314 expect(subEvents).to.be.like([{method: 'CollectionPermissionSet'}]);313 expect(subEvents).to.containSubset([{method: 'CollectionPermissionSet'}]);
315 }314 }
316 unsubscribe();315 unsubscribe();
317}316}
320 const owner = await helper.eth.createAccountWithBalance(donor);319 const owner = await helper.eth.createAccountWithBalance(donor);
321 const sponsor = await helper.ethCrossAccount.createAccountWithBalance(donor);320 const sponsor = await helper.ethCrossAccount.createAccountWithBalance(donor);
322 const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');321 const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');
323 const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);322 const collection = helper.ethNativeContract.collection(collectionAddress, mode, owner);
324 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);323 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
325 const ethEvents: any = [];324 const ethEvents: any = [];
326 collectionHelper.events.allEvents((_: any, event: any) => {325 collectionHelper.events.allEvents((_: any, event: any) => {
332 {331 {
333 await collection.methods.setCollectionSponsorCross(sponsor).send({from: owner});332 await collection.methods.setCollectionSponsorCross(sponsor).send({from: owner});
334 await helper.wait.newBlocks(1);333 await helper.wait.newBlocks(1);
335 expect(ethEvents).to.be.like([334 expect(ethEvents).to.containSubset([{
336 {
337 event: 'CollectionChanged',335 event: 'CollectionChanged',
338 returnValues: {336 returnValues: {
339 collectionId: collectionAddress,337 collectionId: collectionAddress,
340 },338 },
341 },339 }]);
342 ]);
343 expect(subEvents).to.be.like([{method: 'CollectionSponsorSet'}]);340 expect(subEvents).to.containSubset([{method: 'CollectionSponsorSet'}]);
344 clearEvents(ethEvents, subEvents);341 clearEvents(ethEvents, subEvents);
345 }342 }
346 {343 {
347 await collection.methods.confirmCollectionSponsorship().send({from: sponsor.eth});344 await collection.methods.confirmCollectionSponsorship().send({from: sponsor.eth});
348 await helper.wait.newBlocks(1);345 await helper.wait.newBlocks(1);
349 expect(ethEvents).to.be.like([346 expect(ethEvents).to.containSubset([
350 {347 {
351 event: 'CollectionChanged',348 event: 'CollectionChanged',
352 returnValues: {349 returnValues: {
353 collectionId: collectionAddress,350 collectionId: collectionAddress,
354 },351 },
355 },352 },
356 ]);353 ]);
357 expect(subEvents).to.be.like([{method: 'SponsorshipConfirmed'}]);354 expect(subEvents).to.containSubset([{method: 'SponsorshipConfirmed'}]);
358 clearEvents(ethEvents, subEvents);355 clearEvents(ethEvents, subEvents);
359 }356 }
360 {357 {
361 await collection.methods.removeCollectionSponsor().send({from: owner});358 await collection.methods.removeCollectionSponsor().send({from: owner});
362 await helper.wait.newBlocks(1);359 await helper.wait.newBlocks(1);
363 expect(ethEvents).to.be.like([360 expect(ethEvents).to.containSubset([
364 {361 {
365 event: 'CollectionChanged',362 event: 'CollectionChanged',
366 returnValues: {363 returnValues: {
367 collectionId: collectionAddress,364 collectionId: collectionAddress,
368 },365 },
369 },366 },
370 ]);367 ]);
371 expect(subEvents).to.be.like([{method: 'CollectionSponsorRemoved'}]);368 expect(subEvents).to.containSubset([{method: 'CollectionSponsorRemoved'}]);
372 }369 }
373 unsubscribe();370 unsubscribe();
374}371}
375372
376async function testTokenPropertySetAndDeleted(helper: EthUniqueHelper, mode: TCollectionMode) {373async function testTokenPropertySetAndDeleted(helper: EthUniqueHelper, mode: TCollectionMode) {
377 const owner = await helper.eth.createAccountWithBalance(donor);374 const owner = await helper.eth.createAccountWithBalance(donor);
378 const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');375 const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');
379 const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);376 const collection = helper.ethNativeContract.collection(collectionAddress, mode, owner);
380 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);377 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
381 const result = await collection.methods.mint(owner).send({from: owner});378 const result = await collection.methods.mint(owner).send({from: owner});
382 const tokenId = result.events.Transfer.returnValues.tokenId;379 const tokenId = result.events.Transfer.returnValues.tokenId;
397 {394 {
398 await collection.methods.setProperties(tokenId, [{key: 'A', value: [1,2,3]}]).send({from: owner});395 await collection.methods.setProperties(tokenId, [{key: 'A', value: [1,2,3]}]).send({from: owner});
399 await helper.wait.newBlocks(1);396 await helper.wait.newBlocks(1);
400 expect(ethEvents).to.be.like([397 expect(ethEvents).to.containSubset([
401 {398 {
402 event: 'TokenChanged',399 event: 'TokenChanged',
403 returnValues: {400 returnValues: {
404 collectionId: collectionAddress,401 collectionId: collectionAddress,
405 },402 },
406 },403 },
407 ]);404 ]);
408 expect(subEvents).to.be.like([{method: 'TokenPropertySet'}]);405 expect(subEvents).to.containSubset([{method: 'TokenPropertySet'}]);
409 clearEvents(ethEvents, subEvents);406 clearEvents(ethEvents, subEvents);
410 }407 }
411 {408 {
412 await collection.methods.deleteProperties(tokenId, ['A']).send({from: owner});409 await collection.methods.deleteProperties(tokenId, ['A']).send({from: owner});
413 await helper.wait.newBlocks(1);410 await helper.wait.newBlocks(1);
414 expect(ethEvents).to.be.like([411 expect(ethEvents).to.containSubset([
415 {412 {
416 event: 'TokenChanged',413 event: 'TokenChanged',
417 returnValues: {414 returnValues: {
418 collectionId: collectionAddress,415 collectionId: collectionAddress,
419 },416 },
420 },417 },
421 ]);418 ]);
422 expect(subEvents).to.be.like([{method: 'TokenPropertyDeleted'}]);419 expect(subEvents).to.containSubset([{method: 'TokenPropertyDeleted'}]);
423 }420 }
424 unsubscribe();421 unsubscribe();
425}422}
modifiedtests/src/eth/fungible.test.tsdiffbeforeafterboth
79 expect(event.returnValues.value).to.equal('100');79 expect(event.returnValues.value).to.equal('100');
80 });80 });
81 81
82 82 [
83 'substrate' as const,
84 'ethereum' as const,
85 ].map(testCase => {
83 itEth('Can perform mintCross()', async ({helper}) => {86 itEth(`Can perform mintCross() for ${testCase} address`, async ({helper}) => {
87 // 1. Create receiver depending on the test case:
88 const receiverEth = helper.eth.createAccount();
89 const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
90 const receiverSub = owner;
84 const receiverCross = helper.ethCrossAccount.fromKeyringPair(owner);91 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(owner);
92
85 const ethOwner = await helper.eth.createAccountWithBalance(donor);93 const ethOwner = await helper.eth.createAccountWithBalance(donor);
86 const collection = await helper.ft.mintCollection(alice);94 const collection = await helper.ft.mintCollection(alice);
87 await collection.addAdmin(alice, {Ethereum: ethOwner});95 await collection.addAdmin(alice, {Ethereum: ethOwner});
8896
89 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);97 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
90 const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', ethOwner);98 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', ethOwner);
9199
100 // 2. Mint tokens:
92 const result = await contract.methods.mintCross(receiverCross, 100).send();101 const result = await collectionEvm.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, 100).send();
93 102
94 const event = result.events.Transfer;103 const event = result.events.Transfer;
95 expect(event.address).to.equal(collectionAddress);104 expect(event.address).to.equal(collectionAddress);
96 expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');105 expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
97 expect(event.returnValues.to).to.equal(helper.address.substrateToEth(owner.address));106 expect(event.returnValues.to).to.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(receiverSub.address));
98 expect(event.returnValues.value).to.equal('100');107 expect(event.returnValues.value).to.equal('100');
108
109 // 3. Get balance depending on the test case:
110 let balance;
111 if (testCase === 'ethereum') balance = await collection.getBalance({Ethereum: receiverEth});
112 else if (testCase === 'substrate') balance = await collection.getBalance({Substrate: receiverSub.address});
113 // 3.1 Check balance:
114 expect(balance).to.eq(100n);
99 });115 });
116 });
100117
101 itEth('Can perform mintBulk()', async ({helper}) => {118 itEth('Can perform mintBulk()', async ({helper}) => {
102 const owner = await helper.eth.createAccountWithBalance(donor);119 const owner = await helper.eth.createAccountWithBalance(donor);
169 }186 }
170 });187 });
188
189 itEth('Can perform approveCross()', async ({helper}) => {
190 const owner = await helper.eth.createAccountWithBalance(donor);
191 const spender = helper.eth.createAccount();
192 const spenderSub = (await helper.arrange.createAccounts([1n], donor))[0];
193 const spenderCrossEth = helper.ethCrossAccount.fromAddress(spender);
194 const spenderCrossSub = helper.ethCrossAccount.fromKeyringPair(spenderSub);
195
196
197 const collection = await helper.ft.mintCollection(alice);
198 await collection.mint(alice, 200n, {Ethereum: owner});
199
200 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
201 const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
202
203 {
204 const result = await contract.methods.approveCross(spenderCrossEth, 100).send({from: owner});
205 const event = result.events.Approval;
206 expect(event.address).to.be.equal(collectionAddress);
207 expect(event.returnValues.owner).to.be.equal(owner);
208 expect(event.returnValues.spender).to.be.equal(spender);
209 expect(event.returnValues.value).to.be.equal('100');
210 }
211
212 {
213 const allowance = await contract.methods.allowance(owner, spender).call();
214 expect(+allowance).to.equal(100);
215 }
216
217
218 {
219 const result = await contract.methods.approveCross(spenderCrossSub, 100).send({from: owner});
220 const event = result.events.Approval;
221 expect(event.address).to.be.equal(collectionAddress);
222 expect(event.returnValues.owner).to.be.equal(owner);
223 expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(spenderSub.address));
224 expect(event.returnValues.value).to.be.equal('100');
225 }
226
227 {
228 const allowance = await collection.getApprovedTokens({Ethereum: owner}, {Substrate: spenderSub.address});
229 expect(allowance).to.equal(100n);
230 }
231
232 {
233 //TO-DO expect with future allowanceCross(owner, spenderCrossEth).call()
234 }
235 });
236
237 itEth('Non-owner and non admin cannot approveCross', async ({helper}) => {
238 const nonOwner = await helper.eth.createAccountWithBalance(donor);
239 const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner);
240 const owner = await helper.eth.createAccountWithBalance(donor);
241 const collection = await helper.ft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'});
242 await collection.mint(alice, 100n, {Ethereum: owner});
243
244 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
245 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
246
247 await expect(collectionEvm.methods.approveCross(nonOwnerCross, 20).call({from: nonOwner})).to.be.rejectedWith('CantApproveMoreThanOwned');
248 });
249
171250
172 itEth('Can perform burnFromCross()', async ({helper}) => {251 itEth('Can perform burnFromCross()', async ({helper}) => {
modifiedtests/src/eth/nonFungible.test.tsdiffbeforeafterboth
175 // expect(tokenUri).to.be.equal(`https://offchain-service.local/token-info/${nextTokenId}`);175 // expect(tokenUri).to.be.equal(`https://offchain-service.local/token-info/${nextTokenId}`);
176 });176 });
177 177
178 // TODO combine all minting tests in one place
179 [
180 'substrate' as const,
181 'ethereum' as const,
182 ].map(testCase => {
178 itEth('Can perform mintCross()', async ({helper}) => {183 itEth(`Can perform mintCross() for ${testCase} address`, async ({helper}) => {
179 const caller = await helper.eth.createAccountWithBalance(donor);184 const collectionAdmin = await helper.eth.createAccountWithBalance(donor);
185
186 const receiverEth = helper.eth.createAccount();
187 const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
188 const receiverSub = bob;
180 const receiverCross = helper.ethCrossAccount.fromKeyringPair(bob);189 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);
190
191 // const receiverCross = helper.ethCrossAccount.fromKeyringPair(bob);
181 const properties = Array(5).fill(0).map((_, i) => { return {key: `key_${i}`, value: Buffer.from(`value_${i}`)}; });192 const properties = Array(5).fill(0).map((_, i) => { return {key: `key_${i}`, value: Buffer.from(`value_${i}`)}; });
182 const permissions: ITokenPropertyPermission[] = properties193 const permissions: ITokenPropertyPermission[] = properties
183 .map(p => {194 .map(p => {
184 return {195 return {
185 key: p.key, permission: {196 key: p.key, permission: {
186 tokenOwner: true,197 tokenOwner: false,
187 collectionAdmin: true,198 collectionAdmin: true,
188 mutable: true,199 mutable: false,
189 },200 },
190 };201 };
191 });202 });
195 tokenPrefix: 'ethp',206 tokenPrefix: 'ethp',
196 tokenPropertyPermissions: permissions,207 tokenPropertyPermissions: permissions,
197 });208 });
198 await collection.addAdmin(minter, {Ethereum: caller});209 await collection.addAdmin(minter, {Ethereum: collectionAdmin});
199 210
200 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);211 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
201 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', caller, true);212 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', collectionAdmin, true);
202 let expectedTokenId = await contract.methods.nextTokenId().call();213 let expectedTokenId = await contract.methods.nextTokenId().call();
203 let result = await contract.methods.mintCross(receiverCross, []).send();214 let result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, []).send();
204 let tokenId = result.events.Transfer.returnValues.tokenId;215 let tokenId = result.events.Transfer.returnValues.tokenId;
205 expect(tokenId).to.be.equal(expectedTokenId);216 expect(tokenId).to.be.equal(expectedTokenId);
206217
207 let event = result.events.Transfer;218 let event = result.events.Transfer;
208 expect(event.address).to.be.equal(collectionAddress);219 expect(event.address).to.be.equal(collectionAddress);
209 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');220 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
210 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(bob.address));221 expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address));
211 expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);222 expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);
212 223
213 expectedTokenId = await contract.methods.nextTokenId().call();224 expectedTokenId = await contract.methods.nextTokenId().call();
214 result = await contract.methods.mintCross(receiverCross, properties).send();225 result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, properties).send();
215 event = result.events.Transfer;226 event = result.events.Transfer;
216 expect(event.address).to.be.equal(collectionAddress);227 expect(event.address).to.be.equal(collectionAddress);
217 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');228 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
218 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(bob.address));229 expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address));
219 expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);230 expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);
220 231
221 tokenId = result.events.Transfer.returnValues.tokenId;232 tokenId = result.events.Transfer.returnValues.tokenId;
225 expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties236 expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties
226 .map(p => { return helper.ethProperty.property(p.key, p.value.toString()); }));237 .map(p => { return helper.ethProperty.property(p.key, p.value.toString()); }));
238
239 expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId))
240 .to.deep.eq(testCase === 'ethereum' ? {Ethereum: receiverEth.toLowerCase()} : {Substrate: receiverSub.address});
227 });241 });
228 242 });
243
244 itEth('Non-owner and non admin cannot mintCross', async ({helper}) => {
245 const nonOwner = await helper.eth.createAccountWithBalance(donor);
246 const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner);
247
248 const collection = await helper.nft.mintCollection(minter);
249 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
250 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft');
251
252 await expect(collectionEvm.methods.mintCross(nonOwnerCross, []).call({from: nonOwner}))
253 .to.be.rejectedWith('PublicMintingNotAllowed');
254 });
255
229 //TODO: CORE-302 add eth methods256 //TODO: CORE-302 add eth methods
230 itEth.skip('Can perform mintBulk()', async ({helper}) => {257 itEth.skip('Can perform mintBulk()', async ({helper}) => {
376 });403 });
377 }404 }
405
406 expect(await helper.nft.doesTokenExist(collection.collectionId, token.tokenId)).to.be.false;
378 });407 });
379 408
380 itEth('Can perform transfer with ApprovalForAll', async ({helper}) => {409 itEth('Can perform transfer with ApprovalForAll', async ({helper}) => {
455 expect(await token2.doesExist()).to.be.false;484 expect(await token2.doesExist()).to.be.false;
456 });485 });
457486
487 // TODO combine all approve tests in one place
458 itEth('Can perform approveCross()', async ({helper}) => {488 itEth('Can perform approveCross()', async ({helper}) => {
459 // arrange: create accounts489 // arrange: create accounts
460 const owner = await helper.eth.createAccountWithBalance(donor, 100n);490 const owner = await helper.eth.createAccountWithBalance(donor, 100n);
503 expect(await helper.nft.getTokenOwner(collection.collectionId, token2.tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()});533 expect(await helper.nft.getTokenOwner(collection.collectionId, token2.tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()});
504 });534 });
535
536 itEth('Non-owner and non admin cannot approveCross', async ({helper}) => {
537 const nonOwner = await helper.eth.createAccountWithBalance(donor);
538 const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner);
539 const owner = await helper.eth.createAccountWithBalance(donor);
540 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
541 const collectionEvm = helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');
542 const token = await collection.mintToken(minter, {Ethereum: owner});
543
544 await expect(collectionEvm.methods.approveCross(nonOwnerCross, token.tokenId).call({from: nonOwner})).to.be.rejectedWith('CantApproveMoreThanOwned');
545 });
505546
506 itEth('Can reaffirm approved address', async ({helper}) => {547 itEth('Can reaffirm approved address', async ({helper}) => {
507 const owner = await helper.eth.createAccountWithBalance(donor, 100n);548 const owner = await helper.eth.createAccountWithBalance(donor, 100n);
modifiedtests/src/eth/reFungible.test.tsdiffbeforeafterboth
137 expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');137 expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
138 });138 });
139 139
140 [
141 'substrate' as const,
142 'ethereum' as const,
143 ].map(testCase => {
140 itEth('Can perform mintCross()', async ({helper}) => {144 itEth(`Can perform mintCross() for ${testCase} address`, async ({helper}) => {
141 const caller = await helper.eth.createAccountWithBalance(donor);145 const collectionAdmin = await helper.eth.createAccountWithBalance(donor);
146
147 const receiverEth = helper.eth.createAccount();
148 const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
149 const receiverSub = bob;
142 const receiverCross = helper.ethCrossAccount.fromKeyringPair(bob);150 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);
151
143 const properties = Array(5).fill(0).map((_, i) => { return {key: `key_${i}`, value: Buffer.from(`value_${i}`)}; });152 const properties = Array(5).fill(0).map((_, i) => { return {key: `key_${i}`, value: Buffer.from(`value_${i}`)}; });
144 const permissions: ITokenPropertyPermission[] = properties.map(p => { return {key: p.key, permission: {tokenOwner: true,153 const permissions: ITokenPropertyPermission[] = properties.map(p => { return {key: p.key, permission: {
154 tokenOwner: false,
145 collectionAdmin: true,155 collectionAdmin: true,
146 mutable: true}}; });156 mutable: false}};
147 157 });
148 158
159
149 const collection = await helper.rft.mintCollection(minter, {160 const collection = await helper.rft.mintCollection(minter, {
150 tokenPrefix: 'ethp',161 tokenPrefix: 'ethp',
151 tokenPropertyPermissions: permissions,162 tokenPropertyPermissions: permissions,
152 });163 });
153 await collection.addAdmin(minter, {Ethereum: caller});164 await collection.addAdmin(minter, {Ethereum: collectionAdmin});
154 165
155 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);166 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
156 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller, true);167 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', collectionAdmin, true);
157 let expectedTokenId = await contract.methods.nextTokenId().call();168 let expectedTokenId = await contract.methods.nextTokenId().call();
158 let result = await contract.methods.mintCross(receiverCross, []).send();169 let result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, []).send();
159 let tokenId = result.events.Transfer.returnValues.tokenId;170 let tokenId = result.events.Transfer.returnValues.tokenId;
160 expect(tokenId).to.be.equal(expectedTokenId);171 expect(tokenId).to.be.equal(expectedTokenId);
161172
162 let event = result.events.Transfer;173 let event = result.events.Transfer;
163 expect(event.address).to.be.equal(collectionAddress);174 expect(event.address).to.be.equal(collectionAddress);
164 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');175 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
165 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(bob.address));176 expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address));
166 expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);177 expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);
167 178
168 expectedTokenId = await contract.methods.nextTokenId().call();179 expectedTokenId = await contract.methods.nextTokenId().call();
169 result = await contract.methods.mintCross(receiverCross, properties).send();180 result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, properties).send();
170 event = result.events.Transfer;181 event = result.events.Transfer;
171 expect(event.address).to.be.equal(collectionAddress);182 expect(event.address).to.be.equal(collectionAddress);
172 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');183 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
173 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(bob.address));184 expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address));
174 expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);185 expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);
175 186
176 tokenId = result.events.Transfer.returnValues.tokenId;187 tokenId = result.events.Transfer.returnValues.tokenId;
180 expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties191 expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties
181 .map(p => { return helper.ethProperty.property(p.key, p.value.toString()); }));192 .map(p => { return helper.ethProperty.property(p.key, p.value.toString()); }));
193
194 expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId))
195 .to.deep.eq(testCase === 'ethereum' ? {Ethereum: receiverEth.toLowerCase()} : {Substrate: receiverSub.address});
182 });196 });
197 });
183198
184 itEth.skip('Can perform mintBulk()', async ({helper}) => {199 itEth.skip('Can perform mintBulk()', async ({helper}) => {
185 const owner = await helper.eth.createAccountWithBalance(donor);200 const owner = await helper.eth.createAccountWithBalance(donor);
modifiedtests/src/eth/reFungibleToken.test.tsdiffbeforeafterboth
208 }208 }
209 });209 });
210 210
211 itEth('Non-owner and non admin cannot approveCross', async ({helper}) => {
212 const nonOwner = await helper.eth.createAccountWithBalance(donor);
213 const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner);
214 const owner = await helper.eth.createAccountWithBalance(donor);
215 const collection = await helper.rft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'});
216 const token = await collection.mintToken(alice, 100n, {Ethereum: owner});
217
218 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);
219 const tokenEvm = helper.ethNativeContract.rftToken(tokenAddress, owner);
220
221 await expect(tokenEvm.methods.approveCross(nonOwnerCross, 20).call({from: nonOwner})).to.be.rejectedWith('CantApproveMoreThanOwned');
222 });
223
211 [224 [
212 'transferFrom',225 'transferFrom',
modifiedtests/src/eth/tokenProperties.test.tsdiffbeforeafterboth
37 {mode: 'nft' as const, requiredPallets: []},37 {mode: 'nft' as const, requiredPallets: []},
38 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},38 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
39 ].map(testCase =>39 ].map(testCase =>
40 itEth.ifWithPallets(`[${testCase.mode}] Set and get token property permissions`, testCase.requiredPallets, async({helper}) => {40 itEth.ifWithPallets(`[${testCase.mode}] Can set all possible token property permissions`, testCase.requiredPallets, async({helper}) => {
41 const owner = await helper.eth.createAccountWithBalance(donor);41 const owner = await helper.eth.createAccountWithBalance(donor);
42 const caller = await helper.ethCrossAccount.createAccountWithBalance(donor);42 const caller = await helper.ethCrossAccount.createAccountWithBalance(donor);
43 for(const [mutable,collectionAdmin, tokenOwner] of cartesian([], [false, true], [false, true], [false, true])) {43 for(const [mutable,collectionAdmin, tokenOwner] of cartesian([], [false, true], [false, true], [false, true])) {
72 {mode: 'nft' as const, requiredPallets: []},72 {mode: 'nft' as const, requiredPallets: []},
73 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},73 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
74 ].map(testCase =>74 ].map(testCase =>
75 itEth.ifWithPallets(`[${testCase.mode}] Set and get multiple token property permissions as owner`, testCase.requiredPallets, async({helper}) => {75 itEth.ifWithPallets(`[${testCase.mode}] Can set multiple token property permissions as owner`, testCase.requiredPallets, async({helper}) => {
76 const owner = await helper.eth.createAccountWithBalance(donor);76 const owner = await helper.eth.createAccountWithBalance(donor);
77 77
78 const {collectionId, collectionAddress} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');78 const {collectionId, collectionAddress} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');
79 const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);79 const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
8080
81 await collection.methods.setTokenPropertyPermissions([81 await collection.methods.setTokenPropertyPermissions([
82 ['testKey_0', [82 ['testKey_0', [
135 {mode: 'nft' as const, requiredPallets: []},134 {mode: 'nft' as const, requiredPallets: []},
136 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},135 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
137 ].map(testCase =>136 ].map(testCase =>
138 itEth.ifWithPallets(`[${testCase.mode}] Set and get multiple token property permissions as admin`, testCase.requiredPallets, async({helper}) => {137 itEth.ifWithPallets(`[${testCase.mode}] Can set multiple token property permissions as admin`, testCase.requiredPallets, async({helper}) => {
139 const owner = await helper.eth.createAccountWithBalance(donor);138 const owner = await helper.eth.createAccountWithBalance(donor);
140 const caller = await helper.ethCrossAccount.createAccountWithBalance(donor);139 const caller = await helper.ethCrossAccount.createAccountWithBalance(donor);
141 140
142 const {collectionId, collectionAddress} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');141 const {collectionId, collectionAddress} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');
143 const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);142 const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
144 await collection.methods.addCollectionAdminCross(caller).send({from: owner});143 await collection.methods.addCollectionAdminCross(caller).send({from: owner});
145144
146 await collection.methods.setTokenPropertyPermissions([145 await collection.methods.setTokenPropertyPermissions([
452 {mode: 'nft' as const, requiredPallets: []},451 {mode: 'nft' as const, requiredPallets: []},
453 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},452 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
454 ].map(testCase =>453 ].map(testCase =>
455 itEth.ifWithPallets(`[${testCase.mode}] Cant set token property permissions as non owner or admin`, testCase.requiredPallets, async({helper}) => {454 itEth.ifWithPallets(`[${testCase.mode}] Cannot set token property permissions as non owner or admin`, testCase.requiredPallets, async({helper}) => {
456 const owner = await helper.eth.createAccountWithBalance(donor);455 const owner = await helper.eth.createAccountWithBalance(donor);
457 const caller = await helper.eth.createAccountWithBalance(donor);456 const caller = await helper.eth.createAccountWithBalance(donor);
458 457
459 const {collectionAddress} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');458 const {collectionAddress} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');
460 const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);459 const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
461 460
462 await expect(collection.methods.setTokenPropertyPermissions([461 await expect(collection.methods.setTokenPropertyPermissions([
463 ['testKey_0', [462 ['testKey_0', [
472 {mode: 'nft' as const, requiredPallets: []},471 {mode: 'nft' as const, requiredPallets: []},
473 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},472 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
474 ].map(testCase =>473 ].map(testCase =>
475 itEth.ifWithPallets(`[${testCase.mode}] Cant set token property permissions with invalid character`, testCase.requiredPallets, async({helper}) => {474 itEth.ifWithPallets(`[${testCase.mode}] Cannot set token property permissions with invalid character`, testCase.requiredPallets, async({helper}) => {
476 const owner = await helper.eth.createAccountWithBalance(donor);475 const owner = await helper.eth.createAccountWithBalance(donor);
477 476
478 const {collectionAddress} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');477 const {collectionAddress} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');
479 const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);478 const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
480 479
481 await expect(collection.methods.setTokenPropertyPermissions([480 await expect(collection.methods.setTokenPropertyPermissions([
482 // "Space" is invalid character481 // "Space" is invalid character
488 ]).call({from: owner})).to.be.rejectedWith('InvalidCharacterInPropertyKey'); 487 ]).call({from: owner})).to.be.rejectedWith('InvalidCharacterInPropertyKey');
489 }));488 }));
490 489
490 [
491 {mode: 'nft' as const, requiredPallets: []},
492 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
493 ].map(testCase =>
494 itEth.ifWithPallets(`[${testCase.mode}] Can reconfigure token property permissions to stricter ones`, testCase.requiredPallets, async({helper}) => {
495 const owner = await helper.eth.createAccountWithBalance(donor);
496
497 const {collectionAddress, collectionId} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');
498 const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
499
500 // 1. Owner sets strict property-permissions:
501 await collection.methods.setTokenPropertyPermissions([
502 ['testKey', [
503 [EthTokenPermissions.Mutable, true],
504 [EthTokenPermissions.TokenOwner, true],
505 [EthTokenPermissions.CollectionAdmin, true]],
506 ],
507 ]).send({from: owner});
508
509 // 2. Owner can set stricter property-permissions:
510 for(const values of [[true, true, false], [true, false, false], [false, false, false]]) {
511 await collection.methods.setTokenPropertyPermissions([
512 ['testKey', [
513 [EthTokenPermissions.Mutable, values[0]],
514 [EthTokenPermissions.TokenOwner, values[1]],
515 [EthTokenPermissions.CollectionAdmin, values[2]]],
516 ],
517 ]).send({from: owner});
518 }
519
520 expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([{
521 key: 'testKey',
522 permission: {mutable: false, collectionAdmin: false, tokenOwner: false},
523 }]);
524 }));
525
526 [
527 {mode: 'nft' as const, requiredPallets: []},
528 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
529 ].map(testCase =>
530 itEth.ifWithPallets(`[${testCase.mode}] Cannot reconfigure token property permissions to less strict ones`, testCase.requiredPallets, async({helper}) => {
531 const owner = await helper.eth.createAccountWithBalance(donor);
532
533 const {collectionAddress} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');
534 const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
535
536 // 1. Owner sets strict property-permissions:
537 await collection.methods.setTokenPropertyPermissions([
538 ['testKey', [
539 [EthTokenPermissions.Mutable, false],
540 [EthTokenPermissions.TokenOwner, false],
541 [EthTokenPermissions.CollectionAdmin, false]],
542 ],
543 ]).send({from: owner});
544
545 // 2. Owner cannot set less strict property-permissions:
546 for(const values of [[true, false, false], [false, true, false], [false, false, true]]) {
547 await expect(collection.methods.setTokenPropertyPermissions([
548 ['testKey', [
549 [EthTokenPermissions.Mutable, values[0]],
550 [EthTokenPermissions.TokenOwner, values[1]],
551 [EthTokenPermissions.CollectionAdmin, values[2]]],
552 ],
553 ]).call({from: owner})).to.be.rejectedWith('NoPermission');
554 }
555 }));
491});556});
492557
493558
modifiedtests/src/util/index.tsdiffbeforeafterboth
6import {IKeyringPair} from '@polkadot/types/types/interfaces';6import {IKeyringPair} from '@polkadot/types/types/interfaces';
7import chai from 'chai';7import chai from 'chai';
8import chaiAsPromised from 'chai-as-promised';8import chaiAsPromised from 'chai-as-promised';
9import chaiSubset from 'chai-subset';
9import {Context} from 'mocha';10import {Context} from 'mocha';
10import config from '../config';11import config from '../config';
11import {ChainHelperBase} from './playgrounds/unique';12import {ChainHelperBase} from './playgrounds/unique';
12import {ILogger} from './playgrounds/types';13import {ILogger} from './playgrounds/types';
13import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper} from './playgrounds/unique.dev';14import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper} from './playgrounds/unique.dev';
1415
15chai.use(chaiAsPromised);16chai.use(chaiAsPromised);
17chai.use(chaiSubset);
16export const expect = chai.expect;18export const expect = chai.expect;
1719
18const getTestHash = (filename: string) => {20const getTestHash = (filename: string) => {
modifiedtests/src/vesting.test.tsdiffbeforeafterboth
32 // arrange32 // arrange
33 const [sender, recepient] = await helper.arrange.createAccounts([1000n, 1n], donor);33 const [sender, recepient] = await helper.arrange.createAccounts([1000n, 1n], donor);
34 const currentRelayBlock = await helper.chain.getRelayBlockNumber();34 const currentRelayBlock = await helper.chain.getRelayBlockNumber();
35 const SCHEDULE_1_PERIOD = 4n; // 6 blocks one period
36 const SCHEDULE_1_START = currentRelayBlock + 6n; // Block when 1 schedule starts
37 const SCHEDULE_2_PERIOD = 8n; // 12 blocks one period
38 const SCHEDULE_2_START = currentRelayBlock + 12n; // Block when 2 schedule starts
35 const schedule1 = {start: currentRelayBlock + 4n, period: 4n, periodCount: 2n, perPeriod: 50n * nominal};39 const schedule1 = {start: SCHEDULE_1_START, period: SCHEDULE_1_PERIOD, periodCount: 2n, perPeriod: 50n * nominal};
36 const schedule2 = {start: currentRelayBlock + 8n, period: 8n, periodCount: 2n, perPeriod: 100n * nominal};40 const schedule2 = {start: SCHEDULE_2_START, period: SCHEDULE_2_PERIOD, periodCount: 2n, perPeriod: 100n * nominal};
3741
38 // act42 // act
39 await helper.balance.vestedTransfer(sender, recepient.address, schedule1);43 await helper.balance.vestedTransfer(sender, recepient.address, schedule1);
59 expect(schedule[0]).to.deep.eq(schedule1);63 expect(schedule[0]).to.deep.eq(schedule1);
60 expect(schedule[1]).to.deep.eq(schedule2);64 expect(schedule[1]).to.deep.eq(schedule2);
6165
66 // Wait first part available:
62 await helper.wait.forRelayBlockNumber(currentRelayBlock + 8n);67 await helper.wait.forRelayBlockNumber(SCHEDULE_1_START + SCHEDULE_1_PERIOD);
63 await helper.balance.claim(recepient);68 await helper.balance.claim(recepient);
6469
65 // check recepient balance after claim (50 tokens claimed):70 // check recepient balance after claim (50 tokens claimed, 250 left):
66 balanceRecepient = await helper.balance.getSubstrateFull(recepient.address);71 balanceRecepient = await helper.balance.getSubstrateFull(recepient.address);
67 expect(balanceRecepient.free / nominal).to.eq(300n);72 expect(balanceRecepient.free / nominal).to.eq(300n);
68 expect(balanceRecepient.feeFrozen).to.eq(250n * nominal);73 expect(balanceRecepient.feeFrozen).to.eq(250n * nominal);
69 expect(balanceRecepient.miscFrozen).to.eq(250n * nominal);74 expect(balanceRecepient.miscFrozen).to.eq(250n * nominal);
70 expect(balanceRecepient.reserved).to.eq(0n);75 expect(balanceRecepient.reserved).to.eq(0n);
71 76
77 // Wait first schedule ends and first part od second schedule:
72 await helper.wait.forRelayBlockNumber(currentRelayBlock + 16n);78 await helper.wait.forRelayBlockNumber(SCHEDULE_2_START + SCHEDULE_2_PERIOD);
73 await helper.balance.claim(recepient);79 await helper.balance.claim(recepient);
7480
75 // check recepient balance after second claim (150 tokens claimed):81 // check recepient balance after second claim (150 tokens claimed, 100 left):
76 balanceRecepient = await helper.balance.getSubstrateFull(recepient.address);82 balanceRecepient = await helper.balance.getSubstrateFull(recepient.address);
77 expect(balanceRecepient.free / nominal).to.eq(300n);83 expect(balanceRecepient.free / nominal).to.eq(300n);
78 expect(balanceRecepient.feeFrozen).to.eq(100n * nominal);84 expect(balanceRecepient.feeFrozen).to.eq(100n * nominal);
84 expect(schedule).to.has.length(1);90 expect(schedule).to.has.length(1);
85 expect(schedule[0]).to.deep.eq(schedule2);91 expect(schedule[0]).to.deep.eq(schedule2);
8692
93 // Wait 2 schedule ends:
87 await helper.wait.forRelayBlockNumber(currentRelayBlock + 24n);94 await helper.wait.forRelayBlockNumber(SCHEDULE_2_START + SCHEDULE_2_PERIOD * 2n);
88 await helper.balance.claim(recepient);95 await helper.balance.claim(recepient);
8996
90 // check recepient balance after second claim (100 tokens claimed):97 // check recepient balance after second claim (100 tokens claimed, 0 left):
91 balanceRecepient = await helper.balance.getSubstrateFull(recepient.address);98 balanceRecepient = await helper.balance.getSubstrateFull(recepient.address);
92 expect(balanceRecepient.free / nominal).to.eq(300n);99 expect(balanceRecepient.free / nominal).to.eq(300n);
93 expect(balanceRecepient.feeFrozen).to.eq(0n);100 expect(balanceRecepient.feeFrozen).to.eq(0n);
modifiedtests/yarn.lockdiffbeforeafterboth
979 dependencies:979 dependencies:
980 "@types/chai" "*"980 "@types/chai" "*"
981981
982"@types/chai-subset@^1.3.3":
983 version "1.3.3"
984 resolved "https://registry.yarnpkg.com/@types/chai-subset/-/chai-subset-1.3.3.tgz#97893814e92abd2c534de422cb377e0e0bdaac94"
985 integrity sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==
986 dependencies:
987 "@types/chai" "*"
988
982"@types/chai@*", "@types/chai@^4.3.3":989"@types/chai@*", "@types/chai@^4.3.3":
983 version "4.3.4"990 version "4.3.4"
984 resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.4.tgz#e913e8175db8307d78b4e8fa690408ba6b65dee4"991 resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.4.tgz#e913e8175db8307d78b4e8fa690408ba6b65dee4"
1568 version "1.1.1"1575 version "1.1.1"
1569 resolved "https://registry.yarnpkg.com/chai-like/-/chai-like-1.1.1.tgz#8c558a414c34514e814d497c772547ceb7958f64"1576 resolved "https://registry.yarnpkg.com/chai-like/-/chai-like-1.1.1.tgz#8c558a414c34514e814d497c772547ceb7958f64"
1570 integrity sha512-VKa9z/SnhXhkT1zIjtPACFWSoWsqVoaz1Vg+ecrKo5DCKVlgL30F/pEyEvXPBOVwCgLZcWUleCM/C1okaKdTTA==1577 integrity sha512-VKa9z/SnhXhkT1zIjtPACFWSoWsqVoaz1Vg+ecrKo5DCKVlgL30F/pEyEvXPBOVwCgLZcWUleCM/C1okaKdTTA==
1578
1579chai-subset@^1.6.0:
1580 version "1.6.0"
1581 resolved "https://registry.yarnpkg.com/chai-subset/-/chai-subset-1.6.0.tgz#a5d0ca14e329a79596ed70058b6646bd6988cfe9"
1582 integrity sha512-K3d+KmqdS5XKW5DWPd5sgNffL3uxdDe+6GdnJh3AYPhwnBGRY5urfvfcbRtWIvvpz+KxkL9FeBB6MZewLUNwug==
15711583
1572chai@^4.3.6:1584chai@^4.3.6:
1573 version "4.3.7"1585 version "4.3.7"