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

[Uniques V2] Tips #12168

Merged
merged 14 commits into from
Sep 24, 2022
2 changes: 2 additions & 0 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1472,6 +1472,7 @@ parameter_types! {
pub const KeyLimit: u32 = 32;
pub const ValueLimit: u32 = 256;
pub const ApprovalsLimit: u32 = 20;
pub const MaxTips: u32 = 10;
}

impl pallet_uniques::Config for Runtime {
Expand Down Expand Up @@ -1510,6 +1511,7 @@ impl pallet_nfts::Config for Runtime {
type KeyLimit = KeyLimit;
type ValueLimit = ValueLimit;
type ApprovalsLimit = ApprovalsLimit;
type MaxTips = MaxTips;
type WeightInfo = pallet_nfts::weights::SubstrateWeight<Runtime>;
#[cfg(feature = "runtime-benchmarks")]
type Helper = ();
Expand Down
23 changes: 23 additions & 0 deletions frame/nfts/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,5 +453,28 @@ benchmarks_instance_pallet! {
}.into());
}

pay_tips {
let n in 0 .. T::MaxTips::get() as u32;
let amount = BalanceOf::<T, I>::from(100u32);
let caller: T::AccountId = whitelisted_caller();
let collection = T::Helper::collection(0);
let item = T::Helper::item(0);
let tips: BoundedVec<_, _> = vec![
ItemTip
{ collection, item, receiver: caller.clone(), amount }; n as usize
].try_into().unwrap();
}: _(SystemOrigin::Signed(caller.clone()), tips)
verify {
if !n.is_zero() {
assert_last_event::<T, I>(Event::TipSent {
collection,
item,
sender: caller.clone(),
receiver: caller.clone(),
amount,
}.into());
}
}

impl_benchmark_test_suite!(Nfts, crate::mock::new_test_ext(), crate::mock::Test);
}
42 changes: 42 additions & 0 deletions frame/nfts/src/features/buy_sell.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// This file is part of Substrate.

// Copyright (C) 2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::*;
use frame_support::{
pallet_prelude::*,
traits::{Currency, ExistenceRequirement::KeepAlive},
};

impl<T: Config<I>, I: 'static> Pallet<T, I> {
pub fn do_pay_tips(
sender: T::AccountId,
tips: BoundedVec<ItemTipOf<T, I>, T::MaxTips>,
) -> DispatchResult {
for tip in tips {
let ItemTip { collection, item, receiver, amount } = tip;
T::Currency::transfer(&sender, &receiver, amount, KeepAlive)?;
Self::deposit_event(Event::TipSent {
collection,
item,
sender: sender.clone(),
receiver,
amount,
});
}
Ok(())
}
}
18 changes: 18 additions & 0 deletions frame/nfts/src/features/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// This file is part of Substrate.

// Copyright (C) 2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

pub mod buy_sell;
30 changes: 30 additions & 0 deletions frame/nfts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub mod mock;
#[cfg(test)]
mod tests;

mod features;
mod functions;
mod impl_nonfungibles;
mod types;
Expand Down Expand Up @@ -177,6 +178,10 @@ pub mod pallet {
#[pallet::constant]
type ApprovalsLimit: Get<u32>;

/// The max number of tips a user could send.
#[pallet::constant]
type MaxTips: Get<u32>;

#[cfg(feature = "runtime-benchmarks")]
/// A set of helper functions for benchmarking.
type Helper: BenchmarkHelper<Self::CollectionId, Self::ItemId>;
Expand Down Expand Up @@ -420,6 +425,14 @@ pub mod pallet {
seller: T::AccountId,
buyer: T::AccountId,
},
/// A tip was sent.
TipSent {
collection: T::CollectionId,
item: T::ItemId,
sender: T::AccountId,
receiver: T::AccountId,
amount: DepositBalanceOf<T, I>,
},
}

#[pallet::error]
Expand Down Expand Up @@ -1608,5 +1621,22 @@ pub mod pallet {
let origin = ensure_signed(origin)?;
Self::do_buy_item(collection, item, origin, bid_price)
}

/// Allows to pay the tips.
///
/// Origin must be Signed.
///
/// - `tips`: Tips array.
///
/// Emits `TipSent` on every tip transfer.
#[pallet::weight(T::WeightInfo::pay_tips(tips.len() as u32))]
#[transactional]
pub fn pay_tips(
origin: OriginFor<T>,
tips: BoundedVec<ItemTipOf<T, I>, T::MaxTips>,
) -> DispatchResult {
let origin = ensure_signed(origin)?;
Self::do_pay_tips(origin, tips)
}
}
}
1 change: 1 addition & 0 deletions frame/nfts/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ impl Config for Test {
type KeyLimit = ConstU32<50>;
type ValueLimit = ConstU32<50>;
type ApprovalsLimit = ConstU32<10>;
type MaxTips = ConstU32<10>;
type WeightInfo = ();
#[cfg(feature = "runtime-benchmarks")]
type Helper = ();
Expand Down
47 changes: 46 additions & 1 deletion frame/nfts/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1001,7 +1001,7 @@ fn buy_item_should_work() {
);

// can buy when I'm a whitelisted buyer
assert_ok!(Nfts::buy_item(RuntimeOrigin::signed(user_3), collection_id, item_2, price_2,));
assert_ok!(Nfts::buy_item(RuntimeOrigin::signed(user_3), collection_id, item_2, price_2));

assert!(events().contains(&Event::<Test>::ItemBought {
collection: collection_id,
Expand Down Expand Up @@ -1060,3 +1060,48 @@ fn buy_item_should_work() {
}
});
}

#[test]
fn pay_tips_should_work() {
new_test_ext().execute_with(|| {
let user_1 = 1;
let user_2 = 2;
let user_3 = 3;
let collection_id = 0;
let item_id = 1;
let tip = 2;
let initial_balance = 100;

Balances::make_free_balance_be(&user_1, initial_balance);
Balances::make_free_balance_be(&user_2, initial_balance);
Balances::make_free_balance_be(&user_3, initial_balance);

assert_ok!(Nfts::pay_tips(
RuntimeOrigin::signed(user_1),
bvec![
ItemTip { collection: collection_id, item: item_id, receiver: user_2, amount: tip },
ItemTip { collection: collection_id, item: item_id, receiver: user_3, amount: tip },
]
));

assert_eq!(Balances::total_balance(&user_1), initial_balance - tip * 2);
assert_eq!(Balances::total_balance(&user_2), initial_balance + tip);
assert_eq!(Balances::total_balance(&user_3), initial_balance + tip);

let events = events();
assert!(events.contains(&Event::<Test>::TipSent {
collection: collection_id,
item: item_id,
sender: user_1,
receiver: user_2,
amount: tip,
}));
assert!(events.contains(&Event::<Test>::TipSent {
collection: collection_id,
item: item_id,
sender: user_1,
receiver: user_3,
amount: tip,
}));
});
}
21 changes: 20 additions & 1 deletion frame/nfts/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,15 @@ pub(super) type CollectionDetailsFor<T, I> =
CollectionDetails<<T as SystemConfig>::AccountId, DepositBalanceOf<T, I>>;
pub(super) type ItemDetailsFor<T, I> =
ItemDetails<<T as SystemConfig>::AccountId, DepositBalanceOf<T, I>, ApprovalsOf<T, I>>;
pub(super) type ItemPrice<T, I = ()> =
pub(super) type BalanceOf<T, I = ()> =
<<T as Config<I>>::Currency as Currency<<T as SystemConfig>::AccountId>>::Balance;
pub(super) type ItemPrice<T, I = ()> = BalanceOf<T, I>;
pub(super) type ItemTipOf<T, I = ()> = ItemTip<
<T as Config<I>>::CollectionId,
<T as Config<I>>::ItemId,
<T as SystemConfig>::AccountId,
BalanceOf<T, I>,
>;

#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub struct CollectionDetails<AccountId, DepositBalance> {
Expand Down Expand Up @@ -127,3 +134,15 @@ pub struct ItemMetadata<DepositBalance, StringLimit: Get<u32>> {
/// Whether the item metadata may be changed by a non Force origin.
pub(super) is_frozen: bool,
}

#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub struct ItemTip<CollectionId, ItemId, AccountId, Amount> {
/// A collection of the item.
pub(super) collection: CollectionId,
/// An item of which the tip is send for.
pub(super) item: ItemId,
/// A sender of the tip.
pub(super) receiver: AccountId,
/// An amount the sender is willing to tip.
pub(super) amount: Amount,
}
Loading