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 const sponsorSubAddress = helper.address.normalizeSubstrateToChainFormat(helper.address.ethToSubstrate(sponsor));529 const actualSubAddress = helper.address.normalizeSubstrateToChainFormat(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorTuple.sub)));530 expect(actualSubAddress).to.be.equal(sponsorSubAddress);531532 const user = helper.eth.createAccount();533 const userCross = helper.ethCrossAccount.fromAddress(user);534 await collectionEvm.methods.addCollectionAdminCross(userCross).send();535 536 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));537 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));538 539 const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});540 const tokenId = mintingResult.events.Transfer.returnValues.tokenId;541 542 const events = helper.eth.normalizeEvents(mintingResult.events);543 const address = helper.ethAddress.fromCollectionId(collectionId);544 545 expect(events).to.deep.include({546 address,547 event: 'Transfer',548 args: {549 from: '0x0000000000000000000000000000000000000000',550 to: user,551 tokenId: '1',552 },553 });554 expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI');555 556 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));557 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);558 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));559 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;560 }));561562 itEth('Check that collection admin EVM transaction spend money from sponsor sub address', async ({helper}) => {563 const owner = await helper.eth.createAccountWithBalance(donor);564 const sponsor = alice;565 const sponsorCross = helper.ethCrossAccount.fromKeyringPair(sponsor);566567 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');568569 const collectionSub = helper.rft.getCollectionObject(collectionId);570 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner, false);571 572 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;573 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();574 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;575 let collectionData = (await collectionSub.getData())!;576 expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(sponsor.address);577 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');578579 await collectionSub.confirmSponsorship(sponsor);580 collectionData = (await collectionSub.getData())!;581 expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(sponsor.address);582 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;583 const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner});584 expect(BigInt(sponsorTuple.sub)).to.be.equal(BigInt('0x' + Buffer.from(sponsor.addressRaw).toString('hex')));585586 const user = helper.eth.createAccount();587 const userCross = helper.ethCrossAccount.fromAddress(user);588 await collectionEvm.methods.addCollectionAdminCross(userCross).send();589590 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));591 const sponsorBalanceBefore = await helper.balance.getSubstrate(sponsor.address);592 593 const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});594 const tokenId = mintingResult.events.Transfer.returnValues.tokenId;595596 const events = helper.eth.normalizeEvents(mintingResult.events);597598 expect(events).to.deep.include({599 address: collectionAddress,600 event: 'Transfer',601 args: {602 from: '0x0000000000000000000000000000000000000000',603 to: user,604 tokenId: '1',605 },606 });607 expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI');608609 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));610 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);611 const sponsorBalanceAfter = await helper.balance.getSubstrate(sponsor.address);612 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;613 });614615 itEth('Sponsoring collection from substrate address via access list', async ({helper}) => {616 const owner = await helper.eth.createAccountWithBalance(donor);617 const user = helper.eth.createAccount();618 const userCross = helper.ethCrossAccount.fromAddress(user);619 const sponsor = alice;620 const sponsorCross = helper.ethCrossAccount.fromKeyringPair(sponsor);621622 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');623 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner, false);624625 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send({from: owner});626627 const collectionSub = helper.rft.getCollectionObject(collectionId);628 await collectionSub.confirmSponsorship(sponsor);629630 const nextTokenId = await collectionEvm.methods.nextTokenId().call();631 expect(nextTokenId).to.be.equal('1');632633 await collectionEvm.methods.setCollectionAccess(1 ).send({from: owner});634 635 await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner});636 await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});637638 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));639 const sponsorBalanceBefore = await helper.balance.getSubstrate(sponsor.address);640 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));641642 {643 const nextTokenId = await collectionEvm.methods.nextTokenId().call();644 expect(nextTokenId).to.be.equal('1');645 const mintingResult = await collectionEvm.methods.mintWithTokenURI(646 user,647 'Test URI',648 ).send({from: user});649650 const events = helper.eth.normalizeEvents(mintingResult.events);651 652653 expect(events).to.deep.include({654 address: collectionAddress,655 event: 'Transfer',656 args: {657 from: '0x0000000000000000000000000000000000000000',658 to: user,659 tokenId: nextTokenId,660 },661 });662663 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));664 const sponsorBalanceAfter = await helper.balance.getSubstrate(sponsor.address);665 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));666667 expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');668 expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);669 expect(userBalanceAfter).to.be.eq(userBalanceBefore);670 expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;671 }672 });673674 itEth('Can reassign collection sponsor', async ({helper}) => {675 const owner = await helper.eth.createAccountWithBalance(donor);676 const sponsorEth = await helper.eth.createAccountWithBalance(donor);677 const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);678 const [sponsorSub] = await helper.arrange.createAccounts([100n], donor);679 const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub);680681 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');682 const collectionSub = helper.rft.getCollectionObject(collectionId);683 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);684685 686 await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossEth).send({from: owner});687 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});688689 690 await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner});691 const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship;692 expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address});693 });694695 [696 'transfer',697 'transferCross',698 'transferFrom',699 'transferFromCross',700 ].map(testCase => 701 itEth(`[${testCase}] Check that transfer via EVM spend money from sponsor address`, async ({helper}) => {702 const owner = await helper.eth.createAccountWithBalance(donor);703704 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');705 const sponsor = await helper.eth.createAccountWithBalance(donor);706 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);707 const receiver = await helper.eth.createAccountWithBalance(donor);708 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);709710 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();711 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});712713 const user = await helper.eth.createAccountWithBalance(donor);714 const userCross = helper.ethCrossAccount.fromAddress(user);715 await collectionEvm.methods.addCollectionAdminCross(userCross).send();716717 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});718 const tokenId = result.events.Transfer.returnValues.tokenId;719 720 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));721 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));722 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));723724 switch (testCase) {725 case 'transfer':726 await collectionEvm.methods.transfer(receiver, tokenId).send({from: user});727 break;728 case 'transferCross':729 await collectionEvm.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});730 break;731 case 'transferFrom':732 await collectionEvm.methods.transferFrom(user, receiver, tokenId).send({from: user});733 break;734 case 'transferFromCross':735 await collectionEvm.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});736 break;737 }738739 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));740 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);741 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));742 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;743 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));744 expect(userBalanceAfter).to.be.eq(userBalanceBefore);745 }));746});747748describe('evm RFT token sponsoring', () => {749 let donor: IKeyringPair;750751 before(async function() {752 await usingPlaygrounds(async (helper, privateKey) => {753 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);754 donor = await privateKey({filename: __filename});755 });756 });757758 [759 'transfer',760 'transferCross',761 'transferFrom',762 'transferFromCross',763 ].map(testCase => 764 itEth(`[${testCase}] Check that token piece transfer via EVM spend money from sponsor address`, async ({helper}) => {765 const owner = await helper.eth.createAccountWithBalance(donor);766767 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');768 const sponsor = await helper.eth.createAccountWithBalance(donor);769 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);770 const receiver = await helper.eth.createAccountWithBalance(donor);771 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);772773 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();774 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});775776 const user = await helper.eth.createAccountWithBalance(donor);777 const userCross = helper.ethCrossAccount.fromAddress(user);778 await collectionEvm.methods.addCollectionAdminCross(userCross).send();779780 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});781 const tokenId = result.events.Transfer.returnValues.tokenId;782783 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, user); 784 await tokenContract.methods.repartition(2).send();785 786 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));787 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));788 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));789790 switch (testCase) {791 case 'transfer':792 await tokenContract.methods.transfer(receiver, 1).send();793 break;794 case 'transferCross':795 await tokenContract.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), 1).send();796 break;797 case 'transferFrom':798 await tokenContract.methods.transferFrom(user, receiver, 1).send();799 break;800 case 'transferFromCross':801 await tokenContract.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), 1).send();802 break;803 }804805 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));806 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);807 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));808 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;809 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));810 expect(userBalanceAfter).to.be.eq(userBalanceBefore);811 }));812813 [814 'approve',815 'approveCross',816 ].map(testCase => 817 itEth(`[${testCase}] Check that approve via EVM spend money from sponsor address`, async ({helper}) => {818 const owner = await helper.eth.createAccountWithBalance(donor);819820 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');821 const sponsor = await helper.eth.createAccountWithBalance(donor);822 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);823 const receiver = await helper.eth.createAccountWithBalance(donor);824 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);825826 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();827 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});828829 const user = await helper.eth.createAccountWithBalance(donor);830 const userCross = helper.ethCrossAccount.fromAddress(user);831 await collectionEvm.methods.addCollectionAdminCross(userCross).send();832833 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});834 const tokenId = result.events.Transfer.returnValues.tokenId;835836 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, user); 837 await tokenContract.methods.repartition(2).send();838 839 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));840 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));841 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));842843 switch (testCase) {844 case 'approve':845 await tokenContract.methods.approve(receiver, 1).send();846 break;847 case 'approveCross':848 await tokenContract.methods.approveCross(helper.ethCrossAccount.fromAddress(receiver), 1).send();849 break;850 }851852 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));853 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);854 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));855 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;856 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));857 expect(userBalanceAfter).to.be.eq(userBalanceBefore);858 }));859});860