difftreelog
Merge pull request #31 from usetech-llc/feature/NFTPAR-183
in: master
Feature/NFTPAR-183 Storage Refactoring.
3 files changed
README.mddiffbeforeafterboth123# NFT Parachain45## Project Description67The NFT Pallet is the core of NFT functionality. Like ERC-721 standard in Ethereum ecosystem, this pallet provides the basement for creating collections of unique non-divisible things, also called Non Fungible Tokens (NFTs), minting NFT of a given Collection, and managing their ownership.89The pallet also enables storing NFT properties. Though (according to ERC-721) NFT properties belong to logic of a concrete application that operates a Collection, so purposefully the NFT Tracking Module does not have any knowledge about properties except their byte size leaving application logic out to be controlled by Smart Contracts.1011The NFT Chain also provides:1213* Smart Contracts Pallet and example smart contract that interacts with NFT Runtime14* ERC-1155 Functionality (currently PoC as Re-Fungible tokens, i.e. items that are still unique, but that can be split between multiple users)15* Variety of economic options for dapp producers to choose from to create freemium games and other ways to attract users. As a step one, we implemented an economic model when a collection sponsor can be set to pay for collection Transfer transactions.1617Wider NFT Ecosystem (most of it was developed during Hackusama):18* [SubstraPunks Game hosted on IPFS](https://github.com/usetech-llc/substrapunks)19* [NFT Wallet and UI](https://uniqueapps.usetech.com/#/nft)20* [NFT Asset for Unity Framework](https://github.com/usetech-llc/nft_unity)2122Please see our [walk-thorugh instructions](doc/hackusama_walk_through.md) to try everything out!2324## Hackusama Update2526During the Kusama Hackaphon the following changes were made:27* Enabled Smart Contracts Pallet28* Enabled integration between Smart Contracts and NFT Pallet (required special edition of RC4 Substrate version)29* Fixed misc. bugs in NFT Pallet30* Deployed NFT TestNet. Public node available at wss://unique.usetech.com, custom UI types - see below in this README.31* New Features:32 * Re-Fungible Token Mode33 * Off-Chain Schema to store token image URLs34 * Alternative economic model35 * White Lists and Public Mint Permission36* Use example: [SubstraPunks Game](https://github.com/usetech-llc/substrapunks), fully hosted on IPFS and NFT Testnet Blockchain.3738## Application Development3940If you are building an application that operates NFT tokens, use [this document](doc/application_development.md).4142## Building4344Building NFT chain requires special versions of Rust and toolchain. We don't use the most recent versions of everything so that we can keep the builds stable.45461. Install Rust:4748```bash49curl https://sh.rustup.rs -sSf | sh50sudo apt-get install libssl-dev pkg-config libclang-dev clang51```52532. Remove all installed toolchains with `rustup toolchain list` and `rustup toolchain uninstall <toolchain>`.54553. Install Rust Toolchain 1.44.0:5657```bash58rustup install 1.44.059```60614. Make it default (actual toochain version may be different, so do a `rustup toolchain list` first)62```bash63rustup toolchain list64rustup default 1.44.0-x86_64-unknown-linux-gnu65```66675. Install nightly toolchain and add wasm target for it:6869```bash70rustup toolchain install nightly-2020-05-0171rustup target add wasm32-unknown-unknown --toolchain nightly-2020-05-01-x86_64-unknown-linux-gnu72```73746. Build:75```bash76cargo build77```7879## Run8081You can start a development chain with:8283```bash84cargo run -- --dev85```8687Detailed logs may be shown by running the node with the following environment variables set: `RUST_LOG=debug RUST_BACKTRACE=1 cargo run -- --dev`.8889If you want to see the multi-node consensus algorithm in action locally, then you can create a local testnet with two validator nodes for Alice and Bob, who are the initial authorities of the genesis chain that have been endowed with testnet units. Give each node a name and expose them so they are listed on the Polkadot [telemetry site](https://telemetry.polkadot.io/#/Local%20Testnet). You'll need two terminal windows open.9091We'll start Alice's substrate node first on default TCP port 30333 with her chain database stored locally at `/tmp/alice`. The bootnode ID of her node is `QmQZ8TjTqeDj3ciwr93EJ95hxfDsb9pEYDizUAbWpigtQN`, which is generated from the `--node-key` value that we specify below:9293```bash94cargo run -- \95 --base-path /tmp/alice \96 --chain=local \97 --alice \98 --node-key 0000000000000000000000000000000000000000000000000000000000000001 \99 --telemetry-url ws://telemetry.polkadot.io:1024 \100 --validator101```102103In the second terminal, we'll start Bob's substrate node on a different TCP port of 30334, and with his chain database stored locally at `/tmp/bob`. We'll specify a value for the `--bootnodes` option that will connect his node to Alice's bootnode ID on TCP port 30333:104105```bash106cargo run -- \107 --base-path /tmp/bob \108 --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/QmQZ8TjTqeDj3ciwr93EJ95hxfDsb9pEYDizUAbWpigtQN \109 --chain=local \110 --bob \111 --port 30334 \112 --telemetry-url ws://telemetry.polkadot.io:1024 \113 --validator114```115116Additional CLI usage options are available and may be shown by running `cargo run -- --help`.117118## Benchmarks119120First of all, add rust toolchain and make it default.121```bash122rustup target add wasm32-unknown-unknown --toolchain nightly-2020-10-01123```124125Then in "/node/src" run build command below126```bash127cargo +nightly-2020-10-01 build --release --features runtime-benchmarks128```129130Run benchmark131```bash132target/release/nft benchmark --chain dev --pallet "pallet_nft" --extrinsic "*" --repeat 1133```134135## UI custom types136```137{138 "Schedule": {139 "version": "u32",140 "put_code_per_byte_cost": "Gas",141 "grow_mem_cost": "Gas",142 "regular_op_cost": "Gas",143 "return_data_per_byte_cost": "Gas",144 "event_data_per_byte_cost": "Gas",145 "event_per_topic_cost": "Gas",146 "event_base_cost": "Gas",147 "call_base_cost": "Gas",148 "instantiate_base_cost": "Gas",149 "dispatch_base_cost": "Gas",150 "sandbox_data_read_cost": "Gas",151 "sandbox_data_write_cost": "Gas",152 "transfer_cost": "Gas",153 "instantiate_cost": "Gas",154 "max_event_topics": "u32",155 "max_stack_height": "u32",156 "max_memory_pages": "u32",157 "max_table_size": "u32",158 "enable_println": "bool",159 "max_subject_len": "u32"160 },161 "AccessMode": {162 "_enum": [163 "Normal",164 "WhiteList"165 ]166 },167 "CollectionMode": {168 "_enum": {169 "Invalid": null,170 "NFT": null,171 "Fungible": "u32",172 "ReFungible": "u32"173 }174 },175 "Ownership": {176 "Owner": "AccountId",177 "Fraction": "u128"178 },179 "FungibleItemType": {180 "Collection": "u64",181 "Owner": "AccountId",182 "Value": "u128"183 },184 "ReFungibleItemType": {185 "Collection": "u64",186 "Owner": "Vec<Ownership>",187 "Data": "Vec<u8>"188 },189 "NftItemType": {190 "Collection": "u64",191 "Owner": "AccountId",192 "ConstData": "Vec<u8>",193 "VariableData": "Vec<u8>"194 },195 "Ownership": {196 "owner": "AccountId",197 "fraction": "u128"198 },199 "ReFungibleItemType": {200 "Collection": "u64",201 "Owner": "Vec<Ownership<AccountId>>",202 "ConstData": "Vec<u8>",203 "VariableData": "Vec<u8>"204 },205 "CollectionType": {206 "Owner": "AccountId",207 "Mode": "CollectionMode",208 "Access": "AccessMode",209 "DecimalPoints": "u32",210 "Name": "Vec<u16>",211 "Description": "Vec<u16>",212 "TokenPrefix": "Vec<u8>",213 "MintMode": "bool",214 "OffchainSchema": "Vec<u8>",215 "Sponsor": "AccountId",216 "UnconfirmedSponsor": "AccountId",217 "VariableOnChainSchema": "Vec<u8>",218 "ConstOnChainSchema": "Vec<u8>"219 },220 "ApprovePermissions": {221 "Approved": "AccountId",222 "Amount": "u64"223 },224 "RawData": "Vec<u8>",225 "Address": "AccountId",226 "LookupSource": "AccountId",227 "Weight": "u64",228 "CreateNftData": {229 "const_data": "Vec<u8>",230 "variable_data": "Vec<u8>" 231 },232 "CreateFungibleData": {},233 "CreateReFungibleData": {234 "const_data": "Vec<u8>",235 "variable_data": "Vec<u8>" 236 },237 "CreateItemData": {238 "_enum": {239 "NFT": "CreateNftData",240 "Fungible": "CreateFungibleData",241 "ReFungible": "CreateReFungibleData"242 }243 }244}245246```pallets/nft/src/lib.rsdiffbeforeafterboth--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -45,18 +45,25 @@
mod default_weights;
+pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;
+
// Structs
// #region
+pub type CollectionId = u32;
+pub type TokenId = u32;
+
+pub type DecimalPoints = u8;
+
#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub enum CollectionMode {
Invalid,
NFT,
// decimal points
- Fungible(u32),
+ Fungible(DecimalPoints),
// decimal points
- ReFungible(u32),
+ ReFungible(DecimalPoints),
}
impl Into<u8> for CollectionMode {
@@ -101,7 +108,7 @@
pub owner: AccountId,
pub mode: CollectionMode,
pub access: AccessMode,
- pub decimal_points: u32,
+ pub decimal_points: DecimalPoints,
pub name: Vec<u16>, // 64 include null escape char
pub description: Vec<u16>, // 256 include null escape char
pub token_prefix: Vec<u8>, // 16 include null escape char
@@ -116,7 +123,7 @@
#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct NftItemType<AccountId> {
- pub collection: u64,
+ pub collection: CollectionId,
pub owner: AccountId,
pub const_data: Vec<u8>,
pub variable_data: Vec<u8>,
@@ -125,7 +132,7 @@
#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct FungibleItemType<AccountId> {
- pub collection: u64,
+ pub collection: CollectionId,
pub owner: AccountId,
pub value: u128,
}
@@ -133,7 +140,7 @@
#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct ReFungibleItemType<AccountId> {
- pub collection: u64,
+ pub collection: CollectionId,
pub owner: Vec<Ownership<AccountId>>,
pub const_data: Vec<u8>,
pub variable_data: Vec<u8>,
@@ -143,7 +150,7 @@
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct ApprovePermissions<AccountId> {
pub approved: AccountId,
- pub amount: u64,
+ pub amount: u128,
}
#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
@@ -151,8 +158,8 @@
pub struct VestingItem<AccountId, Moment> {
pub sender: AccountId,
pub recipient: AccountId,
- pub collection_id: u64,
- pub item_id: u64,
+ pub collection_id: CollectionId,
+ pub item_id: TokenId,
pub amount: u64,
pub vesting_date: Moment,
}
@@ -167,8 +174,8 @@
#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct ChainLimits {
- pub collection_numbers_limit: u64,
- pub account_token_ownership_limit: u64,
+ pub collection_numbers_limit: u32,
+ pub account_token_ownership_limit: u32,
pub collections_admins_limit: u64,
pub custom_data_limit: u32,
@@ -266,7 +273,7 @@
pub enum Error for Module<T: Trait> {
/// Total collections bound exceeded.
TotalCollectionsLimitExceeded,
- /// Decimal_points parameter must be lower than 4.
+ /// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.
CollectionDecimalPointLimitExceeded,
/// Collection name can not be longer than 63 char.
CollectionNameLimitExceeded,
@@ -341,41 +348,41 @@
trait Store for Module<T: Trait> as Nft {
// Private members
- NextCollectionID: u64;
- CreatedCollectionCount: u64;
+ NextCollectionID: CollectionId;
+ CreatedCollectionCount: u32;
ChainVersion: u64;
- ItemListIndex: map hasher(blake2_128_concat) u64 => u64;
+ ItemListIndex: map hasher(identity) CollectionId => TokenId;
// Chain limits struct
pub ChainLimit get(fn chain_limit) config(): ChainLimits;
// Bound counters
- CollectionCount: u64;
- pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;
+ CollectionCount: u32;
+ pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;
// Basic collections
- pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;
- pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;
- pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;
+ pub Collection get(fn collection) config(): map hasher(identity) CollectionId => CollectionType<T::AccountId>;
+ pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;
+ pub WhiteList get(fn white_list): map hasher(identity) CollectionId => Vec<T::AccountId>;
/// Balance owner per collection map
- pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;
+ pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;
/// second parameter: item id + owner account id
- pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;
+ pub ApprovedList get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;
/// Item collections
- pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;
- pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;
- pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;
+ pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;
+ pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => FungibleItemType<T::AccountId>;
+ pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;
/// Index list
- pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;
+ pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;
/// Tokens transfer baskets
- pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;
- pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => Vec<BasketItem<T::AccountId, T::BlockNumber>>;
- pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;
+ pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;
+ pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Vec<BasketItem<T::AccountId, T::BlockNumber>>;
+ pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;
// Contract Sponsorship and Ownership
pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;
@@ -419,7 +426,7 @@
/// * mode: [CollectionMode] converted into u8.
///
/// * account_id: Collection owner.
- Created(u64, u8, AccountId),
+ Created(CollectionId, u8, AccountId),
/// New item was created.
///
@@ -428,7 +435,7 @@
/// * collection_id: Id of the collection where item was created.
///
/// * item_id: Id of an item. Unique within the collection.
- ItemCreated(u64, u64),
+ ItemCreated(CollectionId, TokenId),
/// Collection item was burned.
///
@@ -437,7 +444,7 @@
/// collection_id.
///
/// item_id: Identifier of burned NFT.
- ItemDestroyed(u64, u64),
+ ItemDestroyed(CollectionId, TokenId),
}
);
@@ -495,7 +502,7 @@
ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);
// check params
- ensure!(decimal_points <= 4, Error::<T>::CollectionDecimalPointLimitExceeded);
+ ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);
let mut name = collection_name.to_vec();
name.push(0);
@@ -558,7 +565,7 @@
///
/// * collection_id: collection to destroy.
#[weight = T::WeightInfo::destroy_collection()]
- pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {
+ pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {
let sender = ensure_signed(origin)?;
Self::check_owner_permissions(collection_id, sender)?;
@@ -605,7 +612,7 @@
///
/// * address.
#[weight = T::WeightInfo::add_to_white_list()]
- pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{
+ pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{
let sender = ensure_signed(origin)?;
Self::check_owner_or_admin_permissions(collection_id, sender)?;
@@ -640,7 +647,7 @@
///
/// * address.
#[weight = T::WeightInfo::remove_from_white_list()]
- pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{
+ pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{
let sender = ensure_signed(origin)?;
Self::check_owner_or_admin_permissions(collection_id, sender)?;
@@ -669,7 +676,7 @@
///
/// * mode: [AccessMode]
#[weight = T::WeightInfo::set_public_access_mode()]
- pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult
+ pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult
{
let sender = ensure_signed(origin)?;
@@ -695,7 +702,7 @@
///
/// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.
#[weight = T::WeightInfo::set_mint_permission()]
- pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult
+ pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult
{
let sender = ensure_signed(origin)?;
@@ -719,7 +726,7 @@
///
/// * new_owner.
#[weight = T::WeightInfo::change_collection_owner()]
- pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {
+ pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {
let sender = ensure_signed(origin)?;
Self::check_owner_permissions(collection_id, sender)?;
@@ -744,7 +751,7 @@
///
/// * new_admin_id: Address of new admin to add.
#[weight = T::WeightInfo::add_collection_admin()]
- pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {
+ pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {
let sender = ensure_signed(origin)?;
Self::check_owner_or_admin_permissions(collection_id, sender)?;
@@ -778,7 +785,7 @@
///
/// * account_id: Address of admin to remove.
#[weight = T::WeightInfo::remove_collection_admin()]
- pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {
+ pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {
let sender = ensure_signed(origin)?;
Self::check_owner_or_admin_permissions(collection_id, sender)?;
@@ -803,7 +810,7 @@
///
/// * new_sponsor.
#[weight = T::WeightInfo::set_collection_sponsor()]
- pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {
+ pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {
let sender = ensure_signed(origin)?;
ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);
@@ -825,7 +832,7 @@
///
/// * collection_id.
#[weight = T::WeightInfo::confirm_sponsorship()]
- pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {
+ pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {
let sender = ensure_signed(origin)?;
ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);
@@ -850,7 +857,7 @@
///
/// * collection_id.
#[weight = T::WeightInfo::remove_collection_sponsor()]
- pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {
+ pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {
let sender = ensure_signed(origin)?;
ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);
@@ -889,7 +896,7 @@
// .saturating_add(RocksDbWeight::get().writes(8 as Weight))]
#[weight = T::WeightInfo::create_item(data.len())]
- pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {
+ pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {
let sender = ensure_signed(origin)?;
@@ -925,7 +932,7 @@
#[weight = T::WeightInfo::create_item(items_data.into_iter()
.map(|data| { data.len() })
.sum())]
- pub fn create_multiple_items(origin, collection_id: u64, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {
+ pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {
ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);
let sender = ensure_signed(origin)?;
@@ -959,7 +966,7 @@
///
/// * item_id: ID of NFT to burn.
#[weight = T::WeightInfo::burn_item()]
- pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {
+ pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {
let sender = ensure_signed(origin)?;
Self::collection_exists(collection_id)?;
@@ -1012,7 +1019,7 @@
/// * Fungible Mode: Must specify transferred amount
/// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)
#[weight = T::WeightInfo::transfer()]
- pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {
+ pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {
let sender = ensure_signed(origin)?;
@@ -1054,7 +1061,7 @@
///
/// * item_id: ID of the item.
#[weight = T::WeightInfo::approve()]
- pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {
+ pub fn approve(origin, approved: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {
let sender = ensure_signed(origin)?;
@@ -1112,7 +1119,7 @@
///
/// * value: Amount to transfer.
#[weight = T::WeightInfo::transfer_from()]
- pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {
+ pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {
let sender = ensure_signed(origin)?;
let mut appoved_transfer = false;
@@ -1157,7 +1164,7 @@
///
#[weight = 0]
- pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {
+ pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {
// let no_perm_mes = "You do not have permissions to modify this collection";
// ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);
@@ -1186,8 +1193,8 @@
#[weight = T::WeightInfo::set_variable_meta_data()]
pub fn set_variable_meta_data (
origin,
- collection_id: u64,
- item_id: u64,
+ collection_id: CollectionId,
+ item_id: TokenId,
data: Vec<u8>
) -> DispatchResult {
let sender = ensure_signed(origin)?;
@@ -1231,7 +1238,7 @@
#[weight = T::WeightInfo::set_offchain_schema()]
pub fn set_offchain_schema(
origin,
- collection_id: u64,
+ collection_id: CollectionId,
schema: Vec<u8>
) -> DispatchResult {
let sender = ensure_signed(origin)?;
@@ -1259,7 +1266,7 @@
#[weight = T::WeightInfo::set_const_on_chain_schema()]
pub fn set_const_on_chain_schema (
origin,
- collection_id: u64,
+ collection_id: CollectionId,
schema: Vec<u8>
) -> DispatchResult {
let sender = ensure_signed(origin)?;
@@ -1287,7 +1294,7 @@
#[weight = T::WeightInfo::set_const_on_chain_schema()]
pub fn set_variable_on_chain_schema (
origin,
- collection_id: u64,
+ collection_id: CollectionId,
schema: Vec<u8>
) -> DispatchResult {
let sender = ensure_signed(origin)?;
@@ -1396,7 +1403,7 @@
impl<T: Trait> Module<T> {
- fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {
+ fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {
if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {
ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);
@@ -1441,7 +1448,7 @@
Ok(())
}
- fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {
+ fn create_item_no_validation(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {
match data
{
CreateItemData::NFT(data) => {
@@ -1458,14 +1465,14 @@
let item = FungibleItemType {
collection: collection_id,
owner,
- value: (10 as u128).pow(collection.decimal_points)
+ value: (10 as u128).pow(collection.decimal_points as u32)
};
Self::add_fungible_item(item)?;
},
CreateItemData::ReFungible(data) => {
let mut owner_list = Vec::new();
- let value = (10 as u128).pow(collection.decimal_points);
+ let value = (10 as u128).pow(collection.decimal_points as u32);
owner_list.push(Ownership {owner: owner.clone(), fraction: value});
let item = ReFungibleItemType {
@@ -1492,7 +1499,6 @@
.ok_or(Error::<T>::NumOverflow)?;
let itemcopy = item.clone();
let owner = item.owner.clone();
- let value = item.value as u64;
Self::add_token_index(item.collection, current_index, owner.clone())?;
@@ -1505,7 +1511,7 @@
// Update balance
let new_balance = <Balance<T>>::get(item.collection, owner.clone())
- .checked_add(value)
+ .checked_add(item.value)
.ok_or(Error::<T>::NumOverflow)?;
<Balance<T>>::insert(item.collection, owner.clone(), new_balance);
@@ -1518,7 +1524,7 @@
.ok_or(Error::<T>::NumOverflow)?;
let itemcopy = item.clone();
- let value = item.owner.first().unwrap().fraction as u64;
+ let value = item.owner.first().unwrap().fraction;
let owner = item.owner.first().unwrap().owner.clone();
Self::add_token_index(item.collection, current_index, owner.clone())?;
@@ -1565,8 +1571,8 @@
}
fn burn_refungible_item(
- collection_id: u64,
- item_id: u64,
+ collection_id: CollectionId,
+ item_id: TokenId,
owner: T::AccountId,
) -> DispatchResult {
ensure!(
@@ -1587,7 +1593,7 @@
// update balance
let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())
- .checked_sub(item.fraction as u64)
+ .checked_sub(item.fraction)
.ok_or(Error::<T>::NumOverflow)?;
<Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);
@@ -1596,7 +1602,7 @@
Ok(())
}
- fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {
+ fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {
ensure!(
<NftItemList<T>>::contains_key(collection_id, item_id),
Error::<T>::TokenNotFound
@@ -1617,7 +1623,7 @@
Ok(())
}
- fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {
+ fn burn_fungible_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {
ensure!(
<FungibleItemList<T>>::contains_key(collection_id, item_id),
Error::<T>::TokenNotFound
@@ -1630,7 +1636,7 @@
// update balance
let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())
- .checked_sub(item.value as u64)
+ .checked_sub(item.value)
.ok_or(Error::<T>::NumOverflow)?;
<Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);
@@ -1639,7 +1645,7 @@
Ok(())
}
- fn collection_exists(collection_id: u64) -> DispatchResult {
+ fn collection_exists(collection_id: CollectionId) -> DispatchResult {
ensure!(
<Collection<T>>::contains_key(collection_id),
Error::<T>::CollectionNotFound
@@ -1647,7 +1653,7 @@
Ok(())
}
- fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {
+ fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {
Self::collection_exists(collection_id)?;
let target_collection = <Collection<T>>::get(collection_id);
@@ -1659,7 +1665,7 @@
Ok(())
}
- fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {
+ fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {
let target_collection = <Collection<T>>::get(collection_id);
let mut result: bool = subject == target_collection.owner;
let exists = <AdminList<T>>::contains_key(collection_id);
@@ -1674,7 +1680,7 @@
}
fn check_owner_or_admin_permissions(
- collection_id: u64,
+ collection_id: CollectionId,
subject: T::AccountId,
) -> DispatchResult {
Self::collection_exists(collection_id)?;
@@ -1687,7 +1693,7 @@
Ok(())
}
- fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {
+ fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {
let target_collection = <Collection<T>>::get(collection_id);
match target_collection.mode {
@@ -1707,7 +1713,7 @@
}
}
- fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {
+ fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {
let mes = Error::<T>::AddresNotInWhiteList;
ensure!(<WhiteList<T>>::contains_key(collection_id), mes);
let wl = <WhiteList<T>>::get(collection_id);
@@ -1717,9 +1723,9 @@
}
fn transfer_fungible(
- collection_id: u64,
- item_id: u64,
- value: u64,
+ collection_id: CollectionId,
+ item_id: TokenId,
+ value: u128,
owner: T::AccountId,
new_owner: T::AccountId,
) -> DispatchResult {
@@ -1731,7 +1737,7 @@
let full_item = <FungibleItemList<T>>::get(collection_id, item_id);
let amount = full_item.value;
- ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);
+ ensure!(amount >= value, Error::<T>::TokenValueTooLow);
// update balance
let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())
@@ -1745,10 +1751,8 @@
new_owner_account_id = new_owner_items[0];
}
- let val64 = value.into();
-
// transfer
- if amount == val64 && new_owner_account_id == 0 {
+ if amount == value && new_owner_account_id == 0 {
// change owner
// new owner do not have account
let mut new_full_item = full_item.clone();
@@ -1765,13 +1769,13 @@
Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;
} else {
let mut new_full_item = full_item.clone();
- new_full_item.value -= val64;
+ new_full_item.value -= value;
// separate amount
if new_owner_account_id > 0 {
// new owner has account
let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);
- item.value += val64;
+ item.value += value;
// update balance
let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())
@@ -1785,13 +1789,13 @@
let item = FungibleItemType {
collection: collection_id,
owner: new_owner.clone(),
- value: val64,
+ value
};
Self::add_fungible_item(item)?;
}
- if amount == val64 {
+ if amount == value {
Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;
// remove approve list
@@ -1806,9 +1810,9 @@
}
fn transfer_refungible(
- collection_id: u64,
- item_id: u64,
- value: u64,
+ collection_id: CollectionId,
+ item_id: TokenId,
+ value: u128,
owner: T::AccountId,
new_owner: T::AccountId,
) -> DispatchResult {
@@ -1826,7 +1830,7 @@
.ok_or(Error::<T>::NumOverflow)?;
let amount = item.fraction;
- ensure!(amount >= value.into(), Error::<T>::TokenValueTooLow);
+ ensure!(amount >= value, Error::<T>::TokenValueTooLow);
// update balance
let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())
@@ -1841,10 +1845,9 @@
let old_owner = item.owner.clone();
let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);
- let val64 = value.into();
// transfer
- if amount == val64 && !new_owner_has_account {
+ if amount == value && !new_owner_has_account {
// change owner
// new owner do not have account
let mut new_full_item = full_item.clone();
@@ -1865,7 +1868,7 @@
.iter_mut()
.find(|i| i.owner == owner)
.unwrap()
- .fraction -= val64;
+ .fraction -= value;
// separate amount
if new_owner_has_account {
@@ -1875,12 +1878,12 @@
.iter_mut()
.find(|i| i.owner == new_owner)
.unwrap()
- .fraction += val64;
+ .fraction += value;
} else {
// new owner do not have account
new_full_item.owner.push(Ownership {
owner: new_owner.clone(),
- fraction: val64,
+ fraction: value,
});
Self::add_token_index(collection_id, item_id, new_owner.clone())?;
}
@@ -1892,8 +1895,8 @@
}
fn transfer_nft(
- collection_id: u64,
- item_id: u64,
+ collection_id: CollectionId,
+ item_id: TokenId,
sender: T::AccountId,
new_owner: T::AccountId,
) -> DispatchResult {
@@ -1934,8 +1937,8 @@
}
fn item_exists(
- collection_id: u64,
- item_id: u64,
+ collection_id: CollectionId,
+ item_id: TokenId,
mode: &CollectionMode
) -> DispatchResult {
match mode {
@@ -1949,8 +1952,8 @@
}
fn set_re_fungible_variable_data(
- collection_id: u64,
- item_id: u64,
+ collection_id: CollectionId,
+ item_id: TokenId,
data: Vec<u8>
) -> DispatchResult {
let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);
@@ -1963,8 +1966,8 @@
}
fn set_nft_variable_data(
- collection_id: u64,
- item_id: u64,
+ collection_id: CollectionId,
+ item_id: TokenId,
data: Vec<u8>
) -> DispatchResult {
let mut item = <NftItemList<T>>::get(collection_id, item_id);
@@ -1979,8 +1982,8 @@
fn init_collection(item: &CollectionType<T::AccountId>) {
// check params
assert!(
- item.decimal_points <= 4,
- "decimal_points parameter must be lower than 4"
+ item.decimal_points <= MAX_DECIMAL_POINTS,
+ "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"
);
assert!(
item.name.len() <= 64,
@@ -2026,7 +2029,6 @@
.checked_add(1)
.unwrap();
let owner = item.owner.clone();
- let value = item.value as u64;
Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();
@@ -2034,7 +2036,7 @@
// Update balance
let new_balance = <Balance<T>>::get(item.collection, owner.clone())
- .checked_add(value)
+ .checked_add(item.value)
.unwrap();
<Balance<T>>::insert(item.collection, owner.clone(), new_balance);
}
@@ -2044,7 +2046,7 @@
.checked_add(1)
.unwrap();
- let value = item.owner.first().unwrap().fraction as u64;
+ let value = item.owner.first().unwrap().fraction;
let owner = item.owner.first().unwrap().owner.clone();
Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();
@@ -2058,7 +2060,7 @@
<Balance<T>>::insert(item.collection, owner.clone(), new_balance);
}
- fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {
+ fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {
// add to account limit
if <AccountItemCount<T>>::contains_key(owner.clone()) {
@@ -2096,8 +2098,8 @@
}
fn remove_token_index(
- collection_id: u64,
- item_index: u64,
+ collection_id: CollectionId,
+ item_index: TokenId,
owner: T::AccountId,
) -> DispatchResult {
@@ -2123,8 +2125,8 @@
}
fn move_token_index(
- collection_id: u64,
- item_index: u64,
+ collection_id: CollectionId,
+ item_index: TokenId,
old_owner: T::AccountId,
new_owner: T::AccountId,
) -> DispatchResult {
pallets/nft/src/tests.rsdiffbeforeafterboth--- a/pallets/nft/src/tests.rs
+++ b/pallets/nft/src/tests.rs
@@ -2,11 +2,12 @@
use super::*;
use crate::mock::*;
use crate::{AccessMode, ApprovePermissions, CollectionMode,
- Ownership, ChainLimits, CreateItemData, CreateNftData, CreateFungibleData, CreateReFungibleData}; //Err
+ Ownership, ChainLimits, CreateItemData, CreateNftData, CreateFungibleData, CreateReFungibleData,
+ CollectionId, TokenId, MAX_DECIMAL_POINTS}; //Err
use frame_support::{assert_noop, assert_ok};
use frame_system::{ RawOrigin };
-fn default_collection_numbers_limit() -> u64 {
+fn default_collection_numbers_limit() -> u32 {
10
}
@@ -34,7 +35,7 @@
CreateReFungibleData { const_data: vec![1, 2, 3], variable_data: vec![3, 2, 1] }
}
-fn create_test_collection_for_owner(mode: &CollectionMode, owner: u64, id: u64) -> u64 {
+fn create_test_collection_for_owner(mode: &CollectionMode, owner: u64, id: CollectionId) -> CollectionId {
let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
@@ -59,11 +60,11 @@
id
}
-fn create_test_collection(mode: &CollectionMode, id: u64) -> u64 {
+fn create_test_collection(mode: &CollectionMode, id: CollectionId) -> CollectionId {
create_test_collection_for_owner(&mode, 1, id)
}
-fn create_test_item(collection_id: u64, data: &CreateItemData) {
+fn create_test_item(collection_id: CollectionId, data: &CreateItemData) {
let origin1 = Origin::signed(1);
assert_ok!(TemplateModule::create_item(
origin1.clone(),
@@ -77,6 +78,46 @@
// Use cases tests region
// #region
#[test]
+fn create_fungible_collection_fails_with_large_decimal_numbers() {
+ new_test_ext().execute_with(|| {
+ default_limits();
+
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+
+ let origin1 = Origin::signed(1);
+ assert_noop!(TemplateModule::create_collection(
+ origin1,
+ col_name1,
+ col_desc1,
+ token_prefix1,
+ CollectionMode::Fungible(MAX_DECIMAL_POINTS + 1)
+ ), Error::<Test>::CollectionDecimalPointLimitExceeded);
+ });
+}
+
+#[test]
+fn create_re_fungible_collection_fails_with_large_decimal_numbers() {
+ new_test_ext().execute_with(|| {
+ default_limits();
+
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+
+ let origin1 = Origin::signed(1);
+ assert_noop!(TemplateModule::create_collection(
+ origin1,
+ col_name1,
+ col_desc1,
+ token_prefix1,
+ CollectionMode::ReFungible(MAX_DECIMAL_POINTS + 1)
+ ), Error::<Test>::CollectionDecimalPointLimitExceeded);
+ });
+}
+
+#[test]
fn create_nft_item() {
new_test_ext().execute_with(|| {
default_limits();
@@ -109,8 +150,8 @@
items_data.clone().into_iter().map(|d| { d.into() }).collect()
));
for (index, data) in items_data.iter().enumerate() {
- assert_eq!(TemplateModule::nft_item_id(1, (index + 1) as u64).const_data.to_vec(), data.const_data);
- assert_eq!(TemplateModule::nft_item_id(1, (index + 1) as u64).variable_data.to_vec(), data.variable_data);
+ assert_eq!(TemplateModule::nft_item_id(1, (index + 1) as TokenId).const_data.to_vec(), data.const_data);
+ assert_eq!(TemplateModule::nft_item_id(1, (index + 1) as TokenId).variable_data.to_vec(), data.variable_data);
}
});
}
@@ -160,7 +201,7 @@
));
for (index, data) in items_data.iter().enumerate() {
- let item = TemplateModule::refungible_item_id(1, (index + 1) as u64);
+ let item = TemplateModule::refungible_item_id(1, (index + 1) as TokenId);
assert_eq!(item.const_data.to_vec(), data.const_data);
assert_eq!(item.variable_data.to_vec(), data.variable_data);
assert_eq!(
@@ -207,7 +248,7 @@
));
for (index, _) in items_data.iter().enumerate() {
- assert_eq!(TemplateModule::fungible_item_id(1, (index + 1) as u64).owner, 1);
+ assert_eq!(TemplateModule::fungible_item_id(1, (index + 1) as TokenId).owner, 1);
}
assert_eq!(TemplateModule::balance_count(1, 1), 3000);
assert_eq!(TemplateModule::address_tokens(1, 1), [1, 2, 3]);