1234567891011121314151617import {IKeyringPair} from '@polkadot/types/types';18import {Pallets, requirePalletsOrSkip, usingPlaygrounds} from '../util/index';19import { CrossAccountId } from '../util/playgrounds/unique';20import {itEth, expect} from './util';2122describe('evm nft collection sponsoring', () => {23 let donor: IKeyringPair;24 let alice: IKeyringPair;25 let nominal: bigint;2627 before(async () => {28 await usingPlaygrounds(async (helper, privateKey) => {29 donor = await privateKey({filename: __filename});30 [alice] = await helper.arrange.createAccounts([100n], donor);31 nominal = helper.balance.getOneTokenNominal();32 });33 });3435 36 itEth('sponsors mint transactions', async ({helper}) => {37 const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'spnr', permissions: {mintMode: true}});38 await collection.setSponsor(alice, alice.address);39 await collection.confirmSponsorship(alice);4041 const minter = helper.eth.createAccount();42 expect(await helper.balance.getEthereum(minter)).to.equal(0n);4344 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);45 const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', minter);4647 await collection.addToAllowList(alice, {Ethereum: minter});4849 const result = await contract.methods.mint(minter).send();5051 const events = helper.eth.normalizeEvents(result.events);52 expect(events).to.be.deep.equal([53 {54 address: collectionAddress,55 event: 'Transfer',56 args: {57 from: '0x0000000000000000000000000000000000000000',58 to: minter,59 tokenId: '1',60 },61 },62 ]);63 });6465 66 67 68 69 70 71 72 7374 75 76 7778 79 80 8182 83 84 8586 [87 'setCollectionSponsorCross',88 'setCollectionSponsor', 89 ].map(testCase =>90 itEth(`[${testCase}] can remove collection sponsor`, async ({helper}) => {91 const owner = await helper.eth.createAccountWithBalance(donor);92 const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);9394 let result = await collectionHelpers.methods.createNFTCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)});95 const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);96 const sponsor = await helper.eth.createAccountWithBalance(donor);97 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);98 const collectionEvm = await helper.ethNativeContract.collection(collectionIdAddress, 'nft', owner, testCase === 'setCollectionSponsor');99100 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;101 result = await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send({from: owner});102 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;103104 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});105 let sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner});106 expect(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorTuple.sub))).to.be.eq(helper.address.ethToSubstrate(sponsor, true));107 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;108109 await collectionEvm.methods.removeCollectionSponsor().send({from: owner});110111 sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner});112 expect(sponsorTuple.eth).to.be.eq('0x0000000000000000000000000000000000000000');113 }));114115 [116 'setCollectionSponsorCross',117 'setCollectionSponsor', 118 ].map(testCase =>119 itEth(`[${testCase}] Can sponsor from evm address via access list`, async ({helper}) => {120 const owner = await helper.eth.createAccountWithBalance(donor);121 const sponsorEth = await helper.eth.createAccountWithBalance(donor);122 const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);123124 const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Sponsor collection', '1', '1', '');125126 const collectionSub = helper.nft.getCollectionObject(collectionId);127 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, testCase === 'setCollectionSponsor');128129 130 await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsorEth : sponsorCrossEth).send({from: owner});131 let sponsorship = (await collectionSub.getData())!.raw.sponsorship;132 expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));133 134 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');135136 137 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});138 sponsorship = (await collectionSub.getData())!.raw.sponsorship;139 expect(sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));140141 142 const user = helper.eth.createAccount();143 const userCross = helper.ethCrossAccount.fromAddress(user);144 const nextTokenId = await collectionEvm.methods.nextTokenId().call();145 expect(nextTokenId).to.be.equal('1');146147 148 const oldPermissions = (await collectionSub.getData())!.raw.permissions;149 expect(oldPermissions.mintMode).to.be.false;150 expect(oldPermissions.access).to.be.equal('Normal');151152 await collectionEvm.methods.setCollectionAccess(1 ).send({from: owner});153 await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner});154 await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});155 156 const newPermissions = (await collectionSub.getData())!.raw.permissions;157 expect(newPermissions.mintMode).to.be.true;158 expect(newPermissions.access).to.be.equal('AllowList');159160 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));161 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));162 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));163164 165 {166 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});167 const event = helper.eth.normalizeEvents(result.events)168 .find(event => event.event === 'Transfer');169170 expect(event).to.be.deep.equal({171 address: collectionAddress,172 event: 'Transfer',173 args: {174 from: '0x0000000000000000000000000000000000000000',175 to: user,176 tokenId: '1',177 },178 });179180 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));181 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));182 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));183184 expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');185 expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);186 expect(userBalanceAfter).to.be.eq(userBalanceBefore);187 expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;188 }189 }));190191 192 193 194 195 196 197 198 199200 201202 203 204205 206 207 208209 210 211 212213 214 215216 217 218 219 220 221 222 223 224 225226 227 228 229 230 231 232 233 234 235 236 237238 239 240241 242 243 244 245 246247 [248 'setCollectionSponsorCross',249 'setCollectionSponsor', 250 ].map(testCase =>251 itEth(`[${testCase}] Check that transaction via EVM spend money from sponsor address`, async ({helper}) => {252 const owner = await helper.eth.createAccountWithBalance(donor);253 const sponsor = await helper.eth.createAccountWithBalance(donor);254 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);255256 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner,'Sponsor collection', '1', '1', '');257258 const collectionSub = helper.nft.getCollectionObject(collectionId);259 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, testCase === 'setCollectionSponsor');260 261 await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send();262 let collectionData = (await collectionSub.getData())!;263 expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));264 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');265266 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});267 collectionData = (await collectionSub.getData())!;268 expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));269270 const user = helper.eth.createAccount();271 const userCross = helper.ethCrossAccount.fromAddress(user);272 await collectionEvm.methods.addCollectionAdminCross(userCross).send();273274 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));275 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));276277 const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});278 const tokenId = mintingResult.events.Transfer.returnValues.tokenId;279280 const event = helper.eth.normalizeEvents(mintingResult.events)281 .find(event => event.event === 'Transfer');282 const address = helper.ethAddress.fromCollectionId(collectionId);283284 expect(event).to.be.deep.equal({285 address,286 event: 'Transfer',287 args: {288 from: '0x0000000000000000000000000000000000000000',289 to: user,290 tokenId: '1',291 },292 });293 expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI');294295 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));296 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);297 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));298 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;299 }));300301 itEth('Can reassign collection sponsor', async ({helper}) => {302 const owner = await helper.eth.createAccountWithBalance(donor);303 const sponsorEth = await helper.eth.createAccountWithBalance(donor);304 const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);305 const [sponsorSub] = await helper.arrange.createAccounts([100n], donor);306 const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub);307308 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner,'Sponsor collection', '1', '1', '');309 const collectionSub = helper.nft.getCollectionObject(collectionId);310 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);311312 313 await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossEth).send({from: owner});314 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});315316 317 await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner});318 const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship;319 expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address});320 });321});322323describe('evm RFT collection sponsoring', () => {324 let donor: IKeyringPair;325 let alice: IKeyringPair;326 let nominal: bigint;327328 before(async function() {329 await usingPlaygrounds(async (helper, privateKey) => {330 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);331 donor = await privateKey({filename: __filename});332 [alice] = await helper.arrange.createAccounts([100n], donor);333 nominal = helper.balance.getOneTokenNominal();334 });335 });336 337 [338 'mintCross',339 'mintWithTokenURI',340 'mintBulk',341 'mintBulkWithTokenUri',342 ].map(testCase => 343 itEth(`[${testCase}] sponsors mint transactions`, async ({helper}) => {344 const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'spnr', permissions: {mintMode: true}, tokenPropertyPermissions: [345 {key: 'URI', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},346 ]});347 348 const owner = await helper.eth.createAccountWithBalance(donor);349 await collection.setSponsor(alice, alice.address);350 await collection.confirmSponsorship(alice);351352 const minter = helper.eth.createAccount();353 const minterCross = helper.ethCrossAccount.fromAddress(minter);354 expect(await helper.balance.getEthereum(minter)).to.equal(0n);355356 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);357 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', minter, true);358359 await collection.addToAllowList(alice, {Ethereum: minter});360 helper.collection.addAdmin(alice, collection.collectionId, {Ethereum: owner});361 const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);362 await collectionHelpers.methods.makeCollectionERC721MetadataCompatible(collectionAddress, 'base/')363 .send();364365 let mintingResult;366 let tokenId;367 const nextTokenId = await contract.methods.nextTokenId().call();368 switch (testCase) {369 case 'mintCross':370 mintingResult = await contract.methods.mintCross(minterCross, []).send();371 break;372 case 'mintWithTokenURI':373 mintingResult = await contract.methods.mintWithTokenURI(minter, 'Test URI').send();374 tokenId = mintingResult.events.Transfer.returnValues.tokenId;375 expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');376 break;377 case 'mintBulk':378 mintingResult = await contract.methods.mintBulk(minter, [nextTokenId]).send();379 break;380 case 'mintBulkWithTokenUri':381 mintingResult = await contract.methods.mintBulkWithTokenURI(minter, [[nextTokenId, 'Test URI']]).send();382 tokenId = mintingResult.events.Transfer.returnValues.tokenId;383 expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');384 break;385 }386387 const events = helper.eth.normalizeEvents(mintingResult.events);388 expect(events).to.deep.include({389 address: collectionAddress,390 event: 'Transfer',391 args: {392 from: '0x0000000000000000000000000000000000000000',393 to: minter,394 tokenId: '1',395 },396 });397 }));398399 [400 'setCollectionSponsorCross',401 'setCollectionSponsor', 402 ].map(testCase => 403 itEth(`[${testCase}] can remove collection sponsor`, async ({helper}) => {404 const owner = await helper.eth.createAccountWithBalance(donor);405 const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);406 407 let result = await collectionHelpers.methods.createRFTCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)});408 const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);409 const sponsor = await helper.eth.createAccountWithBalance(donor);410 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);411 const collectionEvm = helper.ethNativeContract.collection(collectionIdAddress, 'rft', owner, testCase === 'setCollectionSponsor');412 413 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;414 result = await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send({from: owner});415 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;416 417 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});418 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;419 420 await collectionEvm.methods.removeCollectionSponsor().send({from: owner});421 422 const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner});423 expect(sponsorTuple.eth).to.be.eq('0x0000000000000000000000000000000000000000');424 }));425426 [427 'setCollectionSponsorCross',428 'setCollectionSponsor', 429 ].map(testCase => 430 itEth(`[${testCase}] Can sponsor from evm address via access list`, async ({helper}) => {431 const owner = await helper.eth.createAccountWithBalance(donor);432 const sponsorEth = await helper.eth.createAccountWithBalance(donor);433 const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);434 435 const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Sponsor collection', '1', '1', '');436 437 const collectionSub = helper.rft.getCollectionObject(collectionId);438 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner, testCase === 'setCollectionSponsor');439 440 441 await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsorEth : sponsorCrossEth).send({from: owner});442 let sponsorship = (await collectionSub.getData())!.raw.sponsorship;443 expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));444 445 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');446 447 448 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});449 sponsorship = (await collectionSub.getData())!.raw.sponsorship;450 expect(sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));451 452 453 const user = helper.eth.createAccount();454 const userCross = helper.ethCrossAccount.fromAddress(user);455 const nextTokenId = await collectionEvm.methods.nextTokenId().call();456 expect(nextTokenId).to.be.equal('1');457 458 459 const oldPermissions = (await collectionSub.getData())!.raw.permissions;460 expect(oldPermissions.mintMode).to.be.false;461 expect(oldPermissions.access).to.be.equal('Normal');462463 await collectionEvm.methods.setCollectionAccess(1 ).send({from: owner});464 await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner});465 await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});466 467 const newPermissions = (await collectionSub.getData())!.raw.permissions;468 expect(newPermissions.mintMode).to.be.true;469 expect(newPermissions.access).to.be.equal('AllowList');470 471 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));472 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));473 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));474475 476 {477 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});478 const events = helper.eth.normalizeEvents(result.events);479480 expect(events).to.deep.include({481 address: collectionAddress,482 event: 'Transfer',483 args: {484 from: '0x0000000000000000000000000000000000000000',485 to: user,486 tokenId: '1',487 },488 });489 490 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));491 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));492 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));493 494 expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');495 expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);496 expect(userBalanceAfter).to.be.eq(userBalanceBefore);497 expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;498 }499 }));500501 [502 'setCollectionSponsorCross',503 'setCollectionSponsor', 504 ].map(testCase => 505 itEth(`[${testCase}] Check that collection admin EVM transaction spend money from sponsor eth address`, async ({helper}) => {506 const owner = await helper.eth.createAccountWithBalance(donor);507 const sponsor = await helper.eth.createAccountWithBalance(donor);508 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);509 510 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');511512 const collectionSub = helper.rft.getCollectionObject(collectionId);513 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner, testCase === 'setCollectionSponsor');514 515 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;516 await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send();517 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;518 let collectionData = (await collectionSub.getData())!;519 expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));520 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');521 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;522 523 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});524 collectionData = (await collectionSub.getData())!;525 expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));526 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;527 const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner});528 expect(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorTuple.sub))).to.be.equal(helper.address.ethToSubstrate(sponsor));529530 const user = helper.eth.createAccount();531 const userCross = helper.ethCrossAccount.fromAddress(user);532 await collectionEvm.methods.addCollectionAdminCross(userCross).send();533 534 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));535 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));536 537 const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});538 const tokenId = mintingResult.events.Transfer.returnValues.tokenId;539 540 const events = helper.eth.normalizeEvents(mintingResult.events);541 const address = helper.ethAddress.fromCollectionId(collectionId);542 543 expect(events).to.deep.include({544 address,545 event: 'Transfer',546 args: {547 from: '0x0000000000000000000000000000000000000000',548 to: user,549 tokenId: '1',550 },551 });552 expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI');553 554 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));555 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);556 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));557 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;558 }));559560 itEth('Check that collection admin EVM transaction spend money from sponsor sub address', async ({helper}) => {561 const owner = await helper.eth.createAccountWithBalance(donor);562 const sponsor = alice;563 const sponsorCross = helper.ethCrossAccount.fromKeyringPair(sponsor);564565 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');566567 const collectionSub = helper.rft.getCollectionObject(collectionId);568 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner, false);569 570 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;571 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();572 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;573 let collectionData = (await collectionSub.getData())!;574 expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(sponsor.address);575 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');576577 await collectionSub.confirmSponsorship(sponsor);578 collectionData = (await collectionSub.getData())!;579 expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(sponsor.address);580 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;581 const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner});582 expect(BigInt(sponsorTuple.sub)).to.be.equal(BigInt('0x' + Buffer.from(sponsor.addressRaw).toString('hex')));583584 const user = helper.eth.createAccount();585 const userCross = helper.ethCrossAccount.fromAddress(user);586 await collectionEvm.methods.addCollectionAdminCross(userCross).send();587588 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));589 const sponsorBalanceBefore = await helper.balance.getSubstrate(sponsor.address);590 591 const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});592 const tokenId = mintingResult.events.Transfer.returnValues.tokenId;593594 const events = helper.eth.normalizeEvents(mintingResult.events);595596 expect(events).to.deep.include({597 address: collectionAddress,598 event: 'Transfer',599 args: {600 from: '0x0000000000000000000000000000000000000000',601 to: user,602 tokenId: '1',603 },604 });605 expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI');606607 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));608 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);609 const sponsorBalanceAfter = await helper.balance.getSubstrate(sponsor.address);610 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;611 });612613 itEth('Sponsoring collection from substrate address via access list', async ({helper}) => {614 const owner = await helper.eth.createAccountWithBalance(donor);615 const user = helper.eth.createAccount();616 const userCross = helper.ethCrossAccount.fromAddress(user);617 const sponsor = alice;618 const sponsorCross = helper.ethCrossAccount.fromKeyringPair(sponsor);619620 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');621 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner, false);622623 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send({from: owner});624625 const collectionSub = helper.rft.getCollectionObject(collectionId);626 await collectionSub.confirmSponsorship(sponsor);627628 const nextTokenId = await collectionEvm.methods.nextTokenId().call();629 expect(nextTokenId).to.be.equal('1');630631 await collectionEvm.methods.setCollectionAccess(1 ).send({from: owner});632 633 await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner});634 await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});635636 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));637 const sponsorBalanceBefore = await helper.balance.getSubstrate(sponsor.address);638 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));639640 {641 const nextTokenId = await collectionEvm.methods.nextTokenId().call();642 expect(nextTokenId).to.be.equal('1');643 const mintingResult = await collectionEvm.methods.mintWithTokenURI(644 user,645 'Test URI',646 ).send({from: user});647648 const events = helper.eth.normalizeEvents(mintingResult.events);649 650651 expect(events).to.deep.include({652 address: collectionAddress,653 event: 'Transfer',654 args: {655 from: '0x0000000000000000000000000000000000000000',656 to: user,657 tokenId: nextTokenId,658 },659 });660661 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));662 const sponsorBalanceAfter = await helper.balance.getSubstrate(sponsor.address);663 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));664665 expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');666 expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);667 expect(userBalanceAfter).to.be.eq(userBalanceBefore);668 expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;669 }670 });671672 itEth('Can reassign collection sponsor', async ({helper}) => {673 const owner = await helper.eth.createAccountWithBalance(donor);674 const sponsorEth = await helper.eth.createAccountWithBalance(donor);675 const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);676 const [sponsorSub] = await helper.arrange.createAccounts([100n], donor);677 const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub);678679 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');680 const collectionSub = helper.rft.getCollectionObject(collectionId);681 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);682683 684 await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossEth).send({from: owner});685 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});686687 688 await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner});689 const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship;690 expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address});691 });692693 [694 'transfer',695 'transferCross',696 'transferFrom',697 'transferFromCross',698 ].map(testCase => 699 itEth(`[${testCase}] Check that transfer via EVM spend money from sponsor address`, async ({helper}) => {700 const owner = await helper.eth.createAccountWithBalance(donor);701702 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');703 const sponsor = await helper.eth.createAccountWithBalance(donor);704 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);705 const receiver = await helper.eth.createAccountWithBalance(donor);706 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);707708 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();709 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});710711 const user = await helper.eth.createAccountWithBalance(donor);712 const userCross = helper.ethCrossAccount.fromAddress(user);713 await collectionEvm.methods.addCollectionAdminCross(userCross).send();714715 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});716 const tokenId = result.events.Transfer.returnValues.tokenId;717 718 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));719 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));720 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));721722 switch (testCase) {723 case 'transfer':724 await collectionEvm.methods.transfer(receiver, tokenId).send({from: user});725 break;726 case 'transferCross':727 await collectionEvm.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});728 break;729 case 'transferFrom':730 await collectionEvm.methods.transferFrom(user, receiver, tokenId).send({from: user});731 break;732 case 'transferFromCross':733 await collectionEvm.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});734 break;735 }736737 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));738 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);739 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));740 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;741 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));742 expect(userBalanceAfter).to.be.eq(userBalanceBefore);743 }));744});745746describe('evm RFT token sponsoring', () => {747 let donor: IKeyringPair;748749 before(async function() {750 await usingPlaygrounds(async (helper, privateKey) => {751 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);752 donor = await privateKey({filename: __filename});753 });754 });755756 [757 'transfer',758 'transferCross',759 'transferFrom',760 'transferFromCross',761 ].map(testCase => 762 itEth(`[${testCase}] Check that token piece transfer via EVM spend money from sponsor address`, async ({helper}) => {763 const owner = await helper.eth.createAccountWithBalance(donor);764765 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');766 const sponsor = await helper.eth.createAccountWithBalance(donor);767 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);768 const receiver = await helper.eth.createAccountWithBalance(donor);769 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);770771 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();772 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});773774 const user = await helper.eth.createAccountWithBalance(donor);775 const userCross = helper.ethCrossAccount.fromAddress(user);776 await collectionEvm.methods.addCollectionAdminCross(userCross).send();777778 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});779 const tokenId = result.events.Transfer.returnValues.tokenId;780781 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, user); 782 await tokenContract.methods.repartition(2).send();783 784 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));785 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));786 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));787788 switch (testCase) {789 case 'transfer':790 await tokenContract.methods.transfer(receiver, 1).send();791 break;792 case 'transferCross':793 await tokenContract.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), 1).send();794 break;795 case 'transferFrom':796 await tokenContract.methods.transferFrom(user, receiver, 1).send();797 break;798 case 'transferFromCross':799 await tokenContract.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), 1).send();800 break;801 }802803 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));804 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);805 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));806 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;807 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));808 expect(userBalanceAfter).to.be.eq(userBalanceBefore);809 }));810811 [812 'approve',813 'approveCross',814 ].map(testCase => 815 itEth(`[${testCase}] Check that approve via EVM spend money from sponsor address`, async ({helper}) => {816 const owner = await helper.eth.createAccountWithBalance(donor);817818 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');819 const sponsor = await helper.eth.createAccountWithBalance(donor);820 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);821 const receiver = await helper.eth.createAccountWithBalance(donor);822 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);823824 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();825 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});826827 const user = await helper.eth.createAccountWithBalance(donor);828 const userCross = helper.ethCrossAccount.fromAddress(user);829 await collectionEvm.methods.addCollectionAdminCross(userCross).send();830831 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});832 const tokenId = result.events.Transfer.returnValues.tokenId;833834 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, user); 835 await tokenContract.methods.repartition(2).send();836 837 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));838 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));839 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));840841 switch (testCase) {842 case 'approve':843 await tokenContract.methods.approve(receiver, 1).send();844 break;845 case 'approveCross':846 await tokenContract.methods.approveCross(helper.ethCrossAccount.fromAddress(receiver), 1).send();847 break;848 }849850 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));851 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);852 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));853 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;854 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));855 expect(userBalanceAfter).to.be.eq(userBalanceBefore);856 }));857});858