pfc_whitelist/
lib.rs

1use std::collections::HashSet;
2
3use cosmwasm_schema::cw_serde;
4use cosmwasm_std::{Addr, Api, Order, StdError, StdResult, Storage};
5use cw_storage_plus::{Bound, Map};
6
7pub const DEFAULT_LIMIT: u32 = 10;
8pub const MAX_LIMIT: u32 = 30;
9
10#[cfg(test)]
11mod tests;
12
13#[cw_serde]
14pub struct Whitelist {
15    pub address: String,
16    pub reason: Option<String>,
17}
18/// Errors associated with the contract's ownership
19#[derive(thiserror::Error, Debug, PartialEq)]
20pub enum WhitelistError {
21    #[error("{0}")]
22    Std(#[from] StdError),
23    #[error("List Supplied to Whitelist is not unique")]
24    NotUnique(),
25    #[error("Whitelist entry {name} already exists")]
26    EntryExists {
27        name: String,
28    },
29    #[error("Whitelist entry {name} does not exist")]
30    EntryDoesntExist {
31        name: String,
32    },
33}
34const WHITELIST: Map<String, Option<String>> = Map::new("whitelist");
35
36#[cw_serde]
37pub struct WhitelistResponse<T> {
38    pub entries: Vec<T>,
39}
40
41pub fn initialize_whitelist(
42    storage: &mut dyn Storage,
43    api: &dyn Api,
44    addresses: Vec<Whitelist>,
45) -> Result<(), WhitelistError> {
46    let dupe_check: HashSet<String> = addresses.iter().map(|v| v.address.clone()).collect();
47    if dupe_check.len() != addresses.len() {
48        return Err(WhitelistError::NotUnique {});
49    }
50    for rec in addresses {
51        let addr = api.addr_validate(&rec.address)?;
52        WHITELIST.save(storage, addr.to_string(), &rec.reason)?;
53    }
54    Ok(())
55}
56pub fn add_entry(
57    storage: &mut dyn Storage,
58    api: &dyn Api,
59    address: String,
60    reason: Option<String>,
61) -> Result<(), WhitelistError> {
62    let addr = api.addr_validate(&address)?;
63    let entry_exists = WHITELIST.may_load(storage, address.clone())?;
64    if entry_exists.is_some() {
65        return Err(WhitelistError::EntryExists {
66            name: address,
67        });
68    }
69
70    WHITELIST.save(storage, addr.to_string(), &reason)?;
71
72    Ok(())
73}
74pub fn remove_entry(
75    storage: &mut dyn Storage,
76    api: &dyn Api,
77    name: String,
78) -> Result<(), WhitelistError> {
79    let addr = api.addr_validate(&name)?;
80    let entry_exists = WHITELIST.may_load(storage, addr.to_string())?;
81    if let Some(_entry) = entry_exists {
82        WHITELIST.remove(storage, addr.to_string());
83        Ok(())
84    } else {
85        Err(WhitelistError::EntryDoesntExist {
86            name,
87        })
88    }
89}
90pub fn query_entry(storage: &dyn Storage, address: String) -> StdResult<Option<Whitelist>> {
91    if let Some(reason) = WHITELIST.may_load(storage, address.clone())? {
92        Ok(Some(Whitelist {
93            address,
94            reason,
95        }))
96    } else {
97        Ok(None)
98    }
99}
100
101pub fn query_entries(
102    storage: &dyn Storage,
103    start_after: Option<String>,
104    limit: Option<u32>,
105) -> StdResult<WhitelistResponse<Whitelist>> {
106    let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize;
107    //    let start_addr = maybe_addr(deps.api, start_after)?;
108    let start = start_after.as_ref().map(Bound::exclusive);
109    let res = WHITELIST
110        .range(storage, start, None, Order::Ascending)
111        .take(limit)
112        .map(|x| {
113            x.map(|y| Whitelist {
114                address: y.0,
115                reason: y.1,
116            })
117        })
118        .collect::<StdResult<Vec<Whitelist>>>()?;
119    Ok(WhitelistResponse {
120        entries: res,
121    })
122}
123
124pub fn is_listed(storage: &dyn Storage, address: &Addr) -> StdResult<Option<Whitelist>> {
125    if let Some(entry) = WHITELIST.may_load(storage, address.to_string())? {
126        Ok(Some(Whitelist {
127            address: address.to_string(),
128            reason: entry,
129        }))
130    } else {
131        Ok(None)
132    }
133}
134pub fn assert_listed(storage: &dyn Storage, address: &Addr) -> Result<(), WhitelistError> {
135    if WHITELIST.may_load(storage, address.to_string())?.is_some() {
136        Ok(())
137    } else {
138        Err(WhitelistError::EntryDoesntExist {
139            name: address.to_string(),
140        })
141    }
142}