1234567891011121314151617import type {IKeyringPair} from '@polkadot/types/types';18import {Pallets, requirePalletsOrSkip, usingPlaygrounds} from '../util/index.js';19import {itEth, expect} from './util/index.js';20import {CollectionLimitField, TokenPermissionField} from './util/playgrounds/types.js';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({url: import.meta.url});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 sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});106 expect(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorStruct.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 sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});112 expect(sponsorStruct.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.ethCrossAccount.createAccount();143 const nextTokenId = await collectionEvm.methods.nextTokenId().call();144 expect(nextTokenId).to.be.equal('1');145146 147 const oldPermissions = (await collectionSub.getData())!.raw.permissions;148 expect(oldPermissions.mintMode).to.be.false;149 expect(oldPermissions.access).to.be.equal('Normal');150151 await collectionEvm.methods.setCollectionAccess(1 ).send({from: owner});152 await collectionEvm.methods.addToCollectionAllowListCross(user).send({from: owner});153 await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});154 await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsoredDataRateLimit, value: {status: true, value: 30}}).send();155156 const newPermissions = (await collectionSub.getData())!.raw.permissions;157 expect(newPermissions.mintMode).to.be.true;158 expect(newPermissions.access).to.be.equal('AllowList');159160 161 await collectionEvm.methods.setTokenPropertyPermissions([162 ['key', [163 [TokenPermissionField.TokenOwner, true],164 ],165 ],166 ]).send({from: owner});167168 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));169 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));170 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));171172 173 {174 const result = await collectionEvm.methods.mintCross(user, [{key: 'key', value: Buffer.from('Value')}]).send({from: user.eth});175 const event = helper.eth.normalizeEvents(result.events)176 .find(event => event.event === 'Transfer');177178 expect(event).to.be.deep.equal({179 address: collectionAddress,180 event: 'Transfer',181 args: {182 from: '0x0000000000000000000000000000000000000000',183 to: user.eth,184 tokenId: '1',185 },186 });187188 189190 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));191 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));192 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));193194 expect(await collectionEvm.methods.properties(nextTokenId, []).call())195 .to.be.like([196 [197 'key',198 '0x' + Buffer.from('Value').toString('hex'),199 ],200 ]);201 expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);202 expect(userBalanceAfter).to.be.eq(userBalanceBefore);203 expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;204 }205 }));206207 itEth('Can sponsor [set token properties] via access list', async ({helper}) => {208 const owner = await helper.eth.createAccountWithBalance(donor);209 const sponsorEth = await helper.eth.createAccountWithBalance(donor);210 const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);211212 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Sponsor collection', '1', '1', '');213 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, false);214215 216 await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossEth).send({from: owner});217218 219 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});220221 222 const user = helper.ethCrossAccount.createAccount();223 const nextTokenId = await collectionEvm.methods.nextTokenId().call();224 expect(nextTokenId).to.be.equal('1');225226 227 await collectionEvm.methods.setCollectionAccess(1 ).send({from: owner});228 await collectionEvm.methods.addToCollectionAllowListCross(user).send({from: owner});229 await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});230 await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsoredDataRateLimit, value: {status: true, value: 30}}).send();231232 233 await collectionEvm.methods.setTokenPropertyPermissions([234 ['key', [235 [TokenPermissionField.TokenOwner, true],236 ],237 ],238 ]).send({from: owner});239240 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));241 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));242 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));243244 245 {246 const result = await collectionEvm.methods.mintCross(user, []).send({from: user.eth});247 const event = helper.eth.normalizeEvents(result.events)248 .find(event => event.event === 'Transfer');249250 expect(event).to.be.deep.equal({251 address: collectionAddress,252 event: 'Transfer',253 args: {254 from: '0x0000000000000000000000000000000000000000',255 to: user.eth,256 tokenId: '1',257 },258 });259260 await collectionEvm.methods.setProperties(1, [{key: 'key', value: Buffer.from('Value')}]).send({from: user.eth});261262 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));263 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));264 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));265266 expect(await collectionEvm.methods.properties(nextTokenId, []).call())267 .to.be.like([268 [269 'key',270 '0x' + Buffer.from('Value').toString('hex'),271 ],272 ]);273 expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);274 expect(userBalanceAfter).to.be.eq(userBalanceBefore);275 expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;276 }277 });278279 280 281 282 283 284 285 286 287288 289290 291 292293 294 295 296297 298 299 300301 302 303304 305 306 307 308 309 310 311 312 313314 315 316 317 318 319 320 321 322 323 324 325326 327 328329 330 331 332 333 334335 [336 'setCollectionSponsorCross',337 'setCollectionSponsor', 338 ].map(testCase =>339 itEth(`[${testCase}] Check that transaction via EVM spend money from sponsor address`, async ({helper}) => {340 const owner = await helper.eth.createAccountWithBalance(donor);341 const sponsor = await helper.eth.createAccountWithBalance(donor);342 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);343344 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner,'Sponsor collection', '1', '1', '');345346 const collectionSub = helper.nft.getCollectionObject(collectionId);347 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, testCase === 'setCollectionSponsor');348 349 await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send();350 let collectionData = (await collectionSub.getData())!;351 expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));352 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');353354 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});355 collectionData = (await collectionSub.getData())!;356 expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));357358 const user = helper.eth.createAccount();359 const userCross = helper.ethCrossAccount.fromAddress(user);360 await collectionEvm.methods.addCollectionAdminCross(userCross).send();361362 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));363 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));364365 const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});366 const tokenId = mintingResult.events.Transfer.returnValues.tokenId;367368 const event = helper.eth.normalizeEvents(mintingResult.events)369 .find(event => event.event === 'Transfer');370 const address = helper.ethAddress.fromCollectionId(collectionId);371372 expect(event).to.be.deep.equal({373 address,374 event: 'Transfer',375 args: {376 from: '0x0000000000000000000000000000000000000000',377 to: user,378 tokenId: '1',379 },380 });381 expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI');382383 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));384 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);385 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));386 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;387 }));388389 itEth('Can reassign collection sponsor', async ({helper}) => {390 const owner = await helper.eth.createAccountWithBalance(donor);391 const sponsorEth = await helper.eth.createAccountWithBalance(donor);392 const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);393 const [sponsorSub] = await helper.arrange.createAccounts([100n], donor);394 const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub);395396 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner,'Sponsor collection', '1', '1', '');397 const collectionSub = helper.nft.getCollectionObject(collectionId);398 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);399400 401 await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossEth).send({from: owner});402 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});403404 405 await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner});406 const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship;407 expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address});408 });409});410411describe('evm RFT collection sponsoring', () => {412 let donor: IKeyringPair;413 let alice: IKeyringPair;414 let nominal: bigint;415416 before(async function() {417 await usingPlaygrounds(async (helper, privateKey) => {418 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);419 donor = await privateKey({url: import.meta.url});420 [alice] = await helper.arrange.createAccounts([100n], donor);421 nominal = helper.balance.getOneTokenNominal();422 });423 });424425 [426 'mintCross',427 'mintWithTokenURI',428 ].map(testCase =>429 itEth(`[${testCase}] sponsors mint transactions`, async ({helper}) => {430 const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'spnr', permissions: {mintMode: true}, tokenPropertyPermissions: [431 {key: 'URI', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},432 ]});433434 const owner = await helper.eth.createAccountWithBalance(donor);435 await collection.setSponsor(alice, alice.address);436 await collection.confirmSponsorship(alice);437438 const minter = helper.eth.createAccount();439 const minterCross = helper.ethCrossAccount.fromAddress(minter);440 expect(await helper.balance.getEthereum(minter)).to.equal(0n);441442 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);443 const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', minter, true);444445 await collection.addToAllowList(alice, {Ethereum: minter});446 await collection.addAdmin(alice, {Ethereum: owner});447 const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);448 await collectionHelpers.methods.makeCollectionERC721MetadataCompatible(collectionAddress, 'base/')449 .send();450451 let mintingResult;452 let tokenId;453 switch (testCase) {454 case 'mintCross':455 mintingResult = await contract.methods.mintCross(minterCross, []).send();456 break;457 case 'mintWithTokenURI':458 mintingResult = await contract.methods.mintWithTokenURI(minter, 'Test URI').send();459 tokenId = mintingResult.events.Transfer.returnValues.tokenId;460 expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');461 break;462 }463464 const events = helper.eth.normalizeEvents(mintingResult.events);465 expect(events).to.deep.include({466 address: collectionAddress,467 event: 'Transfer',468 args: {469 from: '0x0000000000000000000000000000000000000000',470 to: minter,471 tokenId: '1',472 },473 });474 }));475476 [477 'setCollectionSponsorCross',478 'setCollectionSponsor', 479 ].map(testCase =>480 itEth(`[${testCase}] can remove collection sponsor`, async ({helper}) => {481 const owner = await helper.eth.createAccountWithBalance(donor);482 const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);483484 let result = await collectionHelpers.methods.createRFTCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)});485 const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);486 const sponsor = await helper.eth.createAccountWithBalance(donor);487 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);488 const collectionEvm = await helper.ethNativeContract.collection(collectionIdAddress, 'rft', owner, testCase === 'setCollectionSponsor');489490 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;491 result = await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send({from: owner});492 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;493494 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});495 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;496497 await collectionEvm.methods.removeCollectionSponsor().send({from: owner});498499 const sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});500 expect(sponsorStruct.eth).to.be.eq('0x0000000000000000000000000000000000000000');501 }));502503 [504 'setCollectionSponsorCross',505 'setCollectionSponsor', 506 ].map(testCase =>507 itEth(`[${testCase}] Can sponsor from evm address via access list`, async ({helper}) => {508 const owner = await helper.eth.createAccountWithBalance(donor);509 const sponsorEth = await helper.eth.createAccountWithBalance(donor);510 const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);511512 const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Sponsor collection', '1', '1', '');513514 const collectionSub = helper.rft.getCollectionObject(collectionId);515 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner, testCase === 'setCollectionSponsor');516517 518 await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsorEth : sponsorCrossEth).send({from: owner});519 let sponsorship = (await collectionSub.getData())!.raw.sponsorship;520 expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));521 522 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');523524 525 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});526 sponsorship = (await collectionSub.getData())!.raw.sponsorship;527 expect(sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));528529 530 const user = helper.eth.createAccount();531 const userCross = helper.ethCrossAccount.fromAddress(user);532 const nextTokenId = await collectionEvm.methods.nextTokenId().call();533 expect(nextTokenId).to.be.equal('1');534535 536 const oldPermissions = (await collectionSub.getData())!.raw.permissions;537 expect(oldPermissions.mintMode).to.be.false;538 expect(oldPermissions.access).to.be.equal('Normal');539540 await collectionEvm.methods.setCollectionAccess(1 ).send({from: owner});541 await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner});542 await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});543544 const newPermissions = (await collectionSub.getData())!.raw.permissions;545 expect(newPermissions.mintMode).to.be.true;546 expect(newPermissions.access).to.be.equal('AllowList');547548 549 await collectionEvm.methods.setTokenPropertyPermissions([550 ['URI', [551 [TokenPermissionField.TokenOwner, true],552 [TokenPermissionField.CollectionAdmin, true],553 ],554 ],555 ]).send({from: owner});556557 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));558 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));559 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));560561 562 {563 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});564 const events = helper.eth.normalizeEvents(result.events);565566 expect(events).to.deep.include({567 address: collectionAddress,568 event: 'Transfer',569 args: {570 from: '0x0000000000000000000000000000000000000000',571 to: user,572 tokenId: '1',573 },574 });575576 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));577 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));578 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));579580 expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');581 expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);582 expect(userBalanceAfter).to.be.eq(userBalanceBefore);583 expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;584 }585 }));586587 [588 'setCollectionSponsorCross',589 'setCollectionSponsor', 590 ].map(testCase =>591 itEth(`[${testCase}] Check that collection admin EVM transaction spend money from sponsor eth address`, async ({helper}) => {592 const owner = await helper.eth.createAccountWithBalance(donor);593 const sponsor = await helper.eth.createAccountWithBalance(donor);594 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);595596 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');597598 const collectionSub = helper.rft.getCollectionObject(collectionId);599 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner, testCase === 'setCollectionSponsor');600 601 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;602 await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send();603 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;604 let collectionData = (await collectionSub.getData())!;605 expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));606 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');607 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;608609 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});610 collectionData = (await collectionSub.getData())!;611 expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));612 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;613 const sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});614 const sponsorSubAddress = helper.address.normalizeSubstrateToChainFormat(helper.address.ethToSubstrate(sponsor));615 const actualSubAddress = helper.address.normalizeSubstrateToChainFormat(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorStruct.sub)));616 expect(actualSubAddress).to.be.equal(sponsorSubAddress);617618 const user = helper.eth.createAccount();619 const userCross = helper.ethCrossAccount.fromAddress(user);620 await collectionEvm.methods.addCollectionAdminCross(userCross).send();621622 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));623 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));624625 const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});626 const tokenId = mintingResult.events.Transfer.returnValues.tokenId;627628 const events = helper.eth.normalizeEvents(mintingResult.events);629 const address = helper.ethAddress.fromCollectionId(collectionId);630631 expect(events).to.deep.include({632 address,633 event: 'Transfer',634 args: {635 from: '0x0000000000000000000000000000000000000000',636 to: user,637 tokenId: '1',638 },639 });640 expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI');641642 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));643 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);644 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));645 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;646 }));647648 itEth('Check that collection admin EVM transaction spend money from sponsor sub address', async ({helper}) => {649 const owner = await helper.eth.createAccountWithBalance(donor);650 const sponsor = alice;651 const sponsorCross = helper.ethCrossAccount.fromKeyringPair(sponsor);652653 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');654655 const collectionSub = helper.rft.getCollectionObject(collectionId);656 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner, false);657 658 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;659 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();660 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;661 let collectionData = (await collectionSub.getData())!;662 expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(sponsor.address);663 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');664665 await collectionSub.confirmSponsorship(sponsor);666 collectionData = (await collectionSub.getData())!;667 expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(sponsor.address);668 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;669 const sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});670 expect(BigInt(sponsorStruct.sub)).to.be.equal(BigInt('0x' + Buffer.from(sponsor.addressRaw).toString('hex')));671672 const user = helper.eth.createAccount();673 const userCross = helper.ethCrossAccount.fromAddress(user);674 await collectionEvm.methods.addCollectionAdminCross(userCross).send();675676 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));677 const sponsorBalanceBefore = await helper.balance.getSubstrate(sponsor.address);678679 const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});680 const tokenId = mintingResult.events.Transfer.returnValues.tokenId;681682 const events = helper.eth.normalizeEvents(mintingResult.events);683684 expect(events).to.deep.include({685 address: collectionAddress,686 event: 'Transfer',687 args: {688 from: '0x0000000000000000000000000000000000000000',689 to: user,690 tokenId: '1',691 },692 });693 expect(await collectionEvm.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');694695 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));696 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);697 const sponsorBalanceAfter = await helper.balance.getSubstrate(sponsor.address);698 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;699 });700701 itEth('Sponsoring collection from substrate address via access list', async ({helper}) => {702 const owner = await helper.eth.createAccountWithBalance(donor);703 const user = helper.eth.createAccount();704 const userCross = helper.ethCrossAccount.fromAddress(user);705 const sponsor = alice;706 const sponsorCross = helper.ethCrossAccount.fromKeyringPair(sponsor);707708 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');709 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner, false);710711 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send({from: owner});712713 const collectionSub = helper.rft.getCollectionObject(collectionId);714 await collectionSub.confirmSponsorship(sponsor);715716 const nextTokenId = await collectionEvm.methods.nextTokenId().call();717 expect(nextTokenId).to.be.equal('1');718719 await collectionEvm.methods.setCollectionAccess(1 ).send({from: owner});720721 await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner});722 await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});723724 725 await collectionEvm.methods.setTokenPropertyPermissions([726 ['URI', [727 [TokenPermissionField.TokenOwner, true],728 [TokenPermissionField.CollectionAdmin, true],729 ],730 ],731 ]).send({from: owner});732733 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));734 const sponsorBalanceBefore = await helper.balance.getSubstrate(sponsor.address);735 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));736737 {738 const nextTokenId = await collectionEvm.methods.nextTokenId().call();739 expect(nextTokenId).to.be.equal('1');740 const mintingResult = await collectionEvm.methods.mintWithTokenURI(741 user,742 'Test URI',743 ).send({from: user});744745 const events = helper.eth.normalizeEvents(mintingResult.events);746747748 expect(events).to.deep.include({749 address: collectionAddress,750 event: 'Transfer',751 args: {752 from: '0x0000000000000000000000000000000000000000',753 to: user,754 tokenId: nextTokenId,755 },756 });757758 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));759 const sponsorBalanceAfter = await helper.balance.getSubstrate(sponsor.address);760 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));761762 expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');763 expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);764 expect(userBalanceAfter).to.be.eq(userBalanceBefore);765 expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;766 }767 });768769 itEth('Can reassign collection sponsor', async ({helper}) => {770 const owner = await helper.eth.createAccountWithBalance(donor);771 const sponsorEth = await helper.eth.createAccountWithBalance(donor);772 const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);773 const [sponsorSub] = await helper.arrange.createAccounts([100n], donor);774 const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub);775776 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');777 const collectionSub = helper.rft.getCollectionObject(collectionId);778 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);779780 781 await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossEth).send({from: owner});782 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});783784 785 await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner});786 const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship;787 expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address});788 });789790 [791 'transfer',792 'transferCross',793 'transferFrom',794 'transferFromCross',795 ].map(testCase =>796 itEth(`[${testCase}] Check that transfer via EVM spend money from sponsor address`, async ({helper}) => {797 const owner = await helper.eth.createAccountWithBalance(donor);798799 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');800 const sponsor = await helper.eth.createAccountWithBalance(donor);801 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);802 const receiver = await helper.eth.createAccountWithBalance(donor);803 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);804805 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();806 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});807808 const user = await helper.eth.createAccountWithBalance(donor);809 const userCross = helper.ethCrossAccount.fromAddress(user);810 await collectionEvm.methods.addCollectionAdminCross(userCross).send();811812 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});813 const tokenId = result.events.Transfer.returnValues.tokenId;814815 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));816 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));817 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));818819 switch (testCase) {820 case 'transfer':821 await collectionEvm.methods.transfer(receiver, tokenId).send({from: user});822 break;823 case 'transferCross':824 await collectionEvm.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});825 break;826 case 'transferFrom':827 await collectionEvm.methods.transferFrom(user, receiver, tokenId).send({from: user});828 break;829 case 'transferFromCross':830 await collectionEvm.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});831 break;832 }833834 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));835 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);836 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));837 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;838 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));839 expect(userBalanceAfter).to.be.eq(userBalanceBefore);840 }));841});842843describe('evm RFT token sponsoring', () => {844 let donor: IKeyringPair;845846 before(async function() {847 await usingPlaygrounds(async (helper, privateKey) => {848 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);849 donor = await privateKey({url: import.meta.url});850 });851 });852853 [854 'transfer',855 'transferCross',856 'transferFrom',857 'transferFromCross',858 ].map(testCase =>859 itEth(`[${testCase}] Check that token piece transfer via EVM spend money from sponsor address`, async ({helper}) => {860 const owner = await helper.eth.createAccountWithBalance(donor);861862 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');863 const sponsor = await helper.eth.createAccountWithBalance(donor);864 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);865 const receiver = await helper.eth.createAccountWithBalance(donor);866 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);867868 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();869 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});870871 const user = await helper.eth.createAccountWithBalance(donor);872 const userCross = helper.ethCrossAccount.fromAddress(user);873 await collectionEvm.methods.addCollectionAdminCross(userCross).send();874875 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});876 const tokenId = result.events.Transfer.returnValues.tokenId;877878 const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, user);879 await tokenContract.methods.repartition(2).send();880881 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));882 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));883 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));884885 switch (testCase) {886 case 'transfer':887 await tokenContract.methods.transfer(receiver, 1).send();888 break;889 case 'transferCross':890 await tokenContract.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), 1).send();891 break;892 case 'transferFrom':893 await tokenContract.methods.transferFrom(user, receiver, 1).send();894 break;895 case 'transferFromCross':896 await tokenContract.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), 1).send();897 break;898 }899900 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));901 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);902 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));903 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;904 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));905 expect(userBalanceAfter).to.be.eq(userBalanceBefore);906 }));907908 [909 'approve',910 'approveCross',911 ].map(testCase =>912 itEth(`[${testCase}] Check that approve via EVM spend money from sponsor address`, async ({helper}) => {913 const owner = await helper.eth.createAccountWithBalance(donor);914915 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');916 const sponsor = await helper.eth.createAccountWithBalance(donor);917 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);918 const receiver = await helper.eth.createAccountWithBalance(donor);919 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);920921 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();922 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});923924 const user = await helper.eth.createAccountWithBalance(donor);925 const userCross = helper.ethCrossAccount.fromAddress(user);926 await collectionEvm.methods.addCollectionAdminCross(userCross).send();927928 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});929 const tokenId = result.events.Transfer.returnValues.tokenId;930931 const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, user);932 await tokenContract.methods.repartition(2).send();933934 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));935 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));936 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));937938 switch (testCase) {939 case 'approve':940 await tokenContract.methods.approve(receiver, 1).send();941 break;942 case 'approveCross':943 await tokenContract.methods.approveCross(helper.ethCrossAccount.fromAddress(receiver), 1).send();944 break;945 }946947 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));948 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);949 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));950 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;951 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));952 expect(userBalanceAfter).to.be.eq(userBalanceBefore);953 }));954});955