defuse/contract/accounts/
force.rs

1use std::collections::{HashMap, HashSet};
2
3use defuse_core::{
4    DefuseError, Result, accounts::AccountEvent, crypto::PublicKey, engine::StateView,
5    events::DefuseEvent,
6};
7use defuse_near_utils::Lock;
8use near_plugins::{AccessControllable, access_control_any};
9use near_sdk::{AccountId, assert_one_yocto, near};
10
11use crate::{
12    accounts::ForceAccountManager,
13    contract::{Contract, ContractExt, Role},
14};
15
16#[near]
17impl ForceAccountManager for Contract {
18    fn is_account_locked(&self, account_id: &AccountId) -> bool {
19        StateView::is_account_locked(self, account_id)
20    }
21
22    #[access_control_any(roles(
23        Role::DAO,
24        Role::UnrestrictedAccountLocker,
25        Role::UnrestrictedAccountManager
26    ))]
27    #[payable]
28    fn force_lock_account(&mut self, account_id: AccountId) -> bool {
29        assert_one_yocto();
30        let locked = self
31            .accounts
32            .get_or_create(account_id.clone())
33            .lock()
34            .is_some();
35        if locked {
36            DefuseEvent::AccountLocked(AccountEvent::new(account_id, ())).emit();
37        }
38        locked
39    }
40
41    #[access_control_any(roles(
42        Role::DAO,
43        Role::UnrestrictedAccountUnlocker,
44        Role::UnrestrictedAccountManager
45    ))]
46    #[payable]
47    fn force_unlock_account(&mut self, account_id: &AccountId) -> bool {
48        assert_one_yocto();
49        let unlocked = self
50            .accounts
51            .get_mut(account_id)
52            .and_then(Lock::unlock)
53            .is_some();
54        if unlocked {
55            DefuseEvent::AccountUnlocked(AccountEvent::new(account_id, ())).emit();
56        }
57        unlocked
58    }
59
60    #[access_control_any(roles(
61        Role::DAO,
62        Role::UnrestrictedAccountLocker,
63        Role::UnrestrictedAccountManager
64    ))]
65    #[payable]
66    fn force_disable_auth_by_predecessor_ids(&mut self, account_ids: Vec<AccountId>) {
67        assert_one_yocto();
68
69        for account_id in account_ids {
70            // NOTE: omit errors
71            let _ = self.internal_set_auth_by_predecessor_id(&account_id, false, true);
72        }
73    }
74
75    #[access_control_any(roles(
76        Role::DAO,
77        Role::UnrestrictedAccountUnlocker,
78        Role::UnrestrictedAccountManager
79    ))]
80    #[payable]
81    fn force_enable_auth_by_predecessor_ids(&mut self, account_ids: Vec<AccountId>) {
82        assert_one_yocto();
83
84        for account_id in account_ids {
85            // NOTE: omit errors
86            let _ = self.internal_set_auth_by_predecessor_id(&account_id, true, true);
87        }
88    }
89
90    #[access_control_any(roles(Role::DAO, Role::UnrestrictedAccountManager))]
91    #[payable]
92    fn force_add_public_keys(&mut self, public_keys: HashMap<AccountId, HashSet<PublicKey>>) {
93        assert_one_yocto();
94
95        for (account_id, pks) in public_keys {
96            for pk in pks {
97                self.add_public_key_and_emit_event(account_id.as_ref(), pk);
98            }
99        }
100    }
101
102    #[access_control_any(roles(Role::DAO, Role::UnrestrictedAccountManager))]
103    #[payable]
104    fn force_remove_public_keys(&mut self, public_keys: HashMap<AccountId, HashSet<PublicKey>>) {
105        assert_one_yocto();
106
107        for (account_id, pks) in public_keys {
108            for pk in pks {
109                self.remove_public_key_and_emit_event(account_id.as_ref(), pk);
110            }
111        }
112    }
113}
114
115impl Contract {
116    pub(crate) fn internal_set_auth_by_predecessor_id(
117        &mut self,
118        account_id: &AccountId,
119        enable: bool,
120        force: bool,
121    ) -> Result<bool> {
122        if enable {
123            let Some(account) = self.accounts.get_mut(account_id) else {
124                // no need to create an account: not-yet-existing accounts
125                // have auth by PREDECESSOR_ID enabled by default
126                return Ok(true);
127            };
128            account
129        } else {
130            self.accounts.get_or_create(account_id.clone())
131        }
132        .get_mut_maybe_forced(force)
133        .ok_or_else(|| DefuseError::AccountLocked(account_id.clone()))
134        .map(|account| account.set_auth_by_predecessor_id(account_id, enable))
135    }
136}