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, owner: Address) {
26 self.unchecked_transfer_ownership(Some(owner));
27 }
28
29 pub fn transfer_ownership(&mut self, new_owner: &Address) {
32 self.assert_owner(&self.env().caller());
33 self.unchecked_transfer_ownership(Some(*new_owner));
34 }
35
36 pub fn renounce_ownership(&mut self) {
43 self.assert_owner(&self.env().caller());
44 self.unchecked_transfer_ownership(None);
45 }
46
47 pub fn get_owner(&self) -> Address {
49 self.get_optional_owner()
50 .unwrap_or_revert_with(&self.env(), Error::OwnerNotSet)
51 }
52
53 pub fn assert_owner(&self, address: &Address) {
56 if Some(address) != self.get_optional_owner().as_ref() {
57 self.env().revert(Error::CallerNotTheOwner)
58 }
59 }
60
61 pub fn get_optional_owner(&self) -> Option<Address> {
63 self.owner.get().flatten()
64 }
65
66 pub fn unchecked_transfer_ownership(&mut self, new_owner: Option<Address>) {
69 let previous_owner = self.get_optional_owner();
70 self.owner.set(new_owner);
71
72 self.env().emit_event(OwnershipTransferred {
73 previous_owner,
74 new_owner
75 });
76 }
77}
78
79#[odra::module(events = [OwnershipTransferStarted], errors = Error)]
91pub struct Ownable2Step {
92 ownable: SubModule<Ownable>,
93 pending_owner: Var<Option<Address>>
94}
95
96#[odra::module]
97impl Ownable2Step {
98 pub fn init(&mut self, owner: Address) {
100 self.ownable.init(owner);
101 }
102
103 pub fn get_owner(&self) -> Address {
105 self.ownable.get_owner()
106 }
107
108 pub fn get_pending_owner(&self) -> Option<Address> {
110 self.pending_owner.get().flatten()
111 }
112
113 pub fn transfer_ownership(&mut self, new_owner: &Address) {
118 self.ownable.assert_owner(&self.env().caller());
119
120 let previous_owner = self.ownable.get_optional_owner();
121 let new_owner = Some(*new_owner);
122 self.pending_owner.set(new_owner);
123 self.env().emit_event(OwnershipTransferStarted {
124 previous_owner,
125 new_owner
126 });
127 }
128
129 pub fn renounce_ownership(&mut self) {
136 self.ownable.renounce_ownership()
137 }
138
139 pub fn accept_ownership(&mut self) {
142 let caller = self.env().caller();
143 let caller = Some(caller);
144 let pending_owner = self.pending_owner.get().flatten();
145 if pending_owner != caller {
146 self.env().revert(Error::CallerNotTheNewOwner)
147 }
148 self.pending_owner.set(None);
149 self.ownable.unchecked_transfer_ownership(caller);
150 }
151
152 pub fn assert_owner(&self, address: &Address) {
154 self.ownable.assert_owner(address)
155 }
156}
157
158#[cfg(test)]
159mod test {
160 use super::*;
161 use crate::access::errors::Error;
162 use odra::{
163 external_contract,
164 host::{Deployer, HostEnv, HostRef},
165 prelude::Addressable
166 };
167
168 #[test]
169 fn init() {
170 let (env, ownable, ownable_2step, deployer) = setup_owned();
172
173 assert_eq!(deployer, ownable.get_owner());
175 assert_eq!(deployer, ownable_2step.get_owner());
176 env.emitted_event(
179 &ownable,
180 OwnershipTransferred {
181 previous_owner: None,
182 new_owner: Some(deployer)
183 }
184 );
185 env.emitted_event(
186 &ownable_2step,
187 OwnershipTransferred {
188 previous_owner: None,
189 new_owner: Some(deployer)
190 }
191 );
192 }
193
194 #[test]
195 fn plain_ownership_transfer() {
196 let (mut contract, initial_owner) = setup_ownable();
198
199 let new_owner = contract.env().get_account(1);
201 contract.transfer_ownership(&new_owner);
202
203 assert_eq!(new_owner, contract.get_owner());
205 assert!(contract.env().emitted_event(
207 &contract,
208 OwnershipTransferred {
209 previous_owner: Some(initial_owner),
210 new_owner: Some(new_owner)
211 }
212 ));
213 }
214
215 #[test]
216 fn two_step_ownership_transfer() {
217 let (mut contract, initial_owner) = setup_ownable_2_step();
219
220 let new_owner = contract.env().get_account(1);
222 contract.transfer_ownership(&new_owner);
223
224 contract.env().set_caller(new_owner);
226 contract.accept_ownership();
227
228 assert_eq!(new_owner, contract.get_owner());
230 assert_eq!(None, contract.get_pending_owner());
232 assert!(contract.env().emitted_event(
234 &contract,
235 OwnershipTransferStarted {
236 previous_owner: Some(initial_owner),
237 new_owner: Some(new_owner)
238 }
239 ));
240 assert!(contract.env().emitted_event(
241 &contract,
242 OwnershipTransferred {
243 previous_owner: Some(initial_owner),
244 new_owner: Some(new_owner)
245 }
246 ));
247 }
248
249 #[test]
250 fn failing_plain_ownership_transfer() {
251 let (mut contract, _) = setup_ownable();
253
254 let (caller, new_owner) = (contract.env().get_account(1), contract.env().get_account(2));
256 contract.env().set_caller(caller);
257
258 let err = contract.try_transfer_ownership(&new_owner).unwrap_err();
260 assert_eq!(err, Error::CallerNotTheOwner.into());
261 }
262
263 #[test]
264 fn failing_two_step_transfer() {
265 let (mut contract, initial_owner) = setup_ownable_2_step();
267
268 let (caller, new_owner) = (contract.env().get_account(1), contract.env().get_account(2));
270 contract.env().set_caller(caller);
271
272 let err = contract.try_transfer_ownership(&new_owner).unwrap_err();
274 assert_eq!(err, Error::CallerNotTheOwner.into());
275
276 contract.env().set_caller(initial_owner);
278 contract.transfer_ownership(&new_owner);
279
280 assert_eq!(contract.get_pending_owner(), Some(new_owner));
282
283 let err = contract.try_accept_ownership().unwrap_err();
286 assert_eq!(err, Error::CallerNotTheNewOwner.into());
287
288 assert_eq!(contract.get_owner(), initial_owner);
290 assert_eq!(contract.get_pending_owner(), Some(new_owner));
292 }
293
294 #[test]
295 fn renounce_ownership() {
296 let (mut contracts, initial_owner) = setup_renounceable();
298
299 contracts
300 .iter_mut()
301 .for_each(|contract: &mut RenounceableHostRef| {
302 contract.renounce_ownership();
304
305 assert!(contract.env().emitted_event(
307 contract,
308 OwnershipTransferred {
309 previous_owner: Some(initial_owner),
310 new_owner: None
311 }
312 ));
313 let err = contract.try_get_owner().unwrap_err();
315 assert_eq!(err, Error::OwnerNotSet.into());
316 let err = contract.try_renounce_ownership().unwrap_err();
318 assert_eq!(err, Error::CallerNotTheOwner.into());
319 });
320 }
321
322 #[test]
323 fn renounce_ownership_fail() {
324 let (mut contracts, _) = setup_renounceable();
326
327 contracts.iter_mut().for_each(|contract| {
328 let caller = contract.env().get_account(1);
330 contract.env().set_caller(caller);
331
332 let err = contract.try_renounce_ownership().unwrap_err();
334 assert_eq!(err, Error::CallerNotTheOwner.into());
335 });
336 }
337
338 #[external_contract]
339 trait Owned {
340 fn get_owner(&self) -> Address;
341 }
342
343 #[external_contract]
344 trait Renounceable {
345 fn renounce_ownership(&mut self);
346 fn get_owner(&self) -> Address;
347 }
348
349 fn setup_ownable() -> (OwnableHostRef, Address) {
350 let env = odra_test::env();
351 (
352 Ownable::deploy(
353 &env,
354 OwnableInitArgs {
355 owner: env.get_account(0)
356 }
357 ),
358 env.get_account(0)
359 )
360 }
361
362 fn setup_ownable_2_step() -> (Ownable2StepHostRef, Address) {
363 let env = odra_test::env();
364 (
365 Ownable2Step::deploy(
366 &env,
367 Ownable2StepInitArgs {
368 owner: env.get_account(0)
369 }
370 ),
371 env.get_account(0)
372 )
373 }
374
375 fn setup_renounceable() -> (Vec<RenounceableHostRef>, Address) {
376 let env = odra_test::env();
377 let owner = env.caller();
378 let ownable = Ownable::deploy(&env, OwnableInitArgs { owner });
379 let ownable_2_step = Ownable2Step::deploy(&env, Ownable2StepInitArgs { owner });
380 let renouncable_ref = RenounceableHostRef::new(ownable.address(), env.clone());
381 let renouncable_2_step_ref =
382 RenounceableHostRef::new(ownable_2_step.address(), env.clone());
383 (
384 vec![renouncable_ref, renouncable_2_step_ref],
385 env.get_account(0)
386 )
387 }
388
389 fn setup_owned() -> (HostEnv, OwnableHostRef, Ownable2StepHostRef, Address) {
390 let env = odra_test::env();
391 let owner = env.caller();
392 let ownable = Ownable::deploy(&env, OwnableInitArgs { owner });
393 let ownable_2_step = Ownable2Step::deploy(&env, Ownable2StepInitArgs { owner });
394 (env.clone(), ownable, ownable_2_step, env.get_account(0))
395 }
396}