Skip to content
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

Merged
merged 4 commits into from
Dec 16, 2022
Merged

feat: battlepass #120

merged 4 commits into from
Dec 16, 2022

Conversation

FiberMan
Copy link
Contributor

No description provided.

@FiberMan FiberMan changed the base branch from release-1.1.2 to release-1.2.0 December 15, 2022 11:50
@vovacha vovacha marked this pull request as draft December 15, 2022 12:01
#[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>;

/// Number of battlepasses per organization.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it the number of claimed passes ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's the total number of created battlepasses per org.

Copy link
Member

@2075 2075 Dec 16, 2022

Choose a reason for hiding this comment

The 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 minted when you claim, so claimed==created.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, it's a different entity, as @vovacha explained here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean what @vayesy wrote? or do you reference something else?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, yes, I meant @vayesy.

#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::weight(0)]
pub fn create_battlepass(
Copy link
Member

Choose a reason for hiding this comment

The 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 who, pass, account where who is permitted oracle and account is the user to add. also we need to store respective discord id somewhere otherwise we cannot map it,.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is slight confusion in terminology.
We name two entities the same way: a) general entity, which is created by organization and represents event to participate for the users; b) entity, which users purchase to gain access to participate in the event.
Particularly this extrinsic is used to create a) entity type. So we don't need to store discord id here and extend functionality for the oracle. Those will be applied for working with b) entity type.

Copy link
Member

Choose a reason for hiding this comment

The 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

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The 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

pub org_id: Hash,
pub name: BoundedString,
pub cid: BoundedString,
pub state: BattlepassState,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually, we have separate storage for the state (ex. MemberStates, OrgStates). I'm not sure what ActiveBattlepassByOrg is, but storing the state in the struct and storing it in the separate storage is suboptimal since you need to update both.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will create storage for states, like battlepass_id -> BattlepassState
ActiveBattlepassByOrg - is to check which is a currently active battlepass for the Org (org_id -> active battlepass_id). So the Active state will be stored in two places, but it will bring more benefits in terms of storage and usability, I suppose.

Copy link
Member

Choose a reason for hiding this comment

The 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. org -> vec<battlepass>

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 org -> active battlepass is more convenient and faster than working with vec.

battlepass/src/lib.rs Outdated Show resolved Hide resolved
@2075 2075 changed the title [WIP] Create Battlepass pallet feat: battlepass Dec 16, 2022
ensure!(T::Control::is_org_active(&org_id), Error::<T>::OrgUnknown);
// check if user is a member of organization
ensure!(T::Control::is_org_member_active(&org_id, &claimer), Error::<T>::NotMember);
// check if Org has active battlepass
Copy link
Member

Choose a reason for hiding this comment

The 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 T::Battlepass::is_battlepass_active ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

pub enum BattlepassState {
Draft = 0,
Active = 1,
Closed = 2,
Copy link
Member

@2075 2075 Dec 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Closed => Ended || 'Inactive' ?
also need Locked

I also tend to capitalize state, like DRAFT ACTIVE

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe Closed -> Finished? And the extrinsic accordingly finish_battlepass?
Why do we need Locked? @2075

@FiberMan FiberMan marked this pull request as ready for review December 16, 2022 16:51
battlepass/src/lib.rs Show resolved Hide resolved
battlepass/src/types.rs Show resolved Hide resolved
battlepass/src/lib.rs Show resolved Hide resolved
None, // royalty_amount
BoundedVec::truncate_from(b"NFT meta".to_vec()), // metadata TODO: what should be here?
false, // transferable
None // resources
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like these inline comments.
Here are some standards for documentation of the Rust project.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't find any info about inline comments in the standards.
Did you mean it's better to describe struct fields on the top of the struct? Like:

/// Battlepass struct
///
/// `collection_id`: Collection that will store all claimed Battlepass-NFTs
pub struct Battlepass...

battlepass/src/types.rs Show resolved Hide resolved

#[derive(Encode, Decode, Default, PartialEq, Eq, TypeInfo, MaxEncodedLen)]
pub struct BattlepassNft {
pub collection_id: u32,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I don't know the details here, but there should be a collection identifier in the rmrk NFT object. For me, this struct looks redundant, but feel free to ignore my comment.

Copy link
Member

@2075 2075 Dec 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree, should be stored maybe in battlepass metadata as hash?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree.
Added battlepass_id (hash) to metadata field of minted NFT.
Storage ClaimedBattlepasses: (AccountId, BattlepassId) -> NftId will be used to quickly validate if Battlepass NFT was claimed by user.

#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// New BattlePass created
BattlepassCreated {
Copy link
Contributor

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.

Copy link
Contributor Author

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 ?

/// 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>;
Copy link
Contributor

@vovacha vovacha Dec 16, 2022

Choose a reason for hiding this comment

The 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 Battlepass storage if you do the next steps:

  • remove created and updated from the Battlepass struct, since indexer nows this information it's redundant here
  • this way you'll have a unique hash of the Battlepass without any variables like BlockNumber
  • check if Battlepass doesn't contain a hash

Copy link
Contributor Author

@FiberMan FiberMan Dec 19, 2022

Choose a reason for hiding this comment

The 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.
And with this approach, we can only check if the same Battlepass exists, but we need to check if there is an active battlepass.

///
/// BattlepassStates: map Hash => BattlepassState
#[pallet::storage]
#[pallet::getter(fn get_battlepass_state)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We decided not to use getters. But maybe it's not actual anymore. WDYT? @vayesy

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thumbs up mean not use getters (why not then?) or not actual anymore? :)

#[pallet::weight(0)]
pub fn close_battlepass(
origin: OriginFor<T>,
org_id: T::Hash,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use org_id here instead of battlepass_id.

pub fn claim_battlepass(
origin: OriginFor<T>,
org_id: T::Hash,
) -> DispatchResult {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use org_id here instead of battlepass_id.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would work though

  • claim the active battlepass for an org would require this
  • or param is optional org or battlepass id
  • agree it is cleaner to use battlepass id

ensure!(T::Control::is_org_member_active(&org_id, &claimer), Error::<T>::NotMember);
// check if Org has active battlepass
let battlepass_id = Self::get_active_battlepass(org_id).ok_or(Error::<T>::BattlepassUnknown)?;
// check if Battlepass already claimed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you'd use battlepass_id as an extrinsic param, you can remove this line.

@2075 2075 merged commit e6b8f77 into release-1.2.0 Dec 16, 2022
@vayesy vayesy mentioned this pull request Dec 16, 2022
12 tasks
@2075
Copy link
Member

2075 commented Dec 16, 2022

would recommend to use ended vs closed in general, as battlepass is time based

2075 added a commit that referenced this pull request May 1, 2024
* Remove unused migration. (#111)

* Remove unused migration.

* Remove empty migration.rs files

* Fix tests.

* Add M2 review comments

* USE ORML dependencies from github instead of local version

* fix: rm warnings

* M2 review fixes (#115)

* feat: update member state (#119)

* feat: update orml references to git

* feat: add update member ext

* feat: update missing member state

* feat: update event, api

* Updated extrinsic. Added benchmarks

Co-authored-by: vasylenko-yevhen <[email protected]>

* feat: battlepass (#120)

* Create Battlepass pallete

* docs: add readme

* Update extrinsics.

* Add RMRK integration, mint Battlepass NFTs.

Co-authored-by: 2075 <[email protected]>

* Allow users to join to prime type org (#121)

* Create Battlepass pallete

* Add Battlepass activation, refactor.

* Update lib.rs (#123)

* add levels and rewards to battlepass. (#124)

* Unit tests for Battlepass (#125)

* Add wallet for battlepass Bot

* feat: change root_or_governance for approval

* feat: benchmarks for battlepass

* feat: bump substrate version from 0.9.28 to 0.9.36

* Add extrinsic descriptions for Battlepass.

* feat: Update Battlepass and Reward (#130)

* feat:tests for update_battlepass and update_reward

* feat: new benchmarks for battlepass.

* feat: extend bot privileges

* feat: claim battlepass by bot

* feat: add, remove levels by bot

* update benchmarks

* feat: use unique NFT IDs

* feat: remove ability to claim battlepass for self

* allows non org members to participate in battlepass

* finish tests, update weights

* allows root account to add bot account to the battlepass

* Correctly validate `add_bot` caller

* fix: update_battlepass should update any number of fields

* Remove RMRK

* fix: should create battlepass if there is active one

* feat: add option for name and cid (#142)

* feat: add option for name and cid

* feat: update tests

* feat: add mock, update test

* feat: fix types

* feat: rm unused

* feat: update readme, benchmarking

* Fix type

---------

Co-authored-by: vasylenko-yevhen <[email protected]>

* feat: bump substrate version from 0.9.36 to 0.9.38 (#141)

* feat: bump substrate version from 0.9.36 to 0.9.38

* bump protocol to 1.3.0

* use CID as collection metadata

* Revert "feat: bump substrate version from 0.9.36 to 0.9.38 (#141)" (#144)

This reverts commit d084aca.

* Update substrate v36 to v38 (#145)

* feat: bump substrate version from 0.9.36 to 0.9.38

* bump protocol to 1.3.0

* use CID as collection metadata

---------

Co-authored-by: FiberMan <[email protected]>

* fix: update_reward should update any number of fields (#146)

* feat: allow bot to update the battlepass

* feat: use provided CID as a metadata for NFT.

* address comments from SBP3 review

* Substrate upgrade 0.9.38 to 0.9.39

* Substrate upgrade 0.9.39 to 0.9.40

* remove deprecated macros

* replace pallet_uniques with pallet_nfts (#152)

* fix(battlepass): update permissions (#153)

---------

Co-authored-by: Yura <[email protected]>
Co-authored-by: Daniel Bigos <[email protected]>
Co-authored-by: vasylenko-yevhen <[email protected]>
Co-authored-by: 2075 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants