--- a/tests/src/eth/nonFungible.test.ts +++ b/tests/src/eth/nonFungible.test.ts @@ -4,11 +4,13 @@ // import privateKey from '../substrate/privateKey'; -import { approveExpectSuccess, createCollectionExpectSuccess, createItemExpectSuccess, transferExpectSuccess, transferFromExpectSuccess } from '../util/helpers'; +import { approveExpectSuccess, burnItemExpectSuccess, createCollectionExpectSuccess, createItemExpectSuccess, transferExpectSuccess, transferFromExpectSuccess } from '../util/helpers'; import { collectionIdToAddress, createEthAccount, createEthAccountWithBalance, GAS_ARGS, itWeb3, normalizeEvents, recordEvents, subToEth, transferBalanceToEth } from './util/helpers'; +import { evmToAddress } from '@polkadot/util-crypto'; import nonFungibleAbi from './nonFungibleAbi.json'; import { expect } from 'chai'; import waitNewBlocks from '../substrate/wait-new-blocks'; +import { submitTransactionAsync } from '../substrate/substrate-api'; describe('NFT: Information getting', () => { itWeb3('totalSupply', async ({ api, web3 }) => { @@ -64,6 +66,78 @@ }); describe('NFT: Plain calls', () => { + itWeb3('Can perform mint()', async ({ web3, api }) => { + const collection = await createCollectionExpectSuccess({ + mode: { type: 'NFT' }, + }); + const alice = privateKey('//Alice'); + + const caller = await createEthAccountWithBalance(api, web3); + const changeAdminTx = api.tx.nft.addCollectionAdmin(collection, { ethereum: caller }); + await submitTransactionAsync(alice, changeAdminTx); + const receiver = createEthAccount(web3); + + const address = collectionIdToAddress(collection); + const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}); + + { + const nextTokenId = await contract.methods.nextTokenId().call(); + expect(nextTokenId).to.be.equal('1'); + const result = await contract.methods.mintWithTokenURI( + receiver, + nextTokenId, + 'Test URI', + ).send({from: caller}); + const events = normalizeEvents(result.events); + + expect(events).to.be.deep.equal([ + { + address, + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: receiver, + tokenId: nextTokenId, + }, + }, + ]); + + await waitNewBlocks(api, 1); + expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI'); + } + }); + + itWeb3('Can perform burn()', async ({ web3, api }) => { + const collection = await createCollectionExpectSuccess({ + mode: {type: 'NFT'}, + }); + const alice = privateKey('//Alice'); + + const owner = await createEthAccountWithBalance(api, web3); + + const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', { ethereum: owner }); + + const address = collectionIdToAddress(collection); + const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: owner, ...GAS_ARGS}); + + { + const result = await contract.methods.burn(tokenId).send({ from: owner }); + const events = normalizeEvents(result.events); + + expect(events).to.be.deep.equal([ + { + address, + event: 'Transfer', + args: { + from: owner, + to: '0x0000000000000000000000000000000000000000', + tokenId: tokenId.toString(), + }, + }, + ]); + } + }); + itWeb3('Can perform approve()', async ({ web3, api }) => { const collection = await createCollectionExpectSuccess({ mode: { type: 'NFT' }, @@ -193,6 +267,60 @@ }); describe('NFT: Substrate calls', () => { + itWeb3('Events emitted for mint()', async ({ web3 }) => { + const collection = await createCollectionExpectSuccess({ + mode: { type: 'NFT' }, + }); + const alice = privateKey('//Alice'); + + const address = collectionIdToAddress(collection); + const contract = new web3.eth.Contract(nonFungibleAbi as any, address); + + let tokenId: number; + const events = await recordEvents(contract, async () => { + tokenId = await createItemExpectSuccess(alice, collection, 'NFT'); + }); + + expect(events).to.be.deep.equal([ + { + address, + event: 'Transfer', + args: { + from: '0x0000000000000000000000000000000000000000', + to: subToEth(alice.address), + tokenId: tokenId!.toString(), + }, + }, + ]); + }); + + itWeb3('Events emitted for burn()', async ({ web3 }) => { + const collection = await createCollectionExpectSuccess({ + mode: { type: 'NFT' }, + }); + const alice = privateKey('//Alice'); + + const address = collectionIdToAddress(collection); + const contract = new web3.eth.Contract(nonFungibleAbi as any, address); + + const tokenId = await createItemExpectSuccess(alice, collection, 'NFT'); + const events = await recordEvents(contract, async () => { + await burnItemExpectSuccess(alice, collection, tokenId); + }); + + expect(events).to.be.deep.equal([ + { + address, + event: 'Transfer', + args: { + from: subToEth(alice.address), + to: '0x0000000000000000000000000000000000000000', + tokenId: tokenId.toString(), + }, + }, + ]); + }); + itWeb3('Events emitted for approve()', async ({ web3 }) => { const collection = await createCollectionExpectSuccess({ mode: { type: 'NFT' }, @@ -284,4 +412,4 @@ }, ]); }); -}); \ No newline at end of file +}); --- a/tests/src/eth/nonFungibleAbi.json +++ b/tests/src/eth/nonFungibleAbi.json @@ -50,6 +50,12 @@ "type": "event" }, { + "anonymous": true, + "inputs": [], + "name": "MintingFinished", + "type": "event" + }, + { "anonymous": false, "inputs": [ { @@ -89,7 +95,7 @@ ], "name": "approve", "outputs": [], - "stateMutability": "payable", + "stateMutability": "nonpayable", "type": "function" }, { @@ -119,6 +125,32 @@ "type": "uint256" } ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "finishMinting", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], "name": "getApproved", "outputs": [ { @@ -146,11 +178,77 @@ "name": "isApprovedForAll", "outputs": [ { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [ + { "internalType": "bool", "name": "", "type": "bool" } ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "string", + "name": "tokenURI", + "type": "string" + } + ], + "name": "mintWithTokenURI", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "mintingFinished", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], "stateMutability": "view", "type": "function" }, @@ -160,7 +258,7 @@ "outputs": [ { "internalType": "string", - "name": "res_name", + "name": "", "type": "string" } ], @@ -168,6 +266,19 @@ "type": "function" }, { + "inputs": [], + "name": "nextTokenId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [ { "internalType": "uint256", @@ -206,7 +317,7 @@ ], "name": "safeTransferFrom", "outputs": [], - "stateMutability": "payable", + "stateMutability": "nonpayable", "type": "function" }, { @@ -232,9 +343,9 @@ "type": "bytes" } ], - "name": "safeTransferFrom", + "name": "safeTransferFromWithData", "outputs": [], - "stateMutability": "payable", + "stateMutability": "nonpayable", "type": "function" }, { @@ -258,9 +369,9 @@ { "inputs": [ { - "internalType": "bytes4", - "name": "interfaceID", - "type": "bytes4" + "internalType": "uint32", + "name": "interfaceId", + "type": "uint32" } ], "name": "supportsInterface", @@ -271,7 +382,7 @@ "type": "bool" } ], - "stateMutability": "pure", + "stateMutability": "view", "type": "function" }, { @@ -280,7 +391,7 @@ "outputs": [ { "internalType": "string", - "name": "res_symbol", + "name": "", "type": "string" } ], @@ -366,11 +477,6 @@ "inputs": [ { "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "address", "name": "to", "type": "address" }, @@ -380,15 +486,20 @@ "type": "uint256" } ], - "name": "transferFrom", + "name": "transfer", "outputs": [], - "stateMutability": "payable", + "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", "name": "to", "type": "address" }, @@ -398,9 +509,9 @@ "type": "uint256" } ], - "name": "transfer", + "name": "transferFrom", "outputs": [], - "stateMutability": "payable", + "stateMutability": "nonpayable", "type": "function" } ] \ No newline at end of file