defuse/contract/accounts/
mod.rs1mod 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, Result,
11 accounts::{AccountEvent, PublicKeyEvent},
12 crypto::PublicKey,
13 engine::{State, StateView},
14 events::DefuseEvent,
15 intents::{MaybeIntentEvent, account::SetAuthByPredecessorId},
16};
17
18use defuse_near_utils::{Lock, NestPrefix, UnwrapOrPanic};
19use defuse_serde_utils::base64::AsBase64;
20
21use near_sdk::{
22 AccountId, AccountIdRef, BorshStorageKey, FunctionError, IntoStorageKey, assert_one_yocto,
23 borsh::BorshSerialize, env, near, store::IterableMap,
24};
25
26use crate::{
27 accounts::AccountManager,
28 contract::{Contract, ContractExt, accounts::AccountEntry},
29};
30
31#[near]
32impl AccountManager for Contract {
33 fn has_public_key(&self, account_id: &AccountId, public_key: &PublicKey) -> bool {
34 StateView::has_public_key(self, account_id, public_key)
35 }
36
37 fn public_keys_of(&self, account_id: &AccountId) -> HashSet<PublicKey> {
38 StateView::iter_public_keys(self, account_id).collect()
39 }
40
41 #[payable]
42 fn add_public_key(&mut self, public_key: PublicKey) {
43 assert_one_yocto();
44 let account_id = self.ensure_auth_predecessor_id();
45
46 self.add_public_key_and_emit_event(account_id.as_ref(), public_key);
47 }
48
49 #[payable]
50 fn remove_public_key(&mut self, public_key: PublicKey) {
51 assert_one_yocto();
52 let account_id = self.ensure_auth_predecessor_id();
53
54 self.remove_public_key_and_emit_event(account_id.as_ref(), public_key);
55 }
56
57 fn is_nonce_used(&self, account_id: &AccountId, nonce: AsBase64<Nonce>) -> bool {
58 StateView::is_nonce_used(self, account_id, nonce.into_inner())
59 }
60
61 fn is_auth_by_predecessor_id_enabled(&self, account_id: &AccountId) -> bool {
62 StateView::is_auth_by_predecessor_id_enabled(self, account_id)
63 }
64
65 #[payable]
66 fn disable_auth_by_predecessor_id(&mut self) {
67 assert_one_yocto();
68
69 self.set_auth_by_predecessor_id_and_emit_event(
70 &self.ensure_auth_predecessor_id(),
71 false,
72 false,
73 )
74 .unwrap_or_panic();
75 }
76}
77
78impl Contract {
79 #[inline]
80 pub fn ensure_auth_predecessor_id(&self) -> AccountId {
81 let predecessor_account_id = env::predecessor_account_id();
82 if !StateView::is_auth_by_predecessor_id_enabled(self, &predecessor_account_id) {
83 DefuseError::AuthByPredecessorIdDisabled(predecessor_account_id).panic();
84 }
85 predecessor_account_id
86 }
87
88 pub fn set_auth_by_predecessor_id_and_emit_event(
92 &mut self,
93 account_id: &AccountIdRef,
94 enable: bool,
95 force: bool,
96 ) -> Result<bool> {
97 let toggled = self.internal_set_auth_by_predecessor_id(account_id, enable, force)?;
98
99 if toggled {
100 DefuseEvent::SetAuthByPredecessorId(MaybeIntentEvent::new_fn_call(AccountEvent::new(
101 Cow::Borrowed(account_id),
102 Cow::Owned(SetAuthByPredecessorId { enabled: enable }),
103 )))
104 .emit();
105 }
106
107 Ok(toggled)
108 }
109
110 pub(crate) fn internal_set_auth_by_predecessor_id(
113 &mut self,
114 account_id: &AccountIdRef,
115 enable: bool,
116 force: bool,
117 ) -> Result<bool> {
118 if enable {
119 let Some(account) = self.accounts.get_mut(account_id) else {
120 return Ok(false);
123 };
124 account
125 } else {
126 self.accounts.get_or_create(account_id.into())
127 }
128 .get_mut_maybe_forced(force)
129 .ok_or_else(|| DefuseError::AccountLocked(account_id.into()))
130 .map(|account| account.set_auth_by_predecessor_id(enable))
131 }
132
133 pub fn add_public_key_and_emit_event(
134 &mut self,
135 account_id: &AccountIdRef,
136 public_key: PublicKey,
137 ) {
138 State::add_public_key(self, account_id.into(), public_key).unwrap_or_panic();
139
140 DefuseEvent::PublicKeyAdded(MaybeIntentEvent::new_fn_call(AccountEvent::new(
141 Cow::Borrowed(account_id),
142 PublicKeyEvent {
143 public_key: Cow::Borrowed(&public_key),
144 },
145 )))
146 .emit();
147 }
148
149 pub fn remove_public_key_and_emit_event(
150 &mut self,
151 account_id: &AccountIdRef,
152 public_key: PublicKey,
153 ) {
154 State::remove_public_key(self, account_id.into(), public_key).unwrap_or_panic();
155
156 DefuseEvent::PublicKeyRemoved(MaybeIntentEvent::new_fn_call(AccountEvent::new(
157 Cow::Borrowed(account_id),
158 PublicKeyEvent {
159 public_key: Cow::Borrowed(&public_key),
160 },
161 )))
162 .emit();
163 }
164}
165
166#[derive(Debug)]
167#[near(serializers = [borsh])]
168pub struct Accounts {
169 accounts: IterableMap<AccountId, AccountEntry>,
170 prefix: Vec<u8>,
171}
172
173impl Accounts {
174 #[inline]
175 pub fn new<S>(prefix: S) -> Self
176 where
177 S: IntoStorageKey,
178 {
179 let prefix = prefix.into_storage_key();
180
181 Self {
182 accounts: IterableMap::new(prefix.as_slice().nest(AccountsPrefix::Accounts)),
183 prefix,
184 }
185 }
186
187 #[inline]
188 pub fn get(&self, account_id: &AccountIdRef) -> Option<&Lock<Account>> {
189 self.accounts.get(account_id).map(|a| &**a)
190 }
191
192 #[inline]
193 pub fn get_mut(&mut self, account_id: &AccountIdRef) -> Option<&mut Lock<Account>> {
194 self.accounts.get_mut(account_id).map(|a| &mut **a)
195 }
196
197 #[inline]
200 pub fn get_or_create(&mut self, account_id: AccountId) -> &mut Lock<Account> {
201 self.accounts
202 .entry(account_id)
203 .or_insert_with_key(|account_id| {
204 Lock::unlocked(Account::new(
205 self.prefix
206 .as_slice()
207 .nest(AccountsPrefix::Account(account_id)),
208 account_id,
209 ))
210 .into()
211 })
212 }
213}
214
215#[derive(BorshSerialize, BorshStorageKey)]
216#[borsh(crate = "::near_sdk::borsh")]
217enum AccountsPrefix<'a> {
218 Accounts,
219 Account(&'a AccountIdRef),
220}