Skip to content
Open
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
34 changes: 19 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, T::Currency::minimum_balance() * 10u32.into());
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,12 @@ 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::reducible_balance(
&winner,
Preservation::Expendable,
Fortitude::Polite
)
.is_zero());

Ok(())
}
Expand All @@ -179,18 +181,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 +208,12 @@ 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::reducible_balance(
&winner,
Preservation::Expendable,
Fortitude::Polite
)
.is_zero());

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

type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
<<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::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 @@ -141,7 +145,7 @@ pub mod pallet {
+ From<frame_system::Call<Self>>;

/// The currency trait.
type Currency: ReservableCurrency<Self::AccountId>;
type Currency: Inspect<Self::AccountId> + Mutate<Self::AccountId>;

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

Expand Down Expand Up @@ -371,8 +375,7 @@ pub mod pallet {
// Make sure pot exists.
let lottery_account = Self::account_id();
if T::Currency::total_balance(&lottery_account).is_zero() {
let _ =
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 @@ -410,7 +413,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 @@ -467,7 +470,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
43 changes: 36 additions & 7 deletions substrate/frame/lottery/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,20 @@ fn basic_end_to_end_works() {
assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), price, length, delay, true));
assert!(crate::Lottery::<Test>::get().is_some());

assert_eq!(Balances::free_balance(&1), 100);
assert_eq!(
Balances::reducible_balance(&1, Preservation::Expendable, Fortitude::Polite),
100
);
let call = Box::new(RuntimeCall::Balances(BalancesCall::transfer_allow_death {
dest: 2,
value: 20,
}));
assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(1), call.clone()));
// 20 from the transfer, 10 from buying a ticket
assert_eq!(Balances::free_balance(&1), 100 - 20 - 10);
assert_eq!(
Balances::reducible_balance(&1, Preservation::Expendable, Fortitude::Polite),
100 - 20 - 10
);
assert_eq!(Participants::<Test>::get(&1).1.len(), 1);
assert_eq!(TicketsCount::<Test>::get(), 1);
// 1 owns the 0 ticket
Expand All @@ -81,7 +87,10 @@ fn basic_end_to_end_works() {
// Go to payout
System::run_to_block::<AllPalletsWithSystem>(25);
// User 1 wins
assert_eq!(Balances::free_balance(&1), 70 + 40);
assert_eq!(
Balances::reducible_balance(&1, Preservation::Expendable, Fortitude::Polite),
70 + 40
);
// Lottery is reset and restarted
assert_eq!(TicketsCount::<Test>::get(), 0);
assert_eq!(LotteryIndex::<Test>::get(), 2);
Expand Down Expand Up @@ -210,7 +219,10 @@ fn buy_ticket_works_as_simple_passthrough() {
}));
// This is just a basic transfer then
assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(1), call.clone()));
assert_eq!(Balances::free_balance(&1), 100 - 20);
assert_eq!(
Balances::reducible_balance(&1, Preservation::Expendable, Fortitude::Polite),
100 - 20
);
assert_eq!(TicketsCount::<Test>::get(), 0);

// Lottery is set up, but too expensive to enter, so `do_buy_ticket` fails.
Expand All @@ -223,7 +235,10 @@ fn buy_ticket_works_as_simple_passthrough() {
// Ticket price of 60 would kill the user's account
assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), 60, 10, 5, false));
assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(1), call.clone()));
assert_eq!(Balances::free_balance(&1), 100 - 20 - 20);
assert_eq!(
Balances::reducible_balance(&1, Preservation::Expendable, Fortitude::Polite),
100 - 20 - 20
);
assert_eq!(TicketsCount::<Test>::get(), 0);

// If call would fail, the whole thing still fails the same
Expand Down Expand Up @@ -419,9 +434,23 @@ fn start_lottery_will_create_account() {
let length = 20;
let delay = 5;

assert_eq!(Balances::total_balance(&Lottery::account_id()), 0);
assert_eq!(
Balances::reducible_balance(
&Lottery::account_id(),
Preservation::Expendable,
Fortitude::Polite
),
0
);
assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), price, length, delay, false));
assert_eq!(Balances::total_balance(&Lottery::account_id()), 1);
assert_eq!(
Balances::reducible_balance(
&Lottery::account_id(),
Preservation::Expendable,
Fortitude::Polite
),
1
);
});
}

Expand Down
Loading