From c07446fc926e1d23078b842586fd7553e8a59407 Mon Sep 17 00:00:00 2001 From: ut-akuznetsov <59873862+ut-akuznetsov@users.noreply.github.com> Date: Mon, 05 Dec 2022 14:05:06 +0000 Subject: [PATCH] Merge pull request #745 from UniqueNetwork/tests/eth-helpers --- --- a/tests/src/eth/collectionAdmin.test.ts +++ b/tests/src/eth/collectionAdmin.test.ts @@ -239,19 +239,29 @@ const owner = await helper.eth.createAccountWithBalance(donor); const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - const [newAdmin] = await helper.arrange.createAccounts([10n], donor); - const newAdminCross = helper.ethCrossAccount.fromKeyringPair(newAdmin); + const [adminSub] = await helper.arrange.createAccounts([10n], donor); + const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase(); + const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub); + const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - await collectionEvm.methods.addCollectionAdminCross(newAdminCross).send(); + await collectionEvm.methods.addCollectionAdminCross(adminCrossSub).send(); + await collectionEvm.methods.addCollectionAdminCross(adminCrossEth).send(); + { - const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); - expect(adminList[0].asSubstrate.toString().toLocaleLowerCase()) - .to.be.eq(newAdmin.address.toLocaleLowerCase()); + const adminList = await helper.collection.getAdmins(collectionId); + expect(adminList).to.deep.include({Substrate: adminSub.address}); + expect(adminList).to.deep.include({Ethereum: adminEth}); } - await collectionEvm.methods.removeCollectionAdminCross(newAdminCross).send(); - const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]); + await collectionEvm.methods.removeCollectionAdminCross(adminCrossSub).send(); + await collectionEvm.methods.removeCollectionAdminCross(adminCrossEth).send(); + const adminList = await helper.collection.getAdmins(collectionId); expect(adminList.length).to.be.eq(0); + + // Non admin cannot mint: + await expect(helper.nft.mintToken(adminSub, {collectionId, owner: {Substrate: adminSub.address}})).to.be.rejectedWith(/common.PublicMintingNotAllowed/); + await expect(collectionEvm.methods.mint(adminEth).send({from: adminEth})).to.be.rejected; }); // Soft-deprecated @@ -395,16 +405,27 @@ itEth('Change owner [cross]', async ({helper}) => { const owner = await helper.eth.createAccountWithBalance(donor); - const [newOwner] = await helper.arrange.createAccounts([10n], donor); - const newOwnerCross = helper.ethCrossAccount.fromKeyringPair(newOwner); - const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); - const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + const ownerEth = await helper.eth.createAccountWithBalance(donor); + const ownerCrossEth = helper.ethCrossAccount.fromAddress(ownerEth); + const [ownerSub] = await helper.arrange.createAccounts([10n], donor); + const ownerCrossSub = helper.ethCrossAccount.fromKeyringPair(ownerSub); - expect(await collectionEvm.methods.isOwnerOrAdminCross(newOwnerCross).call()).to.be.false; + const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C'); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); - await collectionEvm.methods.changeCollectionOwnerCross(newOwnerCross).send(); + expect(await collectionEvm.methods.isOwnerOrAdminCross(ownerCrossSub).call()).to.be.false; - expect(await collectionEvm.methods.isOwnerOrAdminCross(newOwnerCross).call()).to.be.true; + // Can set ethereum owner: + await collectionEvm.methods.changeCollectionOwnerCross(ownerCrossEth).send({from: owner}); + expect(await collectionEvm.methods.isOwnerOrAdminCross(ownerCrossEth).call()).to.be.true; + expect(await helper.collection.getData(collectionId)) + .to.have.property('normalizedOwner').that.is.eq(helper.address.ethToSubstrate(ownerEth)); + + // Can set Substrate owner: + await collectionEvm.methods.changeCollectionOwnerCross(ownerCrossSub).send({from: ownerEth}); + expect(await collectionEvm.methods.isOwnerOrAdminCross(ownerCrossSub).call()).to.be.true; + expect(await helper.collection.getData(collectionId)) + .to.have.property('normalizedOwner').that.is.eq(helper.address.normalizeSubstrate(ownerSub.address)); }); itEth.skip('change owner call fee', async ({helper}) => { --- a/tests/src/eth/createFTCollection.test.ts +++ b/tests/src/eth/createFTCollection.test.ts @@ -144,7 +144,7 @@ itEth('destroyCollection', async ({helper}) => { const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Exister', DECIMALS, 'absolutely anything', 'WIWT'); + const {collectionAddress, collectionId} = await helper.eth.createFungibleCollection(owner, 'Exister', DECIMALS, 'absolutely anything', 'WIWT'); const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); const result = await collectionHelper.methods @@ -166,6 +166,7 @@ expect(await collectionHelper.methods .isCollectionExist(collectionAddress) .call()).to.be.false; + expect(await helper.collection.getData(collectionId)).to.be.null; }); }); @@ -214,12 +215,15 @@ } }); - itEth('(!negative test!) Create collection (no funds)', async ({helper}) => { + itEth('(!negative test!) cannot create collection if value !== 2', async ({helper}) => { const owner = await helper.eth.createAccountWithBalance(donor); const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); - await expect(collectionHelper.methods - .createFTCollection('Peasantry', DECIMALS, 'absolutely anything', 'TWIW') - .call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)'); + const expects = [0n, 1n, 30n].map(async value => { + await expect(collectionHelper.methods + .createFTCollection('Peasantry', DECIMALS, 'absolutely anything', 'TWIW') + .call({value: Number(value * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)'); + }); + await Promise.all(expects); }); // Soft-deprecated --- a/tests/src/eth/createNFTCollection.test.ts +++ b/tests/src/eth/createNFTCollection.test.ts @@ -308,7 +308,7 @@ itEth('destroyCollection', async ({helper}) => { const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Limits', 'absolutely anything', 'OLF'); + const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'Limits', 'absolutely anything', 'OLF'); const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); @@ -331,5 +331,6 @@ expect(await collectionHelper.methods .isCollectionExist(collectionAddress) .call()).to.be.false; + expect(await helper.collection.getData(collectionId)).to.be.null; }); }); \ No newline at end of file --- a/tests/src/eth/createRFTCollection.test.ts +++ b/tests/src/eth/createRFTCollection.test.ts @@ -340,7 +340,7 @@ itEth('destroyCollection', async ({helper}) => { const owner = await helper.eth.createAccountWithBalance(donor); - const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Limits', 'absolutely anything', 'OLF'); + const {collectionAddress, collectionId} = await helper.eth.createRFTCollection(owner, 'Limits', 'absolutely anything', 'OLF'); const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); await expect(collectionHelper.methods @@ -349,6 +349,7 @@ expect(await collectionHelper.methods .isCollectionExist(collectionAddress) - .call()).to.be.false; + .call()).to.be.false; + expect(await helper.collection.getData(collectionId)).to.be.null; }); }); --- a/tests/src/eth/destroyCollection.test.ts +++ b/tests/src/eth/destroyCollection.test.ts @@ -15,62 +15,48 @@ // along with Unique Network. If not, see . import {IKeyringPair} from '@polkadot/types/types'; -import {Pallets, requirePalletsOrSkip} from '../util'; +import {Pallets} from '../util'; import {expect, itEth, usingEthPlaygrounds} from './util'; - -describe('Destroy Collection from EVM', () => { +describe('Destroy Collection from EVM', function() { let donor: IKeyringPair; + const testCases = [ + {method: 'createRFTCollection' as const, params: ['Limits', 'absolutely anything', 'OLF'], requiredPallets: [Pallets.ReFungible]}, + {method: 'createNFTCollection' as const, params: ['Limits', 'absolutely anything', 'OLF'], requiredPallets: [Pallets.NFT]}, + {method: 'createFTCollection' as const, params: ['Limits', 'absolutely anything', 'OLF', 18], requiredPallets: [Pallets.Fungible]}, + ]; before(async function() { - await usingEthPlaygrounds(async (helper, privateKey) => { - requirePalletsOrSkip(this, helper, [Pallets.ReFungible, Pallets.NFT]); + await usingEthPlaygrounds(async (_, privateKey) => { donor = await privateKey({filename: __filename}); }); }); - - itEth('(!negative test!) RFT', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const signer = await helper.eth.createAccountWithBalance(donor); - - const unexistedCollection = helper.ethAddress.fromCollectionId(1000000); - - const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Limits', 'absolutely anything', 'OLF'); - const collectionHelper = helper.ethNativeContract.collectionHelpers(signer); - - await expect(collectionHelper.methods - .destroyCollection(collectionAddress) - .send({from: signer})).to.be.rejected; - - await expect(collectionHelper.methods - .destroyCollection(unexistedCollection) - .send({from: signer})).to.be.rejected; - - expect(await collectionHelper.methods - .isCollectionExist(unexistedCollection) - .call()).to.be.false; - }); - - itEth('(!negative test!) NFT', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const signer = await helper.eth.createAccountWithBalance(donor); - - const unexistedCollection = helper.ethAddress.fromCollectionId(1000000); - - const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Limits', 'absolutely anything', 'OLF'); - const collectionHelper = helper.ethNativeContract.collectionHelpers(signer); - - await expect(collectionHelper.methods - .destroyCollection(collectionAddress) - .send({from: signer})).to.be.rejected; - - await expect(collectionHelper.methods - .destroyCollection(unexistedCollection) - .send({from: signer})).to.be.rejected; - - expect(await collectionHelper.methods - .isCollectionExist(unexistedCollection) - .call()).to.be.false; - }); + testCases.map((testCase) => + itEth.ifWithPallets(`Cannot burn non-owned or non-existing collection ${testCase.method}`, testCase.requiredPallets, async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const signer = await helper.eth.createAccountWithBalance(donor); + + const unexistedCollection = helper.ethAddress.fromCollectionId(1000000); + + const collectionHelpers = helper.ethNativeContract.collectionHelpers(signer); + const {collectionAddress} = await helper.eth.createCollecion(testCase.method, owner, ...testCase.params as [string, string, string, number?]); + + // cannot burn collec + await expect(collectionHelpers.methods + .destroyCollection(collectionAddress) + .send({from: signer})).to.be.rejected; + + await expect(collectionHelpers.methods + .destroyCollection(unexistedCollection) + .send({from: signer})).to.be.rejected; + + expect(await collectionHelpers.methods + .isCollectionExist(unexistedCollection) + .call()).to.be.false; + + expect(await collectionHelpers.methods + .isCollectionExist(collectionAddress) + .call()).to.be.true; + })); }); --- a/tests/src/eth/fungible.test.ts +++ b/tests/src/eth/fungible.test.ts @@ -232,56 +232,64 @@ }); itEth('Can perform transferCross()', async ({helper}) => { - const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = await helper.eth.createAccountWithBalance(donor); - const to = helper.ethCrossAccount.fromAddress(receiver); - const toSubstrate = helper.ethCrossAccount.fromKeyringPair(donor); + const sender = await helper.eth.createAccountWithBalance(donor); + const receiverEth = await helper.eth.createAccountWithBalance(donor); + const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); + const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(donor); const collection = await helper.ft.mintCollection(alice); - await collection.mint(alice, 200n, {Ethereum: owner}); + await collection.mint(alice, 200n, {Ethereum: sender}); const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', sender); { - const result = await contract.methods.transferCross(to, 50).send({from: owner}); - + // Can transferCross to ethereum address: + const result = await collectionEvm.methods.transferCross(receiverCrossEth, 50).send({from: sender}); + // Check events: const event = result.events.Transfer; expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal(owner); - expect(event.returnValues.to).to.be.equal(receiver); + expect(event.returnValues.from).to.be.equal(sender); + expect(event.returnValues.to).to.be.equal(receiverEth); expect(event.returnValues.value).to.be.equal('50'); - } - - { - const balance = await contract.methods.balanceOf(owner).call(); - expect(+balance).to.equal(150); - } - - { - const balance = await contract.methods.balanceOf(receiver).call(); - expect(+balance).to.equal(50); + // Sender's balance decreased: + const ownerBalance = await collectionEvm.methods.balanceOf(sender).call(); + expect(+ownerBalance).to.equal(150); + // Receiver's balance increased: + const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call(); + expect(+receiverBalance).to.equal(50); } { - const result = await contract.methods.transferCross(toSubstrate, 50).send({from: owner}); - + // Can transferCross to substrate address: + const result = await collectionEvm.methods.transferCross(receiverCrossSub, 50).send({from: sender}); + // Check events: const event = result.events.Transfer; expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal(owner); + expect(event.returnValues.from).to.be.equal(sender); expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(donor.address)); expect(event.returnValues.value).to.be.equal('50'); + // Sender's balance decreased: + const senderBalance = await collection.getBalance({Ethereum: sender}); + expect(senderBalance).to.equal(100n); + // Receiver's balance increased: + const balance = await collection.getBalance({Substrate: donor.address}); + expect(balance).to.equal(50n); } + }); + + itEth('Cannot transferCross() more than have', async ({helper}) => { + const sender = await helper.eth.createAccountWithBalance(donor); + const receiverEth = await helper.eth.createAccountWithBalance(donor); + const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); + const BALANCE = 200n; + const BALANCE_TO_TRANSFER = BALANCE + 100n; - { - const balance = await collection.getBalance({Ethereum: owner}); - expect(balance).to.equal(100n); - } + const collection = await helper.ft.mintCollection(alice); + await collection.mint(alice, BALANCE, {Ethereum: sender}); + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', sender); - { - const balance = await collection.getBalance({Substrate: donor.address}); - expect(balance).to.equal(50n); - } - + await expect(collectionEvm.methods.transferCross(receiverCrossEth, BALANCE_TO_TRANSFER).send({from: sender})).to.be.rejected; }); itEth('Can perform transfer()', async ({helper}) => { --- a/tests/src/eth/nonFungible.test.ts +++ b/tests/src/eth/nonFungible.test.ts @@ -251,31 +251,48 @@ itEth('Can perform burnFromCross()', async ({helper}) => { const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); + const ownerSub = bob; + const ownerCrossSub = helper.ethCrossAccount.fromKeyringPair(ownerSub); + const ownerEth = await helper.eth.createAccountWithBalance(donor, 100n); + const ownerCrossEth = helper.ethCrossAccount.fromAddress(ownerEth); - const owner = bob; - const spender = await helper.eth.createAccountWithBalance(donor, 100n); + const burnerEth = await helper.eth.createAccountWithBalance(donor, 100n); + const burnerCrossEth = helper.ethCrossAccount.fromAddress(burnerEth); - const token = await collection.mintToken(minter, {Substrate: owner.address}); + const token1 = await collection.mintToken(minter, {Substrate: ownerSub.address}); + const token2 = await collection.mintToken(minter, {Ethereum: ownerEth}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft'); - const address = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = helper.ethNativeContract.collection(address, 'nft'); + // Approve tokens from substrate and ethereum: + await token1.approve(ownerSub, {Ethereum: burnerEth}); + await collectionEvm.methods.approveCross(burnerCrossEth, token2.tokenId).send({from: ownerEth}); - { - await token.approve(owner, {Ethereum: spender}); - const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner); - const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender}); - const events = result.events.Transfer; + // can burnFromCross: + const result1 = await collectionEvm.methods.burnFromCross(ownerCrossSub, token1.tokenId).send({from: burnerEth}); + const result2 = await collectionEvm.methods.burnFromCross(ownerCrossEth, token2.tokenId).send({from: burnerEth}); + const events1 = result1.events.Transfer; + const events2 = result2.events.Transfer; - expect(events).to.be.like({ - address, + // Check events for burnFromCross (substrate and ethereum): + [ + [events1, token1, helper.address.substrateToEth(ownerSub.address)], + [events2, token2, ownerEth], + ].map(burnData => { + expect(burnData[0]).to.be.like({ + address: collectionAddress, event: 'Transfer', returnValues: { - from: helper.address.substrateToEth(owner.address), + from: burnData[2], to: '0x0000000000000000000000000000000000000000', - tokenId: token.tokenId.toString(), + tokenId: burnData[1].tokenId.toString(), }, }); - } + }); + + expect(await token1.doesExist()).to.be.false; + expect(await token2.doesExist()).to.be.false; }); itEth('Can perform approveCross()', async ({helper}) => { @@ -326,6 +343,34 @@ expect(await helper.nft.getTokenOwner(collection.collectionId, token2.tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()}); }); + itEth('Can reaffirm approved address', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor, 100n); + const ownerCrossEth = helper.ethCrossAccount.fromAddress(owner); + const [receiver1, receiver2] = await helper.arrange.createAccounts([100n, 100n], donor); + const receiver1Cross = helper.ethCrossAccount.fromKeyringPair(receiver1); + const receiver2Cross = helper.ethCrossAccount.fromKeyringPair(receiver2); + const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'}); + const token1 = await collection.mintToken(minter, {Ethereum: owner}); + const token2 = await collection.mintToken(minter, {Ethereum: owner}); + const collectionEvm = helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft'); + + // Can approve and reaffirm approved address: + await collectionEvm.methods.approveCross(receiver1Cross, token1.tokenId).send({from: owner}); + await collectionEvm.methods.approveCross(receiver2Cross, token1.tokenId).send({from: owner}); + + // receiver1 cannot transferFrom: + await expect(helper.nft.transferTokenFrom(receiver1, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiver1.address})).to.be.rejected; + // receiver2 can transferFrom: + await helper.nft.transferTokenFrom(receiver2, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiver2.address}); + + // can set approved address to self address to remove approval: + await collectionEvm.methods.approveCross(receiver1Cross, token2.tokenId).send({from: owner}); + await collectionEvm.methods.approveCross(ownerCrossEth, token2.tokenId).send({from: owner}); + + // receiver1 cannot transfer token anymore: + await expect(helper.nft.transferTokenFrom(receiver1, collection.collectionId, token2.tokenId, {Ethereum: owner}, {Substrate: receiver1.address})).to.be.rejected; + }); + itEth('Can perform transferFrom()', async ({helper}) => { const owner = await helper.eth.createAccountWithBalance(donor); const spender = await helper.eth.createAccountWithBalance(donor); @@ -426,54 +471,50 @@ itEth('Can perform transferCross()', async ({helper}) => { const collection = await helper.nft.mintCollection(minter, {}); const owner = await helper.eth.createAccountWithBalance(donor); - const receiver = await helper.eth.createAccountWithBalance(donor); - const to = helper.ethCrossAccount.fromAddress(receiver); - const toSubstrate = helper.ethCrossAccount.fromKeyringPair(minter); + const receiverEth = await helper.eth.createAccountWithBalance(donor); + const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); + const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter); const {tokenId} = await collection.mintToken(minter, {Ethereum: owner}); const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); { - const result = await contract.methods.transferCross(to, tokenId).send({from: owner}); - + // Can transferCross to ethereum address: + const result = await collectionEvm.methods.transferCross(receiverCrossEth, tokenId).send({from: owner}); + // Check events: const event = result.events.Transfer; expect(event.address).to.be.equal(collectionAddress); expect(event.returnValues.from).to.be.equal(owner); - expect(event.returnValues.to).to.be.equal(receiver); + expect(event.returnValues.to).to.be.equal(receiverEth); expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`); - } - - { - const balance = await contract.methods.balanceOf(owner).call(); - expect(+balance).to.equal(0); + + // owner has balance = 0: + const ownerBalance = await collectionEvm.methods.balanceOf(owner).call(); + expect(+ownerBalance).to.equal(0); + // receiver owns token: + const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call(); + expect(+receiverBalance).to.equal(1); + expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()}); } - - { - const balance = await contract.methods.balanceOf(receiver).call(); - expect(+balance).to.equal(1); - } { - const substrateResult = await contract.methods.transferCross(toSubstrate, tokenId).send({from: receiver}); - - + // Can transferCross to substrate address: + const substrateResult = await collectionEvm.methods.transferCross(receiverCrossSub, tokenId).send({from: receiverEth}); + // Check events: const event = substrateResult.events.Transfer; expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal(receiver); + expect(event.returnValues.from).to.be.equal(receiverEth); expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(minter.address)); expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`); - } - - { - const balance = await contract.methods.balanceOf(receiver).call(); - expect(+balance).to.equal(0); - } - - { - const balance = await helper.nft.getTokensByAddress(collection.collectionId, {Substrate: minter.address}); - expect(balance).to.be.contain(tokenId); + + // owner has balance = 0: + const ownerBalance = await collectionEvm.methods.balanceOf(receiverEth).call(); + expect(+ownerBalance).to.equal(0); + // receiver owns token: + const receiverBalance = await helper.nft.getTokensByAddress(collection.collectionId, {Substrate: minter.address}); + expect(receiverBalance).to.contain(tokenId); } }); }); --- a/tests/src/eth/reFungible.test.ts +++ b/tests/src/eth/reFungible.test.ts @@ -363,56 +363,71 @@ }); itEth('Can perform transferCross()', async ({helper}) => { - const caller = await helper.eth.createAccountWithBalance(donor); - const receiver = await helper.eth.createAccountWithBalance(donor); - const to = helper.ethCrossAccount.fromAddress(receiver); - const toSubstrate = helper.ethCrossAccount.fromKeyringPair(minter); + const sender = await helper.eth.createAccountWithBalance(donor); + const receiverEth = await helper.eth.createAccountWithBalance(donor); + const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth); + const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter); + const collection = await helper.rft.mintCollection(minter, {}); const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); - const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', sender); - const {tokenId} = await collection.mintToken(minter, 1n, {Ethereum: caller}); + const token = await collection.mintToken(minter, 50n, {Ethereum: sender}); { - const result = await contract.methods.transferCross(to, tokenId).send({from: caller}); - + // Can transferCross to ethereum address: + const result = await collectionEvm.methods.transferCross(receiverCrossEth, token.tokenId).send({from: sender}); + // Check events: const event = result.events.Transfer; expect(event.address).to.equal(collectionAddress); - expect(event.returnValues.from).to.equal(caller); - expect(event.returnValues.to).to.equal(receiver); - expect(event.returnValues.tokenId).to.equal(tokenId.toString()); - } - - { - const balance = await contract.methods.balanceOf(caller).call(); - expect(+balance).to.equal(0); - } - - { - const balance = await contract.methods.balanceOf(receiver).call(); - expect(+balance).to.equal(1); + expect(event.returnValues.from).to.equal(sender); + expect(event.returnValues.to).to.equal(receiverEth); + expect(event.returnValues.tokenId).to.equal(token.tokenId.toString()); + // Sender's balance decreased: + const senderBalance = await collectionEvm.methods.balanceOf(sender).call(); + expect(+senderBalance).to.equal(0); + expect(await token.getBalance({Ethereum: sender})).to.eq(0n); + // Receiver's balance increased: + const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call(); + expect(+receiverBalance).to.equal(1); + expect(await token.getBalance({Ethereum: receiverEth})).to.eq(50n); } { - const substrateResult = await contract.methods.transferCross(toSubstrate, tokenId).send({from: receiver}); - - + // Can transferCross to substrate address: + const substrateResult = await collectionEvm.methods.transferCross(receiverCrossSub, token.tokenId).send({from: receiverEth}); + // Check events: const event = substrateResult.events.Transfer; expect(event.address).to.be.equal(collectionAddress); - expect(event.returnValues.from).to.be.equal(receiver); + expect(event.returnValues.from).to.be.equal(receiverEth); expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(minter.address)); - expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`); + expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`); + // Sender's balance decreased: + const senderBalance = await collectionEvm.methods.balanceOf(receiverEth).call(); + expect(+senderBalance).to.equal(0); + expect(await token.getBalance({Ethereum: receiverEth})).to.eq(0n); + // Receiver's balance increased: + const receiverBalance = await helper.nft.getTokensByAddress(collection.collectionId, {Substrate: minter.address}); + expect(receiverBalance).to.contain(token.tokenId); + expect(await token.getBalance({Substrate: minter.address})).to.eq(50n); } + }); + + itEth.skip('Cannot transferCross with invalid params', async ({helper}) => { + const sender = await helper.eth.createAccountWithBalance(donor); + const tokenOwner = await helper.eth.createAccountWithBalance(donor); + const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter); - { - const balance = await contract.methods.balanceOf(receiver).call(); - expect(+balance).to.equal(0); - } + const collection = await helper.rft.mintCollection(minter, {}); + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', sender); - { - const balance = await helper.nft.getTokensByAddress(collection.collectionId, {Substrate: minter.address}); - expect(balance).to.be.contain(tokenId); - } + await collection.mintToken(minter, 50n, {Ethereum: sender}); + const notSendersToken = await collection.mintToken(minter, 50n, {Ethereum: tokenOwner}); + // Cannot transferCross someone else's token: + await expect(collectionEvm.methods.transferCross(receiverCrossSub, notSendersToken.tokenId).send({from: sender})).to.be.rejected; + // FIXME: (transaction successful): Cannot transfer token if it does not exist: + await expect(collectionEvm.methods.transferCross(receiverCrossSub, 999999).send({from: sender})).to.be.rejected; }); itEth('transfer event on transfer from partial ownership to full ownership', async ({helper}) => { --- a/tests/src/eth/util/playgrounds/unique.dev.ts +++ b/tests/src/eth/util/playgrounds/unique.dev.ts @@ -186,11 +186,12 @@ return await this.helper.callRpc('api.rpc.eth.call', [{from: signer, to: contractAddress, data: abi}]); } - async createCollecion(functionName: string, signer: string, name: string, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> { + async createCollecion(functionName: 'createNFTCollection' | 'createRFTCollection' | 'createFTCollection', signer: string, name: string, description: string, tokenPrefix: string, decimals?: number): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> { const collectionCreationPrice = this.helper.balance.getCollectionCreationPrice(); const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer); - const result = await collectionHelper.methods[functionName](name, description, tokenPrefix).send({value: Number(collectionCreationPrice)}); + const functionParams = functionName === 'createFTCollection' ? [name, decimals, description, tokenPrefix] : [name, description, tokenPrefix]; + const result = await collectionHelper.methods[functionName](...functionParams).send({value: Number(collectionCreationPrice)}); const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId); const collectionId = this.helper.ethAddress.extractCollectionId(collectionAddress); @@ -217,17 +218,8 @@ return this.createCollecion('createRFTCollection', signer, name, description, tokenPrefix); } - async createFungibleCollection(signer: string, name: string, decimals: number, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[]}> { - const collectionCreationPrice = this.helper.balance.getCollectionCreationPrice(); - const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer); - - const result = await collectionHelper.methods.createFTCollection(name, decimals, description, tokenPrefix).send({value: Number(collectionCreationPrice)}); - const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId); - const collectionId = this.helper.ethAddress.extractCollectionId(collectionAddress); - - const events = this.helper.eth.normalizeEvents(result.events); - - return {collectionId, collectionAddress, events}; + createFungibleCollection(signer: string, name: string, decimals: number, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[]}> { + return this.createCollecion('createFTCollection', signer, name, description, tokenPrefix, decimals); } async createERC721MetadataCompatibleRFTCollection(signer: string, name: string, description: string, tokenPrefix: string, baseUri: string): Promise<{collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> { -- gitstuff