Skip to content

Commit

Permalink
implement MultiIndex
Browse files Browse the repository at this point in the history
  • Loading branch information
TropicalDog17 committed Oct 14, 2023
1 parent 4187c33 commit dead656
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 40 deletions.
91 changes: 63 additions & 28 deletions src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
#[cfg(not(feature = "library"))]
use cosmwasm_std::entry_point;
use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, Uint128, Addr};
use cosmwasm_std::{
to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Order, Response, StdResult, Uint128,
};
use cw2::set_contract_version;
// use cw2::set_contract_version;

use crate::error::ContractError;
use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg, SocialResponse};
use crate::state::{Platform, ProfileId, SocialInfo, ADDRESS_TO_SOCIAL, OWNER, SOCIAL_TO_ADDRESS, ACCEPTED_TOKEN};
use cw20_base::contract::{execute_transfer, execute_send};
use crate::state::{Platform, ProfileId, SocialInfo, UserInfo, ACCEPTED_TOKEN, OWNER, USER_INFOS};
use cw20_base::contract::{execute_send, execute_transfer};
const CONTRACT_NAME: &str = "cosmos:donex-sm";
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

Expand Down Expand Up @@ -39,7 +41,7 @@ pub fn execute(
social_info,
address,
} => submit_social_link(deps, info, social_info, address),
Donate {recipient, amount} => donate(deps, env, info, recipient, amount),
Donate { recipient, amount } => donate(deps, env, info, recipient, amount),
}
}

Expand All @@ -63,51 +65,81 @@ pub fn submit_social_link(
if info.sender != OWNER.load(deps.storage)? {
return Err(ContractError::Unauthorized {});
}
let social_to_address = SOCIAL_TO_ADDRESS.load(deps.storage, social_info.clone());
let address_to_social = ADDRESS_TO_SOCIAL.load(deps.storage, info.sender.clone());
let user_info = UserInfo {
address: address.clone(),
platform_id: social_info.0,
profile_id: social_info.1,
};
USER_INFOS.save(deps.storage, address.as_ref(), &user_info)?;

if social_to_address.is_ok() {
return Err(ContractError::SocialAlreadyLinked {});
}
if address_to_social.is_ok() {
return Err(ContractError::AddressAlreadyLinked {});
}
SOCIAL_TO_ADDRESS.save(deps.storage, social_info.clone(), &address)?;
ADDRESS_TO_SOCIAL.save(deps.storage, address, &social_info)?;
Ok(Response::new().add_attribute("method", "submit_social_link"))
}

pub fn donate(mut deps: DepsMut, env: Env, info: MessageInfo, recipient: Addr, amount: Uint128) -> Result<Response, ContractError>{
pub fn donate(
mut deps: DepsMut,
env: Env,
info: MessageInfo,
recipient: Addr,
amount: Uint128,
) -> Result<Response, ContractError> {
const FEE_RATIO: u64 = 5;
let accepted_token = ACCEPTED_TOKEN.load(deps.storage)?;
let _owner = OWNER.load(deps.storage);
let denom = accepted_token.first().unwrap();
let donation = cw_utils::must_pay(&info, &denom)?.u128();
let donation = cw_utils::must_pay(&info, denom)?.u128();

// Calculate fee and actual amount receive
let fee = donation / FEE_RATIO as u128 * 100;
let fee = donation / FEE_RATIO as u128 * 100;
let actual = donation - fee;

execute_transfer(deps.branch(), env.clone(), info.clone(), recipient.to_string(), actual.into())?;
let msg =format!("Handling donation from {} to {}", info.sender.to_string(), recipient.to_string());
execute_send(deps.branch(), env.clone(), info.clone(), env.contract.address.to_string(), amount, cosmwasm_std::Binary(msg.into_bytes()))?;
execute_transfer(
deps.branch(),
env.clone(),
info.clone(),
recipient.to_string(),
actual.into(),
)?;
let msg = format!("Handling donation from {} to {}", info.sender, recipient);
execute_send(
deps.branch(),
env.clone(),
info.clone(),
env.contract.address.to_string(),
amount,
cosmwasm_std::Binary(msg.into_bytes()),
)?;
Ok(Response::new())
}
}
fn query_by_social_link(
deps: Deps,
profile_id: ProfileId,
platform: Platform,
) -> StdResult<SocialResponse> {
let social_info = (platform, profile_id);
let address = SOCIAL_TO_ADDRESS.load(deps.storage, social_info.clone())?;
Ok(SocialResponse { address })
// Query by (platform, profile_id)
let user_infos: Vec<_> = USER_INFOS
.idx
.social_info
.prefix(social_info)
.range(deps.storage, None, None, Order::Ascending)
.flatten()
.collect();

if user_infos.is_empty() {
return Ok(SocialResponse { address: vec![] });
}
let addresses = user_infos
.iter()
.map(|user_info| user_info.1.address.clone())
.collect::<Vec<_>>();
Ok(SocialResponse { address: addresses })
}

#[cfg(test)]
mod tests {
use super::*;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{from_binary, Addr, Empty};
use cosmwasm_std::{from_binary, Addr};
use cw_multi_test::{App, ContractWrapper, Executor};
#[test]
fn submit_social() {
Expand All @@ -134,7 +166,7 @@ mod tests {
addr,
&ExecuteMsg::SubmitSocial {
social_info: ("twitter".to_string(), "123".to_string()),
address: Addr::unchecked("abcde"),
address: Addr::unchecked("abc"),
},
&[],
)
Expand Down Expand Up @@ -172,7 +204,10 @@ mod tests {
platform: "twitter".to_string(),
},
);
assert!(resp.is_err());
assert!(resp.is_ok());
let resp: SocialResponse = from_binary(&resp.unwrap()).unwrap();

assert_eq!(resp, SocialResponse { address: vec![] });
// execute
let resp = execute(
deps.as_mut(),
Expand All @@ -189,16 +224,16 @@ mod tests {
deps.as_ref(),
env,
QueryMsg::GetSocial {
profile_id: "123".to_string(),
platform: "twitter".to_string(),
profile_id: "123".to_string(),
},
)
.unwrap();
let resp: SocialResponse = from_binary(&resp).unwrap();
assert_eq!(
resp,
SocialResponse {
address: Addr::unchecked("abc")
address: vec![Addr::unchecked("abc")]
}
);
}
Expand Down
9 changes: 6 additions & 3 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use cosmwasm_std::StdError;
use thiserror::Error;
use cw_utils::PaymentError;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ContractError {
#[error("{0}")]
Expand Down Expand Up @@ -50,10 +50,12 @@ pub enum ContractError {
#[error("Duplicate initial balance addresses")]
DuplicateInitialBalanceAddresses {},

#[error("Key already exists")]
Claimed {},
// Add any other custom errors you like here.
// Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details.
}

#[allow(deprecated)]
impl From<cw20_base::ContractError> for ContractError {
fn from(err: cw20_base::ContractError) -> Self {
match err {
Expand All @@ -63,6 +65,7 @@ impl From<cw20_base::ContractError> for ContractError {
ContractError::CannotSetOwnAccount {}
}
cw20_base::ContractError::InvalidExpiration {} => ContractError::InvalidExpiration {},
#[allow(clippy::all)]
cw20_base::ContractError::InvalidZeroAmount {} => ContractError::InvalidZeroAmount {},
cw20_base::ContractError::Expired {} => ContractError::Expired {},
cw20_base::ContractError::NoAllowance {} => ContractError::NoAllowance {},
Expand All @@ -78,4 +81,4 @@ impl From<cw20_base::ContractError> for ContractError {
}
}
}
}
}
8 changes: 4 additions & 4 deletions src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ pub enum ExecuteMsg {
social_info: SocialInfo,
address: Addr,
},
Donate{
Donate {
recipient: Addr,
amount: Uint128
}
amount: Uint128,
},
}

#[cw_serde]
Expand All @@ -29,5 +29,5 @@ pub enum QueryMsg {
}
#[cw_serde]
pub struct SocialResponse {
pub address: Addr,
pub address: Vec<Addr>,
}
40 changes: 35 additions & 5 deletions src/state.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,41 @@
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Addr;
use cw_storage_plus::{Item, Map};

use cw_storage_plus::{Index, IndexList, IndexedMap, Item, MultiIndex};
pub type SocialInfo = (Platform, ProfileId);
pub type Platform = String;
pub type ProfileId = String;
pub const SOCIAL_TO_ADDRESS: Map<SocialInfo, Addr> = Map::new("social_to_address");
pub const ADDRESS_TO_SOCIAL: Map<Addr, SocialInfo> = Map::new("wallet_to_social");
pub struct InfoIndexes<'a> {
pub address: MultiIndex<'a, Addr, UserInfo, String>,
pub social_info: MultiIndex<'a, (String, String), UserInfo, String>,
}
impl<'a> IndexList<UserInfo> for InfoIndexes<'a> {
fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<UserInfo>> + '_> {
let v: Vec<&dyn Index<UserInfo>> = vec![&self.address, &self.social_info];
Box::new(v.into_iter())
}
}
pub const fn infos<'a>() -> IndexedMap<'a, &'a str, UserInfo, InfoIndexes<'a>> {
let indexes = InfoIndexes {
address: MultiIndex::new(
|_pk: &[u8], d: &UserInfo| d.address.clone(),
"infos",
"infos__address",
),
social_info: MultiIndex::new(
|_pk: &[u8], d: &UserInfo| (d.platform_id.clone(), d.profile_id.clone()),
"infos",
"infos__social_info",
),
};
IndexedMap::new("infos", indexes)
}
#[cw_serde]
pub struct UserInfo {
pub address: Addr,
pub platform_id: String,
pub profile_id: String,
}
pub const USER_INFOS: IndexedMap<&str, UserInfo, InfoIndexes> = infos();

pub const OWNER: Item<Addr> = Item::new("owner");
pub const ACCEPTED_TOKEN: Item<Vec<String>> = Item::new("accepted_token");

0 comments on commit dead656

Please sign in to comment.