andromeda_std/ado_base/
permissioning.rs

1use core::fmt;
2
3use cosmwasm_schema::cw_serde;
4use cosmwasm_std::Env;
5
6use crate::{amp::AndrAddr, common::MillisecondsExpiration, error::ContractError};
7
8#[cw_serde]
9pub enum PermissioningMessage {
10    SetPermission {
11        actor: AndrAddr,
12        action: String,
13        permission: Permission,
14    },
15    RemovePermission {
16        action: String,
17        actor: AndrAddr,
18    },
19    PermissionAction {
20        action: String,
21    },
22    DisableActionPermissioning {
23        action: String,
24    },
25}
26
27#[cw_serde]
28pub struct PermissionInfo {
29    pub permission: Permission,
30    pub action: String,
31    pub actor: String,
32}
33
34#[cw_serde]
35pub struct PermissionedActionsResponse {
36    pub actions: Vec<String>,
37}
38
39/// An enum to represent a user's permission for an action
40///
41/// - **Blacklisted** - The user cannot perform the action until after the provided expiration
42/// - **Limited** - The user can perform the action while uses are remaining and before the provided expiration **for a permissioned action**
43/// - **Whitelisted** - The user can perform the action until the provided expiration **for a permissioned action**
44///
45/// Expiration defaults to `Never` if not provided
46#[cw_serde]
47pub enum Permission {
48    Blacklisted(Option<MillisecondsExpiration>),
49    Limited {
50        expiration: Option<MillisecondsExpiration>,
51        uses: u32,
52    },
53    Whitelisted(Option<MillisecondsExpiration>),
54}
55
56impl std::default::Default for Permission {
57    fn default() -> Self {
58        Self::Whitelisted(None)
59    }
60}
61
62impl Permission {
63    pub fn blacklisted(expiration: Option<MillisecondsExpiration>) -> Self {
64        Self::Blacklisted(expiration)
65    }
66
67    pub fn whitelisted(expiration: Option<MillisecondsExpiration>) -> Self {
68        Self::Whitelisted(expiration)
69    }
70
71    pub fn limited(expiration: Option<MillisecondsExpiration>, uses: u32) -> Self {
72        Self::Limited { expiration, uses }
73    }
74
75    pub fn is_permissioned(&self, env: &Env, strict: bool) -> bool {
76        match self {
77            Self::Blacklisted(expiration) => {
78                if let Some(expiration) = expiration {
79                    if expiration.is_expired(&env.block) {
80                        return true;
81                    }
82                }
83                false
84            }
85            Self::Limited { expiration, uses } => {
86                if let Some(expiration) = expiration {
87                    if expiration.is_expired(&env.block) {
88                        return !strict;
89                    }
90                }
91                if *uses == 0 {
92                    return !strict;
93                }
94                true
95            }
96            Self::Whitelisted(expiration) => {
97                if let Some(expiration) = expiration {
98                    if expiration.is_expired(&env.block) {
99                        return !strict;
100                    }
101                }
102                true
103            }
104        }
105    }
106
107    pub fn get_expiration(&self) -> MillisecondsExpiration {
108        match self {
109            Self::Blacklisted(expiration) => expiration.unwrap_or_default(),
110            Self::Limited { expiration, .. } => expiration.unwrap_or_default(),
111            Self::Whitelisted(expiration) => expiration.unwrap_or_default(),
112        }
113    }
114
115    pub fn consume_use(&mut self) -> Result<(), ContractError> {
116        if let Self::Limited { uses, .. } = self {
117            if let Some(remaining_uses) = uses.checked_sub(1) {
118                *uses = remaining_uses;
119                Ok(())
120            } else {
121                Err(ContractError::Underflow {})
122            }
123        } else {
124            Ok(())
125        }
126    }
127}
128
129impl fmt::Display for Permission {
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131        let self_as_string = match self {
132            Self::Blacklisted(expiration) => {
133                if let Some(expiration) = expiration {
134                    format!("blacklisted:{expiration}")
135                } else {
136                    "blacklisted".to_string()
137                }
138            }
139            Self::Limited { expiration, uses } => {
140                if let Some(expiration) = expiration {
141                    format!("limited:{expiration}:{uses}")
142                } else {
143                    format!("limited:{uses}")
144                }
145            }
146            Self::Whitelisted(expiration) => {
147                if let Some(expiration) = expiration {
148                    format!("whitelisted:{expiration}")
149                } else {
150                    "whitelisted".to_string()
151                }
152            }
153        };
154        write!(f, "{self_as_string}")
155    }
156}