git.delta.rocks / unique-network / refs/commits / ee86073ebf66

difftreelog

Style fixes

Andrey2022-11-11parent: #44da5f8.patch.diff
in: master

6 files changed

modifiedtests/.gitignorediffbeforeafterboth
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -1 +1,2 @@
 /node_modules/
+properties.csv
modifiedtests/package.jsondiffbeforeafterboth
--- a/tests/package.json
+++ b/tests/package.json
@@ -101,6 +101,7 @@
     "testXcmTransferAcala": "mocha --timeout 9999999 -r ts-node/register ./**/xcm/xcmTransferAcala.test.ts acalaId=2000 uniqueId=5000",
     "testXcmTransferStatemine": "mocha --timeout 9999999 -r ts-node/register ./**/xcm/xcmTransferStatemine.test.ts statemineId=1000 uniqueId=5000",
     "testXcmTransferMoonbeam": "mocha --timeout 9999999 -r ts-node/register ./**/xcm/xcmTransferMoonbeam.test.ts",
+    "benchMintingFee": "ts-node src/benchmarks/mintFee/benchmark.ts",
     "load": "mocha --timeout 9999999 -r ts-node/register './**/*.load.ts'",
     "loadTransfer": "ts-node src/transfer.nload.ts",
     "polkadot-types-fetch-metadata": "curl -H 'Content-Type: application/json' -d '{\"id\":\"1\", \"jsonrpc\":\"2.0\", \"method\": \"state_getMetadata\", \"params\":[]}' http://localhost:9933 > src/interfaces/metadata.json",
addedtests/src/benchmarks/mintFee/benchmark.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/benchmarks/mintFee/benchmark.ts
@@ -0,0 +1,436 @@
+import {EthUniqueHelper, usingEthPlaygrounds} from '../../eth/util';
+import {readFile} from 'fs/promises';
+import {ContractImports} from '../../eth/util/playgrounds/types';
+import {
+  ICrossAccountId,
+  ITokenPropertyPermission,
+} from '../../util/playgrounds/types';
+import {IKeyringPair} from '@polkadot/types/types';
+import {UniqueNFTCollection} from '../../util/playgrounds/unique';
+import {Contract} from 'web3-eth-contract';
+import {createObjectCsvWriter} from 'csv-writer';
+
+const CONTRACT_IMPORT: ContractImports[] = [
+  {
+    fsPath: `${__dirname}/../../eth/api/CollectionHelpers.sol`,
+    solPath: 'eth/api/CollectionHelpers.sol',
+  },
+  {
+    fsPath: `${__dirname}/../../eth/api/ContractHelpers.sol`,
+    solPath: 'eth/api/ContractHelpers.sol',
+  },
+  {
+    fsPath: `${__dirname}/../../eth/api/UniqueRefungibleToken.sol`,
+    solPath: 'eth/api/UniqueRefungibleToken.sol',
+  },
+  {
+    fsPath: `${__dirname}/../../eth/api/UniqueRefungible.sol`,
+    solPath: 'eth/api/UniqueRefungible.sol',
+  },
+  {
+    fsPath: `${__dirname}/../../eth/api/UniqueNFT.sol`,
+    solPath: 'eth/api/UniqueNFT.sol',
+  },
+];
+
+const PROPERTIES = Array(40)
+  .fill(0)
+  .map((_, i) => {
+    return {
+      key: `key_${i}`,
+      value: Uint8Array.from(Buffer.from(`value_${i}`)),
+    };
+  });
+
+const PERMISSIONS: ITokenPropertyPermission[] = PROPERTIES.map((p) => {
+  return {
+    key: p.key,
+    permission: {
+      tokenOwner: true,
+      collectionAdmin: true,
+      mutable: true,
+    },
+  };
+});
+
+interface IBenchmarkResultForProp {
+	propertiesNumber: number;
+	substrateFee: number;
+	ethFee: number;
+	ethBulkFee: number;
+	evmProxyContractFee: number;
+	evmProxyContractBulkFee: number;
+}
+
+const main = async () => {
+  const benchmarks = [
+    'substrateFee',
+    'ethFee',
+    'ethBulkFee',
+    'evmProxyContractFee',
+    'evmProxyContractBulkFee',
+  ];
+  const headers = [
+    'propertiesNumber',
+    ...benchmarks,
+  ];
+
+
+  const csvWriter = createObjectCsvWriter({
+    path: 'properties.csv',
+    header: headers,
+  });
+
+  await usingEthPlaygrounds(async (helper, privateKey) => {
+    const CONTRACT_SOURCE = (
+      await readFile(`${__dirname}/proxyContract.sol`)
+    ).toString();
+
+    const donor = await privateKey('//Alice'); // Seed from account with balance on this network
+    const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);
+
+    const contract = await helper.ethContract.deployByCode(
+      ethSigner,
+      'ProxyMint',
+      CONTRACT_SOURCE,
+      CONTRACT_IMPORT,
+    );
+
+    const fees = await benchMintFee(helper, privateKey, contract);
+    console.log('Minting without properties');
+    console.table(fees);
+
+    const result: IBenchmarkResultForProp[] = [];
+    const csvResult: IBenchmarkResultForProp[] = [];
+
+    for (let i = 1; i <= 20; i++) {
+      const benchResult = await benchMintWithProperties(helper, privateKey, contract, {
+        propertiesNumber: i,
+      }) as any;
+
+      csvResult.push(benchResult);
+
+      const minFee = Math.min(...(benchmarks.map(x => benchResult[x])));
+      for(const key of benchmarks) {
+        const keyPercent = Math.round((benchResult[key] / minFee) * 100);
+        benchResult[key] = `${benchResult[key]} (${keyPercent}%)`;
+      }
+
+      result.push(benchResult);
+    }
+
+    await csvWriter.writeRecords(csvResult);
+
+    console.log('Minting with properties');
+    console.table(result, headers);
+  });
+};
+
+main()
+  .then(() => process.exit(0))
+  .catch((e) => {
+    console.log(e);
+    process.exit(1);
+  });
+
+async function createCollectionForBenchmarks(
+  helper: EthUniqueHelper,
+  privateKey: (seed: string) => Promise<IKeyringPair>,
+  ethSigner: string,
+  proxyContract: string,
+  permissions: ITokenPropertyPermission[],
+) {
+  const donor = await privateKey('//Alice');
+
+  const collection = await helper.nft.mintCollection(donor, {
+    name: 'test mintToSubstrate',
+    description: 'EVMHelpers',
+    tokenPrefix: 'ap',
+    tokenPropertyPermissions: [
+      {
+        key: 'url',
+        permission: {
+          tokenOwner: true,
+          collectionAdmin: true,
+          mutable: true,
+        },
+      },
+    ],
+    limits: {sponsorTransferTimeout: 0, sponsorApproveTimeout: 0},
+    permissions: {mintMode: true},
+  });
+
+  await collection.addToAllowList(donor, {
+    Ethereum: helper.address.substrateToEth(donor.address),
+  });
+  await collection.addToAllowList(donor, {Substrate: donor.address});
+  await collection.addAdmin(donor, {Ethereum: ethSigner});
+  await collection.addAdmin(donor, {
+    Ethereum: helper.address.substrateToEth(donor.address),
+  });
+  await collection.addToAllowList(donor, {Ethereum: proxyContract});
+  await collection.addAdmin(donor, {Ethereum: proxyContract});
+  await collection.setTokenPropertyPermissions(donor, permissions);
+
+  return collection;
+}
+
+async function benchMintFee(
+  helper: EthUniqueHelper,
+  privateKey: (seed: string) => Promise<IKeyringPair>,
+  proxyContract: Contract,
+): Promise<{
+	substrateFee: number;
+	ethFee: number;
+	evmProxyContractFee: number;
+}> {
+  const donor = await privateKey('//Alice');
+  const substrateReceiver = await privateKey('//Bob');
+  const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);
+
+  const nominal = helper.balance.getOneTokenNominal();
+
+  await helper.eth.transferBalanceFromSubstrate(
+    donor,
+    proxyContract.options.address,
+    100n,
+  );
+
+  const collection = await createCollectionForBenchmarks(
+    helper,
+    privateKey,
+    ethSigner,
+    proxyContract.options.address,
+    PERMISSIONS,
+  );
+
+  const substrateFee = await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.mintToken(donor, {Substrate: substrateReceiver.address}),
+  );
+
+  const collectionEthAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+  const collectionContract = helper.ethNativeContract.collection(
+    collectionEthAddress,
+    'nft',
+  );
+
+  const receiverEthAddress = helper.address.substrateToEth(substrateReceiver.address);
+
+  const encodedCall = collectionContract.methods
+    .mint(receiverEthAddress)
+    .encodeABI();
+
+  const ethFee = await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    async () => {
+      await helper.eth.sendEVM(
+        donor,
+        collectionContract.options.address,
+        encodedCall,
+        '0',
+      );
+    },
+  );
+
+  const evmProxyContractFee = await helper.arrange.calculcateFee(
+    {Ethereum: ethSigner},
+    async () => {
+      await proxyContract.methods
+        .mintToSubstrate(
+          helper.ethAddress.fromCollectionId(collection.collectionId),
+          substrateReceiver.addressRaw,
+        )
+        .send({from: ethSigner});
+    },
+  );
+
+  return {
+    substrateFee: convertToTokens(substrateFee, nominal),
+    ethFee: convertToTokens(ethFee, nominal),
+    evmProxyContractFee: convertToTokens(evmProxyContractFee, nominal),
+  };
+}
+
+async function benchMintWithProperties(
+  helper: EthUniqueHelper,
+  privateKey: (seed: string) => Promise<IKeyringPair>,
+  proxyContract: Contract,
+  setup: { propertiesNumber: number },
+): Promise<IBenchmarkResultForProp> {
+  const donor = await privateKey('//Alice'); // Seed from account with balance on this network
+  const ethSigner = await helper.eth.createAccountWithBalance(donor, 100n);
+
+  const susbstrateReceiver = await privateKey('//Bob');
+  const receiverEthAddress = helper.address.substrateToEth(susbstrateReceiver.address);
+
+  const nominal = helper.balance.getOneTokenNominal();
+
+  const substrateFee = await calculateFeeNftMintWithProperties(
+    helper,
+    privateKey,
+    {Substrate: donor.address},
+    ethSigner,
+    proxyContract.options.address,
+    async (collection) => {
+      await collection.mintToken(
+        donor,
+        {Substrate: susbstrateReceiver.address},
+        PROPERTIES.slice(0, setup.propertiesNumber).map((p) => {
+          return {key: p.key, value: Buffer.from(p.value).toString()};
+        }),
+      );
+    },
+  );
+
+  const ethFee = await calculateFeeNftMintWithProperties(
+    helper,
+    privateKey,
+    {Substrate: donor.address},
+    ethSigner,
+    proxyContract.options.address,
+    async (collection) => {
+      const evmContract = helper.ethNativeContract.collection(
+        helper.ethAddress.fromCollectionId(collection.collectionId),
+        'nft',
+      );
+
+      const subTokenId = await evmContract.methods.nextTokenId().call();
+
+      let encodedCall = evmContract.methods
+        .mint(receiverEthAddress)
+        .encodeABI();
+
+      await helper.eth.sendEVM(
+        donor,
+        evmContract.options.address,
+        encodedCall,
+        '0',
+      );
+
+      for (const val of PROPERTIES.slice(0, setup.propertiesNumber)) {
+        encodedCall = await evmContract.methods
+          .setProperty(subTokenId, val.key, Buffer.from(val.value))
+          .encodeABI();
+
+        await helper.eth.sendEVM(
+          donor,
+          evmContract.options.address,
+          encodedCall,
+          '0',
+        );
+      }
+    },
+  );
+
+  const ethBulkFee = await calculateFeeNftMintWithProperties(
+    helper,
+    privateKey,
+    {Substrate: donor.address},
+    ethSigner,
+    proxyContract.options.address,
+    async (collection) => {
+      const evmContract = helper.ethNativeContract.collection(
+        helper.ethAddress.fromCollectionId(collection.collectionId),
+        'nft',
+      );
+
+      const subTokenId = await evmContract.methods.nextTokenId().call();
+
+      let encodedCall = evmContract.methods
+        .mint(receiverEthAddress)
+        .encodeABI();
+
+      await helper.eth.sendEVM(
+        donor,
+        evmContract.options.address,
+        encodedCall,
+        '0',
+      );
+
+      encodedCall = await evmContract.methods
+        .setProperties(
+          subTokenId,
+          PROPERTIES.slice(0, setup.propertiesNumber).map((p) => {
+            return {field_0: p.key, field_1: p.value};
+          }),
+        )
+        .encodeABI();
+
+      await helper.eth.sendEVM(
+        donor,
+        evmContract.options.address,
+        encodedCall,
+        '0',
+      );
+    },
+  );
+
+  const proxyContractFee = await calculateFeeNftMintWithProperties(
+    helper,
+    privateKey,
+    {Ethereum: ethSigner},
+    ethSigner,
+    proxyContract.options.address,
+    async (collection) => {
+      await proxyContract.methods
+        .mintToSubstrateWithProperty(
+          helper.ethAddress.fromCollectionId(collection.collectionId),
+          susbstrateReceiver.addressRaw,
+          PROPERTIES.slice(0, setup.propertiesNumber),
+        )
+        .send({from: ethSigner});
+    },
+  );
+
+  const proxyContractBulkFee = await calculateFeeNftMintWithProperties(
+    helper,
+    privateKey,
+    {Ethereum: ethSigner},
+    ethSigner,
+    proxyContract.options.address,
+    async (collection) => {
+      await proxyContract.methods
+        .mintToSubstrateBulkProperty(
+          helper.ethAddress.fromCollectionId(collection.collectionId),
+          susbstrateReceiver.addressRaw,
+          PROPERTIES.slice(0, setup.propertiesNumber).map((p) => {
+            return {field_0: p.key, field_1: p.value};
+          }),
+        )
+        .send({from: ethSigner, gas: 25_000_000});
+    },
+  );
+
+  return {
+    propertiesNumber: setup.propertiesNumber,
+    substrateFee: convertToTokens(substrateFee, nominal),
+    ethFee: convertToTokens(ethFee, nominal),
+    ethBulkFee: convertToTokens(ethBulkFee, nominal),
+    evmProxyContractFee: convertToTokens(proxyContractFee, nominal),
+    evmProxyContractBulkFee: convertToTokens(proxyContractBulkFee, nominal),
+  };
+}
+
+async function calculateFeeNftMintWithProperties(
+  helper: EthUniqueHelper,
+  privateKey: (seed: string) => Promise<IKeyringPair>,
+  payer: ICrossAccountId,
+  ethSigner: string,
+  proxyContractAddress: string,
+  calculatedCall: (collection: UniqueNFTCollection) => Promise<any>,
+): Promise<bigint> {
+  const collection = await createCollectionForBenchmarks(
+    helper,
+    privateKey,
+    ethSigner,
+    proxyContractAddress,
+    PERMISSIONS,
+  );
+  return helper.arrange.calculcateFee(payer, async () => {
+    await calculatedCall(collection);
+  });
+}
+function convertToTokens(value: bigint, nominal: bigint): number {
+  return Number((value * 1000n) / nominal) / 1000;
+}
addedtests/src/benchmarks/mintFee/proxyContract.soldiffbeforeafterboth
--- /dev/null
+++ b/tests/src/benchmarks/mintFee/proxyContract.sol
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier:  Apache License
+pragma solidity >=0.8.0;
+import {CollectionHelpers} from "../../eth/api/CollectionHelpers.sol";
+import {ContractHelpers} from "../../eth/api/ContractHelpers.sol";
+import {UniqueRefungibleToken} from "../../eth/api/UniqueRefungibleToken.sol";
+import {UniqueRefungible, Collection, EthCrossAccount as RftCrossAccountId, Tuple20 as RftProperties} from "../../eth/api/UniqueRefungible.sol";
+import {UniqueNFT, EthCrossAccount as NftCrossAccountId, Tuple21 as NftProperty, TokenProperties} from "../../eth/api/UniqueNFT.sol";
+
+struct Property {
+	string key;
+	bytes value;
+}
+
+contract ProxyMint {
+	bytes32 constant REFUNGIBLE_COLLECTION_TYPE = keccak256(bytes("ReFungible"));
+	bytes32 constant NONFUNGIBLE_COLLECTION_TYPE = keccak256(bytes("NFT"));
+
+	modifier checkRestrictions(address _collection) {
+		Collection commonContract = Collection(_collection);
+		require(commonContract.isOwnerOrAdmin(msg.sender), "Only collection admin/owner can call this method");
+		_;
+	}
+
+	/// @dev This emits when a mint to a substrate address has been made.
+	event MintToSub(address _toEth, uint256 _toSub, address _collection, uint256 _tokenId);
+
+	function mintToSubstrate(address _collection, uint256 _substrateReceiver) external checkRestrictions(_collection) {
+		Collection commonContract = Collection(_collection);
+		bytes32 collectionType = keccak256(bytes(commonContract.uniqueCollectionType()));
+		uint256 tokenId;
+
+		if (collectionType == REFUNGIBLE_COLLECTION_TYPE) {
+			UniqueRefungible rftCollection = UniqueRefungible(_collection);
+
+			tokenId = rftCollection.mint(address(this));
+
+			rftCollection.transferFromCross(
+				RftCrossAccountId(address(this), 0),
+				RftCrossAccountId(address(0), _substrateReceiver),
+				tokenId
+			);
+		} else if (collectionType == NONFUNGIBLE_COLLECTION_TYPE) {
+			UniqueNFT nftCollection = UniqueNFT(_collection);
+			tokenId = nftCollection.mint(address(this));
+
+			nftCollection.transferFromCross(
+				NftCrossAccountId(address(this), 0),
+				NftCrossAccountId(address(0), _substrateReceiver),
+				tokenId
+			);
+		} else {
+			revert("Wrong collection type. Works only with NFT or RFT");
+		}
+
+		emit MintToSub(address(0), _substrateReceiver, _collection, tokenId);
+	}
+
+	function mintToSubstrateWithProperty(
+		address _collection,
+		uint256 _substrateReceiver,
+		Property[] calldata properties
+	) external checkRestrictions(_collection) {
+		uint256 propertiesLength = properties.length;
+		require(propertiesLength > 0, "Properies is empty");
+
+		Collection commonContract = Collection(_collection);
+		bytes32 collectionType = keccak256(bytes(commonContract.uniqueCollectionType()));
+		uint256 tokenId;
+
+		if (collectionType == REFUNGIBLE_COLLECTION_TYPE) {
+			UniqueRefungible rftCollection = UniqueRefungible(_collection);
+			tokenId = rftCollection.nextTokenId();
+			rftCollection.mint(address(this));
+			for (uint256 i = 0; i < propertiesLength; ++i) {
+				rftCollection.setProperty(tokenId, properties[i].key, properties[i].value);
+			}
+			rftCollection.transferFromCross(
+				RftCrossAccountId(address(this), 0),
+				RftCrossAccountId(address(0), _substrateReceiver),
+				tokenId
+			);
+		} else if (collectionType == NONFUNGIBLE_COLLECTION_TYPE) {
+			UniqueNFT nftCollection = UniqueNFT(_collection);
+			tokenId = nftCollection.mint(address(this));
+			for (uint256 i = 0; i < propertiesLength; ++i) {
+				nftCollection.setProperty(tokenId, properties[i].key, properties[i].value);
+			}
+			nftCollection.transferFromCross(
+				NftCrossAccountId(address(this), 0),
+				NftCrossAccountId(address(0), _substrateReceiver),
+				tokenId
+			);
+		} else {
+			revert("Wrong collection type. Works only with NFT or RFT");
+		}
+
+		emit MintToSub(address(0), _substrateReceiver, _collection, tokenId);
+	}
+
+	function mintToSubstrateBulkProperty(
+		address _collection,
+		uint256 _substrateReceiver,
+		NftProperty[] calldata _properties
+	) external checkRestrictions(_collection) {
+		uint256 propertiesLength = _properties.length;
+		require(propertiesLength > 0, "Properies is empty");
+
+		Collection commonContract = Collection(_collection);
+		bytes32 collectionType = keccak256(bytes(commonContract.uniqueCollectionType()));
+		uint256 tokenId;
+
+		if (collectionType == REFUNGIBLE_COLLECTION_TYPE) {} else if (collectionType == NONFUNGIBLE_COLLECTION_TYPE) {
+			UniqueNFT nftCollection = UniqueNFT(_collection);
+			tokenId = nftCollection.mint(address(this));
+
+			nftCollection.setProperties(tokenId, _properties);
+
+			nftCollection.transferFromCross(
+				NftCrossAccountId(address(this), 0),
+				NftCrossAccountId(address(0), _substrateReceiver),
+				tokenId
+			);
+		} else {
+			revert("Wrong collection type. Works only with NFT or RFT");
+		}
+
+		emit MintToSub(address(0), _substrateReceiver, _collection, tokenId);
+	}
+}
deletedtests/src/benchmarks/mintFeeBench/feeBench.tsdiffbeforeafterboth

no changes

deletedtests/src/benchmarks/mintFeeBench/proxyContract.soldiffbeforeafterboth
--- a/tests/src/benchmarks/mintFeeBench/proxyContract.sol
+++ /dev/null
@@ -1,129 +0,0 @@
-// SPDX-License-Identifier:  Apache License
-pragma solidity >=0.8.0;
-import {CollectionHelpers} from "../../eth/api/CollectionHelpers.sol";
-import {ContractHelpers} from "../../eth/api/ContractHelpers.sol";
-import {UniqueRefungibleToken} from "../../eth/api/UniqueRefungibleToken.sol";
-import {UniqueRefungible, Collection, EthCrossAccount as RftCrossAccountId, Tuple20 as RftProperties} from "../../eth/api/UniqueRefungible.sol";
-import {UniqueNFT, EthCrossAccount as NftCrossAccountId, Tuple21 as NftProperty, TokenProperties} from "../../eth/api/UniqueNFT.sol";
-
-struct Property {
-	string key;
-	bytes value;
-}
-
-contract ProxyMint {
-	bytes32 constant REFUNGIBLE_COLLECTION_TYPE = keccak256(bytes("ReFungible"));
-	bytes32 constant NONFUNGIBLE_COLLECTION_TYPE = keccak256(bytes("NFT"));
-
-	modifier checkRestrictions(address _collection) {
-		Collection commonContract = Collection(_collection);
-		require(commonContract.isOwnerOrAdmin(msg.sender), "Only collection admin/owner can call this method");
-		_;
-	}
-
-	/// @dev This emits when a mint to a substrate address has been made.
-	event MintToSub(address _toEth, uint256 _toSub, address _collection, uint256 _tokenId);
-
-	function mintToSubstrate(address _collection, uint256 _substrateReceiver) external checkRestrictions(_collection) {
-		Collection commonContract = Collection(_collection);
-		bytes32 collectionType = keccak256(bytes(commonContract.uniqueCollectionType()));
-		uint256 tokenId;
-
-		if (collectionType == REFUNGIBLE_COLLECTION_TYPE) {
-			UniqueRefungible rftCollection = UniqueRefungible(_collection);
-
-			tokenId = rftCollection.mint(address(this));
-
-			rftCollection.transferFromCross(
-				RftCrossAccountId(address(this), 0),
-				RftCrossAccountId(address(0), _substrateReceiver),
-				tokenId
-			);
-		} else if (collectionType == NONFUNGIBLE_COLLECTION_TYPE) {
-			UniqueNFT nftCollection = UniqueNFT(_collection);
-			tokenId = nftCollection.mint(address(this));
-
-			nftCollection.transferFromCross(
-				NftCrossAccountId(address(this), 0),
-				NftCrossAccountId(address(0), _substrateReceiver),
-				tokenId
-			);
-		} else {
-			revert("Wrong collection type. Works only with NFT or RFT");
-		}
-
-		emit MintToSub(address(0), _substrateReceiver, _collection, tokenId);
-	}
-
-	function mintToSubstrateWithProperty(
-		address _collection,
-		uint256 _substrateReceiver,
-		Property[] calldata properties
-	) external checkRestrictions(_collection) {
-		uint256 propertiesLength = properties.length;
-		require(propertiesLength > 0, "Properies is empty");
-
-		Collection commonContract = Collection(_collection);
-		bytes32 collectionType = keccak256(bytes(commonContract.uniqueCollectionType()));
-		uint256 tokenId;
-
-		if (collectionType == REFUNGIBLE_COLLECTION_TYPE) {
-			UniqueRefungible rftCollection = UniqueRefungible(_collection);
-			tokenId = rftCollection.nextTokenId();
-			rftCollection.mint(address(this));
-			for (uint256 i = 0; i < propertiesLength; ++i) {
-				rftCollection.setProperty(tokenId, properties[i].key, properties[i].value);
-			}
-			rftCollection.transferFromCross(
-				RftCrossAccountId(address(this), 0),
-				RftCrossAccountId(address(0), _substrateReceiver),
-				tokenId
-			);
-		} else if (collectionType == NONFUNGIBLE_COLLECTION_TYPE) {
-			UniqueNFT nftCollection = UniqueNFT(_collection);
-			tokenId = nftCollection.mint(address(this));
-			for (uint256 i = 0; i < propertiesLength; ++i) {
-				nftCollection.setProperty(tokenId, properties[i].key, properties[i].value);
-			}
-			nftCollection.transferFromCross(
-				NftCrossAccountId(address(this), 0),
-				NftCrossAccountId(address(0), _substrateReceiver),
-				tokenId
-			);
-		} else {
-			revert("Wrong collection type. Works only with NFT or RFT");
-		}
-
-		emit MintToSub(address(0), _substrateReceiver, _collection, tokenId);
-	}
-
-	function mintToSubstrateBulkProperty(
-		address _collection,
-		uint256 _substrateReceiver,
-		NftProperty[] calldata _properties
-	) external checkRestrictions(_collection) {
-		uint256 propertiesLength = _properties.length;
-		require(propertiesLength > 0, "Properies is empty");
-
-		Collection commonContract = Collection(_collection);
-		bytes32 collectionType = keccak256(bytes(commonContract.uniqueCollectionType()));
-		uint256 tokenId;
-
-		if (collectionType == REFUNGIBLE_COLLECTION_TYPE) {} else if (collectionType == NONFUNGIBLE_COLLECTION_TYPE) {
-			UniqueNFT nftCollection = UniqueNFT(_collection);
-			tokenId = nftCollection.mint(address(this));
-
-			nftCollection.setProperties(tokenId, _properties);
-
-			nftCollection.transferFromCross(
-				NftCrossAccountId(address(this), 0),
-				NftCrossAccountId(address(0), _substrateReceiver),
-				tokenId
-			);
-		} else {
-			revert("Wrong collection type. Works only with NFT or RFT");
-		}
-
-		emit MintToSub(address(0), _substrateReceiver, _collection, tokenId);
-	}
-}