Skip to content

Commit

Permalink
Allow to set the max supply for collection (paritytech#11441)
Browse files Browse the repository at this point in the history
* Allow to set the max supply for collection

* Update error

* Add weights info

* cargo run --quiet --profile=production  --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark pallet --chain=dev --steps=50 --repeat=20 --pallet=pallet_uniques --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/uniques/src/weights.rs --template=./.maintain/frame-weight-template.hbs

* Update frame/uniques/src/lib.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

Co-authored-by: Parity Bot <admin@parity.io>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
  • Loading branch information
3 people authored and ark0f committed Feb 27, 2023
1 parent 7ad9294 commit ae66d6e
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 63 deletions.
10 changes: 10 additions & 0 deletions frame/uniques/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,5 +398,15 @@ benchmarks_instance_pallet! {
}.into());
}

set_collection_max_supply {
let (collection, caller, _) = create_collection::<T, I>();
}: _(SystemOrigin::Signed(caller.clone()), collection, u32::MAX)
verify {
assert_last_event::<T, I>(Event::CollectionMaxSupplySet {
collection,
max_supply: u32::MAX,
}.into());
}

impl_benchmark_test_suite!(Uniques, crate::mock::new_test_ext(), crate::mock::Test);
}
4 changes: 4 additions & 0 deletions frame/uniques/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {

with_details(collection_details)?;

if let Ok(max_supply) = CollectionMaxSupply::<T, I>::try_get(&collection) {
ensure!(collection_details.items < max_supply, Error::<T, I>::MaxSupplyReached);
}

let items =
collection_details.items.checked_add(1).ok_or(ArithmeticError::Overflow)?;
collection_details.items = items;
Expand Down
52 changes: 52 additions & 0 deletions frame/uniques/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,11 @@ pub mod pallet {
OptionQuery,
>;

#[pallet::storage]
/// Keeps track of the number of items a collection might have.
pub(super) type CollectionMaxSupply<T: Config<I>, I: 'static = ()> =
StorageMap<_, Blake2_128Concat, T::CollectionId, u32, OptionQuery>;

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config<I>, I: 'static = ()> {
Expand Down Expand Up @@ -334,6 +339,8 @@ pub mod pallet {
},
/// Ownership acceptance has changed for an account.
OwnershipAcceptanceChanged { who: T::AccountId, maybe_collection: Option<T::CollectionId> },
/// Max supply has been set for a collection.
CollectionMaxSupplySet { collection: T::CollectionId, max_supply: u32 },
}

#[pallet::error]
Expand Down Expand Up @@ -362,6 +369,12 @@ pub mod pallet {
Unaccepted,
/// The item is locked.
Locked,
/// All items have been minted.
MaxSupplyReached,
/// The max supply has already been set.
MaxSupplyAlreadySet,
/// The provided max supply is less to the amount of items a collection already has.
MaxSupplyTooSmall,
}

impl<T: Config<I>, I: 'static> Pallet<T, I> {
Expand Down Expand Up @@ -1356,5 +1369,44 @@ pub mod pallet {
Self::deposit_event(Event::OwnershipAcceptanceChanged { who, maybe_collection });
Ok(())
}

/// Set the maximum amount of items a collection could have.
///
/// Origin must be either `ForceOrigin` or `Signed` and the sender should be the Owner of
/// the `collection`.
///
/// Note: This function can only succeed once per collection.
///
/// - `collection`: The identifier of the collection to change.
/// - `max_supply`: The maximum amount of items a collection could have.
///
/// Emits `CollectionMaxSupplySet` event when successful.
#[pallet::weight(T::WeightInfo::set_collection_max_supply())]
pub fn set_collection_max_supply(
origin: OriginFor<T>,
collection: T::CollectionId,
max_supply: u32,
) -> DispatchResult {
let maybe_check_owner = T::ForceOrigin::try_origin(origin)
.map(|_| None)
.or_else(|origin| ensure_signed(origin).map(Some))?;

ensure!(
!CollectionMaxSupply::<T, I>::contains_key(&collection),
Error::<T, I>::MaxSupplyAlreadySet
);

let details =
Collection::<T, I>::get(&collection).ok_or(Error::<T, I>::UnknownCollection)?;
if let Some(check_owner) = &maybe_check_owner {
ensure!(check_owner == &details.owner, Error::<T, I>::NoPermission);
}

ensure!(details.items <= max_supply, Error::<T, I>::MaxSupplyTooSmall);

CollectionMaxSupply::<T, I>::insert(&collection, max_supply);
Self::deposit_event(Event::CollectionMaxSupplySet { collection, max_supply });
Ok(())
}
}
}
57 changes: 55 additions & 2 deletions frame/uniques/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@

//! Tests for Uniques pallet.
use super::*;
use crate::mock::*;
use crate::{mock::*, Event, *};
use frame_support::{assert_noop, assert_ok, traits::Currency};
use pallet_balances::Error as BalancesError;
use sp_std::prelude::*;
Expand Down Expand Up @@ -71,6 +70,18 @@ fn attributes(collection: u32) -> Vec<(Option<u32>, Vec<u8>, Vec<u8>)> {
s
}

fn events() -> Vec<Event<Test>> {
let result = System::events()
.into_iter()
.map(|r| r.event)
.filter_map(|e| if let mock::Event::Uniques(inner) = e { Some(inner) } else { None })
.collect::<Vec<_>>();

System::reset_events();

result
}

#[test]
fn basic_setup_works() {
new_test_ext().execute_with(|| {
Expand Down Expand Up @@ -633,3 +644,45 @@ fn cancel_approval_works_with_force() {
);
});
}

#[test]
fn max_supply_should_work() {
new_test_ext().execute_with(|| {
let collection_id = 0;
let user_id = 1;
let max_supply = 2;

// validate set_collection_max_supply
assert_ok!(Uniques::force_create(Origin::root(), collection_id, user_id, true));
assert!(!CollectionMaxSupply::<Test>::contains_key(collection_id));

assert_ok!(Uniques::set_collection_max_supply(
Origin::signed(user_id),
collection_id,
max_supply
));
assert_eq!(CollectionMaxSupply::<Test>::get(collection_id).unwrap(), max_supply);

assert!(events().contains(&Event::<Test>::CollectionMaxSupplySet {
collection: collection_id,
max_supply,
}));

assert_noop!(
Uniques::set_collection_max_supply(
Origin::signed(user_id),
collection_id,
max_supply + 1
),
Error::<Test>::MaxSupplyAlreadySet
);

// validate we can't mint more to max supply
assert_ok!(Uniques::mint(Origin::signed(user_id), collection_id, 0, user_id));
assert_ok!(Uniques::mint(Origin::signed(user_id), collection_id, 1, user_id));
assert_noop!(
Uniques::mint(Origin::signed(user_id), collection_id, 2, user_id),
Error::<Test>::MaxSupplyReached
);
});
}
Loading

0 comments on commit ae66d6e

Please sign in to comment.