diff --git a/src/contract.rs b/src/contract.rs index c71db30..f16a597 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -1,7 +1,10 @@ +use std::collections::HashMap; + #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_binary, Binary, CosmosMsg, Deps, DepsMut, Env, MessageInfo, Response, StdResult, WasmMsg, + to_binary, Binary, CosmosMsg, Deps, DepsMut, Env, MessageInfo, Order, Response, StdResult, + WasmMsg, }; use cw2::set_contract_version; use komple_framework_mint_module::msg::ExecuteMsg as KompleMintExecuteMsg; @@ -11,7 +14,7 @@ use crate::msg::{ContractInformation, ExecuteMsg, InstantiateMsg, MigrateMsg, Qu use crate::helpers::throw_err; use crate::receive::receive; -use crate::state::{farm_profile_dto, FarmProfile, FARM_PROFILES, INFORMATION}; +use crate::state::{farm_profile_dto, points, FarmProfile, Points, FARM_PROFILES, INFORMATION}; const CONTRACT_NAME: &str = "crates.io:farm_template"; const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -161,8 +164,18 @@ pub fn execute( } } - farm.harvest(x.into(), y.into(), env.block.height)?; + let harvested = farm.harvest(x.into(), y.into(), env.block.height)?; + let mut pts = match points().may_load(deps.storage, &sender.as_str())? { + None => Points { + addr: sender.clone(), + plants: HashMap::new(), + }, + Some(p) => p, + }; + pts.add(harvested); + FARM_PROFILES.save(deps.storage, sender.as_str(), &farm)?; + points().save(deps.storage, sender.as_str(), &pts)?; Ok(Response::new() .add_attribute("action", "harvested") @@ -201,12 +214,70 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { let farm = FARM_PROFILES.may_load(deps.storage, address.as_str())?; let farm_dto = farm_profile_dto(&farm, env.block.height); - // get possible actions let v = to_binary(&farm_dto)?; Ok(v) } + QueryMsg::Leaderboard {} => { + let res: Result, _> = points() + .idx + .total + .range(deps.storage, None, None, Order::Descending) + .map(|res| match res { + Ok((_, v)) => { + let total: u64 = v.total(); + let addr: String = v.addr.into(); + + Ok((addr, total)) + } + Err(err) => Err(err), + }) + .collect(); + + match res { + Ok(v) => Ok(to_binary(&v)?), + Err(err) => Err(err), + } + } } } #[cfg(test)] -mod tests {} +mod tests { + use cosmwasm_std::testing::mock_dependencies; + + use crate::farm::PlantType; + + use super::*; + + #[test] + fn save_and_load_points() { + let mut deps = mock_dependencies(); + + let mut new_points = Points { + addr: "123".into(), + plants: HashMap::new(), + }; + new_points.add(PlantType::Sunflower); + println!("new points: {:?}", new_points); + let pts = points(); + pts.save(deps.as_mut().storage, "123", &new_points).unwrap(); + + let res: Result, _> = pts + .idx + .total + .range(deps.as_ref().storage, None, None, Order::Descending) + .map(|res| match res { + Ok((_, v)) => { + let total: u64 = v.total(); + let addr: String = v.addr.into(); + + Ok((addr, total)) + } + Err(err) => Err(err), + }) + .collect(); + + println!("result: {:?}", res); + assert_eq!(2, 2) + } +} diff --git a/src/farm.rs b/src/farm.rs index 6c1cfeb..e00faeb 100644 --- a/src/farm.rs +++ b/src/farm.rs @@ -11,6 +11,7 @@ pub enum SlotType { } #[cw_serde] +#[derive(Eq, Hash)] pub enum PlantType { Sunflower, Wheat, diff --git a/src/msg.rs b/src/msg.rs index 90c7b1d..71e25c6 100644 --- a/src/msg.rs +++ b/src/msg.rs @@ -53,6 +53,9 @@ pub enum QueryMsg { // Returns a specific users farm profile from state via query #[returns(FarmProfileDto)] GetFarmProfile { address: String }, + + #[returns(Vec<(u64, String)>)] + Leaderboard {}, } // === RESPONSES === diff --git a/src/state.rs b/src/state.rs index 7d7353d..d9a532d 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,5 +1,7 @@ +use std::collections::HashMap; + use cosmwasm_schema::cw_serde; -use cw_storage_plus::{Item, Map}; +use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, MultiIndex}; use crate::{ farm::{KomplePlant, Plant, PlantType, Slot, SlotType}, @@ -85,6 +87,50 @@ pub struct FarmProfileDto { pub const FARM_PROFILES: Map<&str, FarmProfile> = Map::new("farm_profiles"); pub const INFORMATION: Item = Item::new("info"); +#[cw_serde] +pub struct Points { + pub addr: String, + pub plants: HashMap, +} + +impl Points { + pub fn add(&mut self, plant_type: PlantType) { + let plant_type_str = plant_type.to_string(); + let plant_points = self.plants.get(&plant_type_str).unwrap_or(&0); + self.plants.insert(plant_type_str, plant_points + 1); + } + + pub fn total(&self) -> u64 { + self.plants + .values() + .copied() + .reduce(|acc, next| acc + next) + .unwrap_or(0) + } +} + +pub struct PointsIndexes<'a> { + pub total: MultiIndex<'a, u64, Points, &'a str>, +} + +impl<'a> IndexList for PointsIndexes<'a> { + fn get_indexes( + &'_ self, + ) -> Box> + '_> { + let v: Vec<&dyn Index> = vec![&self.total]; + Box::new(v.into_iter()) + } +} + +pub fn points<'a>() -> IndexedMap<'a, &'a str, Points, PointsIndexes<'a>> { + IndexedMap::new( + "points", + PointsIndexes { + total: MultiIndex::new(|_k, t| t.total(), "points", "points_total"), + }, + ) +} + fn create_meadow_plot(block: u64) -> Slot { return Slot { r#type: SlotType::Meadow, @@ -273,7 +319,7 @@ impl FarmProfile { Ok(()) } - pub fn harvest(&mut self, x: usize, y: usize, block: u64) -> Result<(), ContractError> { + pub fn harvest(&mut self, x: usize, y: usize, block: u64) -> Result { let plot = self.get_plot(x, y); match plot.plant { @@ -291,7 +337,7 @@ impl FarmProfile { self.set_plot(x, y, create_field_plot(block)); - Ok(()) + Ok(plant.r#type) } } }