git.delta.rocks / unique-network / refs/commits / 0b44e9f57a8a

difftreelog

feat add collection sponsoring for rft

Grigoriy Simonov2022-12-23parent: #aea0773.patch.diff
in: master

4 files changed

modifiedruntime/common/config/pallets/app_promotion.rsdiffbeforeafterboth
22use frame_support::{parameter_types, PalletId};22use frame_support::{parameter_types, PalletId};
23use sp_arithmetic::Perbill;23use sp_arithmetic::Perbill;
24use up_common::{24use up_common::{
25 constants::{UNIQUE, RELAY_DAYS, DAYS},25 constants::{UNIQUE, RELAY_DAYS},
26 types::Balance,26 types::Balance,
27};27};
2828
modifiedruntime/common/ethereum/sponsoring.rsdiffbeforeafterboth
32 Config as FungibleConfig,32 Config as FungibleConfig,
33 erc::{UniqueFungibleCall, ERC20Call},33 erc::{UniqueFungibleCall, ERC20Call},
34};34};
35use pallet_refungible::Config as RefungibleConfig;35use pallet_refungible::{
36 Config as RefungibleConfig,
37 erc::UniqueRefungibleCall,
38 erc_token::{RefungibleTokenHandle, UniqueRefungibleTokenCall},
39 RefungibleHandle,
40};
36use pallet_unique::Config as UniqueConfig;41use pallet_unique::Config as UniqueConfig;
37use sp_std::prelude::*;42use sp_std::prelude::*;
38use up_data_structs::{CollectionMode, CreateItemData, CreateNftData, TokenId};43use up_data_structs::{
44 CollectionMode, CreateItemData, CreateNftData, mapping::TokenAddressMapping, TokenId,
45};
39use up_sponsorship::SponsorshipHandler;46use up_sponsorship::SponsorshipHandler;
40
41use crate::{Runtime, runtime_common::sponsoring::*};47use crate::{Runtime, runtime_common::sponsoring::*};
48
49mod refungible;
4250
43pub type EvmSponsorshipHandler = (51pub type EvmSponsorshipHandler = (
44 UniqueEthSponsorshipHandler<Runtime>,52 UniqueEthSponsorshipHandler<Runtime>,
53 who: &T::CrossAccountId,61 who: &T::CrossAccountId,
54 call_context: &CallContext,62 call_context: &CallContext,
55 ) -> Option<T::CrossAccountId> {63 ) -> Option<T::CrossAccountId> {
56 let collection_id = map_eth_to_id(&call_context.contract_address)?;64 if let Some(collection_id) = map_eth_to_id(&call_context.contract_address) {
57 let collection = <CollectionHandle<T>>::new(collection_id)?;65 let collection = <CollectionHandle<T>>::new(collection_id)?;
58 let sponsor = collection.sponsorship.sponsor()?.clone();66 let sponsor = collection.sponsorship.sponsor()?.clone();
59 let (method_id, mut reader) = AbiReader::new_call(&call_context.input).ok()?;67 let (method_id, mut reader) = AbiReader::new_call(&call_context.input).ok()?;
106 _ => None,116 _ => None,
107 }117 }
108 }118 }
119 CollectionMode::ReFungible => {
120 let call = <UniqueRefungibleCall<T>>::parse(method_id, &mut reader).ok()??;
121 refungible::call_sponsor(call, collection, who).map(|()| sponsor)
122 }
109 CollectionMode::Fungible(_) => {123 CollectionMode::Fungible(_) => {
110 let call = <UniqueFungibleCall<T>>::parse(method_id, &mut reader).ok()??;124 let call = <UniqueFungibleCall<T>>::parse(method_id, &mut reader).ok()??;
111 #[allow(clippy::single_match)]
112 match call {125 match call {
113 UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {126 UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {
114 withdraw_transfer::<T>(&collection, who, &TokenId::default())127 withdraw_transfer::<T>(&collection, who, &TokenId::default())
126 _ => None,139 _ => None,
127 }140 }
128 }141 }
129 _ => None,
130 }?))142 }?))
143 } else {
144 let (collection_id, token_id) =
145 T::EvmTokenAddressMapping::address_to_token(&call_context.contract_address)?;
146 let collection = <CollectionHandle<T>>::new(collection_id)?;
147 let sponsor = collection.sponsorship.sponsor()?.clone();
148 let rft_collection = RefungibleHandle::cast(collection);
149 let token = RefungibleTokenHandle(rft_collection, token_id);
150 let (method_id, mut reader) = AbiReader::new_call(&call_context.input).ok()?;
151 let call = <UniqueRefungibleTokenCall<T>>::parse(method_id, &mut reader).ok()??;
152 Some(T::CrossAccountId::from_sub(
153 refungible::token_call_sponsor(call, token, who).map(|()| sponsor)?,
154 ))
155 }
131 }156 }
132}157}
158
159mod common {
160 use super::*;
161
162 use pallet_common::erc::{CollectionCall};
163
164 pub fn collection_call_sponsor<T>(
165 call: CollectionCall<T>,
166 _collection: CollectionHandle<T>,
167 _who: &T::CrossAccountId,
168 ) -> Option<()>
169 where
170 T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,
171 {
172 use CollectionCall::*;
173
174 match call {
175 // Readonly
176 ERC165Call(_, _)
177 | CollectionProperty { .. }
178 | CollectionProperties { .. }
179 | HasCollectionPendingSponsor
180 | CollectionSponsor
181 | ContractAddress
182 | AllowlistedCross { .. }
183 | IsOwnerOrAdminEth { .. }
184 | IsOwnerOrAdminCross { .. }
185 | CollectionOwner
186 | CollectionAdmins
187 | UniqueCollectionType => None,
188
189 // Not sponsored
190 AddToCollectionAllowList { .. }
191 | AddToCollectionAllowListCross { .. }
192 | RemoveFromCollectionAllowList { .. }
193 | RemoveFromCollectionAllowListCross { .. }
194 | AddCollectionAdminCross { .. }
195 | RemoveCollectionAdminCross { .. }
196 | AddCollectionAdmin { .. }
197 | RemoveCollectionAdmin { .. }
198 | SetNestingBool { .. }
199 | SetNesting { .. }
200 | SetCollectionAccess { .. }
201 | SetCollectionMintMode { .. }
202 | SetOwner { .. }
203 | ChangeCollectionOwnerCross { .. }
204 | SetCollectionProperty { .. }
205 | SetCollectionProperties { .. }
206 | DeleteCollectionProperty { .. }
207 | DeleteCollectionProperties { .. }
208 | SetCollectionSponsor { .. }
209 | SetCollectionSponsorCross { .. }
210 | ConfirmCollectionSponsorship
211 | RemoveCollectionSponsor
212 | SetIntLimit { .. } => None,
213 }
214 }
215}
133216
addedruntime/common/ethereum/sponsoring/refungible.rsdiffbeforeafterboth

no changes

modifiedtests/src/eth/collectionSponsoring.test.tsdiffbeforeafterboth
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';
18import {usingPlaygrounds} from '../util/index';18import {Pallets, requirePalletsOrSkip, usingPlaygrounds} from '../util/index';
19import {itEth, expect} from './util';19import {itEth, expect} from './util';
2020
21describe('evm collection sponsoring', () => {21describe('evm nft collection sponsoring', () => {
22 let donor: IKeyringPair;22 let donor: IKeyringPair;
23 let alice: IKeyringPair;23 let alice: IKeyringPair;
24 let nominal: bigint;24 let nominal: bigint;
318 });318 });
319});319});
320
321describe('evm RFT collection sponsoring', () => {
322 let donor: IKeyringPair;
323 let alice: IKeyringPair;
324 let nominal: bigint;
325
326 before(async function() {
327 await usingPlaygrounds(async (helper, privateKey) => {
328 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
329 donor = await privateKey({filename: __filename});
330 [alice] = await helper.arrange.createAccounts([100n], donor);
331 nominal = helper.balance.getOneTokenNominal();
332 });
333 });
334
335 itEth('sponsors mint transactions', async ({helper}) => {
336 const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'spnr', permissions: {mintMode: true}});
337 await collection.setSponsor(alice, alice.address);
338 await collection.confirmSponsorship(alice);
339
340 const minter = helper.eth.createAccount();
341 expect(await helper.balance.getEthereum(minter)).to.equal(0n);
342
343 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
344 const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', minter);
345
346 await collection.addToAllowList(alice, {Ethereum: minter});
347
348 const result = await contract.methods.mint(minter).send();
349
350 const events = helper.eth.normalizeEvents(result.events);
351 expect(events).to.deep.include({
352 address: collectionAddress,
353 event: 'Transfer',
354 args: {
355 from: '0x0000000000000000000000000000000000000000',
356 to: minter,
357 tokenId: '1',
358 },
359 });
360 });
361
362 // TODO: Temprorary off. Need refactor
363 // itWeb3('Set substrate sponsor', async ({api, web3, privateKeyWrapper}) => {
364 // const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
365 // const collectionHelpers = evmCollectionHelpers(web3, owner);
366 // let result = await collectionHelpers.methods.createRFTCollection('Sponsor collection', '1', '1').send();
367 // const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
368 // const sponsor = privateKeyWrapper('//Alice');
369 // const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
370
371 // expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
372 // result = await collectionEvm.methods.setCollectionSponsorSubstrate(sponsor.addressRaw).send({from: owner});
373 // expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;
374
375 // const confirmTx = await api.tx.unique.confirmSponsorship(collectionId);
376 // await submitTransactionAsync(sponsor, confirmTx);
377 // expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
378
379 // const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner});
380 // expect(bigIntToSub(api, BigInt(sponsorTuple[1]))).to.be.eq(sponsor.address);
381 // });
382
383 // Soft-deprecated
384 itEth('[eth] Remove sponsor', async ({helper}) => {
385 const owner = await helper.eth.createAccountWithBalance(donor);
386 const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
387
388 let result = await collectionHelpers.methods.createRFTCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)});
389 const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
390 const sponsor = await helper.eth.createAccountWithBalance(donor);
391 const collectionEvm = helper.ethNativeContract.collection(collectionIdAddress, 'rft', owner, true);
392
393 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
394 result = await collectionEvm.methods.setCollectionSponsor(sponsor).send({from: owner});
395 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;
396
397 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
398 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
399
400 await collectionEvm.methods.removeCollectionSponsor().send({from: owner});
401
402 const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner});
403 expect(sponsorTuple.field_0).to.be.eq('0x0000000000000000000000000000000000000000');
404 });
405
406 itEth('[cross] Remove sponsor', async ({helper}) => {
407 const owner = await helper.eth.createAccountWithBalance(donor);
408 const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
409
410 let result = await collectionHelpers.methods.createRFTCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)});
411 const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
412 const sponsor = await helper.eth.createAccountWithBalance(donor);
413 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
414 const collectionEvm = helper.ethNativeContract.collection(collectionIdAddress, 'rft', owner);
415
416 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
417 result = await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send({from: owner});
418 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;
419
420 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
421 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
422
423 await collectionEvm.methods.removeCollectionSponsor().send({from: owner});
424
425 const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner});
426 expect(sponsorTuple.field_0).to.be.eq('0x0000000000000000000000000000000000000000');
427 });
428
429 // Soft-deprecated
430 itEth('[eth] Sponsoring collection from evm address via access list', async ({helper}) => {
431 const owner = await helper.eth.createAccountWithBalance(donor);
432
433 const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Sponsor collection', '1', '1', '');
434
435 const collection = helper.rft.getCollectionObject(collectionId);
436 const sponsor = await helper.eth.createAccountWithBalance(donor);
437 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner, true);
438
439 await collectionEvm.methods.setCollectionSponsor(sponsor).send({from: owner});
440 let collectionData = (await collection.getData())!;
441 expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
442 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');
443
444 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
445 collectionData = (await collection.getData())!;
446 expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
447
448 const user = helper.eth.createAccount();
449 const nextTokenId = await collectionEvm.methods.nextTokenId().call();
450 expect(nextTokenId).to.be.equal('1');
451
452 const oldPermissions = (await collection.getData())!.raw.permissions; // (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman();
453 expect(oldPermissions.mintMode).to.be.false;
454 expect(oldPermissions.access).to.be.equal('Normal');
455
456 await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});
457 await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner});
458 await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});
459
460 const newPermissions = (await collection.getData())!.raw.permissions; // (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman();
461 expect(newPermissions.mintMode).to.be.true;
462 expect(newPermissions.access).to.be.equal('AllowList');
463
464 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
465 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
466
467 {
468 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
469 const events = helper.eth.normalizeEvents(result.events);
470
471 expect(events).to.deep.include({
472 address: collectionAddress,
473 event: 'Transfer',
474 args: {
475 from: '0x0000000000000000000000000000000000000000',
476 to: user,
477 tokenId: '1',
478 },
479 });
480
481 const ownerBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(owner));
482 const sponsorBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(sponsor));
483
484 expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
485 expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
486 expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
487 }
488 });
489
490 itEth('[cross] Sponsoring collection from evm address via access list', async ({helper}) => {
491 const owner = await helper.eth.createAccountWithBalance(donor);
492
493 const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Sponsor collection', '1', '1', '');
494
495 const collection = helper.rft.getCollectionObject(collectionId);
496 const sponsor = await helper.eth.createAccountWithBalance(donor);
497 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
498 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
499
500 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send({from: owner});
501 let collectionData = (await collection.getData())!;
502 expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
503 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');
504
505 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
506 collectionData = (await collection.getData())!;
507 expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
508
509 const user = helper.eth.createAccount();
510 const userCross = helper.ethCrossAccount.fromAddress(user);
511 const nextTokenId = await collectionEvm.methods.nextTokenId().call();
512 expect(nextTokenId).to.be.equal('1');
513
514 const oldPermissions = (await collection.getData())!.raw.permissions; // (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman();
515 expect(oldPermissions.mintMode).to.be.false;
516 expect(oldPermissions.access).to.be.equal('Normal');
517
518 await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});
519 await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner});
520 await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});
521
522 const newPermissions = (await collection.getData())!.raw.permissions; // (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman();
523 expect(newPermissions.mintMode).to.be.true;
524 expect(newPermissions.access).to.be.equal('AllowList');
525
526 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
527 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
528
529 {
530 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
531 const events = helper.eth.normalizeEvents(result.events);
532
533 expect(events).to.deep.include({
534 address: collectionAddress,
535 event: 'Transfer',
536 args: {
537 from: '0x0000000000000000000000000000000000000000',
538 to: user,
539 tokenId: '1',
540 },
541 });
542
543 const ownerBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(owner));
544 const sponsorBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(sponsor));
545
546 expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
547 expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
548 expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
549 }
550 });
551
552 // TODO: Temprorary off. Need refactor
553 // itWeb3('Sponsoring collection from substrate address via access list', async ({api, web3, privateKeyWrapper}) => {
554 // const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
555 // const collectionHelpers = evmCollectionHelpers(web3, owner);
556 // const result = await collectionHelpers.methods.createERC721MetadataCompatibleRFTCollection('Sponsor collection', '1', '1', '').send();
557 // const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
558 // const sponsor = privateKeyWrapper('//Alice');
559 // const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
560
561 // await collectionEvm.methods.setCollectionSponsorSubstrate(sponsor.addressRaw).send({from: owner});
562
563 // const confirmTx = await api.tx.unique.confirmSponsorship(collectionId);
564 // await submitTransactionAsync(sponsor, confirmTx);
565
566 // const user = createEthAccount(web3);
567 // const nextTokenId = await collectionEvm.methods.nextTokenId().call();
568 // expect(nextTokenId).to.be.equal('1');
569
570 // await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});
571 // await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner});
572 // await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});
573
574 // const ownerBalanceBefore = await ethBalanceViaSub(api, owner);
575 // const sponsorBalanceBefore = (await getBalance(api, [sponsor.address]))[0];
576
577 // {
578 // const nextTokenId = await collectionEvm.methods.nextTokenId().call();
579 // expect(nextTokenId).to.be.equal('1');
580 // const result = await collectionEvm.methods.mintWithTokenURI(
581 // user,
582 // nextTokenId,
583 // 'Test URI',
584 // ).send({from: user});
585 // const events = normalizeEvents(result.events);
586
587 // expect(events).to.be.deep.equal([
588 // {
589 // address: collectionIdAddress,
590 // event: 'Transfer',
591 // args: {
592 // from: '0x0000000000000000000000000000000000000000',
593 // to: user,
594 // tokenId: nextTokenId,
595 // },
596 // },
597 // ]);
598
599 // const ownerBalanceAfter = await ethBalanceViaSub(api, owner);
600 // const sponsorBalanceAfter = (await getBalance(api, [sponsor.address]))[0];
601
602 // expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
603 // expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
604 // expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
605 // }
606 // });
607
608 // Soft-deprecated
609 itEth('[eth] Check that transaction via EVM spend money from sponsor address', async ({helper}) => {
610 const owner = await helper.eth.createAccountWithBalance(donor);
611
612 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
613 const collection = helper.rft.getCollectionObject(collectionId);
614 const sponsor = await helper.eth.createAccountWithBalance(donor);
615 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner, true);
616
617 await collectionEvm.methods.setCollectionSponsor(sponsor).send();
618 let collectionData = (await collection.getData())!;
619 expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
620 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');
621
622 const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true);
623 await sponsorCollection.methods.confirmCollectionSponsorship().send();
624 collectionData = (await collection.getData())!;
625 expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
626
627 const user = helper.eth.createAccount();
628 await collectionEvm.methods.addCollectionAdmin(user).send();
629
630 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
631 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
632
633 const userCollectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', user, true);
634
635 const result = await userCollectionEvm.methods.mintWithTokenURI(user, 'Test URI').send();
636 const tokenId = result.events.Transfer.returnValues.tokenId;
637
638 const events = helper.eth.normalizeEvents(result.events);
639 const address = helper.ethAddress.fromCollectionId(collectionId);
640
641 expect(events).to.deep.include({
642 address,
643 event: 'Transfer',
644 args: {
645 from: '0x0000000000000000000000000000000000000000',
646 to: user,
647 tokenId: '1',
648 },
649 });
650 expect(await userCollectionEvm.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
651
652 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
653 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
654 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
655 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
656 });
657
658 itEth('[cross] Check that transaction via EVM spend money from sponsor address', async ({helper}) => {
659 const owner = await helper.eth.createAccountWithBalance(donor);
660
661 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
662 const collection = helper.rft.getCollectionObject(collectionId);
663 const sponsor = await helper.eth.createAccountWithBalance(donor);
664 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
665 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
666
667 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
668 let collectionData = (await collection.getData())!;
669 expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
670 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');
671
672 const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
673 await sponsorCollection.methods.confirmCollectionSponsorship().send();
674 collectionData = (await collection.getData())!;
675 expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
676
677 const user = helper.eth.createAccount();
678 const userCross = helper.ethCrossAccount.fromAddress(user);
679 await collectionEvm.methods.addCollectionAdminCross(userCross).send();
680
681 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
682 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
683
684 const userCollectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', user);
685
686 const result = await userCollectionEvm.methods.mintWithTokenURI(user, 'Test URI').send();
687 const tokenId = result.events.Transfer.returnValues.tokenId;
688
689 const events = helper.eth.normalizeEvents(result.events);
690 const address = helper.ethAddress.fromCollectionId(collectionId);
691
692 expect(events).to.deep.include({
693 address,
694 event: 'Transfer',
695 args: {
696 from: '0x0000000000000000000000000000000000000000',
697 to: user,
698 tokenId: '1',
699 },
700 });
701 expect(await userCollectionEvm.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
702
703 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
704 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
705 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
706 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
707 });
708});
709
710describe('evm RFT token sponsoring', () => {
711 let donor: IKeyringPair;
712
713 before(async function() {
714 await usingPlaygrounds(async (helper, privateKey) => {
715 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
716 donor = await privateKey({filename: __filename});
717 });
718 });
719
720 itEth('[cross] Check that transfer via EVM spend money from sponsor address', async ({helper}) => {
721 const owner = await helper.eth.createAccountWithBalance(donor);
722
723 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
724 const sponsor = await helper.eth.createAccountWithBalance(donor);
725 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
726 const receiver = await helper.eth.createAccountWithBalance(donor);
727 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
728
729 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
730
731 const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
732 await sponsorCollection.methods.confirmCollectionSponsorship().send();
733
734 const user = await helper.eth.createAccountWithBalance(donor);
735 const userCross = helper.ethCrossAccount.fromAddress(user);
736 await collectionEvm.methods.addCollectionAdminCross(userCross).send();
737
738 const userCollectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', user);
739
740 const result = await userCollectionEvm.methods.mintWithTokenURI(user, 'Test URI').send();
741 const tokenId = result.events.Transfer.returnValues.tokenId;
742
743 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, user);
744 await tokenContract.methods.repartition(2).send();
745
746 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
747 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
748 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
749
750 await tokenContract.methods.transfer(receiver, 1).send();
751
752 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
753 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
754 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
755 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
756 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
757 expect(userBalanceAfter).to.be.eq(userBalanceBefore);
758 });
759
760 itEth('[cross] Check that approve via EVM spend money from sponsor address', async ({helper}) => {
761 const owner = await helper.eth.createAccountWithBalance(donor);
762
763 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
764 const sponsor = await helper.eth.createAccountWithBalance(donor);
765 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
766 const receiver = await helper.eth.createAccountWithBalance(donor);
767 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
768
769 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
770
771 const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
772 await sponsorCollection.methods.confirmCollectionSponsorship().send();
773
774 const user = await helper.eth.createAccountWithBalance(donor);
775 const userCross = helper.ethCrossAccount.fromAddress(user);
776 await collectionEvm.methods.addCollectionAdminCross(userCross).send();
777
778 const userCollectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', user);
779
780 const result = await userCollectionEvm.methods.mintWithTokenURI(user, 'Test URI').send();
781 const tokenId = result.events.Transfer.returnValues.tokenId;
782
783 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));
789
790 await tokenContract.methods.approve(receiver, 1).send();
791
792 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
793 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
794 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
795 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
796 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
797 expect(userBalanceAfter).to.be.eq(userBalanceBefore);
798 });
799
800
801 itEth('[cross] Check that transferFrom via EVM spend money from sponsor address', async ({helper}) => {
802 const owner = await helper.eth.createAccountWithBalance(donor);
803
804 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
805 const sponsor = await helper.eth.createAccountWithBalance(donor);
806 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
807 const receiver = await helper.eth.createAccountWithBalance(donor);
808 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
809
810 await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
811
812 const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
813 await sponsorCollection.methods.confirmCollectionSponsorship().send();
814
815 const user = await helper.eth.createAccountWithBalance(donor);
816 const userCross = helper.ethCrossAccount.fromAddress(user);
817 await collectionEvm.methods.addCollectionAdminCross(userCross).send();
818
819 const userCollectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', user);
820
821 const result = await userCollectionEvm.methods.mintWithTokenURI(user, 'Test URI').send();
822 const tokenId = result.events.Transfer.returnValues.tokenId;
823
824 const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, user);
825 await tokenContract.methods.repartition(2).send();
826 await tokenContract.methods.approve(receiver, 1).send();
827
828 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
829 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
830 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
831 const receiverBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(receiver));
832
833 const receiverTokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, receiver);
834 await receiverTokenContract.methods.transferFrom(user, receiver, 1).send();
835
836 const receiverBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(receiver));
837 expect(receiverBalanceAfter).to.be.eq(receiverBalanceBefore);
838 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
839 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
840 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
841 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
842 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
843 expect(userBalanceAfter).to.be.eq(userBalanceBefore);
844 });
845});
320846