Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Uniques: An economically-secure basic-featured NFT pallet #8813

Merged
merged 61 commits into from
Jun 1, 2021
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
2d98b0d
Uniques: An economically-secure basic-featured NFT pallet
gavofyork May 14, 2021
628b1bd
Merge remote-tracking branch 'origin/master' into gav-uniques
gavofyork May 16, 2021
aa84878
force_transfer
gavofyork May 16, 2021
0c0c4cb
freeze/thaw
gavofyork May 16, 2021
a203c1f
team management
gavofyork May 16, 2021
b2c20f8
approvals
gavofyork May 16, 2021
f98dcaf
Fixes
gavofyork May 16, 2021
da78f22
force_asset_status
gavofyork May 16, 2021
ee5f32a
class_metadata
gavofyork May 17, 2021
c148760
instance metadata
gavofyork May 17, 2021
ec47648
Fixes
gavofyork May 17, 2021
2949d12
use nmap
gavofyork May 17, 2021
30f2f9e
Fixes
gavofyork May 17, 2021
00c8e04
class metadata has information field
gavofyork May 17, 2021
326beba
Intiial mock/tests and a fix
gavofyork May 19, 2021
f5c9d1d
Remove impl_non_fungibles
gavofyork May 19, 2021
7874135
Docs
gavofyork May 19, 2021
6a231a8
Update frame/uniques/src/lib.rs
gavofyork May 19, 2021
4699b23
Update frame/uniques/src/lib.rs
gavofyork May 19, 2021
fb6ea03
Update frame/uniques/src/lib.rs
gavofyork May 19, 2021
87cc1d8
Update frame/uniques/src/lib.rs
gavofyork May 19, 2021
3ad9a52
Merge remote-tracking branch 'origin/master' into gav-uniques
gavofyork May 19, 2021
8e8b4df
Reserve, don't transfer.
gavofyork May 19, 2021
01f9713
Fixes
gavofyork May 19, 2021
4bf6f54
Tests
gavofyork May 19, 2021
821de3d
Tests
gavofyork May 19, 2021
d410298
refresh_deposit
gavofyork May 19, 2021
d703510
Tests and proper handling of metdata destruction
gavofyork May 19, 2021
59ff5e9
test burn
gavofyork May 19, 2021
4be8928
Tests
gavofyork May 19, 2021
de1eb5a
Update impl_fungibles.rs
gavofyork May 20, 2021
53c666c
Initial benchmarking
gavofyork May 20, 2021
1545abe
benchmark
gavofyork May 20, 2021
24e08cc
Fixes
gavofyork May 20, 2021
3027c7b
Merge branch 'master' of https://github.com/paritytech/substrate into…
May 20, 2021
da6f748
cargo run --release --features=runtime-benchmarks --manifest-path=bin…
May 20, 2021
95e49b9
Attributes
gavofyork May 20, 2021
ccaf6ec
Attribute metadata
gavofyork May 20, 2021
c51153f
Fixes
gavofyork May 20, 2021
5361afb
Update frame/uniques/README.md
gavofyork May 21, 2021
87ab1b4
Merge branch 'gav-uniques-attributes' into gav-uniques
gavofyork May 21, 2021
8a45594
Docs
gavofyork May 21, 2021
dad10a7
Docs
gavofyork May 21, 2021
33fd4a8
Docs
gavofyork May 21, 2021
31c6e03
Simple metadata
gavofyork May 21, 2021
092127c
Use BoundedVec
gavofyork May 24, 2021
67b190b
Merge branch 'master' of https://github.com/paritytech/substrate into…
May 24, 2021
f9f6f51
cargo run --release --features=runtime-benchmarks --manifest-path=bin…
May 24, 2021
f38e9b9
Update frame/uniques/src/lib.rs
gavofyork May 31, 2021
04c900a
Update frame/uniques/src/lib.rs
gavofyork May 31, 2021
d41c1b4
Update frame/uniques/src/lib.rs
gavofyork May 31, 2021
7353b34
Update frame/uniques/src/lib.rs
gavofyork May 31, 2021
f84688d
Update frame/uniques/src/lib.rs
gavofyork May 31, 2021
bd82d6e
Fixes
gavofyork May 31, 2021
dce127c
Merge branch 'gav-uniques' of github.com:paritytech/substrate into ga…
gavofyork May 31, 2021
ca0b97c
Update frame/uniques/README.md
gavofyork May 31, 2021
59dbd0f
Update frame/uniques/README.md
gavofyork May 31, 2021
2e36b5c
Update frame/uniques/README.md
gavofyork May 31, 2021
f50d089
Docs
gavofyork Jun 1, 2021
f240bc4
Bump
gavofyork Jun 1, 2021
765a780
Merge branch 'master' into gav-uniques
gavofyork Jun 1, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ members = [
"frame/transaction-payment/rpc/runtime-api",
"frame/treasury",
"frame/tips",
"frame/uniques",
"frame/utility",
"frame/vesting",
"primitives/allocator",
Expand Down
4 changes: 4 additions & 0 deletions bin/node/runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ pallet-treasury = { version = "3.0.0", default-features = false, path = "../../.
pallet-utility = { version = "3.0.0", default-features = false, path = "../../../frame/utility" }
pallet-transaction-payment = { version = "3.0.0", default-features = false, path = "../../../frame/transaction-payment" }
pallet-transaction-payment-rpc-runtime-api = { version = "3.0.0", default-features = false, path = "../../../frame/transaction-payment/rpc/runtime-api/" }
pallet-uniques = { version = "3.0.0", default-features = false, path = "../../../frame/uniques" }
pallet-vesting = { version = "3.0.0", default-features = false, path = "../../../frame/vesting" }

[build-dependencies]
Expand Down Expand Up @@ -155,6 +156,7 @@ std = [
"sp-version/std",
"pallet-society/std",
"pallet-recovery/std",
"pallet-uniques/std",
"pallet-vesting/std",
"log/std",
"frame-try-runtime/std",
Expand Down Expand Up @@ -191,6 +193,7 @@ runtime-benchmarks = [
"pallet-tips/runtime-benchmarks",
"pallet-treasury/runtime-benchmarks",
"pallet-utility/runtime-benchmarks",
"pallet-uniques/runtime-benchmarks",
"pallet-vesting/runtime-benchmarks",
"pallet-offences-benchmarking",
"pallet-session-benchmarking",
Expand Down Expand Up @@ -234,6 +237,7 @@ try-runtime = [
"pallet-utility/try-runtime",
"pallet-society/try-runtime",
"pallet-recovery/try-runtime",
"pallet-uniques/try-runtime",
"pallet-vesting/try-runtime",
"pallet-gilt/try-runtime",
]
Expand Down
26 changes: 26 additions & 0 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1089,6 +1089,30 @@ impl pallet_gilt::Config for Runtime {
type WeightInfo = pallet_gilt::weights::SubstrateWeight<Runtime>;
}

parameter_types! {
pub const ClassDeposit: Balance = 100 * DOLLARS;
pub const InstanceDeposit: Balance = 1 * DOLLARS;
pub const KeyLimit: u32 = 32;
pub const ValueLimit: u32 = 256;
}

impl pallet_uniques::Config for Runtime {
type Event = Event;
type ClassId = u32;
type InstanceId = u32;
type Currency = Balances;
type ForceOrigin = frame_system::EnsureRoot<AccountId>;
type ClassDeposit = ClassDeposit;
type InstanceDeposit = InstanceDeposit;
type MetadataDepositBase = MetadataDepositBase;
type AttributeDepositBase = MetadataDepositBase;
type DepositPerByte = MetadataDepositPerByte;
type StringLimit = StringLimit;
type KeyLimit = KeyLimit;
type ValueLimit = ValueLimit;
type WeightInfo = pallet_uniques::weights::SubstrateWeight<Runtime>;
}

construct_runtime!(
pub enum Runtime where
Block = Block,
Expand Down Expand Up @@ -1133,6 +1157,7 @@ construct_runtime!(
Mmr: pallet_mmr::{Pallet, Storage},
Lottery: pallet_lottery::{Pallet, Call, Storage, Event<T>},
Gilt: pallet_gilt::{Pallet, Call, Storage, Event<T>, Config},
Uniques: pallet_uniques::{Pallet, Call, Storage, Event<T>},
}
);

Expand Down Expand Up @@ -1507,6 +1532,7 @@ impl_runtime_apis! {
add_benchmark!(params, batches, pallet_timestamp, Timestamp);
add_benchmark!(params, batches, pallet_tips, Tips);
add_benchmark!(params, batches, pallet_treasury, Treasury);
add_benchmark!(params, batches, pallet_uniques, Uniques);
add_benchmark!(params, batches, pallet_utility, Utility);
add_benchmark!(params, batches, pallet_vesting, Vesting);

Expand Down
8 changes: 4 additions & 4 deletions frame/assets/src/impl_fungibles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,26 +127,26 @@ impl<T: Config<I>, I: 'static> fungibles::Unbalanced<T::AccountId> for Pallet<T,
});
}
fn decrease_balance(asset: T::AssetId, who: &T::AccountId, amount: Self::Balance)
-> Result<Self::Balance, DispatchError>
-> Result<Self::Balance, DispatchError>
{
let f = DebitFlags { keep_alive: false, best_effort: false };
Self::decrease_balance(asset, who, amount, f, |_, _| Ok(()))
}
fn decrease_balance_at_most(asset: T::AssetId, who: &T::AccountId, amount: Self::Balance)
-> Self::Balance
-> Self::Balance
{
let f = DebitFlags { keep_alive: false, best_effort: true };
Self::decrease_balance(asset, who, amount, f, |_, _| Ok(()))
.unwrap_or(Zero::zero())
}
fn increase_balance(asset: T::AssetId, who: &T::AccountId, amount: Self::Balance)
-> Result<Self::Balance, DispatchError>
-> Result<Self::Balance, DispatchError>
{
Self::increase_balance(asset, who, amount, |_| Ok(()))?;
Ok(amount)
}
fn increase_balance_at_most(asset: T::AssetId, who: &T::AccountId, amount: Self::Balance)
-> Self::Balance
-> Self::Balance
{
match Self::increase_balance(asset, who, amount, |_| Ok(())) {
Ok(()) => amount,
Expand Down
18 changes: 8 additions & 10 deletions frame/assets/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,8 +409,6 @@ pub mod pallet {
/// - `owner`: The owner of this class of assets. The owner has full superuser permissions
/// over this asset, but may later change and configure the permissions using `transfer_ownership`
/// and `set_team`.
/// - `max_zombies`: The total number of accounts which may hold assets in this class yet
/// have no existential deposit.
/// - `min_balance`: The minimum balance of this new asset that any single account must
/// have. If an account's balance is reduced below this, then it collapses to zero.
///
Expand Down Expand Up @@ -517,7 +515,7 @@ pub mod pallet {
/// - `beneficiary`: The account to be credited with the minted assets.
/// - `amount`: The amount of the asset to be minted.
///
/// Emits `Destroyed` event when successful.
/// Emits `Issued` event when successful.
///
/// Weight: `O(1)`
/// Modes: Pre-existing balance of `beneficiary`; Account pre-existence of `beneficiary`.
Expand Down Expand Up @@ -582,8 +580,8 @@ pub mod pallet {
/// to zero.
///
/// Weight: `O(1)`
/// Modes: Pre-existence of `target`; Post-existence of sender; Prior & post zombie-status
/// of sender; Account pre-existence of `target`.
/// Modes: Pre-existence of `target`; Post-existence of sender; Account pre-existence of
/// `target`.
#[pallet::weight(T::WeightInfo::transfer())]
pub(super) fn transfer(
origin: OriginFor<T>,
Expand Down Expand Up @@ -618,8 +616,8 @@ pub mod pallet {
/// to zero.
///
/// Weight: `O(1)`
/// Modes: Pre-existence of `target`; Post-existence of sender; Prior & post zombie-status
/// of sender; Account pre-existence of `target`.
/// Modes: Pre-existence of `target`; Post-existence of sender; Account pre-existence of
/// `target`.
#[pallet::weight(T::WeightInfo::transfer_keep_alive())]
pub(super) fn transfer_keep_alive(
origin: OriginFor<T>,
Expand Down Expand Up @@ -655,8 +653,8 @@ pub mod pallet {
/// to zero.
///
/// Weight: `O(1)`
/// Modes: Pre-existence of `dest`; Post-existence of `source`; Prior & post zombie-status
/// of `source`; Account pre-existence of `dest`.
/// Modes: Pre-existence of `dest`; Post-existence of `source`; Account pre-existence of
/// `dest`.
#[pallet::weight(T::WeightInfo::force_transfer())]
pub(super) fn force_transfer(
origin: OriginFor<T>,
Expand Down Expand Up @@ -773,7 +771,7 @@ pub mod pallet {
///
/// Origin must be Signed and the sender should be the Admin of the asset `id`.
///
/// - `id`: The identifier of the asset to be frozen.
/// - `id`: The identifier of the asset to be thawed.
///
/// Emits `Thawed`.
///
Expand Down
6 changes: 6 additions & 0 deletions frame/support/src/storage/bounded_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ impl<T, S> BoundedVec<T, S> {
}
}

impl<T, S: Get<u32>> From<BoundedVec<T, S>> for Vec<T> {
fn from(x: BoundedVec<T, S>) -> Vec<T> {
x.0
}
}

impl<T, S: Get<u32>> BoundedVec<T, S> {
/// Get the bound of the type in `usize`.
pub fn bound() -> usize {
Expand Down
2 changes: 1 addition & 1 deletion frame/support/src/storage/types/nmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ where

/// Iter over all value of the storage.
///
/// NOTE: If a value failed to decode becaues storage is corrupted then it is skipped.
/// NOTE: If a value failed to decode because storage is corrupted then it is skipped.
pub fn iter_values() -> crate::storage::PrefixIterator<Value> {
<Self as crate::storage::StoragePrefixedMap<Value>>::iter_values()
}
Expand Down
46 changes: 46 additions & 0 deletions frame/uniques/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
[package]
name = "pallet-uniques"
version = "3.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
license = "Apache-2.0"
homepage = "https://substrate.dev"
repository = "https://github.com/paritytech/substrate/"
description = "FRAME NFT asset management pallet"
readme = "README.md"

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false }
sp-std = { version = "3.0.0", default-features = false, path = "../../primitives/std" }
sp-core = { version = "3.0.0", default-features = false, path = "../../primitives/core" }
sp-runtime = { version = "3.0.0", default-features = false, path = "../../primitives/runtime" }
frame-support = { version = "3.0.0", default-features = false, path = "../support" }
frame-system = { version = "3.0.0", default-features = false, path = "../system" }
frame-benchmarking = { version = "3.1.0", default-features = false, path = "../benchmarking", optional = true }

[dev-dependencies]
sp-std = { version = "3.0.0", path = "../../primitives/std" }
sp-core = { version = "3.0.0", path = "../../primitives/core" }
Comment on lines +25 to +26
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
sp-std = { version = "3.0.0", path = "../../primitives/std" }
sp-core = { version = "3.0.0", path = "../../primitives/core" }

this seems redundant as they are part of deps above already

sp-io = { version = "3.0.0", path = "../../primitives/io" }
pallet-balances = { version = "3.0.0", path = "../balances" }

[features]
default = ["std"]
std = [
"codec/std",
"sp-std/std",
"sp-core/std",
"sp-runtime/std",
"frame-support/std",
"frame-system/std",
"frame-benchmarking/std",
]
runtime-benchmarks = [
"frame-benchmarking",
"sp-runtime/runtime-benchmarks",
"frame-system/runtime-benchmarks",
]
try-runtime = ["frame-support/try-runtime"]
117 changes: 117 additions & 0 deletions frame/uniques/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Uniques Module

A simple, secure module for dealing with fungible assets.
gavofyork marked this conversation as resolved.
Show resolved Hide resolved

## Overview

The Assets module provides functionality for asset management of fungible asset classes
with a fixed supply, including:
gavofyork marked this conversation as resolved.
Show resolved Hide resolved

* Asset Issuance
* Asset Transfer
* Asset Destruction
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be deleted or updated


To use it in your runtime, you need to implement the assets [`assets::Trait`](https://docs.rs/pallet-assets/latest/pallet_assets/trait.Trait.html).

The supported dispatchable functions are documented in the [`assets::Call`](https://docs.rs/pallet-assets/latest/pallet_assets/enum.Call.html) enum.
gavofyork marked this conversation as resolved.
Show resolved Hide resolved

### Terminology

* **Asset issuance:** The creation of a new asset, whose total supply will belong to the
account that issues the asset.
* **Asset transfer:** The action of transferring assets from one account to another.
* **Asset destruction:** The process of an account removing its entire holding of an asset.
* **Fungible asset:** An asset whose units are interchangeable.
* **Non-fungible asset:** An asset for which each unit has unique characteristics.

### Goals

The assets system in Substrate is designed to make the following possible:

* Issue a unique asset to its creator's account.
* Move assets between accounts.
* Remove an account's balance of an asset when requested by that account's owner and update
the asset's total supply.

## Interface

### Dispatchable Functions

* `issue` - Issues the total supply of a new fungible asset to the account of the caller of the function.
* `transfer` - Transfers an `amount` of units of fungible asset `id` from the balance of
the function caller's account (`origin`) to a `target` account.
* `destroy` - Destroys the entire holding of a fungible asset `id` associated with the account
that called the function.

Please refer to the [`Call`](https://docs.rs/pallet-assets/latest/pallet_assets/enum.Call.html) enum and its associated variants for documentation on each function.

### Public Functions
<!-- Original author of descriptions: @gavofyork -->

* `balance` - Get the asset `id` balance of `who`.
* `total_supply` - Get the total supply of an asset `id`.

Please refer to the [`Module`](https://docs.rs/pallet-assets/latest/pallet_assets/struct.Module.html) struct for details on publicly available functions.

## Usage

The following example shows how to use the Assets module in your runtime by exposing public functions to:

* Issue a new fungible asset for a token distribution event (airdrop).
* Query the fungible asset holding balance of an account.
* Query the total supply of a fungible asset that has been issued.

### Prerequisites

Import the Assets module and types and derive your runtime's configuration traits from the Assets module trait.

### Simple Code Snippet

```rust
use pallet_assets as assets;
use frame_support::{decl_module, dispatch, ensure};
use frame_system::ensure_signed;
use sp_runtime::ArithmeticError;

pub trait Config: assets::Config { }

decl_module! {
pub struct Module<T: Config> for enum Call where origin: T::Origin {
pub fn issue_token_airdrop(origin) -> dispatch::DispatchResult {
let sender = ensure_signed(origin).map_err(|e| e.as_str())?;

const ACCOUNT_ALICE: u64 = 1;
const ACCOUNT_BOB: u64 = 2;
const COUNT_AIRDROP_RECIPIENTS: u64 = 2;
const TOKENS_FIXED_SUPPLY: u64 = 100;

ensure!(!COUNT_AIRDROP_RECIPIENTS.is_zero(), ArithmeticError::DivisionByZero);

let asset_id = Self::next_asset_id();

<NextAssetId<T>>::mutate(|asset_id| *asset_id += 1);
<Balances<T>>::insert((asset_id, &ACCOUNT_ALICE), TOKENS_FIXED_SUPPLY / COUNT_AIRDROP_RECIPIENTS);
<Balances<T>>::insert((asset_id, &ACCOUNT_BOB), TOKENS_FIXED_SUPPLY / COUNT_AIRDROP_RECIPIENTS);
<TotalSupply<T>>::insert(asset_id, TOKENS_FIXED_SUPPLY);

Self::deposit_event(RawEvent::Issued(asset_id, sender, TOKENS_FIXED_SUPPLY));
Ok(())
}
}
}
```

## Assumptions

Below are assumptions that must be held when using this module. If any of
them are violated, the behavior of this module is undefined.

* The total count of assets should be less than
`Config::AssetId::max_value()`.
gavofyork marked this conversation as resolved.
Show resolved Hide resolved

## Related Modules

* [`System`](https://docs.rs/frame-system/latest/frame_system/)
* [`Support`](https://docs.rs/frame-support/latest/frame_support/)

License: Apache-2.0
Loading