1234567891011121314151617import {Pallets, requirePalletsOrSkip} from '../util/playgrounds';18import {EthUniqueHelper, expect, itEth, usingEthPlaygrounds} from './util/playgrounds';19import {IKeyringPair} from '@polkadot/types/types';20import {Contract} from 'web3-eth-contract';212223describe('Refungible token: Information getting', () => {24 let donor: IKeyringPair;25 let alice: IKeyringPair;2627 before(async function() {28 await usingEthPlaygrounds(async (helper, privateKey) => {29 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);3031 donor = privateKey('//Alice');32 [alice] = await helper.arrange.createAccounts([20n], donor);33 });34 });3536 itEth('totalSupply', async ({helper}) => {37 const caller = await helper.eth.createAccountWithBalance(donor);38 const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'MUON'});39 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: caller});4041 const contract = helper.ethNativeContract.rftTokenById(collection.collectionId, tokenId, caller);42 const totalSupply = await contract.methods.totalSupply().call();43 expect(totalSupply).to.equal('200');44 });4546 itEth('balanceOf', async ({helper}) => {47 const caller = await helper.eth.createAccountWithBalance(donor);48 const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'MUON'});49 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: caller});5051 const contract = helper.ethNativeContract.rftTokenById(collection.collectionId, tokenId, caller);52 const balance = await contract.methods.balanceOf(caller).call();53 expect(balance).to.equal('200');54 });5556 itEth('decimals', async ({helper}) => {57 const caller = await helper.eth.createAccountWithBalance(donor);58 const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'MUON'});59 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: caller});6061 const contract = helper.ethNativeContract.rftTokenById(collection.collectionId, tokenId, caller);62 const decimals = await contract.methods.decimals().call();63 expect(decimals).to.equal('0');64 });65});666768describe('Check ERC721 token URI for ReFungible', () => {69 let donor: IKeyringPair;7071 before(async function() {72 await usingEthPlaygrounds(async (helper, privateKey) => {73 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);7475 donor = privateKey('//Alice');76 });77 });7879 async function setup(helper: EthUniqueHelper, baseUri: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {80 const owner = await helper.eth.createAccountWithBalance(donor);81 const receiver = helper.eth.createAccount();8283 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Mint collection', 'a', 'b', baseUri);84 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);85 86 const nextTokenId = await contract.methods.nextTokenId().call();87 expect(nextTokenId).to.be.equal('1');88 const result = await contract.methods.mint(89 receiver,90 nextTokenId,91 ).send();9293 if (propertyKey && propertyValue) {94 95 await contract.methods.setProperty(nextTokenId, propertyKey, Buffer.from(propertyValue)).send();96 }9798 const event = result.events.Transfer;99 expect(event.address).to.be.equal(collectionAddress);100 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');101 expect(event.returnValues.to).to.be.equal(receiver);102 expect(event.returnValues.tokenId).to.be.equal(nextTokenId);103104 return {contract, nextTokenId};105 }106107 itEth('Empty tokenURI', async ({helper}) => {108 const {contract, nextTokenId} = await setup(helper, '');109 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('');110 });111112 itEth('TokenURI from url', async ({helper}) => {113 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URI', 'Token URI');114 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');115 });116117 itEth('TokenURI from baseURI', async ({helper}) => {118 const {contract, nextTokenId} = await setup(helper, 'BaseURI_');119 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_');120 });121122 itEth('TokenURI from baseURI + suffix', async ({helper}) => {123 const suffix = '/some/suffix';124 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URISuffix', suffix);125 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);126 });127});128129describe('Refungible: Plain calls', () => {130 let donor: IKeyringPair;131 let alice: IKeyringPair;132133 before(async function() {134 await usingEthPlaygrounds(async (helper, privateKey) => {135 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);136137 donor = privateKey('//Alice');138 [alice] = await helper.arrange.createAccounts([50n], donor);139 });140 });141142 itEth('Can perform approve()', async ({helper}) => {143 const owner = await helper.eth.createAccountWithBalance(donor);144 const spender = helper.eth.createAccount();145 const collection = await helper.rft.mintCollection(alice);146 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});147148 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);149 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);150151 {152 const result = await contract.methods.approve(spender, 100).send({from: owner});153 const event = result.events.Approval;154 expect(event.address).to.be.equal(tokenAddress);155 expect(event.returnValues.owner).to.be.equal(owner);156 expect(event.returnValues.spender).to.be.equal(spender);157 expect(event.returnValues.value).to.be.equal('100');158 }159160 {161 const allowance = await contract.methods.allowance(owner, spender).call();162 expect(+allowance).to.equal(100);163 }164 });165166 itEth('Can perform transferFrom()', async ({helper}) => {167 const owner = await helper.eth.createAccountWithBalance(donor);168 const spender = await helper.eth.createAccountWithBalance(donor);169 const receiver = helper.eth.createAccount();170 const collection = await helper.rft.mintCollection(alice);171 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});172173 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);174 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);175176 await contract.methods.approve(spender, 100).send();177178 {179 const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: spender});180 let event = result.events.Transfer;181 expect(event.address).to.be.equal(tokenAddress);182 expect(event.returnValues.from).to.be.equal(owner);183 expect(event.returnValues.to).to.be.equal(receiver);184 expect(event.returnValues.value).to.be.equal('49');185186 event = result.events.Approval;187 expect(event.address).to.be.equal(tokenAddress);188 expect(event.returnValues.owner).to.be.equal(owner);189 expect(event.returnValues.spender).to.be.equal(spender);190 expect(event.returnValues.value).to.be.equal('51');191 }192193 {194 const balance = await contract.methods.balanceOf(receiver).call();195 expect(+balance).to.equal(49);196 }197198 {199 const balance = await contract.methods.balanceOf(owner).call();200 expect(+balance).to.equal(151);201 }202 });203204 itEth('Can perform transfer()', async ({helper}) => {205 const owner = await helper.eth.createAccountWithBalance(donor);206 const receiver = helper.eth.createAccount();207 const collection = await helper.rft.mintCollection(alice);208 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});209210 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);211 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);212213 {214 const result = await contract.methods.transfer(receiver, 50).send({from: owner});215 const event = result.events.Transfer;216 expect(event.address).to.be.equal(tokenAddress);217 expect(event.returnValues.from).to.be.equal(owner);218 expect(event.returnValues.to).to.be.equal(receiver);219 expect(event.returnValues.value).to.be.equal('50');220 }221222 {223 const balance = await contract.methods.balanceOf(owner).call();224 expect(+balance).to.equal(150);225 }226227 {228 const balance = await contract.methods.balanceOf(receiver).call();229 expect(+balance).to.equal(50);230 }231 });232233 itEth('Can perform repartition()', async ({helper}) => {234 const owner = await helper.eth.createAccountWithBalance(donor);235 const receiver = await helper.eth.createAccountWithBalance(donor);236 const collection = await helper.rft.mintCollection(alice);237 const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});238239 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);240 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);241242 await contract.methods.repartition(200).send({from: owner});243 expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(200);244 await contract.methods.transfer(receiver, 110).send({from: owner});245 expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(90);246 expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(110);247248 await expect(contract.methods.repartition(80).send({from: owner})).to.eventually.be.rejected; 249250 await contract.methods.transfer(receiver, 90).send({from: owner});251 expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(0);252 expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(200);253254 await contract.methods.repartition(150).send({from: receiver});255 await expect(contract.methods.transfer(owner, 160).send({from: receiver})).to.eventually.be.rejected; 256 expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(150);257 });258259 itEth('Can repartition with increased amount', async ({helper}) => {260 const owner = await helper.eth.createAccountWithBalance(donor);261 const collection = await helper.rft.mintCollection(alice);262 const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});263264 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);265 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);266267 const result = await contract.methods.repartition(200).send();268269 const event = result.events.Transfer;270 expect(event.address).to.be.equal(tokenAddress);271 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');272 expect(event.returnValues.to).to.be.equal(owner);273 expect(event.returnValues.value).to.be.equal('100');274 });275276 itEth('Can repartition with decreased amount', async ({helper}) => {277 const owner = await helper.eth.createAccountWithBalance(donor);278 const collection = await helper.rft.mintCollection(alice);279 const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});280281 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);282 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);283284 const result = await contract.methods.repartition(50).send();285 const event = result.events.Transfer;286 expect(event.address).to.be.equal(tokenAddress);287 expect(event.returnValues.from).to.be.equal(owner);288 expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');289 expect(event.returnValues.value).to.be.equal('50');290 });291292 itEth('Receiving Transfer event on burning into full ownership', async ({helper}) => {293 const caller = await helper.eth.createAccountWithBalance(donor);294 const receiver = await helper.eth.createAccountWithBalance(donor);295 const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Devastation', '6', '6');296 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);297298 const tokenId = await contract.methods.nextTokenId().call();299 await contract.methods.mint(caller, tokenId).send();300 const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);301 const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, caller);302303 await tokenContract.methods.repartition(2).send();304 await tokenContract.methods.transfer(receiver, 1).send();305306 const events: any = [];307 contract.events.allEvents((_: any, event: any) => {308 events.push(event);309 });310 await tokenContract.methods.burnFrom(caller, 1).send();311312 const event = events[0];313 expect(event.address).to.be.equal(collectionAddress);314 expect(event.returnValues.from).to.be.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');315 expect(event.returnValues.to).to.be.equal(receiver);316 expect(event.returnValues.tokenId).to.be.equal(tokenId);317 });318});319320describe('Refungible: Fees', () => {321 let donor: IKeyringPair;322 let alice: IKeyringPair;323324 before(async function() {325 await usingEthPlaygrounds(async (helper, privateKey) => {326 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);327328 donor = privateKey('//Alice');329 [alice] = await helper.arrange.createAccounts([50n], donor);330 });331 });332333 itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {334 const owner = await helper.eth.createAccountWithBalance(donor);335 const spender = helper.eth.createAccount();336 const collection = await helper.rft.mintCollection(alice);337 const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});338339 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);340 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);341342 const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, 100).send({from: owner}));343 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));344 });345346 itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {347 const owner = await helper.eth.createAccountWithBalance(donor);348 const spender = await helper.eth.createAccountWithBalance(donor);349 const collection = await helper.rft.mintCollection(alice);350 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});351352 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);353 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);354355 await contract.methods.approve(spender, 100).send({from: owner});356357 const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender}));358 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));359 });360361 itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {362 const owner = await helper.eth.createAccountWithBalance(donor);363 const receiver = helper.eth.createAccount();364 const collection = await helper.rft.mintCollection(alice);365 const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});366367 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);368 const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);369370 const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, 100).send({from: owner}));371 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));372 });373});374375describe('Refungible: Substrate calls', () => {376 let donor: IKeyringPair;377 let alice: IKeyringPair;378379 before(async function() {380 await usingEthPlaygrounds(async (helper, privateKey) => {381 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);382383 donor = privateKey('//Alice');384 [alice] = await helper.arrange.createAccounts([50n], donor);385 });386 });387388 itEth('Events emitted for approve()', async ({helper}) => {389 const receiver = helper.eth.createAccount();390 const collection = await helper.rft.mintCollection(alice);391 const token = await collection.mintToken(alice, 200n);392393 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);394 const contract = helper.ethNativeContract.rftToken(tokenAddress);395396 const events: any = [];397 contract.events.allEvents((_: any, event: any) => {398 events.push(event);399 });400 expect(await token.approve(alice, {Ethereum: receiver}, 100n)).to.be.true;401402 const event = events[0];403 expect(event.event).to.be.equal('Approval');404 expect(event.address).to.be.equal(tokenAddress);405 expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));406 expect(event.returnValues.spender).to.be.equal(receiver);407 expect(event.returnValues.value).to.be.equal('100');408 });409410 itEth('Events emitted for transferFrom()', async ({helper}) => {411 const [bob] = await helper.arrange.createAccounts([10n], donor);412 const receiver = helper.eth.createAccount();413 const collection = await helper.rft.mintCollection(alice);414 const token = await collection.mintToken(alice, 200n);415 await token.approve(alice, {Substrate: bob.address}, 100n);416417 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);418 const contract = helper.ethNativeContract.rftToken(tokenAddress);419420 const events: any = [];421 contract.events.allEvents((_: any, event: any) => {422 events.push(event);423 });424425 expect(await token.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver}, 51n)).to.be.true;426427 let event = events[0];428 expect(event.event).to.be.equal('Transfer');429 expect(event.address).to.be.equal(tokenAddress);430 expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));431 expect(event.returnValues.to).to.be.equal(receiver);432 expect(event.returnValues.value).to.be.equal('51');433434 event = events[1];435 expect(event.event).to.be.equal('Approval');436 expect(event.address).to.be.equal(tokenAddress);437 expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));438 expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(bob.address));439 expect(event.returnValues.value).to.be.equal('49');440 });441442 itEth('Events emitted for transfer()', async ({helper}) => {443 const receiver = helper.eth.createAccount();444 const collection = await helper.rft.mintCollection(alice);445 const token = await collection.mintToken(alice, 200n);446447 const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);448 const contract = helper.ethNativeContract.rftToken(tokenAddress);449450 const events: any = [];451 contract.events.allEvents((_: any, event: any) => {452 events.push(event);453 });454455 expect(await token.transfer(alice, {Ethereum: receiver}, 51n)).to.be.true;456457 const event = events[0];458 expect(event.event).to.be.equal('Transfer');459 expect(event.address).to.be.equal(tokenAddress);460 expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));461 expect(event.returnValues.to).to.be.equal(receiver);462 expect(event.returnValues.value).to.be.equal('51');463 });464});465466describe('ERC 1633 implementation', () => {467 let donor: IKeyringPair;468469 before(async function() {470 await usingEthPlaygrounds(async (helper, privateKey) => {471 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);472473 donor = privateKey('//Alice');474 });475 });476477 itEth('Default parent token address and id', async ({helper}) => {478 const owner = await helper.eth.createAccountWithBalance(donor);479480 const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sands', '', 'GRAIN');481 const collectionContract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);482 483 const tokenId = await collectionContract.methods.nextTokenId().call();484 await collectionContract.methods.mint(owner, tokenId).send();485 const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);486 const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, owner);487488 expect(await tokenContract.methods.parentToken().call()).to.be.equal(collectionAddress);489 expect(await tokenContract.methods.parentTokenId().call()).to.be.equal(tokenId);490 });491});