Skip to content

Commit

Permalink
Merge pull request #243 from chexware/bug/nft-minting-hotfix
Browse files Browse the repository at this point in the history
NFT minting bug  hotfix
  • Loading branch information
justinphamnz authored Nov 13, 2022
2 parents 8bd81ed + be481c1 commit fb64619
Show file tree
Hide file tree
Showing 8 changed files with 244 additions and 92 deletions.
5 changes: 5 additions & 0 deletions pallets/nft/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ benchmarks! {
let class_fund = get_class_fund::<T>(0u32.into());
T::Currency::make_free_balance_be(&class_fund, dollar(100).unique_saturated_into());
}: _(RawOrigin::Signed(caller), 0u32.into())
force_update_total_issuance{
let caller = funded_account::<T>("caller", 0);
crate::Pallet::<T>::create_group(RawOrigin::Root.into(), vec![1], vec![1]);
crate::Pallet::<T>::create_class(RawOrigin::Signed(caller.clone()).into(), vec![1], test_attributes(1), 0u32.into(), TokenType::Transferable, CollectionType::Collectable, Perbill::from_percent(0u32), None);
}: _(RawOrigin::Root, 0u32.into(), 0u32.into(), 1u32.into())
}

impl_benchmark_test_suite!(Pallet, crate::benchmarking::tests::new_test_ext(), crate::mock::Test);
43 changes: 42 additions & 1 deletion pallets/nft/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ pub mod pallet {
TokenIdOf<T>,
(ClassIdOf<T>, TokenIdOf<T>),
),
/// Successfully updated class total issuance
ClassTotalIssuanceUpdated(ClassIdOf<T>, TokenIdOf<T>),
/// Signed on NFT
SignedNft(TokenIdOf<T>, <T as frame_system::Config>::AccountId),
/// Promotion enabled
Expand Down Expand Up @@ -345,6 +347,8 @@ pub mod pallet {
TotalMintedAssetsForClassExceededProposedLimit,
/// Hard limit is already set
HardLimitIsAlreadySet,
/// Invalid current total issuance
InvalidCurrentTotalIssuance,
}

#[pallet::call]
Expand All @@ -358,6 +362,7 @@ pub mod pallet {
///
/// Emits `NewNftCollectionCreated` if successful.
#[pallet::weight(T::WeightInfo::create_group())]
#[transactional]
pub fn create_group(
origin: OriginFor<T>,
name: NftMetadata,
Expand Down Expand Up @@ -399,6 +404,7 @@ pub mod pallet {
///
/// Emits `NewNftClassCreated` if successful.
#[pallet::weight(T::WeightInfo::create_class())]
#[transactional]
pub fn create_class(
origin: OriginFor<T>,
metadata: NftMetadata,
Expand Down Expand Up @@ -434,6 +440,7 @@ pub mod pallet {
///
/// Emits `NewNftMinted` if successful.
#[pallet::weight(< T as Config >::WeightInfo::mint() * * quantity as u64)]
#[transactional]
pub fn mint(
origin: OriginFor<T>,
class_id: ClassIdOf<T>,
Expand All @@ -456,6 +463,7 @@ pub mod pallet {
///
/// Emits `TransferedNft` if successful.
#[pallet::weight(T::WeightInfo::transfer())]
#[transactional]
pub fn transfer(
origin: OriginFor<T>,
to: T::AccountId,
Expand Down Expand Up @@ -635,6 +643,7 @@ pub mod pallet {
///
/// Emits `ForceTransferredNft` if successful.
#[pallet::weight(T::WeightInfo::transfer())]
#[transactional]
pub fn force_transfer(
origin: OriginFor<T>,
from: T::AccountId,
Expand Down Expand Up @@ -724,7 +733,7 @@ pub mod pallet {
/// Unlock the provided NFT by governance if already locked
///
/// The dispatch origin for this call must be _Root_.
/// - `class_id`: the class ID of the collection
/// - `token_id`: the class ID nad token ID of the asset
///
/// Emits `NftUnlocked` if successful.
#[pallet::weight(T::WeightInfo::sign_asset())]
Expand All @@ -739,6 +748,38 @@ pub mod pallet {
Ok(())
})
}

/// Force update the total issuance of a given class
///
/// The dispatch origin for this call must be _Root_.
/// - `class_id`: the class ID of the collection
/// - `current_total_issuance`: the current total issuance of the collection
/// - `new_total_issuance`: the new total issuance of the collection
///
/// Emits `ClassTotalIssuanceUpdated` if successful.
#[pallet::weight(T::WeightInfo::force_update_total_issuance())]
pub fn force_update_total_issuance(
origin: OriginFor<T>,
class_id: ClassIdOf<T>,
current_total_issuance: TokenIdOf<T>,
new_total_issuance: TokenIdOf<T>,
) -> DispatchResult {
ensure_root(origin)?;

// update class total issuance
Classes::<T>::try_mutate(class_id.clone(), |class_info| -> DispatchResult {
let info = class_info.as_mut().ok_or(Error::<T>::ClassIdNotFound)?;
ensure!(
current_total_issuance == info.total_issuance,
Error::<T>::InvalidCurrentTotalIssuance
);

info.total_issuance = new_total_issuance;
Self::deposit_event(Event::<T>::ClassTotalIssuanceUpdated(class_id, new_total_issuance));

Ok(())
})
}
}

#[pallet::hooks]
Expand Down
2 changes: 2 additions & 0 deletions pallets/nft/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ pub const CLASS_ID: <Runtime as orml_nft::Config>::ClassId = 0;
pub const CLASS_ID_1: <Runtime as orml_nft::Config>::ClassId = 1;
pub const NON_EXISTING_CLASS_ID: <Runtime as orml_nft::Config>::ClassId = 1000;
pub const TOKEN_ID: <Runtime as orml_nft::Config>::TokenId = 0;
pub const TOKEN_ID_1: <Runtime as orml_nft::Config>::TokenId = 1;
pub const TOKEN_ID_2: <Runtime as orml_nft::Config>::TokenId = 2;
pub const COLLECTION_ID: u64 = 0;

impl frame_system::Config for Runtime {
Expand Down
59 changes: 59 additions & 0 deletions pallets/nft/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -618,3 +618,62 @@ fn setting_hard_limit_should_work() {
assert_eq!(last_event(), event);
})
}

#[test]
fn force_updating_total_issuance_should_work() {
ExtBuilder::default().build().execute_with(|| {
let origin = Origin::signed(ALICE);
let class_deposit = <Runtime as Config>::ClassMintingFee::get();
assert_ok!(Nft::create_group(Origin::root(), vec![1], vec![1],));
assert_ok!(Nft::create_class(
origin.clone(),
vec![1],
test_attributes(1),
COLLECTION_ID,
TokenType::Transferable,
CollectionType::Collectable,
Perbill::from_percent(0u32),
None
));
assert_ok!(Nft::force_update_total_issuance(
Origin::root(),
CLASS_ID,
TOKEN_ID,
TOKEN_ID_2
));
assert_eq!(
NftModule::<Runtime>::classes(CLASS_ID).unwrap().total_issuance,
TOKEN_ID_2
);
let event = mock::Event::Nft(crate::Event::ClassTotalIssuanceUpdated(CLASS_ID, TOKEN_ID_2));
assert_eq!(last_event(), event);
})
}

#[test]
fn force_updating_total_issuance_should_fail() {
ExtBuilder::default().build().execute_with(|| {
let origin = Origin::signed(ALICE);
let class_deposit = <Runtime as Config>::ClassMintingFee::get();
assert_ok!(Nft::create_group(Origin::root(), vec![1], vec![1],));
assert_ok!(Nft::create_class(
origin.clone(),
vec![1],
test_attributes(1),
COLLECTION_ID,
TokenType::Transferable,
CollectionType::Collectable,
Perbill::from_percent(0u32),
None
));
assert_noop!(
Nft::force_update_total_issuance(Origin::root(), NON_EXISTING_CLASS_ID, TOKEN_ID, TOKEN_ID_2),
Error::<Runtime>::ClassIdNotFound
);

assert_noop!(
Nft::force_update_total_issuance(Origin::root(), CLASS_ID, TOKEN_ID_1, TOKEN_ID_2),
Error::<Runtime>::InvalidCurrentTotalIssuance
);
})
}
92 changes: 67 additions & 25 deletions pallets/nft/src/weights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,23 @@
//! Autogenerated weights for nft
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2022-05-20, STEPS: `20`, REPEAT: 10, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2022-10-21, STEPS: `20`, REPEAT: 10, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024
// Executed Command:
// ./target/release/metaverse-node
// benchmark
// --pallet=nft
// --extrinsic=*
// --steps=20
// --repeat=10
// pallet
// --execution=wasm
// --wasm-execution=compiled
// --pallet
// nft
// --extrinsic
// *
// --steps
// 20
// --repeat
// 10
// --template=./template/weight-template.hbs
// --output
// ./pallets/nft/src/weights.rs
Expand All @@ -43,29 +48,66 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
use sp_std::marker::PhantomData;

/// Weight functions needed for nft.
pub trait WeightInfo { fn create_group() -> Weight; fn create_class() -> Weight; fn mint() -> Weight; fn transfer() -> Weight; fn transfer_batch() -> Weight; fn sign_asset() -> Weight; fn set_hard_limit() -> Weight; fn withdraw_funds_from_class_fund() -> Weight;}

pub trait WeightInfo { fn create_group() -> Weight; fn create_class() -> Weight; fn mint() -> Weight; fn transfer() -> Weight; fn transfer_batch() -> Weight; fn sign_asset() -> Weight; fn set_hard_limit() -> Weight; fn withdraw_funds_from_class_fund() -> Weight; fn force_update_total_issuance() -> Weight;}

/// Weights for nft using the for collator node and recommended hardware.
pub struct SubstrateWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { fn create_group() -> Weight {
(25_100_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn create_class() -> Weight {
(68_000_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn mint() -> Weight {
(115_300_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(10 as Weight)) } fn transfer() -> Weight {
(68_700_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn transfer_batch() -> Weight {
(86_600_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(6 as Weight)) } fn sign_asset() -> Weight {
(108_900_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn set_hard_limit() -> Weight {
(29_000_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn withdraw_funds_from_class_fund() -> Weight {
(65_500_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) }}
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
fn create_group() -> Weight {
(15_532_000 as Weight)
.saturating_add(T::DbWeight::get().reads(2 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
}
fn create_class() -> Weight {
(29_496_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(4 as Weight))
}
fn mint() -> Weight {
(49_776_000 as Weight)
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(10 as Weight))
}
fn transfer() -> Weight {
(28_845_000 as Weight)
.saturating_add(T::DbWeight::get().reads(4 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
}
fn transfer_batch() -> Weight {
(46_555_000 as Weight)
.saturating_add(T::DbWeight::get().reads(6 as Weight))
.saturating_add(T::DbWeight::get().writes(6 as Weight))
}
fn sign_asset() -> Weight {
(35_655_000 as Weight)
.saturating_add(T::DbWeight::get().reads(4 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
}
fn set_hard_limit() -> Weight {
(13_747_000 as Weight)
.saturating_add(T::DbWeight::get().reads(1 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
}
fn withdraw_funds_from_class_fund() -> Weight {
(26_662_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(2 as Weight))
}
fn force_update_total_issuance() -> Weight {
(12_784_000 as Weight)
.saturating_add(T::DbWeight::get().reads(1 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
}
}

// For backwards compatibility and tests
impl WeightInfo for () { fn create_group() -> Weight {
(25_100_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn create_class() -> Weight {
(68_000_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn mint() -> Weight {
(115_300_000 as Weight) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(10 as Weight)) } fn transfer() -> Weight {
(68_700_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn transfer_batch() -> Weight {
(86_600_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(6 as Weight)) } fn sign_asset() -> Weight {
(108_900_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn set_hard_limit() -> Weight {
(29_000_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn withdraw_funds_from_class_fund() -> Weight {
(65_500_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) }}

(15_532_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn create_class() -> Weight {
(29_496_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn mint() -> Weight {
(49_776_000 as Weight) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(10 as Weight)) } fn transfer() -> Weight {
(28_845_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn transfer_batch() -> Weight {
(46_555_000 as Weight) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().writes(6 as Weight)) } fn sign_asset() -> Weight {
(35_655_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn set_hard_limit() -> Weight {
(13_747_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn withdraw_funds_from_class_fund() -> Weight {
(26_662_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } fn force_update_total_issuance() -> Weight {
(12_784_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) }}
45 changes: 23 additions & 22 deletions runtime/continuum/src/weights/module_nft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,48 +11,49 @@ use sp_std::marker::PhantomData;
pub struct WeightInfo<T>(PhantomData<T>);

impl<T: frame_system::Config> nft::WeightInfo for WeightInfo<T> {
fn mint() -> Weight {
(1_621_000 as Weight)
// Standard Error: 5_000
.saturating_add((21_976_000 as Weight))
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
.saturating_add(T::DbWeight::get().writes((2 as Weight)))
}
fn create_group() -> Weight {
(60_000_000 as Weight)
(15_532_000 as Weight)
.saturating_add(T::DbWeight::get().reads(2 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
}
fn create_class() -> Weight {
(225_000_000 as Weight)
(29_496_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(4 as Weight))
}
fn mint() -> Weight {
(49_776_000 as Weight)
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(10 as Weight))
}
fn transfer() -> Weight {
(176_000_000 as Weight)
.saturating_add(T::DbWeight::get().reads(6 as Weight))
.saturating_add(T::DbWeight::get().writes(5 as Weight))
(28_845_000 as Weight)
.saturating_add(T::DbWeight::get().reads(4 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
}
fn transfer_batch() -> Weight {
(447_000_000 as Weight)
.saturating_add(T::DbWeight::get().reads(8 as Weight))
.saturating_add(T::DbWeight::get().writes(9 as Weight))
.saturating_add(T::DbWeight::get().writes((2 as Weight)))
(46_555_000 as Weight)
.saturating_add(T::DbWeight::get().reads(6 as Weight))
.saturating_add(T::DbWeight::get().writes(6 as Weight))
}
fn sign_asset() -> Weight {
(47_000_000 as Weight)
.saturating_add(T::DbWeight::get().reads(2 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
(35_655_000 as Weight)
.saturating_add(T::DbWeight::get().reads(4 as Weight))
.saturating_add(T::DbWeight::get().writes(3 as Weight))
}
fn set_hard_limit() -> Weight {
(29_000_000 as Weight)
(13_747_000 as Weight)
.saturating_add(T::DbWeight::get().reads(1 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
}
fn withdraw_funds_from_class_fund() -> Weight {
(65_500_000 as Weight)
(26_662_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(2 as Weight))
}
fn force_update_total_issuance() -> Weight {
(12_784_000 as Weight)
.saturating_add(T::DbWeight::get().reads(1 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
}
}
Loading

0 comments on commit fb64619

Please sign in to comment.