Skip to main content

ownable_std/
ownership.rs

1use cosmwasm_std::Addr;
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4
5/// Core ownable ownership metadata.
6#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
7pub struct OwnableInfo {
8    pub owner: Addr,
9    pub issuer: Addr,
10    pub ownable_type: Option<String>,
11}
12
13/// Response payload for ownable info queries.
14#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
15pub struct InfoResponse {
16    pub owner: Addr,
17    pub issuer: Addr,
18    pub nft: Option<crate::metadata::NFT>,
19    pub ownable_type: Option<String>,
20}
21
22/// Types that can provide an owner address for ownership checks.
23pub trait OwnerAddress {
24    fn owner_address(&self) -> &Addr;
25}
26
27impl OwnerAddress for Addr {
28    fn owner_address(&self) -> &Addr {
29        self
30    }
31}
32
33impl OwnerAddress for OwnableInfo {
34    fn owner_address(&self) -> &Addr {
35        &self.owner
36    }
37}
38
39/// Verifies that `sender` is the owner and returns a caller-provided unauthorized error otherwise.
40pub fn ensure_owner<T, E>(
41    owner: &T,
42    sender: &Addr,
43    unauthorized: impl FnOnce() -> E,
44) -> Result<(), E>
45where
46    T: OwnerAddress + ?Sized,
47{
48    if sender == owner.owner_address() {
49        Ok(())
50    } else {
51        Err(unauthorized())
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[derive(Debug, PartialEq)]
60    enum TestError {
61        Unauthorized(&'static str),
62    }
63
64    #[test]
65    fn ensure_owner_accepts_owner_addr() {
66        let owner = Addr::unchecked("owner");
67        let sender = Addr::unchecked("owner");
68        let result = ensure_owner(&owner, &sender, || TestError::Unauthorized("forbidden"));
69        assert_eq!(result, Ok(()));
70    }
71
72    #[test]
73    fn ensure_owner_rejects_non_owner_addr() {
74        let owner = Addr::unchecked("owner");
75        let sender = Addr::unchecked("not-owner");
76        let result = ensure_owner(&owner, &sender, || TestError::Unauthorized("forbidden"));
77        assert_eq!(result, Err(TestError::Unauthorized("forbidden")));
78    }
79
80    #[test]
81    fn ensure_owner_accepts_owner_struct() {
82        let ownable_info = OwnableInfo {
83            owner: Addr::unchecked("owner"),
84            issuer: Addr::unchecked("issuer"),
85            ownable_type: Some("basic".to_string()),
86        };
87        let sender = Addr::unchecked("owner");
88        let result = ensure_owner(&ownable_info, &sender, || {
89            TestError::Unauthorized("forbidden")
90        });
91        assert_eq!(result, Ok(()));
92    }
93}