--- a/tests/src/eth/createNFTCollection.test.ts +++ b/tests/src/eth/createNFTCollection.test.ts @@ -15,47 +15,49 @@ // along with Unique Network. If not, see . import {evmToAddress} from '@polkadot/util-crypto'; -import {expect} from 'chai'; -import {getCreatedCollectionCount, getDetailedCollectionInfo} from '../util/helpers'; -import { - evmCollectionHelpers, - collectionIdToAddress, - createEthAccount, - createEthAccountWithBalance, - evmCollection, - itWeb3, - getCollectionAddressFromResult, -} from './util/helpers'; +import {IKeyringPair} from '@polkadot/types/types'; +import {expect, itEth, usingEthPlaygrounds} from './util/playgrounds'; describe('Create NFT collection from EVM', () => { - itWeb3('Create collection', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const collectionHelper = evmCollectionHelpers(web3, owner); - const collectionName = 'CollectionEVM'; + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = privateKey('//Alice'); + }); + }); + + itEth('Create collection', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const name = 'CollectionEVM'; const description = 'Some description'; - const tokenPrefix = 'token prefix'; - - const collectionCountBefore = await getCreatedCollectionCount(api); - const result = await collectionHelper.methods - .createNonfungibleCollection(collectionName, description, tokenPrefix) - .send(); - const collectionCountAfter = await getCreatedCollectionCount(api); - - const {collectionId, collection} = await getCollectionAddressFromResult(api, result); + const prefix = 'token prefix'; + + // todo:playgrounds this might fail when in async environment. + const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created; + const {collectionId} = await helper.eth.createNonfungibleCollection(owner, name, description, prefix); + const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created; + + const collection = helper.nft.getCollectionObject(collectionId); + const data = (await collection.getData())!; + expect(collectionCountAfter - collectionCountBefore).to.be.eq(1); expect(collectionId).to.be.eq(collectionCountAfter); - expect(collection.name.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(collectionName); - expect(collection.description.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(description); - expect(collection.tokenPrefix.toHuman()).to.be.eq(tokenPrefix); - expect(collection.mode.isNft).to.be.true; + expect(data.name).to.be.eq(name); + expect(data.description).to.be.eq(description); + expect(data.raw.tokenPrefix).to.be.eq(prefix); + expect(data.raw.mode).to.be.eq('NFT'); }); - itWeb3('Check collection address exist', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const collectionHelpers = evmCollectionHelpers(web3, owner); - - const expectedCollectionId = await getCreatedCollectionCount(api) + 1; - const expectedCollectionAddress = collectionIdToAddress(expectedCollectionId); + // todo:playgrounds this test will fail when in async environment. + itEth('Check collection address exist', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1; + const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId); + const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner); + expect(await collectionHelpers.methods .isCollectionExist(expectedCollectionAddress) .call()).to.be.false; @@ -69,31 +71,30 @@ .call()).to.be.true; }); - itWeb3('Set sponsorship', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const collectionHelpers = evmCollectionHelpers(web3, owner); - let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send(); - const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); - const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const collectionEvm = evmCollection(web3, owner, collectionIdAddress); - result = await collectionEvm.methods.setCollectionSponsor(sponsor).send(); - let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!; - expect(collectionSub.sponsorship.isUnconfirmed).to.be.true; - const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format; - expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format))); - await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor'); - const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress); + itEth('Set sponsorship', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const ss58Format = helper.chain.getChainProperties().ss58Format; + const {collectionId, collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Sponsor', 'absolutely anything', 'ROC'); + + const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + await collection.methods.setCollectionSponsor(sponsor).send(); + + let data = (await helper.nft.getData(collectionId))!; + expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); + + await expect(collection.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(); - collectionSub = (await getDetailedCollectionInfo(api, collectionId))!; - expect(collectionSub.sponsorship.isConfirmed).to.be.true; - expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format))); + + data = (await helper.nft.getData(collectionId))!; + expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); }); - itWeb3('Set limits', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const collectionHelpers = evmCollectionHelpers(web3, owner); - const result = await collectionHelpers.methods.createNonfungibleCollection('Const collection', '5', '5').send(); - const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); + itEth('Set limits', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionId, collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Limits', 'absolutely anything', 'FLO'); const limits = { accountTokenOwnershipLimit: 1000, sponsoredDataSize: 1024, @@ -106,124 +107,122 @@ transfersEnabled: false, }; - const collectionEvm = evmCollection(web3, owner, collectionIdAddress); - await collectionEvm.methods['setCollectionLimit(string,uint32)']('accountTokenOwnershipLimit', limits.accountTokenOwnershipLimit).send(); - await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsoredDataSize', limits.sponsoredDataSize).send(); - await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsoredDataRateLimit', limits.sponsoredDataRateLimit).send(); - await collectionEvm.methods['setCollectionLimit(string,uint32)']('tokenLimit', limits.tokenLimit).send(); - await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsorTransferTimeout', limits.sponsorTransferTimeout).send(); - await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsorApproveTimeout', limits.sponsorApproveTimeout).send(); - await collectionEvm.methods['setCollectionLimit(string,bool)']('ownerCanTransfer', limits.ownerCanTransfer).send(); - await collectionEvm.methods['setCollectionLimit(string,bool)']('ownerCanDestroy', limits.ownerCanDestroy).send(); - await collectionEvm.methods['setCollectionLimit(string,bool)']('transfersEnabled', limits.transfersEnabled).send(); + const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + await collection.methods['setCollectionLimit(string,uint32)']('accountTokenOwnershipLimit', limits.accountTokenOwnershipLimit).send(); + await collection.methods['setCollectionLimit(string,uint32)']('sponsoredDataSize', limits.sponsoredDataSize).send(); + await collection.methods['setCollectionLimit(string,uint32)']('sponsoredDataRateLimit', limits.sponsoredDataRateLimit).send(); + await collection.methods['setCollectionLimit(string,uint32)']('tokenLimit', limits.tokenLimit).send(); + await collection.methods['setCollectionLimit(string,uint32)']('sponsorTransferTimeout', limits.sponsorTransferTimeout).send(); + await collection.methods['setCollectionLimit(string,uint32)']('sponsorApproveTimeout', limits.sponsorApproveTimeout).send(); + await collection.methods['setCollectionLimit(string,bool)']('ownerCanTransfer', limits.ownerCanTransfer).send(); + await collection.methods['setCollectionLimit(string,bool)']('ownerCanDestroy', limits.ownerCanDestroy).send(); + await collection.methods['setCollectionLimit(string,bool)']('transfersEnabled', limits.transfersEnabled).send(); - const collectionSub = (await getDetailedCollectionInfo(api, collectionId))!; - expect(collectionSub.limits.accountTokenOwnershipLimit.unwrap().toNumber()).to.be.eq(limits.accountTokenOwnershipLimit); - expect(collectionSub.limits.sponsoredDataSize.unwrap().toNumber()).to.be.eq(limits.sponsoredDataSize); - expect(collectionSub.limits.sponsoredDataRateLimit.unwrap().asBlocks.toNumber()).to.be.eq(limits.sponsoredDataRateLimit); - expect(collectionSub.limits.tokenLimit.unwrap().toNumber()).to.be.eq(limits.tokenLimit); - expect(collectionSub.limits.sponsorTransferTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorTransferTimeout); - expect(collectionSub.limits.sponsorApproveTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorApproveTimeout); - expect(collectionSub.limits.ownerCanTransfer.toHuman()).to.be.eq(limits.ownerCanTransfer); - expect(collectionSub.limits.ownerCanDestroy.toHuman()).to.be.eq(limits.ownerCanDestroy); - expect(collectionSub.limits.transfersEnabled.toHuman()).to.be.eq(limits.transfersEnabled); + const data = (await helper.nft.getData(collectionId))!; + expect(data.raw.limits.accountTokenOwnershipLimit).to.be.eq(limits.accountTokenOwnershipLimit); + expect(data.raw.limits.sponsoredDataSize).to.be.eq(limits.sponsoredDataSize); + expect(data.raw.limits.sponsoredDataRateLimit.blocks).to.be.eq(limits.sponsoredDataRateLimit); + expect(data.raw.limits.tokenLimit).to.be.eq(limits.tokenLimit); + expect(data.raw.limits.sponsorTransferTimeout).to.be.eq(limits.sponsorTransferTimeout); + expect(data.raw.limits.sponsorApproveTimeout).to.be.eq(limits.sponsorApproveTimeout); + expect(data.raw.limits.ownerCanTransfer).to.be.eq(limits.ownerCanTransfer); + expect(data.raw.limits.ownerCanDestroy).to.be.eq(limits.ownerCanDestroy); + expect(data.raw.limits.transfersEnabled).to.be.eq(limits.transfersEnabled); }); - itWeb3('Collection address exist', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + itEth('Collection address exist', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233'; - const collectionHelpers = evmCollectionHelpers(web3, owner); - expect(await collectionHelpers.methods - .isCollectionExist(collectionAddressForNonexistentCollection).call()) + expect(await helper.ethNativeContract.collectionHelpers(collectionAddressForNonexistentCollection) + .methods.isCollectionExist(collectionAddressForNonexistentCollection).call()) .to.be.false; - const result = await collectionHelpers.methods.createNonfungibleCollection('Collection address exist', '7', '7').send(); - const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); - expect(await collectionHelpers.methods - .isCollectionExist(collectionIdAddress).call()) + const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Exister', 'absolutely anything', 'EVC'); + expect(await helper.ethNativeContract.collectionHelpers(collectionAddress) + .methods.isCollectionExist(collectionAddress).call()) .to.be.true; }); }); describe('(!negative tests!) Create NFT collection from EVM', () => { - itWeb3('(!negative test!) Create collection (bad lengths)', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const helper = evmCollectionHelpers(web3, owner); + let donor: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = privateKey('//Alice'); + }); + }); + + itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); { - const MAX_NAME_LENGHT = 64; - const collectionName = 'A'.repeat(MAX_NAME_LENGHT + 1); + const MAX_NAME_LENGTH = 64; + const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1); const description = 'A'; const tokenPrefix = 'A'; - - await expect(helper.methods + + await expect(collectionHelper.methods .createNonfungibleCollection(collectionName, description, tokenPrefix) - .call()).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGHT); + .call()).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH); } - { - const MAX_DESCRIPTION_LENGHT = 256; + { + const MAX_DESCRIPTION_LENGTH = 256; const collectionName = 'A'; - const description = 'A'.repeat(MAX_DESCRIPTION_LENGHT + 1); + const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1); const tokenPrefix = 'A'; - await expect(helper.methods + await expect(collectionHelper.methods .createNonfungibleCollection(collectionName, description, tokenPrefix) - .call()).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGHT); + .call()).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH); } - { - const MAX_TOKEN_PREFIX_LENGHT = 16; + { + const MAX_TOKEN_PREFIX_LENGTH = 16; const collectionName = 'A'; const description = 'A'; - const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGHT + 1); - await expect(helper.methods + const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1); + await expect(collectionHelper.methods .createNonfungibleCollection(collectionName, description, tokenPrefix) - .call()).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGHT); + .call()).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH); } }); - itWeb3('(!negative test!) Create collection (no funds)', async ({web3}) => { - const owner = createEthAccount(web3); - const helper = evmCollectionHelpers(web3, owner); - const collectionName = 'A'; - const description = 'A'; - const tokenPrefix = 'A'; - - await expect(helper.methods - .createNonfungibleCollection(collectionName, description, tokenPrefix) + itEth('(!negative test!) Create collection (no funds)', async ({helper}) => { + const owner = helper.eth.createAccount(); + const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); + await expect(collectionHelper.methods + .createNonfungibleCollection('Peasantry', 'absolutely anything', 'CVE') .call()).to.be.rejectedWith('NotSufficientFounds'); }); - itWeb3('(!negative test!) Check owner', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const notOwner = createEthAccount(web3); - const collectionHelpers = evmCollectionHelpers(web3, owner); - const result = await collectionHelpers.methods.createNonfungibleCollection('A', 'A', 'A').send(); - const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); - const contractEvmFromNotOwner = evmCollection(web3, notOwner, collectionIdAddress); + itEth('(!negative test!) Check owner', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const malfeasant = helper.eth.createAccount(); + const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Transgressed', 'absolutely anything', 'COR'); + const malfeasantCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', malfeasant); const EXPECTED_ERROR = 'NoPermission'; { - const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - await expect(contractEvmFromNotOwner.methods + const sponsor = await helper.eth.createAccountWithBalance(donor); + await expect(malfeasantCollection.methods .setCollectionSponsor(sponsor) .call()).to.be.rejectedWith(EXPECTED_ERROR); - const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress); + const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor); await expect(sponsorCollection.methods .confirmCollectionSponsorship() .call()).to.be.rejectedWith('caller is not set as sponsor'); } { - await expect(contractEvmFromNotOwner.methods + await expect(malfeasantCollection.methods .setCollectionLimit('account_token_ownership_limit', '1000') .call()).to.be.rejectedWith(EXPECTED_ERROR); } }); - itWeb3('(!negative test!) Set limits', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const collectionHelpers = evmCollectionHelpers(web3, owner); - const result = await collectionHelpers.methods.createNonfungibleCollection('Schema collection', 'A', 'A').send(); - const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); - const collectionEvm = evmCollection(web3, owner, collectionIdAddress); + itEth('(!negative test!) Set limits', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Limits', 'absolutely anything', 'OLF'); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); await expect(collectionEvm.methods .setCollectionLimit('badLimit', 'true') .call()).to.be.rejectedWith('unknown boolean limit "badLimit"'); --- a/tests/src/eth/createRFTCollection.test.ts +++ b/tests/src/eth/createRFTCollection.test.ts @@ -15,51 +15,50 @@ // along with Unique Network. If not, see . import {evmToAddress} from '@polkadot/util-crypto'; -import {expect} from 'chai'; -import {getCreatedCollectionCount, getDetailedCollectionInfo, requirePallets, Pallets} from '../util/helpers'; -import { - evmCollectionHelpers, - collectionIdToAddress, - createEthAccount, - createEthAccountWithBalance, - evmCollection, - itWeb3, - getCollectionAddressFromResult, -} from './util/helpers'; +import {IKeyringPair} from '@polkadot/types/types'; +import {Pallets, requirePalletsOrSkip} from '../util/playgrounds'; +import {expect, itEth, usingEthPlaygrounds} from './util/playgrounds'; describe('Create RFT collection from EVM', () => { + let donor: IKeyringPair; + before(async function() { - await requirePallets(this, [Pallets.ReFungible]); + await usingEthPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + donor = privateKey('//Alice'); + }); }); - itWeb3('Create collection', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const collectionHelper = evmCollectionHelpers(web3, owner); - const collectionName = 'CollectionEVM'; + itEth('Create collection', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const name = 'CollectionEVM'; const description = 'Some description'; - const tokenPrefix = 'token prefix'; + const prefix = 'token prefix'; - const collectionCountBefore = await getCreatedCollectionCount(api); - const result = await collectionHelper.methods - .createRFTCollection(collectionName, description, tokenPrefix) - .send(); - const collectionCountAfter = await getCreatedCollectionCount(api); + // todo:playgrounds this might fail when in async environment. + const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created; + const {collectionId} = await helper.eth.createRefungibleCollection(owner, name, description, prefix); + const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created; - const {collectionId, collection} = await getCollectionAddressFromResult(api, result); + const data = (await helper.rft.getData(collectionId))!; + expect(collectionCountAfter - collectionCountBefore).to.be.eq(1); expect(collectionId).to.be.eq(collectionCountAfter); - expect(collection.name.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(collectionName); - expect(collection.description.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(description); - expect(collection.tokenPrefix.toHuman()).to.be.eq(tokenPrefix); - expect(collection.mode.isReFungible).to.be.true; + expect(data.name).to.be.eq(name); + expect(data.description).to.be.eq(description); + expect(data.raw.tokenPrefix).to.be.eq(prefix); + expect(data.raw.mode).to.be.eq('ReFungible'); }); - itWeb3('Check collection address exist', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const collectionHelpers = evmCollectionHelpers(web3, owner); - - const expectedCollectionId = await getCreatedCollectionCount(api) + 1; - const expectedCollectionAddress = collectionIdToAddress(expectedCollectionId); + // todo:playgrounds this test will fail when in async environment. + itEth('Check collection address exist', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1; + const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId); + const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner); + expect(await collectionHelpers.methods .isCollectionExist(expectedCollectionAddress) .call()).to.be.false; @@ -73,31 +72,30 @@ .call()).to.be.true; }); - itWeb3('Set sponsorship', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const collectionHelpers = evmCollectionHelpers(web3, owner); - let result = await collectionHelpers.methods.createRFTCollection('Sponsor collection', '1', '1').send(); - const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); - const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const collectionEvm = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'}); - result = await collectionEvm.methods.setCollectionSponsor(sponsor).send(); - let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!; - expect(collectionSub.sponsorship.isUnconfirmed).to.be.true; - const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format; - expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format))); - await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor'); - const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress); + itEth('Set sponsorship', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const sponsor = await helper.eth.createAccountWithBalance(donor); + const ss58Format = helper.chain.getChainProperties().ss58Format; + const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Sponsor', 'absolutely anything', 'ENVY'); + + const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner); + await collection.methods.setCollectionSponsor(sponsor).send(); + + let data = (await helper.rft.getData(collectionId))!; + expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); + + await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor'); + + const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor); await sponsorCollection.methods.confirmCollectionSponsorship().send(); - collectionSub = (await getDetailedCollectionInfo(api, collectionId))!; - expect(collectionSub.sponsorship.isConfirmed).to.be.true; - expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format))); + + data = (await helper.rft.getData(collectionId))!; + expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format))); }); - itWeb3('Set limits', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const collectionHelpers = evmCollectionHelpers(web3, owner); - const result = await collectionHelpers.methods.createRFTCollection('Const collection', '5', '5').send(); - const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); + itEth('Set limits', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Limits', 'absolutely anything', 'INSI'); const limits = { accountTokenOwnershipLimit: 1000, sponsoredDataSize: 1024, @@ -110,128 +108,122 @@ transfersEnabled: false, }; - const collectionEvm = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'}); - await collectionEvm.methods['setCollectionLimit(string,uint32)']('accountTokenOwnershipLimit', limits.accountTokenOwnershipLimit).send(); - await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsoredDataSize', limits.sponsoredDataSize).send(); - await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsoredDataRateLimit', limits.sponsoredDataRateLimit).send(); - await collectionEvm.methods['setCollectionLimit(string,uint32)']('tokenLimit', limits.tokenLimit).send(); - await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsorTransferTimeout', limits.sponsorTransferTimeout).send(); - await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsorApproveTimeout', limits.sponsorApproveTimeout).send(); - await collectionEvm.methods['setCollectionLimit(string,bool)']('ownerCanTransfer', limits.ownerCanTransfer).send(); - await collectionEvm.methods['setCollectionLimit(string,bool)']('ownerCanDestroy', limits.ownerCanDestroy).send(); - await collectionEvm.methods['setCollectionLimit(string,bool)']('transfersEnabled', limits.transfersEnabled).send(); + const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner); + await collection.methods['setCollectionLimit(string,uint32)']('accountTokenOwnershipLimit', limits.accountTokenOwnershipLimit).send(); + await collection.methods['setCollectionLimit(string,uint32)']('sponsoredDataSize', limits.sponsoredDataSize).send(); + await collection.methods['setCollectionLimit(string,uint32)']('sponsoredDataRateLimit', limits.sponsoredDataRateLimit).send(); + await collection.methods['setCollectionLimit(string,uint32)']('tokenLimit', limits.tokenLimit).send(); + await collection.methods['setCollectionLimit(string,uint32)']('sponsorTransferTimeout', limits.sponsorTransferTimeout).send(); + await collection.methods['setCollectionLimit(string,uint32)']('sponsorApproveTimeout', limits.sponsorApproveTimeout).send(); + await collection.methods['setCollectionLimit(string,bool)']('ownerCanTransfer', limits.ownerCanTransfer).send(); + await collection.methods['setCollectionLimit(string,bool)']('ownerCanDestroy', limits.ownerCanDestroy).send(); + await collection.methods['setCollectionLimit(string,bool)']('transfersEnabled', limits.transfersEnabled).send(); - const collectionSub = (await getDetailedCollectionInfo(api, collectionId))!; - expect(collectionSub.limits.accountTokenOwnershipLimit.unwrap().toNumber()).to.be.eq(limits.accountTokenOwnershipLimit); - expect(collectionSub.limits.sponsoredDataSize.unwrap().toNumber()).to.be.eq(limits.sponsoredDataSize); - expect(collectionSub.limits.sponsoredDataRateLimit.unwrap().asBlocks.toNumber()).to.be.eq(limits.sponsoredDataRateLimit); - expect(collectionSub.limits.tokenLimit.unwrap().toNumber()).to.be.eq(limits.tokenLimit); - expect(collectionSub.limits.sponsorTransferTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorTransferTimeout); - expect(collectionSub.limits.sponsorApproveTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorApproveTimeout); - expect(collectionSub.limits.ownerCanTransfer.toHuman()).to.be.eq(limits.ownerCanTransfer); - expect(collectionSub.limits.ownerCanDestroy.toHuman()).to.be.eq(limits.ownerCanDestroy); - expect(collectionSub.limits.transfersEnabled.toHuman()).to.be.eq(limits.transfersEnabled); + const data = (await helper.rft.getData(collectionId))!; + expect(data.raw.limits.accountTokenOwnershipLimit).to.be.eq(limits.accountTokenOwnershipLimit); + expect(data.raw.limits.sponsoredDataSize).to.be.eq(limits.sponsoredDataSize); + expect(data.raw.limits.sponsoredDataRateLimit.blocks).to.be.eq(limits.sponsoredDataRateLimit); + expect(data.raw.limits.tokenLimit).to.be.eq(limits.tokenLimit); + expect(data.raw.limits.sponsorTransferTimeout).to.be.eq(limits.sponsorTransferTimeout); + expect(data.raw.limits.sponsorApproveTimeout).to.be.eq(limits.sponsorApproveTimeout); + expect(data.raw.limits.ownerCanTransfer).to.be.eq(limits.ownerCanTransfer); + expect(data.raw.limits.ownerCanDestroy).to.be.eq(limits.ownerCanDestroy); + expect(data.raw.limits.transfersEnabled).to.be.eq(limits.transfersEnabled); }); - itWeb3('Collection address exist', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + itEth('Collection address exist', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233'; - const collectionHelpers = evmCollectionHelpers(web3, owner); - expect(await collectionHelpers.methods - .isCollectionExist(collectionAddressForNonexistentCollection).call()) + expect(await helper.ethNativeContract.collectionHelpers(collectionAddressForNonexistentCollection) + .methods.isCollectionExist(collectionAddressForNonexistentCollection).call()) .to.be.false; - const result = await collectionHelpers.methods.createRFTCollection('Collection address exist', '7', '7').send(); - const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); - expect(await collectionHelpers.methods - .isCollectionExist(collectionIdAddress).call()) + const {collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Exister', 'absolutely anything', 'WIWT'); + expect(await helper.ethNativeContract.collectionHelpers(collectionAddress) + .methods.isCollectionExist(collectionAddress).call()) .to.be.true; }); }); describe('(!negative tests!) Create RFT collection from EVM', () => { + let donor: IKeyringPair; + before(async function() { - await requirePallets(this, [Pallets.ReFungible]); + await usingEthPlaygrounds(async (helper, privateKey) => { + requirePalletsOrSkip(this, helper, [Pallets.ReFungible]); + donor = privateKey('//Alice'); + }); }); - itWeb3('(!negative test!) Create collection (bad lengths)', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const helper = evmCollectionHelpers(web3, owner); + itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); { - const MAX_NAME_LENGHT = 64; - const collectionName = 'A'.repeat(MAX_NAME_LENGHT + 1); + const MAX_NAME_LENGTH = 64; + const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1); const description = 'A'; const tokenPrefix = 'A'; - - await expect(helper.methods + + await expect(collectionHelper.methods .createRFTCollection(collectionName, description, tokenPrefix) - .call()).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGHT); - + .call()).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH); } - { - const MAX_DESCRIPTION_LENGHT = 256; + { + const MAX_DESCRIPTION_LENGTH = 256; const collectionName = 'A'; - const description = 'A'.repeat(MAX_DESCRIPTION_LENGHT + 1); + const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1); const tokenPrefix = 'A'; - await expect(helper.methods + await expect(collectionHelper.methods .createRFTCollection(collectionName, description, tokenPrefix) - .call()).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGHT); + .call()).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH); } - { - const MAX_TOKEN_PREFIX_LENGHT = 16; + { + const MAX_TOKEN_PREFIX_LENGTH = 16; const collectionName = 'A'; const description = 'A'; - const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGHT + 1); - await expect(helper.methods + const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1); + await expect(collectionHelper.methods .createRFTCollection(collectionName, description, tokenPrefix) - .call()).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGHT); + .call()).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH); } }); - itWeb3('(!negative test!) Create collection (no funds)', async ({web3}) => { - const owner = createEthAccount(web3); - const helper = evmCollectionHelpers(web3, owner); - const collectionName = 'A'; - const description = 'A'; - const tokenPrefix = 'A'; - - await expect(helper.methods - .createRFTCollection(collectionName, description, tokenPrefix) + itEth('(!negative test!) Create collection (no funds)', async ({helper}) => { + const owner = helper.eth.createAccount(); + const collectionHelper = helper.ethNativeContract.collectionHelpers(owner); + await expect(collectionHelper.methods + .createRFTCollection('Peasantry', 'absolutely anything', 'TWIW') .call()).to.be.rejectedWith('NotSufficientFounds'); }); - itWeb3('(!negative test!) Check owner', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const notOwner = createEthAccount(web3); - const collectionHelpers = evmCollectionHelpers(web3, owner); - const result = await collectionHelpers.methods.createRFTCollection('A', 'A', 'A').send(); - const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); - const contractEvmFromNotOwner = evmCollection(web3, notOwner, collectionIdAddress, {type: 'ReFungible'}); + itEth('(!negative test!) Check owner', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const peasant = helper.eth.createAccount(); + const {collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Transgressed', 'absolutely anything', 'YVNE'); + const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', peasant); const EXPECTED_ERROR = 'NoPermission'; { - const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - await expect(contractEvmFromNotOwner.methods + const sponsor = await helper.eth.createAccountWithBalance(donor); + await expect(peasantCollection.methods .setCollectionSponsor(sponsor) .call()).to.be.rejectedWith(EXPECTED_ERROR); - const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress); + const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor); await expect(sponsorCollection.methods .confirmCollectionSponsorship() .call()).to.be.rejectedWith('caller is not set as sponsor'); } { - await expect(contractEvmFromNotOwner.methods + await expect(peasantCollection.methods .setCollectionLimit('account_token_ownership_limit', '1000') .call()).to.be.rejectedWith(EXPECTED_ERROR); } }); - itWeb3('(!negative test!) Set limits', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const collectionHelpers = evmCollectionHelpers(web3, owner); - const result = await collectionHelpers.methods.createRFTCollection('Schema collection', 'A', 'A').send(); - const {collectionIdAddress} = await getCollectionAddressFromResult(api, result); - const collectionEvm = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'}); + itEth('(!negative test!) Set limits', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const {collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Limits', 'absolutely anything', 'ISNI'); + const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner); await expect(collectionEvm.methods .setCollectionLimit('badLimit', 'true') .call()).to.be.rejectedWith('unknown boolean limit "badLimit"'); --- a/tests/src/eth/crossTransfer.test.ts +++ b/tests/src/eth/crossTransfer.test.ts @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -import {createCollectionExpectSuccess, +/*import {createCollectionExpectSuccess, createFungibleItemExpectSuccess, transferExpectSuccess, transferFromExpectSuccess, @@ -23,81 +23,94 @@ import {collectionIdToAddress, createEthAccountWithBalance, subToEth, - GAS_ARGS, itWeb3} from './util/helpers'; + GAS_ARGS, itEth} from './util/helpers'; import fungibleAbi from './fungibleAbi.json'; -import nonFungibleAbi from './nonFungibleAbi.json'; +import nonFungibleAbi from './nonFungibleAbi.json';*/ +import {itEth, usingEthPlaygrounds} from './util/playgrounds'; +import {CrossAccountId} from '../util/playgrounds/unique'; +import {IKeyringPair} from '@polkadot/types/types'; describe('Token transfer between substrate address and EVM address. Fungible', () => { - itWeb3('The private key X create a substrate address. Alice sends a token to the corresponding EVM address, and X can send it to Bob in the substrate', async ({privateKeyWrapper}) => { - const collection = await createCollectionExpectSuccess({ - name: 'token name', - mode: {type: 'Fungible', decimalPoints: 0}, + let donor: IKeyringPair; + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = privateKey('//Alice'); + [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); }); - const alice = privateKeyWrapper('//Alice'); - const bob = privateKeyWrapper('//Bob'); - const charlie = privateKeyWrapper('//Charlie'); - await setCollectionLimitsExpectSuccess(alice, collection, {ownerCanTransfer: true}); - await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Substrate: alice.address}); - await transferExpectSuccess(collection, 0, alice, {Ethereum: subToEth(charlie.address)} , 200, 'Fungible'); - await transferFromExpectSuccess(collection, 0, alice, {Ethereum: subToEth(charlie.address)}, charlie, 50, 'Fungible'); - await transferExpectSuccess(collection, 0, charlie, bob, 50, 'Fungible'); }); + + itEth('The private key X create a substrate address. Alice sends a token to the corresponding EVM address, and X can send it to Bob in the substrate', async ({helper}) => { + const bobCA = CrossAccountId.fromKeyring(bob); + const charlieCA = CrossAccountId.fromKeyring(charlie); - itWeb3('The private key X create a EVM address. Alice sends a token to the substrate address corresponding to this EVM address, and X can send it to Bob in the EVM', async ({api, web3, privateKeyWrapper}) => { - const collection = await createCollectionExpectSuccess({ - name: 'token name', - mode: {type: 'Fungible', decimalPoints: 0}, - }); - const alice = privateKeyWrapper('//Alice'); - const bob = privateKeyWrapper('//Bob'); - await setCollectionLimitsExpectSuccess(alice, collection, {ownerCanTransfer: true}); - const bobProxy = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const aliceProxy = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const collection = await helper.ft.mintCollection(alice); + await collection.setLimits(alice, {ownerCanTransfer: true}); - await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, alice.address); - await transferExpectSuccess(collection, 0, alice, {Ethereum: aliceProxy} , 200, 'Fungible'); - const address = collectionIdToAddress(collection); - const contract = new web3.eth.Contract(fungibleAbi as any, address, {from: aliceProxy, ...GAS_ARGS}); + await collection.mint(alice, 200n); + await collection.transfer(alice, charlieCA.toEthereum(), 200n); + await collection.transferFrom(alice, charlieCA.toEthereum(), charlieCA, 50n); + await collection.transfer(charlie, bobCA, 50n); + }); + + itEth('The private key X create a EVM address. Alice sends a token to the substrate address corresponding to this EVM address, and X can send it to Bob in the EVM', async ({helper}) => { + const aliceProxy = await helper.eth.createAccountWithBalance(donor); + const bobProxy = await helper.eth.createAccountWithBalance(donor); + const collection = await helper.ft.mintCollection(alice); + await collection.setLimits(alice, {ownerCanTransfer: true}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = helper.ethNativeContract.collection(address, 'ft', aliceProxy); + + await collection.mint(alice, 200n, {Ethereum: aliceProxy}); await contract.methods.transfer(bobProxy, 50).send({from: aliceProxy}); - await transferFromExpectSuccess(collection, 0, alice, {Ethereum: bobProxy}, bob, 50, 'Fungible'); - await transferExpectSuccess(collection, 0, bob, alice, 50, 'Fungible'); + await collection.transferFrom(alice, {Ethereum: bobProxy}, CrossAccountId.fromKeyring(bob), 50n); + await collection.transfer(bob, CrossAccountId.fromKeyring(alice), 50n); }); }); describe('Token transfer between substrate address and EVM address. NFT', () => { - itWeb3('The private key X create a substrate address. Alice sends a token to the corresponding EVM address, and X can send it to Bob in the substrate', async ({privateKeyWrapper}) => { - const collection = await createCollectionExpectSuccess({ - name: 'token name', - mode: {type: 'NFT'}, + let donor: IKeyringPair; + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = privateKey('//Alice'); + [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor); }); - const alice = privateKeyWrapper('//Alice'); - const bob = privateKeyWrapper('//Bob'); - const charlie = privateKeyWrapper('//Charlie'); - await setCollectionLimitsExpectSuccess(alice, collection, {ownerCanTransfer: true}); - const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Substrate: alice.address}); - await transferExpectSuccess(collection, tokenId, alice, {Ethereum: subToEth(charlie.address)}, 1, 'NFT'); - await transferFromExpectSuccess(collection, tokenId, alice, {Ethereum: subToEth(charlie.address)}, charlie, 1, 'NFT'); - await transferExpectSuccess(collection, tokenId, charlie, bob, 1, 'NFT'); }); + + itEth('The private key X create a substrate address. Alice sends a token to the corresponding EVM address, and X can send it to Bob in the substrate', async ({helper}) => { + const charlieEth = CrossAccountId.fromKeyring(charlie, 'Ethereum'); + + const collection = await helper.nft.mintCollection(alice); + await collection.setLimits(alice, {ownerCanTransfer: true}); + const token = await collection.mintToken(alice); + await token.transfer(alice, charlieEth); + await token.transferFrom(alice, charlieEth, CrossAccountId.fromKeyring(charlie)); + await token.transfer(charlie, CrossAccountId.fromKeyring(bob)); + }); - itWeb3('The private key X create a EVM address. Alice sends a token to the substrate address corresponding to this EVM address, and X can send it to Bob in the EVM', async ({api, web3, privateKeyWrapper}) => { - const collection = await createCollectionExpectSuccess({ - name: 'token name', - mode: {type: 'NFT'}, - }); - const alice = privateKeyWrapper('//Alice'); - const bob = privateKeyWrapper('//Bob'); - const charlie = privateKeyWrapper('//Charlie'); - await setCollectionLimitsExpectSuccess(alice, collection, {ownerCanTransfer: true}); - const bobProxy = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const aliceProxy = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Substrate: alice.address}); - await transferExpectSuccess(collection, tokenId, alice, {Ethereum: aliceProxy} , 1, 'NFT'); - const address = collectionIdToAddress(collection); - const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: aliceProxy, ...GAS_ARGS}); + itEth('The private key X create a EVM address. Alice sends a token to the substrate address corresponding to this EVM address, and X can send it to Bob in the EVM', async ({helper}) => { + const aliceProxy = await helper.eth.createAccountWithBalance(donor); + const bobProxy = await helper.eth.createAccountWithBalance(donor); + + const collection = await helper.nft.mintCollection(alice); + await collection.setLimits(alice, {ownerCanTransfer: true}); + + const address = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = helper.ethNativeContract.collection(address, 'nft', aliceProxy); + + const token = await collection.mintToken(alice); + await token.transfer(alice, {Ethereum: aliceProxy}); await contract.methods.transfer(bobProxy, 1).send({from: aliceProxy}); - await transferFromExpectSuccess(collection, tokenId, alice, {Ethereum: bobProxy}, bob, 1, 'NFT'); - await transferExpectSuccess(collection, tokenId, bob, charlie, 1, 'NFT'); + await token.transferFrom(alice, {Ethereum: bobProxy}, {Substrate: bob.address}); + await token.transfer(bob, {Substrate: charlie.address}); }); }); --- a/tests/src/eth/helpersSmoke.test.ts +++ b/tests/src/eth/helpersSmoke.test.ts @@ -14,21 +14,30 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -import {expect} from 'chai'; -import {createEthAccountWithBalance, deployFlipper, itWeb3, contractHelpers} from './util/helpers'; +import {expect, itEth, usingEthPlaygrounds} from './util/playgrounds'; +import {IKeyringPair} from '@polkadot/types/types'; describe('Helpers sanity check', () => { - itWeb3('Contract owner is recorded', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + let donor: IKeyringPair; - const flipper = await deployFlipper(web3, owner); + before(async function() { + await usingEthPlaygrounds(async (_helper, privateKey) => { + donor = privateKey('//Alice'); + }); + }); + + itEth('Contract owner is recorded', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); - expect(await contractHelpers(web3, owner).methods.contractOwner(flipper.options.address).call()).to.be.equal(owner); + const flipper = await helper.eth.deployFlipper(owner); + + expect(await helper.ethNativeContract.contractHelpers(owner).methods.contractOwner(flipper.options.address).call()).to.be.equal(owner); }); - itWeb3('Flipper is working', async ({api, web3, privateKeyWrapper}) => { - const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const flipper = await deployFlipper(web3, owner); + itEth('Flipper is working', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + + const flipper = await helper.eth.deployFlipper(owner); expect(await flipper.methods.getValue().call()).to.be.false; await flipper.methods.flip().send({from: owner}); --- a/tests/src/eth/migration.test.ts +++ b/tests/src/eth/migration.test.ts @@ -14,12 +14,20 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . -import {expect} from 'chai'; -import {submitTransactionAsync} from '../substrate/substrate-api'; -import {createEthAccountWithBalance, GAS_ARGS, itWeb3} from './util/helpers'; +import {expect, itEth, usingEthPlaygrounds} from './util/playgrounds'; +import {IKeyringPair} from '@polkadot/types/types'; describe('EVM Migrations', () => { - itWeb3('Deploy contract saved state', async ({web3, api, privateKeyWrapper}) => { + let superuser: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (_helper, privateKey) => { + superuser = privateKey('//Alice'); + }); + }); + + // todo:playgrounds requires sudo, look into later + itEth('Deploy contract saved state', async ({helper}) => { /* contract StatefulContract { uint counter; @@ -53,13 +61,16 @@ ['0xedc95719e9a3b28dd8e80877cb5880a9be7de1a13fc8b05e7999683b6b567643', '0x0000000000000000000000000000000000000000000000000000000000000004'], ]; - const alice = privateKeyWrapper('//Alice'); - const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const caller = await helper.eth.createAccountWithBalance(superuser); - await submitTransactionAsync(alice, api.tx.sudo.sudo(api.tx.evmMigration.begin(ADDRESS) as any)); - await submitTransactionAsync(alice, api.tx.sudo.sudo(api.tx.evmMigration.setData(ADDRESS, DATA as any) as any)); - await submitTransactionAsync(alice, api.tx.sudo.sudo(api.tx.evmMigration.finish(ADDRESS, CODE) as any)); + const txBegin = helper.constructApiCall('api.tx.evmMigration.begin', [ADDRESS]); + const txSetData = helper.constructApiCall('api.tx.evmMigration.setData', [ADDRESS, DATA]); + const txFinish = helper.constructApiCall('api.tx.evmMigration.finish', [ADDRESS, CODE]); + await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txBegin])).to.be.fulfilled; + await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txSetData])).to.be.fulfilled; + await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txFinish])).to.be.fulfilled; + const web3 = helper.getWeb3(); const contract = new web3.eth.Contract([ { inputs: [], @@ -87,7 +98,7 @@ stateMutability: 'view', type: 'function', }, - ], ADDRESS, {from: caller, ...GAS_ARGS}); + ], ADDRESS, {from: caller, gas: helper.eth.DEFAULT_GAS}); expect(await contract.methods.counterValue().call()).to.be.equal('10'); for (let i = 1; i <= 4; i++) { --- a/tests/src/eth/nonFungible.test.ts +++ b/tests/src/eth/nonFungible.test.ts @@ -171,70 +171,38 @@ }); //TODO: CORE-302 add eth methods - /* todo:playgrounds skipped test! - itWeb3.skip('Can perform mintBulk()', async ({helper}) => { - const collection = await createCollectionExpectSuccess({ - mode: {type: 'NFT'}, - }); - const alice = privateKeyWrapper('//Alice'); + itEth.skip('Can perform mintBulk()', async ({helper}) => { + const caller = await helper.eth.createAccountWithBalance(donor); + const receiver = helper.eth.createAccount(); - const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper); - const changeAdminTx = api.tx.unique.addCollectionAdmin(collection, {Ethereum: caller}); - await submitTransactionAsync(alice, changeAdminTx); - const receiver = createEthAccount(web3); + const collection = await helper.nft.mintCollection(alice); + await collection.addAdmin(alice, {Ethereum: caller}); - const address = collectionIdToAddress(collection); - const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}); - + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', caller); { + const bulkSize = 3; const nextTokenId = await contract.methods.nextTokenId().call(); expect(nextTokenId).to.be.equal('1'); const result = await contract.methods.mintBulkWithTokenURI( receiver, - [ - [nextTokenId, 'Test URI 0'], - [+nextTokenId + 1, 'Test URI 1'], - [+nextTokenId + 2, 'Test URI 2'], - ], + Array.from({length: bulkSize}, (_, i) => ( + [+nextTokenId + i, `Test URI ${i}`] + )), ).send({from: caller}); - const events = normalizeEvents(result.events); - expect(events).to.be.deep.equal([ - { - address, - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: receiver, - tokenId: nextTokenId, - }, - }, - { - address, - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: receiver, - tokenId: String(+nextTokenId + 1), - }, - }, - { - address, - event: 'Transfer', - args: { - from: '0x0000000000000000000000000000000000000000', - to: receiver, - tokenId: String(+nextTokenId + 2), - }, - }, - ]); + const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId); + for (let i = 0; i < bulkSize; i++) { + const event = events[i]; + expect(event.address).to.equal(collectionAddress); + expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000'); + expect(event.returnValues.to).to.equal(receiver); + expect(event.returnValues.tokenId).to.equal(`${+nextTokenId + i}`); - expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI 0'); - expect(await contract.methods.tokenURI(+nextTokenId + 1).call()).to.be.equal('Test URI 1'); - expect(await contract.methods.tokenURI(+nextTokenId + 2).call()).to.be.equal('Test URI 2'); + expect(await contract.methods.tokenURI(+nextTokenId + i).call()).to.be.equal(`Test URI ${i}`); + } } }); - */ itEth('Can perform burn()', async ({helper}) => { 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 @@ -131,7 +131,7 @@ } } - + class EthGroup extends EthGroupBase { DEFAULT_GAS = 2_500_000; @@ -164,14 +164,14 @@ true, ); } - + async callEVM(signer: TEthereumAccount, contractAddress: string, abi: string) { return await this.helper.callRpc('api.rpc.eth.call', [{from: signer, to: contractAddress, data: abi}]); } async createNonfungibleCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string}> { const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer); - + const result = await collectionHelper.methods.createNonfungibleCollection(name, description, tokenPrefix).send(); const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId); @@ -182,7 +182,7 @@ async createRefungibleCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string}> { const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer); - + const result = await collectionHelper.methods.createRFTCollection(name, description, tokenPrefix).send(); const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId); @@ -246,7 +246,7 @@ return before - after; } } - + class EthAddressGroup extends EthGroupBase { extractCollectionId(address: string): number { if (!(address.length === 42 || address.length === 40)) throw new Error('address wrong format'); --- a/tests/src/util/playgrounds/unique.ts +++ b/tests/src/util/playgrounds/unique.ts @@ -20,8 +20,11 @@ if (account.Ethereum) this.Ethereum = account.Ethereum; } - static fromKeyring(account: IKeyringPair) { - return new CrossAccountId({Substrate: account.address}); + static fromKeyring(account: IKeyringPair, domain: 'Substrate' | 'Ethereum' = 'Substrate') { + switch (domain) { + case 'Substrate': return new CrossAccountId({Substrate: account.address}); + case 'Ethereum': return new CrossAccountId({Substrate: account.address}).toEthereum(); + } } static fromLowerCaseKeys(address: ICrossAccountIdLower): CrossAccountId { @@ -40,6 +43,24 @@ if (this.Substrate) return CrossAccountId.withNormalizedSubstrate(this.Substrate, ss58Format); return this; } + + static translateSubToEth(address: TSubstrateAccount): TEthereumAccount { + return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(address), i => i.toString(16).padStart(2, '0')).join('')); + } + + toEthereum(): CrossAccountId { + if (this.Substrate) return new CrossAccountId({Ethereum: CrossAccountId.translateSubToEth(this.Substrate)}); + return this; + } + + static translateEthToSub(address: TEthereumAccount, ss58Format?: number): TSubstrateAccount { + return evmToAddress(address, ss58Format); + } + + toSubstrate(ss58Format?: number): CrossAccountId { + if (this.Ethereum) return new CrossAccountId({Substrate: CrossAccountId.translateEthToSub(this.Ethereum, ss58Format)}); + return this; + } toLowerCase(): CrossAccountId { if (this.Substrate) this.Substrate = this.Substrate.toLowerCase(); @@ -2093,9 +2114,8 @@ * @example normalizeSubstrateToChainFormat("5GrwvaEF5zXb26Fz...") // returns unjKJQJrRd238pkUZZ... for Unique Network * @returns address in chain format */ - async normalizeSubstrateToChainFormat(address: TSubstrateAccount): Promise { - const info = this.helper.chain.getChainProperties(); - return encodeAddress(decodeAddress(address), info.ss58Format); + normalizeSubstrateToChainFormat(address: TSubstrateAccount): TSubstrateAccount { + return this.normalizeSubstrate(address, this.helper.chain.getChainProperties().ss58Format); } /** @@ -2105,10 +2125,8 @@ * @example ethToSubstrate('0x9F0583DbB855d...') * @returns substrate mirror of a provided ethereum address */ - async ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat=false): Promise { - if(!toChainFormat) return evmToAddress(ethAddress); - const info = this.helper.chain.getChainProperties(); - return evmToAddress(ethAddress, info.ss58Format); + ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat=false): TSubstrateAccount { + return CrossAccountId.translateEthToSub(ethAddress, toChainFormat ? this.helper.chain.getChainProperties().ss58Format : undefined); } /** @@ -2118,7 +2136,7 @@ * @returns ethereum mirror of a provided substrate address */ substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount { - return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(subAddress), i => i.toString(16).padStart(2, '0')).join('')); + return CrossAccountId.translateSubToEth(subAddress); } }