difftreelog
Merge pull request #391 from UniqueNetwork/feature/CORE-412
in: master
Feature/core 412 Helper for create RFT collection
40 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5731,7 +5731,7 @@
[[package]]
name = "pallet-common"
-version = "0.1.2"
+version = "0.1.3"
dependencies = [
"ethereum",
"evm-coder",
@@ -6198,7 +6198,7 @@
[[package]]
name = "pallet-nonfungible"
-version = "0.1.1"
+version = "0.1.2"
dependencies = [
"ethereum",
"evm-coder",
@@ -6638,7 +6638,7 @@
[[package]]
name = "pallet-unique"
-version = "0.1.0"
+version = "0.1.1"
dependencies = [
"ethereum",
"evm-coder",
@@ -6649,6 +6649,7 @@
"pallet-evm",
"pallet-evm-coder-substrate",
"pallet-nonfungible",
+ "pallet-refungible",
"parity-scale-codec 3.1.5",
"scale-info",
"serde",
@@ -12736,7 +12737,7 @@
[[package]]
name = "up-data-structs"
-version = "0.1.1"
+version = "0.1.2"
dependencies = [
"derivative",
"frame-support",
Makefilediffbeforeafterboth--- a/Makefile
+++ b/Makefile
@@ -9,10 +9,12 @@
FUNGIBLE_EVM_STUBS=./pallets/fungible/src/stubs
FUNGIBLE_EVM_ABI=./tests/src/eth/fungibleAbi.json
+REFUNGIBLE_EVM_STUBS=./pallets/refungible/src/stubs
+REFUNGIBLE_EVM_ABI=./tests/src/eth/refungibleAbi.json
+
NONFUNGIBLE_EVM_STUBS=./pallets/nonfungible/src/stubs
NONFUNGIBLE_EVM_ABI=./tests/src/eth/nonFungibleAbi.json
-REFUNGIBLE_EVM_STUBS=./pallets/refungible/src/stubs
RENFUNGIBLE_EVM_ABI=./tests/src/eth/reFungibleAbi.json
RENFUNGIBLE_TOKEN_EVM_ABI=./tests/src/eth/reFungibleTokenAbi.json
@@ -25,7 +27,7 @@
TESTS_API=./tests/src/eth/api/
.PHONY: regenerate_solidity
-regenerate_solidity: UniqueFungible.sol UniqueNFT.sol UniqueRefungibleToken.sol ContractHelpers.sol CollectionHelpers.sol
+regenerate_solidity: UniqueFungible.sol UniqueNFT.sol UniqueRefungible.sol UniqueRefungibleToken.sol ContractHelpers.sol CollectionHelpers.sol
UniqueFungible.sol:
PACKAGE=pallet-fungible NAME=erc::gen_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh
@@ -35,6 +37,10 @@
PACKAGE=pallet-nonfungible NAME=erc::gen_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh
PACKAGE=pallet-nonfungible NAME=erc::gen_impl OUTPUT=$(NONFUNGIBLE_EVM_STUBS)/$@ ./.maintain/scripts/generate_sol.sh
+UniqueRefungible.sol:
+ PACKAGE=pallet-refungible NAME=erc::gen_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh
+ PACKAGE=pallet-refungible NAME=erc::gen_impl OUTPUT=$(REFUNGIBLE_EVM_STUBS)/$@ ./.maintain/scripts/generate_sol.sh
+
UniqueRefungibleToken.sol:
PACKAGE=pallet-refungible NAME=erc_token::gen_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh
PACKAGE=pallet-refungible NAME=erc_token::gen_impl OUTPUT=$(REFUNGIBLE_EVM_STUBS)/$@ ./.maintain/scripts/generate_sol.sh
@@ -59,6 +65,10 @@
INPUT=$(REFUNGIBLE_EVM_STUBS)/$< OUTPUT=$(REFUNGIBLE_EVM_STUBS)/UniqueRefungibleToken.raw ./.maintain/scripts/compile_stub.sh
INPUT=$(REFUNGIBLE_EVM_STUBS)/$< OUTPUT=$(RENFUNGIBLE_TOKEN_EVM_ABI) ./.maintain/scripts/generate_abi.sh
+UniqueRefungible: UniqueRefungible.sol
+ INPUT=$(REFUNGIBLE_EVM_STUBS)/$< OUTPUT=$(REFUNGIBLE_EVM_STUBS)/UniqueRefungible.raw ./.maintain/scripts/compile_stub.sh
+ INPUT=$(REFUNGIBLE_EVM_STUBS)/$< OUTPUT=$(REFUNGIBLE_EVM_ABI) ./.maintain/scripts/generate_abi.sh
+
ContractHelpers: ContractHelpers.sol
INPUT=$(CONTRACT_HELPERS_STUBS)/$< OUTPUT=$(CONTRACT_HELPERS_STUBS)/ContractHelpers.raw ./.maintain/scripts/compile_stub.sh
INPUT=$(CONTRACT_HELPERS_STUBS)/$< OUTPUT=$(CONTRACT_HELPERS_ABI) ./.maintain/scripts/generate_abi.sh
@@ -67,7 +77,7 @@
INPUT=$(COLLECTION_HELPER_STUBS)/$< OUTPUT=$(COLLECTION_HELPER_STUBS)/CollectionHelpers.raw ./.maintain/scripts/compile_stub.sh
INPUT=$(COLLECTION_HELPER_STUBS)/$< OUTPUT=$(COLLECTION_HELPER_ABI) ./.maintain/scripts/generate_abi.sh
-evm_stubs: UniqueFungible UniqueNFT UniqueRefungibleToken ContractHelpers CollectionHelpers
+evm_stubs: UniqueFungible UniqueNFT UniqueRefungible UniqueRefungibleToken ContractHelpers CollectionHelpers
.PHONY: _bench
_bench:
pallets/common/CHANGELOG.MDdiffbeforeafterboth--- a/pallets/common/CHANGELOG.MD
+++ b/pallets/common/CHANGELOG.MD
@@ -2,6 +2,10 @@
All notable changes to this project will be documented in this file.
+## [0.1.3] - 2022-07-25
+### Add
+- Some static property keys and values.
+
## [0.1.2] - 2022-07-20
### Fixed
pallets/common/Cargo.tomldiffbeforeafterboth--- a/pallets/common/Cargo.toml
+++ b/pallets/common/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "pallet-common"
-version = "0.1.2"
+version = "0.1.3"
license = "GPLv3"
edition = "2021"
pallets/common/src/dispatch.rsdiffbeforeafterboth--- a/pallets/common/src/dispatch.rs
+++ b/pallets/common/src/dispatch.rs
@@ -8,6 +8,7 @@
weights::Pays,
traits::Get,
};
+use sp_runtime::DispatchError;
use up_data_structs::{CollectionId, CreateCollectionData};
use crate::{pallet::Config, CommonCollectionOperations, CollectionHandle};
@@ -78,7 +79,7 @@
fn create(
sender: T::CrossAccountId,
data: CreateCollectionData<T::AccountId>,
- ) -> DispatchResult;
+ ) -> Result<CollectionId, DispatchError>;
/// Delete the collection.
///
pallets/common/src/erc.rsdiffbeforeafterboth--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -430,10 +430,70 @@
Ok(())
}
-/// Get the "tokenURI" key as [PropertyKey](up_data_structs::PropertyKey).
-pub fn token_uri_key() -> up_data_structs::PropertyKey {
- b"tokenURI"
- .to_vec()
- .try_into()
- .expect("length < limit; qed")
+/// Contains static property keys and values.
+pub mod static_property {
+ use evm_coder::{
+ execution::{Result, Error},
+ };
+ use alloc::format;
+
+ const EXPECT_CONVERT_ERROR: &str = "length < limit";
+
+ /// Keys.
+ pub mod key {
+ use super::*;
+
+ /// Key "schemaName".
+ pub fn schema_name() -> up_data_structs::PropertyKey {
+ property_key_from_bytes(b"schemaName").expect(EXPECT_CONVERT_ERROR)
+ }
+
+ /// Key "baseURI".
+ pub fn base_uri() -> up_data_structs::PropertyKey {
+ property_key_from_bytes(b"baseURI").expect(EXPECT_CONVERT_ERROR)
+ }
+
+ /// Key "url".
+ pub fn url() -> up_data_structs::PropertyKey {
+ property_key_from_bytes(b"url").expect(EXPECT_CONVERT_ERROR)
+ }
+
+ /// Key "suffix".
+ pub fn suffix() -> up_data_structs::PropertyKey {
+ property_key_from_bytes(b"suffix").expect(EXPECT_CONVERT_ERROR)
+ }
+ }
+
+ /// Values.
+ pub mod value {
+ use super::*;
+
+ /// Value "ERC721Metadata".
+ pub const ERC721_METADATA: &[u8] = b"ERC721Metadata";
+
+ /// Value for [`ERC721_METADATA`].
+ pub fn erc721() -> up_data_structs::PropertyValue {
+ property_value_from_bytes(ERC721_METADATA).expect(EXPECT_CONVERT_ERROR)
+ }
+ }
+
+ /// Convert `byte` to [`PropertyKey`].
+ pub fn property_key_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyKey> {
+ bytes.to_vec().try_into().map_err(|_| {
+ Error::Revert(format!(
+ "Property key is too long. Max length is {}.",
+ up_data_structs::PropertyKey::bound()
+ ))
+ })
+ }
+
+ /// Convert `bytes` to [`PropertyValue`].
+ pub fn property_value_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyValue> {
+ bytes.to_vec().try_into().map_err(|_| {
+ Error::Revert(format!(
+ "Property key is too long. Max length is {}.",
+ up_data_structs::PropertyKey::bound()
+ ))
+ })
+ }
}
pallets/nonfungible/CHANGELOG.mddiffbeforeafterboth223All notable changes to this project will be documented in this file.3All notable changes to this project will be documented in this file.445## [0.1.2] - 2022-07-256### Changed7- New `token_uri` retrieval logic:89 If the collection has a `url` property and it is not empty, it is returned.10 Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.1112 If the property `baseURI` is empty or absent, return "" (empty string)13 otherwise, if property `suffix` present and is non-empty, return concatenation of baseURI and suffix14 otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).155## [0.1.1] - 2022-07-1416## [0.1.1] - 2022-07-1467### Added17### Added8189- Implementation of RPC method `token_owners`.19- Implementation of RPC method `token_owners`.10 For reasons of compatibility with this pallet, returns only one owner if token exists.20 For reasons of compatibility with this pallet, returns only one owner if token exists.11 This was an internal request to improve the web interface and support fractionalization event. 21 This was an internal request to improve the web interface and support fractionalization event. 22pallets/nonfungible/Cargo.tomldiffbeforeafterboth--- a/pallets/nonfungible/Cargo.toml
+++ b/pallets/nonfungible/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "pallet-nonfungible"
-version = "0.1.1"
+version = "0.1.2"
license = "GPLv3"
edition = "2021"
pallets/nonfungible/src/erc.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -33,12 +33,16 @@
use pallet_evm_coder_substrate::dispatch_to_evm;
use sp_std::vec::Vec;
use pallet_common::{
- erc::{CommonEvmHandler, PrecompileResult, CollectionCall, token_uri_key},
+ erc::{
+ CommonEvmHandler, PrecompileResult, CollectionCall,
+ static_property::{key, value as property_value},
+ },
CollectionHandle, CollectionPropertyPermissions,
};
use pallet_evm::{account::CrossAccountId, PrecompileHandle};
use pallet_evm_coder_substrate::call;
use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};
+use alloc::string::ToString;
use crate::{
AccountBalance, Config, CreateItemData, NonfungibleHandle, Pallet, TokenData, TokensMinted,
@@ -212,27 +216,47 @@
}
/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
- /// @dev Throws if `tokenId` is not a valid NFT. URIs are defined in RFC
- /// 3986. The URI may point to a JSON file that conforms to the "ERC721
- /// Metadata JSON Schema".
+ ///
+ /// @dev If the token has a `url` property and it is not empty, it is returned.
+ /// Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
+ /// If the collection property `baseURI` is empty or absent, return "" (empty string)
+ /// otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
+ /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
+ ///
/// @return token's const_metadata
#[solidity(rename_selector = "tokenURI")]
fn token_uri(&self, token_id: uint256) -> Result<string> {
- let key = token_uri_key();
- if !has_token_permission::<T>(self.id, &key) {
- return Err("No tokenURI permission".into());
+ let token_id_u32: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
+
+ if let Ok(url) = get_token_property(self, token_id_u32, &key::url()) {
+ if !url.is_empty() {
+ return Ok(url);
+ }
+ } else if !is_erc721_metadata_compatible::<T>(self.id) {
+ return Err("tokenURI not set".into());
}
- self.consume_store_reads(1)?;
- let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
+ if let Some(base_uri) =
+ pallet_common::Pallet::<T>::get_collection_property(self.id, &key::base_uri())
+ {
+ if !base_uri.is_empty() {
+ let base_uri = string::from_utf8(base_uri.into_inner()).map_err(|e| {
+ Error::Revert(alloc::format!(
+ "Can not convert value \"baseURI\" to string with error \"{}\"",
+ e
+ ))
+ })?;
+ if let Ok(suffix) = get_token_property(self, token_id_u32, &key::suffix()) {
+ if !suffix.is_empty() {
+ return Ok(base_uri + suffix.as_str());
+ }
+ }
- let properties = <TokenProperties<T>>::try_get((self.id, token_id))
- .map_err(|_| Error::Revert("Token properties not found".into()))?;
- if let Some(property) = properties.get(&key) {
- return Ok(string::from_utf8_lossy(property).into());
+ return Ok(base_uri + token_id.to_string().as_str());
+ }
}
- Err("Property tokenURI not found".into())
+ Ok("".into())
}
}
@@ -469,7 +493,7 @@
token_id: uint256,
token_uri: string,
) -> Result<bool> {
- let key = token_uri_key();
+ let key = key::url();
let permission = get_token_permission::<T>(self.id, &key)?;
if !permission.collection_admin {
return Err("Operation is not allowed".into());
@@ -520,6 +544,32 @@
}
}
+fn get_token_property<T: Config>(
+ collection: &CollectionHandle<T>,
+ token_id: u32,
+ key: &up_data_structs::PropertyKey,
+) -> Result<string> {
+ collection.consume_store_reads(1)?;
+ let properties = <TokenProperties<T>>::try_get((collection.id, token_id))
+ .map_err(|_| Error::Revert("Token properties not found".into()))?;
+ if let Some(property) = properties.get(key) {
+ return Ok(string::from_utf8_lossy(property).into());
+ }
+
+ Err("Property tokenURI not found".into())
+}
+
+fn is_erc721_metadata_compatible<T: Config>(collection_id: CollectionId) -> bool {
+ if let Some(shema_name) =
+ pallet_common::Pallet::<T>::get_collection_property(collection_id, &key::schema_name())
+ {
+ let shema_name = shema_name.into_inner();
+ shema_name == property_value::ERC721_METADATA
+ } else {
+ false
+ }
+}
+
fn get_token_permission<T: Config>(
collection_id: CollectionId,
key: &PropertyKey,
@@ -528,8 +578,11 @@
.map_err(|_| Error::Revert("No permissions for collection".into()))?;
let a = token_property_permissions
.get(key)
- .map(|p| p.clone())
- .ok_or_else(|| Error::Revert("No permission".into()))?;
+ .map(Clone::clone)
+ .ok_or_else(|| {
+ let key = string::from_utf8(key.clone().into_inner()).unwrap_or_default();
+ Error::Revert(alloc::format!("No permission for key {}", key))
+ })?;
Ok(a)
}
@@ -656,7 +709,7 @@
to: address,
tokens: Vec<(uint256, string)>,
) -> Result<bool> {
- let key = token_uri_key();
+ let key = key::url();
let caller = T::CrossAccountId::from_eth(caller);
let to = T::CrossAccountId::from_eth(to);
let mut expected_index = <TokensMinted<T>>::get(self.id)
pallets/nonfungible/src/stubs/UniqueNFT.rawdiffbeforeafterbothbinary blob — no preview
pallets/refungible/CHANGELOG.mddiffbeforeafterboth--- a/pallets/refungible/CHANGELOG.md
+++ b/pallets/refungible/CHANGELOG.md
@@ -10,6 +10,8 @@
test(refungible-pallet): add tests for ERC-20 EVM API for RFT token pieces ([#413](https://github.com/UniqueNetwork/unique-chain/pull/413))
## [v0.1.1] - 2022-07-14
+### Added
+- Support for properties for RFT collections and tokens.
### Other changes
pallets/refungible/Changelog.mddiffbeforeafterboth--- a/pallets/refungible/Changelog.md
+++ /dev/null
@@ -1,3 +0,0 @@
-### 0.1.1
----
-* Added support for properties for RFT collections and tokens.
pallets/refungible/src/erc.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -25,14 +25,14 @@
use crate::{Config, RefungibleHandle};
#[solidity_interface(
- name = "UniqueRFT",
+ name = "UniqueRefungible",
is(via("CollectionHandle<T>", common_mut, Collection),)
)]
impl<T: Config> RefungibleHandle<T> where T::AccountId: From<[u8; 32]> {}
// Not a tests, but code generators
-generate_stubgen!(gen_impl, UniqueRFTCall<()>, true);
-generate_stubgen!(gen_iface, UniqueRFTCall<()>, false);
+generate_stubgen!(gen_impl, UniqueRefungibleCall<()>, true);
+generate_stubgen!(gen_iface, UniqueRefungibleCall<()>, false);
impl<T: Config> CommonEvmHandler for RefungibleHandle<T>
where
@@ -43,6 +43,6 @@
self,
handle: &mut impl PrecompileHandle,
) -> Option<pallet_common::erc::PrecompileResult> {
- call::<T, UniqueRFTCall<T>, _, _>(handle, self)
+ call::<T, UniqueRefungibleCall<T>, _, _>(handle, self)
}
}
pallets/refungible/src/stubs/UniqueRefungible.rawdiffbeforeafterbothbinary blob — no preview
pallets/refungible/src/stubs/UniqueRefungible.soldiffbeforeafterboth--- /dev/null
+++ b/pallets/refungible/src/stubs/UniqueRefungible.sol
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: OTHER
+// This code is automatically generated
+
+pragma solidity >=0.8.0 <0.9.0;
+
+// Common stubs holder
+contract Dummy {
+ uint8 dummy;
+ string stub_error = "this contract is implemented in native";
+}
+
+contract ERC165 is Dummy {
+ function supportsInterface(bytes4 interfaceID)
+ external
+ view
+ returns (bool)
+ {
+ require(false, stub_error);
+ interfaceID;
+ return true;
+ }
+}
+
+// Selector: 7d9262e6
+contract Collection is Dummy, ERC165 {
+ // Set collection property.
+ //
+ // @param key Property key.
+ // @param value Propery value.
+ //
+ // Selector: setCollectionProperty(string,bytes) 2f073f66
+ function setCollectionProperty(string memory key, bytes memory value)
+ public
+ {
+ require(false, stub_error);
+ key;
+ value;
+ dummy = 0;
+ }
+
+ // Delete collection property.
+ //
+ // @param key Property key.
+ //
+ // Selector: deleteCollectionProperty(string) 7b7debce
+ function deleteCollectionProperty(string memory key) public {
+ require(false, stub_error);
+ key;
+ dummy = 0;
+ }
+
+ // Get collection property.
+ //
+ // @dev Throws error if key not found.
+ //
+ // @param key Property key.
+ // @return bytes The property corresponding to the key.
+ //
+ // Selector: collectionProperty(string) cf24fd6d
+ function collectionProperty(string memory key)
+ public
+ view
+ returns (bytes memory)
+ {
+ require(false, stub_error);
+ key;
+ dummy;
+ return hex"";
+ }
+
+ // Set the sponsor of the collection.
+ //
+ // @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+ //
+ // @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
+ //
+ // Selector: setCollectionSponsor(address) 7623402e
+ function setCollectionSponsor(address sponsor) public {
+ require(false, stub_error);
+ sponsor;
+ dummy = 0;
+ }
+
+ // Collection sponsorship confirmation.
+ //
+ // @dev After setting the sponsor for the collection, it must be confirmed with this function.
+ //
+ // Selector: confirmCollectionSponsorship() 3c50e97a
+ function confirmCollectionSponsorship() public {
+ require(false, stub_error);
+ dummy = 0;
+ }
+
+ // Set limits for the collection.
+ // @dev Throws error if limit not found.
+ // @param limit Name of the limit. Valid names:
+ // "accountTokenOwnershipLimit",
+ // "sponsoredDataSize",
+ // "sponsoredDataRateLimit",
+ // "tokenLimit",
+ // "sponsorTransferTimeout",
+ // "sponsorApproveTimeout"
+ // @param value Value of the limit.
+ //
+ // Selector: setCollectionLimit(string,uint32) 6a3841db
+ function setCollectionLimit(string memory limit, uint32 value) public {
+ require(false, stub_error);
+ limit;
+ value;
+ dummy = 0;
+ }
+
+ // Set limits for the collection.
+ // @dev Throws error if limit not found.
+ // @param limit Name of the limit. Valid names:
+ // "ownerCanTransfer",
+ // "ownerCanDestroy",
+ // "transfersEnabled"
+ // @param value Value of the limit.
+ //
+ // Selector: setCollectionLimit(string,bool) 993b7fba
+ function setCollectionLimit(string memory limit, bool value) public {
+ require(false, stub_error);
+ limit;
+ value;
+ dummy = 0;
+ }
+
+ // Get contract address.
+ //
+ // Selector: contractAddress() f6b4dfb4
+ function contractAddress() public view returns (address) {
+ require(false, stub_error);
+ dummy;
+ return 0x0000000000000000000000000000000000000000;
+ }
+
+ // Add collection admin by substrate address.
+ // @param new_admin Substrate administrator address.
+ //
+ // Selector: addCollectionAdminSubstrate(uint256) 5730062b
+ function addCollectionAdminSubstrate(uint256 newAdmin) public {
+ require(false, stub_error);
+ newAdmin;
+ dummy = 0;
+ }
+
+ // Remove collection admin by substrate address.
+ // @param admin Substrate administrator address.
+ //
+ // Selector: removeCollectionAdminSubstrate(uint256) 4048fcf9
+ function removeCollectionAdminSubstrate(uint256 admin) public {
+ require(false, stub_error);
+ admin;
+ dummy = 0;
+ }
+
+ // Add collection admin.
+ // @param new_admin Address of the added administrator.
+ //
+ // Selector: addCollectionAdmin(address) 92e462c7
+ function addCollectionAdmin(address newAdmin) public {
+ require(false, stub_error);
+ newAdmin;
+ dummy = 0;
+ }
+
+ // Remove collection admin.
+ //
+ // @param new_admin Address of the removed administrator.
+ //
+ // Selector: removeCollectionAdmin(address) fafd7b42
+ function removeCollectionAdmin(address admin) public {
+ require(false, stub_error);
+ admin;
+ dummy = 0;
+ }
+
+ // Toggle accessibility of collection nesting.
+ //
+ // @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
+ //
+ // Selector: setCollectionNesting(bool) 112d4586
+ function setCollectionNesting(bool enable) public {
+ require(false, stub_error);
+ enable;
+ dummy = 0;
+ }
+
+ // Toggle accessibility of collection nesting.
+ //
+ // @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
+ // @param collections Addresses of collections that will be available for nesting.
+ //
+ // Selector: setCollectionNesting(bool,address[]) 64872396
+ function setCollectionNesting(bool enable, address[] memory collections)
+ public
+ {
+ require(false, stub_error);
+ enable;
+ collections;
+ dummy = 0;
+ }
+
+ // Set the collection access method.
+ // @param mode Access mode
+ // 0 for Normal
+ // 1 for AllowList
+ //
+ // Selector: setCollectionAccess(uint8) 41835d4c
+ function setCollectionAccess(uint8 mode) public {
+ require(false, stub_error);
+ mode;
+ dummy = 0;
+ }
+
+ // Add the user to the allowed list.
+ //
+ // @param user Address of a trusted user.
+ //
+ // Selector: addToCollectionAllowList(address) 67844fe6
+ function addToCollectionAllowList(address user) public {
+ require(false, stub_error);
+ user;
+ dummy = 0;
+ }
+
+ // Remove the user from the allowed list.
+ //
+ // @param user Address of a removed user.
+ //
+ // Selector: removeFromCollectionAllowList(address) 85c51acb
+ function removeFromCollectionAllowList(address user) public {
+ require(false, stub_error);
+ user;
+ dummy = 0;
+ }
+
+ // Switch permission for minting.
+ //
+ // @param mode Enable if "true".
+ //
+ // Selector: setCollectionMintMode(bool) 00018e84
+ function setCollectionMintMode(bool mode) public {
+ require(false, stub_error);
+ mode;
+ dummy = 0;
+ }
+}
+
+contract UniqueRefungible is Dummy, ERC165, Collection {}
pallets/refungible/src/stubs/UniqueRefungibleToken.rawdiffbeforeafterbothbinary blob — no preview
pallets/refungible/src/stubs/UniqueRefungibleToken.soldiffbeforeafterboth--- a/pallets/refungible/src/stubs/UniqueRefungibleToken.sol
+++ b/pallets/refungible/src/stubs/UniqueRefungibleToken.sol
@@ -33,6 +33,8 @@
// Selector: 942e8b22
contract ERC20 is Dummy, ERC165, ERC20Events {
+ // @return the name of the token.
+ //
// Selector: name() 06fdde03
function name() public view returns (string memory) {
require(false, stub_error);
@@ -40,6 +42,8 @@
return "";
}
+ // @return the symbol of the token.
+ //
// Selector: symbol() 95d89b41
function symbol() public view returns (string memory) {
require(false, stub_error);
@@ -47,6 +51,8 @@
return "";
}
+ // @dev Total number of tokens in existence
+ //
// Selector: totalSupply() 18160ddd
function totalSupply() public view returns (uint256) {
require(false, stub_error);
@@ -54,6 +60,8 @@
return 0;
}
+ // @dev Not supported
+ //
// Selector: decimals() 313ce567
function decimals() public view returns (uint8) {
require(false, stub_error);
@@ -61,6 +69,10 @@
return 0;
}
+ // @dev Gets the balance of the specified address.
+ // @param owner The address to query the balance of.
+ // @return An uint256 representing the amount owned by the passed address.
+ //
// Selector: balanceOf(address) 70a08231
function balanceOf(address owner) public view returns (uint256) {
require(false, stub_error);
@@ -69,6 +81,10 @@
return 0;
}
+ // @dev Transfer token for a specified address
+ // @param to The address to transfer to.
+ // @param amount The amount to be transferred.
+ //
// Selector: transfer(address,uint256) a9059cbb
function transfer(address to, uint256 amount) public returns (bool) {
require(false, stub_error);
@@ -78,6 +94,11 @@
return false;
}
+ // @dev Transfer tokens from one address to another
+ // @param from address The address which you want to send tokens from
+ // @param to address The address which you want to transfer to
+ // @param amount uint256 the amount of tokens to be transferred
+ //
// Selector: transferFrom(address,address,uint256) 23b872dd
function transferFrom(
address from,
@@ -92,6 +113,14 @@
return false;
}
+ // @dev Approve the passed address to spend the specified amount of tokens on behalf of `msg.sender`.
+ // Beware that changing an allowance with this method brings the risk that someone may use both the old
+ // and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
+ // race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
+ // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
+ // @param spender The address which will spend the funds.
+ // @param amount The amount of tokens to be spent.
+ //
// Selector: approve(address,uint256) 095ea7b3
function approve(address spender, uint256 amount) public returns (bool) {
require(false, stub_error);
@@ -101,6 +130,11 @@
return false;
}
+ // @dev Function to check the amount of tokens that an owner allowed to a spender.
+ // @param owner address The address which owns the funds.
+ // @param spender address The address which will spend the funds.
+ // @return A uint256 specifying the amount of tokens still available for the spender.
+ //
// Selector: allowance(address,address) dd62ed3e
function allowance(address owner, address spender)
public
@@ -117,6 +151,11 @@
// Selector: ab8deb37
contract ERC20UniqueExtensions is Dummy, ERC165 {
+ // @dev Function that burns an amount of the token of a given account,
+ // deducting from the sender's allowance for said account.
+ // @param from The account whose tokens will be burnt.
+ // @param amount The amount that will be burnt.
+ //
// Selector: burnFrom(address,uint256) 79cc6790
function burnFrom(address from, uint256 amount) public returns (bool) {
require(false, stub_error);
@@ -126,6 +165,10 @@
return false;
}
+ // @dev Function that changes total amount of the tokens.
+ // Throws if `msg.sender` doesn't owns all of the tokens.
+ // @param amount New total amount of the tokens.
+ //
// Selector: repartition(uint256) d2418ca7
function repartition(uint256 amount) public returns (bool) {
require(false, stub_error);
pallets/unique/CHANGELOG.mddiffbeforeafterboth--- /dev/null
+++ b/pallets/unique/CHANGELOG.md
@@ -0,0 +1,9 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+
+## [v0.1.1] - 2022-07-25
+### Added
+- Method for creating `ERC721Metadata` compatible NFT collection.
+- Method for creating `ERC721Metadata` compatible ReFungible collection.
+- Method for creating ReFungible collection.
pallets/unique/Cargo.tomldiffbeforeafterboth--- a/pallets/unique/Cargo.toml
+++ b/pallets/unique/Cargo.toml
@@ -9,7 +9,7 @@
license = 'GPLv3'
name = 'pallet-unique'
repository = 'https://github.com/UniqueNetwork/unique-chain'
-version = '0.1.0'
+version = '0.1.1'
[package.metadata.docs.rs]
targets = ['x86_64-unknown-linux-gnu']
@@ -103,3 +103,4 @@
evm-coder = { default-features = false, path = '../../crates/evm-coder' }
pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' }
pallet-nonfungible = { default-features = false, path = '../../pallets/nonfungible' }
+pallet-refungible = { default-features = false, path = '../../pallets/refungible' }
pallets/unique/src/eth/mod.rsdiffbeforeafterboth--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -17,18 +17,22 @@
//! Implementation of CollectionHelpers contract.
use core::marker::PhantomData;
-use evm_coder::{execution::*, generate_stubgen, solidity_interface, weight, types::*};
+use evm_coder::{execution::*, generate_stubgen, solidity_interface, solidity, weight, types::*};
use ethereum as _;
use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};
use pallet_evm::{OnMethodCall, PrecompileResult, account::CrossAccountId, PrecompileHandle};
use up_data_structs::{
- CreateCollectionData, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,
- MAX_COLLECTION_NAME_LENGTH,
+ CollectionName, CollectionDescription, CollectionTokenPrefix, CreateCollectionData,
+ CollectionMode, PropertyValue,
};
use frame_support::traits::Get;
use pallet_common::{
CollectionById,
- erc::{token_uri_key, CollectionHelpersEvents},
+ erc::{
+ static_property::{key, value as property_value},
+ CollectionHelpersEvents,
+ },
+ dispatch::CollectionDispatch,
};
use crate::{SelfWeightOf, Config, weights::WeightInfo};
@@ -47,9 +51,112 @@
}
}
+fn convert_data<T: Config>(
+ caller: caller,
+ name: string,
+ description: string,
+ token_prefix: string,
+ base_uri: string,
+) -> Result<(
+ T::CrossAccountId,
+ CollectionName,
+ CollectionDescription,
+ CollectionTokenPrefix,
+ PropertyValue,
+)> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let name = name
+ .encode_utf16()
+ .collect::<Vec<u16>>()
+ .try_into()
+ .map_err(|_| error_field_too_long(stringify!(name), CollectionName::bound()))?;
+ let description = description
+ .encode_utf16()
+ .collect::<Vec<u16>>()
+ .try_into()
+ .map_err(|_| {
+ error_field_too_long(stringify!(description), CollectionDescription::bound())
+ })?;
+ let token_prefix = token_prefix.into_bytes().try_into().map_err(|_| {
+ error_field_too_long(stringify!(token_prefix), CollectionTokenPrefix::bound())
+ })?;
+ let base_uri_value = base_uri
+ .into_bytes()
+ .try_into()
+ .map_err(|_| error_field_too_long(stringify!(token_prefix), PropertyValue::bound()))?;
+ Ok((caller, name, description, token_prefix, base_uri_value))
+}
+
+fn make_data<T: Config>(
+ name: CollectionName,
+ mode: CollectionMode,
+ description: CollectionDescription,
+ token_prefix: CollectionTokenPrefix,
+ base_uri_value: PropertyValue,
+ add_properties: bool,
+) -> Result<CreateCollectionData<T::AccountId>> {
+ let mut properties = up_data_structs::CollectionPropertiesVec::default();
+ let mut token_property_permissions =
+ up_data_structs::CollectionPropertiesPermissionsVec::default();
+
+ token_property_permissions
+ .try_push(up_data_structs::PropertyKeyPermission {
+ key: key::url(),
+ permission: up_data_structs::PropertyPermission {
+ mutable: false,
+ collection_admin: true,
+ token_owner: false,
+ },
+ })
+ .map_err(|e| Error::Revert(format!("{:?}", e)))?;
+
+ if add_properties {
+ token_property_permissions
+ .try_push(up_data_structs::PropertyKeyPermission {
+ key: key::suffix(),
+ permission: up_data_structs::PropertyPermission {
+ mutable: false,
+ collection_admin: true,
+ token_owner: false,
+ },
+ })
+ .map_err(|e| Error::Revert(format!("{:?}", e)))?;
+
+ properties
+ .try_push(up_data_structs::Property {
+ key: key::schema_name(),
+ value: property_value::erc721(),
+ })
+ .map_err(|e| Error::Revert(format!("{:?}", e)))?;
+
+ if !base_uri_value.is_empty() {
+ properties
+ .try_push(up_data_structs::Property {
+ key: key::base_uri(),
+ value: base_uri_value,
+ })
+ .map_err(|e| Error::Revert(format!("{:?}", e)))?;
+ }
+ }
+
+ let data = CreateCollectionData {
+ name,
+ mode,
+ description,
+ token_prefix,
+ token_property_permissions,
+ properties,
+ ..Default::default()
+ };
+ Ok(data)
+}
+
/// @title Contract, which allows users to operate with collections
#[solidity_interface(name = "CollectionHelpers", events(CollectionHelpersEvents))]
-impl<T: Config + pallet_nonfungible::Config> EvmCollectionHelpers<T> {
+impl<T> EvmCollectionHelpers<T>
+where
+ T: Config + pallet_nonfungible::Config + pallet_refungible::Config,
+{
/// Create an NFT collection
/// @param name Name of the collection
/// @param description Informative description of the collection
@@ -63,47 +170,97 @@
description: string,
token_prefix: string,
) -> Result<address> {
- let caller = T::CrossAccountId::from_eth(caller);
- let name = name
- .encode_utf16()
- .collect::<Vec<u16>>()
- .try_into()
- .map_err(|_| error_feild_too_long(stringify!(name), MAX_COLLECTION_NAME_LENGTH))?;
- let description = description
- .encode_utf16()
- .collect::<Vec<u16>>()
- .try_into()
- .map_err(|_| {
- error_feild_too_long(stringify!(description), MAX_COLLECTION_DESCRIPTION_LENGTH)
- })?;
- let token_prefix = token_prefix
- .into_bytes()
- .try_into()
- .map_err(|_| error_feild_too_long(stringify!(token_prefix), MAX_TOKEN_PREFIX_LENGTH))?;
+ let (caller, name, description, token_prefix, _base_uri_value) =
+ convert_data::<T>(caller, name, description, token_prefix, "".into())?;
+ let data = make_data::<T>(
+ name,
+ CollectionMode::NFT,
+ description,
+ token_prefix,
+ Default::default(),
+ false,
+ )?;
+ let collection_id = T::CollectionDispatch::create(caller, data)
+ .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
- let key = token_uri_key();
- let permission = up_data_structs::PropertyPermission {
- mutable: true,
- collection_admin: true,
- token_owner: false,
- };
- let mut token_property_permissions =
- up_data_structs::CollectionPropertiesPermissionsVec::default();
- token_property_permissions
- .try_push(up_data_structs::PropertyKeyPermission { key, permission })
- .map_err(|e| Error::Revert(format!("{:?}", e)))?;
+ let address = pallet_common::eth::collection_id_to_address(collection_id);
+ Ok(address)
+ }
- let data = CreateCollectionData {
+ #[weight(<SelfWeightOf<T>>::create_collection())]
+ #[solidity(rename_selector = "createERC721MetadataCompatibleCollection")]
+ fn create_nonfungible_collection_with_properties(
+ &mut self,
+ caller: caller,
+ name: string,
+ description: string,
+ token_prefix: string,
+ base_uri: string,
+ ) -> Result<address> {
+ let (caller, name, description, token_prefix, base_uri_value) =
+ convert_data::<T>(caller, name, description, token_prefix, base_uri)?;
+ let data = make_data::<T>(
name,
+ CollectionMode::NFT,
description,
token_prefix,
- token_property_permissions,
- ..Default::default()
- };
+ base_uri_value,
+ true,
+ )?;
+ let collection_id = T::CollectionDispatch::create(caller, data)
+ .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
- let collection_id =
- <pallet_nonfungible::Pallet<T>>::init_collection(caller.clone(), data, false)
- .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
+ let address = pallet_common::eth::collection_id_to_address(collection_id);
+ Ok(address)
+ }
+
+ #[weight(<SelfWeightOf<T>>::create_collection())]
+ fn create_refungible_collection(
+ &self,
+ caller: caller,
+ name: string,
+ description: string,
+ token_prefix: string,
+ ) -> Result<address> {
+ let (caller, name, description, token_prefix, _base_uri) =
+ convert_data::<T>(caller, name, description, token_prefix, "".into())?;
+ let data = make_data::<T>(
+ name,
+ CollectionMode::ReFungible,
+ description,
+ token_prefix,
+ Default::default(),
+ false,
+ )?;
+ let collection_id = T::CollectionDispatch::create(caller, data)
+ .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
+
+ let address = pallet_common::eth::collection_id_to_address(collection_id);
+ Ok(address)
+ }
+
+ #[weight(<SelfWeightOf<T>>::create_collection())]
+ #[solidity(rename_selector = "createERC721MetadataCompatibleRFTCollection")]
+ fn create_refungible_collection_with_properties(
+ &mut self,
+ caller: caller,
+ name: string,
+ description: string,
+ token_prefix: string,
+ base_uri: string,
+ ) -> Result<address> {
+ let (caller, name, description, token_prefix, base_uri_value) =
+ convert_data::<T>(caller, name, description, token_prefix, base_uri)?;
+ let data = make_data::<T>(
+ name,
+ CollectionMode::NFT,
+ description,
+ token_prefix,
+ base_uri_value,
+ true,
+ )?;
+ let collection_id = T::CollectionDispatch::create(caller, data)
+ .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
let address = pallet_common::eth::collection_id_to_address(collection_id);
Ok(address)
@@ -124,7 +281,9 @@
/// Implements [`OnMethodCall`], which delegates call to [`EvmCollectionHelpers`]
pub struct CollectionHelpersOnMethodCall<T: Config>(PhantomData<*const T>);
-impl<T: Config + pallet_nonfungible::Config> OnMethodCall<T> for CollectionHelpersOnMethodCall<T> {
+impl<T: Config + pallet_nonfungible::Config + pallet_refungible::Config> OnMethodCall<T>
+ for CollectionHelpersOnMethodCall<T>
+{
fn is_reserved(contract: &sp_core::H160) -> bool {
contract == &T::ContractAddress::get()
}
@@ -152,6 +311,6 @@
generate_stubgen!(collection_helper_impl, CollectionHelpersCall<()>, true);
generate_stubgen!(collection_helper_iface, CollectionHelpersCall<()>, false);
-fn error_feild_too_long(feild: &str, bound: u32) -> Error {
+fn error_field_too_long(feild: &str, bound: usize) -> Error {
Error::Revert(format!("{} is too long. Max length is {}.", feild, bound))
}
pallets/unique/src/eth/stubs/CollectionHelpers.rawdiffbeforeafterbothbinary blob — no preview
pallets/unique/src/eth/stubs/CollectionHelpers.soldiffbeforeafterboth--- a/pallets/unique/src/eth/stubs/CollectionHelpers.sol
+++ b/pallets/unique/src/eth/stubs/CollectionHelpers.sol
@@ -29,7 +29,7 @@
);
}
-// Selector: 20947cd0
+// Selector: c20653fc
contract CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {
// Selector: createNonfungibleCollection(string,string,string) e34a6844
function createNonfungibleCollection(
@@ -45,6 +45,36 @@
return 0x0000000000000000000000000000000000000000;
}
+ // Selector: createERC721MetadataCompatibleCollection(string,string,string,string) a634a5f9
+ function createERC721MetadataCompatibleCollection(
+ string memory name,
+ string memory description,
+ string memory tokenPrefix,
+ string memory baseUri
+ ) public returns (address) {
+ require(false, stub_error);
+ name;
+ description;
+ tokenPrefix;
+ baseUri;
+ dummy = 0;
+ return 0x0000000000000000000000000000000000000000;
+ }
+
+ // Selector: createRefungibleCollection(string,string,string) 44a68ad5
+ function createRefungibleCollection(
+ string memory name,
+ string memory description,
+ string memory tokenPrefix
+ ) public view returns (address) {
+ require(false, stub_error);
+ name;
+ description;
+ tokenPrefix;
+ dummy;
+ return 0x0000000000000000000000000000000000000000;
+ }
+
// Selector: isCollectionExist(address) c3de1494
function isCollectionExist(address collectionAddress)
public
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -352,7 +352,7 @@
// =========
- T::CollectionDispatch::create(T::CrossAccountId::from_sub(sender), data)?;
+ let _id = T::CollectionDispatch::create(T::CrossAccountId::from_sub(sender), data)?;
Ok(())
}
primitives/data-structs/CHANGELOG.mddiffbeforeafterboth--- /dev/null
+++ b/primitives/data-structs/CHANGELOG.md
@@ -0,0 +1,11 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+
+
+## [v0.1.2] - 2022-07-25
+### Added
+- Type aliases `CollectionName`, `CollectionDescription`, `CollectionTokenPrefix`
+## [v0.1.1] - 2022-07-22
+### Added
+- Аields with properties to `CreateReFungibleData` and `CreateRefungibleExData`.
\ No newline at end of file
primitives/data-structs/Cargo.tomldiffbeforeafterboth--- a/primitives/data-structs/Cargo.toml
+++ b/primitives/data-structs/Cargo.toml
@@ -6,7 +6,7 @@
license = 'GPLv3'
homepage = "https://unique.network"
repository = 'https://github.com/UniqueNetwork/unique-chain'
-version = '0.1.1'
+version = '0.1.2'
[dependencies]
scale-info = { version = "2.0.1", default-features = false, features = [
primitives/data-structs/Changelog.mddiffbeforeafterboth--- a/primitives/data-structs/Changelog.md
+++ /dev/null
@@ -1,3 +0,0 @@
-### 0.1.1
----
-* Added fields with properties to `CreateReFungibleData` and `CreateRefungibleExData`.
\ No newline at end of file
primitives/data-structs/src/lib.rsdiffbeforeafterboth--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -286,6 +286,10 @@
}
}
+pub type CollectionName = BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>;
+pub type CollectionDescription = BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>;
+pub type CollectionTokenPrefix = BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>;
+
/// Collection parameters, used in storage (see [`RpcCollection`] for the RPC version).
#[struct_versioning::versioned(version = 2, upper)]
#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]
@@ -294,9 +298,9 @@
pub mode: CollectionMode,
#[version(..2)]
pub access: AccessMode,
- pub name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,
- pub description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,
- pub token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,
+ pub name: CollectionName,
+ pub description: CollectionDescription,
+ pub token_prefix: CollectionTokenPrefix,
#[version(..2)]
pub mint_mode: bool,
@@ -350,9 +354,9 @@
#[derivative(Default(value = "CollectionMode::NFT"))]
pub mode: CollectionMode,
pub access: Option<AccessMode>,
- pub name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,
- pub description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,
- pub token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,
+ pub name: CollectionName,
+ pub description: CollectionDescription,
+ pub token_prefix: CollectionTokenPrefix,
pub pending_sponsor: Option<AccountId>,
pub limits: Option<CollectionLimits>,
pub permissions: Option<CollectionPermissions>,
runtime/common/src/dispatch.rsdiffbeforeafterboth--- a/runtime/common/src/dispatch.rs
+++ b/runtime/common/src/dispatch.rs
@@ -17,6 +17,7 @@
use frame_support::{dispatch::DispatchResult, ensure};
use pallet_evm::{PrecompileHandle, PrecompileResult};
use sp_core::H160;
+use sp_runtime::DispatchError;
use sp_std::{borrow::ToOwned, vec::Vec};
use pallet_common::{
CollectionById, CollectionHandle, CommonCollectionOperations, erc::CommonEvmHandler,
@@ -30,6 +31,7 @@
};
use up_data_structs::{
CollectionMode, CreateCollectionData, MAX_DECIMAL_POINTS, mapping::TokenAddressMapping,
+ CollectionId,
};
pub enum CollectionDispatchT<T>
@@ -51,8 +53,8 @@
fn create(
sender: T::CrossAccountId,
data: CreateCollectionData<T::AccountId>,
- ) -> DispatchResult {
- let _id = match data.mode {
+ ) -> Result<CollectionId, DispatchError> {
+ let id = match data.mode {
CollectionMode::NFT => <PalletNonfungible<T>>::init_collection(sender, data, false)?,
CollectionMode::Fungible(decimal_points) => {
// check params
@@ -64,7 +66,7 @@
}
CollectionMode::ReFungible => <PalletRefungible<T>>::init_collection(sender, data)?,
};
- Ok(())
+ Ok(id)
}
fn destroy(sender: T::CrossAccountId, collection: CollectionHandle<T>) -> DispatchResult {
tests/src/eth/api/CollectionHelpers.soldiffbeforeafterboth--- a/tests/src/eth/api/CollectionHelpers.sol
+++ b/tests/src/eth/api/CollectionHelpers.sol
@@ -20,7 +20,7 @@
);
}
-// Selector: 20947cd0
+// Selector: c20653fc
interface CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {
// Selector: createNonfungibleCollection(string,string,string) e34a6844
function createNonfungibleCollection(
@@ -29,6 +29,21 @@
string memory tokenPrefix
) external returns (address);
+ // Selector: createERC721MetadataCompatibleCollection(string,string,string,string) a634a5f9
+ function createERC721MetadataCompatibleCollection(
+ string memory name,
+ string memory description,
+ string memory tokenPrefix,
+ string memory baseUri
+ ) external returns (address);
+
+ // Selector: createRefungibleCollection(string,string,string) 44a68ad5
+ function createRefungibleCollection(
+ string memory name,
+ string memory description,
+ string memory tokenPrefix
+ ) external view returns (address);
+
// Selector: isCollectionExist(address) c3de1494
function isCollectionExist(address collectionAddress)
external
tests/src/eth/api/UniqueRFT.soldiffbeforeafterboth--- /dev/null
+++ b/tests/src/eth/api/UniqueRFT.sol
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: OTHER
+// This code is automatically generated
+
+pragma solidity >=0.8.0 <0.9.0;
+
+// Common stubs holder
+interface Dummy {
+
+}
+
+interface ERC165 is Dummy {
+ function supportsInterface(bytes4 interfaceID) external view returns (bool);
+}
+
+// Selector: 7d9262e6
+interface Collection is Dummy, ERC165 {
+ // Set collection property.
+ //
+ // @param key Property key.
+ // @param value Propery value.
+ //
+ // Selector: setCollectionProperty(string,bytes) 2f073f66
+ function setCollectionProperty(string memory key, bytes memory value)
+ external;
+
+ // Delete collection property.
+ //
+ // @param key Property key.
+ //
+ // Selector: deleteCollectionProperty(string) 7b7debce
+ function deleteCollectionProperty(string memory key) external;
+
+ // Get collection property.
+ //
+ // @dev Throws error if key not found.
+ //
+ // @param key Property key.
+ // @return bytes The property corresponding to the key.
+ //
+ // Selector: collectionProperty(string) cf24fd6d
+ function collectionProperty(string memory key)
+ external
+ view
+ returns (bytes memory);
+
+ // Set the sponsor of the collection.
+ //
+ // @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+ //
+ // @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
+ //
+ // Selector: setCollectionSponsor(address) 7623402e
+ function setCollectionSponsor(address sponsor) external;
+
+ // Collection sponsorship confirmation.
+ //
+ // @dev After setting the sponsor for the collection, it must be confirmed with this function.
+ //
+ // Selector: confirmCollectionSponsorship() 3c50e97a
+ function confirmCollectionSponsorship() external;
+
+ // Set limits for the collection.
+ // @dev Throws error if limit not found.
+ // @param limit Name of the limit. Valid names:
+ // "accountTokenOwnershipLimit",
+ // "sponsoredDataSize",
+ // "sponsoredDataRateLimit",
+ // "tokenLimit",
+ // "sponsorTransferTimeout",
+ // "sponsorApproveTimeout"
+ // @param value Value of the limit.
+ //
+ // Selector: setCollectionLimit(string,uint32) 6a3841db
+ function setCollectionLimit(string memory limit, uint32 value) external;
+
+ // Set limits for the collection.
+ // @dev Throws error if limit not found.
+ // @param limit Name of the limit. Valid names:
+ // "ownerCanTransfer",
+ // "ownerCanDestroy",
+ // "transfersEnabled"
+ // @param value Value of the limit.
+ //
+ // Selector: setCollectionLimit(string,bool) 993b7fba
+ function setCollectionLimit(string memory limit, bool value) external;
+
+ // Get contract address.
+ //
+ // Selector: contractAddress() f6b4dfb4
+ function contractAddress() external view returns (address);
+
+ // Add collection admin by substrate address.
+ // @param new_admin Substrate administrator address.
+ //
+ // Selector: addCollectionAdminSubstrate(uint256) 5730062b
+ function addCollectionAdminSubstrate(uint256 newAdmin) external;
+
+ // Remove collection admin by substrate address.
+ // @param admin Substrate administrator address.
+ //
+ // Selector: removeCollectionAdminSubstrate(uint256) 4048fcf9
+ function removeCollectionAdminSubstrate(uint256 admin) external;
+
+ // Add collection admin.
+ // @param new_admin Address of the added administrator.
+ //
+ // Selector: addCollectionAdmin(address) 92e462c7
+ function addCollectionAdmin(address newAdmin) external;
+
+ // Remove collection admin.
+ //
+ // @param new_admin Address of the removed administrator.
+ //
+ // Selector: removeCollectionAdmin(address) fafd7b42
+ function removeCollectionAdmin(address admin) external;
+
+ // Toggle accessibility of collection nesting.
+ //
+ // @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
+ //
+ // Selector: setCollectionNesting(bool) 112d4586
+ function setCollectionNesting(bool enable) external;
+
+ // Toggle accessibility of collection nesting.
+ //
+ // @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
+ // @param collections Addresses of collections that will be available for nesting.
+ //
+ // Selector: setCollectionNesting(bool,address[]) 64872396
+ function setCollectionNesting(bool enable, address[] memory collections)
+ external;
+
+ // Set the collection access method.
+ // @param mode Access mode
+ // 0 for Normal
+ // 1 for AllowList
+ //
+ // Selector: setCollectionAccess(uint8) 41835d4c
+ function setCollectionAccess(uint8 mode) external;
+
+ // Add the user to the allowed list.
+ //
+ // @param user Address of a trusted user.
+ //
+ // Selector: addToCollectionAllowList(address) 67844fe6
+ function addToCollectionAllowList(address user) external;
+
+ // Remove the user from the allowed list.
+ //
+ // @param user Address of a removed user.
+ //
+ // Selector: removeFromCollectionAllowList(address) 85c51acb
+ function removeFromCollectionAllowList(address user) external;
+
+ // Switch permission for minting.
+ //
+ // @param mode Enable if "true".
+ //
+ // Selector: setCollectionMintMode(bool) 00018e84
+ function setCollectionMintMode(bool mode) external;
+}
+
+interface UniqueRFT is Dummy, ERC165, Collection {}
tests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth--- /dev/null
+++ b/tests/src/eth/api/UniqueRefungible.sol
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: OTHER
+// This code is automatically generated
+
+pragma solidity >=0.8.0 <0.9.0;
+
+// Common stubs holder
+interface Dummy {
+
+}
+
+interface ERC165 is Dummy {
+ function supportsInterface(bytes4 interfaceID) external view returns (bool);
+}
+
+// Selector: 7d9262e6
+interface Collection is Dummy, ERC165 {
+ // Set collection property.
+ //
+ // @param key Property key.
+ // @param value Propery value.
+ //
+ // Selector: setCollectionProperty(string,bytes) 2f073f66
+ function setCollectionProperty(string memory key, bytes memory value)
+ external;
+
+ // Delete collection property.
+ //
+ // @param key Property key.
+ //
+ // Selector: deleteCollectionProperty(string) 7b7debce
+ function deleteCollectionProperty(string memory key) external;
+
+ // Get collection property.
+ //
+ // @dev Throws error if key not found.
+ //
+ // @param key Property key.
+ // @return bytes The property corresponding to the key.
+ //
+ // Selector: collectionProperty(string) cf24fd6d
+ function collectionProperty(string memory key)
+ external
+ view
+ returns (bytes memory);
+
+ // Set the sponsor of the collection.
+ //
+ // @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+ //
+ // @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
+ //
+ // Selector: setCollectionSponsor(address) 7623402e
+ function setCollectionSponsor(address sponsor) external;
+
+ // Collection sponsorship confirmation.
+ //
+ // @dev After setting the sponsor for the collection, it must be confirmed with this function.
+ //
+ // Selector: confirmCollectionSponsorship() 3c50e97a
+ function confirmCollectionSponsorship() external;
+
+ // Set limits for the collection.
+ // @dev Throws error if limit not found.
+ // @param limit Name of the limit. Valid names:
+ // "accountTokenOwnershipLimit",
+ // "sponsoredDataSize",
+ // "sponsoredDataRateLimit",
+ // "tokenLimit",
+ // "sponsorTransferTimeout",
+ // "sponsorApproveTimeout"
+ // @param value Value of the limit.
+ //
+ // Selector: setCollectionLimit(string,uint32) 6a3841db
+ function setCollectionLimit(string memory limit, uint32 value) external;
+
+ // Set limits for the collection.
+ // @dev Throws error if limit not found.
+ // @param limit Name of the limit. Valid names:
+ // "ownerCanTransfer",
+ // "ownerCanDestroy",
+ // "transfersEnabled"
+ // @param value Value of the limit.
+ //
+ // Selector: setCollectionLimit(string,bool) 993b7fba
+ function setCollectionLimit(string memory limit, bool value) external;
+
+ // Get contract address.
+ //
+ // Selector: contractAddress() f6b4dfb4
+ function contractAddress() external view returns (address);
+
+ // Add collection admin by substrate address.
+ // @param new_admin Substrate administrator address.
+ //
+ // Selector: addCollectionAdminSubstrate(uint256) 5730062b
+ function addCollectionAdminSubstrate(uint256 newAdmin) external;
+
+ // Remove collection admin by substrate address.
+ // @param admin Substrate administrator address.
+ //
+ // Selector: removeCollectionAdminSubstrate(uint256) 4048fcf9
+ function removeCollectionAdminSubstrate(uint256 admin) external;
+
+ // Add collection admin.
+ // @param new_admin Address of the added administrator.
+ //
+ // Selector: addCollectionAdmin(address) 92e462c7
+ function addCollectionAdmin(address newAdmin) external;
+
+ // Remove collection admin.
+ //
+ // @param new_admin Address of the removed administrator.
+ //
+ // Selector: removeCollectionAdmin(address) fafd7b42
+ function removeCollectionAdmin(address admin) external;
+
+ // Toggle accessibility of collection nesting.
+ //
+ // @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
+ //
+ // Selector: setCollectionNesting(bool) 112d4586
+ function setCollectionNesting(bool enable) external;
+
+ // Toggle accessibility of collection nesting.
+ //
+ // @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
+ // @param collections Addresses of collections that will be available for nesting.
+ //
+ // Selector: setCollectionNesting(bool,address[]) 64872396
+ function setCollectionNesting(bool enable, address[] memory collections)
+ external;
+
+ // Set the collection access method.
+ // @param mode Access mode
+ // 0 for Normal
+ // 1 for AllowList
+ //
+ // Selector: setCollectionAccess(uint8) 41835d4c
+ function setCollectionAccess(uint8 mode) external;
+
+ // Add the user to the allowed list.
+ //
+ // @param user Address of a trusted user.
+ //
+ // Selector: addToCollectionAllowList(address) 67844fe6
+ function addToCollectionAllowList(address user) external;
+
+ // Remove the user from the allowed list.
+ //
+ // @param user Address of a removed user.
+ //
+ // Selector: removeFromCollectionAllowList(address) 85c51acb
+ function removeFromCollectionAllowList(address user) external;
+
+ // Switch permission for minting.
+ //
+ // @param mode Enable if "true".
+ //
+ // Selector: setCollectionMintMode(bool) 00018e84
+ function setCollectionMintMode(bool mode) external;
+}
+
+interface UniqueRefungible is Dummy, ERC165, Collection {}
tests/src/eth/api/UniqueRefungibleToken.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueRefungibleToken.sol
+++ b/tests/src/eth/api/UniqueRefungibleToken.sol
@@ -24,24 +24,45 @@
// Selector: 942e8b22
interface ERC20 is Dummy, ERC165, ERC20Events {
+ // @return the name of the token.
+ //
// Selector: name() 06fdde03
function name() external view returns (string memory);
+ // @return the symbol of the token.
+ //
// Selector: symbol() 95d89b41
function symbol() external view returns (string memory);
+ // @dev Total number of tokens in existence
+ //
// Selector: totalSupply() 18160ddd
function totalSupply() external view returns (uint256);
+ // @dev Not supported
+ //
// Selector: decimals() 313ce567
function decimals() external view returns (uint8);
+ // @dev Gets the balance of the specified address.
+ // @param owner The address to query the balance of.
+ // @return An uint256 representing the amount owned by the passed address.
+ //
// Selector: balanceOf(address) 70a08231
function balanceOf(address owner) external view returns (uint256);
+ // @dev Transfer token for a specified address
+ // @param to The address to transfer to.
+ // @param amount The amount to be transferred.
+ //
// Selector: transfer(address,uint256) a9059cbb
function transfer(address to, uint256 amount) external returns (bool);
+ // @dev Transfer tokens from one address to another
+ // @param from address The address which you want to send tokens from
+ // @param to address The address which you want to transfer to
+ // @param amount uint256 the amount of tokens to be transferred
+ //
// Selector: transferFrom(address,address,uint256) 23b872dd
function transferFrom(
address from,
@@ -49,9 +70,22 @@
uint256 amount
) external returns (bool);
+ // @dev Approve the passed address to spend the specified amount of tokens on behalf of `msg.sender`.
+ // Beware that changing an allowance with this method brings the risk that someone may use both the old
+ // and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
+ // race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
+ // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
+ // @param spender The address which will spend the funds.
+ // @param amount The amount of tokens to be spent.
+ //
// Selector: approve(address,uint256) 095ea7b3
function approve(address spender, uint256 amount) external returns (bool);
+ // @dev Function to check the amount of tokens that an owner allowed to a spender.
+ // @param owner address The address which owns the funds.
+ // @param spender address The address which will spend the funds.
+ // @return A uint256 specifying the amount of tokens still available for the spender.
+ //
// Selector: allowance(address,address) dd62ed3e
function allowance(address owner, address spender)
external
@@ -61,9 +95,18 @@
// Selector: ab8deb37
interface ERC20UniqueExtensions is Dummy, ERC165 {
+ // @dev Function that burns an amount of the token of a given account,
+ // deducting from the sender's allowance for said account.
+ // @param from The account whose tokens will be burnt.
+ // @param amount The amount that will be burnt.
+ //
// Selector: burnFrom(address,uint256) 79cc6790
function burnFrom(address from, uint256 amount) external returns (bool);
+ // @dev Function that changes total amount of the tokens.
+ // Throws if `msg.sender` doesn't owns all of the tokens.
+ // @param amount New total amount of the tokens.
+ //
// Selector: repartition(uint256) d2418ca7
function repartition(uint256 amount) external returns (bool);
}
tests/src/eth/collectionHelpersAbi.jsondiffbeforeafterboth--- a/tests/src/eth/collectionHelpersAbi.json
+++ b/tests/src/eth/collectionHelpersAbi.json
@@ -22,6 +22,18 @@
"inputs": [
{ "internalType": "string", "name": "name", "type": "string" },
{ "internalType": "string", "name": "description", "type": "string" },
+ { "internalType": "string", "name": "tokenPrefix", "type": "string" },
+ { "internalType": "string", "name": "baseUri", "type": "string" }
+ ],
+ "name": "createERC721MetadataCompatibleCollection",
+ "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ { "internalType": "string", "name": "name", "type": "string" },
+ { "internalType": "string", "name": "description", "type": "string" },
{ "internalType": "string", "name": "tokenPrefix", "type": "string" }
],
"name": "createNonfungibleCollection",
@@ -31,6 +43,17 @@
},
{
"inputs": [
+ { "internalType": "string", "name": "name", "type": "string" },
+ { "internalType": "string", "name": "description", "type": "string" },
+ { "internalType": "string", "name": "tokenPrefix", "type": "string" }
+ ],
+ "name": "createRefungibleCollection",
+ "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
{
"internalType": "address",
"name": "collectionAddress",
tests/src/eth/createCollection.test.tsdiffbeforeafterboth--- a/tests/src/eth/createCollection.test.ts
+++ /dev/null
@@ -1,230 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-import {evmToAddress} from '@polkadot/util-crypto';
-import {expect} from 'chai';
-import {getCreatedCollectionCount, getDetailedCollectionInfo} from '../util/helpers';
-import {
- evmCollectionHelpers,
- collectionIdToAddress,
- createEthAccount,
- createEthAccountWithBalance,
- evmCollection,
- itWeb3,
- getCollectionAddressFromResult,
-} from './util/helpers';
-
-describe('Create collection from EVM', () => {
- // itWeb3('Create collection', async ({api, web3, privateKeyWrapper}) => {
- // const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- // const collectionHelper = evmCollectionHelpers(web3, owner);
- // const collectionName = 'CollectionEVM';
- // const description = 'Some description';
- // const tokenPrefix = 'token prefix';
-
- // const collectionCountBefore = await getCreatedCollectionCount(api);
- // const result = await collectionHelper.methods
- // .createNonfungibleCollection(collectionName, description, tokenPrefix)
- // .send();
- // const collectionCountAfter = await getCreatedCollectionCount(api);
-
- // const {collectionId, collection} = await getCollectionAddressFromResult(api, result);
- // expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);
- // expect(collectionId).to.be.eq(collectionCountAfter);
- // expect(collection.name.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(collectionName);
- // expect(collection.description.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(description);
- // expect(collection.tokenPrefix.toHuman()).to.be.eq(tokenPrefix);
- // });
-
- // itWeb3('Check collection address exist', async ({api, web3, privateKeyWrapper}) => {
- // const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- // const collectionHelpers = evmCollectionHelpers(web3, owner);
-
- // const expectedCollectionId = await getCreatedCollectionCount(api) + 1;
- // const expectedCollectionAddress = collectionIdToAddress(expectedCollectionId);
- // expect(await collectionHelpers.methods
- // .isCollectionExist(expectedCollectionAddress)
- // .call()).to.be.false;
-
- // await collectionHelpers.methods
- // .createNonfungibleCollection('A', 'A', 'A')
- // .send();
-
- // expect(await collectionHelpers.methods
- // .isCollectionExist(expectedCollectionAddress)
- // .call()).to.be.true;
- // });
-
- itWeb3('Set sponsorship', async ({api, web3, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const collectionHelpers = evmCollectionHelpers(web3, owner);
- let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send();
- const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
- result = await collectionEvm.methods.setCollectionSponsor(sponsor).send();
- let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
- expect(collectionSub.sponsorship.isUnconfirmed).to.be.true;
- const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format;
- expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
- await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');
- const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);
- await sponsorCollection.methods.confirmCollectionSponsorship().send();
- collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
- expect(collectionSub.sponsorship.isConfirmed).to.be.true;
- expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
- });
-
- itWeb3('Set limits', async ({api, web3, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const collectionHelpers = evmCollectionHelpers(web3, owner);
- const result = await collectionHelpers.methods.createNonfungibleCollection('Const collection', '5', '5').send();
- const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const limits = {
- accountTokenOwnershipLimit: 1000,
- sponsoredDataSize: 1024,
- sponsoredDataRateLimit: 30,
- tokenLimit: 1000000,
- sponsorTransferTimeout: 6,
- sponsorApproveTimeout: 6,
- ownerCanTransfer: false,
- ownerCanDestroy: false,
- transfersEnabled: false,
- };
-
- const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
- await collectionEvm.methods['setCollectionLimit(string,uint32)']('accountTokenOwnershipLimit', limits.accountTokenOwnershipLimit).send();
- await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsoredDataSize', limits.sponsoredDataSize).send();
- await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsoredDataRateLimit', limits.sponsoredDataRateLimit).send();
- await collectionEvm.methods['setCollectionLimit(string,uint32)']('tokenLimit', limits.tokenLimit).send();
- await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsorTransferTimeout', limits.sponsorTransferTimeout).send();
- await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsorApproveTimeout', limits.sponsorApproveTimeout).send();
- await collectionEvm.methods['setCollectionLimit(string,bool)']('ownerCanTransfer', limits.ownerCanTransfer).send();
- await collectionEvm.methods['setCollectionLimit(string,bool)']('ownerCanDestroy', limits.ownerCanDestroy).send();
- await collectionEvm.methods['setCollectionLimit(string,bool)']('transfersEnabled', limits.transfersEnabled).send();
-
- const collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
- expect(collectionSub.limits.accountTokenOwnershipLimit.unwrap().toNumber()).to.be.eq(limits.accountTokenOwnershipLimit);
- expect(collectionSub.limits.sponsoredDataSize.unwrap().toNumber()).to.be.eq(limits.sponsoredDataSize);
- expect(collectionSub.limits.sponsoredDataRateLimit.unwrap().asBlocks.toNumber()).to.be.eq(limits.sponsoredDataRateLimit);
- expect(collectionSub.limits.tokenLimit.unwrap().toNumber()).to.be.eq(limits.tokenLimit);
- expect(collectionSub.limits.sponsorTransferTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorTransferTimeout);
- expect(collectionSub.limits.sponsorApproveTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorApproveTimeout);
- expect(collectionSub.limits.ownerCanTransfer.toHuman()).to.be.eq(limits.ownerCanTransfer);
- expect(collectionSub.limits.ownerCanDestroy.toHuman()).to.be.eq(limits.ownerCanDestroy);
- expect(collectionSub.limits.transfersEnabled.toHuman()).to.be.eq(limits.transfersEnabled);
- });
-
- itWeb3('Collection address exist', async ({api, web3, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
- const collectionHelpers = evmCollectionHelpers(web3, owner);
- expect(await collectionHelpers.methods
- .isCollectionExist(collectionAddressForNonexistentCollection).call())
- .to.be.false;
-
- const result = await collectionHelpers.methods.createNonfungibleCollection('Collection address exist', '7', '7').send();
- const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
- expect(await collectionHelpers.methods
- .isCollectionExist(collectionIdAddress).call())
- .to.be.true;
- });
-});
-
-describe('(!negative tests!) Create collection from EVM', () => {
- itWeb3('(!negative test!) Create collection (bad lengths)', async ({api, web3, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const helper = evmCollectionHelpers(web3, owner);
- {
- const MAX_NAME_LENGHT = 64;
- const collectionName = 'A'.repeat(MAX_NAME_LENGHT + 1);
- const description = 'A';
- const tokenPrefix = 'A';
-
- await expect(helper.methods
- .createNonfungibleCollection(collectionName, description, tokenPrefix)
- .call()).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGHT);
-
- }
- {
- const MAX_DESCRIPTION_LENGHT = 256;
- const collectionName = 'A';
- const description = 'A'.repeat(MAX_DESCRIPTION_LENGHT + 1);
- const tokenPrefix = 'A';
- await expect(helper.methods
- .createNonfungibleCollection(collectionName, description, tokenPrefix)
- .call()).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGHT);
- }
- {
- const MAX_TOKEN_PREFIX_LENGHT = 16;
- const collectionName = 'A';
- const description = 'A';
- const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGHT + 1);
- await expect(helper.methods
- .createNonfungibleCollection(collectionName, description, tokenPrefix)
- .call()).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGHT);
- }
- });
-
- itWeb3('(!negative test!) Create collection (no funds)', async ({web3}) => {
- const owner = await createEthAccount(web3);
- const helper = evmCollectionHelpers(web3, owner);
- const collectionName = 'A';
- const description = 'A';
- const tokenPrefix = 'A';
-
- await expect(helper.methods
- .createNonfungibleCollection(collectionName, description, tokenPrefix)
- .call()).to.be.rejectedWith('NotSufficientFounds');
- });
-
- itWeb3('(!negative test!) Check owner', async ({api, web3, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const notOwner = await createEthAccount(web3);
- const collectionHelpers = evmCollectionHelpers(web3, owner);
- const result = await collectionHelpers.methods.createNonfungibleCollection('A', 'A', 'A').send();
- const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
- const contractEvmFromNotOwner = evmCollection(web3, notOwner, collectionIdAddress);
- const EXPECTED_ERROR = 'NoPermission';
- {
- const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- await expect(contractEvmFromNotOwner.methods
- .setCollectionSponsor(sponsor)
- .call()).to.be.rejectedWith(EXPECTED_ERROR);
-
- const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);
- await expect(sponsorCollection.methods
- .confirmCollectionSponsorship()
- .call()).to.be.rejectedWith('caller is not set as sponsor');
- }
- {
- await expect(contractEvmFromNotOwner.methods
- .setCollectionLimit('account_token_ownership_limit', '1000')
- .call()).to.be.rejectedWith(EXPECTED_ERROR);
- }
- });
-
- itWeb3('(!negative test!) Set limits', async ({api, web3, privateKeyWrapper}) => {
- const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
- const collectionHelpers = evmCollectionHelpers(web3, owner);
- const result = await collectionHelpers.methods.createNonfungibleCollection('Schema collection', 'A', 'A').send();
- const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
- const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
- await expect(collectionEvm.methods
- .setCollectionLimit('badLimit', 'true')
- .call()).to.be.rejectedWith('unknown boolean limit "badLimit"');
- });
-});
\ No newline at end of file
tests/src/eth/createNFTCollection.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/eth/createNFTCollection.test.ts
@@ -0,0 +1,231 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {evmToAddress} from '@polkadot/util-crypto';
+import {expect} from 'chai';
+import {getCreatedCollectionCount, getDetailedCollectionInfo} from '../util/helpers';
+import {
+ evmCollectionHelpers,
+ collectionIdToAddress,
+ createEthAccount,
+ createEthAccountWithBalance,
+ evmCollection,
+ itWeb3,
+ getCollectionAddressFromResult,
+} from './util/helpers';
+
+describe('Create NFT collection from EVM', () => {
+ itWeb3('Create collection', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const collectionHelper = evmCollectionHelpers(web3, owner);
+ const collectionName = 'CollectionEVM';
+ const description = 'Some description';
+ const tokenPrefix = 'token prefix';
+
+ const collectionCountBefore = await getCreatedCollectionCount(api);
+ const result = await collectionHelper.methods
+ .createNonfungibleCollection(collectionName, description, tokenPrefix)
+ .send();
+ const collectionCountAfter = await getCreatedCollectionCount(api);
+
+ const {collectionId, collection} = await getCollectionAddressFromResult(api, result);
+ expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);
+ expect(collectionId).to.be.eq(collectionCountAfter);
+ expect(collection.name.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(collectionName);
+ expect(collection.description.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(description);
+ expect(collection.tokenPrefix.toHuman()).to.be.eq(tokenPrefix);
+ expect(collection.mode.isNft).to.be.true;
+ });
+
+ itWeb3('Check collection address exist', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const collectionHelpers = evmCollectionHelpers(web3, owner);
+
+ const expectedCollectionId = await getCreatedCollectionCount(api) + 1;
+ const expectedCollectionAddress = collectionIdToAddress(expectedCollectionId);
+ expect(await collectionHelpers.methods
+ .isCollectionExist(expectedCollectionAddress)
+ .call()).to.be.false;
+
+ await collectionHelpers.methods
+ .createNonfungibleCollection('A', 'A', 'A')
+ .send();
+
+ expect(await collectionHelpers.methods
+ .isCollectionExist(expectedCollectionAddress)
+ .call()).to.be.true;
+ });
+
+ itWeb3('Set sponsorship', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const collectionHelpers = evmCollectionHelpers(web3, owner);
+ let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send();
+ const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+ const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+ result = await collectionEvm.methods.setCollectionSponsor(sponsor).send();
+ let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
+ expect(collectionSub.sponsorship.isUnconfirmed).to.be.true;
+ const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format;
+ expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
+ await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');
+ const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);
+ await sponsorCollection.methods.confirmCollectionSponsorship().send();
+ collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
+ expect(collectionSub.sponsorship.isConfirmed).to.be.true;
+ expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
+ });
+
+ itWeb3('Set limits', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const collectionHelpers = evmCollectionHelpers(web3, owner);
+ const result = await collectionHelpers.methods.createNonfungibleCollection('Const collection', '5', '5').send();
+ const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+ const limits = {
+ accountTokenOwnershipLimit: 1000,
+ sponsoredDataSize: 1024,
+ sponsoredDataRateLimit: 30,
+ tokenLimit: 1000000,
+ sponsorTransferTimeout: 6,
+ sponsorApproveTimeout: 6,
+ ownerCanTransfer: false,
+ ownerCanDestroy: false,
+ transfersEnabled: false,
+ };
+
+ const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+ await collectionEvm.methods['setCollectionLimit(string,uint32)']('accountTokenOwnershipLimit', limits.accountTokenOwnershipLimit).send();
+ await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsoredDataSize', limits.sponsoredDataSize).send();
+ await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsoredDataRateLimit', limits.sponsoredDataRateLimit).send();
+ await collectionEvm.methods['setCollectionLimit(string,uint32)']('tokenLimit', limits.tokenLimit).send();
+ await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsorTransferTimeout', limits.sponsorTransferTimeout).send();
+ await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsorApproveTimeout', limits.sponsorApproveTimeout).send();
+ await collectionEvm.methods['setCollectionLimit(string,bool)']('ownerCanTransfer', limits.ownerCanTransfer).send();
+ await collectionEvm.methods['setCollectionLimit(string,bool)']('ownerCanDestroy', limits.ownerCanDestroy).send();
+ await collectionEvm.methods['setCollectionLimit(string,bool)']('transfersEnabled', limits.transfersEnabled).send();
+
+ const collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
+ expect(collectionSub.limits.accountTokenOwnershipLimit.unwrap().toNumber()).to.be.eq(limits.accountTokenOwnershipLimit);
+ expect(collectionSub.limits.sponsoredDataSize.unwrap().toNumber()).to.be.eq(limits.sponsoredDataSize);
+ expect(collectionSub.limits.sponsoredDataRateLimit.unwrap().asBlocks.toNumber()).to.be.eq(limits.sponsoredDataRateLimit);
+ expect(collectionSub.limits.tokenLimit.unwrap().toNumber()).to.be.eq(limits.tokenLimit);
+ expect(collectionSub.limits.sponsorTransferTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorTransferTimeout);
+ expect(collectionSub.limits.sponsorApproveTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorApproveTimeout);
+ expect(collectionSub.limits.ownerCanTransfer.toHuman()).to.be.eq(limits.ownerCanTransfer);
+ expect(collectionSub.limits.ownerCanDestroy.toHuman()).to.be.eq(limits.ownerCanDestroy);
+ expect(collectionSub.limits.transfersEnabled.toHuman()).to.be.eq(limits.transfersEnabled);
+ });
+
+ itWeb3('Collection address exist', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
+ const collectionHelpers = evmCollectionHelpers(web3, owner);
+ expect(await collectionHelpers.methods
+ .isCollectionExist(collectionAddressForNonexistentCollection).call())
+ .to.be.false;
+
+ const result = await collectionHelpers.methods.createNonfungibleCollection('Collection address exist', '7', '7').send();
+ const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
+ expect(await collectionHelpers.methods
+ .isCollectionExist(collectionIdAddress).call())
+ .to.be.true;
+ });
+});
+
+describe('(!negative tests!) Create NFT collection from EVM', () => {
+ itWeb3('(!negative test!) Create collection (bad lengths)', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const helper = evmCollectionHelpers(web3, owner);
+ {
+ const MAX_NAME_LENGHT = 64;
+ const collectionName = 'A'.repeat(MAX_NAME_LENGHT + 1);
+ const description = 'A';
+ const tokenPrefix = 'A';
+
+ await expect(helper.methods
+ .createNonfungibleCollection(collectionName, description, tokenPrefix)
+ .call()).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGHT);
+
+ }
+ {
+ const MAX_DESCRIPTION_LENGHT = 256;
+ const collectionName = 'A';
+ const description = 'A'.repeat(MAX_DESCRIPTION_LENGHT + 1);
+ const tokenPrefix = 'A';
+ await expect(helper.methods
+ .createNonfungibleCollection(collectionName, description, tokenPrefix)
+ .call()).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGHT);
+ }
+ {
+ const MAX_TOKEN_PREFIX_LENGHT = 16;
+ const collectionName = 'A';
+ const description = 'A';
+ const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGHT + 1);
+ await expect(helper.methods
+ .createNonfungibleCollection(collectionName, description, tokenPrefix)
+ .call()).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGHT);
+ }
+ });
+
+ itWeb3('(!negative test!) Create collection (no funds)', async ({web3}) => {
+ const owner = await createEthAccount(web3);
+ const helper = evmCollectionHelpers(web3, owner);
+ const collectionName = 'A';
+ const description = 'A';
+ const tokenPrefix = 'A';
+
+ await expect(helper.methods
+ .createNonfungibleCollection(collectionName, description, tokenPrefix)
+ .call()).to.be.rejectedWith('NotSufficientFounds');
+ });
+
+ itWeb3('(!negative test!) Check owner', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const notOwner = await createEthAccount(web3);
+ const collectionHelpers = evmCollectionHelpers(web3, owner);
+ const result = await collectionHelpers.methods.createNonfungibleCollection('A', 'A', 'A').send();
+ const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
+ const contractEvmFromNotOwner = evmCollection(web3, notOwner, collectionIdAddress);
+ const EXPECTED_ERROR = 'NoPermission';
+ {
+ const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ await expect(contractEvmFromNotOwner.methods
+ .setCollectionSponsor(sponsor)
+ .call()).to.be.rejectedWith(EXPECTED_ERROR);
+
+ const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);
+ await expect(sponsorCollection.methods
+ .confirmCollectionSponsorship()
+ .call()).to.be.rejectedWith('caller is not set as sponsor');
+ }
+ {
+ await expect(contractEvmFromNotOwner.methods
+ .setCollectionLimit('account_token_ownership_limit', '1000')
+ .call()).to.be.rejectedWith(EXPECTED_ERROR);
+ }
+ });
+
+ itWeb3('(!negative test!) Set limits', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const collectionHelpers = evmCollectionHelpers(web3, owner);
+ const result = await collectionHelpers.methods.createNonfungibleCollection('Schema collection', 'A', 'A').send();
+ const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
+ const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+ await expect(collectionEvm.methods
+ .setCollectionLimit('badLimit', 'true')
+ .call()).to.be.rejectedWith('unknown boolean limit "badLimit"');
+ });
+});
\ No newline at end of file
tests/src/eth/createRFTCollection.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/eth/createRFTCollection.test.ts
@@ -0,0 +1,231 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {evmToAddress} from '@polkadot/util-crypto';
+import {expect} from 'chai';
+import {getCreatedCollectionCount, getDetailedCollectionInfo} from '../util/helpers';
+import {
+ evmCollectionHelpers,
+ collectionIdToAddress,
+ createEthAccount,
+ createEthAccountWithBalance,
+ evmCollection,
+ itWeb3,
+ getCollectionAddressFromResult,
+} from './util/helpers';
+
+describe('Create RFT collection from EVM', () => {
+ itWeb3('Create collection', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const collectionHelper = evmCollectionHelpers(web3, owner);
+ const collectionName = 'CollectionEVM';
+ const description = 'Some description';
+ const tokenPrefix = 'token prefix';
+
+ const collectionCountBefore = await getCreatedCollectionCount(api);
+ const result = await collectionHelper.methods
+ .createRefungibleCollection(collectionName, description, tokenPrefix)
+ .send();
+ const collectionCountAfter = await getCreatedCollectionCount(api);
+
+ const {collectionId, collection} = await getCollectionAddressFromResult(api, result);
+ expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);
+ expect(collectionId).to.be.eq(collectionCountAfter);
+ expect(collection.name.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(collectionName);
+ expect(collection.description.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(description);
+ expect(collection.tokenPrefix.toHuman()).to.be.eq(tokenPrefix);
+ expect(collection.mode.isReFungible).to.be.true;
+ });
+
+ itWeb3('Check collection address exist', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const collectionHelpers = evmCollectionHelpers(web3, owner);
+
+ const expectedCollectionId = await getCreatedCollectionCount(api) + 1;
+ const expectedCollectionAddress = collectionIdToAddress(expectedCollectionId);
+ expect(await collectionHelpers.methods
+ .isCollectionExist(expectedCollectionAddress)
+ .call()).to.be.false;
+
+ await collectionHelpers.methods
+ .createRefungibleCollection('A', 'A', 'A')
+ .send();
+
+ expect(await collectionHelpers.methods
+ .isCollectionExist(expectedCollectionAddress)
+ .call()).to.be.true;
+ });
+
+ itWeb3('Set sponsorship', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const collectionHelpers = evmCollectionHelpers(web3, owner);
+ let result = await collectionHelpers.methods.createRefungibleCollection('Sponsor collection', '1', '1').send();
+ const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+ const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const collectionEvm = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'});
+ result = await collectionEvm.methods.setCollectionSponsor(sponsor).send();
+ let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
+ expect(collectionSub.sponsorship.isUnconfirmed).to.be.true;
+ const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format;
+ expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
+ await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');
+ const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);
+ await sponsorCollection.methods.confirmCollectionSponsorship().send();
+ collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
+ expect(collectionSub.sponsorship.isConfirmed).to.be.true;
+ expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
+ });
+
+ itWeb3('Set limits', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const collectionHelpers = evmCollectionHelpers(web3, owner);
+ const result = await collectionHelpers.methods.createRefungibleCollection('Const collection', '5', '5').send();
+ const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+ const limits = {
+ accountTokenOwnershipLimit: 1000,
+ sponsoredDataSize: 1024,
+ sponsoredDataRateLimit: 30,
+ tokenLimit: 1000000,
+ sponsorTransferTimeout: 6,
+ sponsorApproveTimeout: 6,
+ ownerCanTransfer: false,
+ ownerCanDestroy: false,
+ transfersEnabled: false,
+ };
+
+ const collectionEvm = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'});
+ await collectionEvm.methods['setCollectionLimit(string,uint32)']('accountTokenOwnershipLimit', limits.accountTokenOwnershipLimit).send();
+ await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsoredDataSize', limits.sponsoredDataSize).send();
+ await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsoredDataRateLimit', limits.sponsoredDataRateLimit).send();
+ await collectionEvm.methods['setCollectionLimit(string,uint32)']('tokenLimit', limits.tokenLimit).send();
+ await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsorTransferTimeout', limits.sponsorTransferTimeout).send();
+ await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsorApproveTimeout', limits.sponsorApproveTimeout).send();
+ await collectionEvm.methods['setCollectionLimit(string,bool)']('ownerCanTransfer', limits.ownerCanTransfer).send();
+ await collectionEvm.methods['setCollectionLimit(string,bool)']('ownerCanDestroy', limits.ownerCanDestroy).send();
+ await collectionEvm.methods['setCollectionLimit(string,bool)']('transfersEnabled', limits.transfersEnabled).send();
+
+ const collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
+ expect(collectionSub.limits.accountTokenOwnershipLimit.unwrap().toNumber()).to.be.eq(limits.accountTokenOwnershipLimit);
+ expect(collectionSub.limits.sponsoredDataSize.unwrap().toNumber()).to.be.eq(limits.sponsoredDataSize);
+ expect(collectionSub.limits.sponsoredDataRateLimit.unwrap().asBlocks.toNumber()).to.be.eq(limits.sponsoredDataRateLimit);
+ expect(collectionSub.limits.tokenLimit.unwrap().toNumber()).to.be.eq(limits.tokenLimit);
+ expect(collectionSub.limits.sponsorTransferTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorTransferTimeout);
+ expect(collectionSub.limits.sponsorApproveTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorApproveTimeout);
+ expect(collectionSub.limits.ownerCanTransfer.toHuman()).to.be.eq(limits.ownerCanTransfer);
+ expect(collectionSub.limits.ownerCanDestroy.toHuman()).to.be.eq(limits.ownerCanDestroy);
+ expect(collectionSub.limits.transfersEnabled.toHuman()).to.be.eq(limits.transfersEnabled);
+ });
+
+ itWeb3('Collection address exist', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
+ const collectionHelpers = evmCollectionHelpers(web3, owner);
+ expect(await collectionHelpers.methods
+ .isCollectionExist(collectionAddressForNonexistentCollection).call())
+ .to.be.false;
+
+ const result = await collectionHelpers.methods.createRefungibleCollection('Collection address exist', '7', '7').send();
+ const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
+ expect(await collectionHelpers.methods
+ .isCollectionExist(collectionIdAddress).call())
+ .to.be.true;
+ });
+});
+
+describe('(!negative tests!) Create RFT collection from EVM', () => {
+ itWeb3('(!negative test!) Create collection (bad lengths)', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const helper = evmCollectionHelpers(web3, owner);
+ {
+ const MAX_NAME_LENGHT = 64;
+ const collectionName = 'A'.repeat(MAX_NAME_LENGHT + 1);
+ const description = 'A';
+ const tokenPrefix = 'A';
+
+ await expect(helper.methods
+ .createRefungibleCollection(collectionName, description, tokenPrefix)
+ .call()).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGHT);
+
+ }
+ {
+ const MAX_DESCRIPTION_LENGHT = 256;
+ const collectionName = 'A';
+ const description = 'A'.repeat(MAX_DESCRIPTION_LENGHT + 1);
+ const tokenPrefix = 'A';
+ await expect(helper.methods
+ .createRefungibleCollection(collectionName, description, tokenPrefix)
+ .call()).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGHT);
+ }
+ {
+ const MAX_TOKEN_PREFIX_LENGHT = 16;
+ const collectionName = 'A';
+ const description = 'A';
+ const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGHT + 1);
+ await expect(helper.methods
+ .createRefungibleCollection(collectionName, description, tokenPrefix)
+ .call()).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGHT);
+ }
+ });
+
+ itWeb3('(!negative test!) Create collection (no funds)', async ({web3}) => {
+ const owner = await createEthAccount(web3);
+ const helper = evmCollectionHelpers(web3, owner);
+ const collectionName = 'A';
+ const description = 'A';
+ const tokenPrefix = 'A';
+
+ await expect(helper.methods
+ .createRefungibleCollection(collectionName, description, tokenPrefix)
+ .call()).to.be.rejectedWith('NotSufficientFounds');
+ });
+
+ itWeb3('(!negative test!) Check owner', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const notOwner = await createEthAccount(web3);
+ const collectionHelpers = evmCollectionHelpers(web3, owner);
+ const result = await collectionHelpers.methods.createRefungibleCollection('A', 'A', 'A').send();
+ const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
+ const contractEvmFromNotOwner = evmCollection(web3, notOwner, collectionIdAddress, {type: 'ReFungible'});
+ const EXPECTED_ERROR = 'NoPermission';
+ {
+ const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ await expect(contractEvmFromNotOwner.methods
+ .setCollectionSponsor(sponsor)
+ .call()).to.be.rejectedWith(EXPECTED_ERROR);
+
+ const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);
+ await expect(sponsorCollection.methods
+ .confirmCollectionSponsorship()
+ .call()).to.be.rejectedWith('caller is not set as sponsor');
+ }
+ {
+ await expect(contractEvmFromNotOwner.methods
+ .setCollectionLimit('account_token_ownership_limit', '1000')
+ .call()).to.be.rejectedWith(EXPECTED_ERROR);
+ }
+ });
+
+ itWeb3('(!negative test!) Set limits', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const collectionHelpers = evmCollectionHelpers(web3, owner);
+ const result = await collectionHelpers.methods.createRefungibleCollection('Schema collection', 'A', 'A').send();
+ const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
+ const collectionEvm = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'});
+ await expect(collectionEvm.methods
+ .setCollectionLimit('badLimit', 'true')
+ .call()).to.be.rejectedWith('unknown boolean limit "badLimit"');
+ });
+});
\ No newline at end of file
tests/src/eth/nonFungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/nonFungible.test.ts
+++ b/tests/src/eth/nonFungible.test.ts
@@ -72,6 +72,147 @@
});
});
+describe('Check ERC721 token URI for NFT', () => {
+ itWeb3('Empty tokenURI', async ({web3, api, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const helper = evmCollectionHelpers(web3, owner);
+ let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', '').send();
+ const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+ const receiver = createEthAccount(web3);
+ const contract = evmCollection(web3, owner, collectionIdAddress);
+
+ const nextTokenId = await contract.methods.nextTokenId().call();
+ expect(nextTokenId).to.be.equal('1');
+ result = await contract.methods.mint(
+ receiver,
+ nextTokenId,
+ ).send();
+
+ const events = normalizeEvents(result.events);
+ const address = collectionIdToAddress(collectionId);
+
+ expect(events).to.be.deep.equal([
+ {
+ address,
+ event: 'Transfer',
+ args: {
+ from: '0x0000000000000000000000000000000000000000',
+ to: receiver,
+ tokenId: nextTokenId,
+ },
+ },
+ ]);
+
+ expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('');
+ });
+
+ itWeb3('TokenURI from url', async ({web3, api, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const helper = evmCollectionHelpers(web3, owner);
+ let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', 'BaseURI_').send();
+ const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+ const receiver = createEthAccount(web3);
+ const contract = evmCollection(web3, owner, collectionIdAddress);
+
+ const nextTokenId = await contract.methods.nextTokenId().call();
+ expect(nextTokenId).to.be.equal('1');
+ result = await contract.methods.mint(
+ receiver,
+ nextTokenId,
+ ).send();
+
+ // Set URL
+ await contract.methods.setProperty(nextTokenId, 'url', Buffer.from('Token URI')).send();
+
+ const events = normalizeEvents(result.events);
+ const address = collectionIdToAddress(collectionId);
+
+ expect(events).to.be.deep.equal([
+ {
+ address,
+ event: 'Transfer',
+ args: {
+ from: '0x0000000000000000000000000000000000000000',
+ to: receiver,
+ tokenId: nextTokenId,
+ },
+ },
+ ]);
+
+ expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');
+ });
+
+ itWeb3('TokenURI from baseURI + tokenId', async ({web3, api, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const helper = evmCollectionHelpers(web3, owner);
+ let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', 'BaseURI_').send();
+ const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+ const receiver = createEthAccount(web3);
+ const contract = evmCollection(web3, owner, collectionIdAddress);
+
+ const nextTokenId = await contract.methods.nextTokenId().call();
+ expect(nextTokenId).to.be.equal('1');
+ result = await contract.methods.mint(
+ receiver,
+ nextTokenId,
+ ).send();
+
+ const events = normalizeEvents(result.events);
+ const address = collectionIdToAddress(collectionId);
+
+ expect(events).to.be.deep.equal([
+ {
+ address,
+ event: 'Transfer',
+ args: {
+ from: '0x0000000000000000000000000000000000000000',
+ to: receiver,
+ tokenId: nextTokenId,
+ },
+ },
+ ]);
+
+ expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + nextTokenId);
+ });
+
+ itWeb3('TokenURI from baseURI + suffix', async ({web3, api, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const helper = evmCollectionHelpers(web3, owner);
+ let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', 'BaseURI_').send();
+ const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+ const receiver = createEthAccount(web3);
+ const contract = evmCollection(web3, owner, collectionIdAddress);
+
+ const nextTokenId = await contract.methods.nextTokenId().call();
+ expect(nextTokenId).to.be.equal('1');
+ result = await contract.methods.mint(
+ receiver,
+ nextTokenId,
+ ).send();
+
+ // Set suffix
+ const suffix = '/some/suffix';
+ await contract.methods.setProperty(nextTokenId, 'suffix', Buffer.from(suffix)).send();
+
+ const events = normalizeEvents(result.events);
+ const address = collectionIdToAddress(collectionId);
+
+ expect(events).to.be.deep.equal([
+ {
+ address,
+ event: 'Transfer',
+ args: {
+ from: '0x0000000000000000000000000000000000000000',
+ to: receiver,
+ tokenId: nextTokenId,
+ },
+ },
+ ]);
+
+ expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);
+ });
+});
+
describe('NFT: Plain calls', () => {
itWeb3('Can perform mint()', async ({web3, api, privateKeyWrapper}) => {
const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
tests/src/eth/reFungibleToken.test.tsdiffbeforeafterboth--- a/tests/src/eth/reFungibleToken.test.ts
+++ b/tests/src/eth/reFungibleToken.test.ts
@@ -15,7 +15,7 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import {approve, createCollection, createRefungibleToken, transfer, transferFrom, UNIQUE} from '../util/helpers';
-import {createEthAccount, createEthAccountWithBalance, GAS_ARGS, itWeb3, normalizeEvents, recordEthFee, recordEvents, subToEth, tokenIdToAddress, transferBalanceToEth} from './util/helpers';
+import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, evmCollection, evmCollectionHelpers, GAS_ARGS, getCollectionAddressFromResult, itWeb3, normalizeEvents, recordEthFee, recordEvents, subToEth, tokenIdToAddress, transferBalanceToEth} from './util/helpers';
import reFungibleTokenAbi from './reFungibleTokenAbi.json';
import chai from 'chai';
@@ -73,6 +73,148 @@
});
});
+// FIXME: Need erc721 for ReFubgible.
+describe.skip('Check ERC721 token URI for ReFungible', () => {
+ itWeb3('Empty tokenURI', async ({web3, api, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const helper = evmCollectionHelpers(web3, owner);
+ let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', '').send();
+ const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+ const receiver = createEthAccount(web3);
+ const contract = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'});
+
+ const nextTokenId = await contract.methods.nextTokenId().call();
+ expect(nextTokenId).to.be.equal('1');
+ result = await contract.methods.mint(
+ receiver,
+ nextTokenId,
+ ).send();
+
+ const events = normalizeEvents(result.events);
+ const address = collectionIdToAddress(collectionId);
+
+ expect(events).to.be.deep.equal([
+ {
+ address,
+ event: 'Transfer',
+ args: {
+ from: '0x0000000000000000000000000000000000000000',
+ to: receiver,
+ tokenId: nextTokenId,
+ },
+ },
+ ]);
+
+ expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('');
+ });
+
+ itWeb3('TokenURI from url', async ({web3, api, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const helper = evmCollectionHelpers(web3, owner);
+ let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', 'BaseURI_').send();
+ const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+ const receiver = createEthAccount(web3);
+ const contract = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'});
+
+ const nextTokenId = await contract.methods.nextTokenId().call();
+ expect(nextTokenId).to.be.equal('1');
+ result = await contract.methods.mint(
+ receiver,
+ nextTokenId,
+ ).send();
+
+ // Set URL
+ await contract.methods.setProperty(nextTokenId, 'url', Buffer.from('Token URI')).send();
+
+ const events = normalizeEvents(result.events);
+ const address = collectionIdToAddress(collectionId);
+
+ expect(events).to.be.deep.equal([
+ {
+ address,
+ event: 'Transfer',
+ args: {
+ from: '0x0000000000000000000000000000000000000000',
+ to: receiver,
+ tokenId: nextTokenId,
+ },
+ },
+ ]);
+
+ expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');
+ });
+
+ itWeb3('TokenURI from baseURI + tokenId', async ({web3, api, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const helper = evmCollectionHelpers(web3, owner);
+ let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', 'BaseURI_').send();
+ const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+ const receiver = createEthAccount(web3);
+ const contract = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'});
+
+ const nextTokenId = await contract.methods.nextTokenId().call();
+ expect(nextTokenId).to.be.equal('1');
+ result = await contract.methods.mint(
+ receiver,
+ nextTokenId,
+ ).send();
+
+ const events = normalizeEvents(result.events);
+ const address = collectionIdToAddress(collectionId);
+
+ expect(events).to.be.deep.equal([
+ {
+ address,
+ event: 'Transfer',
+ args: {
+ from: '0x0000000000000000000000000000000000000000',
+ to: receiver,
+ tokenId: nextTokenId,
+ },
+ },
+ ]);
+
+ expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + nextTokenId);
+ });
+
+ itWeb3('TokenURI from baseURI + suffix', async ({web3, api, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const helper = evmCollectionHelpers(web3, owner);
+ let result = await helper.methods.createERC721MetadataCompatibleCollection('Mint collection', '1', '1', 'BaseURI_').send();
+ const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+ const receiver = createEthAccount(web3);
+ const contract = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'});
+
+ const nextTokenId = await contract.methods.nextTokenId().call();
+ expect(nextTokenId).to.be.equal('1');
+ result = await contract.methods.mint(
+ receiver,
+ nextTokenId,
+ ).send();
+
+ // Set suffix
+ const suffix = '/some/suffix';
+ await contract.methods.setProperty(nextTokenId, 'suffix', Buffer.from(suffix)).send();
+
+ const events = normalizeEvents(result.events);
+ const address = collectionIdToAddress(collectionId);
+
+ expect(events).to.be.deep.equal([
+ {
+ address,
+ event: 'Transfer',
+ args: {
+ from: '0x0000000000000000000000000000000000000000',
+ to: receiver,
+ tokenId: nextTokenId,
+ },
+ },
+ ]);
+
+ expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);
+ });
+});
+
describe('Refungible: Plain calls', () => {
itWeb3('Can perform approve()', async ({web3, api, privateKeyWrapper}) => {
const alice = privateKeyWrapper('//Alice');
tests/src/eth/refungibleAbi.jsondiffbeforeafterboth--- /dev/null
+++ b/tests/src/eth/refungibleAbi.json
@@ -0,0 +1,167 @@
+[
+ {
+ "inputs": [
+ { "internalType": "address", "name": "newAdmin", "type": "address" }
+ ],
+ "name": "addCollectionAdmin",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ { "internalType": "uint256", "name": "newAdmin", "type": "uint256" }
+ ],
+ "name": "addCollectionAdminSubstrate",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ { "internalType": "address", "name": "user", "type": "address" }
+ ],
+ "name": "addToCollectionAllowList",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
+ "name": "collectionProperty",
+ "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "confirmCollectionSponsorship",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "contractAddress",
+ "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
+ "name": "deleteCollectionProperty",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ { "internalType": "address", "name": "admin", "type": "address" }
+ ],
+ "name": "removeCollectionAdmin",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ { "internalType": "uint256", "name": "admin", "type": "uint256" }
+ ],
+ "name": "removeCollectionAdminSubstrate",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ { "internalType": "address", "name": "user", "type": "address" }
+ ],
+ "name": "removeFromCollectionAllowList",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [{ "internalType": "uint8", "name": "mode", "type": "uint8" }],
+ "name": "setCollectionAccess",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ { "internalType": "string", "name": "limit", "type": "string" },
+ { "internalType": "uint32", "name": "value", "type": "uint32" }
+ ],
+ "name": "setCollectionLimit",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ { "internalType": "string", "name": "limit", "type": "string" },
+ { "internalType": "bool", "name": "value", "type": "bool" }
+ ],
+ "name": "setCollectionLimit",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }],
+ "name": "setCollectionMintMode",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }],
+ "name": "setCollectionNesting",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ { "internalType": "bool", "name": "enable", "type": "bool" },
+ {
+ "internalType": "address[]",
+ "name": "collections",
+ "type": "address[]"
+ }
+ ],
+ "name": "setCollectionNesting",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ { "internalType": "string", "name": "key", "type": "string" },
+ { "internalType": "bytes", "name": "value", "type": "bytes" }
+ ],
+ "name": "setCollectionProperty",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ { "internalType": "address", "name": "sponsor", "type": "address" }
+ ],
+ "name": "setCollectionSponsor",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
+ ],
+ "name": "supportsInterface",
+ "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+ "stateMutability": "view",
+ "type": "function"
+ }
+]
tests/src/eth/util/helpers.tsdiffbeforeafterboth--- a/tests/src/eth/util/helpers.ts
+++ b/tests/src/eth/util/helpers.ts
@@ -25,12 +25,13 @@
import Web3 from 'web3';
import config from '../../config';
import getBalance from '../../substrate/get-balance';
-import privateKey from '../../substrate/privateKey';
import usingApi, {submitTransactionAsync} from '../../substrate/substrate-api';
import waitNewBlocks from '../../substrate/wait-new-blocks';
-import {CrossAccountId, getDetailedCollectionInfo, getGenericResult, UNIQUE} from '../../util/helpers';
+import {CollectionMode, CrossAccountId, getDetailedCollectionInfo, getGenericResult, UNIQUE} from '../../util/helpers';
import collectionHelpersAbi from '../collectionHelpersAbi.json';
+import fungibleAbi from '../fungibleAbi.json';
import nonFungibleAbi from '../nonFungibleAbi.json';
+import refungibleAbi from '../refungibleAbi.json';
import contractHelpersAbi from './contractHelpersAbi.json';
export const GAS_ARGS = {gas: 2500000};
@@ -307,8 +308,26 @@
* @param caller - eth address
* @returns
*/
-export function evmCollection(web3: Web3, caller: string, collection: string) {
- return new web3.eth.Contract(nonFungibleAbi as any, collection, {from: caller, ...GAS_ARGS});
+export function evmCollection(web3: Web3, caller: string, collection: string, mode: CollectionMode = {type: 'NFT'}) {
+ let abi;
+ switch (mode.type) {
+ case 'Fungible':
+ abi = fungibleAbi;
+ break;
+
+ case 'NFT':
+ abi = nonFungibleAbi;
+ break;
+
+ case 'ReFungible':
+ abi = refungibleAbi;
+ break;
+
+ default:
+ throw 'Bad collection mode';
+ }
+ const contract = new web3.eth.Contract(abi as any, collection, {from: caller, ...GAS_ARGS});
+ return contract;
}
/**