1#![doc = include_str!("README.md")]
2
3pub use crate::objects::gov_type::{GovAction, GovernanceDetails};
4use crate::{objects::storage_namespaces::OWNERSHIP_STORAGE_KEY, AbstractError};
5
6use cosmwasm_std::{
7 Addr, Attribute, BlockInfo, CustomQuery, DepsMut, QuerierWrapper, StdError, StdResult, Storage,
8};
9use cw_address_like::AddressLike;
10use cw_storage_plus::Item;
11pub use cw_utils::Expiration;
12
13use super::nested_admin::query_top_level_owner;
14
15#[derive(thiserror::Error, Debug, PartialEq)]
17pub enum GovOwnershipError {
18 #[error(transparent)]
19 Std(#[from] StdError),
20
21 #[error(transparent)]
22 Abstract(#[from] AbstractError),
23
24 #[error("Contract ownership has been renounced")]
25 NoOwner,
26
27 #[error("Caller is not the contract's current owner")]
28 NotOwner,
29
30 #[error("Caller is not the contract's pending owner")]
31 NotPendingOwner,
32
33 #[error("There isn't a pending ownership transfer")]
34 TransferNotFound,
35
36 #[error("A pending ownership transfer exists but it has expired")]
37 TransferExpired,
38
39 #[error("Cannot transfer ownership to renounced structure. use action::renounce")]
40 TransferToRenounced,
41
42 #[error("Cannot change NFT ownership. Transfer the NFT to change account ownership.")]
43 ChangeOfNftOwned,
44}
45
46const OWNERSHIP: Item<Ownership<Addr>> = Item::new(OWNERSHIP_STORAGE_KEY);
48
49#[cosmwasm_schema::cw_serde]
51pub struct Ownership<T: AddressLike> {
52 pub owner: GovernanceDetails<T>,
54
55 pub pending_owner: Option<GovernanceDetails<T>>,
58
59 pub pending_expiry: Option<Expiration>,
63}
64
65impl<T: AddressLike> Ownership<T> {
66 pub fn into_attributes(self) -> Vec<Attribute> {
103 fn none_or<T: std::fmt::Display>(or: Option<&T>) -> String {
104 or.map_or_else(|| "none".to_string(), |or| or.to_string())
105 }
106 vec![
107 Attribute::new("owner", self.owner.to_string()),
108 Attribute::new("pending_owner", none_or(self.pending_owner.as_ref())),
109 Attribute::new("pending_expiry", none_or(self.pending_expiry.as_ref())),
110 ]
111 }
112
113 pub fn assert_owner_can_change(&self) -> Result<(), GovOwnershipError> {
115 if let GovernanceDetails::NFT { .. } = self.owner {
116 return Err(GovOwnershipError::ChangeOfNftOwned);
117 }
118
119 Ok(())
120 }
121}
122
123impl Ownership<Addr> {
124 fn assert_owner(
126 &self,
127 querier: &QuerierWrapper,
128 sender: &Addr,
129 ) -> Result<(), GovOwnershipError> {
130 let Some(current_owner) = &self.owner.owner_address(querier) else {
132 return Err(GovOwnershipError::NoOwner);
133 };
134
135 if sender != current_owner {
137 return Err(GovOwnershipError::NotOwner);
138 }
139
140 Ok(())
141 }
142
143 fn assert_nested_sender_can_change_owner(
145 &self,
146 querier: &QuerierWrapper,
147 sender: &Addr,
148 ) -> Result<(), GovOwnershipError> {
149 match &self.owner {
150 GovernanceDetails::SubAccount { account } => {
151 let top_level_owner = query_top_level_owner(querier, account.clone())?;
152 top_level_owner.assert_owner_can_change()?;
155
156 if self.assert_owner(querier, sender).is_err() {
159 top_level_owner.assert_owner(querier, sender)?
160 }
161 }
162 _ => {
163 self.assert_owner_can_change()?;
166
167 self.assert_owner(querier, sender)?;
169 }
170 }
171
172 Ok(())
173 }
174}
175
176pub fn initialize_owner(
180 deps: DepsMut,
181 owner: GovernanceDetails<String>,
182) -> Result<Ownership<Addr>, GovOwnershipError> {
183 let ownership = Ownership {
184 owner: owner.verify(deps.as_ref())?,
185 pending_owner: None,
186 pending_expiry: None,
187 };
188 OWNERSHIP.save(deps.storage, &ownership)?;
189 Ok(ownership)
190}
191
192pub fn is_owner(store: &dyn Storage, querier: &QuerierWrapper, addr: &Addr) -> StdResult<bool> {
197 let ownership = OWNERSHIP.load(store)?;
198
199 if let Some(owner) = ownership.owner.owner_address(querier) {
200 if *addr == owner {
201 return Ok(true);
202 }
203 }
204
205 Ok(false)
206}
207
208pub fn assert_nested_owner(
210 store: &dyn Storage,
211 querier: &QuerierWrapper,
212 sender: &Addr,
213) -> Result<(), GovOwnershipError> {
214 let ownership = OWNERSHIP.load(store)?;
215 let owner_assertion = ownership.assert_owner(querier, sender);
217 if owner_assertion.is_ok() {
218 #[cfg(feature = "xion")]
219 if let GovernanceDetails::AbstractAccount { .. } = ownership.owner {
221 if let Some(true) = crate::account::state::AUTH_ADMIN.may_load(store)? {
222 return Ok(());
223 } else {
224 return Err(crate::objects::ownership::GovOwnershipError::NotOwner);
225 }
226 }
227 return Ok(());
228 }
229 let top_level_ownership = if let GovernanceDetails::SubAccount { account } = ownership.owner {
231 query_top_level_owner(querier, account)?
232 } else {
233 return owner_assertion;
234 };
235 match top_level_ownership.assert_owner(querier, sender) {
237 Ok(_) => {
238 #[cfg(feature = "xion")]
239 if let GovernanceDetails::AbstractAccount { address } = top_level_ownership.owner {
241 if let Ok(true) = crate::account::state::AUTH_ADMIN.query(querier, address) {
242 return Ok(());
243 } else {
244 return Err(crate::objects::ownership::GovOwnershipError::NotOwner);
245 }
246 }
247 Ok(())
248 }
249 Err(e) => Err(e),
250 }
251}
252
253pub fn update_ownership(
256 deps: DepsMut,
257 block: &BlockInfo,
258 sender: &Addr,
259 action: GovAction,
260) -> Result<Ownership<Addr>, GovOwnershipError> {
261 match action {
262 GovAction::TransferOwnership { new_owner, expiry } => {
263 transfer_ownership(deps, sender, new_owner, expiry)
264 }
265 GovAction::AcceptOwnership => accept_ownership(deps.storage, &deps.querier, block, sender),
266 GovAction::RenounceOwnership => renounce_ownership(deps.storage, &deps.querier, sender),
267 }
268}
269
270pub fn get_ownership(storage: &dyn Storage) -> StdResult<Ownership<Addr>> {
272 OWNERSHIP.load(storage)
273}
274
275pub fn query_ownership<Q: CustomQuery>(
276 querier: &QuerierWrapper<Q>,
277 remote_contract: Addr,
278) -> StdResult<Ownership<Addr>> {
279 OWNERSHIP.query(querier, remote_contract)
280}
281
282fn transfer_ownership(
285 deps: DepsMut,
286 sender: &Addr,
287 new_owner: GovernanceDetails<String>,
288 expiry: Option<Expiration>,
289) -> Result<Ownership<Addr>, GovOwnershipError> {
290 let new_owner = new_owner.verify(deps.as_ref())?;
291
292 if new_owner.owner_address(&deps.querier).is_none() {
293 return Err(GovOwnershipError::TransferToRenounced {});
294 }
295
296 OWNERSHIP.update(deps.storage, |ownership| {
297 ownership.assert_nested_sender_can_change_owner(&deps.querier, sender)?;
299 Ok(Ownership {
311 pending_owner: Some(new_owner),
312 pending_expiry: expiry,
313 ..ownership
314 })
315 })
316}
317
318fn accept_ownership(
320 store: &mut dyn Storage,
321 querier: &QuerierWrapper,
322 block: &BlockInfo,
323 sender: &Addr,
324) -> Result<Ownership<Addr>, GovOwnershipError> {
325 OWNERSHIP.update(store, |ownership| {
326 let Some(maybe_pending_owner) = ownership.pending_owner else {
328 return Err(GovOwnershipError::TransferNotFound);
329 };
330
331 let Some(pending_owner) = maybe_pending_owner.owner_address(querier) else {
333 return Err(GovOwnershipError::TransferNotFound);
338 };
339
340 let is_pending_owner = if sender == pending_owner {
341 true
342 } else if let GovernanceDetails::SubAccount { account, .. } = &maybe_pending_owner {
343 query_top_level_owner(querier, account.clone())?
347 .owner
348 .owner_address(querier)
349 .map(|top_sender| top_sender == sender)
350 .unwrap_or_default()
351 } else {
352 false
353 };
354
355 if !is_pending_owner {
357 return Err(GovOwnershipError::NotPendingOwner);
358 }
359
360 if let Some(expiry) = &ownership.pending_expiry {
362 if expiry.is_expired(block) {
363 return Err(GovOwnershipError::TransferExpired);
364 }
365 }
366
367 Ok(Ownership {
368 owner: maybe_pending_owner,
369 pending_owner: None,
370 pending_expiry: None,
371 })
372 })
373}
374
375fn renounce_ownership(
377 store: &mut dyn Storage,
378 querier: &QuerierWrapper,
379 sender: &Addr,
380) -> Result<Ownership<Addr>, GovOwnershipError> {
381 OWNERSHIP.update(store, |ownership| {
382 ownership.assert_nested_sender_can_change_owner(querier, sender)?;
384
385 Ok(Ownership {
386 owner: GovernanceDetails::Renounced {},
387 pending_owner: None,
388 pending_expiry: None,
389 })
390 })
391}
392
393#[cfg(test)]
398mod tests {
399 use cosmwasm_std::{
400 testing::{mock_dependencies, MockApi},
401 Attribute, Timestamp,
402 };
403
404 use super::*;
405
406 fn mock_govs(mock_api: MockApi) -> [GovernanceDetails<Addr>; 3] {
407 [
408 GovernanceDetails::Monarchy {
409 monarch: mock_api.addr_make("larry"),
410 },
411 GovernanceDetails::Monarchy {
412 monarch: mock_api.addr_make("jake"),
413 },
414 GovernanceDetails::Monarchy {
415 monarch: mock_api.addr_make("pumpkin"),
416 },
417 ]
418 }
419
420 fn mock_block_at_height(height: u64) -> BlockInfo {
421 BlockInfo {
422 height,
423 time: Timestamp::from_seconds(10000),
424 chain_id: "".into(),
425 }
426 }
427
428 #[coverage_helper::test]
429 fn initializing_ownership() {
430 let mut deps = mock_dependencies();
431 let [larry, _, _] = mock_govs(deps.api);
432
433 let ownership = initialize_owner(deps.as_mut(), larry.clone().into()).unwrap();
434
435 assert_eq!(ownership, OWNERSHIP.load(deps.as_ref().storage).unwrap());
437
438 assert_eq!(
439 ownership,
440 Ownership {
441 owner: larry,
442 pending_owner: None,
443 pending_expiry: None,
444 },
445 );
446 }
447
448 #[coverage_helper::test]
449 fn initialize_ownership_no_owner() {
450 let mut deps = mock_dependencies();
451
452 let ownership = initialize_owner(deps.as_mut(), GovernanceDetails::Renounced {}).unwrap();
453 assert_eq!(
454 ownership,
455 Ownership {
456 owner: GovernanceDetails::Renounced {},
457 pending_owner: None,
458 pending_expiry: None,
459 },
460 );
461 }
462
463 #[coverage_helper::test]
464 fn asserting_ownership() {
465 let mut deps = mock_dependencies();
466 let [larry, jake, _] = mock_govs(deps.api);
467 let larry_address = larry.owner_address(&deps.as_ref().querier).unwrap();
468 let jake_address = jake.owner_address(&deps.as_ref().querier).unwrap();
469
470 {
472 initialize_owner(deps.as_mut(), larry.clone().into()).unwrap();
473
474 let res = assert_nested_owner(
475 deps.as_ref().storage,
476 &deps.as_ref().querier,
477 &larry_address,
478 );
479 assert!(res.is_ok());
480
481 let res =
482 assert_nested_owner(deps.as_ref().storage, &deps.as_ref().querier, &jake_address);
483 assert_eq!(res.unwrap_err(), GovOwnershipError::NotOwner);
484 }
485
486 {
488 let depsmut = deps.as_mut();
489 renounce_ownership(depsmut.storage, &depsmut.querier, &larry_address).unwrap();
490
491 let res = assert_nested_owner(
492 deps.as_ref().storage,
493 &deps.as_ref().querier,
494 &larry_address,
495 );
496 assert_eq!(res.unwrap_err(), GovOwnershipError::NoOwner);
497 }
498 }
499
500 #[coverage_helper::test]
501 fn transferring_ownership() {
502 let mut deps = mock_dependencies();
503 let [larry, jake, pumpkin] = mock_govs(deps.api);
504 let larry_address = larry.owner_address(&deps.as_ref().querier).unwrap();
505 let jake_address = jake.owner_address(&deps.as_ref().querier).unwrap();
506
507 initialize_owner(deps.as_mut(), larry.clone().into()).unwrap();
508
509 {
511 let depsmut = deps.as_mut();
512
513 let err = update_ownership(
514 depsmut,
515 &mock_block_at_height(12345),
516 &jake_address,
517 GovAction::TransferOwnership {
518 new_owner: pumpkin.clone().into(),
519 expiry: None,
520 },
521 )
522 .unwrap_err();
523 assert_eq!(err, GovOwnershipError::NotOwner);
524 }
525
526 {
528 let ownership = update_ownership(
529 deps.as_mut(),
530 &mock_block_at_height(12345),
531 &larry_address,
532 GovAction::TransferOwnership {
533 new_owner: pumpkin.clone().into(),
534 expiry: Some(Expiration::AtHeight(42069)),
535 },
536 )
537 .unwrap();
538 assert_eq!(
539 ownership,
540 Ownership {
541 owner: larry,
542 pending_owner: Some(pumpkin),
543 pending_expiry: Some(Expiration::AtHeight(42069)),
544 },
545 );
546
547 let saved_ownership = OWNERSHIP.load(deps.as_ref().storage).unwrap();
548 assert_eq!(saved_ownership, ownership);
549 }
550 }
551
552 #[coverage_helper::test]
553 fn accepting_ownership() {
554 let mut deps = mock_dependencies();
555 let [larry, jake, pumpkin] = mock_govs(deps.api);
556 let larry_address = larry.owner_address(&deps.as_ref().querier).unwrap();
557 let jake_address = jake.owner_address(&deps.as_ref().querier).unwrap();
558 let pumpkin_address = pumpkin.owner_address(&deps.as_ref().querier).unwrap();
559
560 initialize_owner(deps.as_mut(), larry.clone().into()).unwrap();
561
562 {
564 let err = update_ownership(
565 deps.as_mut(),
566 &mock_block_at_height(12345),
567 &pumpkin_address,
568 GovAction::AcceptOwnership,
569 )
570 .unwrap_err();
571 assert_eq!(err, GovOwnershipError::TransferNotFound);
572 }
573
574 transfer_ownership(
575 deps.as_mut(),
576 &larry_address,
577 pumpkin.clone().into(),
578 Some(Expiration::AtHeight(42069)),
579 )
580 .unwrap();
581
582 {
584 let err = update_ownership(
585 deps.as_mut(),
586 &mock_block_at_height(12345),
587 &jake_address,
588 GovAction::AcceptOwnership,
589 )
590 .unwrap_err();
591 assert_eq!(err, GovOwnershipError::NotPendingOwner);
592 }
593
594 {
596 let err = update_ownership(
597 deps.as_mut(),
598 &mock_block_at_height(69420),
599 &pumpkin_address,
600 GovAction::AcceptOwnership,
601 )
602 .unwrap_err();
603 assert_eq!(err, GovOwnershipError::TransferExpired);
604 }
605
606 {
608 let ownership = update_ownership(
609 deps.as_mut(),
610 &mock_block_at_height(10000),
611 &pumpkin_address,
612 GovAction::AcceptOwnership,
613 )
614 .unwrap();
615 assert_eq!(
616 ownership,
617 Ownership {
618 owner: pumpkin,
619 pending_owner: None,
620 pending_expiry: None,
621 },
622 );
623
624 let saved_ownership = OWNERSHIP.load(deps.as_ref().storage).unwrap();
625 assert_eq!(saved_ownership, ownership);
626 }
627 }
628
629 #[coverage_helper::test]
630 fn renouncing_ownership() {
631 let mut deps = mock_dependencies();
632 let [larry, jake, pumpkin] = mock_govs(deps.api);
633 let larry_address = larry.owner_address(&deps.as_ref().querier).unwrap();
634 let jake_address = jake.owner_address(&deps.as_ref().querier).unwrap();
635
636 let ownership = Ownership {
637 owner: larry.clone(),
638 pending_owner: Some(pumpkin),
639 pending_expiry: None,
640 };
641 OWNERSHIP.save(deps.as_mut().storage, &ownership).unwrap();
642
643 {
645 let err = update_ownership(
646 deps.as_mut(),
647 &mock_block_at_height(12345),
648 &jake_address,
649 GovAction::RenounceOwnership,
650 )
651 .unwrap_err();
652 assert_eq!(err, GovOwnershipError::NotOwner);
653 }
654
655 {
657 let ownership = update_ownership(
658 deps.as_mut(),
659 &mock_block_at_height(12345),
660 &larry_address,
661 GovAction::RenounceOwnership,
662 )
663 .unwrap();
664
665 assert_eq!(ownership, OWNERSHIP.load(deps.as_ref().storage).unwrap());
667
668 assert_eq!(
669 ownership,
670 Ownership {
671 owner: GovernanceDetails::Renounced {},
672 pending_owner: None,
673 pending_expiry: None,
674 },
675 );
676 }
677
678 {
680 let err = update_ownership(
681 deps.as_mut(),
682 &mock_block_at_height(12345),
683 &larry_address,
684 GovAction::RenounceOwnership,
685 )
686 .unwrap_err();
687 assert_eq!(err, GovOwnershipError::NoOwner);
688 }
689 }
690
691 #[coverage_helper::test]
692 fn into_attributes_works() {
693 use cw_utils::Expiration;
694 assert_eq!(
695 Ownership {
696 owner: GovernanceDetails::Monarchy {
697 monarch: "batman".to_string()
698 },
699 pending_owner: None,
700 pending_expiry: Some(Expiration::Never {})
701 }
702 .into_attributes(),
703 vec![
704 Attribute::new("owner", "monarch"),
705 Attribute::new("pending_owner", "none"),
706 Attribute::new("pending_expiry", "expiration: never")
707 ],
708 );
709 }
710}