1234567891011121314151617import {IKeyringPair} from '@polkadot/types/types';18import {Pallets, requirePalletsOrSkip, usingPlaygrounds} from '../util/index';19import {itEth, expect} from './util';2021describe('evm nft collection sponsoring', () => {22 let donor: IKeyringPair;23 let alice: IKeyringPair;24 let nominal: bigint;2526 before(async () => {27 await usingPlaygrounds(async (helper, privateKey) => {28 donor = await privateKey({url: import.meta.url});29 [alice] = await helper.arrange.createAccounts([100n], donor);30 nominal = helper.balance.getOneTokenNominal();31 });32 });3334 35 itEth('sponsors mint transactions', async ({helper}) => {36 const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'spnr', permissions: {mintMode: true}});37 await collection.setSponsor(alice, alice.address);38 await collection.confirmSponsorship(alice);3940 const minter = helper.eth.createAccount();41 expect(await helper.balance.getEthereum(minter)).to.equal(0n);4243 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);44 const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', minter);4546 await collection.addToAllowList(alice, {Ethereum: minter});4748 const result = await contract.methods.mint(minter).send();4950 const events = helper.eth.normalizeEvents(result.events);51 expect(events).to.be.deep.equal([52 {53 address: collectionAddress,54 event: 'Transfer',55 args: {56 from: '0x0000000000000000000000000000000000000000',57 to: minter,58 tokenId: '1',59 },60 },61 ]);62 });6364 65 66 67 68 69 70 71 7273 74 75 7677 78 79 8081 82 83 8485 [86 'setCollectionSponsorCross',87 'setCollectionSponsor', 88 ].map(testCase =>89 itEth(`[${testCase}] can remove collection sponsor`, async ({helper}) => {90 const owner = await helper.eth.createAccountWithBalance(donor);91 const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);9293 let result = await collectionHelpers.methods.createNFTCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)});94 const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);95 const sponsor = await helper.eth.createAccountWithBalance(donor);96 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);97 const collectionEvm = await helper.ethNativeContract.collection(collectionIdAddress, 'nft', owner, testCase === 'setCollectionSponsor');9899 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;100 result = await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send({from: owner});101 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;102103 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});104 let sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});105 expect(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorStruct.sub))).to.be.eq(helper.address.ethToSubstrate(sponsor, true));106 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;107108 await collectionEvm.methods.removeCollectionSponsor().send({from: owner});109110 sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});111 expect(sponsorStruct.eth).to.be.eq('0x0000000000000000000000000000000000000000');112 }));113114 [115 'setCollectionSponsorCross',116 'setCollectionSponsor', 117 ].map(testCase =>118 itEth(`[${testCase}] Can sponsor from evm address via access list`, async ({helper}) => {119 const owner = await helper.eth.createAccountWithBalance(donor);120 const sponsorEth = await helper.eth.createAccountWithBalance(donor);121 const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);122123 const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Sponsor collection', '1', '1', '');124125 const collectionSub = helper.nft.getCollectionObject(collectionId);126 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, testCase === 'setCollectionSponsor');127128 129 await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsorEth : sponsorCrossEth).send({from: owner});130 let sponsorship = (await collectionSub.getData())!.raw.sponsorship;131 expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));132 133 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');134135 136 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});137 sponsorship = (await collectionSub.getData())!.raw.sponsorship;138 expect(sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));139140 141 const user = helper.eth.createAccount();142 const userCross = helper.ethCrossAccount.fromAddress(user);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(userCross).send({from: owner});153 await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});154155 const newPermissions = (await collectionSub.getData())!.raw.permissions;156 expect(newPermissions.mintMode).to.be.true;157 expect(newPermissions.access).to.be.equal('AllowList');158159 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));160 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));161 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));162163 164 {165 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});166 const event = helper.eth.normalizeEvents(result.events)167 .find(event => event.event === 'Transfer');168169 expect(event).to.be.deep.equal({170 address: collectionAddress,171 event: 'Transfer',172 args: {173 from: '0x0000000000000000000000000000000000000000',174 to: user,175 tokenId: '1',176 },177 });178179 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));180 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));181 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));182183 expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');184 expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);185 expect(userBalanceAfter).to.be.eq(userBalanceBefore);186 expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;187 }188 }));189190 191 192 193 194 195 196 197 198199 200201 202 203204 205 206 207208 209 210 211212 213 214215 216 217 218 219 220 221 222 223 224225 226 227 228 229 230 231 232 233 234 235 236237 238 239240 241 242 243 244 245246 [247 'setCollectionSponsorCross',248 'setCollectionSponsor', 249 ].map(testCase =>250 itEth(`[${testCase}] Check that transaction via EVM spend money from sponsor address`, async ({helper}) => {251 const owner = await helper.eth.createAccountWithBalance(donor);252 const sponsor = await helper.eth.createAccountWithBalance(donor);253 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);254255 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner,'Sponsor collection', '1', '1', '');256257 const collectionSub = helper.nft.getCollectionObject(collectionId);258 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, testCase === 'setCollectionSponsor');259 260 await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send();261 let collectionData = (await collectionSub.getData())!;262 expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));263 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');264265 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});266 collectionData = (await collectionSub.getData())!;267 expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));268269 const user = helper.eth.createAccount();270 const userCross = helper.ethCrossAccount.fromAddress(user);271 await collectionEvm.methods.addCollectionAdminCross(userCross).send();272273 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));274 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));275276 const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});277 const tokenId = mintingResult.events.Transfer.returnValues.tokenId;278279 const event = helper.eth.normalizeEvents(mintingResult.events)280 .find(event => event.event === 'Transfer');281 const address = helper.ethAddress.fromCollectionId(collectionId);282283 expect(event).to.be.deep.equal({284 address,285 event: 'Transfer',286 args: {287 from: '0x0000000000000000000000000000000000000000',288 to: user,289 tokenId: '1',290 },291 });292 expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI');293294 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));295 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);296 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));297 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;298 }));299300 itEth('Can reassign collection sponsor', async ({helper}) => {301 const owner = await helper.eth.createAccountWithBalance(donor);302 const sponsorEth = await helper.eth.createAccountWithBalance(donor);303 const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);304 const [sponsorSub] = await helper.arrange.createAccounts([100n], donor);305 const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub);306307 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner,'Sponsor collection', '1', '1', '');308 const collectionSub = helper.nft.getCollectionObject(collectionId);309 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);310311 312 await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossEth).send({from: owner});313 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});314315 316 await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner});317 const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship;318 expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address});319 });320});321322describe('evm RFT collection sponsoring', () => {323 let donor: IKeyringPair;324 let alice: IKeyringPair;325 let nominal: bigint;326327 before(async function() {328 await usingPlaygrounds(async (helper, privateKey) => {329 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);330 donor = await privateKey({url: import.meta.url});331 [alice] = await helper.arrange.createAccounts([100n], donor);332 nominal = helper.balance.getOneTokenNominal();333 });334 });335336 [337 'mintCross',338 'mintWithTokenURI',339 ].map(testCase =>340 itEth(`[${testCase}] sponsors mint transactions`, async ({helper}) => {341 const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'spnr', permissions: {mintMode: true}, tokenPropertyPermissions: [342 {key: 'URI', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},343 ]});344345 const owner = await helper.eth.createAccountWithBalance(donor);346 await collection.setSponsor(alice, alice.address);347 await collection.confirmSponsorship(alice);348349 const minter = helper.eth.createAccount();350 const minterCross = helper.ethCrossAccount.fromAddress(minter);351 expect(await helper.balance.getEthereum(minter)).to.equal(0n);352353 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);354 const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', minter, true);355356 await collection.addToAllowList(alice, {Ethereum: minter});357 await collection.addAdmin(alice, {Ethereum: owner});358 const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);359 await collectionHelpers.methods.makeCollectionERC721MetadataCompatible(collectionAddress, 'base/')360 .send();361362 let mintingResult;363 let tokenId;364 switch (testCase) {365 case 'mintCross':366 mintingResult = await contract.methods.mintCross(minterCross, []).send();367 break;368 case 'mintWithTokenURI':369 mintingResult = await contract.methods.mintWithTokenURI(minter, 'Test URI').send();370 tokenId = mintingResult.events.Transfer.returnValues.tokenId;371 expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');372 break;373 }374375 const events = helper.eth.normalizeEvents(mintingResult.events);376 expect(events).to.deep.include({377 address: collectionAddress,378 event: 'Transfer',379 args: {380 from: '0x0000000000000000000000000000000000000000',381 to: minter,382 tokenId: '1',383 },384 });385 }));386387 [388 'setCollectionSponsorCross',389 'setCollectionSponsor', 390 ].map(testCase =>391 itEth(`[${testCase}] can remove collection sponsor`, async ({helper}) => {392 const owner = await helper.eth.createAccountWithBalance(donor);393 const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);394395 let result = await collectionHelpers.methods.createRFTCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)});396 const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);397 const sponsor = await helper.eth.createAccountWithBalance(donor);398 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);399 const collectionEvm = await helper.ethNativeContract.collection(collectionIdAddress, 'rft', owner, testCase === 'setCollectionSponsor');400401 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;402 result = await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send({from: owner});403 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;404405 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});406 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;407408 await collectionEvm.methods.removeCollectionSponsor().send({from: owner});409410 const sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});411 expect(sponsorStruct.eth).to.be.eq('0x0000000000000000000000000000000000000000');412 }));413414 [415 'setCollectionSponsorCross',416 'setCollectionSponsor', 417 ].map(testCase =>418 itEth(`[${testCase}] Can sponsor from evm address via access list`, async ({helper}) => {419 const owner = await helper.eth.createAccountWithBalance(donor);420 const sponsorEth = await helper.eth.createAccountWithBalance(donor);421 const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);422423 const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Sponsor collection', '1', '1', '');424425 const collectionSub = helper.rft.getCollectionObject(collectionId);426 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner, testCase === 'setCollectionSponsor');427428 429 await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsorEth : sponsorCrossEth).send({from: owner});430 let sponsorship = (await collectionSub.getData())!.raw.sponsorship;431 expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));432 433 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');434435 436 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});437 sponsorship = (await collectionSub.getData())!.raw.sponsorship;438 expect(sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));439440 441 const user = helper.eth.createAccount();442 const userCross = helper.ethCrossAccount.fromAddress(user);443 const nextTokenId = await collectionEvm.methods.nextTokenId().call();444 expect(nextTokenId).to.be.equal('1');445446 447 const oldPermissions = (await collectionSub.getData())!.raw.permissions;448 expect(oldPermissions.mintMode).to.be.false;449 expect(oldPermissions.access).to.be.equal('Normal');450451 await collectionEvm.methods.setCollectionAccess(1 ).send({from: owner});452 await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner});453 await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});454455 const newPermissions = (await collectionSub.getData())!.raw.permissions;456 expect(newPermissions.mintMode).to.be.true;457 expect(newPermissions.access).to.be.equal('AllowList');458459 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));460 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));461 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));462463 464 {465 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});466 const events = helper.eth.normalizeEvents(result.events);467468 expect(events).to.deep.include({469 address: collectionAddress,470 event: 'Transfer',471 args: {472 from: '0x0000000000000000000000000000000000000000',473 to: user,474 tokenId: '1',475 },476 });477478 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));479 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));480 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));481482 expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');483 expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);484 expect(userBalanceAfter).to.be.eq(userBalanceBefore);485 expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;486 }487 }));488489 [490 'setCollectionSponsorCross',491 'setCollectionSponsor', 492 ].map(testCase =>493 itEth(`[${testCase}] Check that collection admin EVM transaction spend money from sponsor eth address`, async ({helper}) => {494 const owner = await helper.eth.createAccountWithBalance(donor);495 const sponsor = await helper.eth.createAccountWithBalance(donor);496 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);497498 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');499500 const collectionSub = helper.rft.getCollectionObject(collectionId);501 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner, testCase === 'setCollectionSponsor');502 503 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;504 await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send();505 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;506 let collectionData = (await collectionSub.getData())!;507 expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));508 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');509 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;510511 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});512 collectionData = (await collectionSub.getData())!;513 expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));514 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;515 const sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});516 const sponsorSubAddress = helper.address.normalizeSubstrateToChainFormat(helper.address.ethToSubstrate(sponsor));517 const actualSubAddress = helper.address.normalizeSubstrateToChainFormat(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorStruct.sub)));518 expect(actualSubAddress).to.be.equal(sponsorSubAddress);519520 const user = helper.eth.createAccount();521 const userCross = helper.ethCrossAccount.fromAddress(user);522 await collectionEvm.methods.addCollectionAdminCross(userCross).send();523524 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));525 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));526527 const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});528 const tokenId = mintingResult.events.Transfer.returnValues.tokenId;529530 const events = helper.eth.normalizeEvents(mintingResult.events);531 const address = helper.ethAddress.fromCollectionId(collectionId);532533 expect(events).to.deep.include({534 address,535 event: 'Transfer',536 args: {537 from: '0x0000000000000000000000000000000000000000',538 to: user,539 tokenId: '1',540 },541 });542 expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI');543544 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));545 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);546 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));547 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;548 }));549550 itEth('Check that collection admin EVM transaction spend money from sponsor sub address', async ({helper}) => {551 const owner = await helper.eth.createAccountWithBalance(donor);552 const sponsor = alice;553 const sponsorCross = helper.ethCrossAccount.fromKeyringPair(sponsor);554555 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');556557 const collectionSub = helper.rft.getCollectionObject(collectionId);558 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner, false);559 560 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;561 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();562 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;563 let collectionData = (await collectionSub.getData())!;564 expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(sponsor.address);565 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');566567 await collectionSub.confirmSponsorship(sponsor);568 collectionData = (await collectionSub.getData())!;569 expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(sponsor.address);570 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;571 const sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});572 expect(BigInt(sponsorStruct.sub)).to.be.equal(BigInt('0x' + Buffer.from(sponsor.addressRaw).toString('hex')));573574 const user = helper.eth.createAccount();575 const userCross = helper.ethCrossAccount.fromAddress(user);576 await collectionEvm.methods.addCollectionAdminCross(userCross).send();577578 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));579 const sponsorBalanceBefore = await helper.balance.getSubstrate(sponsor.address);580581 const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});582 const tokenId = mintingResult.events.Transfer.returnValues.tokenId;583584 const events = helper.eth.normalizeEvents(mintingResult.events);585586 expect(events).to.deep.include({587 address: collectionAddress,588 event: 'Transfer',589 args: {590 from: '0x0000000000000000000000000000000000000000',591 to: user,592 tokenId: '1',593 },594 });595 expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI');596597 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));598 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);599 const sponsorBalanceAfter = await helper.balance.getSubstrate(sponsor.address);600 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;601 });602603 itEth('Sponsoring collection from substrate address via access list', async ({helper}) => {604 const owner = await helper.eth.createAccountWithBalance(donor);605 const user = helper.eth.createAccount();606 const userCross = helper.ethCrossAccount.fromAddress(user);607 const sponsor = alice;608 const sponsorCross = helper.ethCrossAccount.fromKeyringPair(sponsor);609610 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');611 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner, false);612613 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send({from: owner});614615 const collectionSub = helper.rft.getCollectionObject(collectionId);616 await collectionSub.confirmSponsorship(sponsor);617618 const nextTokenId = await collectionEvm.methods.nextTokenId().call();619 expect(nextTokenId).to.be.equal('1');620621 await collectionEvm.methods.setCollectionAccess(1 ).send({from: owner});622623 await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner});624 await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});625626 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));627 const sponsorBalanceBefore = await helper.balance.getSubstrate(sponsor.address);628 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));629630 {631 const nextTokenId = await collectionEvm.methods.nextTokenId().call();632 expect(nextTokenId).to.be.equal('1');633 const mintingResult = await collectionEvm.methods.mintWithTokenURI(634 user,635 'Test URI',636 ).send({from: user});637638 const events = helper.eth.normalizeEvents(mintingResult.events);639640641 expect(events).to.deep.include({642 address: collectionAddress,643 event: 'Transfer',644 args: {645 from: '0x0000000000000000000000000000000000000000',646 to: user,647 tokenId: nextTokenId,648 },649 });650651 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));652 const sponsorBalanceAfter = await helper.balance.getSubstrate(sponsor.address);653 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));654655 expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');656 expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);657 expect(userBalanceAfter).to.be.eq(userBalanceBefore);658 expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;659 }660 });661662 itEth('Can reassign collection sponsor', async ({helper}) => {663 const owner = await helper.eth.createAccountWithBalance(donor);664 const sponsorEth = await helper.eth.createAccountWithBalance(donor);665 const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);666 const [sponsorSub] = await helper.arrange.createAccounts([100n], donor);667 const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub);668669 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');670 const collectionSub = helper.rft.getCollectionObject(collectionId);671 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);672673 674 await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossEth).send({from: owner});675 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});676677 678 await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner});679 const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship;680 expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address});681 });682683 [684 'transfer',685 'transferCross',686 'transferFrom',687 'transferFromCross',688 ].map(testCase =>689 itEth(`[${testCase}] Check that transfer via EVM spend money from sponsor address`, async ({helper}) => {690 const owner = await helper.eth.createAccountWithBalance(donor);691692 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');693 const sponsor = await helper.eth.createAccountWithBalance(donor);694 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);695 const receiver = await helper.eth.createAccountWithBalance(donor);696 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);697698 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();699 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});700701 const user = await helper.eth.createAccountWithBalance(donor);702 const userCross = helper.ethCrossAccount.fromAddress(user);703 await collectionEvm.methods.addCollectionAdminCross(userCross).send();704705 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});706 const tokenId = result.events.Transfer.returnValues.tokenId;707708 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));709 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));710 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));711712 switch (testCase) {713 case 'transfer':714 await collectionEvm.methods.transfer(receiver, tokenId).send({from: user});715 break;716 case 'transferCross':717 await collectionEvm.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});718 break;719 case 'transferFrom':720 await collectionEvm.methods.transferFrom(user, receiver, tokenId).send({from: user});721 break;722 case 'transferFromCross':723 await collectionEvm.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});724 break;725 }726727 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));728 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);729 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));730 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;731 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));732 expect(userBalanceAfter).to.be.eq(userBalanceBefore);733 }));734});735736describe('evm RFT token sponsoring', () => {737 let donor: IKeyringPair;738739 before(async function() {740 await usingPlaygrounds(async (helper, privateKey) => {741 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);742 donor = await privateKey({url: import.meta.url});743 });744 });745746 [747 'transfer',748 'transferCross',749 'transferFrom',750 'transferFromCross',751 ].map(testCase =>752 itEth(`[${testCase}] Check that token piece transfer via EVM spend money from sponsor address`, async ({helper}) => {753 const owner = await helper.eth.createAccountWithBalance(donor);754755 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');756 const sponsor = await helper.eth.createAccountWithBalance(donor);757 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);758 const receiver = await helper.eth.createAccountWithBalance(donor);759 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);760761 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();762 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});763764 const user = await helper.eth.createAccountWithBalance(donor);765 const userCross = helper.ethCrossAccount.fromAddress(user);766 await collectionEvm.methods.addCollectionAdminCross(userCross).send();767768 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});769 const tokenId = result.events.Transfer.returnValues.tokenId;770771 const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, user);772 await tokenContract.methods.repartition(2).send();773774 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));775 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));776 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));777778 switch (testCase) {779 case 'transfer':780 await tokenContract.methods.transfer(receiver, 1).send();781 break;782 case 'transferCross':783 await tokenContract.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), 1).send();784 break;785 case 'transferFrom':786 await tokenContract.methods.transferFrom(user, receiver, 1).send();787 break;788 case 'transferFromCross':789 await tokenContract.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), 1).send();790 break;791 }792793 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));794 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);795 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));796 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;797 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));798 expect(userBalanceAfter).to.be.eq(userBalanceBefore);799 }));800801 [802 'approve',803 'approveCross',804 ].map(testCase =>805 itEth(`[${testCase}] Check that approve via EVM spend money from sponsor address`, async ({helper}) => {806 const owner = await helper.eth.createAccountWithBalance(donor);807808 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');809 const sponsor = await helper.eth.createAccountWithBalance(donor);810 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);811 const receiver = await helper.eth.createAccountWithBalance(donor);812 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);813814 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();815 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});816817 const user = await helper.eth.createAccountWithBalance(donor);818 const userCross = helper.ethCrossAccount.fromAddress(user);819 await collectionEvm.methods.addCollectionAdminCross(userCross).send();820821 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});822 const tokenId = result.events.Transfer.returnValues.tokenId;823824 const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, user);825 await tokenContract.methods.repartition(2).send();826827 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));828 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));829 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));830831 switch (testCase) {832 case 'approve':833 await tokenContract.methods.approve(receiver, 1).send();834 break;835 case 'approveCross':836 await tokenContract.methods.approveCross(helper.ethCrossAccount.fromAddress(receiver), 1).send();837 break;838 }839840 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));841 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);842 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));843 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;844 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));845 expect(userBalanceAfter).to.be.eq(userBalanceBefore);846 }));847});848