defuse/contract/accounts/
mod.rs

1mod account;
2mod force;
3mod state;
4
5pub use self::{account::*, state::*};
6
7use std::{borrow::Cow, collections::HashSet};
8
9use defuse_core::{
10    DefuseError, Nonce,
11    accounts::{AccountEvent, PublicKeyEvent},
12    crypto::PublicKey,
13    engine::{State, StateView},
14    events::DefuseEvent,
15};
16
17use defuse_near_utils::{Lock, NestPrefix, UnwrapOrPanic};
18use defuse_serde_utils::base64::AsBase64;
19
20use near_sdk::{
21    AccountId, AccountIdRef, BorshStorageKey, FunctionError, IntoStorageKey, assert_one_yocto,
22    borsh::BorshSerialize, env, near, store::IterableMap,
23};
24
25use crate::{
26    accounts::AccountManager,
27    contract::{Contract, ContractExt, accounts::AccountEntry},
28};
29
30#[near]
31impl AccountManager for Contract {
32    fn has_public_key(&self, account_id: &AccountId, public_key: &PublicKey) -> bool {
33        StateView::has_public_key(self, account_id, public_key)
34    }
35
36    fn public_keys_of(&self, account_id: &AccountId) -> HashSet<PublicKey> {
37        StateView::iter_public_keys(self, account_id).collect()
38    }
39
40    #[payable]
41    fn add_public_key(&mut self, public_key: PublicKey) {
42        assert_one_yocto();
43        let account_id = self.ensure_auth_predecessor_id();
44
45        self.add_public_key_and_emit_event(account_id.as_ref(), public_key);
46    }
47
48    #[payable]
49    fn remove_public_key(&mut self, public_key: PublicKey) {
50        assert_one_yocto();
51        let account_id = self.ensure_auth_predecessor_id();
52
53        self.remove_public_key_and_emit_event(account_id.as_ref(), public_key);
54    }
55
56    fn is_nonce_used(&self, account_id: &AccountId, nonce: AsBase64<Nonce>) -> bool {
57        StateView::is_nonce_used(self, account_id, nonce.into_inner())
58    }
59
60    fn is_auth_by_predecessor_id_enabled(&self, account_id: &AccountId) -> bool {
61        StateView::is_auth_by_predecessor_id_enabled(self, account_id)
62    }
63
64    #[payable]
65    fn disable_auth_by_predecessor_id(&mut self) {
66        assert_one_yocto();
67        State::set_auth_by_predecessor_id(self, self.ensure_auth_predecessor_id(), false)
68            .unwrap_or_panic();
69    }
70}
71
72impl Contract {
73    #[inline]
74    pub fn ensure_auth_predecessor_id(&self) -> AccountId {
75        let predecessor_account_id = env::predecessor_account_id();
76        if !StateView::is_auth_by_predecessor_id_enabled(self, &predecessor_account_id) {
77            DefuseError::AuthByPredecessorIdDisabled(predecessor_account_id).panic();
78        }
79        predecessor_account_id
80    }
81
82    pub fn add_public_key_and_emit_event(
83        &mut self,
84        account_id: &AccountIdRef,
85        public_key: PublicKey,
86    ) {
87        State::add_public_key(self, account_id.into(), public_key).unwrap_or_panic();
88
89        DefuseEvent::PublicKeyAdded(AccountEvent::new(
90            Cow::Borrowed(account_id),
91            PublicKeyEvent {
92                public_key: Cow::Borrowed(&public_key),
93            },
94        ))
95        .emit();
96    }
97
98    pub fn remove_public_key_and_emit_event(
99        &mut self,
100        account_id: &AccountIdRef,
101        public_key: PublicKey,
102    ) {
103        State::remove_public_key(self, account_id.into(), public_key).unwrap_or_panic();
104
105        DefuseEvent::PublicKeyRemoved(AccountEvent::new(
106            Cow::Borrowed(account_id),
107            PublicKeyEvent {
108                public_key: Cow::Borrowed(&public_key),
109            },
110        ))
111        .emit();
112    }
113}
114
115#[derive(Debug)]
116#[near(serializers = [borsh])]
117pub struct Accounts {
118    accounts: IterableMap<AccountId, AccountEntry>,
119    prefix: Vec<u8>,
120}
121
122impl Accounts {
123    #[inline]
124    pub fn new<S>(prefix: S) -> Self
125    where
126        S: IntoStorageKey,
127    {
128        let prefix = prefix.into_storage_key();
129
130        Self {
131            accounts: IterableMap::new(prefix.as_slice().nest(AccountsPrefix::Accounts)),
132            prefix,
133        }
134    }
135
136    #[inline]
137    pub fn get(&self, account_id: &AccountIdRef) -> Option<&Lock<Account>> {
138        self.accounts.get(account_id).map(|a| &**a)
139    }
140
141    #[inline]
142    pub fn get_mut(&mut self, account_id: &AccountIdRef) -> Option<&mut Lock<Account>> {
143        self.accounts.get_mut(account_id).map(|a| &mut **a)
144    }
145
146    /// Gets or creates an account with given `account_id`.
147    /// NOTE: The created account will be unblocked by default.
148    #[inline]
149    pub fn get_or_create(&mut self, account_id: AccountId) -> &mut Lock<Account> {
150        self.accounts
151            .entry(account_id)
152            .or_insert_with_key(|account_id| {
153                Lock::unlocked(Account::new(
154                    self.prefix
155                        .as_slice()
156                        .nest(AccountsPrefix::Account(account_id)),
157                    account_id,
158                ))
159                .into()
160            })
161    }
162}
163
164#[derive(BorshSerialize, BorshStorageKey)]
165#[borsh(crate = "::near_sdk::borsh")]
166enum AccountsPrefix<'a> {
167    Accounts,
168    Account(&'a AccountIdRef),
169}