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, Lock, Nonce, PublicKey, Result,
12 accounts::{AccountEvent, PublicKeyEvent},
13 engine::{State, StateView},
14 events::DefuseEvent,
15 intents::{MaybeIntentEvent, account::SetAuthByPredecessorId},
16};
17
18use defuse_serde_utils::base64::AsBase64;
19
20use near_sdk::{
21 AccountId, AccountIdRef, BorshStorageKey, FunctionError, IntoStorageKey, assert_one_yocto, env,
22 near, store::IterableMap,
23};
24
25use crate::{
26 accounts::AccountManager,
27 contract::{Contract, ContractExt, accounts::AccountEntry, prefix::NestPrefix},
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
68 self.set_auth_by_predecessor_id_and_emit_event(
69 &self.ensure_auth_predecessor_id(),
70 false,
71 false,
72 )
73 .unwrap_or_else(|err| err.panic());
74 }
75}
76
77impl Contract {
78 #[inline]
79 pub fn ensure_auth_predecessor_id(&self) -> AccountId {
80 let predecessor_account_id = env::predecessor_account_id();
81 if !StateView::is_auth_by_predecessor_id_enabled(self, &predecessor_account_id) {
82 DefuseError::AuthByPredecessorIdDisabled(predecessor_account_id).panic();
83 }
84 predecessor_account_id
85 }
86
87 pub fn set_auth_by_predecessor_id_and_emit_event(
91 &mut self,
92 account_id: &AccountIdRef,
93 enable: bool,
94 force: bool,
95 ) -> Result<bool> {
96 let toggled = self.internal_set_auth_by_predecessor_id(account_id, enable, force)?;
97
98 if toggled {
99 DefuseEvent::SetAuthByPredecessorId(MaybeIntentEvent::new_fn_call(AccountEvent::new(
100 Cow::Borrowed(account_id),
101 Cow::Owned(SetAuthByPredecessorId { enabled: enable }),
102 )))
103 .emit();
104 }
105
106 Ok(toggled)
107 }
108
109 pub(crate) fn internal_set_auth_by_predecessor_id(
112 &mut self,
113 account_id: &AccountIdRef,
114 enable: bool,
115 force: bool,
116 ) -> Result<bool> {
117 if enable {
118 let Some(account) = self.accounts.get_mut(account_id) else {
119 return Ok(false);
122 };
123 account
124 } else {
125 self.accounts.get_or_create(account_id.into())
126 }
127 .get_mut_maybe_forced(force)
128 .ok_or_else(|| DefuseError::AccountLocked(account_id.into()))
129 .map(|account| account.set_auth_by_predecessor_id(enable))
130 }
131
132 pub fn add_public_key_and_emit_event(
133 &mut self,
134 account_id: &AccountIdRef,
135 public_key: PublicKey,
136 ) {
137 State::add_public_key(self, account_id.into(), public_key)
138 .unwrap_or_else(|err| err.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)
155 .unwrap_or_else(|err| err.panic());
156
157 DefuseEvent::PublicKeyRemoved(MaybeIntentEvent::new_fn_call(AccountEvent::new(
158 Cow::Borrowed(account_id),
159 PublicKeyEvent {
160 public_key: Cow::Borrowed(&public_key),
161 },
162 )))
163 .emit();
164 }
165}
166
167#[cfg_attr(feature = "abi", derive(::borsh::BorshSchema))]
168#[derive(Debug, BorshSerialize, BorshDeserialize)]
169pub struct Accounts {
170 accounts: IterableMap<AccountId, AccountEntry>,
171 prefix: Vec<u8>,
172}
173
174impl Accounts {
175 #[inline]
176 pub fn new<S>(prefix: S) -> Self
177 where
178 S: IntoStorageKey,
179 {
180 let prefix = prefix.into_storage_key();
181
182 Self {
183 accounts: IterableMap::new(prefix.as_slice().nest(AccountsPrefix::Accounts)),
184 prefix,
185 }
186 }
187
188 #[inline]
189 pub fn get(&self, account_id: &AccountIdRef) -> Option<&Lock<Account>> {
190 self.accounts.get(account_id).map(|a| &**a)
191 }
192
193 #[inline]
194 pub fn get_mut(&mut self, account_id: &AccountIdRef) -> Option<&mut Lock<Account>> {
195 self.accounts.get_mut(account_id).map(|a| &mut **a)
196 }
197
198 #[inline]
201 pub fn get_or_create(&mut self, account_id: AccountId) -> &mut Lock<Account> {
202 self.accounts
203 .entry(account_id)
204 .or_insert_with_key(|account_id| {
205 Lock::unlocked(Account::new(
206 self.prefix
207 .as_slice()
208 .nest(AccountsPrefix::Account(account_id)),
209 account_id,
210 ))
211 .into()
212 })
213 }
214}
215
216#[derive(BorshSerialize, BorshStorageKey)]
217#[borsh(crate = "::near_sdk::borsh")]
218enum AccountsPrefix<'a> {
219 Accounts,
220 Account(&'a AccountIdRef),
221}