--- /dev/null +++ b/.docker/.env @@ -0,0 +1,7 @@ +RUST_TOOLCHAIN=nightly-2022-05-11 +RUST_C=1.62.0-nightly +POLKA_VERSION=release-v0.9.24 +UNIQUE_BRANCH=develop +FEATURE='--features=quartz-runtime' +USER=*** +PASS=*** --- a/.docker/Dockerfile-parachain +++ b/.docker/Dockerfile-parachain @@ -12,7 +12,9 @@ RUN apt-get update && \ apt-get dist-upgrade -y -o Dpkg::Options::="--force-confold" && \ - apt-get install -y cmake pkg-config libssl-dev git clang + apt-get install -y cmake pkg-config libssl-dev git clang && \ + apt-get clean && \ + rm -r /var/lib/apt/lists/* RUN rustup toolchain uninstall $(rustup toolchain list) && \ rustup toolchain install $RUST_TOOLCHAIN && \ @@ -27,7 +29,7 @@ WORKDIR /unique_parachain # ===== BUILD ====== -FROM rust-builder as builder +FROM rust-builder as builder-unique ARG PROFILE=release @@ -56,8 +58,6 @@ # ===== RUN ====== FROM ubuntu:20.04 - -ARG PROFILE=release RUN apt-get -y update && \ apt-get -y upgrade && \ @@ -68,7 +68,7 @@ nvm install v16.16.0 && \ nvm use v16.16.0 -RUN git clone https://github.com/uniquenetwork/polkadot-launch -b feature/parachain-forking +RUN git clone https://github.com/uniquenetwork/polkadot-launch -b feature/runtime-upgrade-testing RUN export NVM_DIR="$HOME/.nvm" && \ [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \ @@ -76,8 +76,11 @@ npm install --global yarn && \ yarn install -COPY --from=builder /unique_parachain/target/$PROFILE/unique-collator /unique-chain/target/$PROFILE/ -COPY --from=builder-polkadot /unique_parachain/polkadot/target/$PROFILE/polkadot /polkadot/target/$PROFILE/ +COPY --from=builder-unique /unique_parachain/target/release/unique-collator /unique-chain/target/release/ +COPY --from=builder-polkadot /unique_parachain/polkadot/target/release/polkadot /polkadot/target/release/ + +COPY ["./launch-config.json", "/polkadot-launch/launch-config.json"] + CMD export NVM_DIR="$HOME/.nvm" && \ [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \ --- /dev/null +++ b/.docker/Dockerfile-parachain-v2 @@ -0,0 +1,97 @@ +# ===== Rust builder ===== +FROM phusion/baseimage:focal-1.1.0 as rust-builder +LABEL maintainer="Unique.Network" + +ARG RUST_TOOLCHAIN={{ actual_toolchain }} +ARG POLKA_VERSION=release-v0.9.24 + + +ENV RUST_TOOLCHAIN $RUST_TOOLCHAIN +ENV POLKA_VERSION $POLKA_VERSION + +ENV CARGO_HOME="/cargo-home" +ENV PATH="/cargo-home/bin:$PATH" + +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain none + +RUN apt-get update && \ + apt-get dist-upgrade -y -o Dpkg::Options::="--force-confold" && \ + apt-get install -y cmake pkg-config libssl-dev git clang + +RUN rustup toolchain uninstall $(rustup toolchain list) && \ + rustup toolchain install $RUST_TOOLCHAIN && \ + rustup default $RUST_TOOLCHAIN && \ + rustup target list --installed && \ + rustup show +RUN rustup target add wasm32-unknown-unknown --toolchain $RUST_TOOLCHAIN + +####works inside repo folder### + + +# ===== BUILD ====== +FROM rust-builder as builder-unique + +RUN mkdir unique_parachain +WORKDIR /unique_parachain + +ARG PROFILE='features=quartz-runtime' + +ENV PROFILE $PROFILE + +COPY . . +RUN cargo build --$PROFILE + # && \ + # cargo test + +# ===== BUILD POLKADOT ===== +FROM rust-builder as builder-polkadot + +RUN mkdir unique_parachain +WORKDIR /unique_parachain + +RUN git clone -b $POLKA_VERSION --depth 1 https://github.com/paritytech/polkadot.git && \ + cd polkadot && \ + git tag -n && \ + cargo build --release + +# ===== RUN ====== + +FROM phusion/baseimage:focal-1.1.0 + + +RUN apt-get -y update && \ + apt-get -y upgrade && \ + apt-get -y install curl git && \ + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash && \ + export NVM_DIR="$HOME/.nvm" && \ + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \ + nvm install v15.5.0 && \ + nvm use v15.5.0 + +RUN git clone https://github.com/UniqueNetwork/polkadot-launch.git && \ + cd ./polkadot-launch && \ + git checkout feature/runtime-upgrade-testing + +RUN export NVM_DIR="$HOME/.nvm" && \ + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \ + cd /polkadot-launch && \ + npm install --global yarn && \ + yarn + +COPY --from=builder-unique /unique_parachain/target/release/unique-collator /unique-chain/target/release/ +COPY --from=builder-polkadot /unique_parachain/polkadot/target/release/polkadot /polkadot/target/release/ + +COPY ["./launch-config.json", "/polkadot-launch/launch-config.json"] + + +EXPOSE 9844 +EXPOSE 9944 +EXPOSE 9933 +EXPOSE 9833 +EXPOSE 40333 +EXPOSE 30333 + +CMD export NVM_DIR="$HOME/.nvm" && \ + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \ + cd /polkadot-launch && \ + yarn start launch-config.json --- a/.docker/docker-compose-dev.yaml +++ b/.docker/docker-compose-dev.yaml @@ -3,8 +3,8 @@ services: node-dev: build: - context: . - dockerfile: Dockerfile-chain-dev + context: ../ + dockerfile: .docker/Dockerfile-chain-dev image: node-dev container_name: node-dev expose: --- /dev/null +++ b/.docker/docker-compose-tests-parachain.yml @@ -0,0 +1,34 @@ +version: "3.5" + +services: + blockchain_nodes: + build: + context: ../ + dockerfile: .docker/Dockerfile-parachain + args: + - RUST_TOOLCHAIN=${RUST_TOOLCHAIN:?err} + - RUST_C=${RUST_C:?err} + - POLKA_VERSION=${POLKA_VERSION:?err} + - FEATURE=${FEATURE:?err} + volumes: + - ../launch-config.json:/polkadot-launch/launch-config.json + env_file: + - ./.env + expose: + - 9944 + - 9933 + ports: + - 127.0.0.1:9944:9944 + - 127.0.0.1:9933:9933 + +# integration_tests: +# build: +# context: ../tests/ +# dockerfile: Dockerfile-tests +# environment: +# RPC_URL: http://blockchain_nodes:9933/ +# volumes: +# - ./tests/src/config_docker.ts:/unique_parachain/src/config.ts +# - /home/ubuntu/mochawesome-report:/unique_parachain/mochawesome-report +# depends_on: +# - blockchain_nodes --- /dev/null +++ b/.docker/docker-compose.tmp-master.j2 @@ -0,0 +1,10 @@ +version: "3.5" + +services: + blockchain_nodes: + build: + args: + - "RUST_TOOLCHAIN={{ RUST_TOOLCHAIN }}" + - "BRANCH={{ BRANCH }}" + - "REPO_URL={{ REPO_URL }}" + - "FEATURE={{ FEATURE }}" --- a/.docker/docker-compose.tmp.j2 +++ b/.docker/docker-compose.tmp.j2 @@ -4,6 +4,7 @@ node-dev: build: args: +<<<<<<< HEAD - "RUST_TOOLCHAIN={{ RUST_TOOLCHAIN:?err }}" - "BRANCH={{ BRANCH:?err }}" - "REPO_URL={{ REPO_URL:?err }}" @@ -11,3 +12,9 @@ - "POLKA_VERSION={{ POLKA_VERSION:?err }}" command: cargo run --release $FEATURE -- --dev -linfo --unsafe-ws-external --rpc-cors=all --unsafe-rpc-external +======= + - "RUST_TOOLCHAIN={{ RUST_TOOLCHAIN }}" + - "BRANCH={{ BRANCH }}" + - "REPO_URL={{ REPO_URL }}" + - "FEATURE={{ FEATURE }}" +>>>>>>> develop --- /dev/null +++ b/.github/workflows/build-test-master.yml @@ -0,0 +1,126 @@ +name: Build & Test Master + +# Controls when the action will run. +on: + # Triggers the workflow on push or pull request events but only for the master branch + #push: + # branches: [ develop ] + pull_request: + branches: + - master + types: + - opened + - edited + - reopened + # pull_request: + # branches: [ develop ] + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +#Define Workflow variables +env: + ubuntu_version: focal + chains_release_dir: /opt/runner/chains_release + opal_chain_workdir: ./src_opal_chain + quartz_chain_workdir: ./src_quartz_chain + unique_chain_workdir: ./src_unique_chain + RUST_TOOLCHAIN: nightly-2022-05-11 + REPO_URL: ${{ github.server_url }}/${{ github.repository }} + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + pre-requisites: + # The type of runner that the job will run on + runs-on: self-hosted-ci + + steps: + #runs ssh connection + - name: Install dependencies + run: | + sudo apt-get install git curl libssl-dev llvm pkg-config libclang-dev clang make cmake + sudo apt autoremove + curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain none + . $HOME/.cargo/env && cargo install --locked --git https://github.com/chevdor/subwasm + rustup toolchain install ${{ env.RUST_TOOLCHAIN }} + rustup default ${{ env.RUST_TOOLCHAIN }} + rustup target add wasm32-unknown-unknown --toolchain ${{ env.RUST_TOOLCHAIN }} + + + build: + # The type of runner that the job will run on + runs-on: self-hosted-ci + + needs: pre-requisites + name: Build Container, Spin it Up an test + + continue-on-error: true #Do not stop testing of matrix runs failed. + + strategy: + matrix: + include: + - network: "Opal" + features: " " + - network: "Quartz" + features: "--features=quartz-runtime" + - network: "Unique" + features: "--features=unique-runtime" + + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + + - name: Generate ENV related extend file for docker-compose + uses: cuchi/jinja2-action@v1.2.0 + with: + template: .docker/docker-compose.tmp-master.j2 + output_file: .docker/docker-compose.${{ matrix.network }}.yml + variables: | + REPO_URL=${{ github.server_url }}/${{ github.repository }}.git + RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} + FEATURE=${{ matrix.features }} + BRANCH=${{ github.head_ref }} + + - name: Show temporary file + run: cat .docker/docker-compose.${{ matrix.network }}.yml + + - name: Build the stack + run: docker-compose -f ".docker/docker-compose-tests-parachain.yml" -f ".docker/docker-compose.${{ matrix.network }}.yml" up -d --build + +# - name: "Build the Docker image with Feature: ${{ env.features }}" +# run: docker build -t build-${{ github.head_ref }} --file .docker/Dockerfile-chain-dev --build-arg REPO_URL=${{ github.server_url }}/${{ github.repository }}.git --build-arg RUST_TOOLCHAIN=${{ env.actual_toolchain }} --build-arg FEATURE="${{ env.features }}" --build-arg BRANCH=${{ github.head_ref }} --no-cache . + + - name: Wait + run: sleep 420s + + - name: Install node + uses: actions/setup-node@v1 + with: + node-version: 14.x + + - name: Install dependencies + run: | + cd tests + yarn install + yarn add mochawesome + yarn --pure-lockfile + + - name: Run tests + run: | + cd tests + NOW=$(date +%s) && yarn test --reporter mochawesome --reporter-options reportFilename=test-${NOW} + env: + RPC_URL: http://127.0.0.1:9933/ + + - name: Test Report + uses: phoenix-actions/test-reporting@v8 + id: test-report # Set ID reference for step + if: success() || failure() # run this step even if previous step failed + with: + name: Tests ${{ matrix.network }} # Name of the check run which will be created + path: tests/mochawesome-report/test-*.json # Path to test results + reporter: mochawesome-json + fail-on-error: 'false' + + - name: Stop running containers + if: always() # run this step always + run: docker-compose -f ".docker/docker-compose-tests-parachain.yml" -f ".docker/docker-compose.${{ matrix.network }}.yml" down --- a/pallets/common/CHANGELOG.md +++ b/pallets/common/CHANGELOG.md @@ -2,11 +2,13 @@ All notable changes to this project will be documented in this file. -## [0.1.1] - 2022-07-14 +## [0.1.3] - 2022-07-25 +### Add +- Some static property keys and values. -### Added +## [0.1.2] - 2022-07-20 - - Implementation of RPC method `token_owners` returning 10 owners in no particular order. - This was an internal request to improve the web interface and support fractionalization event. +### Fixed - \ No newline at end of file +- Some methods in `#[solidity_interface]` for `CollectionHandle` had invalid + mutability modifiers, causing invalid stub/abi generation. --- a/primitives/data-structs/src/bounded.rs +++ b/primitives/data-structs/src/bounded.rs @@ -1,3 +1,21 @@ +// 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 . + +//! This module contins implementations for support bounded structures ([`BoundedVec`], [`BoundedBTreeMap`], [`BoundedBTreeSet`]) in [`serde`]. + use core::fmt; use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; use sp_std::vec::Vec; @@ -7,7 +25,7 @@ storage::{bounded_btree_map::BoundedBTreeMap, bounded_btree_set::BoundedBTreeSet}, }; -/// BoundedVec doesn't supports serde +/// [`serde`] implementations for [`BoundedVec`]. #[cfg(feature = "serde1")] pub mod vec_serde { use core::convert::TryFrom; @@ -39,6 +57,7 @@ } } +/// Format [`BoundedVec`] for debug output. pub fn vec_debug(v: &BoundedVec, f: &mut fmt::Formatter) -> Result<(), fmt::Error> where V: fmt::Debug, @@ -49,6 +68,7 @@ #[cfg(feature = "serde1")] #[allow(dead_code)] +/// [`serde`] implementations for [`BoundedBTreeMap`]. pub mod map_serde { use core::convert::TryFrom; use sp_std::collections::btree_map::BTreeMap; @@ -84,6 +104,7 @@ } } +/// Format [`BoundedBTreeMap`] for debug output. pub fn map_debug( v: &BoundedBTreeMap, f: &mut fmt::Formatter, @@ -98,6 +119,7 @@ #[cfg(feature = "serde1")] #[allow(dead_code)] +/// [`serde`] implementations for [`BoundedBTreeSet`]. pub mod set_serde { use core::convert::TryFrom; use sp_std::collections::btree_set::BTreeSet; @@ -129,6 +151,7 @@ } } +/// Format [`BoundedBTreeSet`] for debug output. pub fn set_debug(v: &BoundedBTreeSet, f: &mut fmt::Formatter) -> Result<(), fmt::Error> where K: fmt::Debug + Ord, --- a/primitives/data-structs/src/lib.rs +++ b/primitives/data-structs/src/lib.rs @@ -14,6 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Unique Network. If not, see . +//! # Primitives crate. +//! +//! This crate contains types, traits and constants. + #![cfg_attr(not(feature = "std"), no_std)] use core::{ @@ -55,38 +59,55 @@ pub mod mapping; mod migration; +/// Maximum of decimal points. pub const MAX_DECIMAL_POINTS: DecimalPoints = 30; + +/// Maximum pieces for refungible token. pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000; pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000; +/// Maximum tokens for user. pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) { 100_000 } else { 10 }; + +/// Maximum for collections can be created. pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) { 100_000 } else { 10 }; + +/// Maximum for various custom data of token. pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) { 2048 } else { 10 }; + +/// Maximum admins per collection. pub const COLLECTION_ADMINS_LIMIT: u32 = 5; + +/// Maximum tokens per collection. pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX; + +/// Maximum tokens per account. pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) { 1_000_000 } else { 10 }; -// Timeouts for item types in passed blocks +/// Default timeout for transfer sponsoring NFT item. pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5; +/// Default timeout for transfer sponsoring fungible item. pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5; +/// Default timeout for transfer sponsoring refungible item. pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5; +/// Default timeout for sponsored approving. pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5; // Schema limits @@ -94,27 +115,44 @@ pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192; pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 32768; +// TODO: not used. Delete? pub const COLLECTION_FIELD_LIMIT: u32 = CONST_ON_CHAIN_SCHEMA_LIMIT; +/// Maximum length for collection name. pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64; + +/// Maximum length for collection description. pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256; + +/// Maximal token prefix length. pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16; +/// Maximal lenght of property key. pub const MAX_PROPERTY_KEY_LENGTH: u32 = 256; + +/// Maximal lenght of property value. pub const MAX_PROPERTY_VALUE_LENGTH: u32 = 32768; + +/// Maximum properties that can be assigned to token. pub const MAX_PROPERTIES_PER_ITEM: u32 = 64; +/// Maximal lenght of extended property value. pub const MAX_AUX_PROPERTY_VALUE_LENGTH: u32 = 2048; +/// Maximum size for all collection properties. pub const MAX_COLLECTION_PROPERTIES_SIZE: u32 = 40960; + +/// Maximum size for all token properties. pub const MAX_TOKEN_PROPERTIES_SIZE: u32 = 32768; /// How much items can be created per single -/// create_many call +/// create_many call. pub const MAX_ITEMS_PER_BATCH: u32 = 200; +/// Used for limit bounded types of token custom data. pub type CustomDataLimit = ConstU32; +/// Collection id. #[derive( Encode, Decode, @@ -134,6 +172,7 @@ impl EncodeLike for CollectionId {} impl EncodeLike for u32 {} +/// Token id. #[derive( Encode, Decode, @@ -154,6 +193,9 @@ impl EncodeLike for u32 {} impl TokenId { + /// Try to get next token id. + /// + /// If next id cause overflow, then [`ArithmeticError::Overflow`] returned. pub fn try_next(self) -> Result { self.0 .checked_add(1) @@ -176,14 +218,21 @@ } } +/// Token data. #[derive(Encode, Decode, Clone, PartialEq, TypeInfo)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct TokenData { + /// Properties of token. pub properties: Vec, + + /// Token owner. pub owner: Option, + + /// Token pieces. pub pieces: u128, } +// TODO: unused type pub struct OverflowError; impl From for &'static str { fn from(_: OverflowError) -> Self { @@ -191,17 +240,27 @@ } } +/// Alias for decimal points type. pub type DecimalPoints = u8; +/// Collection mode. +/// +/// Collection can represent various types of tokens. +/// Each collection can contain only one type of tokens at a time. +/// This type helps to understand which tokens the collection contains. #[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub enum CollectionMode { + /// Non fungible tokens. NFT, + /// Fungible tokens. Fungible(DecimalPoints), + /// Refungible tokens. ReFungible, } impl CollectionMode { + /// Get collection mod as number. pub fn id(&self) -> u8 { match self { CollectionMode::NFT => 1, @@ -211,14 +270,18 @@ } } +// TODO: unused trait pub trait SponsoringResolve { fn resolve(who: &AccountId, call: &Call) -> Option; } +/// Access mode for some token operations. #[derive(Encode, Decode, Eq, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub enum AccessMode { + /// Access grant for owner and admins. Used as default. Normal, + /// Like a [`Normal`](AccessMode::Normal) but also users in allow list. AllowList, } impl Default for AccessMode { @@ -227,6 +290,7 @@ } } +// TODO: remove in future. #[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub enum SchemaVersion { @@ -239,6 +303,7 @@ } } +// TODO: unused type #[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct Ownership { @@ -246,19 +311,21 @@ pub fraction: u128, } +/// The state of collection sponsorship. #[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub enum SponsorshipState { - /// The fees are applied to the transaction sender + /// The fees are applied to the transaction sender. Disabled, - /// Pending confirmation from a sponsor-to-be + /// The sponsor is under consideration. Until the sponsor gives his consent, + /// the fee will still be charged to sender. Unconfirmed(AccountId), - /// Transactions are sponsored by specified account + /// Transactions are sponsored by specified account. Confirmed(AccountId), } impl SponsorshipState { - /// Get the acting sponsor account, if present + /// Get a sponsor of the collection who has confirmed his status. pub fn sponsor(&self) -> Option<&AccountId> { match self { Self::Confirmed(sponsor) => Some(sponsor), @@ -266,7 +333,7 @@ } } - /// Get the sponsor account currently pending confirmation, if present + /// Get a sponsor of the collection who has pending or confirmed status. pub fn pending_sponsor(&self) -> Option<&AccountId> { match self { Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor), @@ -274,7 +341,7 @@ } } - /// Is sponsorship set and acting + /// Whether the sponsorship is confirmed. pub fn confirmed(&self) -> bool { matches!(self, Self::Confirmed(_)) } @@ -290,16 +357,32 @@ pub type CollectionDescription = BoundedVec>; pub type CollectionTokenPrefix = BoundedVec>; +/// Base structure for represent collection. +/// +/// Used to provide basic functionality for all types of collections. +/// +/// #### Note /// Collection parameters, used in storage (see [`RpcCollection`] for the RPC version). #[struct_versioning::versioned(version = 2, upper)] #[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)] pub struct Collection { + /// Collection owner account. pub owner: AccountId, + + /// Collection mode. pub mode: CollectionMode, + + /// Access mode. #[version(..2)] pub access: AccessMode, + + /// Collection name. pub name: CollectionName, + + /// Collection description. pub description: CollectionDescription, + + /// Token prefix. pub token_prefix: CollectionTokenPrefix, #[version(..2)] @@ -310,10 +393,14 @@ #[version(..2)] pub schema_version: SchemaVersion, + + /// The state of sponsorship of the collection. pub sponsorship: SponsorshipState, + /// Collection limits. pub limits: CollectionLimits, + /// Collection permissions. #[version(2.., upper(Default::default()))] pub permissions: CollectionPermissions, @@ -335,116 +422,215 @@ #[derive(Encode, Decode, Clone, PartialEq, TypeInfo)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct RpcCollection { + /// Collection owner account. pub owner: AccountId, + + /// Collection mode. pub mode: CollectionMode, + + /// Collection name. pub name: Vec, + + /// Collection description. pub description: Vec, + + /// Token prefix. pub token_prefix: Vec, + + /// The state of sponsorship of the collection. pub sponsorship: SponsorshipState, + + /// Collection limits. pub limits: CollectionLimits, + + /// Collection permissions. pub permissions: CollectionPermissions, + + /// Token property permissions. pub token_property_permissions: Vec, + + /// Collection properties. pub properties: Vec, + + /// Is collection read only. pub read_only: bool, } +/// Data used for create collection. +/// +/// All fields are wrapped in [`Option`], where `None` means chain default. #[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)] #[derivative(Debug, Default(bound = ""))] pub struct CreateCollectionData { + /// Collection mode. #[derivative(Default(value = "CollectionMode::NFT"))] pub mode: CollectionMode, + + /// Access mode. pub access: Option, + + /// Collection name. pub name: CollectionName, + + /// Collection description. pub description: CollectionDescription, + + /// Token prefix. pub token_prefix: CollectionTokenPrefix, + + /// Pending collection sponsor. pub pending_sponsor: Option, + + /// Collection limits. pub limits: Option, + + /// Collection permissions. pub permissions: Option, + + /// Token property permissions. pub token_property_permissions: CollectionPropertiesPermissionsVec, + + /// Collection properties. pub properties: CollectionPropertiesVec, } +/// Bounded vector of properties permissions. Max length is [`MAX_PROPERTIES_PER_ITEM`]. +// TODO: maybe rename to PropertiesPermissionsVec pub type CollectionPropertiesPermissionsVec = BoundedVec>; +/// Bounded vector of properties. Max length is [`MAX_PROPERTIES_PER_ITEM`]. pub type CollectionPropertiesVec = BoundedVec>; /// Limits and restrictions of a collection. -/// All fields are wrapped in `Option`s, where None means chain default. /// -/// todo:doc links to chain defaults +/// All fields are wrapped in [`Option`], where `None` means chain default. +/// +/// Update with `pallet_common::Pallet::clamp_limits`. // IMPORTANT: When adding/removing fields from this struct - don't forget to also -// update clamp_limits() in pallet-common. #[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +// When adding/removing fields from this struct - don't forget to also update with `pallet_common::Pallet::clamp_limits`. +// TODO: move `pallet_common::Pallet::clamp_limits` into `impl CollectionLimits`. +// TODO: may be remove [`Option`] and **pub** from fields and create struct with default values. pub struct CollectionLimits { - /// Maximum number of owned tokens per account. Chain default: [`ACCOUNT_TOKEN_OWNERSHIP_LIMIT`] + /// How many tokens can a user have on one account. + /// * Default - [`ACCOUNT_TOKEN_OWNERSHIP_LIMIT`]. + /// * Limit - [`MAX_TOKEN_OWNERSHIP`]. pub account_token_ownership_limit: Option, - /// Maximum size of data in bytes of a sponsored transaction. Chain default: [`CUSTOM_DATA_LIMIT`] + + /// How many bytes of data are available for sponsorship. + /// * Default - [`CUSTOM_DATA_LIMIT`]. + /// * Limit - [`CUSTOM_DATA_LIMIT`]. pub sponsored_data_size: Option, - /// FIXME should we delete this or repurpose it? - /// None - setVariableMetadata is not sponsored - /// Some(v) - setVariableMetadata is sponsored - /// if there is v block between txs + // FIXME should we delete this or repurpose it? + /// Times in how many blocks we sponsor data. + /// + /// If is `Some(v)` then **setVariableMetadata** is sponsored if there is `v` block between transactions. + /// + /// * Default - [`SponsoringDisabled`](SponsoringRateLimit::SponsoringDisabled). + /// * Limit - [`MAX_SPONSOR_TIMEOUT`]. /// /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`] pub sponsored_data_rate_limit: Option, /// Maximum amount of tokens inside the collection. Chain default: [`COLLECTION_TOKEN_LIMIT`] + + /// How many tokens can be mined into this collection. + /// + /// * Default - [`COLLECTION_TOKEN_LIMIT`]. + /// * Limit - [`COLLECTION_TOKEN_LIMIT`]. pub token_limit: Option, - /// Timeout for sponsoring a token transfer in passed blocks. Chain default: - /// either [`NFT_SPONSOR_TRANSFER_TIMEOUT`], [`FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`], or [`REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`], - /// depending on the collection type. + /// Timeouts for transfer sponsoring. + /// + /// * Default + /// - **Fungible** - [`FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`] + /// - **NFT** - [`NFT_SPONSOR_TRANSFER_TIMEOUT`] + /// - **Refungible** - [`REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`] + /// * Limit - [`MAX_SPONSOR_TIMEOUT`]. pub sponsor_transfer_timeout: Option, - /// Timeout for sponsoring an approval in passed blocks. Chain default: [`SPONSOR_APPROVE_TIMEOUT`] + + /// Timeout for sponsoring an approval in passed blocks. + /// + /// * Default - [`SPONSOR_APPROVE_TIMEOUT`]. + /// * Limit - [`MAX_SPONSOR_TIMEOUT`]. pub sponsor_approve_timeout: Option, - /// Can a token be transferred by the owner. Chain default: `false` + + /// Whether the collection owner of the collection can send tokens (which belong to other users). + /// + /// * Default - **false**. pub owner_can_transfer: Option, - /// Can a token be burned by the owner. Chain default: `true` + + /// Can the collection owner burn other people's tokens. + /// + /// * Default - **true**. pub owner_can_destroy: Option, - /// Can a token be transferred at all. Chain default: `true` + + /// Is it possible to send tokens from this collection between users. + /// + /// * Default - **true**. pub transfers_enabled: Option, } impl CollectionLimits { + /// Get effective value for [`account_token_ownership_limit`](self.account_token_ownership_limit). pub fn account_token_ownership_limit(&self) -> u32 { self.account_token_ownership_limit .unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT) .min(MAX_TOKEN_OWNERSHIP) } + + /// Get effective value for [`sponsored_data_size`](self.sponsored_data_size). pub fn sponsored_data_size(&self) -> u32 { self.sponsored_data_size .unwrap_or(CUSTOM_DATA_LIMIT) .min(CUSTOM_DATA_LIMIT) } + + /// Get effective value for [`token_limit`](self.token_limit). pub fn token_limit(&self) -> u32 { self.token_limit .unwrap_or(COLLECTION_TOKEN_LIMIT) .min(COLLECTION_TOKEN_LIMIT) } + + // TODO: may be replace u32 to mode? + /// Get effective value for [`sponsor_transfer_timeout`](self.sponsor_transfer_timeout). pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 { self.sponsor_transfer_timeout .unwrap_or(default) .min(MAX_SPONSOR_TIMEOUT) } + + /// Get effective value for [`sponsor_approve_timeout`](self.sponsor_approve_timeout). pub fn sponsor_approve_timeout(&self) -> u32 { self.sponsor_approve_timeout .unwrap_or(SPONSOR_APPROVE_TIMEOUT) .min(MAX_SPONSOR_TIMEOUT) } + + /// Get effective value for [`owner_can_transfer`](self.owner_can_transfer). pub fn owner_can_transfer(&self) -> bool { self.owner_can_transfer.unwrap_or(false) } + + /// Get effective value for [`owner_can_transfer_instaled`](self.owner_can_transfer_instaled). pub fn owner_can_transfer_instaled(&self) -> bool { self.owner_can_transfer.is_some() } + + /// Get effective value for [`owner_can_destroy`](self.owner_can_destroy). pub fn owner_can_destroy(&self) -> bool { self.owner_can_destroy.unwrap_or(true) } + + /// Get effective value for [`transfers_enabled`](self.transfers_enabled). pub fn transfers_enabled(&self) -> bool { self.transfers_enabled.unwrap_or(true) } + + /// Get effective value for [`sponsored_data_rate_limit`](self.sponsored_data_rate_limit). pub fn sponsored_data_rate_limit(&self) -> Option { match self .sponsored_data_rate_limit @@ -457,24 +643,46 @@ } /// Permissions on certain operations within a collection. -/// All fields are wrapped in `Option`s, where None means chain default. -// IMPORTANT: When adding/removing fields from this struct - don't forget to also -// update clamp_limits() in pallet-common. +/// +/// Some fields are wrapped in [`Option`], where `None` means chain default. +/// +/// Update with `pallet_common::Pallet::clamp_permissions`. #[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +// When adding/removing fields from this struct - don't forget to also update `pallet_common::Pallet::clamp_permissions`. +// TODO: move `pallet_common::Pallet::clamp_permissions` into `impl CollectionPermissions`. pub struct CollectionPermissions { + /// Access mode. + /// + /// * Default - [`AccessMode::Normal`]. pub access: Option, + + /// Minting allowance. + /// + /// * Default - **false**. pub mint_mode: Option, + + /// Permissions for nesting. + /// + /// * Default + /// - `token_owner` - **false** + /// - `collection_admin` - **false** + /// - `restricted` - **None** pub nesting: Option, } impl CollectionPermissions { + /// Get effective value for [`access`](self.access). pub fn access(&self) -> AccessMode { self.access.unwrap_or(AccessMode::Normal) } + + /// Get effective value for [`mint_mode`](self.mint_mode). pub fn mint_mode(&self) -> bool { self.mint_mode.unwrap_or(false) } + + /// Get effective value for [`nesting`](self.nesting). pub fn nesting(&self) -> &NestingPermissions { static DEFAULT: NestingPermissions = NestingPermissions { token_owner: false, @@ -487,8 +695,10 @@ } } +/// Inner set for collections allowed to nest. type OwnerRestrictedSetInner = BoundedBTreeSet>; +/// Wraper for collections set allowing nest. #[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] #[derivative(Debug)] @@ -497,7 +707,9 @@ #[derivative(Debug(format_with = "bounded::set_debug"))] pub OwnerRestrictedSetInner, ); + impl OwnerRestrictedSet { + /// Create new set. pub fn new() -> Self { Self(Default::default()) } @@ -519,19 +731,21 @@ #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] #[derivative(Debug)] pub struct NestingPermissions { - /// Owner of token can nest tokens under it + /// Owner of token can nest tokens under it. pub token_owner: bool, - /// Admin of token collection can nest tokens under token + /// Admin of token collection can nest tokens under token. pub collection_admin: bool, - /// If set - only tokens from specified collections can be nested + /// If set - only tokens from specified collections can be nested. pub restricted: Option, #[cfg(feature = "runtime-benchmarks")] - /// Anyone can nest tokens, mutually exclusive with `token_owner`, `admin` + /// Anyone can nest tokens, mutually exclusive with `token_owner`, `admin`. pub permissive: bool, } /// Enum denominating how often can sponsoring occur if it is enabled. +/// +/// Used for [`collection limits`](CollectionLimits). #[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub enum SponsoringRateLimit { @@ -549,6 +763,7 @@ /// Key-value pairs used to describe the token as metadata #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))] #[derivative(Debug(format_with = "bounded::vec_debug"))] + /// Properties that wil be assignet to created item. pub properties: CollectionPropertiesVec, } @@ -570,7 +785,7 @@ #[derivative(Debug(format_with = "bounded::vec_debug"))] pub const_data: BoundedVec, - /// Number of pieces the RFT is split into + /// Pieces of created token. pub pieces: u128, /// Key-value pairs used to describe the token as metadata @@ -579,6 +794,7 @@ pub properties: CollectionPropertiesVec, } +// TODO: remove this. #[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub enum MetaUpdatePermission { @@ -588,57 +804,75 @@ } /// Enum holding data used for creation of all three item types. +/// Unified data for create item. #[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub enum CreateItemData { + /// Data for create NFT. NFT(CreateNftData), + /// Data for create Fungible item. Fungible(CreateFungibleData), + /// Data for create ReFungible item. ReFungible(CreateReFungibleData), } -/// Explicit NFT creation data with meta parameters. +/// Extended data for create NFT. #[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)] #[derivative(Debug)] pub struct CreateNftExData { + /// Properties that wil be assignet to created item. #[derivative(Debug(format_with = "bounded::vec_debug"))] pub properties: CollectionPropertiesVec, + + /// Owner of creating item. pub owner: CrossAccountId, } -/// Explicit RFT creation data with meta parameters. +/// Extended data for create ReFungible item. #[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)] #[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))] pub struct CreateRefungibleExData { + /// Custom data stored in token. #[derivative(Debug(format_with = "bounded::vec_debug"))] pub const_data: BoundedVec, + + /// Users who will be assigned the specified number of token parts. #[derivative(Debug(format_with = "bounded::map_debug"))] pub users: BoundedBTreeMap>, #[derivative(Debug(format_with = "bounded::vec_debug"))] pub properties: CollectionPropertiesVec, } -/// Explicit item creation data with meta parameters, namely the owner. +/// Unified extended data for creating item. #[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)] #[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))] pub enum CreateItemExData { + /// Extended data for create NFT. NFT( #[derivative(Debug(format_with = "bounded::vec_debug"))] BoundedVec, ConstU32>, ), + + /// Extended data for create Fungible item. Fungible( #[derivative(Debug(format_with = "bounded::map_debug"))] BoundedBTreeMap>, ), - /// Many tokens, each may have only one owner + + /// Extended data for create ReFungible item in case of + /// many tokens, each may have only one owner RefungibleMultipleItems( #[derivative(Debug(format_with = "bounded::vec_debug"))] BoundedVec, ConstU32>, ), - /// Single token, which may have many owners + + /// Extended data for create ReFungible item in case of + /// single token, which may have many owners RefungibleMultipleOwners(CreateRefungibleExData), } impl CreateItemData { + /// Get size of custom data. pub fn data_size(&self) -> usize { match self { CreateItemData::ReFungible(data) => data.const_data.len(), @@ -670,18 +904,28 @@ #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] // todo possibly rename to be used generally as an address pair pub struct TokenChild { + /// Token id. pub token: TokenId, + + /// Collection id. pub collection: CollectionId, } +/// Collection statistics. #[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct CollectionStats { + /// Number of created items. pub created: u32, + + /// Number of burned items. pub destroyed: u32, + + /// Number of current items. pub alive: u32, } +/// This type works like [`PhantomData`] but supports generating _scale-info_ descriptions to generate node metadata. #[derive(Encode, Decode, Clone, Debug)] #[cfg_attr(feature = "std", derive(PartialEq))] pub struct PhantomType(core::marker::PhantomData); @@ -707,22 +951,36 @@ } } +/// Bounded vector of bytes. pub type BoundedBytes = BoundedVec; +/// Extra properties for external collections. pub type AuxPropertyValue = BoundedBytes>; +/// Property key. pub type PropertyKey = BoundedBytes>; + +/// Property value. pub type PropertyValue = BoundedBytes>; +/// Property permission. #[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct PropertyPermission { + /// Permission to change the property and property permission. + /// + /// If it **false** then you can not change corresponding property even if [`collection_admin`] and [`token_owner`] are **true**. pub mutable: bool, + + /// Change permission for the collection administrator. pub collection_admin: bool, + + /// Permission to change the property for the owner of the token. pub token_owner: bool, } impl PropertyPermission { + /// Creates mutable property permission but changes restricted for collection admin and token owner. pub fn none() -> Self { Self { mutable: true, @@ -732,12 +990,15 @@ } } +/// Property is simpl key-value record. #[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct Property { + /// Property key. #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))] pub key: PropertyKey, + /// Property value. #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))] pub value: PropertyValue, } @@ -748,12 +1009,15 @@ } } +/// Record for proprty key permission. #[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct PropertyKeyPermission { + /// Key. #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))] pub key: PropertyKey, + /// Permission. pub permission: PropertyPermission, } @@ -763,15 +1027,35 @@ } } +/// Errors for properties actions. #[derive(Debug)] pub enum PropertiesError { + /// The space allocated for properties has run out. + /// + /// * Limit for colection - [`MAX_COLLECTION_PROPERTIES_SIZE`]. + /// * Limit for token - [`MAX_TOKEN_PROPERTIES_SIZE`]. NoSpaceForProperty, + + /// The property limit has been reached. + /// + /// * Limit - [`MAX_PROPERTIES_PER_ITEM`]. PropertyLimitReached, + + /// Property key contains not allowed character. InvalidCharacterInPropertyKey, + + /// Property key length is too long. + /// + /// * Limit - [`MAX_PROPERTY_KEY_LENGTH`]. PropertyKeyIsTooLong, + + /// Property key is empty. EmptyPropertyKey, } +/// Marker for scope of property. +/// +/// Scoped property can't be changed by user. Used for external collections. #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Clone, Copy)] pub enum PropertyScope { None, @@ -779,6 +1063,7 @@ } impl PropertyScope { + /// Apply scope to property key. pub fn apply(self, key: PropertyKey) -> Result { let scope_str: &[u8] = match self { Self::None => return Ok(key), @@ -792,9 +1077,11 @@ } } +/// Trait for operate with properties. pub trait TrySetProperty: Sized { type Value; + /// Try to set property with scope. fn try_scoped_set( &mut self, scope: PropertyScope, @@ -802,6 +1089,7 @@ value: Self::Value, ) -> Result<(), PropertiesError>; + /// Try to set property with scope from iterator. fn try_scoped_set_from_iter( &mut self, scope: PropertyScope, @@ -819,10 +1107,12 @@ Ok(()) } + /// Try to set property. fn try_set(&mut self, key: PropertyKey, value: Self::Value) -> Result<(), PropertiesError> { self.try_scoped_set(PropertyScope::None, key, value) } + /// Try to set property from iterator. fn try_set_from_iter(&mut self, iter: I) -> Result<(), PropertiesError> where I: Iterator, @@ -832,6 +1122,7 @@ } } +/// Wrapped map for storing properties. #[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)] #[derivative(Default(bound = ""))] pub struct PropertiesMap( @@ -839,24 +1130,29 @@ ); impl PropertiesMap { + /// Create new property map. pub fn new() -> Self { Self(BoundedBTreeMap::new()) } + /// Remove property from map. pub fn remove(&mut self, key: &PropertyKey) -> Result, PropertiesError> { Self::check_property_key(key)?; Ok(self.0.remove(key)) } + /// Get property with appropriate key from map. pub fn get(&self, key: &PropertyKey) -> Option<&Value> { self.0.get(key) } + /// Check if map contains key. pub fn contains_key(&self, key: &PropertyKey) -> bool { self.0.contains_key(key) } + /// Check if map contains key with key validation. fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> { if key.is_empty() { return Err(PropertiesError::EmptyPropertyKey); @@ -909,8 +1205,10 @@ } } +/// Alias for property permissions map. pub type PropertiesPermissionMap = PropertiesMap; +/// Wrapper for properties map with consumed space control. #[derive(Encode, Decode, TypeInfo, Clone, PartialEq, MaxEncodedLen)] pub struct Properties { map: PropertiesMap, @@ -919,6 +1217,7 @@ } impl Properties { + /// Create new properies container. pub fn new(space_limit: u32) -> Self { Self { map: PropertiesMap::new(), @@ -927,6 +1226,7 @@ } } + /// Remove propery with appropiate key. pub fn remove(&mut self, key: &PropertyKey) -> Result, PropertiesError> { let value = self.map.remove(key)?; @@ -938,6 +1238,7 @@ Ok(value) } + /// Get property with appropriate key. pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> { self.map.get(key) } @@ -977,6 +1278,7 @@ } } +/// Utility struct for using in `StorageMap`. pub struct CollectionProperties; impl Get for CollectionProperties { @@ -985,6 +1287,7 @@ } } +/// Utility struct for using in `StorageMap`. pub struct TokenProperties; impl Get for TokenProperties { --- a/primitives/data-structs/src/mapping.rs +++ b/primitives/data-structs/src/mapping.rs @@ -1,3 +1,21 @@ +// 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 . + +//! This module contains mapping between different addresses. + use core::marker::PhantomData; use sp_core::H160; @@ -5,12 +23,19 @@ use crate::{CollectionId, TokenId}; use pallet_evm::account::CrossAccountId; +/// Trait for mapping between token id and some `Address`. pub trait TokenAddressMapping
{ + /// Map token id to `Address`. fn token_to_address(collection: CollectionId, token: TokenId) -> Address; + + /// Map `Address` to token id. fn address_to_token(address: &Address) -> Option<(CollectionId, TokenId)>; + + /// Check is address for token. fn is_token_address(address: &Address) -> bool; } +/// Unit struct for mapping token id to/from *Evm address* represented by [`H160`]. pub struct EvmTokenAddressMapping; /// 0xf8238ccfff8ed887463fd5e00000000100000002 - collection 1, token 2 @@ -46,6 +71,7 @@ } } +/// Unit struct for mapping token id to/from [`CrossAccountId`]. pub struct CrossTokenAddressMapping(PhantomData); impl> TokenAddressMapping for CrossTokenAddressMapping { --- a/primitives/rpc/src/lib.rs +++ b/primitives/rpc/src/lib.rs @@ -28,6 +28,7 @@ sp_api::decl_runtime_apis! { #[api_version(2)] + /// Trait for generate rpc. pub trait UniqueApi where AccountId: Decode, CrossAccountId: pallet_evm::account::CrossAccountId, @@ -35,36 +36,57 @@ #[changed_in(2)] fn token_owner(collection: CollectionId, token: TokenId) -> Result; + /// Get number of tokens in collection owned by account. fn account_tokens(collection: CollectionId, account: CrossAccountId) -> Result>; + + /// Number of existing tokens in collection. fn collection_tokens(collection: CollectionId) -> Result>; + + /// Check token exist. fn token_exists(collection: CollectionId, token: TokenId) -> Result; + /// Get token owner. fn token_owner(collection: CollectionId, token: TokenId) -> Result>; + + /// Get real owner of nested token. fn topmost_token_owner(collection: CollectionId, token: TokenId) -> Result>; + + /// Get nested tokens for the specified item. fn token_children(collection: CollectionId, token: TokenId) -> Result>; + /// Get collection properties. fn collection_properties(collection: CollectionId, properties: Option>>) -> Result>; + /// Get token properties. fn token_properties( collection: CollectionId, token_id: TokenId, properties: Option>> ) -> Result>; + /// Get permissions for token properties. fn property_permissions( collection: CollectionId, properties: Option>> ) -> Result>; + /// Get token data. fn token_data( collection: CollectionId, token_id: TokenId, keys: Option>> ) -> Result>; + /// Total number of tokens in collection. fn total_supply(collection: CollectionId) -> Result; + + /// Get account balance for collection (sum of tokens pieces). fn account_balance(collection: CollectionId, account: CrossAccountId) -> Result; + + /// Get account balance for specified token. fn balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Result; + + /// Amount of token pieces allowed to spend from granded account. fn allowance( collection: CollectionId, sender: CrossAccountId, @@ -72,14 +94,31 @@ token: TokenId, ) -> Result; + /// Get list of collection admins. fn adminlist(collection: CollectionId) -> Result>; + + /// Get list of users that allowet to mint tikens in collection. fn allowlist(collection: CollectionId) -> Result>; + + /// Check that user is in allowed list (see [`allowlist`]). fn allowed(collection: CollectionId, user: CrossAccountId) -> Result; + + /// Last minted token id. fn last_token_id(collection: CollectionId) -> Result; + + /// Get collection by id. fn collection_by_id(collection: CollectionId) -> Result>>; + + /// Get collection stats. fn collection_stats() -> Result; + + /// Get the number of blocks through which sponsorship will be available. fn next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Result>; + + /// Get effective colletion limits. fn effective_collection_limits(collection_id: CollectionId) -> Result>; + + /// Get total pieces of token. fn total_pieces(collection_id: CollectionId, token_id: TokenId) -> Result>; fn token_owners(collection: CollectionId, token: TokenId) -> Result>; }