odra_modules/access/
ownable.rs1use crate::access::errors::Error;
3use crate::access::events::{OwnershipTransferStarted, OwnershipTransferred};
4use odra::prelude::*;
5
6#[odra::module(events = [OwnershipTransferred], errors = Error)]
18pub struct Ownable {
19 owner: Var<Option<Address>>
20}
21
22#[odra::module]
23impl Ownable {
24 pub fn init(&mut self) {
26 let caller = self.env().caller();
27 let initial_owner = Some(caller);
28 self.unchecked_transfer_ownership(initial_owner);
29 }
30
31 pub fn transfer_ownership(&mut self, new_owner: &Address) {
34 self.assert_owner(&self.env().caller());
35 self.unchecked_transfer_ownership(Some(*new_owner));
36 }
37
38 pub fn renounce_ownership(&mut self) {
45 self.assert_owner(&self.env().caller());
46 self.unchecked_transfer_ownership(None);
47 }
48
49 pub fn get_owner(&self) -> Address {
51 self.get_optional_owner()
52 .unwrap_or_revert_with(&self.env(), Error::OwnerNotSet)
53 }
54
55 pub fn assert_owner(&self, address: &Address) {
58 if Some(address) != self.get_optional_owner().as_ref() {
59 self.env().revert(Error::CallerNotTheOwner)
60 }
61 }
62
63 pub fn get_optional_owner(&self) -> Option<Address> {
65 self.owner.get().flatten()
66 }
67
68 pub fn unchecked_transfer_ownership(&mut self, new_owner: Option<Address>) {
71 let previous_owner = self.get_optional_owner();
72 self.owner.set(new_owner);
73
74 self.env().emit_event(OwnershipTransferred {
75 previous_owner,
76 new_owner
77 });
78 }
79}
80
81#[odra::module(events = [OwnershipTransferStarted], errors = Error)]
93pub struct Ownable2Step {
94 ownable: SubModule<Ownable>,
95 pending_owner: Var<Option<Address>>
96}
97
98#[odra::module]
99impl Ownable2Step {
100 pub fn init(&mut self) {
102 self.ownable.init();
103 }
104
105 pub fn get_owner(&self) -> Address {
107 self.ownable.get_owner()
108 }
109
110 pub fn get_pending_owner(&self) -> Option<Address> {
112 self.pending_owner.get().flatten()
113 }
114
115 pub fn transfer_ownership(&mut self, new_owner: &Address) {
120 self.ownable.assert_owner(&self.env().caller());
121
122 let previous_owner = self.ownable.get_optional_owner();
123 let new_owner = Some(*new_owner);
124 self.pending_owner.set(new_owner);
125 self.env().emit_event(OwnershipTransferred {
126 previous_owner,
127 new_owner
128 });
129 }
130
131 pub fn renounce_ownership(&mut self) {
138 self.ownable.renounce_ownership()
139 }
140
141 pub fn accept_ownership(&mut self) {
144 let caller = self.env().caller();
145 let caller = Some(caller);
146 let pending_owner = self.pending_owner.get().flatten();
147 if pending_owner != caller {
148 self.env().revert(Error::CallerNotTheNewOwner)
149 }
150 self.pending_owner.set(None);
151 self.ownable.unchecked_transfer_ownership(caller);
152 }
153}
154
155#[cfg(test)]
156mod test {
157 use super::*;
158 use crate::access::errors::Error;
159 use odra::{
160 external_contract,
161 host::{Deployer, HostEnv, HostRef, NoArgs}
162 };
163
164 #[test]
165 fn init() {
166 let (env, ownable, ownable_2step, deployer) = setup_owned();
168
169 assert_eq!(deployer, ownable.get_owner());
171 assert_eq!(deployer, ownable_2step.get_owner());
172 let event = OwnershipTransferred {
175 previous_owner: None,
176 new_owner: Some(deployer)
177 };
178
179 env.emitted_event(ownable.address(), &event);
180 env.emitted_event(ownable_2step.address(), &event);
181 }
182
183 #[test]
184 fn plain_ownership_transfer() {
185 let (mut contract, initial_owner) = setup_ownable();
187
188 let new_owner = contract.env().get_account(1);
190 contract.transfer_ownership(&new_owner);
191
192 assert_eq!(new_owner, contract.get_owner());
194 contract.env().emitted_event(
196 contract.address(),
197 &OwnershipTransferred {
198 previous_owner: Some(initial_owner),
199 new_owner: Some(new_owner)
200 }
201 );
202 }
203
204 #[test]
205 fn two_step_ownership_transfer() {
206 let (mut contract, initial_owner) = setup_ownable_2_step();
208
209 let new_owner = contract.env().get_account(1);
211 contract.transfer_ownership(&new_owner);
212
213 contract.env().set_caller(new_owner);
215 contract.accept_ownership();
216
217 assert_eq!(new_owner, contract.get_owner());
219 assert_eq!(None, contract.get_pending_owner());
221 contract.env().emitted_event(
223 contract.address(),
224 &OwnershipTransferStarted {
225 previous_owner: Some(initial_owner),
226 new_owner: Some(new_owner)
227 }
228 );
229 contract.env().emitted_event(
230 contract.address(),
231 &OwnershipTransferred {
232 previous_owner: Some(initial_owner),
233 new_owner: Some(new_owner)
234 }
235 );
236 }
237
238 #[test]
239 fn failing_plain_ownership_transfer() {
240 let (mut contract, _) = setup_ownable();
242
243 let (caller, new_owner) = (contract.env().get_account(1), contract.env().get_account(2));
245 contract.env().set_caller(caller);
246
247 let err = contract.try_transfer_ownership(&new_owner).unwrap_err();
249 assert_eq!(err, Error::CallerNotTheOwner.into());
250 }
251
252 #[test]
253 fn failing_two_step_transfer() {
254 let (mut contract, initial_owner) = setup_ownable_2_step();
256
257 let (caller, new_owner) = (contract.env().get_account(1), contract.env().get_account(2));
259 contract.env().set_caller(caller);
260
261 let err = contract.try_transfer_ownership(&new_owner).unwrap_err();
263 assert_eq!(err, Error::CallerNotTheOwner.into());
264
265 contract.env().set_caller(initial_owner);
267 contract.transfer_ownership(&new_owner);
268
269 assert_eq!(contract.get_pending_owner(), Some(new_owner));
271
272 let err = contract.try_accept_ownership().unwrap_err();
275 assert_eq!(err, Error::CallerNotTheNewOwner.into());
276
277 assert_eq!(contract.get_owner(), initial_owner);
279 assert_eq!(contract.get_pending_owner(), Some(new_owner));
281 }
282
283 #[test]
284 fn renounce_ownership() {
285 let (mut contracts, initial_owner) = setup_renounceable();
287
288 contracts
289 .iter_mut()
290 .for_each(|contract: &mut RenounceableHostRef| {
291 contract.renounce_ownership();
293
294 contract.env().emitted_event(
296 contract.address(),
297 &OwnershipTransferred {
298 previous_owner: Some(initial_owner),
299 new_owner: None
300 }
301 );
302 let err = contract.try_get_owner().unwrap_err();
304 assert_eq!(err, Error::OwnerNotSet.into());
305 let err = contract.try_renounce_ownership().unwrap_err();
307 assert_eq!(err, Error::CallerNotTheOwner.into());
308 });
309 }
310
311 #[test]
312 fn renounce_ownership_fail() {
313 let (mut contracts, _) = setup_renounceable();
315
316 contracts.iter_mut().for_each(|contract| {
317 let caller = contract.env().get_account(1);
319 contract.env().set_caller(caller);
320
321 let err = contract.try_renounce_ownership().unwrap_err();
323 assert_eq!(err, Error::CallerNotTheOwner.into());
324 });
325 }
326
327 #[external_contract]
328 trait Owned {
329 fn get_owner(&self) -> Address;
330 }
331
332 #[external_contract]
333 trait Renounceable {
334 fn renounce_ownership(&mut self);
335 fn get_owner(&self) -> Address;
336 }
337
338 fn setup_ownable() -> (OwnableHostRef, Address) {
339 let env = odra_test::env();
340 (Ownable::deploy(&env, NoArgs), env.get_account(0))
341 }
342
343 fn setup_ownable_2_step() -> (Ownable2StepHostRef, Address) {
344 let env = odra_test::env();
345 (Ownable2Step::deploy(&env, NoArgs), env.get_account(0))
346 }
347
348 fn setup_renounceable() -> (Vec<RenounceableHostRef>, Address) {
349 let env = odra_test::env();
350 let ownable = Ownable::deploy(&env, NoArgs);
351 let ownable_2_step = Ownable2Step::deploy(&env, NoArgs);
352 let renouncable_ref = RenounceableHostRef::new(*ownable.address(), env.clone());
353 let renouncable_2_step_ref =
354 RenounceableHostRef::new(*ownable_2_step.address(), env.clone());
355 (
356 vec![renouncable_ref, renouncable_2_step_ref],
357 env.get_account(0)
358 )
359 }
360
361 fn setup_owned() -> (HostEnv, OwnableHostRef, Ownable2StepHostRef, Address) {
362 let env = odra_test::env();
363 let ownable = Ownable::deploy(&env, NoArgs);
364 let ownable_2_step = Ownable2Step::deploy(&env, NoArgs);
365 (env.clone(), ownable, ownable_2_step, env.get_account(0))
366 }
367}