difftreelog
eth/proxy/nonFungibleProxy migrated
in: master
1 file changed
tests/src/eth/proxy/nonFungibleProxy.test.tsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {createCollectionExpectSuccess, createItemExpectSuccess} from '../../util/helpers';18import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, evmCollection, evmCollectionHelpers, GAS_ARGS, getCollectionAddressFromResult, itWeb3, normalizeEvents} from '../util/helpers';19import nonFungibleAbi from '../nonFungibleAbi.json';20import {expect} from 'chai';21import {submitTransactionAsync} from '../../substrate/substrate-api';22import Web3 from 'web3';23import {readFile} from 'fs/promises';24import {ApiPromise} from '@polkadot/api';25import {IKeyringPair} from '@polkadot/types/types';2627async function proxyWrap(api: ApiPromise, web3: Web3, wrapped: any, privateKeyWrapper: (account: string) => IKeyringPair) {28 // Proxy owner has no special privilegies, we don't need to reuse them29 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);30 const proxyContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/UniqueNFTProxy.abi`)).toString()), undefined, {31 from: owner,32 ...GAS_ARGS,33 });34 const proxy = await proxyContract.deploy({data: (await readFile(`${__dirname}/UniqueNFTProxy.bin`)).toString(), arguments: [wrapped.options.address]}).send({from: owner});35 return proxy;36}3738describe('NFT (Via EVM proxy): Information getting', () => {39 itWeb3('totalSupply', async ({api, web3, privateKeyWrapper}) => {40 const collection = await createCollectionExpectSuccess({41 mode: {type: 'NFT'},42 });43 const alice = privateKeyWrapper('//Alice');44 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);4546 await createItemExpectSuccess(alice, collection, 'NFT', {Substrate: alice.address});4748 const address = collectionIdToAddress(collection);49 const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}), privateKeyWrapper);50 const totalSupply = await contract.methods.totalSupply().call();5152 expect(totalSupply).to.equal('1');53 });5455 itWeb3('balanceOf', async ({api, web3, privateKeyWrapper}) => {56 const collection = await createCollectionExpectSuccess({57 mode: {type: 'NFT'},58 });59 const alice = privateKeyWrapper('//Alice');6061 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);62 await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: caller});63 await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: caller});64 await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: caller});6566 const address = collectionIdToAddress(collection);67 const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}), privateKeyWrapper);68 const balance = await contract.methods.balanceOf(caller).call();6970 expect(balance).to.equal('3');71 });7273 itWeb3('ownerOf', async ({api, web3, privateKeyWrapper}) => {74 const collection = await createCollectionExpectSuccess({75 mode: {type: 'NFT'},76 });77 const alice = privateKeyWrapper('//Alice');7879 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);80 const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: caller});8182 const address = collectionIdToAddress(collection);83 const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}), privateKeyWrapper);84 const owner = await contract.methods.ownerOf(tokenId).call();8586 expect(owner).to.equal(caller);87 });88});8990describe('NFT (Via EVM proxy): Plain calls', () => {91 itWeb3('Can perform mint()', async ({web3, api, privateKeyWrapper}) => {92 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);93 const collectionHelper = evmCollectionHelpers(web3, owner);94 const result = await collectionHelper.methods95 .createNonfungibleCollection('A', 'A', 'A')96 .send();97 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);98 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);99 const receiver = createEthAccount(web3);100 const collectionEvmOwned = evmCollection(web3, owner, collectionIdAddress);101 const collectionEvm = evmCollection(web3, caller, collectionIdAddress);102 const contract = await proxyWrap(api, web3, collectionEvm, privateKeyWrapper);103 await collectionEvmOwned.methods.addCollectionAdmin(contract.options.address).send();104105 {106 const nextTokenId = await contract.methods.nextTokenId().call();107 expect(nextTokenId).to.be.equal('1');108 const result = await contract.methods.mintWithTokenURI(109 receiver,110 nextTokenId,111 'Test URI',112 ).send({from: caller});113 const events = normalizeEvents(result.events);114 events[0].address = events[0].address.toLocaleLowerCase();115116 expect(events).to.be.deep.equal([117 {118 address: collectionIdAddress.toLocaleLowerCase(),119 event: 'Transfer',120 args: {121 from: '0x0000000000000000000000000000000000000000',122 to: receiver,123 tokenId: nextTokenId,124 },125 },126 ]);127128 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');129 }130 });131 132 //TODO: CORE-302 add eth methods133 itWeb3.skip('Can perform mintBulk()', async ({web3, api, privateKeyWrapper}) => {134 const collection = await createCollectionExpectSuccess({135 mode: {type: 'NFT'},136 });137 const alice = privateKeyWrapper('//Alice');138139 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);140 const receiver = createEthAccount(web3);141142 const address = collectionIdToAddress(collection);143 const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}), privateKeyWrapper);144 const changeAdminTx = api.tx.unique.addCollectionAdmin(collection, {Ethereum: contract.options.address});145 await submitTransactionAsync(alice, changeAdminTx);146147 {148 const nextTokenId = await contract.methods.nextTokenId().call();149 expect(nextTokenId).to.be.equal('1');150 const result = await contract.methods.mintBulkWithTokenURI(151 receiver,152 [153 [nextTokenId, 'Test URI 0'],154 [+nextTokenId + 1, 'Test URI 1'],155 [+nextTokenId + 2, 'Test URI 2'],156 ],157 ).send({from: caller});158 const events = normalizeEvents(result.events);159160 expect(events).to.be.deep.equal([161 {162 address,163 event: 'Transfer',164 args: {165 from: '0x0000000000000000000000000000000000000000',166 to: receiver,167 tokenId: nextTokenId,168 },169 },170 {171 address,172 event: 'Transfer',173 args: {174 from: '0x0000000000000000000000000000000000000000',175 to: receiver,176 tokenId: String(+nextTokenId + 1),177 },178 },179 {180 address,181 event: 'Transfer',182 args: {183 from: '0x0000000000000000000000000000000000000000',184 to: receiver,185 tokenId: String(+nextTokenId + 2),186 },187 },188 ]);189190 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI 0');191 expect(await contract.methods.tokenURI(+nextTokenId + 1).call()).to.be.equal('Test URI 1');192 expect(await contract.methods.tokenURI(+nextTokenId + 2).call()).to.be.equal('Test URI 2');193 }194 });195196 itWeb3('Can perform burn()', async ({web3, api, privateKeyWrapper}) => {197 const collection = await createCollectionExpectSuccess({198 mode: {type: 'NFT'},199 });200 const alice = privateKeyWrapper('//Alice');201 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);202203 const address = collectionIdToAddress(collection);204 const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}), privateKeyWrapper);205 const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: contract.options.address});206207 const changeAdminTx = api.tx.unique.addCollectionAdmin(collection, {Ethereum: contract.options.address});208 await submitTransactionAsync(alice, changeAdminTx);209210 {211 const result = await contract.methods.burn(tokenId).send({from: caller});212 const events = normalizeEvents(result.events);213214 expect(events).to.be.deep.equal([215 {216 address,217 event: 'Transfer',218 args: {219 from: contract.options.address,220 to: '0x0000000000000000000000000000000000000000',221 tokenId: tokenId.toString(),222 },223 },224 ]);225 }226 });227228 itWeb3('Can perform approve()', async ({web3, api, privateKeyWrapper}) => {229 const collection = await createCollectionExpectSuccess({230 mode: {type: 'NFT'},231 });232 const alice = privateKeyWrapper('//Alice');233 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);234 const spender = createEthAccount(web3);235236 const address = collectionIdToAddress(collection);237 const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address), privateKeyWrapper);238 const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: contract.options.address});239240 {241 const result = await contract.methods.approve(spender, tokenId).send({from: caller, ...GAS_ARGS});242 const events = normalizeEvents(result.events);243244 expect(events).to.be.deep.equal([245 {246 address,247 event: 'Approval',248 args: {249 owner: contract.options.address,250 approved: spender,251 tokenId: tokenId.toString(),252 },253 },254 ]);255 }256 });257258 itWeb3('Can perform transferFrom()', async ({web3, api, privateKeyWrapper}) => {259 const collection = await createCollectionExpectSuccess({260 mode: {type: 'NFT'},261 });262 const alice = privateKeyWrapper('//Alice');263 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);264 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);265266 const receiver = createEthAccount(web3);267268 const address = collectionIdToAddress(collection);269 const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS});270 const contract = await proxyWrap(api, web3, evmCollection, privateKeyWrapper);271 const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: owner});272273 await evmCollection.methods.approve(contract.options.address, tokenId).send({from: owner});274275 {276 const result = await contract.methods.transferFrom(owner, receiver, tokenId).send({from: caller});277 const events = normalizeEvents(result.events);278 expect(events).to.be.deep.equal([279 {280 address,281 event: 'Transfer',282 args: {283 from: owner,284 to: receiver,285 tokenId: tokenId.toString(),286 },287 },288 ]);289 }290291 {292 const balance = await contract.methods.balanceOf(receiver).call();293 expect(+balance).to.equal(1);294 }295296 {297 const balance = await contract.methods.balanceOf(contract.options.address).call();298 expect(+balance).to.equal(0);299 }300 });301302 itWeb3('Can perform transfer()', async ({web3, api, privateKeyWrapper}) => {303 const collection = await createCollectionExpectSuccess({304 mode: {type: 'NFT'},305 });306 const alice = privateKeyWrapper('//Alice');307 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);308 const receiver = createEthAccount(web3);309310 const address = collectionIdToAddress(collection);311 const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}), privateKeyWrapper);312 const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: contract.options.address});313314 {315 const result = await contract.methods.transfer(receiver, tokenId).send({from: caller});316 const events = normalizeEvents(result.events);317 expect(events).to.be.deep.equal([318 {319 address,320 event: 'Transfer',321 args: {322 from: contract.options.address,323 to: receiver,324 tokenId: tokenId.toString(),325 },326 },327 ]);328 }329330 {331 const balance = await contract.methods.balanceOf(contract.options.address).call();332 expect(+balance).to.equal(0);333 }334335 {336 const balance = await contract.methods.balanceOf(receiver).call();337 expect(+balance).to.equal(1);338 }339 });340});1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {GAS_ARGS, itWeb3, normalizeEvents} from '../util/helpers';18import {expect} from 'chai';19import {readFile} from 'fs/promises';20import {IKeyringPair} from '@polkadot/types/types';21import {EthUniqueHelper, itEth, usingEthPlaygrounds} from '../util/playgrounds';2223async function proxyWrap(helper: EthUniqueHelper, wrapped: any, donor: IKeyringPair) {24 // Proxy owner has no special privilegies, we don't need to reuse them25 const owner = await helper.eth.createAccountWithBalance(donor);26 const proxyContract = new helper.web3!.eth.Contract(JSON.parse((await readFile(`${__dirname}/UniqueNFTProxy.abi`)).toString()), undefined, {27 from: owner,28 ...GAS_ARGS,29 });30 const proxy = await proxyContract.deploy({data: (await readFile(`${__dirname}/UniqueNFTProxy.bin`)).toString(), arguments: [wrapped.options.address]}).send({from: owner});31 return proxy;32}3334describe('NFT (Via EVM proxy): Information getting', () => {35 let alice: IKeyringPair;36 let donor: IKeyringPair;3738 before(async function() {39 await usingEthPlaygrounds(async (helper, privateKey) => {40 donor = privateKey('//Alice');41 [alice] = await helper.arrange.createAccounts([10n], donor);42 });43 });4445 itEth('totalSupply', async ({helper}) => {46 const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});47 const caller = await helper.eth.createAccountWithBalance(donor);48 await collection.mintToken(alice, {Substrate: alice.address});4950 const address = helper.ethAddress.fromCollectionId(collection.collectionId);51 const evmCollection = helper.ethNativeContract.collection(address, 'nft', caller);52 const contract = await proxyWrap(helper, evmCollection, donor);53 const totalSupply = await contract.methods.totalSupply().call();5455 expect(totalSupply).to.equal('1');56 });5758 itEth('balanceOf', async ({helper}) => {59 const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});6061 const caller = await helper.eth.createAccountWithBalance(donor);62 await collection.mintMultipleTokens(alice, [63 {owner: {Ethereum: caller}},64 {owner: {Ethereum: caller}},65 {owner: {Ethereum: caller}},66 ]);6768 const address = helper.ethAddress.fromCollectionId(collection.collectionId);69 const evmCollection = helper.ethNativeContract.collection(address, 'nft', caller);70 const contract = await proxyWrap(helper, evmCollection, donor);71 const balance = await contract.methods.balanceOf(caller).call();7273 expect(balance).to.equal('3');74 });7576 itEth('ownerOf', async ({helper}) => {77 const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});7879 const caller = await helper.eth.createAccountWithBalance(donor);80 const {tokenId} = await collection.mintToken(alice, {Ethereum: caller});8182 const address = helper.ethAddress.fromCollectionId(collection.collectionId);83 const evmCollection = helper.ethNativeContract.collection(address, 'nft', caller);84 const contract = await proxyWrap(helper, evmCollection, donor);85 const owner = await contract.methods.ownerOf(tokenId).call();8687 expect(owner).to.equal(caller);88 });89});9091describe('NFT (Via EVM proxy): Plain calls', () => {92 let alice: IKeyringPair;93 let donor: IKeyringPair;9495 before(async function() {96 await usingEthPlaygrounds(async (helper, privateKey) => {97 donor = privateKey('//Alice');98 [alice] = await helper.arrange.createAccounts([10n], donor);99 });100 });101102 itEth('Can perform mint()', async ({helper}) => {103 const owner = await helper.eth.createAccountWithBalance(donor);104 const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'A', 'A');105 const caller = await helper.eth.createAccountWithBalance(donor);106 const receiver = helper.eth.createAccount();107108 const collectionEvmOwned = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);109 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', caller);110 const contract = await proxyWrap(helper, collectionEvm, donor);111 await collectionEvmOwned.methods.addCollectionAdmin(contract.options.address).send();112113 {114 const nextTokenId = await contract.methods.nextTokenId().call();115 expect(nextTokenId).to.be.equal('1');116 const result = await contract.methods.mintWithTokenURI(117 receiver,118 nextTokenId,119 'Test URI',120 ).send({from: caller});121 const events = normalizeEvents(result.events);122 events[0].address = events[0].address.toLocaleLowerCase();123124 expect(events).to.be.deep.equal([125 {126 address: collectionAddress.toLocaleLowerCase(),127 event: 'Transfer',128 args: {129 from: '0x0000000000000000000000000000000000000000',130 to: receiver,131 tokenId: nextTokenId,132 },133 },134 ]);135136 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');137 }138 });139140 //TODO: CORE-302 add eth methods141 itWeb3.skip('Can perform mintBulk()', async ({web3, api, privateKeyWrapper}) => {142 /*143 const collection = await createCollectionExpectSuccess({144 mode: {type: 'NFT'},145 });146 const alice = privateKeyWrapper('//Alice');147148 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);149 const receiver = createEthAccount(web3);150151 const address = collectionIdToAddress(collection);152 const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}), privateKeyWrapper);153 const changeAdminTx = api.tx.unique.addCollectionAdmin(collection, {Ethereum: contract.options.address});154 await submitTransactionAsync(alice, changeAdminTx);155156 {157 const nextTokenId = await contract.methods.nextTokenId().call();158 expect(nextTokenId).to.be.equal('1');159 const result = await contract.methods.mintBulkWithTokenURI(160 receiver,161 [162 [nextTokenId, 'Test URI 0'],163 [+nextTokenId + 1, 'Test URI 1'],164 [+nextTokenId + 2, 'Test URI 2'],165 ],166 ).send({from: caller});167 const events = normalizeEvents(result.events);168169 expect(events).to.be.deep.equal([170 {171 address,172 event: 'Transfer',173 args: {174 from: '0x0000000000000000000000000000000000000000',175 to: receiver,176 tokenId: nextTokenId,177 },178 },179 {180 address,181 event: 'Transfer',182 args: {183 from: '0x0000000000000000000000000000000000000000',184 to: receiver,185 tokenId: String(+nextTokenId + 1),186 },187 },188 {189 address,190 event: 'Transfer',191 args: {192 from: '0x0000000000000000000000000000000000000000',193 to: receiver,194 tokenId: String(+nextTokenId + 2),195 },196 },197 ]);198199 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI 0');200 expect(await contract.methods.tokenURI(+nextTokenId + 1).call()).to.be.equal('Test URI 1');201 expect(await contract.methods.tokenURI(+nextTokenId + 2).call()).to.be.equal('Test URI 2');202 }203 */204 });205206 itEth('Can perform burn()', async ({helper}) => {207 const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});208 const caller = await helper.eth.createAccountWithBalance(donor);209210 const address = helper.ethAddress.fromCollectionId(collection.collectionId);211 const evmCollection = helper.ethNativeContract.collection(address, 'nft', caller);212 const contract = await proxyWrap(helper, evmCollection, donor);213 const {tokenId} = await collection.mintToken(alice, {Ethereum: contract.options.address});214 await collection.addAdmin(alice, {Ethereum: contract.options.address});215216 {217 const result = await contract.methods.burn(tokenId).send({from: caller});218 const events = normalizeEvents(result.events);219220 expect(events).to.be.deep.equal([221 {222 address,223 event: 'Transfer',224 args: {225 from: contract.options.address,226 to: '0x0000000000000000000000000000000000000000',227 tokenId: tokenId.toString(),228 },229 },230 ]);231 }232 });233234 itEth('Can perform approve()', async ({helper}) => {235 const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});236 const caller = await helper.eth.createAccountWithBalance(donor);237 const spender = helper.eth.createAccount();238239 const address = helper.ethAddress.fromCollectionId(collection.collectionId);240 const evmCollection = helper.ethNativeContract.collection(address, 'nft', caller);241 const contract = await proxyWrap(helper, evmCollection, donor);242 const {tokenId} = await collection.mintToken(alice, {Ethereum: contract.options.address});243244 {245 const result = await contract.methods.approve(spender, tokenId).send({from: caller, ...GAS_ARGS});246 const events = normalizeEvents(result.events);247248 expect(events).to.be.deep.equal([249 {250 address,251 event: 'Approval',252 args: {253 owner: contract.options.address,254 approved: spender,255 tokenId: tokenId.toString(),256 },257 },258 ]);259 }260 });261262 itEth('Can perform transferFrom()', async ({helper}) => {263 const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});264 const caller = await helper.eth.createAccountWithBalance(donor);265 const owner = await helper.eth.createAccountWithBalance(donor);266267 const receiver = helper.eth.createAccount();268269 const address = helper.ethAddress.fromCollectionId(collection.collectionId);270 const evmCollection = helper.ethNativeContract.collection(address, 'nft', caller);271 const contract = await proxyWrap(helper, evmCollection, donor);272 const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});273274 await evmCollection.methods.approve(contract.options.address, tokenId).send({from: owner});275276 {277 const result = await contract.methods.transferFrom(owner, receiver, tokenId).send({from: caller});278 const events = normalizeEvents(result.events);279 expect(events).to.be.deep.equal([280 {281 address,282 event: 'Transfer',283 args: {284 from: owner,285 to: receiver,286 tokenId: tokenId.toString(),287 },288 },289 ]);290 }291292 {293 const balance = await contract.methods.balanceOf(receiver).call();294 expect(+balance).to.equal(1);295 }296297 {298 const balance = await contract.methods.balanceOf(contract.options.address).call();299 expect(+balance).to.equal(0);300 }301 });302303 itEth('Can perform transfer()', async ({helper}) => {304 const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});305 const caller = await helper.eth.createAccountWithBalance(donor);306 const receiver = helper.eth.createAccount();307308 const address = helper.ethAddress.fromCollectionId(collection.collectionId);309 const evmCollection = helper.ethNativeContract.collection(address, 'nft', caller);310 const contract = await proxyWrap(helper, evmCollection, donor);311 const {tokenId} = await collection.mintToken(alice, {Ethereum: contract.options.address});312313 {314 const result = await contract.methods.transfer(receiver, tokenId).send({from: caller});315 const events = normalizeEvents(result.events);316 expect(events).to.be.deep.equal([317 {318 address,319 event: 'Transfer',320 args: {321 from: contract.options.address,322 to: receiver,323 tokenId: tokenId.toString(),324 },325 },326 ]);327 }328329 {330 const balance = await contract.methods.balanceOf(contract.options.address).call();331 expect(+balance).to.equal(0);332 }333334 {335 const balance = await contract.methods.balanceOf(receiver).call();336 expect(+balance).to.equal(1);337 }338 });339});