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

Commit b9b1bbb

Browse files
vstam1Jegor Sidorenko
andauthored
changes to nfts pallet for xcm integration (#14395)
* Use Incrementable from frame_support::traits * Chore * make incremental fallible and new nfts function for custom collection ids * fmt * fix benchmark tests nfts * add test * fmt * add safety comment to CollectionId * fmt * add comments to Incrementable * line wrapping * rewrap comments * address feedback * fmt * change unwrap for expect --------- Co-authored-by: Jegor Sidorenko <jegor@parity.io> Co-authored-by: parity-processbot <>
1 parent ac9c1c2 commit b9b1bbb

9 files changed

Lines changed: 145 additions & 29 deletions

File tree

frame/asset-conversion/src/lib.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,8 @@ pub mod pallet {
356356
PathError,
357357
/// The provided path must consists of unique assets.
358358
NonUniquePath,
359+
/// It was not possible to get or increment the Id of the pool.
360+
IncorrectPoolAssetId,
359361
/// Unable to find an element in an array/vec that should have one-to-one correspondence
360362
/// with another. For example, an array of assets constituting a `path` should have a
361363
/// corresponding array of `amounts` along the path.
@@ -426,8 +428,10 @@ pub mod pallet {
426428
MultiAssetIdConversionResult::Native => (),
427429
}
428430

429-
let lp_token = NextPoolAssetId::<T>::get().unwrap_or(T::PoolAssetId::initial_value());
430-
let next_lp_token_id = lp_token.increment();
431+
let lp_token = NextPoolAssetId::<T>::get()
432+
.or(T::PoolAssetId::initial_value())
433+
.ok_or(Error::<T>::IncorrectPoolAssetId)?;
434+
let next_lp_token_id = lp_token.increment().ok_or(Error::<T>::IncorrectPoolAssetId)?;
431435
NextPoolAssetId::<T>::set(Some(next_lp_token_id));
432436

433437
T::PoolAssets::create(lp_token.clone(), pool_account.clone(), false, 1u32.into())?;
@@ -1223,7 +1227,9 @@ pub mod pallet {
12231227
/// Returns the next pool asset id for benchmark purposes only.
12241228
#[cfg(any(test, feature = "runtime-benchmarks"))]
12251229
pub fn get_next_pool_asset_id() -> T::PoolAssetId {
1226-
NextPoolAssetId::<T>::get().unwrap_or(T::PoolAssetId::initial_value())
1230+
NextPoolAssetId::<T>::get()
1231+
.or(T::PoolAssetId::initial_value())
1232+
.expect("Next pool asset ID can not be None")
12271233
}
12281234
}
12291235
}

frame/nfts/src/benchmarking.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,15 +247,15 @@ benchmarks_instance_pallet! {
247247
let call = Call::<T, I>::create { admin, config: default_collection_config::<T, I>() };
248248
}: { call.dispatch_bypass_filter(origin)? }
249249
verify {
250-
assert_last_event::<T, I>(Event::Created { collection: T::Helper::collection(0), creator: caller.clone(), owner: caller }.into());
250+
assert_last_event::<T, I>(Event::NextCollectionIdIncremented { next_id: Some(T::Helper::collection(1)) }.into());
251251
}
252252

253253
force_create {
254254
let caller: T::AccountId = whitelisted_caller();
255255
let caller_lookup = T::Lookup::unlookup(caller.clone());
256256
}: _(SystemOrigin::Root, caller_lookup, default_collection_config::<T, I>())
257257
verify {
258-
assert_last_event::<T, I>(Event::ForceCreated { collection: T::Helper::collection(0), owner: caller }.into());
258+
assert_last_event::<T, I>(Event::NextCollectionIdIncremented { next_id: Some(T::Helper::collection(1)) }.into());
259259
}
260260

261261
destroy {

frame/nfts/src/common_functions.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,21 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
5555
Ok(())
5656
}
5757

58+
pub(crate) fn set_next_collection_id(collection: T::CollectionId) {
59+
let next_id = collection.increment();
60+
NextCollectionId::<T, I>::set(next_id);
61+
Self::deposit_event(Event::NextCollectionIdIncremented { next_id });
62+
}
63+
5864
#[cfg(any(test, feature = "runtime-benchmarks"))]
5965
pub fn set_next_id(id: T::CollectionId) {
6066
NextCollectionId::<T, I>::set(Some(id));
6167
}
6268

6369
#[cfg(test)]
6470
pub fn get_next_id() -> T::CollectionId {
65-
NextCollectionId::<T, I>::get().unwrap_or(T::CollectionId::initial_value())
71+
NextCollectionId::<T, I>::get()
72+
.or(T::CollectionId::initial_value())
73+
.expect("Failed to get next collection ID")
6674
}
6775
}

frame/nfts/src/features/create_delete_collection.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,8 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
5050
),
5151
);
5252

53-
let next_id = collection.increment();
54-
5553
CollectionConfigOf::<T, I>::insert(&collection, config);
5654
CollectionAccount::<T, I>::insert(&owner, &collection, ());
57-
NextCollectionId::<T, I>::set(Some(next_id));
58-
59-
Self::deposit_event(Event::NextCollectionIdIncremented { next_id });
6055
Self::deposit_event(event);
6156
Ok(())
6257
}

frame/nfts/src/impl_nonfungibles.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,9 @@ impl<T: Config<I>, I: 'static> Create<<T as SystemConfig>::AccountId, Collection
162162
Error::<T, I>::WrongSetting
163163
);
164164

165-
let collection =
166-
NextCollectionId::<T, I>::get().unwrap_or(T::CollectionId::initial_value());
165+
let collection = NextCollectionId::<T, I>::get()
166+
.or(T::CollectionId::initial_value())
167+
.ok_or(Error::<T, I>::UnknownCollection)?;
167168

168169
Self::do_create_collection(
169170
collection,
@@ -173,8 +174,40 @@ impl<T: Config<I>, I: 'static> Create<<T as SystemConfig>::AccountId, Collection
173174
T::CollectionDeposit::get(),
174175
Event::Created { collection, creator: who.clone(), owner: admin.clone() },
175176
)?;
177+
178+
Self::set_next_collection_id(collection);
179+
176180
Ok(collection)
177181
}
182+
183+
/// Create a collection of nonfungible items with `collection` Id to be owned by `who` and
184+
/// managed by `admin`. Should be only used for applications that do not have an
185+
/// incremental order for the collection IDs and is a replacement for the auto id creation.
186+
///
187+
///
188+
/// SAFETY: This function can break the pallet if it is used in combination with the auto
189+
/// increment functionality, as it can claim a value in the ID sequence.
190+
fn create_collection_with_id(
191+
collection: T::CollectionId,
192+
who: &T::AccountId,
193+
admin: &T::AccountId,
194+
config: &CollectionConfigFor<T, I>,
195+
) -> Result<(), DispatchError> {
196+
// DepositRequired can be disabled by calling the force_create() only
197+
ensure!(
198+
!config.has_disabled_setting(CollectionSetting::DepositRequired),
199+
Error::<T, I>::WrongSetting
200+
);
201+
202+
Self::do_create_collection(
203+
collection,
204+
who.clone(),
205+
admin.clone(),
206+
*config,
207+
T::CollectionDeposit::get(),
208+
Event::Created { collection, creator: who.clone(), owner: admin.clone() },
209+
)
210+
}
178211
}
179212

180213
impl<T: Config<I>, I: 'static> Destroy<<T as SystemConfig>::AccountId> for Pallet<T, I> {

frame/nfts/src/lib.rs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,14 @@ pub mod pallet {
101101
+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
102102

103103
/// Identifier for the collection of item.
104+
///
105+
/// SAFETY: The functions in the `Incrementable` trait are fallible. If the functions
106+
/// of the implementation both return `None`, the automatic CollectionId generation
107+
/// should not be used. So the `create` and `force_create` extrinsics and the
108+
/// `create_collection` function will return an `UnknownCollection` Error. Instead use
109+
/// the `create_collection_with_id` function. However, if the `Incrementable` trait
110+
/// implementation has an incremental order, the `create_collection_with_id` function
111+
/// should not be used as it can claim a value in the ID sequence.
104112
type CollectionId: Member + Parameter + MaxEncodedLen + Copy + Incrementable;
105113

106114
/// The type used to identify a unique item within a collection.
@@ -476,7 +484,7 @@ pub mod pallet {
476484
/// Mint settings for a collection had changed.
477485
CollectionMintSettingsUpdated { collection: T::CollectionId },
478486
/// Event gets emitted when the `NextCollectionId` gets incremented.
479-
NextCollectionIdIncremented { next_id: T::CollectionId },
487+
NextCollectionIdIncremented { next_id: Option<T::CollectionId> },
480488
/// The price was set for the item.
481489
ItemPriceSet {
482490
collection: T::CollectionId,
@@ -665,8 +673,9 @@ pub mod pallet {
665673
admin: AccountIdLookupOf<T>,
666674
config: CollectionConfigFor<T, I>,
667675
) -> DispatchResult {
668-
let collection =
669-
NextCollectionId::<T, I>::get().unwrap_or(T::CollectionId::initial_value());
676+
let collection = NextCollectionId::<T, I>::get()
677+
.or(T::CollectionId::initial_value())
678+
.ok_or(Error::<T, I>::UnknownCollection)?;
670679

671680
let owner = T::CreateOrigin::ensure_origin(origin, &collection)?;
672681
let admin = T::Lookup::lookup(admin)?;
@@ -684,7 +693,10 @@ pub mod pallet {
684693
config,
685694
T::CollectionDeposit::get(),
686695
Event::Created { collection, creator: owner, owner: admin },
687-
)
696+
)?;
697+
698+
Self::set_next_collection_id(collection);
699+
Ok(())
688700
}
689701

690702
/// Issue a new collection of non-fungible items from a privileged origin.
@@ -712,8 +724,9 @@ pub mod pallet {
712724
T::ForceOrigin::ensure_origin(origin)?;
713725
let owner = T::Lookup::lookup(owner)?;
714726

715-
let collection =
716-
NextCollectionId::<T, I>::get().unwrap_or(T::CollectionId::initial_value());
727+
let collection = NextCollectionId::<T, I>::get()
728+
.or(T::CollectionId::initial_value())
729+
.ok_or(Error::<T, I>::UnknownCollection)?;
717730

718731
Self::do_create_collection(
719732
collection,
@@ -722,7 +735,10 @@ pub mod pallet {
722735
config,
723736
Zero::zero(),
724737
Event::ForceCreated { collection, owner },
725-
)
738+
)?;
739+
740+
Self::set_next_collection_id(collection);
741+
Ok(())
726742
}
727743

728744
/// Destroy a collection of fungible items.

frame/nfts/src/tests.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use frame_support::{
2323
assert_noop, assert_ok,
2424
dispatch::Dispatchable,
2525
traits::{
26-
tokens::nonfungibles_v2::{Destroy, Mutate},
26+
tokens::nonfungibles_v2::{Create, Destroy, Mutate},
2727
Currency, Get,
2828
},
2929
};
@@ -3678,3 +3678,41 @@ fn pre_signed_attributes_should_work() {
36783678
);
36793679
})
36803680
}
3681+
3682+
#[test]
3683+
fn basic_create_collection_with_id_should_work() {
3684+
new_test_ext().execute_with(|| {
3685+
assert_noop!(
3686+
Nfts::create_collection_with_id(
3687+
0u32,
3688+
&account(1),
3689+
&account(1),
3690+
&default_collection_config(),
3691+
),
3692+
Error::<Test>::WrongSetting
3693+
);
3694+
3695+
Balances::make_free_balance_be(&account(1), 100);
3696+
Balances::make_free_balance_be(&account(2), 100);
3697+
3698+
assert_ok!(Nfts::create_collection_with_id(
3699+
0u32,
3700+
&account(1),
3701+
&account(1),
3702+
&collection_config_with_all_settings_enabled(),
3703+
));
3704+
3705+
assert_eq!(collections(), vec![(account(1), 0)]);
3706+
3707+
// CollectionId already taken.
3708+
assert_noop!(
3709+
Nfts::create_collection_with_id(
3710+
0u32,
3711+
&account(2),
3712+
&account(2),
3713+
&collection_config_with_all_settings_enabled(),
3714+
),
3715+
Error::<Test>::CollectionIdInUse
3716+
);
3717+
});
3718+
}

frame/support/src/traits/storage.rs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -132,24 +132,37 @@ macro_rules! impl_incrementable {
132132
($($type:ty),+) => {
133133
$(
134134
impl Incrementable for $type {
135-
fn increment(&self) -> Self {
135+
fn increment(&self) -> Option<Self> {
136136
let mut val = self.clone();
137137
val.saturating_inc();
138-
val
138+
Some(val)
139139
}
140140

141-
fn initial_value() -> Self {
142-
0
141+
fn initial_value() -> Option<Self> {
142+
Some(0)
143143
}
144144
}
145145
)+
146146
};
147147
}
148148

149-
/// For example: allows new identifiers to be created in a linear fashion.
150-
pub trait Incrementable {
151-
fn increment(&self) -> Self;
152-
fn initial_value() -> Self;
149+
/// A trait representing an incrementable type.
150+
///
151+
/// The `increment` and `initial_value` functions are fallible.
152+
/// They should either both return `Some` with a valid value, or `None`.
153+
pub trait Incrementable
154+
where
155+
Self: Sized,
156+
{
157+
/// Increments the value.
158+
///
159+
/// Returns `Some` with the incremented value if it is possible, or `None` if it is not.
160+
fn increment(&self) -> Option<Self>;
161+
162+
/// Returns the initial value.
163+
///
164+
/// Returns `Some` with the initial value if it is available, or `None` if it is not.
165+
fn initial_value() -> Option<Self>;
153166
}
154167

155168
impl_incrementable!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);

frame/support/src/traits/tokens/nonfungibles_v2.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,13 @@ pub trait Create<AccountId, CollectionConfig>: Inspect<AccountId> {
198198
admin: &AccountId,
199199
config: &CollectionConfig,
200200
) -> Result<Self::CollectionId, DispatchError>;
201+
202+
fn create_collection_with_id(
203+
collection: Self::CollectionId,
204+
who: &AccountId,
205+
admin: &AccountId,
206+
config: &CollectionConfig,
207+
) -> Result<(), DispatchError>;
201208
}
202209

203210
/// Trait for providing the ability to destroy collections of nonfungible items.

0 commit comments

Comments
 (0)