-
Notifications
You must be signed in to change notification settings - Fork 4
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
feat: battlepass #120
feat: battlepass #120
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ | ||
# ███░▄▄▄█░▄▄▀█░▄▀▄░█░▄▄█░▄▀█░▄▄▀█▀▄▄▀██ | ||
# ███░█▄▀█░▀▀░█░█▄█░█░▄▄█░█░█░▀▀░█░██░██ | ||
# ███▄▄▄▄█▄██▄█▄███▄█▄▄▄█▄▄██▄██▄██▄▄███ | ||
# ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ | ||
[package] | ||
name = "gamedao-battlepass" | ||
version = "1.2.0" | ||
authors = ["zero.io","gamedao.co"] | ||
repository = "https://github.com/gamedaoco/gamedao-protocol" | ||
edition = "2018" | ||
license = "GPL-3.0-or-later" | ||
description = "BattlePass pallet provides functionality to create, manage and participate in battlepasses." | ||
|
||
[dependencies] | ||
serde = { version = "1.0.143", optional = true } | ||
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } | ||
scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } | ||
sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } | ||
sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } | ||
sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } | ||
sp-storage = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } | ||
sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.28", default-features=false } | ||
frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false } | ||
frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false } | ||
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false, optional = true } | ||
|
||
[dev-dependencies] | ||
frame-support-test = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } | ||
|
||
[features] | ||
default = ['std'] | ||
runtime-benchmarks = ["frame-benchmarking"] | ||
std = [ | ||
"serde/std", | ||
'codec/std', | ||
"scale-info/std", | ||
|
||
"frame-support/std", | ||
"frame-system/std", | ||
"frame-benchmarking/std", | ||
|
||
"sp-core/std", | ||
"sp-std/std", | ||
"sp-runtime/std", | ||
] | ||
try-runtime = ["frame-support/try-runtime"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
// _______ ________ ________ ________ ______ _______ _______ | ||
// ╱╱ ╲╱ ╲╱ ╲╱ ╲_╱ ╲╲╱ ╲╲╱ ╲╲ | ||
// ╱╱ __╱ ╱ ╱ ╱ ╱╱ ╱╱ ╱╱ | ||
// ╱ ╱ ╱ ╱ ╱ _╱ ╱ ╱ ╱ | ||
// ╲________╱╲___╱____╱╲__╱__╱__╱╲________╱╲________╱╲___╱____╱╲________╱ | ||
// | ||
// This file is part of GameDAO Protocol. | ||
// Copyright (C) 2018-2022 GameDAO AG. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
//! BATTLEPASS | ||
//! This pallet provides functionality to create, manage and participate in battlepasses. | ||
#![cfg_attr(not(feature = "std"), no_std)] | ||
|
||
pub use pallet::*; | ||
use frame_support::pallet_prelude::*; | ||
use frame_system::pallet_prelude::*; | ||
use sp_std::convert::TryInto; | ||
use sp_runtime::traits::Hash; | ||
|
||
pub mod types; | ||
pub use types::*; | ||
|
||
type String<T> = BoundedVec<u8, <T as pallet::Config>::StringLimit>; | ||
|
||
#[frame_support::pallet] | ||
pub mod pallet { | ||
use super::*; | ||
|
||
#[pallet::pallet] | ||
#[pallet::generate_store(pub(super) trait Store)] | ||
pub struct Pallet<T>(_); | ||
|
||
#[pallet::config] | ||
pub trait Config: frame_system::Config { | ||
type Event: From<Event<Self>> | ||
+ IsType<<Self as frame_system::Config>::Event> | ||
+ Into<<Self as frame_system::Config>::Event>; | ||
|
||
/// The maximum length of a name or cid stored on-chain. | ||
#[pallet::constant] | ||
type StringLimit: Get<u32>; | ||
} | ||
|
||
#[pallet::event] | ||
#[pallet::generate_deposit(pub(super) fn deposit_event)] | ||
pub enum Event<T: Config> { | ||
/// New BattlePass created | ||
BattlepassCreated { | ||
// org_id: T::Hash, | ||
org_id: T::Hash, | ||
battlepass_id: T::Hash, | ||
block_number: T::BlockNumber | ||
}, | ||
|
||
/// BattlePass claimed | ||
BattlepassClaimed { | ||
claimer: T::AccountId, | ||
org_id: T::Hash, | ||
battlepass_id: T::Hash, | ||
nft_id: T::Hash, | ||
block_number: T::BlockNumber | ||
FiberMan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}, | ||
} | ||
|
||
#[pallet::error] | ||
pub enum Error<T> { | ||
AuthorizationError, | ||
MissingParameter, | ||
InvalidParameter, | ||
OrganizationUnknown, | ||
OrgHasActiveBattlepass, | ||
BattlepassExists, | ||
BattlepassClaimed, | ||
BattlepassUnknown, | ||
NotMember, | ||
} | ||
|
||
/// Battlepass by its id. | ||
/// | ||
/// Battlepasses: map Hash => Battlepass | ||
#[pallet::storage] | ||
#[pallet::getter(fn get_battlepass)] | ||
pub(super) type Battlepasses<T: Config> = StorageMap<_, Blake2_128Concat, T::Hash, Battlepass<T::Hash, T::AccountId, T::BlockNumber, String<T>>, OptionQuery>; | ||
FiberMan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// Number of battlepasses per organization. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is it the number of claimed passes ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, it's the total number of created battlepasses per org. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so created not claimed? what is the difference? i think the pass is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you mean what @vayesy wrote? or do you reference something else? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sorry, yes, I meant @vayesy. |
||
/// | ||
/// BattlepassCount: map Hash => u32 | ||
#[pallet::storage] | ||
#[pallet::getter(fn get_battlepass_count)] | ||
pub type BattlepassCount<T: Config> = StorageMap<_, Blake2_128Concat, T::Hash, u32, ValueQuery>; | ||
|
||
/// Current active battlepass in organization. | ||
/// | ||
/// ActiveBattlepassByOrg: map Hash => Hash | ||
#[pallet::storage] | ||
#[pallet::getter(fn get_active_battlepass)] | ||
pub type ActiveBattlepassByOrg<T: Config> = StorageMap<_, Blake2_128Concat, T::Hash, T::Hash, OptionQuery>; | ||
FiberMan marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is only one usage of this storage and it's avoiding duplicates. And it's possible to achieve this with the existing
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea. But in this case, we need to keep in mind to recreate all Battlepasses if something changed in Battlepass struct. |
||
|
||
/// Claimed Battlepass by user. | ||
/// | ||
/// ClaimedBattlepass: map (AccountId, Hash) => Hash | ||
#[pallet::storage] | ||
#[pallet::getter(fn get_claimed_battlepass)] | ||
pub(super) type ClaimedBattlepass<T: Config> = StorageDoubleMap<_, | ||
Blake2_128Concat, T::AccountId, | ||
Blake2_128Concat, T::Hash, | ||
T::Hash, | ||
OptionQuery | ||
>; | ||
|
||
#[pallet::call] | ||
impl<T: Config> Pallet<T> { | ||
#[pallet::weight(0)] | ||
pub fn create_battlepass( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if you put the transactional part into the extrinsic fn, you may need to double the code unneccessarily as we need a way to add a battlepass via oracle, therefore There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there is slight confusion in terminology. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. battlepass is the general seasonal wrapper for achievements which result in scores and which unlock claiming rewards. i have written a doc for it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you add the proper names for those entities and structs and show where they are stored? @vayesy |
||
origin: OriginFor<T>, | ||
org_id: T::Hash, | ||
name: String<T>, | ||
cid: String<T>, | ||
price: u16, | ||
) -> DispatchResult { | ||
let creator = ensure_signed(origin)?; | ||
|
||
// check if origin is Org Prime or Root | ||
|
||
// check if Org exists | ||
|
||
// check if active battlepass does not exist for the Org | ||
|
||
|
||
|
||
// Create Battlepass | ||
let now = <frame_system::Pallet<T>>::block_number(); | ||
let battlepass: Battlepass<T::Hash, T::AccountId, T::BlockNumber, String<T>> = types::Battlepass { | ||
creator, | ||
org_id, | ||
name, | ||
cid, | ||
state: types::BattlepassState::Active, | ||
season: Self::get_battlepass_count(org_id), | ||
price, | ||
FiberMan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
created: now.clone(), mutated: now | ||
}; | ||
let battlepass_id = <T as frame_system::Config>::Hashing::hash_of(&battlepass); | ||
|
||
Battlepasses::<T>::insert(&battlepass_id, battlepass); | ||
|
||
Self::deposit_event(Event::BattlepassCreated { org_id, battlepass_id, block_number: now }); | ||
|
||
Ok(()) | ||
} | ||
|
||
#[pallet::weight(0)] | ||
pub fn claim_battlepass( | ||
origin: OriginFor<T>, | ||
org_id: T::Hash, | ||
) -> DispatchResult { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would work though
|
||
let claimer = ensure_signed(origin)?; | ||
let battlepass_id = Self::get_active_battlepass(org_id).ok_or(Error::<T>::BattlepassUnknown)?; | ||
let now = <frame_system::Pallet<T>>::block_number(); | ||
|
||
// check if user is a member of organization | ||
|
||
// check if Org exists | ||
2075 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// check if Org has active battlepass | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if you look to the two checks above maybe we should introduce the same for battlepass, like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here we need to get the active battlepass, not just check if it's active. |
||
|
||
// check if Battlepass already claimed | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you'd use |
||
|
||
|
||
|
||
|
||
// Create NFT | ||
let nft_id = <T as frame_system::Config>::Hashing::hash_of(&claimer); | ||
|
||
|
||
ClaimedBattlepass::<T>::insert(&claimer, battlepass_id, nft_id); | ||
|
||
Self::deposit_event(Event::BattlepassClaimed { claimer, org_id, battlepass_id, nft_id, block_number: now }); | ||
|
||
Ok(()) | ||
} | ||
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
use frame_support::pallet_prelude::*; | ||
use codec::MaxEncodedLen; | ||
|
||
#[derive(Encode, Decode, Clone, PartialEq, Eq, PartialOrd, Ord, TypeInfo, MaxEncodedLen)] | ||
// #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] | ||
pub enum BattlepassState { | ||
Inactive = 0, | ||
Active = 1, | ||
} | ||
impl Default for BattlepassState { | ||
fn default() -> Self { | ||
Self::Active | ||
} | ||
} | ||
|
||
|
||
#[derive(Encode, Decode, Default, PartialEq, Eq, TypeInfo, MaxEncodedLen)] | ||
// #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] | ||
pub struct Battlepass<Hash, AccountId, BlockNumber, BoundedString> { | ||
pub creator: AccountId, | ||
pub org_id: Hash, | ||
pub name: BoundedString, | ||
pub cid: BoundedString, | ||
pub state: BattlepassState, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Usually, we have separate storage for the state (ex. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will create storage for states, like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you can query the active battlepass either via the graph as a client or by filtering for active pass as a first class citizen (the pallet), what is the usecase for the pallet? it would only operate per id on a specific battlepass and reject if the operation is not allowed, e.g. not active, not owner e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Example of use case: upon creation of new Battlepass for the Org, we need to check if there is an Active Battlepass (current season) already. Org may have multiple Battlepasses, but only one can be Active. So having a mapping |
||
pub season: u32, | ||
pub price: u16, | ||
pub created: BlockNumber, | ||
pub mutated: BlockNumber, | ||
FiberMan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest using "Created", "Claimed", "Closed" instead. Like we have for Flow. Since the subject is always the same - Battlepass.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There will be other entities in the pallet, like
Quests
,Achievements
, etc which will have similar events.Of course, we may put them in separate pallets, but they are related only to Battlepass, so I'm not sure if we should do so.
WDYT @vovacha , @vayesy , @2075 ?