git.delta.rocks / unique-network / refs/commits / 7f98069c7b60

difftreelog

feat Add `collection_admins` method to eth collection.

Trubnikov Sergey2022-10-24parent: #ab2e6a7.patch.diff
in: master

19 files changed

modifiedpallets/common/src/erc.rsdiffbeforeafterboth
--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -580,7 +580,7 @@
 
 	/// Get collection owner.
 	///
-	/// @return Tuble with sponsor address and his substrate mirror.
+	/// @return Tuple with sponsor address and his substrate mirror.
 	/// If address is canonical then substrate mirror is zero and vice versa.
 	fn collection_owner(&self) -> Result<(address, uint256)> {
 		Ok(convert_cross_account_to_tuple::<T>(
@@ -616,13 +616,16 @@
 	// 		.map_err(dispatch_to_evm::<T>)
 	// }
 
-	// TODO: need implement AbiWriter for &Vec<T>
-	// fn collection_admins(&self) -> Result<Vec<(address, uint256)>> {
-	// 	let result = pallet_common::IsAdmin::<T>::iter_prefix((self.id,))
-	// 		.map(|(admin, _)| pallet_common::eth::convert_cross_account_to_tuple::<T>(&admin))
-	// 		.collect();
-	// 	Ok(result)
-	// }
+	/// Get collection administrators
+	///
+	/// @return Vector of tuples with admins address and his substrate mirror.
+	/// If address is canonical then substrate mirror is zero and vice versa.
+	fn collection_admins(&self) -> Result<Vec<(address, uint256)>> {
+		let result = crate::IsAdmin::<T>::iter_prefix((self.id,))
+			.map(|(admin, _)| crate::eth::convert_cross_account_to_tuple::<T>(&admin))
+			.collect();
+		Ok(result)
+	}
 }
 
 /// ### Note
modifiedpallets/evm-contract-helpers/src/stubs/ContractHelpers.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/fungible/src/stubs/UniqueFungible.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/fungible/src/stubs/UniqueFungible.soldiffbeforeafterboth
--- a/pallets/fungible/src/stubs/UniqueFungible.sol
+++ b/pallets/fungible/src/stubs/UniqueFungible.sol
@@ -18,7 +18,7 @@
 }
 
 /// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x62e22290
+/// @dev the ERC-165 identifier for this interface is 0x3af103fb
 contract Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -282,7 +282,7 @@
 
 	/// Get collection owner.
 	///
-	/// @return Tuble with sponsor address and his substrate mirror.
+	/// @return Tuple with sponsor address and his substrate mirror.
 	/// If address is canonical then substrate mirror is zero and vice versa.
 	/// @dev EVM selector for this function is: 0xdf727d3b,
 	///  or in textual repr: collectionOwner()
@@ -303,6 +303,18 @@
 		newOwner;
 		dummy = 0;
 	}
+
+	/// Get collection administrators
+	///
+	/// @return Vector of tuples with admins address and his substrate mirror.
+	/// If address is canonical then substrate mirror is zero and vice versa.
+	/// @dev EVM selector for this function is: 0x5813216b,
+	///  or in textual repr: collectionAdmins()
+	function collectionAdmins() public view returns (Tuple6[] memory) {
+		require(false, stub_error);
+		dummy;
+		return new Tuple6[](0);
+	}
 }
 
 /// @dev the ERC-165 identifier for this interface is 0x63034ac5
modifiedpallets/nonfungible/src/stubs/UniqueNFT.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth
--- a/pallets/nonfungible/src/stubs/UniqueNFT.sol
+++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol
@@ -91,7 +91,7 @@
 }
 
 /// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x62e22290
+/// @dev the ERC-165 identifier for this interface is 0x3af103fb
 contract Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -355,7 +355,7 @@
 
 	/// Get collection owner.
 	///
-	/// @return Tuble with sponsor address and his substrate mirror.
+	/// @return Tuple with sponsor address and his substrate mirror.
 	/// If address is canonical then substrate mirror is zero and vice versa.
 	/// @dev EVM selector for this function is: 0xdf727d3b,
 	///  or in textual repr: collectionOwner()
@@ -376,6 +376,18 @@
 		newOwner;
 		dummy = 0;
 	}
+
+	/// Get collection administrators
+	///
+	/// @return Vector of tuples with admins address and his substrate mirror.
+	/// If address is canonical then substrate mirror is zero and vice versa.
+	/// @dev EVM selector for this function is: 0x5813216b,
+	///  or in textual repr: collectionAdmins()
+	function collectionAdmins() public view returns (Tuple17[] memory) {
+		require(false, stub_error);
+		dummy;
+		return new Tuple17[](0);
+	}
 }
 
 /// @dev anonymous struct
modifiedpallets/refungible/src/stubs/UniqueRefungible.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/refungible/src/stubs/UniqueRefungible.soldiffbeforeafterboth
--- a/pallets/refungible/src/stubs/UniqueRefungible.sol
+++ b/pallets/refungible/src/stubs/UniqueRefungible.sol
@@ -91,7 +91,7 @@
 }
 
 /// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x62e22290
+/// @dev the ERC-165 identifier for this interface is 0x3af103fb
 contract Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -355,7 +355,7 @@
 
 	/// Get collection owner.
 	///
-	/// @return Tuble with sponsor address and his substrate mirror.
+	/// @return Tuple with sponsor address and his substrate mirror.
 	/// If address is canonical then substrate mirror is zero and vice versa.
 	/// @dev EVM selector for this function is: 0xdf727d3b,
 	///  or in textual repr: collectionOwner()
@@ -376,6 +376,18 @@
 		newOwner;
 		dummy = 0;
 	}
+
+	/// Get collection administrators
+	///
+	/// @return Vector of tuples with admins address and his substrate mirror.
+	/// If address is canonical then substrate mirror is zero and vice versa.
+	/// @dev EVM selector for this function is: 0x5813216b,
+	///  or in textual repr: collectionAdmins()
+	function collectionAdmins() public view returns (Tuple17[] memory) {
+		require(false, stub_error);
+		dummy;
+		return new Tuple17[](0);
+	}
 }
 
 /// @dev anonymous struct
modifiedpallets/refungible/src/stubs/UniqueRefungibleToken.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/unique/src/eth/stubs/CollectionHelpers.rawdiffbeforeafterboth

binary blob — no preview

modifiedtests/src/eth/api/UniqueFungible.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueFungible.sol
+++ b/tests/src/eth/api/UniqueFungible.sol
@@ -13,7 +13,7 @@
 }
 
 /// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x62e22290
+/// @dev the ERC-165 identifier for this interface is 0x3af103fb
 interface Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -184,7 +184,7 @@
 
 	/// Get collection owner.
 	///
-	/// @return Tuble with sponsor address and his substrate mirror.
+	/// @return Tuple with sponsor address and his substrate mirror.
 	/// If address is canonical then substrate mirror is zero and vice versa.
 	/// @dev EVM selector for this function is: 0xdf727d3b,
 	///  or in textual repr: collectionOwner()
@@ -197,6 +197,14 @@
 	/// @dev EVM selector for this function is: 0x4f53e226,
 	///  or in textual repr: changeCollectionOwner(address)
 	function changeCollectionOwner(address newOwner) external;
+
+	/// Get collection administrators
+	///
+	/// @return Vector of tuples with admins address and his substrate mirror.
+	/// If address is canonical then substrate mirror is zero and vice versa.
+	/// @dev EVM selector for this function is: 0x5813216b,
+	///  or in textual repr: collectionAdmins()
+	function collectionAdmins() external view returns (Tuple6[] memory);
 }
 
 /// @dev the ERC-165 identifier for this interface is 0x63034ac5
modifiedtests/src/eth/api/UniqueNFT.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -62,7 +62,7 @@
 }
 
 /// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x62e22290
+/// @dev the ERC-165 identifier for this interface is 0x3af103fb
 interface Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -233,7 +233,7 @@
 
 	/// Get collection owner.
 	///
-	/// @return Tuble with sponsor address and his substrate mirror.
+	/// @return Tuple with sponsor address and his substrate mirror.
 	/// If address is canonical then substrate mirror is zero and vice versa.
 	/// @dev EVM selector for this function is: 0xdf727d3b,
 	///  or in textual repr: collectionOwner()
@@ -246,6 +246,14 @@
 	/// @dev EVM selector for this function is: 0x4f53e226,
 	///  or in textual repr: changeCollectionOwner(address)
 	function changeCollectionOwner(address newOwner) external;
+
+	/// Get collection administrators
+	///
+	/// @return Vector of tuples with admins address and his substrate mirror.
+	/// If address is canonical then substrate mirror is zero and vice versa.
+	/// @dev EVM selector for this function is: 0x5813216b,
+	///  or in textual repr: collectionAdmins()
+	function collectionAdmins() external view returns (Tuple17[] memory);
 }
 
 /// @dev anonymous struct
modifiedtests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueRefungible.sol
+++ b/tests/src/eth/api/UniqueRefungible.sol
@@ -62,7 +62,7 @@
 }
 
 /// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x62e22290
+/// @dev the ERC-165 identifier for this interface is 0x3af103fb
 interface Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -233,7 +233,7 @@
 
 	/// Get collection owner.
 	///
-	/// @return Tuble with sponsor address and his substrate mirror.
+	/// @return Tuple with sponsor address and his substrate mirror.
 	/// If address is canonical then substrate mirror is zero and vice versa.
 	/// @dev EVM selector for this function is: 0xdf727d3b,
 	///  or in textual repr: collectionOwner()
@@ -246,6 +246,14 @@
 	/// @dev EVM selector for this function is: 0x4f53e226,
 	///  or in textual repr: changeCollectionOwner(address)
 	function changeCollectionOwner(address newOwner) external;
+
+	/// Get collection administrators
+	///
+	/// @return Vector of tuples with admins address and his substrate mirror.
+	/// If address is canonical then substrate mirror is zero and vice versa.
+	/// @dev EVM selector for this function is: 0x5813216b,
+	///  or in textual repr: collectionAdmins()
+	function collectionAdmins() external view returns (Tuple17[] memory);
 }
 
 /// @dev anonymous struct
modifiedtests/src/eth/collectionAdmin.test.tsdiffbeforeafterboth
--- a/tests/src/eth/collectionAdmin.test.ts
+++ b/tests/src/eth/collectionAdmin.test.ts
@@ -14,6 +14,7 @@
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
 import {IKeyringPair} from '@polkadot/types/types';
+import {IEthCrossAccountId} from '../util/playgrounds/types';
 import {usingEthPlaygrounds, itEth, expect, EthUniqueHelper} from './util';
 
 async function recordEthFee(helper: EthUniqueHelper, userAddress: string, call: () => Promise<any>) {
@@ -68,11 +69,31 @@
 
     const newAdmin = helper.eth.createAccount();
     const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+  
     expect(await collectionEvm.methods.isOwnerOrAdmin(newAdmin).call()).to.be.false;
     await collectionEvm.methods.addCollectionAdmin(newAdmin).send();
     expect(await collectionEvm.methods.isOwnerOrAdmin(newAdmin).call()).to.be.true;
   });
+  
+  itEth.skip('Check adminlist', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+        
+    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+    
+    const admin1 = helper.eth.createAccount();
+    const [admin2] = await helper.arrange.createAccounts([10n], donor);
+    await collectionEvm.methods.addCollectionAdmin(admin1).send();
+    await collectionEvm.methods.addCollectionAdminSubstrate(admin2.addressRaw).send();
 
+    const adminListRpc = await helper.collection.getAdmins(collectionId);
+    let adminListEth = await collectionEvm.methods.collectionAdmins().call();
+    adminListEth = adminListEth.map((element: IEthCrossAccountId) => {
+      return helper.address.convertCrossAccountFromEthCrossAcoount(element);
+    });
+    expect(adminListRpc).to.be.like(adminListEth);
+  });  
+    
   itEth('(!negative tests!) Add admin by ADMIN is not allowed', async ({helper}) => {
     const owner = await helper.eth.createAccountWithBalance(donor);
     const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
modifiedtests/src/eth/fungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/fungibleAbi.json
+++ b/tests/src/eth/fungibleAbi.json
@@ -126,6 +126,23 @@
   },
   {
     "inputs": [],
+    "name": "collectionAdmins",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "field_0", "type": "address" },
+          { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+        ],
+        "internalType": "struct Tuple6[]",
+        "name": "",
+        "type": "tuple[]"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
     "name": "collectionOwner",
     "outputs": [
       {
modifiedtests/src/eth/nonFungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/nonFungibleAbi.json
+++ b/tests/src/eth/nonFungibleAbi.json
@@ -156,6 +156,23 @@
   },
   {
     "inputs": [],
+    "name": "collectionAdmins",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "field_0", "type": "address" },
+          { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+        ],
+        "internalType": "struct Tuple17[]",
+        "name": "",
+        "type": "tuple[]"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
     "name": "collectionOwner",
     "outputs": [
       {
modifiedtests/src/eth/reFungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/reFungibleAbi.json
+++ b/tests/src/eth/reFungibleAbi.json
@@ -156,6 +156,23 @@
   },
   {
     "inputs": [],
+    "name": "collectionAdmins",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "field_0", "type": "address" },
+          { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+        ],
+        "internalType": "struct Tuple17[]",
+        "name": "",
+        "type": "tuple[]"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
     "name": "collectionOwner",
     "outputs": [
       {
modifiedtests/src/util/playgrounds/types.tsdiffbeforeafterboth
before · tests/src/util/playgrounds/types.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// SPDX-License-Identifier: Apache-2.034import {IKeyringPair} from '@polkadot/types/types';56export interface IEvent {7  section: string;8  method: string;9  index: [number, number] | string;10  data: any[];11  phase: {applyExtrinsic: number} | 'Initialization',12}1314export interface ITransactionResult {15  status: 'Fail' | 'Success';16  result: {17      dispatchError: any,18      events: {19        phase: any, // {ApplyExtrinsic: number} | 'Initialization',20        event: IEvent;21      }[];22  },23  moduleError?: string;24}2526export interface ISubscribeBlockEventsData {27  number: number;28  hash: string;29  timestamp: number; 30  events: IEvent[];31}3233export interface ILogger {34  log: (msg: any, level?: string) => void;35  level: {36    ERROR: 'ERROR';37    WARNING: 'WARNING';38    INFO: 'INFO';39    [key: string]: string;40  }41}4243export interface IUniqueHelperLog {44  executedAt: number;45  executionTime: number;46  type: 'extrinsic' | 'rpc';47  status: 'Fail' | 'Success';48  call: string;49  params: any[];50  moduleError?: string;51  dispatchError?: any;52  events?: any;53}5455export interface IApiListeners {56  connected?: (...args: any[]) => any;57  disconnected?: (...args: any[]) => any;58  error?: (...args: any[]) => any;59  ready?: (...args: any[]) => any; 60  decorated?: (...args: any[]) => any;61}6263export interface ICrossAccountId {64  Substrate?: TSubstrateAccount;65  Ethereum?: TEthereumAccount;66}6768export interface ICrossAccountIdLower {69  substrate?: TSubstrateAccount;70  ethereum?: TEthereumAccount;71}7273export interface ICollectionLimits {74  accountTokenOwnershipLimit?: number | null;75  sponsoredDataSize?: number | null;76  sponsoredDataRateLimit?: {blocks: number} | {sponsoringDisabled: null} | null;77  tokenLimit?: number | null;78  sponsorTransferTimeout?: number | null;79  sponsorApproveTimeout?: number | null;80  ownerCanTransfer?: boolean | null;81  ownerCanDestroy?: boolean | null;82  transfersEnabled?: boolean | null;83}8485export interface INestingPermissions {86  tokenOwner?: boolean;87  collectionAdmin?: boolean;88  restricted?: number[] | null;89}9091export interface ICollectionPermissions {92  access?: 'Normal' | 'AllowList';93  mintMode?: boolean;94  nesting?: INestingPermissions;95}9697export interface IProperty {98  key: string;99  value?: string;100}101102export interface ITokenPropertyPermission {103  key: string;104  permission: {105    mutable?: boolean;106    tokenOwner?: boolean;107    collectionAdmin?: boolean;108  }109}110111export interface IToken {112  collectionId: number;113  tokenId: number;114}115116export interface IBlock {117  extrinsics: IExtrinsic[]118  header: {119    parentHash: string,120    number: number,121  };122}123124export interface IExtrinsic {125  isSigned: boolean,126  method: {127    method: string,128    section: string,129    args: any[]130  }131}132133export interface ICollectionCreationOptions {134  name?: string | number[];135  description?: string | number[];136  tokenPrefix?: string | number[];137  mode?: {138    nft?: null;139    refungible?: null;140    fungible?: number;141  }142  permissions?: ICollectionPermissions;143  properties?: IProperty[];144  tokenPropertyPermissions?: ITokenPropertyPermission[];145  limits?: ICollectionLimits;146  pendingSponsor?: TSubstrateAccount;147}148149export interface IChainProperties {150  ss58Format: number;151  tokenDecimals: number[];152  tokenSymbol: string[]153}154155export interface ISubstrateBalance {156  free: bigint,157  reserved: bigint,158  miscFrozen: bigint,159  feeFrozen: bigint160}161162export interface IStakingInfo {163  block: bigint,164  amount: bigint,165}166167export interface ISchedulerOptions {168  priority?: number,169  periodic?: {170    period: number,171    repetitions: number,172  },173}174175export interface IForeignAssetMetadata {176  name?: number | Uint8Array,177  symbol?: string,178  decimals?: number,179  minimalBalance?: bigint,180}181182export interface MoonbeamAssetInfo {183  location: any,184  metadata: {185    name: string,186    symbol: string,187    decimals: number,188    isFrozen: boolean,189    minimalBalance: bigint,190  },191  existentialDeposit: bigint,192  isSufficient: boolean,193  unitsPerSecond: bigint,194  numAssetsWeightHint: number,195}196197export interface AcalaAssetMetadata {198  name: string,199  symbol: string,200  decimals: number,201  minimalBalance: bigint,202}203204export interface DemocracyStandardAccountVote {205  balance: bigint,206  vote: {207    aye: boolean,208    conviction: number,209  },210}211212export type TSubstrateAccount = string;213export type TEthereumAccount = string;214export type TApiAllowedListeners = 'connected' | 'disconnected' | 'error' | 'ready' | 'decorated';215export type TUniqueNetworks = 'opal' | 'quartz' | 'unique';216export type TSiblingNetworkds = 'moonbeam' | 'moonriver' | 'acala' | 'karura' | 'westmint';217export type TRelayNetworks = 'rococo' | 'westend';218export type TNetworks = TUniqueNetworks | TSiblingNetworkds | TRelayNetworks;219export type TSigner = IKeyringPair; // | 'string'
after · tests/src/util/playgrounds/types.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// SPDX-License-Identifier: Apache-2.034import {IKeyringPair} from '@polkadot/types/types';56export interface IEvent {7  section: string;8  method: string;9  index: [number, number] | string;10  data: any[];11  phase: {applyExtrinsic: number} | 'Initialization',12}1314export interface ITransactionResult {15  status: 'Fail' | 'Success';16  result: {17      dispatchError: any,18      events: {19        phase: any, // {ApplyExtrinsic: number} | 'Initialization',20        event: IEvent;21      }[];22  },23  moduleError?: string;24}2526export interface ISubscribeBlockEventsData {27  number: number;28  hash: string;29  timestamp: number; 30  events: IEvent[];31}3233export interface ILogger {34  log: (msg: any, level?: string) => void;35  level: {36    ERROR: 'ERROR';37    WARNING: 'WARNING';38    INFO: 'INFO';39    [key: string]: string;40  }41}4243export interface IUniqueHelperLog {44  executedAt: number;45  executionTime: number;46  type: 'extrinsic' | 'rpc';47  status: 'Fail' | 'Success';48  call: string;49  params: any[];50  moduleError?: string;51  dispatchError?: any;52  events?: any;53}5455export interface IApiListeners {56  connected?: (...args: any[]) => any;57  disconnected?: (...args: any[]) => any;58  error?: (...args: any[]) => any;59  ready?: (...args: any[]) => any; 60  decorated?: (...args: any[]) => any;61}6263export interface ICrossAccountId {64  Substrate?: TSubstrateAccount;65  Ethereum?: TEthereumAccount;66}6768export interface ICrossAccountIdLower {69  substrate?: TSubstrateAccount;70  ethereum?: TEthereumAccount;71}7273export interface IEthCrossAccountId {74  0: TEthereumAccount;75  1: TSubstrateAccount;76  field_0: TEthereumAccount;77  field_1: TSubstrateAccount;78}7980export interface ICollectionLimits {81  accountTokenOwnershipLimit?: number | null;82  sponsoredDataSize?: number | null;83  sponsoredDataRateLimit?: {blocks: number} | {sponsoringDisabled: null} | null;84  tokenLimit?: number | null;85  sponsorTransferTimeout?: number | null;86  sponsorApproveTimeout?: number | null;87  ownerCanTransfer?: boolean | null;88  ownerCanDestroy?: boolean | null;89  transfersEnabled?: boolean | null;90}9192export interface INestingPermissions {93  tokenOwner?: boolean;94  collectionAdmin?: boolean;95  restricted?: number[] | null;96}9798export interface ICollectionPermissions {99  access?: 'Normal' | 'AllowList';100  mintMode?: boolean;101  nesting?: INestingPermissions;102}103104export interface IProperty {105  key: string;106  value?: string;107}108109export interface ITokenPropertyPermission {110  key: string;111  permission: {112    mutable?: boolean;113    tokenOwner?: boolean;114    collectionAdmin?: boolean;115  }116}117118export interface IToken {119  collectionId: number;120  tokenId: number;121}122123export interface IBlock {124  extrinsics: IExtrinsic[]125  header: {126    parentHash: string,127    number: number,128  };129}130131export interface IExtrinsic {132  isSigned: boolean,133  method: {134    method: string,135    section: string,136    args: any[]137  }138}139140export interface ICollectionCreationOptions {141  name?: string | number[];142  description?: string | number[];143  tokenPrefix?: string | number[];144  mode?: {145    nft?: null;146    refungible?: null;147    fungible?: number;148  }149  permissions?: ICollectionPermissions;150  properties?: IProperty[];151  tokenPropertyPermissions?: ITokenPropertyPermission[];152  limits?: ICollectionLimits;153  pendingSponsor?: TSubstrateAccount;154}155156export interface IChainProperties {157  ss58Format: number;158  tokenDecimals: number[];159  tokenSymbol: string[]160}161162export interface ISubstrateBalance {163  free: bigint,164  reserved: bigint,165  miscFrozen: bigint,166  feeFrozen: bigint167}168169export interface IStakingInfo {170  block: bigint,171  amount: bigint,172}173174export interface ISchedulerOptions {175  priority?: number,176  periodic?: {177    period: number,178    repetitions: number,179  },180}181182export interface IForeignAssetMetadata {183  name?: number | Uint8Array,184  symbol?: string,185  decimals?: number,186  minimalBalance?: bigint,187}188189export interface MoonbeamAssetInfo {190  location: any,191  metadata: {192    name: string,193    symbol: string,194    decimals: number,195    isFrozen: boolean,196    minimalBalance: bigint,197  },198  existentialDeposit: bigint,199  isSufficient: boolean,200  unitsPerSecond: bigint,201  numAssetsWeightHint: number,202}203204export interface AcalaAssetMetadata {205  name: string,206  symbol: string,207  decimals: number,208  minimalBalance: bigint,209}210211export interface DemocracyStandardAccountVote {212  balance: bigint,213  vote: {214    aye: boolean,215    conviction: number,216  },217}218219export type TSubstrateAccount = string;220export type TEthereumAccount = string;221export type TApiAllowedListeners = 'connected' | 'disconnected' | 'error' | 'ready' | 'decorated';222export type TUniqueNetworks = 'opal' | 'quartz' | 'unique';223export type TSiblingNetworkds = 'moonbeam' | 'moonriver' | 'acala' | 'karura' | 'westmint';224export type TRelayNetworks = 'rococo' | 'westend';225export type TNetworks = TUniqueNetworks | TSiblingNetworkds | TRelayNetworks;226export type TSigner = IKeyringPair; // | 'string'
modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -7,9 +7,11 @@
 
 import {ApiPromise, WsProvider, Keyring} from '@polkadot/api';
 import {ApiInterfaceEvents, SignerOptions} from '@polkadot/api/types';
-import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm} from '@polkadot/util-crypto';
+import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm, base58Encode, blake2AsU8a} from '@polkadot/util-crypto';
 import {IKeyringPair} from '@polkadot/types/types';
-import {IApiListeners, IBlock, IEvent, IChainProperties, ICollectionCreationOptions, ICollectionLimits, ICollectionPermissions, ICrossAccountId, ICrossAccountIdLower, ILogger, INestingPermissions, IProperty, IStakingInfo, ISchedulerOptions, ISubstrateBalance, IToken, ITokenPropertyPermission, ITransactionResult, IUniqueHelperLog, TApiAllowedListeners, TEthereumAccount, TSigner, TSubstrateAccount, IForeignAssetMetadata, TNetworks, MoonbeamAssetInfo, DemocracyStandardAccountVote, AcalaAssetMetadata} from './types';
+import {IApiListeners, IBlock, IEvent, IChainProperties, ICollectionCreationOptions, ICollectionLimits, ICollectionPermissions, ICrossAccountId, ICrossAccountIdLower, ILogger, INestingPermissions, IProperty, IStakingInfo, ISchedulerOptions, ISubstrateBalance, IToken, ITokenPropertyPermission, ITransactionResult, IUniqueHelperLog, TApiAllowedListeners, TEthereumAccount, TSigner, TSubstrateAccount, IForeignAssetMetadata, TNetworks, MoonbeamAssetInfo, DemocracyStandardAccountVote, AcalaAssetMetadata, IEthCrossAccountId} from './types';
+import {hexToU8a} from '@polkadot/util/hex';
+import {u8aConcat} from '@polkadot/util/u8a';
 
 export class CrossAccountId implements ICrossAccountId {
   Substrate?: TSubstrateAccount;
@@ -2309,6 +2311,73 @@
 
     return siblingPrefix + encodedParaId + suffix;
   }
+
+  /**
+   * Encode key to substrate address
+   * @param key key for encoding address
+   * @param ss58Format prefix for encoding to the address of the corresponding network
+   * @returns encoded substrate address
+   */
+  encodeSubstrateAddress (key: Uint8Array | string | bigint, ss58Format = 42): string {
+    const u8a :Uint8Array = typeof key === 'string'
+      ? hexToU8a(key)
+      : typeof key === 'bigint'
+        ? hexToU8a(key.toString(16))
+        : key;
+  
+    if (ss58Format < 0 || ss58Format > 16383 || [46, 47].includes(ss58Format)) {
+      throw new Error(`ss58Format is not valid, received ${typeof ss58Format} "${ss58Format}"`);
+    }
+  
+    const allowedDecodedLengths = [1, 2, 4, 8, 32, 33];
+    if (!allowedDecodedLengths.includes(u8a.length)) {
+      throw new Error(`key length is not valid, received ${u8a.length}, valid values are ${allowedDecodedLengths.join(', ')}`);
+    }
+  
+    const u8aPrefix = ss58Format < 64
+      ? new Uint8Array([ss58Format])
+      : new Uint8Array([
+        ((ss58Format & 0xfc) >> 2) | 0x40,
+        (ss58Format >> 8) | ((ss58Format & 0x03) << 6),
+      ]);
+
+    const input = u8aConcat(u8aPrefix, u8a);
+  
+    return base58Encode(u8aConcat(
+      input,
+      blake2AsU8a(input).subarray(0, [32, 33].includes(u8a.length) ? 2 : 1),
+    ));
+  }
+
+  /**
+   * Restore substrate address from bigint representation
+   * @param number decimal representation of substrate address
+   * @returns substrate address
+   */
+  restoreCrossAccountFromBigInt(number: bigint): TSubstrateAccount {
+    if (this.helper.api === null) {
+      throw 'Not connected';
+    }
+    const res = this.helper.api.registry.createType('AccountId', '0x' + number.toString(16).padStart(64, '0')).toJSON();
+    if (res === undefined || res === null) {
+      throw 'Restore address error';
+    }
+    return res.toString();
+  }
+
+  /**
+   * Convert etherium cross account id to substrate cross account id
+   * @param ethCrossAccount etherium cross account
+   * @returns substrate cross account id
+   */
+  convertCrossAccountFromEthCrossAcoount(ethCrossAccount: IEthCrossAccountId): ICrossAccountId {
+    if (ethCrossAccount.field_1 === '0') {
+      return {Ethereum: ethCrossAccount.field_0.toLocaleLowerCase()};
+    }
+    
+    const ss58 = this.restoreCrossAccountFromBigInt(BigInt(ethCrossAccount.field_1));
+    return {Substrate: ss58};
+  }
 }
 
 class StakingGroup extends HelperGroup<UniqueHelper> {