use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use cosmwasm_std::{
attr, CanonicalAddr, Deps, DepsMut, HandleResponse, HumanAddr, MessageInfo, StdError, StdResult,
};
use cw0::maybe_canonical;
use cw_storage_plus::Item;
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
pub struct AdminResponse {
pub admin: Option<HumanAddr>,
}
#[derive(Error, Debug, PartialEq)]
pub enum AdminError {
#[error("{0}")]
Std(#[from] StdError),
#[error("Caller is not admin")]
NotAdmin {},
}
pub struct Admin<'a>(Item<'a, Option<CanonicalAddr>>);
impl<'a> Admin<'a> {
pub const fn new(namespace: &'a str) -> Self {
Admin(Item::new(namespace))
}
pub fn set(&self, deps: DepsMut, admin: Option<HumanAddr>) -> StdResult<()> {
let admin_raw = maybe_canonical(deps.api, admin)?;
self.0.save(deps.storage, &admin_raw)
}
pub fn get(&self, deps: Deps) -> StdResult<Option<HumanAddr>> {
let canon = self.0.load(deps.storage)?;
canon.map(|c| deps.api.human_address(&c)).transpose()
}
pub fn is_admin(&self, deps: Deps, caller: &HumanAddr) -> StdResult<bool> {
match self.0.load(deps.storage)? {
Some(owner) => {
let caller_raw = deps.api.canonical_address(caller)?;
Ok(caller_raw == owner)
}
None => Ok(false),
}
}
pub fn assert_admin(&self, deps: Deps, caller: &HumanAddr) -> Result<(), AdminError> {
if !self.is_admin(deps, caller)? {
Err(AdminError::NotAdmin {})
} else {
Ok(())
}
}
pub fn handle_update_admin(
&self,
deps: DepsMut,
info: MessageInfo,
new_admin: Option<HumanAddr>,
) -> Result<HandleResponse, AdminError> {
self.assert_admin(deps.as_ref(), &info.sender)?;
let admin_str = match new_admin.as_ref() {
Some(admin) => admin.to_string(),
None => "None".to_string(),
};
let attributes = vec![
attr("action", "update_admin"),
attr("admin", admin_str),
attr("sender", info.sender),
];
self.set(deps, new_admin)?;
Ok(HandleResponse {
messages: vec![],
attributes,
data: None,
})
}
pub fn query_admin(&self, deps: Deps) -> StdResult<AdminResponse> {
let admin = self.get(deps)?;
Ok(AdminResponse { admin })
}
}
#[cfg(test)]
mod tests {
use super::*;
use cosmwasm_std::testing::{mock_dependencies, mock_info};
#[test]
fn set_and_get_admin() {
let mut deps = mock_dependencies(&[]);
let control = Admin::new("foo");
let admin = Some(HumanAddr::from("admin"));
control.set(deps.as_mut(), admin.clone()).unwrap();
let got = control.get(deps.as_ref()).unwrap();
assert_eq!(admin, got);
control.set(deps.as_mut(), None).unwrap();
let got = control.get(deps.as_ref()).unwrap();
assert_eq!(None, got);
}
#[test]
fn admin_checks() {
let mut deps = mock_dependencies(&[]);
let control = Admin::new("foo");
let owner = HumanAddr::from("big boss");
let imposter = HumanAddr::from("imposter");
control.set(deps.as_mut(), Some(owner.clone())).unwrap();
assert_eq!(true, control.is_admin(deps.as_ref(), &owner).unwrap());
assert_eq!(false, control.is_admin(deps.as_ref(), &imposter).unwrap());
control.assert_admin(deps.as_ref(), &owner).unwrap();
let err = control.assert_admin(deps.as_ref(), &imposter).unwrap_err();
assert_eq!(AdminError::NotAdmin {}, err);
control.set(deps.as_mut(), None).unwrap();
assert_eq!(false, control.is_admin(deps.as_ref(), &owner).unwrap());
assert_eq!(false, control.is_admin(deps.as_ref(), &imposter).unwrap());
let err = control.assert_admin(deps.as_ref(), &owner).unwrap_err();
assert_eq!(AdminError::NotAdmin {}, err);
let err = control.assert_admin(deps.as_ref(), &imposter).unwrap_err();
assert_eq!(AdminError::NotAdmin {}, err);
}
#[test]
fn test_handle_query() {
let mut deps = mock_dependencies(&[]);
let control = Admin::new("foo");
let owner = HumanAddr::from("big boss");
let imposter = HumanAddr::from("imposter");
let friend = HumanAddr::from("buddy");
control.set(deps.as_mut(), Some(owner.clone())).unwrap();
let res = control.query_admin(deps.as_ref()).unwrap();
assert_eq!(Some(owner.clone()), res.admin);
let info = mock_info(&imposter, &[]);
let new_admin = Some(friend.clone());
let err = control
.handle_update_admin(deps.as_mut(), info, new_admin.clone())
.unwrap_err();
assert_eq!(AdminError::NotAdmin {}, err);
let info = mock_info(&owner, &[]);
let res = control
.handle_update_admin(deps.as_mut(), info, new_admin)
.unwrap();
assert_eq!(0, res.messages.len());
let res = control.query_admin(deps.as_ref()).unwrap();
assert_eq!(Some(friend.clone()), res.admin);
}
}