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(OwnershipTransferred {
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 };
166
167 #[test]
168 fn init() {
169 let (env, ownable, ownable_2step, deployer) = setup_owned();
171
172 assert_eq!(deployer, ownable.get_owner());
174 assert_eq!(deployer, ownable_2step.get_owner());
175 let event = OwnershipTransferred {
178 previous_owner: None,
179 new_owner: Some(deployer)
180 };
181
182 env.emitted_event(ownable.address(), &event);
183 env.emitted_event(ownable_2step.address(), &event);
184 }
185
186 #[test]
187 fn plain_ownership_transfer() {
188 let (mut contract, initial_owner) = setup_ownable();
190
191 let new_owner = contract.env().get_account(1);
193 contract.transfer_ownership(&new_owner);
194
195 assert_eq!(new_owner, contract.get_owner());
197 contract.env().emitted_event(
199 contract.address(),
200 &OwnershipTransferred {
201 previous_owner: Some(initial_owner),
202 new_owner: Some(new_owner)
203 }
204 );
205 }
206
207 #[test]
208 fn two_step_ownership_transfer() {
209 let (mut contract, initial_owner) = setup_ownable_2_step();
211
212 let new_owner = contract.env().get_account(1);
214 contract.transfer_ownership(&new_owner);
215
216 contract.env().set_caller(new_owner);
218 contract.accept_ownership();
219
220 assert_eq!(new_owner, contract.get_owner());
222 assert_eq!(None, contract.get_pending_owner());
224 contract.env().emitted_event(
226 contract.address(),
227 &OwnershipTransferStarted {
228 previous_owner: Some(initial_owner),
229 new_owner: Some(new_owner)
230 }
231 );
232 contract.env().emitted_event(
233 contract.address(),
234 &OwnershipTransferred {
235 previous_owner: Some(initial_owner),
236 new_owner: Some(new_owner)
237 }
238 );
239 }
240
241 #[test]
242 fn failing_plain_ownership_transfer() {
243 let (mut contract, _) = setup_ownable();
245
246 let (caller, new_owner) = (contract.env().get_account(1), contract.env().get_account(2));
248 contract.env().set_caller(caller);
249
250 let err = contract.try_transfer_ownership(&new_owner).unwrap_err();
252 assert_eq!(err, Error::CallerNotTheOwner.into());
253 }
254
255 #[test]
256 fn failing_two_step_transfer() {
257 let (mut contract, initial_owner) = setup_ownable_2_step();
259
260 let (caller, new_owner) = (contract.env().get_account(1), contract.env().get_account(2));
262 contract.env().set_caller(caller);
263
264 let err = contract.try_transfer_ownership(&new_owner).unwrap_err();
266 assert_eq!(err, Error::CallerNotTheOwner.into());
267
268 contract.env().set_caller(initial_owner);
270 contract.transfer_ownership(&new_owner);
271
272 assert_eq!(contract.get_pending_owner(), Some(new_owner));
274
275 let err = contract.try_accept_ownership().unwrap_err();
278 assert_eq!(err, Error::CallerNotTheNewOwner.into());
279
280 assert_eq!(contract.get_owner(), initial_owner);
282 assert_eq!(contract.get_pending_owner(), Some(new_owner));
284 }
285
286 #[test]
287 fn renounce_ownership() {
288 let (mut contracts, initial_owner) = setup_renounceable();
290
291 contracts
292 .iter_mut()
293 .for_each(|contract: &mut RenounceableHostRef| {
294 contract.renounce_ownership();
296
297 contract.env().emitted_event(
299 contract.address(),
300 &OwnershipTransferred {
301 previous_owner: Some(initial_owner),
302 new_owner: None
303 }
304 );
305 let err = contract.try_get_owner().unwrap_err();
307 assert_eq!(err, Error::OwnerNotSet.into());
308 let err = contract.try_renounce_ownership().unwrap_err();
310 assert_eq!(err, Error::CallerNotTheOwner.into());
311 });
312 }
313
314 #[test]
315 fn renounce_ownership_fail() {
316 let (mut contracts, _) = setup_renounceable();
318
319 contracts.iter_mut().for_each(|contract| {
320 let caller = contract.env().get_account(1);
322 contract.env().set_caller(caller);
323
324 let err = contract.try_renounce_ownership().unwrap_err();
326 assert_eq!(err, Error::CallerNotTheOwner.into());
327 });
328 }
329
330 #[external_contract]
331 trait Owned {
332 fn get_owner(&self) -> Address;
333 }
334
335 #[external_contract]
336 trait Renounceable {
337 fn renounce_ownership(&mut self);
338 fn get_owner(&self) -> Address;
339 }
340
341 fn setup_ownable() -> (OwnableHostRef, Address) {
342 let env = odra_test::env();
343 (
344 Ownable::deploy(
345 &env,
346 OwnableInitArgs {
347 owner: env.get_account(0)
348 }
349 ),
350 env.get_account(0)
351 )
352 }
353
354 fn setup_ownable_2_step() -> (Ownable2StepHostRef, Address) {
355 let env = odra_test::env();
356 (
357 Ownable2Step::deploy(
358 &env,
359 Ownable2StepInitArgs {
360 owner: env.get_account(0)
361 }
362 ),
363 env.get_account(0)
364 )
365 }
366
367 fn setup_renounceable() -> (Vec<RenounceableHostRef>, Address) {
368 let env = odra_test::env();
369 let owner = env.caller();
370 let ownable = Ownable::deploy(&env, OwnableInitArgs { owner });
371 let ownable_2_step = Ownable2Step::deploy(&env, Ownable2StepInitArgs { owner });
372 let renouncable_ref = RenounceableHostRef::new(*ownable.address(), env.clone());
373 let renouncable_2_step_ref =
374 RenounceableHostRef::new(*ownable_2_step.address(), env.clone());
375 (
376 vec![renouncable_ref, renouncable_2_step_ref],
377 env.get_account(0)
378 )
379 }
380
381 fn setup_owned() -> (HostEnv, OwnableHostRef, Ownable2StepHostRef, Address) {
382 let env = odra_test::env();
383 let owner = env.caller();
384 let ownable = Ownable::deploy(&env, OwnableInitArgs { owner });
385 let ownable_2_step = Ownable2Step::deploy(&env, Ownable2StepInitArgs { owner });
386 (env.clone(), ownable, ownable_2_step, env.get_account(0))
387 }
388}