difftreelog
chore fix code review requests
in: master
3 files changed
runtime/common/ethereum/sponsoring/refungible.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! Implements EVM sponsoring logic via TransactionValidityHack1819use core::convert::TryInto;20use pallet_common::CollectionHandle;21use pallet_evm::account::CrossAccountId;22use pallet_fungible::Config as FungibleConfig;23use pallet_refungible::Config as RefungibleConfig;24use pallet_nonfungible::Config as NonfungibleConfig;25use pallet_unique::Config as UniqueConfig;26use up_data_structs::{CreateItemData, CreateNftData, TokenId};2728use super::common;29use crate::runtime_common::sponsoring::*;3031use pallet_refungible::{32 erc::{33 ERC721BurnableCall, ERC721Call, ERC721EnumerableCall, ERC721MetadataCall,34 ERC721UniqueExtensionsCall, ERC721UniqueMintableCall, TokenPropertiesCall,35 UniqueRefungibleCall,36 },37 erc_token::{38 ERC1633Call, ERC20Call, ERC20UniqueExtensionsCall, RefungibleTokenHandle,39 UniqueRefungibleTokenCall,40 },41};4243pub fn call_sponsor<T>(44 call: UniqueRefungibleCall<T>,45 collection: CollectionHandle<T>,46 who: &T::CrossAccountId,47) -> Option<()>48where49 T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,50{51 use UniqueRefungibleCall::*;5253 match call {54 // Readonly55 ERC165Call(_, _) => None,5657 ERC721Enumerable(call) => erc721::enumerable_call_sponsor(call, collection, who),58 ERC721Burnable(call) => erc721::burnable_call_sponsor(call, collection, who),59 ERC721Metadata(call) => erc721::metadata_call_sponsor(call, collection, who),60 Collection(call) => common::collection_call_sponsor(call, collection, who),61 ERC721(call) => erc721::call_sponsor(call, collection, who),62 ERC721UniqueExtensions(call) => {63 erc721::unique_extensions_call_sponsor(call, collection, who)64 }65 ERC721UniqueMintable(call) => erc721::unique_mintable_call_sponsor(call, collection, who),66 TokenProperties(call) => token_properties_call_sponsor(call, collection, who),67 }68}6970pub fn token_properties_call_sponsor<T>(71 call: TokenPropertiesCall<T>,72 collection: CollectionHandle<T>,73 who: &T::CrossAccountId,74) -> Option<()>75where76 T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,77{78 use TokenPropertiesCall::*;7980 match call {81 // Readonly82 ERC165Call(_, _) | Property { .. } | TokenPropertyPermissions => None,8384 // Not sponsored85 SetTokenPropertyPermission { .. }86 | SetTokenPropertyPermissions { .. }87 | SetProperties { .. }88 | DeleteProperty { .. }89 | DeleteProperties { .. } => None,9091 SetProperty {92 token_id,93 key,94 value,95 ..96 } => {97 let token_id = TokenId::try_from(token_id).ok()?;98 withdraw_set_token_property::<T>(&collection, &who, &token_id, key.len() + value.len())99 }100 }101}102103pub fn token_call_sponsor<T>(104 call: UniqueRefungibleTokenCall<T>,105 token: RefungibleTokenHandle<T>,106 who: &T::CrossAccountId,107) -> Option<()>108where109 T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,110{111 use UniqueRefungibleTokenCall::*;112113 match call {114 // Readonly115 ERC165Call(_, _) => None,116117 ERC20(call) => erc20::call_sponsor(call, token, who),118 ERC20UniqueExtensions(call) => erc20::unique_extensions_call_sponsor(call, token, who),119 ERC1633(call) => erc1633::call_sponsor(call, token, who),120 }121}122123mod erc721 {124 use super::*;125126 pub fn call_sponsor<T>(127 call: ERC721Call<T>,128 collection: CollectionHandle<T>,129 _who: &T::CrossAccountId,130 ) -> Option<()>131 where132 T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,133 {134 use ERC721Call::*;135136 match call {137 // Readonly138 ERC165Call(_, _)139 | BalanceOf { .. }140 | OwnerOf { .. }141 | GetApproved { .. }142 | IsApprovedForAll { .. }143 | CollectionHelperAddress => None,144145 // Not sponsored146 SafeTransferFromWithData { .. }147 | SafeTransferFrom { .. }148 | SetApprovalForAll { .. } => None,149150 TransferFrom { token_id, from, .. } => {151 let token_id = TokenId::try_from(token_id).ok()?;152 let from = T::CrossAccountId::from_eth(from);153 withdraw_transfer::<T>(&collection, &from, &token_id)154 }155156 // Not supported157 Approve { .. } => None,158 }159 }160161 pub fn enumerable_call_sponsor<T>(162 call: ERC721EnumerableCall<T>,163 _collection: CollectionHandle<T>,164 _who: &T::CrossAccountId,165 ) -> Option<()>166 where167 T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,168 {169 use ERC721EnumerableCall::*;170171 match call {172 // Readonly173 ERC165Call(_, _) | TokenByIndex { .. } | TokenOfOwnerByIndex { .. } | TotalSupply => {174 None175 }176 }177 }178179 pub fn burnable_call_sponsor<T>(180 call: ERC721BurnableCall<T>,181 _collection: CollectionHandle<T>,182 _who: &T::CrossAccountId,183 ) -> Option<()>184 where185 T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,186 {187 use ERC721BurnableCall::*;188189 match call {190 // Readonly191 ERC165Call(_, _) => None,192193 // Not sponsored194 Burn { .. } => None,195 }196 }197198 pub fn metadata_call_sponsor<T>(199 call: ERC721MetadataCall<T>,200 _collection: CollectionHandle<T>,201 _who: &T::CrossAccountId,202 ) -> Option<()>203 where204 T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,205 {206 use ERC721MetadataCall::*;207208 match call {209 // Readonly210 ERC165Call(_, _) | NameProxy | SymbolProxy | TokenUri { .. } => None,211 }212 }213214 pub fn unique_extensions_call_sponsor<T>(215 call: ERC721UniqueExtensionsCall<T>,216 collection: CollectionHandle<T>,217 who: &T::CrossAccountId,218 ) -> Option<()>219 where220 T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,221 {222 use ERC721UniqueExtensionsCall::*;223224 match call {225 // Readonly226 ERC165Call(_, _)227 | Name228 | Symbol229 | Description230 | CrossOwnerOf { .. }231 | Properties { .. }232 | NextTokenId233 | TokenContractAddress { .. } => None,234235 // Not sponsored236 TransferCross { .. }237 | TransferFromCross { .. }238 | BurnFrom { .. }239 | BurnFromCross { .. }240 | MintCross { .. }241 | MintBulk { .. }242 | MintBulkWithTokenUri { .. } => None,243244 Transfer { token_id, .. } => {245 let token_id = TokenId::try_from(token_id).ok()?;246 withdraw_transfer::<T>(&collection, &who, &token_id)247 }248 }249 }250251 pub fn unique_mintable_call_sponsor<T>(252 call: ERC721UniqueMintableCall<T>,253 collection: CollectionHandle<T>,254 who: &T::CrossAccountId,255 ) -> Option<()>256 where257 T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,258 {259 use ERC721UniqueMintableCall::*;260261 match call {262 // Readonly263 ERC165Call(_, _) | MintingFinished => None,264265 // Not sponsored266 FinishMinting => None,267268 Mint { .. }269 | MintCheckId { .. }270 | MintWithTokenUri { .. }271 | MintWithTokenUriCheckId { .. } => withdraw_create_item::<T>(272 &collection,273 &who,274 &CreateItemData::NFT(CreateNftData::default()),275 ),276 }277 }278}279280mod erc20 {281 use super::*;282283 pub fn call_sponsor<T>(284 call: ERC20Call<T>,285 token: RefungibleTokenHandle<T>,286 who: &T::CrossAccountId,287 ) -> Option<()>288 where289 T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,290 {291 use ERC20Call::*;292293 match call {294 // Readonly295 ERC165Call(_, _)296 | Name297 | Symbol298 | TotalSupply299 | Decimals300 | BalanceOf { .. }301 | Allowance { .. } => None,302303 Transfer { .. } => {304 let RefungibleTokenHandle(handle, token_id) = token;305 let token_id = token_id.try_into().ok()?;306 withdraw_transfer::<T>(&handle, &who, &token_id)307 }308 TransferFrom { from, .. } => {309 let RefungibleTokenHandle(handle, token_id) = token;310 let token_id = token_id.try_into().ok()?;311 let from = T::CrossAccountId::from_eth(from);312 withdraw_transfer::<T>(&handle, &from, &token_id)313 }314 Approve { .. } => {315 let RefungibleTokenHandle(handle, token_id) = token;316 let token_id = token_id.try_into().ok()?;317 withdraw_approve::<T>(&handle, who.as_sub(), &token_id)318 }319 }320 }321322 pub fn unique_extensions_call_sponsor<T>(323 call: ERC20UniqueExtensionsCall<T>,324 _token: RefungibleTokenHandle<T>,325 _who: &T::CrossAccountId,326 ) -> Option<()>327 where328 T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,329 {330 use ERC20UniqueExtensionsCall::*;331332 match call {333 // Readonly334 ERC165Call(_, _) => None,335336 // Not sponsored337 BurnFrom { .. }338 | BurnFromCross { .. }339 | ApproveCross { .. }340 | TransferCross { .. }341 | TransferFromCross { .. }342 | Repartition { .. } => None,343 }344 }345}346347mod erc1633 {348 use super::*;349350 pub fn call_sponsor<T>(351 call: ERC1633Call<T>,352 _token: RefungibleTokenHandle<T>,353 _who: &T::CrossAccountId,354 ) -> Option<()>355 where356 T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,357 {358 use ERC1633Call::*;359360 match call {361 // Readonly362 ERC165Call(_, _) | ParentToken | ParentTokenId => None,363 }364 }365}tests/src/eth/abi/reFungibleDeprecated.jsondiffbeforeafterboth--- a/tests/src/eth/abi/reFungibleDeprecated.json
+++ b/tests/src/eth/abi/reFungibleDeprecated.json
@@ -98,5 +98,33 @@
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
+ },
+ {
+ "inputs": [
+ { "internalType": "address", "name": "to", "type": "address" },
+ { "internalType": "uint256[]", "name": "tokenIds", "type": "uint256[]" }
+ ],
+ "name": "mintBulk",
+ "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ { "internalType": "address", "name": "to", "type": "address" },
+ {
+ "components": [
+ { "internalType": "uint256", "name": "field_0", "type": "uint256" },
+ { "internalType": "string", "name": "field_1", "type": "string" }
+ ],
+ "internalType": "struct Tuple0[]",
+ "name": "tokens",
+ "type": "tuple[]"
+ }
+ ],
+ "name": "mintBulkWithTokenURI",
+ "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+ "stateMutability": "nonpayable",
+ "type": "function"
}
]
tests/src/eth/collectionSponsoring.test.tsdiffbeforeafterboth--- a/tests/src/eth/collectionSponsoring.test.ts
+++ b/tests/src/eth/collectionSponsoring.test.ts
@@ -16,6 +16,7 @@
import {IKeyringPair} from '@polkadot/types/types';
import {Pallets, requirePalletsOrSkip, usingPlaygrounds} from '../util/index';
+import { CrossAccountId } from '../util/playgrounds/unique';
import {itEth, expect} from './util';
describe('evm nft collection sponsoring', () => {
@@ -158,6 +159,7 @@
const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));
+ const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
// User can mint token without balance:
{
@@ -181,7 +183,7 @@
expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
- expect(userBalanceAfter).to.be.eq(0n);
+ expect(userBalanceAfter).to.be.eq(userBalanceBefore);
expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
}
}));
@@ -332,32 +334,67 @@
});
});
- itEth('sponsors mint transactions', async ({helper}) => {
- const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'spnr', permissions: {mintMode: true}});
- await collection.setSponsor(alice, alice.address);
- await collection.confirmSponsorship(alice);
+ [
+ 'mintCross',
+ 'mintWithTokenURI',
+ 'mintBulk',
+ 'mintBulkWithTokenUri',
+ ].map(testCase =>
+ itEth(`[${testCase}] sponsors mint transactions`, async ({helper}) => {
+ const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'spnr', permissions: {mintMode: true}, tokenPropertyPermissions: [
+ {key: 'URI', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},
+ ]});
+
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ await collection.setSponsor(alice, alice.address);
+ await collection.confirmSponsorship(alice);
- const minter = helper.eth.createAccount();
- expect(await helper.balance.getEthereum(minter)).to.equal(0n);
+ const minter = helper.eth.createAccount();
+ const minterCross = helper.ethCrossAccount.fromAddress(minter);
+ expect(await helper.balance.getEthereum(minter)).to.equal(0n);
- const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
- const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', minter);
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', minter, true);
- await collection.addToAllowList(alice, {Ethereum: minter});
+ await collection.addToAllowList(alice, {Ethereum: minter});
+ helper.collection.addAdmin(alice, collection.collectionId, {Ethereum: owner});
+ const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
+ await collectionHelpers.methods.makeCollectionERC721MetadataCompatible(collectionAddress, 'base/')
+ .send();
- const result = await contract.methods.mint(minter).send();
+ let mintingResult;
+ let tokenId;
+ const nextTokenId = await contract.methods.nextTokenId().call();
+ switch (testCase) {
+ case 'mintCross':
+ mintingResult = await contract.methods.mintCross(minterCross, []).send();
+ break;
+ case 'mintWithTokenURI':
+ mintingResult = await contract.methods.mintWithTokenURI(minter, 'Test URI').send();
+ tokenId = mintingResult.events.Transfer.returnValues.tokenId;
+ expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
+ break;
+ case 'mintBulk':
+ mintingResult = await contract.methods.mintBulk(minter, [nextTokenId]).send();
+ break;
+ case 'mintBulkWithTokenUri':
+ mintingResult = await contract.methods.mintBulkWithTokenURI(minter, [[nextTokenId, 'Test URI']]).send();
+ tokenId = mintingResult.events.Transfer.returnValues.tokenId;
+ expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
+ break;
+ }
- const events = helper.eth.normalizeEvents(result.events);
- expect(events).to.deep.include({
- address: collectionAddress,
- event: 'Transfer',
- args: {
- from: '0x0000000000000000000000000000000000000000',
- to: minter,
- tokenId: '1',
- },
- });
- });
+ const events = helper.eth.normalizeEvents(mintingResult.events);
+ expect(events).to.deep.include({
+ address: collectionAddress,
+ event: 'Transfer',
+ args: {
+ from: '0x0000000000000000000000000000000000000000',
+ to: minter,
+ tokenId: '1',
+ },
+ });
+ }));
[
'setCollectionSponsorCross',
@@ -405,7 +442,6 @@
let sponsorship = (await collectionSub.getData())!.raw.sponsorship;
expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));
// Account cannot confirm sponsorship if it is not set as a sponsor
- //await collectionEvm.methods.confirmCollectionSponsorship().call();
await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
// Sponsor can confirm sponsorship:
@@ -434,6 +470,7 @@
const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));
+ const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
// User can mint token without balance:
{
@@ -456,7 +493,7 @@
expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
- expect(userBalanceAfter).to.be.eq(0n);
+ expect(userBalanceAfter).to.be.eq(userBalanceBefore);
expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
}
}));
@@ -465,7 +502,7 @@
'setCollectionSponsorCross',
'setCollectionSponsor', // Soft-deprecated
].map(testCase =>
- itEth(`[${testCase}] Check that transaction via EVM spend money from sponsor address`, async ({helper}) => {
+ itEth(`[${testCase}] Check that collection admin EVM transaction spend money from sponsor eth address`, async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const sponsor = await helper.eth.createAccountWithBalance(donor);
const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
@@ -475,15 +512,21 @@
const collectionSub = helper.rft.getCollectionObject(collectionId);
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner, testCase === 'setCollectionSponsor');
// Set collection sponsor:
+ expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;
await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send();
+ expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;
let collectionData = (await collectionSub.getData())!;
expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+ expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;
await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
collectionData = (await collectionSub.getData())!;
expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
-
+ expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;
+ const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner});
+ expect(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorTuple.sub))).to.be.equal(helper.address.ethToSubstrate(sponsor));
+
const user = helper.eth.createAccount();
const userCross = helper.ethCrossAccount.fromAddress(user);
await collectionEvm.methods.addCollectionAdminCross(userCross).send();
@@ -514,7 +557,7 @@
expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
}));
- itEth('Check that transaction via EVM spend money from substrate sponsor address', async ({helper}) => {
+ itEth('Check that collection admin EVM transaction spend money from sponsor sub address', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const sponsor = alice;
const sponsorCross = helper.ethCrossAccount.fromKeyringPair(sponsor);
@@ -527,9 +570,16 @@
expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;
await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;
+ let collectionData = (await collectionSub.getData())!;
+ expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(sponsor.address);
+ await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
await collectionSub.confirmSponsorship(sponsor);
+ collectionData = (await collectionSub.getData())!;
+ expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(sponsor.address);
expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;
+ const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner});
+ expect(BigInt(sponsorTuple.sub)).to.be.equal(BigInt('0x' + Buffer.from(sponsor.addressRaw).toString('hex')));
const user = helper.eth.createAccount();
const userCross = helper.ethCrossAccount.fromAddress(user);
@@ -585,6 +635,7 @@
const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
const sponsorBalanceBefore = await helper.balance.getSubstrate(sponsor.address);
+ const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
{
const nextTokenId = await collectionEvm.methods.nextTokenId().call();
@@ -609,9 +660,11 @@
const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
const sponsorBalanceAfter = await helper.balance.getSubstrate(sponsor.address);
+ const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
+ expect(userBalanceAfter).to.be.eq(userBalanceBefore);
expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
}
});
@@ -636,6 +689,58 @@
const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship;
expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address});
});
+
+ [
+ 'transfer',
+ 'transferCross',
+ 'transferFrom',
+ 'transferFromCross',
+ ].map(testCase =>
+ itEth(`[${testCase}] Check that transfer via EVM spend money from sponsor address`, async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+
+ const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
+ const sponsor = await helper.eth.createAccountWithBalance(donor);
+ const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
+ const receiver = await helper.eth.createAccountWithBalance(donor);
+ const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+
+ await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
+ await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
+
+ const user = await helper.eth.createAccountWithBalance(donor);
+ const userCross = helper.ethCrossAccount.fromAddress(user);
+ await collectionEvm.methods.addCollectionAdminCross(userCross).send();
+
+ const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
+ const tokenId = result.events.Transfer.returnValues.tokenId;
+
+ const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+ const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+ const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+
+ switch (testCase) {
+ case 'transfer':
+ await collectionEvm.methods.transfer(receiver, tokenId).send({from: user});
+ break;
+ case 'transferCross':
+ await collectionEvm.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});
+ break;
+ case 'transferFrom':
+ await collectionEvm.methods.transferFrom(user, receiver, tokenId).send({from: user});
+ break;
+ case 'transferFromCross':
+ await collectionEvm.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});
+ break;
+ }
+
+ const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+ expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
+ const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+ expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+ const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+ expect(userBalanceAfter).to.be.eq(userBalanceBefore);
+ }));
});
describe('evm RFT token sponsoring', () => {
@@ -648,117 +753,106 @@
});
});
- itEth('[cross] Check that transfer via EVM spend money from sponsor address', async ({helper}) => {
- const owner = await helper.eth.createAccountWithBalance(donor);
+ [
+ 'transfer',
+ 'transferCross',
+ 'transferFrom',
+ 'transferFromCross',
+ ].map(testCase =>
+ itEth(`[${testCase}] Check that token piece transfer via EVM spend money from sponsor address`, async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
- const sponsor = await helper.eth.createAccountWithBalance(donor);
- const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
- const receiver = await helper.eth.createAccountWithBalance(donor);
- const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+ const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
+ const sponsor = await helper.eth.createAccountWithBalance(donor);
+ const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
+ const receiver = await helper.eth.createAccountWithBalance(donor);
+ const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
- await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
- await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
+ await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
+ await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
- const user = await helper.eth.createAccountWithBalance(donor);
- const userCross = helper.ethCrossAccount.fromAddress(user);
- await collectionEvm.methods.addCollectionAdminCross(userCross).send();
+ const user = await helper.eth.createAccountWithBalance(donor);
+ const userCross = helper.ethCrossAccount.fromAddress(user);
+ await collectionEvm.methods.addCollectionAdminCross(userCross).send();
- const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
- const tokenId = result.events.Transfer.returnValues.tokenId;
+ const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
+ const tokenId = result.events.Transfer.returnValues.tokenId;
- const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, user);
- await tokenContract.methods.repartition(2).send();
-
- const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
- const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
- const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+ const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, user);
+ await tokenContract.methods.repartition(2).send();
+
+ const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+ const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+ const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
- await tokenContract.methods.transfer(receiver, 1).send();
+ switch (testCase) {
+ case 'transfer':
+ await tokenContract.methods.transfer(receiver, 1).send();
+ break;
+ case 'transferCross':
+ await tokenContract.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), 1).send();
+ break;
+ case 'transferFrom':
+ await tokenContract.methods.transferFrom(user, receiver, 1).send();
+ break;
+ case 'transferFromCross':
+ await tokenContract.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), 1).send();
+ break;
+ }
- const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
- expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
- const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
- expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
- const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
- expect(userBalanceAfter).to.be.eq(userBalanceBefore);
- });
+ const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+ expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
+ const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+ expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+ const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+ expect(userBalanceAfter).to.be.eq(userBalanceBefore);
+ }));
- itEth('[cross] Check that approve via EVM spend money from sponsor address', async ({helper}) => {
- const owner = await helper.eth.createAccountWithBalance(donor);
+ [
+ 'approve',
+ 'approveCross',
+ ].map(testCase =>
+ itEth(`[${testCase}] Check that approve via EVM spend money from sponsor address`, async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
- const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
- const sponsor = await helper.eth.createAccountWithBalance(donor);
- const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
- const receiver = await helper.eth.createAccountWithBalance(donor);
- const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+ const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
+ const sponsor = await helper.eth.createAccountWithBalance(donor);
+ const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
+ const receiver = await helper.eth.createAccountWithBalance(donor);
+ const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
- await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
- await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
+ await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
+ await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
- const user = await helper.eth.createAccountWithBalance(donor);
- const userCross = helper.ethCrossAccount.fromAddress(user);
- await collectionEvm.methods.addCollectionAdminCross(userCross).send();
+ const user = await helper.eth.createAccountWithBalance(donor);
+ const userCross = helper.ethCrossAccount.fromAddress(user);
+ await collectionEvm.methods.addCollectionAdminCross(userCross).send();
- const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
- const tokenId = result.events.Transfer.returnValues.tokenId;
+ const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
+ const tokenId = result.events.Transfer.returnValues.tokenId;
- const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, user);
- await tokenContract.methods.repartition(2).send();
-
- const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
- const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
- const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+ const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, user);
+ await tokenContract.methods.repartition(2).send();
+
+ const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+ const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+ const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
- await tokenContract.methods.approve(receiver, 1).send();
+ switch (testCase) {
+ case 'approve':
+ await tokenContract.methods.approve(receiver, 1).send();
+ break;
+ case 'approveCross':
+ await tokenContract.methods.approveCross(helper.ethCrossAccount.fromAddress(receiver), 1).send();
+ break;
+ }
- const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
- expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
- const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
- expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
- const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
- expect(userBalanceAfter).to.be.eq(userBalanceBefore);
- });
-
-
- itEth('[cross] Check that transferFrom via EVM spend money from sponsor address', async ({helper}) => {
- const owner = await helper.eth.createAccountWithBalance(donor);
-
- const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
- const sponsor = await helper.eth.createAccountWithBalance(donor);
- const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
- const receiver = await helper.eth.createAccountWithBalance(donor);
- const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+ const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+ expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
+ const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+ expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+ const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+ expect(userBalanceAfter).to.be.eq(userBalanceBefore);
+ }));
+});
- await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
- await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
-
- const user = await helper.eth.createAccountWithBalance(donor);
- const userCross = helper.ethCrossAccount.fromAddress(user);
- await collectionEvm.methods.addCollectionAdminCross(userCross).send();
-
- const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
- const tokenId = result.events.Transfer.returnValues.tokenId;
-
- const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, user);
- await tokenContract.methods.repartition(2).send();
- await tokenContract.methods.approve(receiver, 1).send();
-
- const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
- const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
- const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
- const receiverBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(receiver));
-
- const receiverTokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, receiver);
- await receiverTokenContract.methods.transferFrom(user, receiver, 1).send();
-
- const receiverBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(receiver));
- expect(receiverBalanceAfter).to.be.eq(receiverBalanceBefore);
- const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
- expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
- const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
- expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
- const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
- expect(userBalanceAfter).to.be.eq(userBalanceBefore);
- });
-});