Skip to main content

cougr_core/standards/
ownable.rs

1use soroban_sdk::{contracttype, Address, Env, Symbol};
2
3use super::error::StandardsError;
4
5const OWNER_PREFIX: &str = "std_owner";
6const PENDING_OWNER_PREFIX: &str = "std_powner";
7
8/// OpenZeppelin-style single-owner control primitive.
9#[derive(Clone, Debug)]
10pub struct Ownable {
11    id: Symbol,
12}
13
14/// Two-step ownership handoff built on top of [`Ownable`].
15#[derive(Clone, Debug)]
16pub struct Ownable2Step {
17    inner: Ownable,
18}
19
20#[contracttype]
21#[derive(Clone, Debug, Eq, PartialEq)]
22pub struct OwnershipTransferredEvent {
23    pub previous_owner: Option<Address>,
24    pub new_owner: Option<Address>,
25}
26
27#[contracttype]
28#[derive(Clone, Debug, Eq, PartialEq)]
29pub struct OwnershipTransferStartedEvent {
30    pub owner: Address,
31    pub pending_owner: Address,
32}
33
34#[contracttype]
35#[derive(Clone, Debug, Eq, PartialEq)]
36pub struct OwnershipTransferCancelledEvent {
37    pub owner: Address,
38    pub pending_owner: Address,
39}
40
41impl Ownable {
42    pub fn new(id: Symbol) -> Self {
43        Self { id }
44    }
45
46    pub fn initialize(
47        &self,
48        env: &Env,
49        owner: &Address,
50    ) -> Result<OwnershipTransferredEvent, StandardsError> {
51        if self.owner(env).is_some() {
52            return Err(StandardsError::AlreadyInitialized);
53        }
54
55        env.storage().persistent().set(&self.owner_key(env), owner);
56        env.storage()
57            .persistent()
58            .remove(&self.pending_owner_key(env));
59
60        Ok(OwnershipTransferredEvent {
61            previous_owner: None,
62            new_owner: Some(owner.clone()),
63        })
64    }
65
66    pub fn owner(&self, env: &Env) -> Option<Address> {
67        env.storage().persistent().get(&self.owner_key(env))
68    }
69
70    pub fn pending_owner(&self, env: &Env) -> Option<Address> {
71        env.storage().persistent().get(&self.pending_owner_key(env))
72    }
73
74    pub fn require_owner(&self, env: &Env, caller: &Address) -> Result<(), StandardsError> {
75        let owner = self.owner(env).ok_or(StandardsError::OwnerNotSet)?;
76        if owner != *caller {
77            return Err(StandardsError::Unauthorized);
78        }
79        Ok(())
80    }
81
82    pub fn transfer_ownership(
83        &self,
84        env: &Env,
85        caller: &Address,
86        new_owner: &Address,
87    ) -> Result<OwnershipTransferredEvent, StandardsError> {
88        self.require_owner(env, caller)?;
89        let previous_owner = self.owner(env);
90        env.storage()
91            .persistent()
92            .set(&self.owner_key(env), new_owner);
93        env.storage()
94            .persistent()
95            .remove(&self.pending_owner_key(env));
96
97        Ok(OwnershipTransferredEvent {
98            previous_owner,
99            new_owner: Some(new_owner.clone()),
100        })
101    }
102
103    pub fn renounce_ownership(
104        &self,
105        env: &Env,
106        caller: &Address,
107    ) -> Result<OwnershipTransferredEvent, StandardsError> {
108        self.require_owner(env, caller)?;
109        let previous_owner = self.owner(env);
110        env.storage().persistent().remove(&self.owner_key(env));
111        env.storage()
112            .persistent()
113            .remove(&self.pending_owner_key(env));
114
115        Ok(OwnershipTransferredEvent {
116            previous_owner,
117            new_owner: None,
118        })
119    }
120
121    fn owner_key(&self, env: &Env) -> (Symbol, Symbol) {
122        (Symbol::new(env, OWNER_PREFIX), self.id.clone())
123    }
124
125    fn pending_owner_key(&self, env: &Env) -> (Symbol, Symbol) {
126        (Symbol::new(env, PENDING_OWNER_PREFIX), self.id.clone())
127    }
128}
129
130impl Ownable2Step {
131    pub fn new(id: Symbol) -> Self {
132        Self {
133            inner: Ownable::new(id),
134        }
135    }
136
137    pub fn initialize(
138        &self,
139        env: &Env,
140        owner: &Address,
141    ) -> Result<OwnershipTransferredEvent, StandardsError> {
142        self.inner.initialize(env, owner)
143    }
144
145    pub fn owner(&self, env: &Env) -> Option<Address> {
146        self.inner.owner(env)
147    }
148
149    pub fn pending_owner(&self, env: &Env) -> Option<Address> {
150        self.inner.pending_owner(env)
151    }
152
153    pub fn require_owner(&self, env: &Env, caller: &Address) -> Result<(), StandardsError> {
154        self.inner.require_owner(env, caller)
155    }
156
157    pub fn begin_transfer(
158        &self,
159        env: &Env,
160        caller: &Address,
161        pending_owner: &Address,
162    ) -> Result<OwnershipTransferStartedEvent, StandardsError> {
163        self.inner.require_owner(env, caller)?;
164        env.storage()
165            .persistent()
166            .set(&self.inner.pending_owner_key(env), pending_owner);
167
168        Ok(OwnershipTransferStartedEvent {
169            owner: caller.clone(),
170            pending_owner: pending_owner.clone(),
171        })
172    }
173
174    pub fn accept_transfer(
175        &self,
176        env: &Env,
177        caller: &Address,
178    ) -> Result<OwnershipTransferredEvent, StandardsError> {
179        let pending_owner = self
180            .pending_owner(env)
181            .ok_or(StandardsError::PendingOwnerNotSet)?;
182        if pending_owner != *caller {
183            return Err(StandardsError::PendingOwnerMismatch);
184        }
185
186        let previous_owner = self.owner(env);
187        env.storage()
188            .persistent()
189            .set(&self.inner.owner_key(env), caller);
190        env.storage()
191            .persistent()
192            .remove(&self.inner.pending_owner_key(env));
193
194        Ok(OwnershipTransferredEvent {
195            previous_owner,
196            new_owner: Some(caller.clone()),
197        })
198    }
199
200    pub fn cancel_transfer(
201        &self,
202        env: &Env,
203        caller: &Address,
204    ) -> Result<OwnershipTransferCancelledEvent, StandardsError> {
205        self.inner.require_owner(env, caller)?;
206        let pending_owner = self
207            .pending_owner(env)
208            .ok_or(StandardsError::PendingOwnerNotSet)?;
209        env.storage()
210            .persistent()
211            .remove(&self.inner.pending_owner_key(env));
212
213        Ok(OwnershipTransferCancelledEvent {
214            owner: caller.clone(),
215            pending_owner,
216        })
217    }
218}