Skip to content

Pallet Lottery migrate to Fungibles traits #1810

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 19 additions & 0 deletions prdoc/pr_1810.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Schema: Parity PR Documentation Schema (prdoc)
# See doc at https://github.com/paritytech/prdoc

title: Pallet Lottery migrate to Fungibles traits

doc:
- audience: Core Dev
description: |
Part of <https://github.com/paritytech/polkadot-sdk/issues/226>.

This pallet used `type Currency: ReservableCurrency<Self::AccountId>;` but in the logic, no reservation/hold logic existed, or exists in now. Thus we now only use a simple `type Currency: fungible::Balanced<Self::AccountId> + fungible::Mutate<Self::AccountId>;` to manage the lottery's pot.

We know of no downstream runtimes that are somehow able to place reservations/holds/locks/... on the `T::PalletId::get().into_account_truncating()` account for the pot, so no migrations are needed at all in this PR. There are no braking changes whatsoever here.

migrations:
db: []
runtime: []
crates: []
host_functions: []
24 changes: 9 additions & 15 deletions substrate/frame/lottery/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ mod benchmarks {
#[benchmark]
fn buy_ticket() -> Result<(), BenchmarkError> {
let caller = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
T::Currency::set_balance(&caller, BalanceOf::<T>::max_value());
setup_lottery::<T>(false)?;
// force user to have a long vec of calls participating
let set_code_index: CallIndex = Lottery::<T>::call_to_index(
Expand Down Expand Up @@ -140,18 +140,15 @@ mod benchmarks {
setup_lottery::<T>(false)?;
let winner = account("winner", 0, 0);
// User needs more than min balance to get ticket
T::Currency::make_free_balance_be(&winner, T::Currency::minimum_balance() * 10u32.into());
T::Currency::set_balance(&winner, T::Currency::minimum_balance() * 10u32.into());
// Make sure lottery account has at least min balance too
let lottery_account = Lottery::<T>::account_id();
T::Currency::make_free_balance_be(
&lottery_account,
T::Currency::minimum_balance() * 10u32.into(),
);
T::Currency::set_balance(&lottery_account, T::Currency::minimum_balance() * 10u32.into());
// Buy a ticket
let call = frame_system::Call::<T>::remark { remark: vec![] };
Lottery::<T>::buy_ticket(RawOrigin::Signed(winner.clone()).into(), Box::new(call.into()))?;
// Kill user account for worst case
T::Currency::make_free_balance_be(&winner, 0u32.into());
T::Currency::set_balance(&winner, 0u32.into());
// Assert that lotto is set up for winner
assert_eq!(TicketsCount::<T>::get(), 1);
assert!(!Lottery::<T>::pot().1.is_zero());
Expand All @@ -169,7 +166,7 @@ mod benchmarks {
assert!(crate::Lottery::<T>::get().is_none());
assert_eq!(TicketsCount::<T>::get(), 0);
assert_eq!(Lottery::<T>::pot().1, 0u32.into());
assert!(!T::Currency::free_balance(&winner).is_zero());
assert!(!T::Currency::total_balance(&winner).is_zero());

Ok(())
}
Expand All @@ -179,18 +176,15 @@ mod benchmarks {
setup_lottery::<T>(true)?;
let winner = account("winner", 0, 0);
// User needs more than min balance to get ticket
T::Currency::make_free_balance_be(&winner, T::Currency::minimum_balance() * 10u32.into());
T::Currency::set_balance(&winner, T::Currency::minimum_balance() * 10u32.into());
// Make sure lottery account has at least min balance too
let lottery_account = Lottery::<T>::account_id();
T::Currency::make_free_balance_be(
&lottery_account,
T::Currency::minimum_balance() * 10u32.into(),
);
T::Currency::set_balance(&lottery_account, T::Currency::minimum_balance() * 10u32.into());
// Buy a ticket
let call = frame_system::Call::<T>::remark { remark: vec![] };
Lottery::<T>::buy_ticket(RawOrigin::Signed(winner.clone()).into(), Box::new(call.into()))?;
// Kill user account for worst case
T::Currency::make_free_balance_be(&winner, 0u32.into());
T::Currency::set_balance(&winner, 0u32.into());
// Assert that lotto is set up for winner
assert_eq!(TicketsCount::<T>::get(), 1);
assert!(!Lottery::<T>::pot().1.is_zero());
Expand All @@ -209,7 +203,7 @@ mod benchmarks {
assert_eq!(LotteryIndex::<T>::get(), 2);
assert_eq!(TicketsCount::<T>::get(), 0);
assert_eq!(Lottery::<T>::pot().1, 0u32.into());
assert!(!T::Currency::free_balance(&winner).is_zero());
assert!(!T::Currency::total_balance(&winner).is_zero());

Ok(())
}
Expand Down
27 changes: 18 additions & 9 deletions substrate/frame/lottery/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ use frame_support::{
ensure,
pallet_prelude::MaxEncodedLen,
storage::bounded_vec::BoundedVec,
traits::{Currency, ExistenceRequirement::KeepAlive, Get, Randomness, ReservableCurrency},
traits::{
fungible::{self, Inspect as _, Mutate as _},
tokens::{Fortitude, Preservation},
Get, Randomness,
},
PalletId,
};
pub use pallet::*;
Expand All @@ -71,8 +75,8 @@ use sp_runtime::{
use sp_std::prelude::*;
pub use weights::WeightInfo;

type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
type BalanceOf<T> = <<T as Config>::Currency as fungible::Inspect<AccountIdOf<T>>>::Balance;

// Any runtime call can be encoded into two bytes which represent the pallet and call index.
// We use this to uniquely match someone's incoming call with the calls configured for the lottery.
Expand Down Expand Up @@ -138,8 +142,8 @@ pub mod pallet {
+ GetDispatchInfo
+ From<frame_system::Call<Self>>;

/// The currency trait.
type Currency: ReservableCurrency<Self::AccountId>;
/// The fungible currency used for the lottery pot.
type Currency: fungible::Balanced<Self::AccountId> + fungible::Mutate<Self::AccountId>;

/// Something that provides randomness in the runtime.
type Randomness: Randomness<Self::Hash, BlockNumberFor<Self>>;
Expand Down Expand Up @@ -253,7 +257,7 @@ pub mod pallet {
&Self::account_id(),
&winner,
lottery_balance,
KeepAlive,
Preservation::Preserve,
);
debug_assert!(res.is_ok());

Expand Down Expand Up @@ -368,7 +372,7 @@ pub mod pallet {
// Make sure pot exists.
let lottery_account = Self::account_id();
if T::Currency::total_balance(&lottery_account).is_zero() {
T::Currency::deposit_creating(&lottery_account, T::Currency::minimum_balance());
let _ = T::Currency::mint_into(&lottery_account, T::Currency::minimum_balance());
}
Self::deposit_event(Event::<T>::LotteryStarted);
Ok(())
Expand Down Expand Up @@ -406,7 +410,7 @@ impl<T: Config> Pallet<T> {
fn pot() -> (T::AccountId, BalanceOf<T>) {
let account_id = Self::account_id();
let balance =
T::Currency::free_balance(&account_id).saturating_sub(T::Currency::minimum_balance());
T::Currency::reducible_balance(&account_id, Preservation::Preserve, Fortitude::Polite);

(account_id, balance)
}
Expand Down Expand Up @@ -463,7 +467,12 @@ impl<T: Config> Pallet<T> {
}
participating_calls.try_push(call_index).map_err(|_| Error::<T>::TooManyCalls)?;
// Check user has enough funds and send it to the Lottery account.
T::Currency::transfer(caller, &Self::account_id(), config.price, KeepAlive)?;
T::Currency::transfer(
caller,
&Self::account_id(),
config.price,
Preservation::Preserve,
)?;
// Create a new ticket.
TicketsCount::<T>::put(new_ticket_count);
Tickets::<T>::insert(ticket_count, caller.clone());
Expand Down