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 borsh::{BorshDeserialize, BorshSerialize};
10use defuse_core::{
11 DefuseError, Nonce, PublicKey, Result,
12 accounts::{AccountEvent, PublicKeyEvent},
13 engine::{State, StateView},
14 events::DefuseEvent,
15 intents::{MaybeIntentEvent, account::SetAuthByPredecessorId},
16};
17
18use defuse_near_utils::{Lock, NestPrefix};
19use defuse_serde_utils::base64::AsBase64;
20
21use near_sdk::{
22 AccountId, AccountIdRef, BorshStorageKey, FunctionError, IntoStorageKey, assert_one_yocto, env,
23 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_else(|err| err.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)
139 .unwrap_or_else(|err| err.panic());
140
141 DefuseEvent::PublicKeyAdded(MaybeIntentEvent::new_fn_call(AccountEvent::new(
142 Cow::Borrowed(account_id),
143 PublicKeyEvent {
144 public_key: Cow::Borrowed(&public_key),
145 },
146 )))
147 .emit();
148 }
149
150 pub fn remove_public_key_and_emit_event(
151 &mut self,
152 account_id: &AccountIdRef,
153 public_key: PublicKey,
154 ) {
155 State::remove_public_key(self, account_id.into(), public_key)
156 .unwrap_or_else(|err| err.panic());
157
158 DefuseEvent::PublicKeyRemoved(MaybeIntentEvent::new_fn_call(AccountEvent::new(
159 Cow::Borrowed(account_id),
160 PublicKeyEvent {
161 public_key: Cow::Borrowed(&public_key),
162 },
163 )))
164 .emit();
165 }
166}
167
168#[cfg_attr(feature = "abi", derive(::borsh::BorshSchema))]
169#[derive(Debug, BorshSerialize, BorshDeserialize)]
170pub struct Accounts {
171 accounts: IterableMap<AccountId, AccountEntry>,
172 prefix: Vec<u8>,
173}
174
175impl Accounts {
176 #[inline]
177 pub fn new<S>(prefix: S) -> Self
178 where
179 S: IntoStorageKey,
180 {
181 let prefix = prefix.into_storage_key();
182
183 Self {
184 accounts: IterableMap::new(prefix.as_slice().nest(AccountsPrefix::Accounts)),
185 prefix,
186 }
187 }
188
189 #[inline]
190 pub fn get(&self, account_id: &AccountIdRef) -> Option<&Lock<Account>> {
191 self.accounts.get(account_id).map(|a| &**a)
192 }
193
194 #[inline]
195 pub fn get_mut(&mut self, account_id: &AccountIdRef) -> Option<&mut Lock<Account>> {
196 self.accounts.get_mut(account_id).map(|a| &mut **a)
197 }
198
199 #[inline]
202 pub fn get_or_create(&mut self, account_id: AccountId) -> &mut Lock<Account> {
203 self.accounts
204 .entry(account_id)
205 .or_insert_with_key(|account_id| {
206 Lock::unlocked(Account::new(
207 self.prefix
208 .as_slice()
209 .nest(AccountsPrefix::Account(account_id)),
210 account_id,
211 ))
212 .into()
213 })
214 }
215}
216
217#[derive(BorshSerialize, BorshStorageKey)]
218#[borsh(crate = "::near_sdk::borsh")]
219enum AccountsPrefix<'a> {
220 Accounts,
221 Account(&'a AccountIdRef),
222}