difftreelog
Merge pull request #799 from UniqueNetwork/tests/eth-helpers
in: master
14 files changed
.envdiffbeforeafterboth--- a/.env
+++ b/.env
@@ -3,21 +3,21 @@
POLKADOT_MAINNET_BRANCH=release-v0.9.30
STATEMINT_BUILD_BRANCH=release-parachains-v9320
-ACALA_BUILD_BRANCH=2.10.1
+ACALA_BUILD_BRANCH=2.11.0
MOONBEAM_BUILD_BRANCH=runtime-1901
-UNIQUE_MAINNET_BRANCH=v930033
+UNIQUE_MAINNET_BRANCH=release-v930033
UNIQUE_REPLICA_FROM=wss://eu-ws.unique.network:443
-KUSAMA_MAINNET_BRANCH=release-v0.9.34
-STATEMINE_BUILD_BRANCH=release-parachains-v9320
-KARURA_BUILD_BRANCH=release-karura-2.10.0
-MOONRIVER_BUILD_BRANCH=runtime-1901
-QUARTZ_MAINNET_BRANCH=v930033
+KUSAMA_MAINNET_BRANCH=release-v0.9.35
+STATEMINE_BUILD_BRANCH=release-parachains-v9330
+KARURA_BUILD_BRANCH=release-karura-2.11.0
+MOONRIVER_BUILD_BRANCH=runtime-2000
+QUARTZ_MAINNET_BRANCH=release-v930034
QUARTZ_REPLICA_FROM=wss://eu-ws-quartz.unique.network:443
UNQWND_MAINNET_BRANCH=release-v0.9.30
WESTMINT_BUILD_BRANCH=parachains-v9330
-OPAL_MAINNET_BRANCH=v930032
+OPAL_MAINNET_BRANCH=release-v930034
OPAL_REPLICA_FROM=wss://eu-ws-opal.unique.network:443
POLKADOT_LAUNCH_BRANCH=unique-network
tests/package.jsondiffbeforeafterboth--- a/tests/package.json
+++ b/tests/package.json
@@ -8,11 +8,13 @@
"@types/chai": "^4.3.3",
"@types/chai-as-promised": "^7.1.5",
"@types/chai-like": "^1.1.1",
+ "@types/chai-subset": "^1.3.3",
"@types/mocha": "^10.0.0",
"@types/node": "^18.11.2",
"@typescript-eslint/eslint-plugin": "^5.40.1",
"@typescript-eslint/parser": "^5.40.1",
"chai": "^4.3.6",
+ "chai-subset": "^1.6.0",
"eslint": "^8.25.0",
"eslint-plugin-mocha": "^10.1.0",
"mocha": "^10.1.0",
tests/src/eth/collectionAdmin.test.tsdiffbeforeafterboth--- a/tests/src/eth/collectionAdmin.test.ts
+++ b/tests/src/eth/collectionAdmin.test.ts
@@ -15,6 +15,7 @@
import {IKeyringPair} from '@polkadot/types/types';
import {expect} from 'chai';
+import {Pallets} from '../util';
import {IEthCrossAccountId} from '../util/playgrounds/types';
import {usingEthPlaygrounds, itEth} from './util';
import {EthUniqueHelper} from './util/playgrounds/unique.dev';
@@ -39,36 +40,52 @@
});
});
- itEth('can add account admin by owner', async ({helper, privateKey}) => {
- // arrange
- const owner = await helper.eth.createAccountWithBalance(donor);
- const adminSub = await privateKey('//admin2');
- const adminEth = helper.eth.createAccount().toLowerCase();
-
- const adminDeprecated = helper.eth.createAccount().toLowerCase();
- const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);
- const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);
-
- const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
- const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
-
- // Soft-deprecated: can addCollectionAdmin
- await collectionEvm.methods.addCollectionAdmin(adminDeprecated).send();
- // Can addCollectionAdminCross for substrate and ethereum address
- await collectionEvm.methods.addCollectionAdminCross(adminCrossSub).send();
- await collectionEvm.methods.addCollectionAdminCross(adminCrossEth).send();
+ [
+ {mode: 'nft' as const, requiredPallets: []},
+ {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+ {mode: 'ft' as const, requiredPallets: []},
+ ].map(testCase => {
+ itEth.ifWithPallets(`can add account admin by owner for ${testCase.mode}`, testCase.requiredPallets, async ({helper, privateKey}) => {
+ // arrange
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const adminSub = await privateKey('//admin2');
+ const adminEth = helper.eth.createAccount().toLowerCase();
+
+ const adminDeprecated = helper.eth.createAccount().toLowerCase();
+ const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);
+ const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);
+
+ const {collectionAddress, collectionId} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');
+ const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, true);
- // 1. Expect api.rpc.unique.adminlist returns admins:
- const adminListRpc = await helper.collection.getAdmins(collectionId);
- expect(adminListRpc).to.has.length(3);
- expect(adminListRpc).to.be.deep.contain.members([{Substrate: adminSub.address}, {Ethereum: adminEth}, {Ethereum: adminDeprecated}]);
+ // Check isOwnerOrAdminCross returns false:
+ expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossSub).call()).to.be.false;
+ expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossEth).call()).to.be.false;
+ expect(await collectionEvm.methods.isOwnerOrAdminCross(helper.ethCrossAccount.fromAddress(adminDeprecated)).call()).to.be.false;
+
+ // Soft-deprecated: can addCollectionAdmin
+ await collectionEvm.methods.addCollectionAdmin(adminDeprecated).send();
+ // Can addCollectionAdminCross for substrate and ethereum address
+ await collectionEvm.methods.addCollectionAdminCross(adminCrossSub).send();
+ await collectionEvm.methods.addCollectionAdminCross(adminCrossEth).send();
+
+ // 1. Expect api.rpc.unique.adminlist returns admins:
+ const adminListRpc = await helper.collection.getAdmins(collectionId);
+ expect(adminListRpc).to.has.length(3);
+ expect(adminListRpc).to.be.deep.contain.members([{Substrate: adminSub.address}, {Ethereum: adminEth}, {Ethereum: adminDeprecated}]);
+
+ // 2. Expect methods.collectionAdmins == api.rpc.unique.adminlist
+ let adminListEth = await collectionEvm.methods.collectionAdmins().call();
+ adminListEth = adminListEth.map((element: IEthCrossAccountId) => {
+ return helper.address.convertCrossAccountFromEthCrossAccount(element);
+ });
+ expect(adminListRpc).to.be.like(adminListEth);
- // 2. Expect methods.collectionAdmins == api.rpc.unique.adminlist
- let adminListEth = await collectionEvm.methods.collectionAdmins().call();
- adminListEth = adminListEth.map((element: IEthCrossAccountId) => {
- return helper.address.convertCrossAccountFromEthCrossAccount(element);
+ // 3. check isOwnerOrAdminCross returns true:
+ expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossSub).call()).to.be.true;
+ expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossEth).call()).to.be.true;
+ expect(await collectionEvm.methods.isOwnerOrAdminCross(helper.ethCrossAccount.fromAddress(adminDeprecated)).call()).to.be.true;
});
- expect(adminListRpc).to.be.like(adminListEth);
});
itEth('cross account admin can mint', async ({helper}) => {
tests/src/eth/collectionProperties.test.tsdiffbeforeafterboth--- a/tests/src/eth/collectionProperties.test.ts
+++ b/tests/src/eth/collectionProperties.test.ts
@@ -26,27 +26,36 @@
before(async function() {
await usingEthPlaygrounds(async (_helper, privateKey) => {
donor = await privateKey({filename: __filename});
- [alice] = await _helper.arrange.createAccounts([20n], donor);
+ [alice] = await _helper.arrange.createAccounts([50n], donor);
});
});
// Soft-deprecated: setCollectionProperty
[
- {method: 'setCollectionProperties', methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},
- {method: 'setCollectionProperty', methodParams: ['testKey', Buffer.from('testValue')], expectedProps: [{key: 'testKey', value: 'testValue'}]},
+ {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'}]},
+ {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'}]},
+ {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'}]},
+ {method: 'setCollectionProperty', mode: 'nft' as const, methodParams: ['testKey', Buffer.from('testValue')], expectedProps: [{key: 'testKey', value: 'testValue'}]},
].map(testCase =>
- itEth(`Collection properties can be set: ${testCase.method}`, async({helper}) => {
+ itEth.ifWithPallets(`Collection properties can be set: ${testCase.method}() for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => {
const caller = await helper.eth.createAccountWithBalance(donor);
- const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties: []});
+ const collection = await helper[testCase.mode].mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties: []});
await collection.addAdmin(alice, {Ethereum: caller});
-
+
const address = helper.ethAddress.fromCollectionId(collection.collectionId);
- const contract = helper.ethNativeContract.collection(address, 'nft', caller, testCase.method === 'setCollectionProperty');
+ const collectionEvm = helper.ethNativeContract.collection(address, 'nft', caller, testCase.method === 'setCollectionProperty');
- await contract.methods[testCase.method](...testCase.methodParams).send({from: caller});
+ // collectionProperties returns an empty array if no properties:
+ expect(await collectionEvm.methods.collectionProperties([]).call()).to.be.like([]);
+ expect(await collectionEvm.methods.collectionProperties(['NonExistingKey']).call()).to.be.like([]);
+
+ await collectionEvm.methods[testCase.method](...testCase.methodParams).send({from: caller});
const raw = (await collection.getData())?.raw;
expect(raw.properties).to.deep.equal(testCase.expectedProps);
+
+ // collectionProperties returns properties:
+ expect(await collectionEvm.methods.collectionProperties([]).call()).to.be.like(testCase.expectedProps.map(prop => helper.ethProperty.property(prop.key, prop.value)));
}));
itEth('Cannot set invalid properties', async({helper}) => {
@@ -68,16 +77,18 @@
// Soft-deprecated: deleteCollectionProperty
[
- {method: 'deleteCollectionProperties', methodParams: [['testKey1', 'testKey2']], expectedProps: [{key: 'testKey3', value: 'testValue3'}]},
- {method: 'deleteCollectionProperty', methodParams: ['testKey1'], expectedProps: [{key: 'testKey2', value: 'testValue2'}, {key: 'testKey3', value: 'testValue3'}]},
+ {method: 'deleteCollectionProperties', mode: 'nft' as const, methodParams: [['testKey1', 'testKey2']], expectedProps: [{key: 'testKey3', value: 'testValue3'}]},
+ {method: 'deleteCollectionProperties', mode: 'rft' as const, methodParams: [['testKey1', 'testKey2']], expectedProps: [{key: 'testKey3', value: 'testValue3'}]},
+ {method: 'deleteCollectionProperties', mode: 'ft' as const, methodParams: [['testKey1', 'testKey2']], expectedProps: [{key: 'testKey3', value: 'testValue3'}]},
+ {method: 'deleteCollectionProperty', mode: 'nft' as const, methodParams: ['testKey1'], expectedProps: [{key: 'testKey2', value: 'testValue2'}, {key: 'testKey3', value: 'testValue3'}]},
].map(testCase =>
- itEth(`Collection properties can be deleted: ${testCase.method}()`, async({helper}) => {
+ itEth.ifWithPallets(`Collection properties can be deleted: ${testCase.method}() for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => {
const properties = [
{key: 'testKey1', value: 'testValue1'},
{key: 'testKey2', value: 'testValue2'},
{key: 'testKey3', value: 'testValue3'}];
const caller = await helper.eth.createAccountWithBalance(donor);
- const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties});
+ const collection = await helper[testCase.mode].mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties});
await collection.addAdmin(alice, {Ethereum: caller});
@@ -92,7 +103,6 @@
expect(raw.properties).to.deep.equal(testCase.expectedProps);
}));
-
[
{method: 'deleteCollectionProperties', methodParams: [['testKey2']]},
{method: 'deleteCollectionProperty', methodParams: ['testKey2']},
@@ -207,82 +217,5 @@
await contract.methods.setProperties(tokenId2, [{key: 'URISuffix', value: Buffer.from(SUFFIX)}]).send();
expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(BASE_URI + SUFFIX);
- }));
-});
-
-describe('EVM collection property', () => {
- let donor: IKeyringPair;
-
- before(async function() {
- await usingEthPlaygrounds(async (_helper, privateKey) => {
- donor = await privateKey({filename: __filename});
- });
- });
-
- [
- {case: 'nft' as const},
- {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
- {case: 'ft' as const},
- ].map(testCase =>
- itEth.ifWithPallets(`can set/read properties ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => {
- const collection = await helper[testCase.case].mintCollection(donor, {name: 'A', description: 'B', tokenPrefix: 'C'});
-
- const sender = await helper.eth.createAccountWithBalance(donor, 100n);
- await collection.addAdmin(donor, {Ethereum: sender});
-
- const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
- const contract = helper.ethNativeContract.collection(collectionAddress, testCase.case, sender);
-
- const keys = ['key0', 'key1'];
-
- const writeProperties = [
- helper.ethProperty.property(keys[0], 'value0'),
- helper.ethProperty.property(keys[1], 'value1'),
- ];
-
- await contract.methods.setCollectionProperties(writeProperties).send();
- const readProperties = await contract.methods.collectionProperties([keys[0], keys[1]]).call();
- expect(readProperties).to.be.like(writeProperties);
- }));
-
- [
- {case: 'nft' as const},
- {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
- {case: 'ft' as const},
- ].map(testCase =>
- itEth.ifWithPallets(`can delete properties ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => {
- const collection = await helper[testCase.case].mintCollection(donor, {name: 'A', description: 'B', tokenPrefix: 'C'});
-
- const sender = await helper.eth.createAccountWithBalance(donor, 100n);
- await collection.addAdmin(donor, {Ethereum: sender});
-
- const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
- const contract = helper.ethNativeContract.collection(collectionAddress, testCase.case, sender);
-
- const keys = ['key0', 'key1', 'key2', 'key3'];
-
- {
- const writeProperties = [
- helper.ethProperty.property(keys[0], 'value0'),
- helper.ethProperty.property(keys[1], 'value1'),
- helper.ethProperty.property(keys[2], 'value2'),
- helper.ethProperty.property(keys[3], 'value3'),
- ];
-
- await contract.methods.setCollectionProperties(writeProperties).send();
- const readProperties = await contract.methods.collectionProperties([keys[0], keys[1], keys[2], keys[3]]).call();
- expect(readProperties).to.be.like(writeProperties);
- }
-
- {
- const expectProperties = [
- helper.ethProperty.property(keys[0], 'value0'),
- helper.ethProperty.property(keys[1], 'value1'),
- ];
-
- await contract.methods.deleteCollectionProperties([keys[2], keys[3]]).send();
- const readProperties = await contract.methods.collectionProperties([]).call();
- expect(readProperties).to.be.like(expectProperties);
- }
}));
});
tests/src/eth/contractSponsoring.test.tsdiffbeforeafterboth--- a/tests/src/eth/contractSponsoring.test.ts
+++ b/tests/src/eth/contractSponsoring.test.ts
@@ -22,29 +22,31 @@
describe('Sponsoring EVM contracts', () => {
let donor: IKeyringPair;
+ let nominal: bigint;
before(async () => {
- await usingPlaygrounds(async (_helper, privateKey) => {
+ await usingPlaygrounds(async (helper, privateKey) => {
donor = await privateKey({filename: __filename});
+ nominal = helper.balance.getOneTokenNominal();
});
});
- itEth('Self sponsored can be set by the address that deployed the contract', async ({helper}) => {
+ itEth('Self sponsoring can be set by the address that deployed the contract', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const flipper = await helper.eth.deployFlipper(owner);
const helpers = helper.ethNativeContract.contractHelpers(owner);
+ // 1. owner can set selfSponsoring:
expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
- await expect(helpers.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.not.rejected;
+ const result = await helpers.methods.selfSponsoredEnable(flipper.options.address).send({from: owner});
expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
- });
- itEth('Set self sponsored events', async ({helper}) => {
- const owner = await helper.eth.createAccountWithBalance(donor);
- const flipper = await helper.eth.deployFlipper(owner);
- const helpers = helper.ethNativeContract.contractHelpers(owner);
-
- const result = await helpers.methods.selfSponsoredEnable(flipper.options.address).send();
+ // 1.1 Can get sponsor using methods.sponsor:
+ const actualSponsor = await helpers.methods.sponsor(flipper.options.address).call();
+ expect(actualSponsor.eth).to.eq(flipper.options.address);
+ expect(actualSponsor.sub).to.eq('0');
+
+ // 2. Events should be:
const ethEvents = helper.eth.helper.eth.normalizeEvents(result.events);
expect(ethEvents).to.be.deep.equal([
{
@@ -66,7 +68,7 @@
]);
});
- itEth('Self sponsored can not be set by the address that did not deployed the contract', async ({helper}) => {
+ itEth('Self sponsoring cannot be set by the address that did not deployed the contract', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const notOwner = await helper.eth.createAccountWithBalance(donor);
const helpers = helper.ethNativeContract.contractHelpers(owner);
@@ -83,7 +85,7 @@
const flipper = await helper.eth.deployFlipper(owner);
expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
- await expect(helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner})).to.be.not.rejected;
+ await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;
});
@@ -104,18 +106,12 @@
const helpers = helper.ethNativeContract.contractHelpers(owner);
const flipper = await helper.eth.deployFlipper(owner);
+ // 1. owner can set a sponsor:
expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;
- await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).send()).to.be.not.rejected;
- expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.true;
- });
-
- itEth('Set sponsor event', async ({helper}) => {
- const owner = await helper.eth.createAccountWithBalance(donor);
- const sponsor = await helper.eth.createAccountWithBalance(donor);
- const helpers = helper.ethNativeContract.contractHelpers(owner);
- const flipper = await helper.eth.deployFlipper(owner);
-
const result = await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
+ expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.true;
+
+ // 2. Events should be:
const events = helper.eth.normalizeEvents(result.events);
expect(events).to.be.deep.equal([
{
@@ -129,7 +125,7 @@
]);
});
- itEth('Sponsor can not be set by the address that did not deployed the contract', async ({helper}) => {
+ itEth('Sponsor cannot be set by the address that did not deployed the contract', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const sponsor = await helper.eth.createAccountWithBalance(donor);
const notOwner = await helper.eth.createAccountWithBalance(donor);
@@ -148,19 +144,18 @@
const flipper = await helper.eth.deployFlipper(owner);
expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
- await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).send()).to.be.not.rejected;
- await expect(helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor})).to.be.not.rejected;
+ await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
+
+ // 1. sponsor can confirm sponsorship:
+ const result = await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
- });
- itEth('Confirm sponsorship event', async ({helper}) => {
- const owner = await helper.eth.createAccountWithBalance(donor);
- const sponsor = await helper.eth.createAccountWithBalance(donor);
- const helpers = helper.ethNativeContract.contractHelpers(owner);
- const flipper = await helper.eth.deployFlipper(owner);
+ // 1.1 Can get sponsor using methods.sponsor:
+ const actualSponsor = await helpers.methods.sponsor(flipper.options.address).call();
+ expect(actualSponsor.eth).to.eq(sponsor);
+ expect(actualSponsor.sub).to.eq('0');
- await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).send()).to.be.not.rejected;
- const result = await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
+ // 2. Events should be:
const events = helper.eth.normalizeEvents(result.events);
expect(events).to.be.deep.equal([
{
@@ -196,34 +191,6 @@
expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
await expect(helpers.methods.confirmSponsorship(flipper.options.address).call({from: notSponsor})).to.be.rejectedWith('NoPendingSponsor');
expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
- });
-
- itEth('Get self sponsored sponsor', async ({helper}) => {
- const owner = await helper.eth.createAccountWithBalance(donor);
- const helpers = helper.ethNativeContract.contractHelpers(owner);
- const flipper = await helper.eth.deployFlipper(owner);
-
- await helpers.methods.selfSponsoredEnable(flipper.options.address).send();
-
- const result = await helpers.methods.sponsor(flipper.options.address).call();
-
- expect(result[0]).to.be.eq(flipper.options.address);
- expect(result[1]).to.be.eq('0');
- });
-
- itEth('Get confirmed sponsor', async ({helper}) => {
- const owner = await helper.eth.createAccountWithBalance(donor);
- const sponsor = await helper.eth.createAccountWithBalance(donor);
- const helpers = helper.ethNativeContract.contractHelpers(owner);
- const flipper = await helper.eth.deployFlipper(owner);
-
- await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
- await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
-
- const result = await helpers.methods.sponsor(flipper.options.address).call();
-
- expect(result[0]).to.be.eq(sponsor);
- expect(result[1]).to.be.eq('0');
});
itEth('Sponsor can be removed by the address that deployed the contract', async ({helper}) => {
@@ -236,21 +203,11 @@
await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
-
- await helpers.methods.removeSponsor(flipper.options.address).send();
+ // 1. Can remove sponsor:
+ const result = await helpers.methods.removeSponsor(flipper.options.address).send();
expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
- });
- itEth('Remove sponsor event', async ({helper}) => {
- const owner = await helper.eth.createAccountWithBalance(donor);
- const sponsor = await helper.eth.createAccountWithBalance(donor);
- const helpers = helper.ethNativeContract.contractHelpers(owner);
- const flipper = await helper.eth.deployFlipper(owner);
-
- await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
- await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
-
- const result = await helpers.methods.removeSponsor(flipper.options.address).send();
+ // 2. Events should be:
const events = helper.eth.normalizeEvents(result.events);
expect(events).to.be.deep.equal([
{
@@ -261,6 +218,11 @@
},
},
]);
+
+ // TODO: why call method reverts?
+ // const actualSponsor = await helpers.methods.sponsor(flipper.options.address).call();
+ // expect(actualSponsor.eth).to.eq(sponsor);
+ // expect(actualSponsor.sub).to.eq('0');
});
itEth('Sponsor can not be removed by the address that did not deployed the contract', async ({helper}) => {
@@ -276,6 +238,7 @@
expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
await expect(helpers.methods.removeSponsor(flipper.options.address).call({from: notOwner})).to.be.rejectedWith('NoPermission');
+ await expect(helpers.methods.removeSponsor(flipper.options.address).send({from: notOwner})).to.be.rejected;
expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
});
@@ -292,15 +255,15 @@
await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner});
await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
- const sponsorBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(sponsor));
- const callerBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(caller));
+ const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+ const callerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));
await flipper.methods.flip().send({from: caller});
expect(await flipper.methods.getValue().call()).to.be.true;
// Balance should be taken from sponsor instead of caller
- const sponsorBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(sponsor));
- const callerBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(caller));
+ const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+ const callerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));
expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
expect(callerBalanceAfter).to.be.eq(callerBalanceBefore);
});
@@ -331,82 +294,67 @@
expect(callerBalanceAfter).to.be.eq(callerBalanceBefore);
});
- itEth('Sponsoring is set, an address that has no UNQ can send a transaction and it works. Sponsor balance should decrease (allowlisted)', async ({helper}) => {
- const owner = await helper.eth.createAccountWithBalance(donor);
- const sponsor = await helper.eth.createAccountWithBalance(donor);
- const caller = helper.eth.createAccount();
- const helpers = helper.ethNativeContract.contractHelpers(owner);
- const flipper = await helper.eth.deployFlipper(owner);
-
- await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});
- await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});
-
- await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
- await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
-
- await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
- await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
-
- const sponsorBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(sponsor));
- expect(sponsorBalanceBefore).to.be.not.equal('0');
-
- await flipper.methods.flip().send({from: caller});
- expect(await flipper.methods.getValue().call()).to.be.true;
-
- // Balance should be taken from flipper instead of caller
- const sponsorBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(sponsor));
- expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+ [
+ {balance: 0n, label: '0'},
+ {balance: 10n, label: '10'},
+ ].map(testCase => {
+ itEth(`Allow-listed address that has ${testCase.label} UNQ can call a contract. Sponsor balance should decrease`, async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const sponsor = await helper.eth.createAccountWithBalance(donor);
+ const caller = helper.eth.createAccount();
+ await helper.eth.transferBalanceFromSubstrate(donor, caller, testCase.balance);
+ const helpers = helper.ethNativeContract.contractHelpers(owner);
+ const flipper = await helper.eth.deployFlipper(owner);
+
+ await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});
+ await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});
+
+ await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
+ await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
+
+ await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
+ await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
+
+ const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+ expect(sponsorBalanceBefore > 0n).to.be.true;
+
+ await flipper.methods.flip().send({from: caller});
+ expect(await flipper.methods.getValue().call()).to.be.true;
+
+ // Balance should be taken from flipper instead of caller
+ const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+ expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+ // Caller's balance does not change:
+ const callerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));
+ expect(callerBalanceAfter).to.eq(testCase.balance * nominal);
+ });
});
- 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}) => {
+ itEth('Non-allow-listed address can call a contract. Sponsor balance should not decrease', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const caller = await helper.eth.createAccount();
- const helpers = helper.ethNativeContract.contractHelpers(owner);
- const flipper = await helper.eth.deployFlipper(owner);
-
- await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
- await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
+ const caller = helper.eth.createAccount();
+ const contractHelpers = helper.ethNativeContract.contractHelpers(owner);
+ // Deploy flipper and send some tokens:
+ const flipper = await helper.eth.deployFlipper(owner);
await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address);
-
+ expect(await flipper.methods.getValue().call()).to.be.false;
+ // flipper address has some tokens:
const originalFlipperBalance = await helper.balance.getEthereum(flipper.options.address);
- expect(originalFlipperBalance).to.be.not.equal('0');
+ expect(originalFlipperBalance > 0n).to.be.true;
+
+ // Set Allowlisted sponsoring mode. caller is not in allow list:
+ await contractHelpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});
+ await contractHelpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
+ await contractHelpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
+ // 1. Caller has no UNQ and is not in allow list. So he cannot flip:
await expect(flipper.methods.flip().send({from: caller})).to.be.rejectedWith(/Returned error: insufficient funds for gas \* price \+ value/);
expect(await flipper.methods.getValue().call()).to.be.false;
- // Balance should be taken from flipper instead of caller
- // FIXME the comment is wrong! What check should be here?
+ // Flipper's balance does not change:
const balanceAfter = await helper.balance.getEthereum(flipper.options.address);
expect(balanceAfter).to.be.equal(originalFlipperBalance);
- });
-
- itEth('Sponsoring is set, an address that has UNQ can send a transaction and it works. User balance should not change', async ({helper}) => {
- const owner = await helper.eth.createAccountWithBalance(donor);
- const sponsor = await helper.eth.createAccountWithBalance(donor);
- const caller = await helper.eth.createAccountWithBalance(donor);
- const helpers = helper.ethNativeContract.contractHelpers(owner);
- const flipper = await helper.eth.deployFlipper(owner);
-
- await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});
- await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});
-
- await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
- await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
-
- await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
- await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
-
- const sponsorBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(sponsor));
- const callerBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(caller));
-
- await flipper.methods.flip().send({from: caller});
- expect(await flipper.methods.getValue().call()).to.be.true;
-
- const sponsorBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(sponsor));
- const callerBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(caller));
- expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
- expect(callerBalanceAfter).to.be.equal(callerBalanceBefore);
});
itEth('Sponsoring is limited, with setContractRateLimit. The limitation is working if transactions are sent more often, the sender pays the commission.', async ({helper}) => {
@@ -427,7 +375,7 @@
await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
const originalFlipperBalance = await helper.balance.getEthereum(sponsor);
- expect(originalFlipperBalance).to.be.not.equal('0');
+ expect(originalFlipperBalance > 0n).to.be.true;
await flipper.methods.flip().send({from: caller});
expect(await flipper.methods.getValue().call()).to.be.true;
tests/src/eth/events.test.tsdiffbeforeafterboth--- a/tests/src/eth/events.test.ts
+++ b/tests/src/eth/events.test.ts
@@ -40,7 +40,7 @@
const {collectionAddress, events: ethEvents} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');
await helper.wait.newBlocks(1);
{
- expect(ethEvents).to.be.like([
+ expect(ethEvents).to.containSubset([
{
event: 'CollectionCreated',
args: {
@@ -49,21 +49,21 @@
},
},
]);
- expect(subEvents).to.be.like([{method: 'CollectionCreated'}]);
+ expect(subEvents).to.containSubset([{method: 'CollectionCreated'}]);
clearEvents(ethEvents, subEvents);
}
{
const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
const result = await collectionHelper.methods.destroyCollection(collectionAddress).send({from:owner});
await helper.wait.newBlocks(1);
- expect(result.events).to.be.like({
+ expect(result.events).to.containSubset({
CollectionDestroyed: {
returnValues: {
collectionId: collectionAddress,
},
},
});
- expect(subEvents).to.be.like([{method: 'CollectionDestroyed'}]);
+ expect(subEvents).to.containSubset([{method: 'CollectionDestroyed'}]);
}
unsubscribe();
}
@@ -71,7 +71,7 @@
async function testCollectionPropertySetAndDeleted(helper: EthUniqueHelper, mode: TCollectionMode) {
const owner = await helper.eth.createAccountWithBalance(donor);
const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');
- const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
+ const collection = helper.ethNativeContract.collection(collectionAddress, mode, owner);
const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
const ethEvents: any = [];
@@ -82,7 +82,7 @@
{
await collection.methods.setCollectionProperties([{key: 'A', value: [0,1,2,3]}]).send({from:owner});
await helper.wait.newBlocks(1);
- expect(ethEvents).to.be.like([
+ expect(ethEvents).to.containSubset([
{
event: 'CollectionChanged',
returnValues: {
@@ -90,13 +90,13 @@
},
},
]);
- expect(subEvents).to.be.like([{method: 'CollectionPropertySet'}]);
+ expect(subEvents).to.containSubset([{method: 'CollectionPropertySet'}]);
clearEvents(ethEvents, subEvents);
}
{
await collection.methods.deleteCollectionProperties(['A']).send({from:owner});
await helper.wait.newBlocks(1);
- expect(ethEvents).to.be.like([
+ expect(ethEvents).to.containSubset([
{
event: 'CollectionChanged',
returnValues: {
@@ -104,7 +104,7 @@
},
},
]);
- expect(subEvents).to.be.like([{method: 'CollectionPropertyDeleted'}]);
+ expect(subEvents).to.containSubset([{method: 'CollectionPropertyDeleted'}]);
}
unsubscribe();
}
@@ -112,7 +112,7 @@
async function testPropertyPermissionSet(helper: EthUniqueHelper, mode: TCollectionMode) {
const owner = await helper.eth.createAccountWithBalance(donor);
const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');
- const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
+ const collection = helper.ethNativeContract.collection(collectionAddress, mode, owner);
const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
const ethEvents: any = [];
collectionHelper.events.allEvents((_: any, event: any) => {
@@ -127,7 +127,7 @@
],
]).send({from: owner});
await helper.wait.newBlocks(1);
- expect(ethEvents).to.be.like([
+ expect(ethEvents).to.containSubset([
{
event: 'CollectionChanged',
returnValues: {
@@ -135,7 +135,7 @@
},
},
]);
- expect(subEvents).to.be.like([{method: 'PropertyPermissionSet'}]);
+ expect(subEvents).to.containSubset([{method: 'PropertyPermissionSet'}]);
unsubscribe();
}
@@ -143,7 +143,7 @@
const owner = await helper.eth.createAccountWithBalance(donor);
const user = helper.ethCrossAccount.createAccount();
const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');
- const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
+ const collection = helper.ethNativeContract.collection(collectionAddress, mode, owner);
const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
const ethEvents: any[] = [];
collectionHelper.events.allEvents((_: any, event: any) => {
@@ -154,7 +154,7 @@
{
await collection.methods.addToCollectionAllowListCross(user).send({from: owner});
await helper.wait.newBlocks(1);
- expect(ethEvents).to.be.like([
+ expect(ethEvents).to.containSubset([
{
event: 'CollectionChanged',
returnValues: {
@@ -162,14 +162,13 @@
},
},
]);
- expect(subEvents).to.be.like([{method: 'AllowListAddressAdded'}]);
+ expect(subEvents).to.containSubset([{method: 'AllowListAddressAdded'}]);
clearEvents(ethEvents, subEvents);
}
{
await collection.methods.removeFromCollectionAllowListCross(user).send({from: owner});
await helper.wait.newBlocks(1);
- expect(ethEvents.length).to.be.eq(1);
- expect(ethEvents).to.be.like([
+ expect(ethEvents).to.containSubset([
{
event: 'CollectionChanged',
returnValues: {
@@ -177,7 +176,7 @@
},
},
]);
- expect(subEvents).to.be.like([{method: 'AllowListAddressRemoved'}]);
+ expect(subEvents).to.containSubset([{method: 'AllowListAddressRemoved'}]);
}
unsubscribe();
}
@@ -186,7 +185,7 @@
const owner = await helper.eth.createAccountWithBalance(donor);
const user = helper.ethCrossAccount.createAccount();
const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');
- const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
+ const collection = helper.ethNativeContract.collection(collectionAddress, mode, owner);
const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
const ethEvents: any = [];
collectionHelper.events.allEvents((_: any, event: any) => {
@@ -196,7 +195,7 @@
{
await collection.methods.addCollectionAdminCross(user).send({from: owner});
await helper.wait.newBlocks(1);
- expect(ethEvents).to.be.like([
+ expect(ethEvents).to.containSubset([
{
event: 'CollectionChanged',
returnValues: {
@@ -204,13 +203,13 @@
},
},
]);
- expect(subEvents).to.be.like([{method: 'CollectionAdminAdded'}]);
+ expect(subEvents).to.containSubset([{method: 'CollectionAdminAdded'}]);
clearEvents(ethEvents, subEvents);
}
{
await collection.methods.removeCollectionAdminCross(user).send({from: owner});
await helper.wait.newBlocks(1);
- expect(ethEvents).to.be.like([
+ expect(ethEvents).to.containSubset([
{
event: 'CollectionChanged',
returnValues: {
@@ -218,7 +217,7 @@
},
},
]);
- expect(subEvents).to.be.like([{method: 'CollectionAdminRemoved'}]);
+ expect(subEvents).to.containSubset([{method: 'CollectionAdminRemoved'}]);
}
unsubscribe();
}
@@ -226,7 +225,7 @@
async function testCollectionLimitSet(helper: EthUniqueHelper, mode: TCollectionMode) {
const owner = await helper.eth.createAccountWithBalance(donor);
const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');
- const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
+ const collection = helper.ethNativeContract.collection(collectionAddress, mode, owner);
const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
const ethEvents: any = [];
collectionHelper.events.allEvents((_: any, event: any) => {
@@ -236,7 +235,7 @@
{
await collection.methods.setCollectionLimit(CollectionLimits.OwnerCanTransfer, true, 0).send({from: owner});
await helper.wait.newBlocks(1);
- expect(ethEvents).to.be.like([
+ expect(ethEvents).to.containSubset([
{
event: 'CollectionChanged',
returnValues: {
@@ -244,7 +243,7 @@
},
},
]);
- expect(subEvents).to.be.like([{method: 'CollectionLimitSet'}]);
+ expect(subEvents).to.containSubset([{method: 'CollectionLimitSet'}]);
}
unsubscribe();
}
@@ -253,7 +252,7 @@
const owner = await helper.eth.createAccountWithBalance(donor);
const newOwner = helper.ethCrossAccount.createAccount();
const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');
- const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
+ const collection = helper.ethNativeContract.collection(collectionAddress, mode, owner);
const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
const ethEvents: any = [];
collectionHelper.events.allEvents((_: any, event: any) => {
@@ -263,7 +262,7 @@
{
await collection.methods.changeCollectionOwnerCross(newOwner).send({from: owner});
await helper.wait.newBlocks(1);
- expect(ethEvents).to.be.like([
+ expect(ethEvents).to.containSubset([
{
event: 'CollectionChanged',
returnValues: {
@@ -271,7 +270,7 @@
},
},
]);
- expect(subEvents).to.be.like([{method: 'CollectionOwnerChanged'}]);
+ expect(subEvents).to.containSubset([{method: 'CollectionOwnerChanged'}]);
}
unsubscribe();
}
@@ -279,7 +278,7 @@
async function testCollectionPermissionSet(helper: EthUniqueHelper, mode: TCollectionMode) {
const owner = await helper.eth.createAccountWithBalance(donor);
const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');
- const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
+ const collection = helper.ethNativeContract.collection(collectionAddress, mode, owner);
const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
const ethEvents: any = [];
collectionHelper.events.allEvents((_: any, event: any) => {
@@ -289,7 +288,7 @@
{
await collection.methods.setCollectionMintMode(true).send({from: owner});
await helper.wait.newBlocks(1);
- expect(ethEvents).to.be.like([
+ expect(ethEvents).to.containSubset([
{
event: 'CollectionChanged',
returnValues: {
@@ -297,13 +296,13 @@
},
},
]);
- expect(subEvents).to.be.like([{method: 'CollectionPermissionSet'}]);
+ expect(subEvents).to.containSubset([{method: 'CollectionPermissionSet'}]);
clearEvents(ethEvents, subEvents);
}
{
await collection.methods.setCollectionAccess(1).send({from: owner});
await helper.wait.newBlocks(1);
- expect(ethEvents).to.be.like([
+ expect(ethEvents).to.containSubset([
{
event: 'CollectionChanged',
returnValues: {
@@ -311,7 +310,7 @@
},
},
]);
- expect(subEvents).to.be.like([{method: 'CollectionPermissionSet'}]);
+ expect(subEvents).to.containSubset([{method: 'CollectionPermissionSet'}]);
}
unsubscribe();
}
@@ -320,7 +319,7 @@
const owner = await helper.eth.createAccountWithBalance(donor);
const sponsor = await helper.ethCrossAccount.createAccountWithBalance(donor);
const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');
- const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
+ const collection = helper.ethNativeContract.collection(collectionAddress, mode, owner);
const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
const ethEvents: any = [];
collectionHelper.events.allEvents((_: any, event: any) => {
@@ -332,21 +331,19 @@
{
await collection.methods.setCollectionSponsorCross(sponsor).send({from: owner});
await helper.wait.newBlocks(1);
- expect(ethEvents).to.be.like([
- {
- event: 'CollectionChanged',
- returnValues: {
- collectionId: collectionAddress,
- },
+ expect(ethEvents).to.containSubset([{
+ event: 'CollectionChanged',
+ returnValues: {
+ collectionId: collectionAddress,
},
- ]);
- expect(subEvents).to.be.like([{method: 'CollectionSponsorSet'}]);
+ }]);
+ expect(subEvents).to.containSubset([{method: 'CollectionSponsorSet'}]);
clearEvents(ethEvents, subEvents);
}
{
await collection.methods.confirmCollectionSponsorship().send({from: sponsor.eth});
await helper.wait.newBlocks(1);
- expect(ethEvents).to.be.like([
+ expect(ethEvents).to.containSubset([
{
event: 'CollectionChanged',
returnValues: {
@@ -354,13 +351,13 @@
},
},
]);
- expect(subEvents).to.be.like([{method: 'SponsorshipConfirmed'}]);
+ expect(subEvents).to.containSubset([{method: 'SponsorshipConfirmed'}]);
clearEvents(ethEvents, subEvents);
}
{
await collection.methods.removeCollectionSponsor().send({from: owner});
await helper.wait.newBlocks(1);
- expect(ethEvents).to.be.like([
+ expect(ethEvents).to.containSubset([
{
event: 'CollectionChanged',
returnValues: {
@@ -368,7 +365,7 @@
},
},
]);
- expect(subEvents).to.be.like([{method: 'CollectionSponsorRemoved'}]);
+ expect(subEvents).to.containSubset([{method: 'CollectionSponsorRemoved'}]);
}
unsubscribe();
}
@@ -376,7 +373,7 @@
async function testTokenPropertySetAndDeleted(helper: EthUniqueHelper, mode: TCollectionMode) {
const owner = await helper.eth.createAccountWithBalance(donor);
const {collectionAddress} = await helper.eth.createCollection(mode, owner, 'A', 'B', 'C');
- const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
+ const collection = helper.ethNativeContract.collection(collectionAddress, mode, owner);
const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
const result = await collection.methods.mint(owner).send({from: owner});
const tokenId = result.events.Transfer.returnValues.tokenId;
@@ -397,7 +394,7 @@
{
await collection.methods.setProperties(tokenId, [{key: 'A', value: [1,2,3]}]).send({from: owner});
await helper.wait.newBlocks(1);
- expect(ethEvents).to.be.like([
+ expect(ethEvents).to.containSubset([
{
event: 'TokenChanged',
returnValues: {
@@ -405,13 +402,13 @@
},
},
]);
- expect(subEvents).to.be.like([{method: 'TokenPropertySet'}]);
+ expect(subEvents).to.containSubset([{method: 'TokenPropertySet'}]);
clearEvents(ethEvents, subEvents);
}
{
await collection.methods.deleteProperties(tokenId, ['A']).send({from: owner});
await helper.wait.newBlocks(1);
- expect(ethEvents).to.be.like([
+ expect(ethEvents).to.containSubset([
{
event: 'TokenChanged',
returnValues: {
@@ -419,7 +416,7 @@
},
},
]);
- expect(subEvents).to.be.like([{method: 'TokenPropertyDeleted'}]);
+ expect(subEvents).to.containSubset([{method: 'TokenPropertyDeleted'}]);
}
unsubscribe();
}
tests/src/eth/fungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/fungible.test.ts
+++ b/tests/src/eth/fungible.test.ts
@@ -79,23 +79,40 @@
expect(event.returnValues.value).to.equal('100');
});
+ [
+ 'substrate' as const,
+ 'ethereum' as const,
+ ].map(testCase => {
+ itEth(`Can perform mintCross() for ${testCase} address`, async ({helper}) => {
+ // 1. Create receiver depending on the test case:
+ const receiverEth = helper.eth.createAccount();
+ const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
+ const receiverSub = owner;
+ const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(owner);
+
+ const ethOwner = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper.ft.mintCollection(alice);
+ await collection.addAdmin(alice, {Ethereum: ethOwner});
- itEth('Can perform mintCross()', async ({helper}) => {
- const receiverCross = helper.ethCrossAccount.fromKeyringPair(owner);
- const ethOwner = await helper.eth.createAccountWithBalance(donor);
- const collection = await helper.ft.mintCollection(alice);
- await collection.addAdmin(alice, {Ethereum: ethOwner});
-
- const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
- const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', ethOwner);
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', ethOwner);
+
+ // 2. Mint tokens:
+ const result = await collectionEvm.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, 100).send();
+
+ const event = result.events.Transfer;
+ expect(event.address).to.equal(collectionAddress);
+ expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+ expect(event.returnValues.to).to.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(receiverSub.address));
+ expect(event.returnValues.value).to.equal('100');
- const result = await contract.methods.mintCross(receiverCross, 100).send();
-
- const event = result.events.Transfer;
- expect(event.address).to.equal(collectionAddress);
- expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
- expect(event.returnValues.to).to.equal(helper.address.substrateToEth(owner.address));
- expect(event.returnValues.value).to.equal('100');
+ // 3. Get balance depending on the test case:
+ let balance;
+ if (testCase === 'ethereum') balance = await collection.getBalance({Ethereum: receiverEth});
+ else if (testCase === 'substrate') balance = await collection.getBalance({Substrate: receiverSub.address});
+ // 3.1 Check balance:
+ expect(balance).to.eq(100n);
+ });
});
itEth('Can perform mintBulk()', async ({helper}) => {
@@ -169,6 +186,68 @@
}
});
+ itEth('Can perform approveCross()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const spender = helper.eth.createAccount();
+ const spenderSub = (await helper.arrange.createAccounts([1n], donor))[0];
+ const spenderCrossEth = helper.ethCrossAccount.fromAddress(spender);
+ const spenderCrossSub = helper.ethCrossAccount.fromKeyringPair(spenderSub);
+
+
+ const collection = await helper.ft.mintCollection(alice);
+ await collection.mint(alice, 200n, {Ethereum: owner});
+
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+ {
+ const result = await contract.methods.approveCross(spenderCrossEth, 100).send({from: owner});
+ const event = result.events.Approval;
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.owner).to.be.equal(owner);
+ expect(event.returnValues.spender).to.be.equal(spender);
+ expect(event.returnValues.value).to.be.equal('100');
+ }
+
+ {
+ const allowance = await contract.methods.allowance(owner, spender).call();
+ expect(+allowance).to.equal(100);
+ }
+
+
+ {
+ const result = await contract.methods.approveCross(spenderCrossSub, 100).send({from: owner});
+ const event = result.events.Approval;
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.owner).to.be.equal(owner);
+ expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(spenderSub.address));
+ expect(event.returnValues.value).to.be.equal('100');
+ }
+
+ {
+ const allowance = await collection.getApprovedTokens({Ethereum: owner}, {Substrate: spenderSub.address});
+ expect(allowance).to.equal(100n);
+ }
+
+ {
+ //TO-DO expect with future allowanceCross(owner, spenderCrossEth).call()
+ }
+ });
+
+ itEth('Non-owner and non admin cannot approveCross', async ({helper}) => {
+ const nonOwner = await helper.eth.createAccountWithBalance(donor);
+ const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner);
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper.ft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'});
+ await collection.mint(alice, 100n, {Ethereum: owner});
+
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+ await expect(collectionEvm.methods.approveCross(nonOwnerCross, 20).call({from: nonOwner})).to.be.rejectedWith('CantApproveMoreThanOwned');
+ });
+
+
itEth('Can perform burnFromCross()', async ({helper}) => {
const sender = await helper.eth.createAccountWithBalance(donor, 100n);
tests/src/eth/nonFungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/nonFungible.test.ts
+++ b/tests/src/eth/nonFungible.test.ts
@@ -175,55 +175,82 @@
// expect(tokenUri).to.be.equal(`https://offchain-service.local/token-info/${nextTokenId}`);
});
- itEth('Can perform mintCross()', async ({helper}) => {
- const caller = await helper.eth.createAccountWithBalance(donor);
- const receiverCross = helper.ethCrossAccount.fromKeyringPair(bob);
- const properties = Array(5).fill(0).map((_, i) => { return {key: `key_${i}`, value: Buffer.from(`value_${i}`)}; });
- const permissions: ITokenPropertyPermission[] = properties
- .map(p => {
- return {
- key: p.key, permission: {
- tokenOwner: true,
- collectionAdmin: true,
- mutable: true,
- },
- };
- });
+ // TODO combine all minting tests in one place
+ [
+ 'substrate' as const,
+ 'ethereum' as const,
+ ].map(testCase => {
+ itEth(`Can perform mintCross() for ${testCase} address`, async ({helper}) => {
+ const collectionAdmin = await helper.eth.createAccountWithBalance(donor);
+
+ const receiverEth = helper.eth.createAccount();
+ const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
+ const receiverSub = bob;
+ const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);
+
+ // const receiverCross = helper.ethCrossAccount.fromKeyringPair(bob);
+ const properties = Array(5).fill(0).map((_, i) => { return {key: `key_${i}`, value: Buffer.from(`value_${i}`)}; });
+ const permissions: ITokenPropertyPermission[] = properties
+ .map(p => {
+ return {
+ key: p.key, permission: {
+ tokenOwner: false,
+ collectionAdmin: true,
+ mutable: false,
+ },
+ };
+ });
- const collection = await helper.nft.mintCollection(minter, {
- tokenPrefix: 'ethp',
- tokenPropertyPermissions: permissions,
- });
- await collection.addAdmin(minter, {Ethereum: caller});
+ const collection = await helper.nft.mintCollection(minter, {
+ tokenPrefix: 'ethp',
+ tokenPropertyPermissions: permissions,
+ });
+ await collection.addAdmin(minter, {Ethereum: collectionAdmin});
- const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
- const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', caller, true);
- let expectedTokenId = await contract.methods.nextTokenId().call();
- let result = await contract.methods.mintCross(receiverCross, []).send();
- let tokenId = result.events.Transfer.returnValues.tokenId;
- expect(tokenId).to.be.equal(expectedTokenId);
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', collectionAdmin, true);
+ let expectedTokenId = await contract.methods.nextTokenId().call();
+ let result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, []).send();
+ let tokenId = result.events.Transfer.returnValues.tokenId;
+ expect(tokenId).to.be.equal(expectedTokenId);
- let event = result.events.Transfer;
- expect(event.address).to.be.equal(collectionAddress);
- expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
- expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(bob.address));
- expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);
+ let event = result.events.Transfer;
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
+ expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address));
+ expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);
- expectedTokenId = await contract.methods.nextTokenId().call();
- result = await contract.methods.mintCross(receiverCross, properties).send();
- event = result.events.Transfer;
- expect(event.address).to.be.equal(collectionAddress);
- expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
- expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(bob.address));
- expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);
+ expectedTokenId = await contract.methods.nextTokenId().call();
+ result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, properties).send();
+ event = result.events.Transfer;
+ expect(event.address).to.be.equal(collectionAddress);
+ expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
+ expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address));
+ expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);
- tokenId = result.events.Transfer.returnValues.tokenId;
+ tokenId = result.events.Transfer.returnValues.tokenId;
- expect(tokenId).to.be.equal(expectedTokenId);
+ expect(tokenId).to.be.equal(expectedTokenId);
- expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties
- .map(p => { return helper.ethProperty.property(p.key, p.value.toString()); }));
+ expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties
+ .map(p => { return helper.ethProperty.property(p.key, p.value.toString()); }));
+
+ expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId))
+ .to.deep.eq(testCase === 'ethereum' ? {Ethereum: receiverEth.toLowerCase()} : {Substrate: receiverSub.address});
+ });
+ });
+
+ itEth('Non-owner and non admin cannot mintCross', async ({helper}) => {
+ const nonOwner = await helper.eth.createAccountWithBalance(donor);
+ const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner);
+
+ const collection = await helper.nft.mintCollection(minter);
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft');
+
+ await expect(collectionEvm.methods.mintCross(nonOwnerCross, []).call({from: nonOwner}))
+ .to.be.rejectedWith('PublicMintingNotAllowed');
});
//TODO: CORE-302 add eth methods
@@ -375,6 +402,8 @@
},
});
}
+
+ expect(await helper.nft.doesTokenExist(collection.collectionId, token.tokenId)).to.be.false;
});
itEth('Can perform transfer with ApprovalForAll', async ({helper}) => {
@@ -455,6 +484,7 @@
expect(await token2.doesExist()).to.be.false;
});
+ // TODO combine all approve tests in one place
itEth('Can perform approveCross()', async ({helper}) => {
// arrange: create accounts
const owner = await helper.eth.createAccountWithBalance(donor, 100n);
@@ -503,6 +533,17 @@
expect(await helper.nft.getTokenOwner(collection.collectionId, token2.tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()});
});
+ itEth('Non-owner and non admin cannot approveCross', async ({helper}) => {
+ const nonOwner = await helper.eth.createAccountWithBalance(donor);
+ const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner);
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+ const collectionEvm = helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');
+ const token = await collection.mintToken(minter, {Ethereum: owner});
+
+ await expect(collectionEvm.methods.approveCross(nonOwnerCross, token.tokenId).call({from: nonOwner})).to.be.rejectedWith('CantApproveMoreThanOwned');
+ });
+
itEth('Can reaffirm approved address', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor, 100n);
const ownerCrossEth = helper.ethCrossAccount.fromAddress(owner);
tests/src/eth/reFungible.test.tsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {Pallets, requirePalletsOrSkip} from '../util';18import {expect, itEth, usingEthPlaygrounds} from './util';19import {IKeyringPair} from '@polkadot/types/types';20import {ITokenPropertyPermission} from '../util/playgrounds/types';2122describe('Refungible: Information getting', () => {23 let donor: IKeyringPair;2425 before(async function() {26 await usingEthPlaygrounds(async (helper, privateKey) => {27 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);2829 donor = await privateKey({filename: __filename});30 });31 });3233 itEth('totalSupply', async ({helper}) => {34 const caller = await helper.eth.createAccountWithBalance(donor);35 const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'TotalSupply', '6', '6');36 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);3738 await contract.methods.mint(caller).send();3940 const totalSupply = await contract.methods.totalSupply().call();41 expect(totalSupply).to.equal('1');42 });4344 itEth('balanceOf', async ({helper}) => {45 const caller = await helper.eth.createAccountWithBalance(donor);46 const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'BalanceOf', '6', '6');47 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);4849 await contract.methods.mint(caller).send();50 await contract.methods.mint(caller).send();51 await contract.methods.mint(caller).send();5253 const balance = await contract.methods.balanceOf(caller).call();54 expect(balance).to.equal('3');55 });5657 itEth('ownerOf', async ({helper}) => {58 const caller = await helper.eth.createAccountWithBalance(donor);59 const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'OwnerOf', '6', '6');60 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);6162 const result = await contract.methods.mint(caller).send();63 const tokenId = result.events.Transfer.returnValues.tokenId;6465 const owner = await contract.methods.ownerOf(tokenId).call();66 expect(owner).to.equal(caller);67 });6869 itEth('ownerOf after burn', async ({helper}) => {70 const caller = await helper.eth.createAccountWithBalance(donor);71 const receiver = helper.eth.createAccount();72 const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'OwnerOf-AfterBurn', '6', '6');73 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);7475 const result = await contract.methods.mint(caller).send();76 const tokenId = result.events.Transfer.returnValues.tokenId;77 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);7879 await tokenContract.methods.repartition(2).send();80 await tokenContract.methods.transfer(receiver, 1).send();8182 await tokenContract.methods.burnFrom(caller, 1).send();8384 const owner = await contract.methods.ownerOf(tokenId).call();85 expect(owner).to.equal(receiver);86 });8788 itEth('ownerOf for partial ownership', async ({helper}) => {89 const caller = await helper.eth.createAccountWithBalance(donor);90 const receiver = helper.eth.createAccount();91 const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Partial-OwnerOf', '6', '6');92 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);9394 const result = await contract.methods.mint(caller).send();95 const tokenId = result.events.Transfer.returnValues.tokenId;96 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);9798 await tokenContract.methods.repartition(2).send();99 await tokenContract.methods.transfer(receiver, 1).send();100101 const owner = await contract.methods.ownerOf(tokenId).call();102 expect(owner).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');103 });104});105106describe('Refungible: Plain calls', () => {107 let donor: IKeyringPair;108 let minter: IKeyringPair;109 let bob: IKeyringPair;110 let charlie: IKeyringPair;111112 before(async function() {113 await usingEthPlaygrounds(async (helper, privateKey) => {114 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);115116 donor = await privateKey({filename: __filename});117 [minter, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);118 });119 });120121 itEth('Can perform mint() & crossOwnerOf()', async ({helper}) => {122 const owner = await helper.eth.createAccountWithBalance(donor);123 const receiver = helper.eth.createAccount();124 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Minty', '6', '6', '');125 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);126127 const result = await contract.methods.mintWithTokenURI(receiver, 'Test URI').send();128129 const event = result.events.Transfer;130 expect(event.address).to.equal(collectionAddress);131 expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');132 expect(event.returnValues.to).to.equal(receiver);133 const tokenId = event.returnValues.tokenId;134 expect(tokenId).to.be.equal('1');135136 expect(await contract.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);137 expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');138 });139 140 itEth('Can perform mintCross()', async ({helper}) => {141 const caller = await helper.eth.createAccountWithBalance(donor);142 const receiverCross = helper.ethCrossAccount.fromKeyringPair(bob);143 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,145 collectionAdmin: true,146 mutable: true}}; });147 148 149 const collection = await helper.rft.mintCollection(minter, {150 tokenPrefix: 'ethp',151 tokenPropertyPermissions: permissions,152 });153 await collection.addAdmin(minter, {Ethereum: caller});154 155 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);156 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller, true);157 let expectedTokenId = await contract.methods.nextTokenId().call();158 let result = await contract.methods.mintCross(receiverCross, []).send();159 let tokenId = result.events.Transfer.returnValues.tokenId;160 expect(tokenId).to.be.equal(expectedTokenId);161162 let event = result.events.Transfer;163 expect(event.address).to.be.equal(collectionAddress);164 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');165 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(bob.address));166 expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);167 168 expectedTokenId = await contract.methods.nextTokenId().call();169 result = await contract.methods.mintCross(receiverCross, properties).send();170 event = result.events.Transfer;171 expect(event.address).to.be.equal(collectionAddress);172 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');173 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(bob.address));174 expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);175 176 tokenId = result.events.Transfer.returnValues.tokenId;177178 expect(tokenId).to.be.equal(expectedTokenId);179 180 expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties181 .map(p => { return helper.ethProperty.property(p.key, p.value.toString()); }));182 });183184 itEth.skip('Can perform mintBulk()', async ({helper}) => {185 const owner = await helper.eth.createAccountWithBalance(donor);186 const receiver = helper.eth.createAccount();187 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'MintBulky', '6', '6', '');188 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);189190 {191 const nextTokenId = await contract.methods.nextTokenId().call();192 expect(nextTokenId).to.be.equal('1');193 const result = await contract.methods.mintBulkWithTokenURI(194 receiver,195 [196 [nextTokenId, 'Test URI 0'],197 [+nextTokenId + 1, 'Test URI 1'],198 [+nextTokenId + 2, 'Test URI 2'],199 ],200 ).send();201202 const events = result.events.Transfer;203 for (let i = 0; i < 2; i++) {204 const event = events[i];205 expect(event.address).to.equal(collectionAddress);206 expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');207 expect(event.returnValues.to).to.equal(receiver);208 expect(event.returnValues.tokenId).to.equal(String(+nextTokenId + i));209 }210211 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI 0');212 expect(await contract.methods.tokenURI(+nextTokenId + 1).call()).to.be.equal('Test URI 1');213 expect(await contract.methods.tokenURI(+nextTokenId + 2).call()).to.be.equal('Test URI 2');214 }215 });216217 itEth('Can perform setApprovalForAll()', async ({helper}) => {218 const owner = await helper.eth.createAccountWithBalance(donor);219 const operator = helper.eth.createAccount();220221 const collection = await helper.rft.mintCollection(minter, {});222223 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);224 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);225226 const approvedBefore = await contract.methods.isApprovedForAll(owner, operator).call();227 expect(approvedBefore).to.be.equal(false);228229 {230 const result = await contract.methods.setApprovalForAll(operator, true).send({from: owner});231232 expect(result.events.ApprovalForAll).to.be.like({233 address: collectionAddress,234 event: 'ApprovalForAll',235 returnValues: {236 owner,237 operator,238 approved: true,239 },240 });241242 const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();243 expect(approvedAfter).to.be.equal(true);244 }245246 {247 const result = await contract.methods.setApprovalForAll(operator, false).send({from: owner});248249 expect(result.events.ApprovalForAll).to.be.like({250 address: collectionAddress,251 event: 'ApprovalForAll',252 returnValues: {253 owner,254 operator,255 approved: false,256 },257 });258259 const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();260 expect(approvedAfter).to.be.equal(false);261 }262 });263264 itEth('Can perform burn with ApprovalForAll', async ({helper}) => {265 const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});266267 const owner = await helper.eth.createAccountWithBalance(donor);268 const operator = await helper.eth.createAccountWithBalance(donor, 100n);269270 const token = await collection.mintToken(minter, 100n, {Ethereum: owner});271272 const address = helper.ethAddress.fromCollectionId(collection.collectionId);273 const contract = helper.ethNativeContract.collection(address, 'rft');274275 {276 await contract.methods.setApprovalForAll(operator, true).send({from: owner});277 const ownerCross = helper.ethCrossAccount.fromAddress(owner);278 const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: operator});279 const events = result.events.Transfer;280281 expect(events).to.be.like({282 address,283 event: 'Transfer',284 returnValues: {285 from: owner,286 to: '0x0000000000000000000000000000000000000000',287 tokenId: token.tokenId.toString(),288 },289 });290 }291 });292293 itEth('Can perform burn with approve and approvalForAll', async ({helper}) => {294 const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});295296 const owner = await helper.eth.createAccountWithBalance(donor);297 const operator = await helper.eth.createAccountWithBalance(donor, 100n);298299 const token = await collection.mintToken(minter, 100n, {Ethereum: owner});300301 const address = helper.ethAddress.fromCollectionId(collection.collectionId);302 const contract = helper.ethNativeContract.collection(address, 'rft');303304 const rftToken = helper.ethNativeContract.rftTokenById(token.collectionId, token.tokenId, owner);305306 {307 await rftToken.methods.approve(operator, 15n).send({from: owner});308 await contract.methods.setApprovalForAll(operator, true).send({from: owner});309 await rftToken.methods.burnFrom(owner, 10n).send({from: operator});310 const allowance = await rftToken.methods.allowance(owner, operator).call();311 expect(allowance).to.be.equal('5');312 }313 });314 315 itEth('Can perform transfer with ApprovalForAll', async ({helper}) => {316 const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});317318 const owner = await helper.eth.createAccountWithBalance(donor);319 const operator = await helper.eth.createAccountWithBalance(donor);320 const receiver = charlie;321322 const token = await collection.mintToken(minter, 100n, {Ethereum: owner});323324 const address = helper.ethAddress.fromCollectionId(collection.collectionId);325 const contract = helper.ethNativeContract.collection(address, 'rft');326327 {328 await contract.methods.setApprovalForAll(operator, true).send({from: owner});329 const ownerCross = helper.ethCrossAccount.fromAddress(owner);330 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);331 const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: operator});332 const event = result.events.Transfer;333 expect(event).to.be.like({334 address: helper.ethAddress.fromCollectionId(collection.collectionId),335 event: 'Transfer',336 returnValues: {337 from: owner,338 to: helper.address.substrateToEth(receiver.address),339 tokenId: token.tokenId.toString(),340 },341 });342 }343344 expect(await token.getTop10Owners()).to.be.like([{Substrate: receiver.address}]);345 });346347 itEth('Can perform burn()', async ({helper}) => {348 const caller = await helper.eth.createAccountWithBalance(donor);349 const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Burny', '6', '6');350 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);351352 const result = await contract.methods.mint(caller).send();353 const tokenId = result.events.Transfer.returnValues.tokenId;354 {355 const result = await contract.methods.burn(tokenId).send();356 const event = result.events.Transfer;357 expect(event.address).to.equal(collectionAddress);358 expect(event.returnValues.from).to.equal(caller);359 expect(event.returnValues.to).to.equal('0x0000000000000000000000000000000000000000');360 expect(event.returnValues.tokenId).to.equal(tokenId.toString());361 }362 });363364 itEth('Can perform transferFrom()', async ({helper}) => {365 const caller = await helper.eth.createAccountWithBalance(donor);366 const receiver = helper.eth.createAccount();367 const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'TransferFromy', '6', '6');368 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);369370 const result = await contract.methods.mint(caller).send();371 const tokenId = result.events.Transfer.returnValues.tokenId;372373 const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);374375 const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, caller);376 await tokenContract.methods.repartition(15).send();377378 {379 const tokenEvents: any = [];380 tokenContract.events.allEvents((_: any, event: any) => {381 tokenEvents.push(event);382 });383 const result = await contract.methods.transferFrom(caller, receiver, tokenId).send();384 if (tokenEvents.length == 0) await helper.wait.newBlocks(1);385386 let event = result.events.Transfer;387 expect(event.address).to.equal(collectionAddress);388 expect(event.returnValues.from).to.equal(caller);389 expect(event.returnValues.to).to.equal(receiver);390 expect(event.returnValues.tokenId).to.equal(tokenId.toString());391392 event = tokenEvents[0];393 expect(event.address).to.equal(tokenAddress);394 expect(event.returnValues.from).to.equal(caller);395 expect(event.returnValues.to).to.equal(receiver);396 expect(event.returnValues.value).to.equal('15');397 }398399 {400 const balance = await contract.methods.balanceOf(receiver).call();401 expect(+balance).to.equal(1);402 }403404 {405 const balance = await contract.methods.balanceOf(caller).call();406 expect(+balance).to.equal(0);407 }408 });409410 // Soft-deprecated411 itEth('Can perform burnFrom()', async ({helper}) => {412 const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});413414 const owner = await helper.eth.createAccountWithBalance(donor, 100n);415 const spender = await helper.eth.createAccountWithBalance(donor, 100n);416417 const token = await collection.mintToken(minter, 100n, {Ethereum: owner});418419 const address = helper.ethAddress.fromCollectionId(collection.collectionId);420 const contract = helper.ethNativeContract.collection(address, 'rft', spender, true);421422 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);423 const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, owner);424 await tokenContract.methods.repartition(15).send();425 await tokenContract.methods.approve(spender, 15).send();426427 {428 const result = await contract.methods.burnFrom(owner, token.tokenId).send();429 const event = result.events.Transfer;430 expect(event).to.be.like({431 address: helper.ethAddress.fromCollectionId(collection.collectionId),432 event: 'Transfer',433 returnValues: {434 from: owner,435 to: '0x0000000000000000000000000000000000000000',436 tokenId: token.tokenId.toString(),437 },438 });439 }440441 expect(await collection.getTokenBalance(token.tokenId, {Ethereum: owner})).to.be.eq(0n);442 });443444 itEth('Can perform burnFromCross()', async ({helper}) => {445 const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});446 447 const owner = bob;448 const spender = await helper.eth.createAccountWithBalance(donor, 100n);449450 const token = await collection.mintToken(minter, 100n, {Substrate: owner.address});451452 const address = helper.ethAddress.fromCollectionId(collection.collectionId);453 const contract = helper.ethNativeContract.collection(address, 'rft');454455 await token.repartition(owner, 15n);456 await token.approve(owner, {Ethereum: spender}, 15n);457458 {459 const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);460 const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender});461 const event = result.events.Transfer;462 expect(event).to.be.like({463 address: helper.ethAddress.fromCollectionId(collection.collectionId),464 event: 'Transfer',465 returnValues: {466 from: helper.address.substrateToEth(owner.address),467 to: '0x0000000000000000000000000000000000000000',468 tokenId: token.tokenId.toString(),469 },470 });471 }472473 expect(await collection.getTokenBalance(token.tokenId, {Substrate: owner.address})).to.be.eq(0n);474 });475476 itEth('Can perform transferFromCross()', async ({helper}) => {477 const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});478479 const owner = bob;480 const spender = await helper.eth.createAccountWithBalance(donor, 100n);481 const receiver = charlie;482483 const token = await collection.mintToken(minter, 100n, {Substrate: owner.address});484485 const address = helper.ethAddress.fromCollectionId(collection.collectionId);486 const contract = helper.ethNativeContract.collection(address, 'rft');487488 await token.repartition(owner, 15n);489 await token.approve(owner, {Ethereum: spender}, 15n);490491 {492 const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);493 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);494 const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});495 const event = result.events.Transfer;496 expect(event).to.be.like({497 address: helper.ethAddress.fromCollectionId(collection.collectionId),498 event: 'Transfer',499 returnValues: {500 from: helper.address.substrateToEth(owner.address),501 to: helper.address.substrateToEth(receiver.address),502 tokenId: token.tokenId.toString(),503 },504 });505 }506507 expect(await token.getTop10Owners()).to.be.like([{Substrate: receiver.address}]);508 });509510 itEth('Can perform transfer()', async ({helper}) => {511 const caller = await helper.eth.createAccountWithBalance(donor);512 const receiver = helper.eth.createAccount();513 const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry', '6', '6');514 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);515516 const result = await contract.methods.mint(caller).send();517 const tokenId = result.events.Transfer.returnValues.tokenId;518519 {520 const result = await contract.methods.transfer(receiver, tokenId).send();521522 const event = result.events.Transfer;523 expect(event.address).to.equal(collectionAddress);524 expect(event.returnValues.from).to.equal(caller);525 expect(event.returnValues.to).to.equal(receiver);526 expect(event.returnValues.tokenId).to.equal(tokenId.toString());527 }528529 {530 const balance = await contract.methods.balanceOf(caller).call();531 expect(+balance).to.equal(0);532 }533534 {535 const balance = await contract.methods.balanceOf(receiver).call();536 expect(+balance).to.equal(1);537 }538 });539 540 itEth('Can perform transferCross()', async ({helper}) => {541 const sender = await helper.eth.createAccountWithBalance(donor);542 const receiverEth = await helper.eth.createAccountWithBalance(donor);543 const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);544 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);545546 const collection = await helper.rft.mintCollection(minter, {});547 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);548 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', sender);549550 const token = await collection.mintToken(minter, 50n, {Ethereum: sender});551552 {553 // Can transferCross to ethereum address:554 const result = await collectionEvm.methods.transferCross(receiverCrossEth, token.tokenId).send({from: sender});555 // Check events:556 const event = result.events.Transfer;557 expect(event.address).to.equal(collectionAddress);558 expect(event.returnValues.from).to.equal(sender);559 expect(event.returnValues.to).to.equal(receiverEth);560 expect(event.returnValues.tokenId).to.equal(token.tokenId.toString());561 // Sender's balance decreased:562 const senderBalance = await collectionEvm.methods.balanceOf(sender).call();563 expect(+senderBalance).to.equal(0);564 expect(await token.getBalance({Ethereum: sender})).to.eq(0n);565 // Receiver's balance increased:566 const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call();567 expect(+receiverBalance).to.equal(1);568 expect(await token.getBalance({Ethereum: receiverEth})).to.eq(50n);569 }570 571 {572 // Can transferCross to substrate address:573 const substrateResult = await collectionEvm.methods.transferCross(receiverCrossSub, token.tokenId).send({from: receiverEth});574 // Check events:575 const event = substrateResult.events.Transfer;576 expect(event.address).to.be.equal(collectionAddress);577 expect(event.returnValues.from).to.be.equal(receiverEth);578 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(minter.address));579 expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);580 // Sender's balance decreased:581 const senderBalance = await collectionEvm.methods.balanceOf(receiverEth).call();582 expect(+senderBalance).to.equal(0);583 expect(await token.getBalance({Ethereum: receiverEth})).to.eq(0n);584 // Receiver's balance increased:585 const receiverBalance = await helper.nft.getTokensByAddress(collection.collectionId, {Substrate: minter.address});586 expect(receiverBalance).to.contain(token.tokenId);587 expect(await token.getBalance({Substrate: minter.address})).to.eq(50n);588 }589 });590591 ['transfer', 'transferCross'].map(testCase => itEth(`Cannot ${testCase} non-owned token`, async ({helper}) => {592 const sender = await helper.eth.createAccountWithBalance(donor);593 const tokenOwner = await helper.eth.createAccountWithBalance(donor);594 const receiverSub = minter;595 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);596597 const collection = await helper.rft.mintCollection(minter, {});598 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);599 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', sender);600601 await collection.mintToken(minter, 50n, {Ethereum: sender});602 const nonSendersToken = await collection.mintToken(minter, 50n, {Ethereum: tokenOwner});603604 // Cannot transferCross someone else's token:605 const receiver = testCase === 'transfer' ? helper.address.substrateToEth(receiverSub.address) : receiverCrossSub;606 await expect(collectionEvm.methods[testCase](receiver, nonSendersToken.tokenId).send({from: sender})).to.be.rejected;607 // Cannot transfer token if it does not exist:608 await expect(collectionEvm.methods[testCase](receiver, 999999).send({from: sender})).to.be.rejected;609 }));610611 itEth('transfer event on transfer from partial ownership to full ownership', async ({helper}) => {612 const caller = await helper.eth.createAccountWithBalance(donor);613 const receiver = helper.eth.createAccount();614 const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry-Partial-to-Full', '6', '6');615 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);616617 const result = await contract.methods.mint(caller).send();618 const tokenId = result.events.Transfer.returnValues.tokenId;619620 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);621622 await tokenContract.methods.repartition(2).send();623 await tokenContract.methods.transfer(receiver, 1).send();624625 const events: any = [];626 contract.events.allEvents((_: any, event: any) => {627 events.push(event);628 });629630 await tokenContract.methods.transfer(receiver, 1).send();631 if (events.length == 0) await helper.wait.newBlocks(1);632 const event = events[0];633634 expect(event.address).to.equal(collectionAddress);635 expect(event.returnValues.from).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');636 expect(event.returnValues.to).to.equal(receiver);637 expect(event.returnValues.tokenId).to.equal(tokenId.toString());638 });639640 itEth('transfer event on transfer from full ownership to partial ownership', async ({helper}) => {641 const caller = await helper.eth.createAccountWithBalance(donor);642 const receiver = helper.eth.createAccount();643 const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry-Full-to-Partial', '6', '6');644 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);645646 const result = await contract.methods.mint(caller).send();647 const tokenId = result.events.Transfer.returnValues.tokenId;648649 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);650651 await tokenContract.methods.repartition(2).send();652653 const events: any = [];654 contract.events.allEvents((_: any, event: any) => {655 events.push(event);656 });657658 await tokenContract.methods.transfer(receiver, 1).send();659 if (events.length == 0) await helper.wait.newBlocks(1);660 const event = events[0];661662 expect(event.address).to.equal(collectionAddress);663 expect(event.returnValues.from).to.equal(caller);664 expect(event.returnValues.to).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');665 expect(event.returnValues.tokenId).to.equal(tokenId.toString());666 });667});668669describe('RFT: Fees', () => {670 let donor: IKeyringPair;671672 before(async function() {673 await usingEthPlaygrounds(async (helper, privateKey) => {674 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);675676 donor = await privateKey({filename: __filename});677 });678 });679680 itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {681 const caller = await helper.eth.createAccountWithBalance(donor);682 const receiver = helper.eth.createAccount();683 const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Feeful-Transfer-From', '6', '6');684 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);685686 const result = await contract.methods.mint(caller).send();687 const tokenId = result.events.Transfer.returnValues.tokenId;688689 const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transferFrom(caller, receiver, tokenId).send());690 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));691 expect(cost > 0n);692 });693694 itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {695 const caller = await helper.eth.createAccountWithBalance(donor);696 const receiver = helper.eth.createAccount();697 const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Feeful-Transfer', '6', '6');698 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);699700 const result = await contract.methods.mint(caller).send();701 const tokenId = result.events.Transfer.returnValues.tokenId;702703 const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transfer(receiver, tokenId).send());704 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));705 expect(cost > 0n);706 });707});708709describe('Common metadata', () => {710 let donor: IKeyringPair;711 let alice: IKeyringPair;712713 before(async function() {714 await usingEthPlaygrounds(async (helper, privateKey) => {715 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);716717 donor = await privateKey({filename: __filename});718 [alice] = await helper.arrange.createAccounts([20n], donor);719 });720 });721722 itEth('Returns collection name', async ({helper}) => {723 const caller = helper.eth.createAccount();724 const tokenPropertyPermissions = [{725 key: 'URI',726 permission: {727 mutable: true,728 collectionAdmin: true,729 tokenOwner: false,730 },731 }];732 const collection = await helper.rft.mintCollection(733 alice,734 {735 name: 'Leviathan',736 tokenPrefix: '11',737 properties: [{key: 'ERC721Metadata', value: '1'}],738 tokenPropertyPermissions,739 },740 );741742 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'rft', caller);743 const name = await contract.methods.name().call();744 expect(name).to.equal('Leviathan');745 });746747 itEth('Returns symbol name', async ({helper}) => {748 const caller = await helper.eth.createAccountWithBalance(donor);749 const tokenPropertyPermissions = [{750 key: 'URI',751 permission: {752 mutable: true,753 collectionAdmin: true,754 tokenOwner: false,755 },756 }];757 const {collectionId} = await helper.rft.mintCollection(758 alice,759 {760 name: 'Leviathan',761 tokenPrefix: '12',762 properties: [{key: 'ERC721Metadata', value: '1'}],763 tokenPropertyPermissions,764 },765 );766767 const contract = helper.ethNativeContract.collectionById(collectionId, 'rft', caller);768 const symbol = await contract.methods.symbol().call();769 expect(symbol).to.equal('12');770 });771});772773describe('Negative tests', () => {774 let donor: IKeyringPair;775 let minter: IKeyringPair;776 let alice: IKeyringPair;777778 before(async function() {779 await usingEthPlaygrounds(async (helper, privateKey) => {780 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);781 782 donor = await privateKey({filename: __filename});783 [minter, alice] = await helper.arrange.createAccounts([100n, 100n], donor);784 });785 });786787 itEth('[negative] Cant perform burn without approval', async ({helper}) => {788 const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});789790 const owner = await helper.eth.createAccountWithBalance(donor, 100n);791 const spender = await helper.eth.createAccountWithBalance(donor, 100n);792793 const token = await collection.mintToken(minter, 100n, {Ethereum: owner});794795 const address = helper.ethAddress.fromCollectionId(collection.collectionId);796 const contract = helper.ethNativeContract.collection(address, 'rft');797798 const ownerCross = helper.ethCrossAccount.fromAddress(owner);799800 await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;801802 await contract.methods.setApprovalForAll(spender, true).send({from: owner});803 await contract.methods.setApprovalForAll(spender, false).send({from: owner});804805 await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;806 });807808 itEth('[negative] Cant perform transfer without approval', async ({helper}) => {809 const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});810 const owner = await helper.eth.createAccountWithBalance(donor, 100n);811 const receiver = alice;812813 const spender = await helper.eth.createAccountWithBalance(donor, 100n);814815 const token = await collection.mintToken(minter, 100n, {Ethereum: owner});816817 const address = helper.ethAddress.fromCollectionId(collection.collectionId);818 const contract = helper.ethNativeContract.collection(address, 'rft');819820 const ownerCross = helper.ethCrossAccount.fromAddress(owner);821 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);822 823 await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;824825 await contract.methods.setApprovalForAll(spender, true).send({from: owner});826 await contract.methods.setApprovalForAll(spender, false).send({from: owner});827 828 await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;829 });830});1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {Pallets, requirePalletsOrSkip} from '../util';18import {expect, itEth, usingEthPlaygrounds} from './util';19import {IKeyringPair} from '@polkadot/types/types';20import {ITokenPropertyPermission} from '../util/playgrounds/types';2122describe('Refungible: Information getting', () => {23 let donor: IKeyringPair;2425 before(async function() {26 await usingEthPlaygrounds(async (helper, privateKey) => {27 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);2829 donor = await privateKey({filename: __filename});30 });31 });3233 itEth('totalSupply', async ({helper}) => {34 const caller = await helper.eth.createAccountWithBalance(donor);35 const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'TotalSupply', '6', '6');36 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);3738 await contract.methods.mint(caller).send();3940 const totalSupply = await contract.methods.totalSupply().call();41 expect(totalSupply).to.equal('1');42 });4344 itEth('balanceOf', async ({helper}) => {45 const caller = await helper.eth.createAccountWithBalance(donor);46 const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'BalanceOf', '6', '6');47 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);4849 await contract.methods.mint(caller).send();50 await contract.methods.mint(caller).send();51 await contract.methods.mint(caller).send();5253 const balance = await contract.methods.balanceOf(caller).call();54 expect(balance).to.equal('3');55 });5657 itEth('ownerOf', async ({helper}) => {58 const caller = await helper.eth.createAccountWithBalance(donor);59 const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'OwnerOf', '6', '6');60 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);6162 const result = await contract.methods.mint(caller).send();63 const tokenId = result.events.Transfer.returnValues.tokenId;6465 const owner = await contract.methods.ownerOf(tokenId).call();66 expect(owner).to.equal(caller);67 });6869 itEth('ownerOf after burn', async ({helper}) => {70 const caller = await helper.eth.createAccountWithBalance(donor);71 const receiver = helper.eth.createAccount();72 const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'OwnerOf-AfterBurn', '6', '6');73 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);7475 const result = await contract.methods.mint(caller).send();76 const tokenId = result.events.Transfer.returnValues.tokenId;77 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);7879 await tokenContract.methods.repartition(2).send();80 await tokenContract.methods.transfer(receiver, 1).send();8182 await tokenContract.methods.burnFrom(caller, 1).send();8384 const owner = await contract.methods.ownerOf(tokenId).call();85 expect(owner).to.equal(receiver);86 });8788 itEth('ownerOf for partial ownership', async ({helper}) => {89 const caller = await helper.eth.createAccountWithBalance(donor);90 const receiver = helper.eth.createAccount();91 const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Partial-OwnerOf', '6', '6');92 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);9394 const result = await contract.methods.mint(caller).send();95 const tokenId = result.events.Transfer.returnValues.tokenId;96 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);9798 await tokenContract.methods.repartition(2).send();99 await tokenContract.methods.transfer(receiver, 1).send();100101 const owner = await contract.methods.ownerOf(tokenId).call();102 expect(owner).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');103 });104});105106describe('Refungible: Plain calls', () => {107 let donor: IKeyringPair;108 let minter: IKeyringPair;109 let bob: IKeyringPair;110 let charlie: IKeyringPair;111112 before(async function() {113 await usingEthPlaygrounds(async (helper, privateKey) => {114 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);115116 donor = await privateKey({filename: __filename});117 [minter, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);118 });119 });120121 itEth('Can perform mint() & crossOwnerOf()', async ({helper}) => {122 const owner = await helper.eth.createAccountWithBalance(donor);123 const receiver = helper.eth.createAccount();124 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Minty', '6', '6', '');125 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);126127 const result = await contract.methods.mintWithTokenURI(receiver, 'Test URI').send();128129 const event = result.events.Transfer;130 expect(event.address).to.equal(collectionAddress);131 expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');132 expect(event.returnValues.to).to.equal(receiver);133 const tokenId = event.returnValues.tokenId;134 expect(tokenId).to.be.equal('1');135136 expect(await contract.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);137 expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');138 });139 140 [141 'substrate' as const,142 'ethereum' as const,143 ].map(testCase => {144 itEth(`Can perform mintCross() for ${testCase} address`, async ({helper}) => {145 const collectionAdmin = await helper.eth.createAccountWithBalance(donor);146147 const receiverEth = helper.eth.createAccount();148 const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);149 const receiverSub = bob;150 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);151152 const properties = Array(5).fill(0).map((_, i) => { return {key: `key_${i}`, value: Buffer.from(`value_${i}`)}; });153 const permissions: ITokenPropertyPermission[] = properties.map(p => { return {key: p.key, permission: {154 tokenOwner: false,155 collectionAdmin: true,156 mutable: false}};157 });158 159 160 const collection = await helper.rft.mintCollection(minter, {161 tokenPrefix: 'ethp',162 tokenPropertyPermissions: permissions,163 });164 await collection.addAdmin(minter, {Ethereum: collectionAdmin});165 166 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);167 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', collectionAdmin, true);168 let expectedTokenId = await contract.methods.nextTokenId().call();169 let result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, []).send();170 let tokenId = result.events.Transfer.returnValues.tokenId;171 expect(tokenId).to.be.equal(expectedTokenId);172173 let event = result.events.Transfer;174 expect(event.address).to.be.equal(collectionAddress);175 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');176 expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address));177 expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);178 179 expectedTokenId = await contract.methods.nextTokenId().call();180 result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, properties).send();181 event = result.events.Transfer;182 expect(event.address).to.be.equal(collectionAddress);183 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');184 expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address));185 expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);186 187 tokenId = result.events.Transfer.returnValues.tokenId;188189 expect(tokenId).to.be.equal(expectedTokenId);190 191 expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties192 .map(p => { return helper.ethProperty.property(p.key, p.value.toString()); }));193194 expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId))195 .to.deep.eq(testCase === 'ethereum' ? {Ethereum: receiverEth.toLowerCase()} : {Substrate: receiverSub.address});196 });197 });198199 itEth.skip('Can perform mintBulk()', async ({helper}) => {200 const owner = await helper.eth.createAccountWithBalance(donor);201 const receiver = helper.eth.createAccount();202 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'MintBulky', '6', '6', '');203 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);204205 {206 const nextTokenId = await contract.methods.nextTokenId().call();207 expect(nextTokenId).to.be.equal('1');208 const result = await contract.methods.mintBulkWithTokenURI(209 receiver,210 [211 [nextTokenId, 'Test URI 0'],212 [+nextTokenId + 1, 'Test URI 1'],213 [+nextTokenId + 2, 'Test URI 2'],214 ],215 ).send();216217 const events = result.events.Transfer;218 for (let i = 0; i < 2; i++) {219 const event = events[i];220 expect(event.address).to.equal(collectionAddress);221 expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');222 expect(event.returnValues.to).to.equal(receiver);223 expect(event.returnValues.tokenId).to.equal(String(+nextTokenId + i));224 }225226 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI 0');227 expect(await contract.methods.tokenURI(+nextTokenId + 1).call()).to.be.equal('Test URI 1');228 expect(await contract.methods.tokenURI(+nextTokenId + 2).call()).to.be.equal('Test URI 2');229 }230 });231232 itEth('Can perform setApprovalForAll()', async ({helper}) => {233 const owner = await helper.eth.createAccountWithBalance(donor);234 const operator = helper.eth.createAccount();235236 const collection = await helper.rft.mintCollection(minter, {});237238 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);239 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);240241 const approvedBefore = await contract.methods.isApprovedForAll(owner, operator).call();242 expect(approvedBefore).to.be.equal(false);243244 {245 const result = await contract.methods.setApprovalForAll(operator, true).send({from: owner});246247 expect(result.events.ApprovalForAll).to.be.like({248 address: collectionAddress,249 event: 'ApprovalForAll',250 returnValues: {251 owner,252 operator,253 approved: true,254 },255 });256257 const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();258 expect(approvedAfter).to.be.equal(true);259 }260261 {262 const result = await contract.methods.setApprovalForAll(operator, false).send({from: owner});263264 expect(result.events.ApprovalForAll).to.be.like({265 address: collectionAddress,266 event: 'ApprovalForAll',267 returnValues: {268 owner,269 operator,270 approved: false,271 },272 });273274 const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();275 expect(approvedAfter).to.be.equal(false);276 }277 });278279 itEth('Can perform burn with ApprovalForAll', async ({helper}) => {280 const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});281282 const owner = await helper.eth.createAccountWithBalance(donor);283 const operator = await helper.eth.createAccountWithBalance(donor, 100n);284285 const token = await collection.mintToken(minter, 100n, {Ethereum: owner});286287 const address = helper.ethAddress.fromCollectionId(collection.collectionId);288 const contract = helper.ethNativeContract.collection(address, 'rft');289290 {291 await contract.methods.setApprovalForAll(operator, true).send({from: owner});292 const ownerCross = helper.ethCrossAccount.fromAddress(owner);293 const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: operator});294 const events = result.events.Transfer;295296 expect(events).to.be.like({297 address,298 event: 'Transfer',299 returnValues: {300 from: owner,301 to: '0x0000000000000000000000000000000000000000',302 tokenId: token.tokenId.toString(),303 },304 });305 }306 });307308 itEth('Can perform burn with approve and approvalForAll', async ({helper}) => {309 const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});310311 const owner = await helper.eth.createAccountWithBalance(donor);312 const operator = await helper.eth.createAccountWithBalance(donor, 100n);313314 const token = await collection.mintToken(minter, 100n, {Ethereum: owner});315316 const address = helper.ethAddress.fromCollectionId(collection.collectionId);317 const contract = helper.ethNativeContract.collection(address, 'rft');318319 const rftToken = helper.ethNativeContract.rftTokenById(token.collectionId, token.tokenId, owner);320321 {322 await rftToken.methods.approve(operator, 15n).send({from: owner});323 await contract.methods.setApprovalForAll(operator, true).send({from: owner});324 await rftToken.methods.burnFrom(owner, 10n).send({from: operator});325 const allowance = await rftToken.methods.allowance(owner, operator).call();326 expect(allowance).to.be.equal('5');327 }328 });329 330 itEth('Can perform transfer with ApprovalForAll', async ({helper}) => {331 const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});332333 const owner = await helper.eth.createAccountWithBalance(donor);334 const operator = await helper.eth.createAccountWithBalance(donor);335 const receiver = charlie;336337 const token = await collection.mintToken(minter, 100n, {Ethereum: owner});338339 const address = helper.ethAddress.fromCollectionId(collection.collectionId);340 const contract = helper.ethNativeContract.collection(address, 'rft');341342 {343 await contract.methods.setApprovalForAll(operator, true).send({from: owner});344 const ownerCross = helper.ethCrossAccount.fromAddress(owner);345 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);346 const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: operator});347 const event = result.events.Transfer;348 expect(event).to.be.like({349 address: helper.ethAddress.fromCollectionId(collection.collectionId),350 event: 'Transfer',351 returnValues: {352 from: owner,353 to: helper.address.substrateToEth(receiver.address),354 tokenId: token.tokenId.toString(),355 },356 });357 }358359 expect(await token.getTop10Owners()).to.be.like([{Substrate: receiver.address}]);360 });361362 itEth('Can perform burn()', async ({helper}) => {363 const caller = await helper.eth.createAccountWithBalance(donor);364 const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Burny', '6', '6');365 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);366367 const result = await contract.methods.mint(caller).send();368 const tokenId = result.events.Transfer.returnValues.tokenId;369 {370 const result = await contract.methods.burn(tokenId).send();371 const event = result.events.Transfer;372 expect(event.address).to.equal(collectionAddress);373 expect(event.returnValues.from).to.equal(caller);374 expect(event.returnValues.to).to.equal('0x0000000000000000000000000000000000000000');375 expect(event.returnValues.tokenId).to.equal(tokenId.toString());376 }377 });378379 itEth('Can perform transferFrom()', async ({helper}) => {380 const caller = await helper.eth.createAccountWithBalance(donor);381 const receiver = helper.eth.createAccount();382 const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'TransferFromy', '6', '6');383 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);384385 const result = await contract.methods.mint(caller).send();386 const tokenId = result.events.Transfer.returnValues.tokenId;387388 const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);389390 const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, caller);391 await tokenContract.methods.repartition(15).send();392393 {394 const tokenEvents: any = [];395 tokenContract.events.allEvents((_: any, event: any) => {396 tokenEvents.push(event);397 });398 const result = await contract.methods.transferFrom(caller, receiver, tokenId).send();399 if (tokenEvents.length == 0) await helper.wait.newBlocks(1);400401 let event = result.events.Transfer;402 expect(event.address).to.equal(collectionAddress);403 expect(event.returnValues.from).to.equal(caller);404 expect(event.returnValues.to).to.equal(receiver);405 expect(event.returnValues.tokenId).to.equal(tokenId.toString());406407 event = tokenEvents[0];408 expect(event.address).to.equal(tokenAddress);409 expect(event.returnValues.from).to.equal(caller);410 expect(event.returnValues.to).to.equal(receiver);411 expect(event.returnValues.value).to.equal('15');412 }413414 {415 const balance = await contract.methods.balanceOf(receiver).call();416 expect(+balance).to.equal(1);417 }418419 {420 const balance = await contract.methods.balanceOf(caller).call();421 expect(+balance).to.equal(0);422 }423 });424425 // Soft-deprecated426 itEth('Can perform burnFrom()', async ({helper}) => {427 const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});428429 const owner = await helper.eth.createAccountWithBalance(donor, 100n);430 const spender = await helper.eth.createAccountWithBalance(donor, 100n);431432 const token = await collection.mintToken(minter, 100n, {Ethereum: owner});433434 const address = helper.ethAddress.fromCollectionId(collection.collectionId);435 const contract = helper.ethNativeContract.collection(address, 'rft', spender, true);436437 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);438 const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, owner);439 await tokenContract.methods.repartition(15).send();440 await tokenContract.methods.approve(spender, 15).send();441442 {443 const result = await contract.methods.burnFrom(owner, token.tokenId).send();444 const event = result.events.Transfer;445 expect(event).to.be.like({446 address: helper.ethAddress.fromCollectionId(collection.collectionId),447 event: 'Transfer',448 returnValues: {449 from: owner,450 to: '0x0000000000000000000000000000000000000000',451 tokenId: token.tokenId.toString(),452 },453 });454 }455456 expect(await collection.getTokenBalance(token.tokenId, {Ethereum: owner})).to.be.eq(0n);457 });458459 itEth('Can perform burnFromCross()', async ({helper}) => {460 const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});461 462 const owner = bob;463 const spender = await helper.eth.createAccountWithBalance(donor, 100n);464465 const token = await collection.mintToken(minter, 100n, {Substrate: owner.address});466467 const address = helper.ethAddress.fromCollectionId(collection.collectionId);468 const contract = helper.ethNativeContract.collection(address, 'rft');469470 await token.repartition(owner, 15n);471 await token.approve(owner, {Ethereum: spender}, 15n);472473 {474 const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);475 const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender});476 const event = result.events.Transfer;477 expect(event).to.be.like({478 address: helper.ethAddress.fromCollectionId(collection.collectionId),479 event: 'Transfer',480 returnValues: {481 from: helper.address.substrateToEth(owner.address),482 to: '0x0000000000000000000000000000000000000000',483 tokenId: token.tokenId.toString(),484 },485 });486 }487488 expect(await collection.getTokenBalance(token.tokenId, {Substrate: owner.address})).to.be.eq(0n);489 });490491 itEth('Can perform transferFromCross()', async ({helper}) => {492 const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});493494 const owner = bob;495 const spender = await helper.eth.createAccountWithBalance(donor, 100n);496 const receiver = charlie;497498 const token = await collection.mintToken(minter, 100n, {Substrate: owner.address});499500 const address = helper.ethAddress.fromCollectionId(collection.collectionId);501 const contract = helper.ethNativeContract.collection(address, 'rft');502503 await token.repartition(owner, 15n);504 await token.approve(owner, {Ethereum: spender}, 15n);505506 {507 const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);508 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);509 const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});510 const event = result.events.Transfer;511 expect(event).to.be.like({512 address: helper.ethAddress.fromCollectionId(collection.collectionId),513 event: 'Transfer',514 returnValues: {515 from: helper.address.substrateToEth(owner.address),516 to: helper.address.substrateToEth(receiver.address),517 tokenId: token.tokenId.toString(),518 },519 });520 }521522 expect(await token.getTop10Owners()).to.be.like([{Substrate: receiver.address}]);523 });524525 itEth('Can perform transfer()', async ({helper}) => {526 const caller = await helper.eth.createAccountWithBalance(donor);527 const receiver = helper.eth.createAccount();528 const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry', '6', '6');529 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);530531 const result = await contract.methods.mint(caller).send();532 const tokenId = result.events.Transfer.returnValues.tokenId;533534 {535 const result = await contract.methods.transfer(receiver, tokenId).send();536537 const event = result.events.Transfer;538 expect(event.address).to.equal(collectionAddress);539 expect(event.returnValues.from).to.equal(caller);540 expect(event.returnValues.to).to.equal(receiver);541 expect(event.returnValues.tokenId).to.equal(tokenId.toString());542 }543544 {545 const balance = await contract.methods.balanceOf(caller).call();546 expect(+balance).to.equal(0);547 }548549 {550 const balance = await contract.methods.balanceOf(receiver).call();551 expect(+balance).to.equal(1);552 }553 });554 555 itEth('Can perform transferCross()', async ({helper}) => {556 const sender = await helper.eth.createAccountWithBalance(donor);557 const receiverEth = await helper.eth.createAccountWithBalance(donor);558 const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);559 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);560561 const collection = await helper.rft.mintCollection(minter, {});562 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);563 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', sender);564565 const token = await collection.mintToken(minter, 50n, {Ethereum: sender});566567 {568 // Can transferCross to ethereum address:569 const result = await collectionEvm.methods.transferCross(receiverCrossEth, token.tokenId).send({from: sender});570 // Check events:571 const event = result.events.Transfer;572 expect(event.address).to.equal(collectionAddress);573 expect(event.returnValues.from).to.equal(sender);574 expect(event.returnValues.to).to.equal(receiverEth);575 expect(event.returnValues.tokenId).to.equal(token.tokenId.toString());576 // Sender's balance decreased:577 const senderBalance = await collectionEvm.methods.balanceOf(sender).call();578 expect(+senderBalance).to.equal(0);579 expect(await token.getBalance({Ethereum: sender})).to.eq(0n);580 // Receiver's balance increased:581 const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call();582 expect(+receiverBalance).to.equal(1);583 expect(await token.getBalance({Ethereum: receiverEth})).to.eq(50n);584 }585 586 {587 // Can transferCross to substrate address:588 const substrateResult = await collectionEvm.methods.transferCross(receiverCrossSub, token.tokenId).send({from: receiverEth});589 // Check events:590 const event = substrateResult.events.Transfer;591 expect(event.address).to.be.equal(collectionAddress);592 expect(event.returnValues.from).to.be.equal(receiverEth);593 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(minter.address));594 expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);595 // Sender's balance decreased:596 const senderBalance = await collectionEvm.methods.balanceOf(receiverEth).call();597 expect(+senderBalance).to.equal(0);598 expect(await token.getBalance({Ethereum: receiverEth})).to.eq(0n);599 // Receiver's balance increased:600 const receiverBalance = await helper.nft.getTokensByAddress(collection.collectionId, {Substrate: minter.address});601 expect(receiverBalance).to.contain(token.tokenId);602 expect(await token.getBalance({Substrate: minter.address})).to.eq(50n);603 }604 });605606 ['transfer', 'transferCross'].map(testCase => itEth(`Cannot ${testCase} non-owned token`, async ({helper}) => {607 const sender = await helper.eth.createAccountWithBalance(donor);608 const tokenOwner = await helper.eth.createAccountWithBalance(donor);609 const receiverSub = minter;610 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);611612 const collection = await helper.rft.mintCollection(minter, {});613 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);614 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', sender);615616 await collection.mintToken(minter, 50n, {Ethereum: sender});617 const nonSendersToken = await collection.mintToken(minter, 50n, {Ethereum: tokenOwner});618619 // Cannot transferCross someone else's token:620 const receiver = testCase === 'transfer' ? helper.address.substrateToEth(receiverSub.address) : receiverCrossSub;621 await expect(collectionEvm.methods[testCase](receiver, nonSendersToken.tokenId).send({from: sender})).to.be.rejected;622 // Cannot transfer token if it does not exist:623 await expect(collectionEvm.methods[testCase](receiver, 999999).send({from: sender})).to.be.rejected;624 }));625626 itEth('transfer event on transfer from partial ownership to full ownership', async ({helper}) => {627 const caller = await helper.eth.createAccountWithBalance(donor);628 const receiver = helper.eth.createAccount();629 const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry-Partial-to-Full', '6', '6');630 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);631632 const result = await contract.methods.mint(caller).send();633 const tokenId = result.events.Transfer.returnValues.tokenId;634635 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);636637 await tokenContract.methods.repartition(2).send();638 await tokenContract.methods.transfer(receiver, 1).send();639640 const events: any = [];641 contract.events.allEvents((_: any, event: any) => {642 events.push(event);643 });644645 await tokenContract.methods.transfer(receiver, 1).send();646 if (events.length == 0) await helper.wait.newBlocks(1);647 const event = events[0];648649 expect(event.address).to.equal(collectionAddress);650 expect(event.returnValues.from).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');651 expect(event.returnValues.to).to.equal(receiver);652 expect(event.returnValues.tokenId).to.equal(tokenId.toString());653 });654655 itEth('transfer event on transfer from full ownership to partial ownership', async ({helper}) => {656 const caller = await helper.eth.createAccountWithBalance(donor);657 const receiver = helper.eth.createAccount();658 const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry-Full-to-Partial', '6', '6');659 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);660661 const result = await contract.methods.mint(caller).send();662 const tokenId = result.events.Transfer.returnValues.tokenId;663664 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);665666 await tokenContract.methods.repartition(2).send();667668 const events: any = [];669 contract.events.allEvents((_: any, event: any) => {670 events.push(event);671 });672673 await tokenContract.methods.transfer(receiver, 1).send();674 if (events.length == 0) await helper.wait.newBlocks(1);675 const event = events[0];676677 expect(event.address).to.equal(collectionAddress);678 expect(event.returnValues.from).to.equal(caller);679 expect(event.returnValues.to).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');680 expect(event.returnValues.tokenId).to.equal(tokenId.toString());681 });682});683684describe('RFT: Fees', () => {685 let donor: IKeyringPair;686687 before(async function() {688 await usingEthPlaygrounds(async (helper, privateKey) => {689 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);690691 donor = await privateKey({filename: __filename});692 });693 });694695 itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {696 const caller = await helper.eth.createAccountWithBalance(donor);697 const receiver = helper.eth.createAccount();698 const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Feeful-Transfer-From', '6', '6');699 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);700701 const result = await contract.methods.mint(caller).send();702 const tokenId = result.events.Transfer.returnValues.tokenId;703704 const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transferFrom(caller, receiver, tokenId).send());705 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));706 expect(cost > 0n);707 });708709 itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {710 const caller = await helper.eth.createAccountWithBalance(donor);711 const receiver = helper.eth.createAccount();712 const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Feeful-Transfer', '6', '6');713 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);714715 const result = await contract.methods.mint(caller).send();716 const tokenId = result.events.Transfer.returnValues.tokenId;717718 const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transfer(receiver, tokenId).send());719 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));720 expect(cost > 0n);721 });722});723724describe('Common metadata', () => {725 let donor: IKeyringPair;726 let alice: IKeyringPair;727728 before(async function() {729 await usingEthPlaygrounds(async (helper, privateKey) => {730 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);731732 donor = await privateKey({filename: __filename});733 [alice] = await helper.arrange.createAccounts([20n], donor);734 });735 });736737 itEth('Returns collection name', async ({helper}) => {738 const caller = helper.eth.createAccount();739 const tokenPropertyPermissions = [{740 key: 'URI',741 permission: {742 mutable: true,743 collectionAdmin: true,744 tokenOwner: false,745 },746 }];747 const collection = await helper.rft.mintCollection(748 alice,749 {750 name: 'Leviathan',751 tokenPrefix: '11',752 properties: [{key: 'ERC721Metadata', value: '1'}],753 tokenPropertyPermissions,754 },755 );756757 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'rft', caller);758 const name = await contract.methods.name().call();759 expect(name).to.equal('Leviathan');760 });761762 itEth('Returns symbol name', async ({helper}) => {763 const caller = await helper.eth.createAccountWithBalance(donor);764 const tokenPropertyPermissions = [{765 key: 'URI',766 permission: {767 mutable: true,768 collectionAdmin: true,769 tokenOwner: false,770 },771 }];772 const {collectionId} = await helper.rft.mintCollection(773 alice,774 {775 name: 'Leviathan',776 tokenPrefix: '12',777 properties: [{key: 'ERC721Metadata', value: '1'}],778 tokenPropertyPermissions,779 },780 );781782 const contract = helper.ethNativeContract.collectionById(collectionId, 'rft', caller);783 const symbol = await contract.methods.symbol().call();784 expect(symbol).to.equal('12');785 });786});787788describe('Negative tests', () => {789 let donor: IKeyringPair;790 let minter: IKeyringPair;791 let alice: IKeyringPair;792793 before(async function() {794 await usingEthPlaygrounds(async (helper, privateKey) => {795 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);796 797 donor = await privateKey({filename: __filename});798 [minter, alice] = await helper.arrange.createAccounts([100n, 100n], donor);799 });800 });801802 itEth('[negative] Cant perform burn without approval', async ({helper}) => {803 const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});804805 const owner = await helper.eth.createAccountWithBalance(donor, 100n);806 const spender = await helper.eth.createAccountWithBalance(donor, 100n);807808 const token = await collection.mintToken(minter, 100n, {Ethereum: owner});809810 const address = helper.ethAddress.fromCollectionId(collection.collectionId);811 const contract = helper.ethNativeContract.collection(address, 'rft');812813 const ownerCross = helper.ethCrossAccount.fromAddress(owner);814815 await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;816817 await contract.methods.setApprovalForAll(spender, true).send({from: owner});818 await contract.methods.setApprovalForAll(spender, false).send({from: owner});819820 await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;821 });822823 itEth('[negative] Cant perform transfer without approval', async ({helper}) => {824 const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});825 const owner = await helper.eth.createAccountWithBalance(donor, 100n);826 const receiver = alice;827828 const spender = await helper.eth.createAccountWithBalance(donor, 100n);829830 const token = await collection.mintToken(minter, 100n, {Ethereum: owner});831832 const address = helper.ethAddress.fromCollectionId(collection.collectionId);833 const contract = helper.ethNativeContract.collection(address, 'rft');834835 const ownerCross = helper.ethCrossAccount.fromAddress(owner);836 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);837 838 await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;839840 await contract.methods.setApprovalForAll(spender, true).send({from: owner});841 await contract.methods.setApprovalForAll(spender, false).send({from: owner});842 843 await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;844 });845});tests/src/eth/reFungibleToken.test.tsdiffbeforeafterboth--- a/tests/src/eth/reFungibleToken.test.ts
+++ b/tests/src/eth/reFungibleToken.test.ts
@@ -207,7 +207,20 @@
//TO-DO expect with future allowanceCross(owner, spenderCrossEth).call()
}
});
-
+
+ itEth('Non-owner and non admin cannot approveCross', async ({helper}) => {
+ const nonOwner = await helper.eth.createAccountWithBalance(donor);
+ const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner);
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const collection = await helper.rft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'});
+ const token = await collection.mintToken(alice, 100n, {Ethereum: owner});
+
+ const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);
+ const tokenEvm = helper.ethNativeContract.rftToken(tokenAddress, owner);
+
+ await expect(tokenEvm.methods.approveCross(nonOwnerCross, 20).call({from: nonOwner})).to.be.rejectedWith('CantApproveMoreThanOwned');
+ });
+
[
'transferFrom',
'transferFromCross',
tests/src/eth/tokenProperties.test.tsdiffbeforeafterboth--- a/tests/src/eth/tokenProperties.test.ts
+++ b/tests/src/eth/tokenProperties.test.ts
@@ -37,7 +37,7 @@
{mode: 'nft' as const, requiredPallets: []},
{mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
].map(testCase =>
- itEth.ifWithPallets(`[${testCase.mode}] Set and get token property permissions`, testCase.requiredPallets, async({helper}) => {
+ itEth.ifWithPallets(`[${testCase.mode}] Can set all possible token property permissions`, testCase.requiredPallets, async({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const caller = await helper.ethCrossAccount.createAccountWithBalance(donor);
for(const [mutable,collectionAdmin, tokenOwner] of cartesian([], [false, true], [false, true], [false, true])) {
@@ -72,11 +72,11 @@
{mode: 'nft' as const, requiredPallets: []},
{mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
].map(testCase =>
- itEth.ifWithPallets(`[${testCase.mode}] Set and get multiple token property permissions as owner`, testCase.requiredPallets, async({helper}) => {
+ itEth.ifWithPallets(`[${testCase.mode}] Can set multiple token property permissions as owner`, testCase.requiredPallets, async({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const {collectionId, collectionAddress} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');
- const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+ const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
await collection.methods.setTokenPropertyPermissions([
['testKey_0', [
@@ -128,19 +128,18 @@
[EthTokenPermissions.CollectionAdmin.toString(), false]],
],
]);
-
}));
[
{mode: 'nft' as const, requiredPallets: []},
{mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
].map(testCase =>
- itEth.ifWithPallets(`[${testCase.mode}] Set and get multiple token property permissions as admin`, testCase.requiredPallets, async({helper}) => {
+ itEth.ifWithPallets(`[${testCase.mode}] Can set multiple token property permissions as admin`, testCase.requiredPallets, async({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const caller = await helper.ethCrossAccount.createAccountWithBalance(donor);
const {collectionId, collectionAddress} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');
- const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+ const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
await collection.methods.addCollectionAdminCross(caller).send({from: owner});
await collection.methods.setTokenPropertyPermissions([
@@ -452,12 +451,12 @@
{mode: 'nft' as const, requiredPallets: []},
{mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
].map(testCase =>
- itEth.ifWithPallets(`[${testCase.mode}] Cant set token property permissions as non owner or admin`, testCase.requiredPallets, async({helper}) => {
+ itEth.ifWithPallets(`[${testCase.mode}] Cannot set token property permissions as non owner or admin`, testCase.requiredPallets, async({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const caller = await helper.eth.createAccountWithBalance(donor);
const {collectionAddress} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');
- const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+ const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
await expect(collection.methods.setTokenPropertyPermissions([
['testKey_0', [
@@ -472,11 +471,11 @@
{mode: 'nft' as const, requiredPallets: []},
{mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
].map(testCase =>
- itEth.ifWithPallets(`[${testCase.mode}] Cant set token property permissions with invalid character`, testCase.requiredPallets, async({helper}) => {
+ itEth.ifWithPallets(`[${testCase.mode}] Cannot set token property permissions with invalid character`, testCase.requiredPallets, async({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const {collectionAddress} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');
- const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+ const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
await expect(collection.methods.setTokenPropertyPermissions([
// "Space" is invalid character
@@ -487,7 +486,73 @@
],
]).call({from: owner})).to.be.rejectedWith('InvalidCharacterInPropertyKey');
}));
-
+
+ [
+ {mode: 'nft' as const, requiredPallets: []},
+ {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+ ].map(testCase =>
+ itEth.ifWithPallets(`[${testCase.mode}] Can reconfigure token property permissions to stricter ones`, testCase.requiredPallets, async({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+
+ const {collectionAddress, collectionId} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');
+ const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+
+ // 1. Owner sets strict property-permissions:
+ await collection.methods.setTokenPropertyPermissions([
+ ['testKey', [
+ [EthTokenPermissions.Mutable, true],
+ [EthTokenPermissions.TokenOwner, true],
+ [EthTokenPermissions.CollectionAdmin, true]],
+ ],
+ ]).send({from: owner});
+
+ // 2. Owner can set stricter property-permissions:
+ for(const values of [[true, true, false], [true, false, false], [false, false, false]]) {
+ await collection.methods.setTokenPropertyPermissions([
+ ['testKey', [
+ [EthTokenPermissions.Mutable, values[0]],
+ [EthTokenPermissions.TokenOwner, values[1]],
+ [EthTokenPermissions.CollectionAdmin, values[2]]],
+ ],
+ ]).send({from: owner});
+ }
+
+ expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([{
+ key: 'testKey',
+ permission: {mutable: false, collectionAdmin: false, tokenOwner: false},
+ }]);
+ }));
+
+ [
+ {mode: 'nft' as const, requiredPallets: []},
+ {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+ ].map(testCase =>
+ itEth.ifWithPallets(`[${testCase.mode}] Cannot reconfigure token property permissions to less strict ones`, testCase.requiredPallets, async({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+
+ const {collectionAddress} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');
+ const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+
+ // 1. Owner sets strict property-permissions:
+ await collection.methods.setTokenPropertyPermissions([
+ ['testKey', [
+ [EthTokenPermissions.Mutable, false],
+ [EthTokenPermissions.TokenOwner, false],
+ [EthTokenPermissions.CollectionAdmin, false]],
+ ],
+ ]).send({from: owner});
+
+ // 2. Owner cannot set less strict property-permissions:
+ for(const values of [[true, false, false], [false, true, false], [false, false, true]]) {
+ await expect(collection.methods.setTokenPropertyPermissions([
+ ['testKey', [
+ [EthTokenPermissions.Mutable, values[0]],
+ [EthTokenPermissions.TokenOwner, values[1]],
+ [EthTokenPermissions.CollectionAdmin, values[2]]],
+ ],
+ ]).call({from: owner})).to.be.rejectedWith('NoPermission');
+ }
+ }));
});
tests/src/util/index.tsdiffbeforeafterboth--- a/tests/src/util/index.ts
+++ b/tests/src/util/index.ts
@@ -6,6 +6,7 @@
import {IKeyringPair} from '@polkadot/types/types/interfaces';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
+import chaiSubset from 'chai-subset';
import {Context} from 'mocha';
import config from '../config';
import {ChainHelperBase} from './playgrounds/unique';
@@ -13,6 +14,7 @@
import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper} from './playgrounds/unique.dev';
chai.use(chaiAsPromised);
+chai.use(chaiSubset);
export const expect = chai.expect;
const getTestHash = (filename: string) => {
tests/src/vesting.test.tsdiffbeforeafterboth--- a/tests/src/vesting.test.ts
+++ b/tests/src/vesting.test.ts
@@ -32,8 +32,12 @@
// arrange
const [sender, recepient] = await helper.arrange.createAccounts([1000n, 1n], donor);
const currentRelayBlock = await helper.chain.getRelayBlockNumber();
- const schedule1 = {start: currentRelayBlock + 4n, period: 4n, periodCount: 2n, perPeriod: 50n * nominal};
- const schedule2 = {start: currentRelayBlock + 8n, period: 8n, periodCount: 2n, perPeriod: 100n * nominal};
+ const SCHEDULE_1_PERIOD = 4n; // 6 blocks one period
+ const SCHEDULE_1_START = currentRelayBlock + 6n; // Block when 1 schedule starts
+ const SCHEDULE_2_PERIOD = 8n; // 12 blocks one period
+ const SCHEDULE_2_START = currentRelayBlock + 12n; // Block when 2 schedule starts
+ const schedule1 = {start: SCHEDULE_1_START, period: SCHEDULE_1_PERIOD, periodCount: 2n, perPeriod: 50n * nominal};
+ const schedule2 = {start: SCHEDULE_2_START, period: SCHEDULE_2_PERIOD, periodCount: 2n, perPeriod: 100n * nominal};
// act
await helper.balance.vestedTransfer(sender, recepient.address, schedule1);
@@ -59,20 +63,22 @@
expect(schedule[0]).to.deep.eq(schedule1);
expect(schedule[1]).to.deep.eq(schedule2);
- await helper.wait.forRelayBlockNumber(currentRelayBlock + 8n);
+ // Wait first part available:
+ await helper.wait.forRelayBlockNumber(SCHEDULE_1_START + SCHEDULE_1_PERIOD);
await helper.balance.claim(recepient);
- // check recepient balance after claim (50 tokens claimed):
+ // check recepient balance after claim (50 tokens claimed, 250 left):
balanceRecepient = await helper.balance.getSubstrateFull(recepient.address);
expect(balanceRecepient.free / nominal).to.eq(300n);
expect(balanceRecepient.feeFrozen).to.eq(250n * nominal);
expect(balanceRecepient.miscFrozen).to.eq(250n * nominal);
expect(balanceRecepient.reserved).to.eq(0n);
- await helper.wait.forRelayBlockNumber(currentRelayBlock + 16n);
+ // Wait first schedule ends and first part od second schedule:
+ await helper.wait.forRelayBlockNumber(SCHEDULE_2_START + SCHEDULE_2_PERIOD);
await helper.balance.claim(recepient);
- // check recepient balance after second claim (150 tokens claimed):
+ // check recepient balance after second claim (150 tokens claimed, 100 left):
balanceRecepient = await helper.balance.getSubstrateFull(recepient.address);
expect(balanceRecepient.free / nominal).to.eq(300n);
expect(balanceRecepient.feeFrozen).to.eq(100n * nominal);
@@ -84,10 +90,11 @@
expect(schedule).to.has.length(1);
expect(schedule[0]).to.deep.eq(schedule2);
- await helper.wait.forRelayBlockNumber(currentRelayBlock + 24n);
+ // Wait 2 schedule ends:
+ await helper.wait.forRelayBlockNumber(SCHEDULE_2_START + SCHEDULE_2_PERIOD * 2n);
await helper.balance.claim(recepient);
- // check recepient balance after second claim (100 tokens claimed):
+ // check recepient balance after second claim (100 tokens claimed, 0 left):
balanceRecepient = await helper.balance.getSubstrateFull(recepient.address);
expect(balanceRecepient.free / nominal).to.eq(300n);
expect(balanceRecepient.feeFrozen).to.eq(0n);
tests/yarn.lockdiffbeforeafterboth--- a/tests/yarn.lock
+++ b/tests/yarn.lock
@@ -979,6 +979,13 @@
dependencies:
"@types/chai" "*"
+"@types/chai-subset@^1.3.3":
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/@types/chai-subset/-/chai-subset-1.3.3.tgz#97893814e92abd2c534de422cb377e0e0bdaac94"
+ integrity sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==
+ dependencies:
+ "@types/chai" "*"
+
"@types/chai@*", "@types/chai@^4.3.3":
version "4.3.4"
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.4.tgz#e913e8175db8307d78b4e8fa690408ba6b65dee4"
@@ -1569,6 +1576,11 @@
resolved "https://registry.yarnpkg.com/chai-like/-/chai-like-1.1.1.tgz#8c558a414c34514e814d497c772547ceb7958f64"
integrity sha512-VKa9z/SnhXhkT1zIjtPACFWSoWsqVoaz1Vg+ecrKo5DCKVlgL30F/pEyEvXPBOVwCgLZcWUleCM/C1okaKdTTA==
+chai-subset@^1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/chai-subset/-/chai-subset-1.6.0.tgz#a5d0ca14e329a79596ed70058b6646bd6988cfe9"
+ integrity sha512-K3d+KmqdS5XKW5DWPd5sgNffL3uxdDe+6GdnJh3AYPhwnBGRY5urfvfcbRtWIvvpz+KxkL9FeBB6MZewLUNwug==
+
chai@^4.3.6:
version "4.3.7"
resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51"