From e7f81f393f614d5f524ad8df568eec009823b24a Mon Sep 17 00:00:00 2001 From: ut-akuznetsov <59873862+ut-akuznetsov@users.noreply.github.com> Date: Thu, 08 Dec 2022 09:14:59 +0000 Subject: [PATCH] Merge pull request #753 from UniqueNetwork/tests/eth-helpers Tests/eth helpers --- --- /dev/null +++ b/tests/src/eth/collectionLimits.test.ts @@ -0,0 +1,100 @@ +import {IKeyringPair} from '@polkadot/types/types'; +import {Pallets} from '../util'; +import {expect, itEth, usingEthPlaygrounds} from './util'; + + +describe('Can set collection limits', () => { + let donor: IKeyringPair; + + before(async () => { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({filename: __filename}); + }); + }); + + [ + {case: 'nft' as const, method: 'createNFTCollection' as const}, + {case: 'rft' as const, method: 'createRFTCollection' as const, requiredPallets: [Pallets.ReFungible]}, + {case: 'ft' as const, method: 'createFTCollection' as const}, + ].map(testCase => + itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionId, collectionAddress} = await helper.eth.createCollecion(testCase.method, owner, 'Limits', 'absolutely anything', 'FLO', 18); + const limits = { + accountTokenOwnershipLimit: 1000, + sponsoredDataSize: 1024, + sponsoredDataRateLimit: 30, + tokenLimit: 1000000, + sponsorTransferTimeout: 6, + sponsorApproveTimeout: 6, + ownerCanTransfer: 1, + ownerCanDestroy: 0, + transfersEnabled: 0, + }; + + const expectedLimits = { + accountTokenOwnershipLimit: 1000, + sponsoredDataSize: 1024, + sponsoredDataRateLimit: {blocks: 30}, + tokenLimit: 1000000, + sponsorTransferTimeout: 6, + sponsorApproveTimeout: 6, + ownerCanTransfer: true, + ownerCanDestroy: false, + transfersEnabled: false, + }; + + const collection = helper.ethNativeContract.collection(collectionAddress, testCase.case, owner); + await collection.methods.setCollectionLimit('accountTokenOwnershipLimit', limits.accountTokenOwnershipLimit).send(); + await collection.methods.setCollectionLimit('sponsoredDataSize', limits.sponsoredDataSize).send(); + await collection.methods.setCollectionLimit('sponsoredDataRateLimit', limits.sponsoredDataRateLimit).send(); + await collection.methods.setCollectionLimit('tokenLimit', limits.tokenLimit).send(); + await collection.methods.setCollectionLimit('sponsorTransferTimeout', limits.sponsorTransferTimeout).send(); + await collection.methods.setCollectionLimit('sponsorApproveTimeout', limits.sponsorApproveTimeout).send(); + await collection.methods.setCollectionLimit('ownerCanTransfer', limits.ownerCanTransfer).send(); + await collection.methods.setCollectionLimit('ownerCanDestroy', limits.ownerCanDestroy).send(); + await collection.methods.setCollectionLimit('transfersEnabled', limits.transfersEnabled).send(); + + const data = (await helper.rft.getData(collectionId))!; + expect(data.raw.limits).to.deep.eq(expectedLimits); + expect(await helper.collection.getEffectiveLimits(collectionId)).to.deep.eq(expectedLimits); + })); +}); + +describe('Cannot set invalid collection limits', () => { + let donor: IKeyringPair; + + before(async () => { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = await privateKey({filename: __filename}); + }); + }); + + [ + {case: 'nft' as const, method: 'createNFTCollection' as const}, + {case: 'rft' as const, method: 'createRFTCollection' as const, requiredPallets: [Pallets.ReFungible]}, + {case: 'ft' as const, method: 'createFTCollection' as const}, + ].map(testCase => + itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => { + const invalidLimits = { + accountTokenOwnershipLimit: BigInt(Number.MAX_SAFE_INTEGER), + transfersEnabled: 3, + }; + + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress} = await helper.eth.createCollecion(testCase.method, owner, 'Limits', 'absolutely anything', 'ISNI', 18); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.case, owner); + await expect(collectionEvm.methods + .setCollectionLimit('badLimit', '1') + .call()).to.be.rejectedWith('unknown limit "badLimit"'); + + await expect(collectionEvm.methods + .setCollectionLimit(Object.keys(invalidLimits)[0], invalidLimits.accountTokenOwnershipLimit) + .call()).to.be.rejectedWith(`can't convert value to u32 "${invalidLimits.accountTokenOwnershipLimit}"`); + + await expect(collectionEvm.methods + .setCollectionLimit(Object.keys(invalidLimits)[1], invalidLimits.transfersEnabled) + .call()).to.be.rejectedWith(`can't convert value to boolean "${invalidLimits.transfersEnabled}"`); + })); +}); + \ No newline at end of file --- a/tests/src/eth/collectionProperties.test.ts +++ b/tests/src/eth/collectionProperties.test.ts @@ -18,7 +18,6 @@ import {Pallets} from '../util'; import {IProperty, ITokenPropertyPermission} from '../util/playgrounds/types'; import {IKeyringPair} from '@polkadot/types/types'; -import {TCollectionMode} from '../util/playgrounds/types'; describe('EVM collection properties', () => { let donor: IKeyringPair; @@ -31,7 +30,26 @@ }); }); - itEth('Can be set', async({helper}) => { + // 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'}]}, + ].map(testCase => + itEth(`Collection properties can be set: ${testCase.method}`, async({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const collection = await helper.nft.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'); + + await contract.methods[testCase.method](...testCase.methodParams).send({from: caller}); + + const raw = (await collection.getData())?.raw; + expect(raw.properties).to.deep.equal(testCase.expectedProps); + })); + + itEth('Cannot set invalid properties', async({helper}) => { const caller = await helper.eth.createAccountWithBalance(donor); const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties: []}); await collection.addAdmin(alice, {Ethereum: caller}); @@ -39,28 +57,60 @@ const address = helper.ethAddress.fromCollectionId(collection.collectionId); const contract = helper.ethNativeContract.collection(address, 'nft', caller); - await contract.methods.setCollectionProperties([{key: 'testKey', value: Buffer.from('testValue')}]).send({from: caller}); - + await expect(contract.methods.setCollectionProperties([{key: '', value: Buffer.from('val1')}]).send({from: caller})).to.be.rejected; + await expect(contract.methods.setCollectionProperties([{key: 'déjà vu', value: Buffer.from('hmm...')}]).send({from: caller})).to.be.rejected; + await expect(contract.methods.setCollectionProperties([{key: 'a'.repeat(257), value: Buffer.from('val3')}]).send({from: caller})).to.be.rejected; + // TODO add more expects const raw = (await collection.getData())?.raw; - - expect(raw.properties[0].value).to.equal('testValue'); + expect(raw.properties).to.deep.equal([]); }); - itEth('Can be deleted', async({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties: [{key: 'testKey', value: 'testValue'}]}); - - await collection.addAdmin(alice, {Ethereum: caller}); - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = helper.ethNativeContract.collection(address, 'nft', caller); + // 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'}]}, + ].map(testCase => + itEth(`Collection properties can be deleted: ${testCase.method}()`, 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}); + + await collection.addAdmin(alice, {Ethereum: caller}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = helper.ethNativeContract.collection(address, 'nft', caller, testCase.method === 'deleteCollectionProperty'); + + await contract.methods[testCase.method](...testCase.methodParams).send({from: caller}); + + const raw = (await collection.getData())?.raw; + + expect(raw.properties.length).to.equal(testCase.expectedProps.length); + expect(raw.properties).to.deep.equal(testCase.expectedProps); + })); - await contract.methods.deleteCollectionProperties(['testKey']).send({from: caller}); + + [ + {method: 'deleteCollectionProperties', methodParams: [['testKey2']]}, + {method: 'deleteCollectionProperty', methodParams: ['testKey2']}, + ].map(testCase => + itEth(`cannot ${testCase.method}() of non-owned collections`, async ({helper}) => { + const properties = [ + {key: 'testKey1', value: 'testValue1'}, + {key: 'testKey2', value: 'testValue2'}, + ]; + const caller = await helper.eth.createAccountWithBalance(donor); + const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties}); - const raw = (await collection.getData())?.raw; + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionEvm = helper.ethNativeContract.collection(address, 'nft', caller, testCase.method === 'deleteCollectionProperty'); - expect(raw.properties.length).to.equal(0); - }); + await expect(collectionEvm.methods[testCase.method](...testCase.methodParams).send({from: caller})).to.be.rejected; + expect(await collection.getProperties()).to.deep.eq(properties); + })); itEth('Can be read', async({helper}) => { const caller = helper.eth.createAccount(); @@ -72,39 +122,6 @@ const value = await contract.methods.collectionProperty('testKey').call(); expect(value).to.equal(helper.getWeb3().utils.toHex('testValue')); }); - - // Soft-deprecated - itEth('Collection property can be set', async({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const collection = await helper.nft.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, true); - - await contract.methods.setCollectionProperty('testKey', Buffer.from('testValue')).send(); - - const raw = (await collection.getData())?.raw; - - expect(raw.properties[0].value).to.equal('testValue'); - }); - - // Soft-deprecated - itEth('Collection property can be deleted', async({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties: [{key: 'testKey', value: 'testValue'}]}); - - await collection.addAdmin(alice, {Ethereum: caller}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = helper.ethNativeContract.collection(address, 'nft', caller, true); - - await contract.methods.deleteCollectionProperty('testKey').send({from: caller}); - - const raw = (await collection.getData())?.raw; - - expect(raw.properties.length).to.equal(0); - }); }); describe('Supports ERC721Metadata', () => { @@ -116,85 +133,81 @@ }); }); - const checkERC721Metadata = async (helper: EthUniqueHelper, mode: 'nft' | 'rft') => { - const caller = await helper.eth.createAccountWithBalance(donor); - const bruh = await helper.eth.createAccountWithBalance(donor); - - const BASE_URI = 'base/'; - const SUFFIX = 'suffix1'; - const URI = 'uri1'; - - const collectionHelpers = helper.ethNativeContract.collectionHelpers(caller); - const creatorMethod = mode === 'rft' ? 'createRFTCollection' : 'createNFTCollection'; - - const {collectionId, collectionAddress} = await helper.eth[creatorMethod](caller, 'n', 'd', 'p'); - const bruhCross = helper.ethCrossAccount.fromAddress(bruh); - - const contract = helper.ethNativeContract.collectionById(collectionId, mode, caller); - await contract.methods.addCollectionAdminCross(bruhCross).send(); // to check that admin will work too - - const collection1 = helper.nft.getCollectionObject(collectionId); - const data1 = await collection1.getData(); - expect(data1?.raw.flags.erc721metadata).to.be.false; - expect(await contract.methods.supportsInterface('0x5b5e139f').call()).to.be.false; - - await collectionHelpers.methods.makeCollectionERC721MetadataCompatible(collectionAddress, BASE_URI) - .send({from: bruh}); - - expect(await contract.methods.supportsInterface('0x5b5e139f').call()).to.be.true; - - const collection2 = helper.nft.getCollectionObject(collectionId); - const data2 = await collection2.getData(); - expect(data2?.raw.flags.erc721metadata).to.be.true; - - const propertyPermissions = data2?.raw.tokenPropertyPermissions; - expect(propertyPermissions?.length).to.equal(2); - - expect(propertyPermissions.find((tpp: ITokenPropertyPermission) => { - return tpp.key === 'URI' && tpp.permission.mutable && tpp.permission.collectionAdmin && !tpp.permission.tokenOwner; - })).to.be.not.null; - - expect(propertyPermissions.find((tpp: ITokenPropertyPermission) => { - return tpp.key === 'URISuffix' && tpp.permission.mutable && tpp.permission.collectionAdmin && !tpp.permission.tokenOwner; - })).to.be.not.null; - - expect(data2?.raw.properties?.find((property: IProperty) => { - return property.key === 'baseURI' && property.value === BASE_URI; - })).to.be.not.null; - - const token1Result = await contract.methods.mint(bruh).send(); - const tokenId1 = token1Result.events.Transfer.returnValues.tokenId; - - expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI); - - await contract.methods.setProperties(tokenId1, [{key: 'URISuffix', value: Buffer.from(SUFFIX)}]).send(); - expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI + SUFFIX); - - await contract.methods.setProperties(tokenId1, [{key: 'URI', value: Buffer.from(URI)}]).send(); - expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(URI); - - await contract.methods.deleteProperties(tokenId1, ['URI']).send(); - expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI + SUFFIX); - - const token2Result = await contract.methods.mintWithTokenURI(bruh, URI).send(); - const tokenId2 = token2Result.events.Transfer.returnValues.tokenId; - - expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(URI); - - await contract.methods.deleteProperties(tokenId2, ['URI']).send(); - expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(BASE_URI); - - await contract.methods.setProperties(tokenId2, [{key: 'URISuffix', value: Buffer.from(SUFFIX)}]).send(); - expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(BASE_URI + SUFFIX); - }; - - itEth('ERC721Metadata property can be set for NFT collection', async({helper}) => { - await checkERC721Metadata(helper, 'nft'); - }); - - itEth.ifWithPallets('ERC721Metadata property can be set for RFT collection', [Pallets.ReFungible], async({helper}) => { - await checkERC721Metadata(helper, 'rft'); - }); + [ + {case: 'nft' as const}, + {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]}, + ].map(testCase => + itEth.ifWithPallets(`ERC721Metadata property can be set for ${testCase.case} collection`, testCase.requiredPallets || [], async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const bruh = await helper.eth.createAccountWithBalance(donor); + + const BASE_URI = 'base/'; + const SUFFIX = 'suffix1'; + const URI = 'uri1'; + + const collectionHelpers = helper.ethNativeContract.collectionHelpers(caller); + const creatorMethod = testCase.case === 'rft' ? 'createRFTCollection' : 'createNFTCollection'; + + const {collectionId, collectionAddress} = await helper.eth[creatorMethod](caller, 'n', 'd', 'p'); + const bruhCross = helper.ethCrossAccount.fromAddress(bruh); + + const contract = helper.ethNativeContract.collectionById(collectionId, testCase.case, caller); + await contract.methods.addCollectionAdminCross(bruhCross).send(); // to check that admin will work too + + const collection1 = helper.nft.getCollectionObject(collectionId); + const data1 = await collection1.getData(); + expect(data1?.raw.flags.erc721metadata).to.be.false; + expect(await contract.methods.supportsInterface('0x5b5e139f').call()).to.be.false; + + await collectionHelpers.methods.makeCollectionERC721MetadataCompatible(collectionAddress, BASE_URI) + .send({from: bruh}); + + expect(await contract.methods.supportsInterface('0x5b5e139f').call()).to.be.true; + + const collection2 = helper.nft.getCollectionObject(collectionId); + const data2 = await collection2.getData(); + expect(data2?.raw.flags.erc721metadata).to.be.true; + + const propertyPermissions = data2?.raw.tokenPropertyPermissions; + expect(propertyPermissions?.length).to.equal(2); + + expect(propertyPermissions.find((tpp: ITokenPropertyPermission) => { + return tpp.key === 'URI' && tpp.permission.mutable && tpp.permission.collectionAdmin && !tpp.permission.tokenOwner; + })).to.be.not.null; + + expect(propertyPermissions.find((tpp: ITokenPropertyPermission) => { + return tpp.key === 'URISuffix' && tpp.permission.mutable && tpp.permission.collectionAdmin && !tpp.permission.tokenOwner; + })).to.be.not.null; + + expect(data2?.raw.properties?.find((property: IProperty) => { + return property.key === 'baseURI' && property.value === BASE_URI; + })).to.be.not.null; + + const token1Result = await contract.methods.mint(bruh).send(); + const tokenId1 = token1Result.events.Transfer.returnValues.tokenId; + + expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI); + + await contract.methods.setProperties(tokenId1, [{key: 'URISuffix', value: Buffer.from(SUFFIX)}]).send(); + expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI + SUFFIX); + + await contract.methods.setProperties(tokenId1, [{key: 'URI', value: Buffer.from(URI)}]).send(); + expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(URI); + + await contract.methods.deleteProperties(tokenId1, ['URI']).send(); + expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI + SUFFIX); + + const token2Result = await contract.methods.mintWithTokenURI(bruh, URI).send(); + const tokenId2 = token2Result.events.Transfer.returnValues.tokenId; + + expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(URI); + + await contract.methods.deleteProperties(tokenId2, ['URI']).send(); + expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(BASE_URI); + + 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', () => { @@ -206,81 +219,70 @@ }); }); - async function testSetReadProperties(helper: EthUniqueHelper, mode: TCollectionMode) { - const collection = await helper[mode].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, mode, 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); - } - - itEth('Set/read properties ft', async ({helper}) => { - await testSetReadProperties(helper, 'ft'); - }); - itEth.ifWithPallets('Set/read properties rft', [Pallets.ReFungible], async ({helper}) => { - await testSetReadProperties(helper, 'rft'); - }); - itEth('Set/read properties nft', async ({helper}) => { - await testSetReadProperties(helper, 'nft'); - }); - - async function testDeleteProperties(helper: EthUniqueHelper, mode: TCollectionMode) { - const collection = await helper[mode].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, mode, sender); + [ + {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 keys = ['key0', 'key1', 'key2', 'key3']; - - { + 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'), - 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(); + const readProperties = await contract.methods.collectionProperties([keys[0], keys[1]]).call(); expect(readProperties).to.be.like(writeProperties); - } + })); - { - const expectProperties = [ - helper.ethProperty.property(keys[0], 'value0'), - helper.ethProperty.property(keys[1], 'value1'), - ]; + [ + {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'}); - await contract.methods.deleteCollectionProperties([keys[2], keys[3]]).send(); - const readProperties = await contract.methods.collectionProperties([]).call(); - expect(readProperties).to.be.like(expectProperties); - } - } + 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); - itEth('Delete properties ft', async ({helper}) => { - await testDeleteProperties(helper, 'ft'); - }); - itEth.ifWithPallets('Delete properties rft', [Pallets.ReFungible], async ({helper}) => { - await testDeleteProperties(helper, 'rft'); - }); - itEth('Delete properties nft', async ({helper}) => { - await testDeleteProperties(helper, 'nft'); - }); - + 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); + } + })); }); --- a/tests/src/eth/collectionSponsoring.test.ts +++ b/tests/src/eth/collectionSponsoring.test.ts @@ -31,6 +31,7 @@ }); }); + // TODO: move to substrate tests itEth('sponsors mint transactions', async ({helper}) => { const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'spnr', permissions: {mintMode: true}}); await collection.setSponsor(alice, alice.address); @@ -80,180 +81,110 @@ // const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner}); // expect(bigIntToSub(api, BigInt(sponsorTuple[1]))).to.be.eq(sponsor.address); // }); - - // Soft-deprecated - itEth('[eth] Remove sponsor', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner); - - let result = await collectionHelpers.methods.createNFTCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)}); - const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const collectionEvm = helper.ethNativeContract.collection(collectionIdAddress, 'nft', owner, true); - - expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false; - result = await collectionEvm.methods.setCollectionSponsor(sponsor).send({from: owner}); - expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true; - - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); - expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false; - - await collectionEvm.methods.removeCollectionSponsor().send({from: owner}); - - const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner}); - expect(sponsorTuple.field_0).to.be.eq('0x0000000000000000000000000000000000000000'); - }); - - itEth('[cross] Remove sponsor', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner); - - let result = await collectionHelpers.methods.createNFTCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)}); - const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); - const collectionEvm = helper.ethNativeContract.collection(collectionIdAddress, 'nft', owner); - - expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false; - result = await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send({from: owner}); - expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true; - - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); - expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false; - await collectionEvm.methods.removeCollectionSponsor().send({from: owner}); - - const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner}); - expect(sponsorTuple.field_0).to.be.eq('0x0000000000000000000000000000000000000000'); - }); - - // Soft-deprecated - itEth('[eth] Sponsoring collection from evm address via access list', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Sponsor collection', '1', '1', ''); - - const collection = helper.nft.getCollectionObject(collectionId); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); - - await collectionEvm.methods.setCollectionSponsor(sponsor).send({from: owner}); - let collectionData = (await collection.getData())!; - expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); - await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor'); - - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); - collectionData = (await collection.getData())!; - expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); - - const user = helper.eth.createAccount(); - const nextTokenId = await collectionEvm.methods.nextTokenId().call(); - expect(nextTokenId).to.be.equal('1'); - - const oldPermissions = (await collection.getData())!.raw.permissions; // (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman(); - expect(oldPermissions.mintMode).to.be.false; - expect(oldPermissions.access).to.be.equal('Normal'); - - await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner}); - await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner}); - await collectionEvm.methods.setCollectionMintMode(true).send({from: owner}); - - const newPermissions = (await collection.getData())!.raw.permissions; // (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman(); - expect(newPermissions.mintMode).to.be.true; - expect(newPermissions.access).to.be.equal('AllowList'); + [ + 'setCollectionSponsorCross', + 'setCollectionSponsor', // Soft-deprecated + ].map(testCase => + itEth(`[${testCase}] can remove collection sponsor`, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner); + + let result = await collectionHelpers.methods.createNFTCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)}); + const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); + const collectionEvm = helper.ethNativeContract.collection(collectionIdAddress, 'nft', owner, testCase === 'setCollectionSponsor'); + + expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false; + result = await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send({from: owner}); + expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true; + + await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); + expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false; + + await collectionEvm.methods.removeCollectionSponsor().send({from: owner}); + + const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner}); + expect(sponsorTuple.field_0).to.be.eq('0x0000000000000000000000000000000000000000'); + })); - const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + [ + 'setCollectionSponsorCross', + 'setCollectionSponsor', // Soft-deprecated + ].map(testCase => + itEth(`[${testCase}] Can sponsor from evm address via access list`, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsorEth = await helper.eth.createAccountWithBalance(donor); + const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth); + + const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Sponsor collection', '1', '1', ''); + + const collectionSub = helper.nft.getCollectionObject(collectionId); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner, testCase === 'setCollectionSponsor'); + + // Set collection sponsor: + await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsorEth : sponsorCrossEth).send({from: owner}); + let sponsorship = (await collectionSub.getData())!.raw.sponsorship; + expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true)); + // Account cannot confirm sponsorship if it is not set as a sponsor + await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor'); + + // Sponsor can confirm sponsorship: + await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth}); + sponsorship = (await collectionSub.getData())!.raw.sponsorship; + expect(sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true)); + + // Create user with no balance: + const user = helper.eth.createAccount(); + const userCross = helper.ethCrossAccount.fromAddress(user); + const nextTokenId = await collectionEvm.methods.nextTokenId().call(); + expect(nextTokenId).to.be.equal('1'); + + // Set collection permissions: + const oldPermissions = (await collectionSub.getData())!.raw.permissions; // (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman(); + expect(oldPermissions.mintMode).to.be.false; + expect(oldPermissions.access).to.be.equal('Normal'); - { - const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); - const events = helper.eth.normalizeEvents(result.events); + await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner}); + await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner}); + await collectionEvm.methods.setCollectionMintMode(true).send({from: owner}); + + const newPermissions = (await collectionSub.getData())!.raw.permissions; // (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman(); + expect(newPermissions.mintMode).to.be.true; + expect(newPermissions.access).to.be.equal('AllowList'); + + const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth)); - expect(events).to.be.deep.equal([ - { - address: collectionAddress, - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: user, - tokenId: '1', + // User can mint token without balance: + { + const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); + const events = helper.eth.normalizeEvents(result.events); + + expect(events).to.be.deep.equal([ + { + address: collectionAddress, + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: user, + tokenId: '1', + }, }, - }, - ]); - - const ownerBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(owner)); - const sponsorBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(sponsor)); - - expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI'); - expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter); - expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true; - } - }); - - itEth('[cross] Sponsoring collection from evm address via access list', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Sponsor collection', '1', '1', ''); - - const collection = helper.nft.getCollectionObject(collectionId); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); - const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + ]); + + const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth)); + const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user)); + + expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI'); + expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter); + expect(userBalanceAfter).to.be.eq(0n); + expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true; + } + })); - await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send({from: owner}); - let collectionData = (await collection.getData())!; - expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); - await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor'); - - await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); - collectionData = (await collection.getData())!; - expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); - - const user = helper.eth.createAccount(); - const userCross = helper.ethCrossAccount.fromAddress(user); - const nextTokenId = await collectionEvm.methods.nextTokenId().call(); - expect(nextTokenId).to.be.equal('1'); - - const oldPermissions = (await collection.getData())!.raw.permissions; // (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman(); - expect(oldPermissions.mintMode).to.be.false; - expect(oldPermissions.access).to.be.equal('Normal'); - - await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner}); - await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner}); - await collectionEvm.methods.setCollectionMintMode(true).send({from: owner}); - - const newPermissions = (await collection.getData())!.raw.permissions; // (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman(); - expect(newPermissions.mintMode).to.be.true; - expect(newPermissions.access).to.be.equal('AllowList'); - - const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - - { - const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); - const events = helper.eth.normalizeEvents(result.events); - - expect(events).to.be.deep.equal([ - { - address: collectionAddress, - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: user, - tokenId: '1', - }, - }, - ]); - - const ownerBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(owner)); - const sponsorBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(sponsor)); - - expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI'); - expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter); - expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true; - } - }); - // TODO: Temprorary off. Need refactor // itWeb3('Sponsoring collection from substrate address via access list', async ({api, web3, privateKeyWrapper}) => { // const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); @@ -310,108 +241,79 @@ // } // }); - // Soft-deprecated - itEth('[eth] Check that transaction via EVM spend money from sponsor address', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - - const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner,'Sponsor collection', '1', '1', ''); - const collection = helper.nft.getCollectionObject(collectionId); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true); - - await collectionEvm.methods.setCollectionSponsor(sponsor).send(); - let collectionData = (await collection.getData())!; - expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); - await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor'); - - const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor, true); - await sponsorCollection.methods.confirmCollectionSponsorship().send(); - collectionData = (await collection.getData())!; - expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); - - const user = helper.eth.createAccount(); - await collectionEvm.methods.addCollectionAdmin(user).send(); - - const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - - const userCollectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', user, true); - - const result = await userCollectionEvm.methods.mintWithTokenURI(user, 'Test URI').send(); - const tokenId = result.events.Transfer.returnValues.tokenId; + [ + 'setCollectionSponsorCross', + 'setCollectionSponsor', // Soft-deprecated + ].map(testCase => + itEth(`[${testCase}] Check that transaction via EVM spend money from sponsor address`, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); + + const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner,'Sponsor collection', '1', '1', ''); - const events = helper.eth.normalizeEvents(result.events); - const address = helper.ethAddress.fromCollectionId(collectionId); - - expect(events).to.be.deep.equal([ - { - address, - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: user, - tokenId: '1', + const collectionSub = helper.nft.getCollectionObject(collectionId); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner, testCase === 'setCollectionSponsor'); + // Set collection sponsor: + await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send(); + let collectionData = (await collectionSub.getData())!; + expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); + await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor'); + + await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor}); + collectionData = (await collectionSub.getData())!; + expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); + + const user = helper.eth.createAccount(); + const userCross = helper.ethCrossAccount.fromAddress(user); + await collectionEvm.methods.addCollectionAdminCross(userCross).send(); + + const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + + const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user}); + const tokenId = mintingResult.events.Transfer.returnValues.tokenId; + + const events = helper.eth.normalizeEvents(mintingResult.events); + const address = helper.ethAddress.fromCollectionId(collectionId); + + expect(events).to.be.deep.equal([ + { + address, + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: user, + tokenId: '1', + }, }, - }, - ]); - expect(await userCollectionEvm.methods.tokenURI(tokenId).call()).to.be.equal('Test URI'); + ]); + expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI'); + + const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); + expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore); + const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); + expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; + })); - const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore); - const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; - }); - - itEth('[cross] Check that transaction via EVM spend money from sponsor address', async ({helper}) => { + itEth('Can reassign collection sponsor', async ({helper}) => { const owner = await helper.eth.createAccountWithBalance(donor); + const sponsorEth = await helper.eth.createAccountWithBalance(donor); + const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth); + const [sponsorSub] = await helper.arrange.createAccounts([100n], donor); + const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub); const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner,'Sponsor collection', '1', '1', ''); - const collection = helper.nft.getCollectionObject(collectionId); - const sponsor = await helper.eth.createAccountWithBalance(donor); - const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor); + const collectionSub = helper.nft.getCollectionObject(collectionId); const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - - await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send(); - let collectionData = (await collection.getData())!; - expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); - await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor'); - - const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor); - await sponsorCollection.methods.confirmCollectionSponsorship().send(); - collectionData = (await collection.getData())!; - expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true)); - - const user = helper.eth.createAccount(); - const userCross = helper.ethCrossAccount.fromAddress(user); - await collectionEvm.methods.addCollectionAdminCross(userCross).send(); - - const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - - const userCollectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', user); - - const result = await userCollectionEvm.methods.mintWithTokenURI(user, 'Test URI').send(); - const tokenId = result.events.Transfer.returnValues.tokenId; - const events = helper.eth.normalizeEvents(result.events); - const address = helper.ethAddress.fromCollectionId(collectionId); + // Set and confirm sponsor: + await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossEth).send({from: owner}); + await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth}); - expect(events).to.be.deep.equal([ - { - address, - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: user, - tokenId: '1', - }, - }, - ]); - expect(await userCollectionEvm.methods.tokenURI(tokenId).call()).to.be.equal('Test URI'); - - const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner)); - expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore); - const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor)); - expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true; + // Can reassign sponsor: + await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner}); + const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship; + expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address}); }); }); --- a/tests/src/eth/createFTCollection.test.ts +++ b/tests/src/eth/createFTCollection.test.ts @@ -79,56 +79,6 @@ expect(await collection.methods.description().call()).to.deep.equal(description); }); - itEth('Set limits', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionId, collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Limits', DECIMALS, 'absolutely anything', 'INSI'); - const limits = { - accountTokenOwnershipLimit: 1000, - sponsoredDataSize: 1024, - sponsoredDataRateLimit: 30, - tokenLimit: 1000000, - sponsorTransferTimeout: 6, - sponsorApproveTimeout: 6, - ownerCanTransfer: 0, - ownerCanDestroy: 0, - transfersEnabled: 0, - }; - - const expectedLimits = { - accountTokenOwnershipLimit: 1000, - sponsoredDataSize: 1024, - sponsoredDataRateLimit: 30, - tokenLimit: 1000000, - sponsorTransferTimeout: 6, - sponsorApproveTimeout: 6, - ownerCanTransfer: false, - ownerCanDestroy: false, - transfersEnabled: false, - }; - - const collection = helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - await collection.methods.setCollectionLimit('accountTokenOwnershipLimit', limits.accountTokenOwnershipLimit).send(); - await collection.methods.setCollectionLimit('sponsoredDataSize', limits.sponsoredDataSize).send(); - await collection.methods.setCollectionLimit('sponsoredDataRateLimit', limits.sponsoredDataRateLimit).send(); - await collection.methods.setCollectionLimit('tokenLimit', limits.tokenLimit).send(); - await collection.methods.setCollectionLimit('sponsorTransferTimeout', limits.sponsorTransferTimeout).send(); - await collection.methods.setCollectionLimit('sponsorApproveTimeout', limits.sponsorApproveTimeout).send(); - await collection.methods.setCollectionLimit('ownerCanTransfer', limits.ownerCanTransfer).send(); - await collection.methods.setCollectionLimit('ownerCanDestroy', limits.ownerCanDestroy).send(); - await collection.methods.setCollectionLimit('transfersEnabled', limits.transfersEnabled).send(); - - const data = (await helper.rft.getData(collectionId))!; - expect(data.raw.limits.accountTokenOwnershipLimit).to.be.eq(expectedLimits.accountTokenOwnershipLimit); - expect(data.raw.limits.sponsoredDataSize).to.be.eq(expectedLimits.sponsoredDataSize); - expect(data.raw.limits.sponsoredDataRateLimit.blocks).to.be.eq(expectedLimits.sponsoredDataRateLimit); - expect(data.raw.limits.tokenLimit).to.be.eq(expectedLimits.tokenLimit); - expect(data.raw.limits.sponsorTransferTimeout).to.be.eq(expectedLimits.sponsorTransferTimeout); - expect(data.raw.limits.sponsorApproveTimeout).to.be.eq(expectedLimits.sponsorApproveTimeout); - expect(data.raw.limits.ownerCanTransfer).to.be.eq(expectedLimits.ownerCanTransfer); - expect(data.raw.limits.ownerCanDestroy).to.be.eq(expectedLimits.ownerCanDestroy); - expect(data.raw.limits.transfersEnabled).to.be.eq(expectedLimits.transfersEnabled); - }); - itEth('Collection address exist', async ({helper}) => { const owner = await helper.eth.createAccountWithBalance(donor); const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233'; @@ -274,31 +224,5 @@ .setCollectionLimit('account_token_ownership_limit', '1000') .call()).to.be.rejectedWith(EXPECTED_ERROR); } - }); - - itEth('(!negative test!) Set limits', async ({helper}) => { - - const invalidLimits = { - accountTokenOwnershipLimit: BigInt(Number.MAX_SAFE_INTEGER), - transfersEnabled: 3, - }; - - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Limits', DECIMALS, 'absolutely anything', 'ISNI'); - const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner); - await expect(collectionEvm.methods - .setCollectionLimit('badLimit', '1') - .call()).to.be.rejectedWith('unknown limit "badLimit"'); - - await expect(collectionEvm.methods - .setCollectionLimit(Object.keys(invalidLimits)[0], invalidLimits.accountTokenOwnershipLimit) - .call()).to.be.rejectedWith(`can't convert value to u32 "${invalidLimits.accountTokenOwnershipLimit}"`); - - await expect(collectionEvm.methods - .setCollectionLimit(Object.keys(invalidLimits)[1], invalidLimits.transfersEnabled) - .call()).to.be.rejectedWith(`can't convert value to boolean "${invalidLimits.transfersEnabled}"`); - }); - - - + }); }); --- a/tests/src/eth/createNFTCollection.test.ts +++ b/tests/src/eth/createNFTCollection.test.ts @@ -120,56 +120,6 @@ expect(await sponsorCollection.methods.description().call()).to.deep.equal(description); }); - itEth('Set limits', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(owner, 'Limits', 'absolutely anything', 'FLO'); - const limits = { - accountTokenOwnershipLimit: 1000, - sponsoredDataSize: 1024, - sponsoredDataRateLimit: 30, - tokenLimit: 1000000, - sponsorTransferTimeout: 6, - sponsorApproveTimeout: 6, - ownerCanTransfer: 0, - ownerCanDestroy: 0, - transfersEnabled: 0, - }; - - const expectedLimits = { - accountTokenOwnershipLimit: 1000, - sponsoredDataSize: 1024, - sponsoredDataRateLimit: 30, - tokenLimit: 1000000, - sponsorTransferTimeout: 6, - sponsorApproveTimeout: 6, - ownerCanTransfer: false, - ownerCanDestroy: false, - transfersEnabled: false, - }; - - const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - await collection.methods.setCollectionLimit('accountTokenOwnershipLimit', limits.accountTokenOwnershipLimit).send(); - await collection.methods.setCollectionLimit('sponsoredDataSize', limits.sponsoredDataSize).send(); - await collection.methods.setCollectionLimit('sponsoredDataRateLimit', limits.sponsoredDataRateLimit).send(); - await collection.methods.setCollectionLimit('tokenLimit', limits.tokenLimit).send(); - await collection.methods.setCollectionLimit('sponsorTransferTimeout', limits.sponsorTransferTimeout).send(); - await collection.methods.setCollectionLimit('sponsorApproveTimeout', limits.sponsorApproveTimeout).send(); - await collection.methods.setCollectionLimit('ownerCanTransfer', limits.ownerCanTransfer).send(); - await collection.methods.setCollectionLimit('ownerCanDestroy', limits.ownerCanDestroy).send(); - await collection.methods.setCollectionLimit('transfersEnabled', limits.transfersEnabled).send(); - - const data = (await helper.rft.getData(collectionId))!; - expect(data.raw.limits.accountTokenOwnershipLimit).to.be.eq(expectedLimits.accountTokenOwnershipLimit); - expect(data.raw.limits.sponsoredDataSize).to.be.eq(expectedLimits.sponsoredDataSize); - expect(data.raw.limits.sponsoredDataRateLimit.blocks).to.be.eq(expectedLimits.sponsoredDataRateLimit); - expect(data.raw.limits.tokenLimit).to.be.eq(expectedLimits.tokenLimit); - expect(data.raw.limits.sponsorTransferTimeout).to.be.eq(expectedLimits.sponsorTransferTimeout); - expect(data.raw.limits.sponsorApproveTimeout).to.be.eq(expectedLimits.sponsorApproveTimeout); - expect(data.raw.limits.ownerCanTransfer).to.be.eq(expectedLimits.ownerCanTransfer); - expect(data.raw.limits.ownerCanDestroy).to.be.eq(expectedLimits.ownerCanDestroy); - expect(data.raw.limits.transfersEnabled).to.be.eq(expectedLimits.transfersEnabled); - }); - itEth('Collection address exist', async ({helper}) => { const owner = await helper.eth.createAccountWithBalance(donor); const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233'; @@ -285,25 +235,6 @@ .setCollectionLimit('account_token_ownership_limit', '1000') .call()).to.be.rejectedWith(EXPECTED_ERROR); } - }); - - itEth('(!negative test!) Set limits', async ({helper}) => { - const invalidLimits = { - accountTokenOwnershipLimit: BigInt(Number.MAX_SAFE_INTEGER), - transfersEnabled: 3, - }; - - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Limits', 'absolutely anything', 'OLF'); - const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - - await expect(collectionEvm.methods - .setCollectionLimit(Object.keys(invalidLimits)[0], invalidLimits.accountTokenOwnershipLimit) - .call()).to.be.rejectedWith(`can't convert value to u32 "${invalidLimits.accountTokenOwnershipLimit}"`); - - await expect(collectionEvm.methods - .setCollectionLimit(Object.keys(invalidLimits)[1], invalidLimits.transfersEnabled) - .call()).to.be.rejectedWith(`can't convert value to boolean "${invalidLimits.transfersEnabled}"`); }); itEth('destroyCollection', async ({helper}) => { --- a/tests/src/eth/createRFTCollection.test.ts +++ b/tests/src/eth/createRFTCollection.test.ts @@ -152,56 +152,6 @@ expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); }); - itEth('Set limits', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Limits', 'absolutely anything', 'INSI'); - const limits = { - accountTokenOwnershipLimit: 1000, - sponsoredDataSize: 1024, - sponsoredDataRateLimit: 30, - tokenLimit: 1000000, - sponsorTransferTimeout: 6, - sponsorApproveTimeout: 6, - ownerCanTransfer: 0, - ownerCanDestroy: 0, - transfersEnabled: 0, - }; - - const expectedLimits = { - accountTokenOwnershipLimit: 1000, - sponsoredDataSize: 1024, - sponsoredDataRateLimit: 30, - tokenLimit: 1000000, - sponsorTransferTimeout: 6, - sponsorApproveTimeout: 6, - ownerCanTransfer: false, - ownerCanDestroy: false, - transfersEnabled: false, - }; - - const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner); - await collection.methods.setCollectionLimit('accountTokenOwnershipLimit', limits.accountTokenOwnershipLimit).send(); - await collection.methods.setCollectionLimit('sponsoredDataSize', limits.sponsoredDataSize).send(); - await collection.methods.setCollectionLimit('sponsoredDataRateLimit', limits.sponsoredDataRateLimit).send(); - await collection.methods.setCollectionLimit('tokenLimit', limits.tokenLimit).send(); - await collection.methods.setCollectionLimit('sponsorTransferTimeout', limits.sponsorTransferTimeout).send(); - await collection.methods.setCollectionLimit('sponsorApproveTimeout', limits.sponsorApproveTimeout).send(); - await collection.methods.setCollectionLimit('ownerCanTransfer', limits.ownerCanTransfer).send(); - await collection.methods.setCollectionLimit('ownerCanDestroy', limits.ownerCanDestroy).send(); - await collection.methods.setCollectionLimit('transfersEnabled', limits.transfersEnabled).send(); - - const data = (await helper.rft.getData(collectionId))!; - expect(data.raw.limits.accountTokenOwnershipLimit).to.be.eq(expectedLimits.accountTokenOwnershipLimit); - expect(data.raw.limits.sponsoredDataSize).to.be.eq(expectedLimits.sponsoredDataSize); - expect(data.raw.limits.sponsoredDataRateLimit.blocks).to.be.eq(expectedLimits.sponsoredDataRateLimit); - expect(data.raw.limits.tokenLimit).to.be.eq(expectedLimits.tokenLimit); - expect(data.raw.limits.sponsorTransferTimeout).to.be.eq(expectedLimits.sponsorTransferTimeout); - expect(data.raw.limits.sponsorApproveTimeout).to.be.eq(expectedLimits.sponsorApproveTimeout); - expect(data.raw.limits.ownerCanTransfer).to.be.eq(expectedLimits.ownerCanTransfer); - expect(data.raw.limits.ownerCanDestroy).to.be.eq(expectedLimits.ownerCanDestroy); - expect(data.raw.limits.transfersEnabled).to.be.eq(expectedLimits.transfersEnabled); - }); - itEth('Collection address exist', async ({helper}) => { const owner = await helper.eth.createAccountWithBalance(donor); const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233'; @@ -317,25 +267,6 @@ .setCollectionLimit('account_token_ownership_limit', '1000') .call()).to.be.rejectedWith(EXPECTED_ERROR); } - }); - - itEth('(!negative test!) Set limits', async ({helper}) => { - const invalidLimits = { - accountTokenOwnershipLimit: BigInt(Number.MAX_SAFE_INTEGER), - transfersEnabled: 3, - }; - - const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Limits', 'absolutely anything', 'ISNI'); - const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner); - - await expect(collectionEvm.methods - .setCollectionLimit(Object.keys(invalidLimits)[0], invalidLimits.accountTokenOwnershipLimit) - .call()).to.be.rejectedWith(`can't convert value to u32 "${invalidLimits.accountTokenOwnershipLimit}"`); - - await expect(collectionEvm.methods - .setCollectionLimit(Object.keys(invalidLimits)[1], invalidLimits.transfersEnabled) - .call()).to.be.rejectedWith(`can't convert value to boolean "${invalidLimits.transfersEnabled}"`); }); itEth('destroyCollection', async ({helper}) => { --- a/tests/src/eth/tokenProperties.test.ts +++ b/tests/src/eth/tokenProperties.test.ts @@ -49,52 +49,44 @@ } }); - itEth('Can be set', async({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const collection = await helper.nft.mintCollection(alice, { - tokenPropertyPermissions: [{ - key: 'testKey', - permission: { - collectionAdmin: true, - }, - }], - }); - const token = await collection.mintToken(alice); + [ + { + method: 'setProperties', + methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]], + expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}], + }, + { + method: 'setProperty' /*Soft-deprecated*/, + methodParams: ['testKey1', Buffer.from('testValue1')], + expectedProps: [{key: 'testKey1', value: 'testValue1'}], + }, + ].map(testCase => + itEth(`[${testCase.method}] Can be set`, async({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const collection = await helper.nft.mintCollection(alice, { + tokenPropertyPermissions: [{ + key: 'testKey1', + permission: { + collectionAdmin: true, + }, + }, { + key: 'testKey2', + permission: { + collectionAdmin: true, + }, + }], + }); - await collection.addAdmin(alice, {Ethereum: caller}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = helper.ethNativeContract.collection(address, 'nft', caller); - - await contract.methods.setProperties(token.tokenId, [{key: 'testKey', value: Buffer.from('testValue')}]).send({from: caller}); - - const [{value}] = await token.getProperties(['testKey']); - expect(value).to.equal('testValue'); - }); - - // Soft-deprecated - itEth('Property can be set', async({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const collection = await helper.nft.mintCollection(alice, { - tokenPropertyPermissions: [{ - key: 'testKey', - permission: { - collectionAdmin: true, - }, - }], - }); - const token = await collection.mintToken(alice); - - await collection.addAdmin(alice, {Ethereum: caller}); - - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = helper.ethNativeContract.collection(address, 'nft', caller, true); - - await contract.methods.setProperty(token.tokenId, 'testKey', Buffer.from('testValue')).send({from: caller}); - - const [{value}] = await token.getProperties(['testKey']); - expect(value).to.equal('testValue'); - }); + await collection.addAdmin(alice, {Ethereum: caller}); + const token = await collection.mintToken(alice); + + const collectionEvm = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller, testCase.method === 'setProperty'); + + await collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller}); + + const properties = await token.getProperties(); + expect(properties).to.deep.equal(testCase.expectedProps); + })); async function checkProps(helper: EthUniqueHelper, mode: TCollectionMode) { const caller = await helper.eth.createAccountWithBalance(donor); --- a/tests/src/eth/util/playgrounds/unique.dev.ts +++ b/tests/src/eth/util/playgrounds/unique.dev.ts @@ -129,8 +129,8 @@ return new web3.eth.Contract(abi as any, address, {gas: this.helper.eth.DEFAULT_GAS, ...(caller ? {from: caller} : {})}); } - collectionById(collectionId: number, mode: 'nft' | 'rft' | 'ft', caller?: string): Contract { - return this.collection(this.helper.ethAddress.fromCollectionId(collectionId), mode, caller); + collectionById(collectionId: number, mode: 'nft' | 'rft' | 'ft', caller?: string, mergeDeprecated = false): Contract { + return this.collection(this.helper.ethAddress.fromCollectionId(collectionId), mode, caller, mergeDeprecated); } rftToken(address: string, caller?: string): Contract { -- gitstuff