mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-18 03:41:02 +00:00
Migrate pallet-lottery to pallet attribute macro (#8762)
* Migrate pallet-lottery to pallet attribute macro. * Fix metadata inconsistency. * fix * Use DispatchResult in call returns.
This commit is contained in:
@@ -1019,7 +1019,7 @@ impl pallet_mmr::Config for Runtime {
|
|||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
pub const LotteryPalletId: PalletId = PalletId(*b"py/lotto");
|
pub const LotteryPalletId: PalletId = PalletId(*b"py/lotto");
|
||||||
pub const MaxCalls: usize = 10;
|
pub const MaxCalls: u32 = 10;
|
||||||
pub const MaxGenerateRandom: u32 = 10;
|
pub const MaxGenerateRandom: u32 = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,11 +22,11 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use frame_system::RawOrigin;
|
use frame_system::RawOrigin;
|
||||||
use frame_support::traits::{OnInitialize, UnfilteredDispatchable};
|
use frame_support::traits::{EnsureOrigin, OnInitialize, UnfilteredDispatchable};
|
||||||
use frame_benchmarking::{benchmarks, account, whitelisted_caller, impl_benchmark_test_suite};
|
use frame_benchmarking::{benchmarks, account, whitelisted_caller, impl_benchmark_test_suite};
|
||||||
use sp_runtime::traits::{Bounded, Zero};
|
use sp_runtime::traits::{Bounded, Zero};
|
||||||
|
|
||||||
use crate::Module as Lottery;
|
use crate::Pallet as Lottery;
|
||||||
|
|
||||||
// Set up and start a lottery
|
// Set up and start a lottery
|
||||||
fn setup_lottery<T: Config>(repeat: bool) -> Result<(), &'static str> {
|
fn setup_lottery<T: Config>(repeat: bool) -> Result<(), &'static str> {
|
||||||
@@ -36,7 +36,7 @@ fn setup_lottery<T: Config>(repeat: bool) -> Result<(), &'static str> {
|
|||||||
// Calls will be maximum length...
|
// Calls will be maximum length...
|
||||||
let mut calls = vec![
|
let mut calls = vec![
|
||||||
frame_system::Call::<T>::set_code(vec![]).into();
|
frame_system::Call::<T>::set_code(vec![]).into();
|
||||||
T::MaxCalls::get().saturating_sub(1)
|
T::MaxCalls::get().saturating_sub(1) as usize
|
||||||
];
|
];
|
||||||
// Last call will be the match for worst case scenario.
|
// Last call will be the match for worst case scenario.
|
||||||
calls.push(frame_system::Call::<T>::remark(vec![]).into());
|
calls.push(frame_system::Call::<T>::remark(vec![]).into());
|
||||||
@@ -56,10 +56,10 @@ benchmarks! {
|
|||||||
&frame_system::Call::<T>::set_code(vec![]).into()
|
&frame_system::Call::<T>::set_code(vec![]).into()
|
||||||
)?;
|
)?;
|
||||||
let already_called: (u32, Vec<CallIndex>) = (
|
let already_called: (u32, Vec<CallIndex>) = (
|
||||||
LotteryIndex::get(),
|
LotteryIndex::<T>::get(),
|
||||||
vec![
|
vec![
|
||||||
set_code_index;
|
set_code_index;
|
||||||
T::MaxCalls::get().saturating_sub(1)
|
T::MaxCalls::get().saturating_sub(1) as usize
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
Participants::<T>::insert(&caller, already_called);
|
Participants::<T>::insert(&caller, already_called);
|
||||||
@@ -67,7 +67,7 @@ benchmarks! {
|
|||||||
let call = frame_system::Call::<T>::remark(vec![]);
|
let call = frame_system::Call::<T>::remark(vec![]);
|
||||||
}: _(RawOrigin::Signed(caller), Box::new(call.into()))
|
}: _(RawOrigin::Signed(caller), Box::new(call.into()))
|
||||||
verify {
|
verify {
|
||||||
assert_eq!(TicketsCount::get(), 1);
|
assert_eq!(TicketsCount::<T>::get(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_calls {
|
set_calls {
|
||||||
@@ -76,11 +76,11 @@ benchmarks! {
|
|||||||
|
|
||||||
let call = Call::<T>::set_calls(calls);
|
let call = Call::<T>::set_calls(calls);
|
||||||
let origin = T::ManagerOrigin::successful_origin();
|
let origin = T::ManagerOrigin::successful_origin();
|
||||||
assert!(CallIndices::get().is_empty());
|
assert!(CallIndices::<T>::get().is_empty());
|
||||||
}: { call.dispatch_bypass_filter(origin)? }
|
}: { call.dispatch_bypass_filter(origin)? }
|
||||||
verify {
|
verify {
|
||||||
if !n.is_zero() {
|
if !n.is_zero() {
|
||||||
assert!(!CallIndices::get().is_empty());
|
assert!(!CallIndices::<T>::get().is_empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ benchmarks! {
|
|||||||
// Kill user account for worst case
|
// Kill user account for worst case
|
||||||
T::Currency::make_free_balance_be(&winner, 0u32.into());
|
T::Currency::make_free_balance_be(&winner, 0u32.into());
|
||||||
// Assert that lotto is set up for winner
|
// Assert that lotto is set up for winner
|
||||||
assert_eq!(TicketsCount::get(), 1);
|
assert_eq!(TicketsCount::<T>::get(), 1);
|
||||||
assert!(!Lottery::<T>::pot().1.is_zero());
|
assert!(!Lottery::<T>::pot().1.is_zero());
|
||||||
}: {
|
}: {
|
||||||
// Generate `MaxGenerateRandom` numbers for worst case scenario
|
// Generate `MaxGenerateRandom` numbers for worst case scenario
|
||||||
@@ -132,7 +132,7 @@ benchmarks! {
|
|||||||
}
|
}
|
||||||
verify {
|
verify {
|
||||||
assert!(crate::Lottery::<T>::get().is_none());
|
assert!(crate::Lottery::<T>::get().is_none());
|
||||||
assert_eq!(TicketsCount::get(), 0);
|
assert_eq!(TicketsCount::<T>::get(), 0);
|
||||||
assert_eq!(Lottery::<T>::pot().1, 0u32.into());
|
assert_eq!(Lottery::<T>::pot().1, 0u32.into());
|
||||||
assert!(!T::Currency::free_balance(&winner).is_zero())
|
assert!(!T::Currency::free_balance(&winner).is_zero())
|
||||||
}
|
}
|
||||||
@@ -151,7 +151,7 @@ benchmarks! {
|
|||||||
// Kill user account for worst case
|
// Kill user account for worst case
|
||||||
T::Currency::make_free_balance_be(&winner, 0u32.into());
|
T::Currency::make_free_balance_be(&winner, 0u32.into());
|
||||||
// Assert that lotto is set up for winner
|
// Assert that lotto is set up for winner
|
||||||
assert_eq!(TicketsCount::get(), 1);
|
assert_eq!(TicketsCount::<T>::get(), 1);
|
||||||
assert!(!Lottery::<T>::pot().1.is_zero());
|
assert!(!Lottery::<T>::pot().1.is_zero());
|
||||||
}: {
|
}: {
|
||||||
// Generate `MaxGenerateRandom` numbers for worst case scenario
|
// Generate `MaxGenerateRandom` numbers for worst case scenario
|
||||||
@@ -163,8 +163,8 @@ benchmarks! {
|
|||||||
}
|
}
|
||||||
verify {
|
verify {
|
||||||
assert!(crate::Lottery::<T>::get().is_some());
|
assert!(crate::Lottery::<T>::get().is_some());
|
||||||
assert_eq!(LotteryIndex::get(), 2);
|
assert_eq!(LotteryIndex::<T>::get(), 2);
|
||||||
assert_eq!(TicketsCount::get(), 0);
|
assert_eq!(TicketsCount::<T>::get(), 0);
|
||||||
assert_eq!(Lottery::<T>::pot().1, 0u32.into());
|
assert_eq!(Lottery::<T>::pot().1, 0u32.into());
|
||||||
assert!(!T::Currency::free_balance(&winner).is_zero())
|
assert!(!T::Currency::free_balance(&winner).is_zero())
|
||||||
}
|
}
|
||||||
|
|||||||
+206
-184
@@ -60,58 +60,18 @@ use sp_runtime::{
|
|||||||
traits::{AccountIdConversion, Saturating, Zero},
|
traits::{AccountIdConversion, Saturating, Zero},
|
||||||
};
|
};
|
||||||
use frame_support::{
|
use frame_support::{
|
||||||
Parameter, decl_module, decl_error, decl_event, decl_storage, ensure, RuntimeDebug,
|
ensure, PalletId, RuntimeDebug,
|
||||||
dispatch::{Dispatchable, DispatchResult, GetDispatchInfo},
|
dispatch::{Dispatchable, DispatchResult, GetDispatchInfo},
|
||||||
traits::{
|
traits::{
|
||||||
Currency, ReservableCurrency, Get, EnsureOrigin, ExistenceRequirement::KeepAlive, Randomness,
|
Currency, ReservableCurrency, Get, ExistenceRequirement::KeepAlive, Randomness,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use frame_support::{weights::Weight, PalletId};
|
|
||||||
use frame_system::ensure_signed;
|
|
||||||
use codec::{Encode, Decode};
|
use codec::{Encode, Decode};
|
||||||
pub use weights::WeightInfo;
|
pub use weights::WeightInfo;
|
||||||
|
pub use pallet::*;
|
||||||
|
|
||||||
type BalanceOf<T> = <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
|
type BalanceOf<T> = <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
|
||||||
|
|
||||||
/// The module's config trait.
|
|
||||||
pub trait Config: frame_system::Config {
|
|
||||||
/// The Lottery's module id
|
|
||||||
type PalletId: Get<PalletId>;
|
|
||||||
|
|
||||||
/// A dispatchable call.
|
|
||||||
type Call: Parameter + Dispatchable<Origin=Self::Origin> + GetDispatchInfo + From<frame_system::Call<Self>>;
|
|
||||||
|
|
||||||
/// The currency trait.
|
|
||||||
type Currency: ReservableCurrency<Self::AccountId>;
|
|
||||||
|
|
||||||
/// Something that provides randomness in the runtime.
|
|
||||||
type Randomness: Randomness<Self::Hash, Self::BlockNumber>;
|
|
||||||
|
|
||||||
/// The overarching event type.
|
|
||||||
type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;
|
|
||||||
|
|
||||||
/// The manager origin.
|
|
||||||
type ManagerOrigin: EnsureOrigin<Self::Origin>;
|
|
||||||
|
|
||||||
/// The max number of calls available in a single lottery.
|
|
||||||
type MaxCalls: Get<usize>;
|
|
||||||
|
|
||||||
/// Used to determine if a call would be valid for purchasing a ticket.
|
|
||||||
///
|
|
||||||
/// Be conscious of the implementation used here. We assume at worst that
|
|
||||||
/// a vector of `MaxCalls` indices are queried for any call validation.
|
|
||||||
/// You may need to provide a custom benchmark if this assumption is broken.
|
|
||||||
type ValidateCall: ValidateCall<Self>;
|
|
||||||
|
|
||||||
/// Number of time we should try to generate a random number that has no modulo bias.
|
|
||||||
/// The larger this number, the more potential computation is used for picking the winner,
|
|
||||||
/// but also the more likely that the chosen winner is done fairly.
|
|
||||||
type MaxGenerateRandom: Get<u32>;
|
|
||||||
|
|
||||||
/// Weight information for extrinsics in this pallet.
|
|
||||||
type WeightInfo: WeightInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Any runtime call can be encoded into two bytes which represent the pallet and call index.
|
// 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.
|
// We use this to uniquely match someone's incoming call with the calls configured for the lottery.
|
||||||
type CallIndex = (u8, u8);
|
type CallIndex = (u8, u8);
|
||||||
@@ -139,9 +99,9 @@ impl<T: Config> ValidateCall<T> for () {
|
|||||||
fn validate_call(_: &<T as Config>::Call) -> bool { false }
|
fn validate_call(_: &<T as Config>::Call) -> bool { false }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Config> ValidateCall<T> for Module<T> {
|
impl<T: Config> ValidateCall<T> for Pallet<T> {
|
||||||
fn validate_call(call: &<T as Config>::Call) -> bool {
|
fn validate_call(call: &<T as Config>::Call) -> bool {
|
||||||
let valid_calls = CallIndices::get();
|
let valid_calls = CallIndices::<T>::get();
|
||||||
let call_index = match Self::call_to_index(&call) {
|
let call_index = match Self::call_to_index(&call) {
|
||||||
Ok(call_index) => call_index,
|
Ok(call_index) => call_index,
|
||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
@@ -150,44 +110,74 @@ impl<T: Config> ValidateCall<T> for Module<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
decl_storage! {
|
#[frame_support::pallet]
|
||||||
trait Store for Module<T: Config> as Lottery {
|
pub mod pallet {
|
||||||
LotteryIndex: u32;
|
use frame_support::{Parameter, pallet_prelude::*, traits::EnsureOrigin, weights::Weight};
|
||||||
/// The configuration for the current lottery.
|
use frame_system::{ensure_signed, pallet_prelude::*};
|
||||||
Lottery: Option<LotteryConfig<T::BlockNumber, BalanceOf<T>>>;
|
use super::*;
|
||||||
/// Users who have purchased a ticket. (Lottery Index, Tickets Purchased)
|
|
||||||
Participants: map hasher(twox_64_concat) T::AccountId => (u32, Vec<CallIndex>);
|
|
||||||
/// Total number of tickets sold.
|
|
||||||
TicketsCount: u32;
|
|
||||||
/// Each ticket's owner.
|
|
||||||
///
|
|
||||||
/// May have residual storage from previous lotteries. Use `TicketsCount` to see which ones
|
|
||||||
/// are actually valid ticket mappings.
|
|
||||||
Tickets: map hasher(twox_64_concat) u32 => Option<T::AccountId>;
|
|
||||||
/// The calls stored in this pallet to be used in an active lottery if configured
|
|
||||||
/// by `Config::ValidateCall`.
|
|
||||||
CallIndices: Vec<CallIndex>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
decl_event!(
|
#[pallet::pallet]
|
||||||
pub enum Event<T> where
|
#[pallet::generate_store(pub(super) trait Store)]
|
||||||
<T as frame_system::Config>::AccountId,
|
pub struct Pallet<T>(_);
|
||||||
Balance = BalanceOf<T>,
|
|
||||||
{
|
/// The pallet's config trait.
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {
|
||||||
|
/// The Lottery's pallet id
|
||||||
|
#[pallet::constant]
|
||||||
|
type PalletId: Get<PalletId>;
|
||||||
|
|
||||||
|
/// A dispatchable call.
|
||||||
|
type Call: Parameter + Dispatchable<Origin=Self::Origin> + GetDispatchInfo + From<frame_system::Call<Self>>;
|
||||||
|
|
||||||
|
/// The currency trait.
|
||||||
|
type Currency: ReservableCurrency<Self::AccountId>;
|
||||||
|
|
||||||
|
/// Something that provides randomness in the runtime.
|
||||||
|
type Randomness: Randomness<Self::Hash, Self::BlockNumber>;
|
||||||
|
|
||||||
|
/// The overarching event type.
|
||||||
|
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
|
||||||
|
|
||||||
|
/// The manager origin.
|
||||||
|
type ManagerOrigin: EnsureOrigin<Self::Origin>;
|
||||||
|
|
||||||
|
/// The max number of calls available in a single lottery.
|
||||||
|
#[pallet::constant]
|
||||||
|
type MaxCalls: Get<u32>;
|
||||||
|
|
||||||
|
/// Used to determine if a call would be valid for purchasing a ticket.
|
||||||
|
///
|
||||||
|
/// Be conscious of the implementation used here. We assume at worst that
|
||||||
|
/// a vector of `MaxCalls` indices are queried for any call validation.
|
||||||
|
/// You may need to provide a custom benchmark if this assumption is broken.
|
||||||
|
type ValidateCall: ValidateCall<Self>;
|
||||||
|
|
||||||
|
/// Number of time we should try to generate a random number that has no modulo bias.
|
||||||
|
/// The larger this number, the more potential computation is used for picking the winner,
|
||||||
|
/// but also the more likely that the chosen winner is done fairly.
|
||||||
|
type MaxGenerateRandom: Get<u32>;
|
||||||
|
|
||||||
|
/// Weight information for extrinsics in this pallet.
|
||||||
|
type WeightInfo: WeightInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::event]
|
||||||
|
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||||
|
#[pallet::metadata(T::AccountId = "AccountId", BalanceOf<T> = "Balance")]
|
||||||
|
pub enum Event<T: Config> {
|
||||||
/// A lottery has been started!
|
/// A lottery has been started!
|
||||||
LotteryStarted,
|
LotteryStarted,
|
||||||
/// A new set of calls have been set!
|
/// A new set of calls have been set!
|
||||||
CallsUpdated,
|
CallsUpdated,
|
||||||
/// A winner has been chosen!
|
/// A winner has been chosen!
|
||||||
Winner(AccountId, Balance),
|
Winner(T::AccountId, BalanceOf<T>),
|
||||||
/// A ticket has been bought!
|
/// A ticket has been bought!
|
||||||
TicketBought(AccountId, CallIndex),
|
TicketBought(T::AccountId, CallIndex),
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
|
||||||
decl_error! {
|
#[pallet::error]
|
||||||
pub enum Error for Module<T: Config> {
|
pub enum Error<T> {
|
||||||
/// A lottery has not been configured.
|
/// A lottery has not been configured.
|
||||||
NotConfigured,
|
NotConfigured,
|
||||||
/// A lottery is already in progress.
|
/// A lottery is already in progress.
|
||||||
@@ -203,114 +193,41 @@ decl_error! {
|
|||||||
/// Failed to encode calls
|
/// Failed to encode calls
|
||||||
EncodingFailed,
|
EncodingFailed,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
decl_module! {
|
#[pallet::storage]
|
||||||
pub struct Module<T: Config> for enum Call where origin: T::Origin, system = frame_system {
|
pub(crate) type LotteryIndex<T> = StorageValue<_, u32, ValueQuery>;
|
||||||
type Error = Error<T>;
|
|
||||||
|
|
||||||
const PalletId: PalletId = T::PalletId::get();
|
/// The configuration for the current lottery.
|
||||||
const MaxCalls: u32 = T::MaxCalls::get() as u32;
|
#[pallet::storage]
|
||||||
|
pub(crate) type Lottery<T: Config> = StorageValue<_, LotteryConfig<T::BlockNumber, BalanceOf<T>>>;
|
||||||
|
|
||||||
fn deposit_event() = default;
|
/// Users who have purchased a ticket. (Lottery Index, Tickets Purchased)
|
||||||
|
#[pallet::storage]
|
||||||
|
pub(crate) type Participants<T: Config> = StorageMap<
|
||||||
|
_,
|
||||||
|
Twox64Concat, T::AccountId,
|
||||||
|
(u32, Vec<CallIndex>),
|
||||||
|
ValueQuery,
|
||||||
|
>;
|
||||||
|
|
||||||
/// Buy a ticket to enter the lottery.
|
/// Total number of tickets sold.
|
||||||
///
|
#[pallet::storage]
|
||||||
/// This extrinsic acts as a passthrough function for `call`. In all
|
pub(crate) type TicketsCount<T> = StorageValue<_, u32, ValueQuery>;
|
||||||
/// situations where `call` alone would succeed, this extrinsic should
|
|
||||||
/// succeed.
|
|
||||||
///
|
|
||||||
/// If `call` is successful, then we will attempt to purchase a ticket,
|
|
||||||
/// which may fail silently. To detect success of a ticket purchase, you
|
|
||||||
/// should listen for the `TicketBought` event.
|
|
||||||
///
|
|
||||||
/// This extrinsic must be called by a signed origin.
|
|
||||||
#[weight =
|
|
||||||
T::WeightInfo::buy_ticket()
|
|
||||||
.saturating_add(call.get_dispatch_info().weight)
|
|
||||||
]
|
|
||||||
fn buy_ticket(origin, call: Box<<T as Config>::Call>) {
|
|
||||||
let caller = ensure_signed(origin.clone())?;
|
|
||||||
call.clone().dispatch(origin).map_err(|e| e.error)?;
|
|
||||||
|
|
||||||
let _ = Self::do_buy_ticket(&caller, &call);
|
/// Each ticket's owner.
|
||||||
}
|
///
|
||||||
|
/// May have residual storage from previous lotteries. Use `TicketsCount` to see which ones
|
||||||
|
/// are actually valid ticket mappings.
|
||||||
|
#[pallet::storage]
|
||||||
|
pub(crate) type Tickets<T: Config> = StorageMap<_, Twox64Concat, u32, T::AccountId>;
|
||||||
|
|
||||||
/// Set calls in storage which can be used to purchase a lottery ticket.
|
/// The calls stored in this pallet to be used in an active lottery if configured
|
||||||
///
|
/// by `Config::ValidateCall`.
|
||||||
/// This function only matters if you use the `ValidateCall` implementation
|
#[pallet::storage]
|
||||||
/// provided by this pallet, which uses storage to determine the valid calls.
|
pub(crate) type CallIndices<T> = StorageValue<_, Vec<CallIndex>, ValueQuery>;
|
||||||
///
|
|
||||||
/// This extrinsic must be called by the Manager origin.
|
|
||||||
#[weight = T::WeightInfo::set_calls(calls.len() as u32)]
|
|
||||||
fn set_calls(origin, calls: Vec<<T as Config>::Call>) {
|
|
||||||
T::ManagerOrigin::ensure_origin(origin)?;
|
|
||||||
ensure!(calls.len() <= T::MaxCalls::get(), Error::<T>::TooManyCalls);
|
|
||||||
if calls.is_empty() {
|
|
||||||
CallIndices::kill();
|
|
||||||
} else {
|
|
||||||
let indices = Self::calls_to_indices(&calls)?;
|
|
||||||
CallIndices::put(indices);
|
|
||||||
}
|
|
||||||
Self::deposit_event(RawEvent::CallsUpdated);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Start a lottery using the provided configuration.
|
|
||||||
///
|
|
||||||
/// This extrinsic must be called by the `ManagerOrigin`.
|
|
||||||
///
|
|
||||||
/// Parameters:
|
|
||||||
///
|
|
||||||
/// * `price`: The cost of a single ticket.
|
|
||||||
/// * `length`: How long the lottery should run for starting at the current block.
|
|
||||||
/// * `delay`: How long after the lottery end we should wait before picking a winner.
|
|
||||||
/// * `repeat`: If the lottery should repeat when completed.
|
|
||||||
#[weight = T::WeightInfo::start_lottery()]
|
|
||||||
fn start_lottery(origin,
|
|
||||||
price: BalanceOf<T>,
|
|
||||||
length: T::BlockNumber,
|
|
||||||
delay: T::BlockNumber,
|
|
||||||
repeat: bool,
|
|
||||||
) {
|
|
||||||
T::ManagerOrigin::ensure_origin(origin)?;
|
|
||||||
Lottery::<T>::try_mutate(|lottery| -> DispatchResult {
|
|
||||||
ensure!(lottery.is_none(), Error::<T>::InProgress);
|
|
||||||
let index = LotteryIndex::get();
|
|
||||||
let new_index = index.checked_add(1).ok_or(ArithmeticError::Overflow)?;
|
|
||||||
let start = frame_system::Pallet::<T>::block_number();
|
|
||||||
// Use new_index to more easily track everything with the current state.
|
|
||||||
*lottery = Some(LotteryConfig {
|
|
||||||
price,
|
|
||||||
start,
|
|
||||||
length,
|
|
||||||
delay,
|
|
||||||
repeat,
|
|
||||||
});
|
|
||||||
LotteryIndex::put(new_index);
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
// 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());
|
|
||||||
}
|
|
||||||
Self::deposit_event(RawEvent::LotteryStarted);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If a lottery is repeating, you can use this to stop the repeat.
|
|
||||||
/// The lottery will continue to run to completion.
|
|
||||||
///
|
|
||||||
/// This extrinsic must be called by the `ManagerOrigin`.
|
|
||||||
#[weight = T::WeightInfo::stop_repeat()]
|
|
||||||
fn stop_repeat(origin) {
|
|
||||||
T::ManagerOrigin::ensure_origin(origin)?;
|
|
||||||
Lottery::<T>::mutate(|mut lottery| {
|
|
||||||
if let Some(config) = &mut lottery {
|
|
||||||
config.repeat = false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#[pallet::hooks]
|
||||||
|
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
|
||||||
fn on_initialize(n: T::BlockNumber) -> Weight {
|
fn on_initialize(n: T::BlockNumber) -> Weight {
|
||||||
Lottery::<T>::mutate(|mut lottery| -> Weight {
|
Lottery::<T>::mutate(|mut lottery| -> Weight {
|
||||||
if let Some(config) = &mut lottery {
|
if let Some(config) = &mut lottery {
|
||||||
@@ -319,7 +236,7 @@ decl_module! {
|
|||||||
.saturating_add(config.delay);
|
.saturating_add(config.delay);
|
||||||
if payout_block <= n {
|
if payout_block <= n {
|
||||||
let (lottery_account, lottery_balance) = Self::pot();
|
let (lottery_account, lottery_balance) = Self::pot();
|
||||||
let ticket_count = TicketsCount::get();
|
let ticket_count = TicketsCount::<T>::get();
|
||||||
|
|
||||||
let winning_number = Self::choose_winner(ticket_count);
|
let winning_number = Self::choose_winner(ticket_count);
|
||||||
let winner = Tickets::<T>::get(winning_number).unwrap_or(lottery_account);
|
let winner = Tickets::<T>::get(winning_number).unwrap_or(lottery_account);
|
||||||
@@ -327,13 +244,13 @@ decl_module! {
|
|||||||
let res = T::Currency::transfer(&Self::account_id(), &winner, lottery_balance, KeepAlive);
|
let res = T::Currency::transfer(&Self::account_id(), &winner, lottery_balance, KeepAlive);
|
||||||
debug_assert!(res.is_ok());
|
debug_assert!(res.is_ok());
|
||||||
|
|
||||||
Self::deposit_event(RawEvent::Winner(winner, lottery_balance));
|
Self::deposit_event(Event::<T>::Winner(winner, lottery_balance));
|
||||||
|
|
||||||
TicketsCount::kill();
|
TicketsCount::<T>::kill();
|
||||||
|
|
||||||
if config.repeat {
|
if config.repeat {
|
||||||
// If lottery should repeat, increment index by 1.
|
// If lottery should repeat, increment index by 1.
|
||||||
LotteryIndex::mutate(|index| *index = index.saturating_add(1));
|
LotteryIndex::<T>::mutate(|index| *index = index.saturating_add(1));
|
||||||
// Set a new start with the current block.
|
// Set a new start with the current block.
|
||||||
config.start = n;
|
config.start = n;
|
||||||
return T::WeightInfo::on_initialize_repeat()
|
return T::WeightInfo::on_initialize_repeat()
|
||||||
@@ -351,9 +268,114 @@ decl_module! {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[pallet::call]
|
||||||
|
impl<T: Config> Pallet<T> {
|
||||||
|
/// Buy a ticket to enter the lottery.
|
||||||
|
///
|
||||||
|
/// This extrinsic acts as a passthrough function for `call`. In all
|
||||||
|
/// situations where `call` alone would succeed, this extrinsic should
|
||||||
|
/// succeed.
|
||||||
|
///
|
||||||
|
/// If `call` is successful, then we will attempt to purchase a ticket,
|
||||||
|
/// which may fail silently. To detect success of a ticket purchase, you
|
||||||
|
/// should listen for the `TicketBought` event.
|
||||||
|
///
|
||||||
|
/// This extrinsic must be called by a signed origin.
|
||||||
|
#[pallet::weight(
|
||||||
|
T::WeightInfo::buy_ticket()
|
||||||
|
.saturating_add(call.get_dispatch_info().weight)
|
||||||
|
)]
|
||||||
|
pub(crate) fn buy_ticket(origin: OriginFor<T>, call: Box<<T as Config>::Call>) -> DispatchResult {
|
||||||
|
let caller = ensure_signed(origin.clone())?;
|
||||||
|
call.clone().dispatch(origin).map_err(|e| e.error)?;
|
||||||
|
|
||||||
|
let _ = Self::do_buy_ticket(&caller, &call);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set calls in storage which can be used to purchase a lottery ticket.
|
||||||
|
///
|
||||||
|
/// This function only matters if you use the `ValidateCall` implementation
|
||||||
|
/// provided by this pallet, which uses storage to determine the valid calls.
|
||||||
|
///
|
||||||
|
/// This extrinsic must be called by the Manager origin.
|
||||||
|
#[pallet::weight(T::WeightInfo::set_calls(calls.len() as u32))]
|
||||||
|
pub(crate) fn set_calls(origin: OriginFor<T>, calls: Vec<<T as Config>::Call>) -> DispatchResult {
|
||||||
|
T::ManagerOrigin::ensure_origin(origin)?;
|
||||||
|
ensure!(calls.len() <= T::MaxCalls::get() as usize, Error::<T>::TooManyCalls);
|
||||||
|
if calls.is_empty() {
|
||||||
|
CallIndices::<T>::kill();
|
||||||
|
} else {
|
||||||
|
let indices = Self::calls_to_indices(&calls)?;
|
||||||
|
CallIndices::<T>::put(indices);
|
||||||
|
}
|
||||||
|
Self::deposit_event(Event::<T>::CallsUpdated);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start a lottery using the provided configuration.
|
||||||
|
///
|
||||||
|
/// This extrinsic must be called by the `ManagerOrigin`.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * `price`: The cost of a single ticket.
|
||||||
|
/// * `length`: How long the lottery should run for starting at the current block.
|
||||||
|
/// * `delay`: How long after the lottery end we should wait before picking a winner.
|
||||||
|
/// * `repeat`: If the lottery should repeat when completed.
|
||||||
|
#[pallet::weight(T::WeightInfo::start_lottery())]
|
||||||
|
pub(crate) fn start_lottery(
|
||||||
|
origin: OriginFor<T>,
|
||||||
|
price: BalanceOf<T>,
|
||||||
|
length: T::BlockNumber,
|
||||||
|
delay: T::BlockNumber,
|
||||||
|
repeat: bool,
|
||||||
|
) -> DispatchResult {
|
||||||
|
T::ManagerOrigin::ensure_origin(origin)?;
|
||||||
|
Lottery::<T>::try_mutate(|lottery| -> DispatchResult {
|
||||||
|
ensure!(lottery.is_none(), Error::<T>::InProgress);
|
||||||
|
let index = LotteryIndex::<T>::get();
|
||||||
|
let new_index = index.checked_add(1).ok_or(ArithmeticError::Overflow)?;
|
||||||
|
let start = frame_system::Pallet::<T>::block_number();
|
||||||
|
// Use new_index to more easily track everything with the current state.
|
||||||
|
*lottery = Some(LotteryConfig {
|
||||||
|
price,
|
||||||
|
start,
|
||||||
|
length,
|
||||||
|
delay,
|
||||||
|
repeat,
|
||||||
|
});
|
||||||
|
LotteryIndex::<T>::put(new_index);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
// 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());
|
||||||
|
}
|
||||||
|
Self::deposit_event(Event::<T>::LotteryStarted);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If a lottery is repeating, you can use this to stop the repeat.
|
||||||
|
/// The lottery will continue to run to completion.
|
||||||
|
///
|
||||||
|
/// This extrinsic must be called by the `ManagerOrigin`.
|
||||||
|
#[pallet::weight(T::WeightInfo::stop_repeat())]
|
||||||
|
pub(crate) fn stop_repeat(origin: OriginFor<T>) -> DispatchResult {
|
||||||
|
T::ManagerOrigin::ensure_origin(origin)?;
|
||||||
|
Lottery::<T>::mutate(|mut lottery| {
|
||||||
|
if let Some(config) = &mut lottery {
|
||||||
|
config.repeat = false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Config> Module<T> {
|
impl<T: Config> Pallet<T> {
|
||||||
/// The account ID of the lottery pot.
|
/// The account ID of the lottery pot.
|
||||||
///
|
///
|
||||||
/// This actually does computation. If you need to keep using it, then make sure you cache the
|
/// This actually does computation. If you need to keep using it, then make sure you cache the
|
||||||
@@ -397,11 +419,11 @@ impl<T: Config> Module<T> {
|
|||||||
ensure!(block_number < config.start.saturating_add(config.length), Error::<T>::AlreadyEnded);
|
ensure!(block_number < config.start.saturating_add(config.length), Error::<T>::AlreadyEnded);
|
||||||
ensure!(T::ValidateCall::validate_call(call), Error::<T>::InvalidCall);
|
ensure!(T::ValidateCall::validate_call(call), Error::<T>::InvalidCall);
|
||||||
let call_index = Self::call_to_index(call)?;
|
let call_index = Self::call_to_index(call)?;
|
||||||
let ticket_count = TicketsCount::get();
|
let ticket_count = TicketsCount::<T>::get();
|
||||||
let new_ticket_count = ticket_count.checked_add(1).ok_or(ArithmeticError::Overflow)?;
|
let new_ticket_count = ticket_count.checked_add(1).ok_or(ArithmeticError::Overflow)?;
|
||||||
// Try to update the participant status
|
// Try to update the participant status
|
||||||
Participants::<T>::try_mutate(&caller, |(lottery_index, participating_calls)| -> DispatchResult {
|
Participants::<T>::try_mutate(&caller, |(lottery_index, participating_calls)| -> DispatchResult {
|
||||||
let index = LotteryIndex::get();
|
let index = LotteryIndex::<T>::get();
|
||||||
// If lottery index doesn't match, then reset participating calls and index.
|
// If lottery index doesn't match, then reset participating calls and index.
|
||||||
if *lottery_index != index {
|
if *lottery_index != index {
|
||||||
*participating_calls = Vec::new();
|
*participating_calls = Vec::new();
|
||||||
@@ -413,13 +435,13 @@ impl<T: Config> Module<T> {
|
|||||||
// Check user has enough funds and send it to the Lottery account.
|
// 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, KeepAlive)?;
|
||||||
// Create a new ticket.
|
// Create a new ticket.
|
||||||
TicketsCount::put(new_ticket_count);
|
TicketsCount::<T>::put(new_ticket_count);
|
||||||
Tickets::<T>::insert(ticket_count, caller.clone());
|
Tickets::<T>::insert(ticket_count, caller.clone());
|
||||||
participating_calls.push(call_index);
|
participating_calls.push(call_index);
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Self::deposit_event(RawEvent::TicketBought(caller.clone(), call_index));
|
Self::deposit_event(Event::<T>::TicketBought(caller.clone(), call_index));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ impl pallet_balances::Config for Test {
|
|||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
pub const LotteryPalletId: PalletId = PalletId(*b"py/lotto");
|
pub const LotteryPalletId: PalletId = PalletId(*b"py/lotto");
|
||||||
pub const MaxCalls: usize = 2;
|
pub const MaxCalls: u32 = 2;
|
||||||
pub const MaxGenerateRandom: u32 = 10;
|
pub const MaxGenerateRandom: u32 = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ fn initial_state() {
|
|||||||
assert_eq!(Balances::free_balance(Lottery::account_id()), 0);
|
assert_eq!(Balances::free_balance(Lottery::account_id()), 0);
|
||||||
assert!(crate::Lottery::<Test>::get().is_none());
|
assert!(crate::Lottery::<Test>::get().is_none());
|
||||||
assert_eq!(Participants::<Test>::get(&1), (0, vec![]));
|
assert_eq!(Participants::<Test>::get(&1), (0, vec![]));
|
||||||
assert_eq!(TicketsCount::get(), 0);
|
assert_eq!(TicketsCount::<Test>::get(), 0);
|
||||||
assert!(Tickets::<Test>::get(0).is_none());
|
assert!(Tickets::<Test>::get(0).is_none());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@ fn basic_end_to_end_works() {
|
|||||||
// 20 from the transfer, 10 from buying a ticket
|
// 20 from the transfer, 10 from buying a ticket
|
||||||
assert_eq!(Balances::free_balance(&1), 100 - 20 - 10);
|
assert_eq!(Balances::free_balance(&1), 100 - 20 - 10);
|
||||||
assert_eq!(Participants::<Test>::get(&1).1.len(), 1);
|
assert_eq!(Participants::<Test>::get(&1).1.len(), 1);
|
||||||
assert_eq!(TicketsCount::get(), 1);
|
assert_eq!(TicketsCount::<Test>::get(), 1);
|
||||||
// 1 owns the 0 ticket
|
// 1 owns the 0 ticket
|
||||||
assert_eq!(Tickets::<Test>::get(0), Some(1));
|
assert_eq!(Tickets::<Test>::get(0), Some(1));
|
||||||
|
|
||||||
@@ -69,21 +69,21 @@ fn basic_end_to_end_works() {
|
|||||||
assert_ok!(Lottery::buy_ticket(Origin::signed(2), call.clone()));
|
assert_ok!(Lottery::buy_ticket(Origin::signed(2), call.clone()));
|
||||||
assert_ok!(Lottery::buy_ticket(Origin::signed(3), call.clone()));
|
assert_ok!(Lottery::buy_ticket(Origin::signed(3), call.clone()));
|
||||||
assert_ok!(Lottery::buy_ticket(Origin::signed(4), call.clone()));
|
assert_ok!(Lottery::buy_ticket(Origin::signed(4), call.clone()));
|
||||||
assert_eq!(TicketsCount::get(), 4);
|
assert_eq!(TicketsCount::<Test>::get(), 4);
|
||||||
|
|
||||||
// Go to end
|
// Go to end
|
||||||
run_to_block(20);
|
run_to_block(20);
|
||||||
assert_ok!(Lottery::buy_ticket(Origin::signed(5), call.clone()));
|
assert_ok!(Lottery::buy_ticket(Origin::signed(5), call.clone()));
|
||||||
// Ticket isn't bought
|
// Ticket isn't bought
|
||||||
assert_eq!(TicketsCount::get(), 4);
|
assert_eq!(TicketsCount::<Test>::get(), 4);
|
||||||
|
|
||||||
// Go to payout
|
// Go to payout
|
||||||
run_to_block(25);
|
run_to_block(25);
|
||||||
// User 1 wins
|
// User 1 wins
|
||||||
assert_eq!(Balances::free_balance(&1), 70 + 40);
|
assert_eq!(Balances::free_balance(&1), 70 + 40);
|
||||||
// Lottery is reset and restarted
|
// Lottery is reset and restarted
|
||||||
assert_eq!(TicketsCount::get(), 0);
|
assert_eq!(TicketsCount::<Test>::get(), 0);
|
||||||
assert_eq!(LotteryIndex::get(), 2);
|
assert_eq!(LotteryIndex::<Test>::get(), 2);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
crate::Lottery::<Test>::get().unwrap(),
|
crate::Lottery::<Test>::get().unwrap(),
|
||||||
LotteryConfig {
|
LotteryConfig {
|
||||||
@@ -100,7 +100,7 @@ fn basic_end_to_end_works() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn set_calls_works() {
|
fn set_calls_works() {
|
||||||
new_test_ext().execute_with(|| {
|
new_test_ext().execute_with(|| {
|
||||||
assert!(!CallIndices::exists());
|
assert!(!CallIndices::<Test>::exists());
|
||||||
|
|
||||||
let calls = vec![
|
let calls = vec![
|
||||||
Call::Balances(BalancesCall::force_transfer(0, 0, 0)),
|
Call::Balances(BalancesCall::force_transfer(0, 0, 0)),
|
||||||
@@ -108,7 +108,7 @@ fn set_calls_works() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
assert_ok!(Lottery::set_calls(Origin::root(), calls));
|
assert_ok!(Lottery::set_calls(Origin::root(), calls));
|
||||||
assert!(CallIndices::exists());
|
assert!(CallIndices::<Test>::exists());
|
||||||
|
|
||||||
let too_many_calls = vec![
|
let too_many_calls = vec![
|
||||||
Call::Balances(BalancesCall::force_transfer(0, 0, 0)),
|
Call::Balances(BalancesCall::force_transfer(0, 0, 0)),
|
||||||
@@ -123,7 +123,7 @@ fn set_calls_works() {
|
|||||||
|
|
||||||
// Clear calls
|
// Clear calls
|
||||||
assert_ok!(Lottery::set_calls(Origin::root(), vec![]));
|
assert_ok!(Lottery::set_calls(Origin::root(), vec![]));
|
||||||
assert!(CallIndices::get().is_empty());
|
assert!(CallIndices::<Test>::get().is_empty());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +161,7 @@ fn buy_ticket_works_as_simple_passthrough() {
|
|||||||
// This is just a basic transfer then
|
// This is just a basic transfer then
|
||||||
assert_ok!(Lottery::buy_ticket(Origin::signed(1), call.clone()));
|
assert_ok!(Lottery::buy_ticket(Origin::signed(1), call.clone()));
|
||||||
assert_eq!(Balances::free_balance(&1), 100 - 20);
|
assert_eq!(Balances::free_balance(&1), 100 - 20);
|
||||||
assert_eq!(TicketsCount::get(), 0);
|
assert_eq!(TicketsCount::<Test>::get(), 0);
|
||||||
|
|
||||||
// Lottery is set up, but too expensive to enter, so `do_buy_ticket` fails.
|
// Lottery is set up, but too expensive to enter, so `do_buy_ticket` fails.
|
||||||
let calls = vec![
|
let calls = vec![
|
||||||
@@ -174,7 +174,7 @@ fn buy_ticket_works_as_simple_passthrough() {
|
|||||||
assert_ok!(Lottery::start_lottery(Origin::root(), 60, 10, 5, false));
|
assert_ok!(Lottery::start_lottery(Origin::root(), 60, 10, 5, false));
|
||||||
assert_ok!(Lottery::buy_ticket(Origin::signed(1), call.clone()));
|
assert_ok!(Lottery::buy_ticket(Origin::signed(1), call.clone()));
|
||||||
assert_eq!(Balances::free_balance(&1), 100 - 20 - 20);
|
assert_eq!(Balances::free_balance(&1), 100 - 20 - 20);
|
||||||
assert_eq!(TicketsCount::get(), 0);
|
assert_eq!(TicketsCount::<Test>::get(), 0);
|
||||||
|
|
||||||
// If call would fail, the whole thing still fails the same
|
// If call would fail, the whole thing still fails the same
|
||||||
let fail_call = Box::new(Call::Balances(BalancesCall::transfer(2, 1000)));
|
let fail_call = Box::new(Call::Balances(BalancesCall::transfer(2, 1000)));
|
||||||
@@ -192,11 +192,11 @@ fn buy_ticket_works_as_simple_passthrough() {
|
|||||||
// User can call other txs, but doesn't get a ticket
|
// User can call other txs, but doesn't get a ticket
|
||||||
let remark_call = Box::new(Call::System(SystemCall::remark(b"hello, world!".to_vec())));
|
let remark_call = Box::new(Call::System(SystemCall::remark(b"hello, world!".to_vec())));
|
||||||
assert_ok!(Lottery::buy_ticket(Origin::signed(2), remark_call));
|
assert_ok!(Lottery::buy_ticket(Origin::signed(2), remark_call));
|
||||||
assert_eq!(TicketsCount::get(), 0);
|
assert_eq!(TicketsCount::<Test>::get(), 0);
|
||||||
|
|
||||||
let successful_call = Box::new(Call::Balances(BalancesCall::transfer(2, 1)));
|
let successful_call = Box::new(Call::Balances(BalancesCall::transfer(2, 1)));
|
||||||
assert_ok!(Lottery::buy_ticket(Origin::signed(2), successful_call));
|
assert_ok!(Lottery::buy_ticket(Origin::signed(2), successful_call));
|
||||||
assert_eq!(TicketsCount::get(), 1);
|
assert_eq!(TicketsCount::<Test>::get(), 1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,7 +214,7 @@ fn buy_ticket_works() {
|
|||||||
// Can't buy ticket before start
|
// Can't buy ticket before start
|
||||||
let call = Box::new(Call::Balances(BalancesCall::transfer(2, 1)));
|
let call = Box::new(Call::Balances(BalancesCall::transfer(2, 1)));
|
||||||
assert_ok!(Lottery::buy_ticket(Origin::signed(1), call.clone()));
|
assert_ok!(Lottery::buy_ticket(Origin::signed(1), call.clone()));
|
||||||
assert_eq!(TicketsCount::get(), 0);
|
assert_eq!(TicketsCount::<Test>::get(), 0);
|
||||||
|
|
||||||
// Start lottery
|
// Start lottery
|
||||||
assert_ok!(Lottery::start_lottery(Origin::root(), 1, 20, 5, false));
|
assert_ok!(Lottery::start_lottery(Origin::root(), 1, 20, 5, false));
|
||||||
@@ -222,28 +222,28 @@ fn buy_ticket_works() {
|
|||||||
// Go to start, buy ticket for transfer
|
// Go to start, buy ticket for transfer
|
||||||
run_to_block(5);
|
run_to_block(5);
|
||||||
assert_ok!(Lottery::buy_ticket(Origin::signed(1), call));
|
assert_ok!(Lottery::buy_ticket(Origin::signed(1), call));
|
||||||
assert_eq!(TicketsCount::get(), 1);
|
assert_eq!(TicketsCount::<Test>::get(), 1);
|
||||||
|
|
||||||
// Can't buy another of the same ticket (even if call is slightly changed)
|
// Can't buy another of the same ticket (even if call is slightly changed)
|
||||||
let call = Box::new(Call::Balances(BalancesCall::transfer(3, 30)));
|
let call = Box::new(Call::Balances(BalancesCall::transfer(3, 30)));
|
||||||
assert_ok!(Lottery::buy_ticket(Origin::signed(1), call));
|
assert_ok!(Lottery::buy_ticket(Origin::signed(1), call));
|
||||||
assert_eq!(TicketsCount::get(), 1);
|
assert_eq!(TicketsCount::<Test>::get(), 1);
|
||||||
|
|
||||||
// Buy ticket for remark
|
// Buy ticket for remark
|
||||||
let call = Box::new(Call::System(SystemCall::remark(b"hello, world!".to_vec())));
|
let call = Box::new(Call::System(SystemCall::remark(b"hello, world!".to_vec())));
|
||||||
assert_ok!(Lottery::buy_ticket(Origin::signed(1), call.clone()));
|
assert_ok!(Lottery::buy_ticket(Origin::signed(1), call.clone()));
|
||||||
assert_eq!(TicketsCount::get(), 2);
|
assert_eq!(TicketsCount::<Test>::get(), 2);
|
||||||
|
|
||||||
// Go to end, can't buy tickets anymore
|
// Go to end, can't buy tickets anymore
|
||||||
run_to_block(20);
|
run_to_block(20);
|
||||||
assert_ok!(Lottery::buy_ticket(Origin::signed(2), call.clone()));
|
assert_ok!(Lottery::buy_ticket(Origin::signed(2), call.clone()));
|
||||||
assert_eq!(TicketsCount::get(), 2);
|
assert_eq!(TicketsCount::<Test>::get(), 2);
|
||||||
|
|
||||||
// Go to payout, can't buy tickets when there is no lottery open
|
// Go to payout, can't buy tickets when there is no lottery open
|
||||||
run_to_block(25);
|
run_to_block(25);
|
||||||
assert_ok!(Lottery::buy_ticket(Origin::signed(2), call.clone()));
|
assert_ok!(Lottery::buy_ticket(Origin::signed(2), call.clone()));
|
||||||
assert_eq!(TicketsCount::get(), 0);
|
assert_eq!(TicketsCount::<Test>::get(), 0);
|
||||||
assert_eq!(LotteryIndex::get(), 1);
|
assert_eq!(LotteryIndex::<Test>::get(), 1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user