defuse/contract/
mod.rs

1#[cfg(all(feature = "abi", not(target_arch = "wasm32")))]
2mod abi;
3mod accounts;
4mod admin;
5pub mod config;
6mod events;
7mod fees;
8mod garbage_collector;
9mod intents;
10mod salts;
11mod state;
12mod tokens;
13mod upgrade;
14mod versioned;
15
16use core::iter;
17
18use defuse_borsh_utils::adapters::As;
19use defuse_core::Result;
20use impl_tools::autoimpl;
21use near_plugins::{AccessControlRole, AccessControllable, Pausable, access_control};
22use near_sdk::{
23    BorshStorageKey, IntoStorageKey, PanicOnDefault, borsh::BorshDeserialize, near, require,
24    store::LookupSet,
25};
26use versioned::MaybeVersionedContractStorage;
27
28use crate::{Defuse, contract::events::PostponedMtBurnEvents};
29
30use self::{
31    accounts::Accounts,
32    config::{DefuseConfig, RolesConfig},
33    state::ContractState,
34};
35
36#[near(serializers = [json])]
37#[derive(AccessControlRole, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
38pub enum Role {
39    DAO,
40
41    FeesManager,
42    RelayerKeysManager,
43
44    UnrestrictedWithdrawer,
45
46    PauseManager,
47    Upgrader,
48    UnpauseManager,
49
50    UnrestrictedAccountLocker,
51    UnrestrictedAccountUnlocker,
52
53    SaltManager,
54
55    GarbageCollector,
56
57    UnrestrictedAccountManager,
58}
59
60#[access_control(role_type(Role))]
61#[derive(Pausable, PanicOnDefault)]
62#[pausable(
63    pause_roles(Role::DAO, Role::PauseManager),
64    unpause_roles(Role::DAO, Role::UnpauseManager)
65)]
66#[near(
67    contract_state,
68    contract_metadata(
69        standard(standard = "dip4", version = "0.1.0"),
70        standard(standard = "nep245", version = "1.0.0"),
71    )
72)]
73#[autoimpl(Deref using self.storage)]
74#[autoimpl(DerefMut using self.storage)]
75pub struct Contract {
76    #[borsh(
77        deserialize_with = "As::<MaybeVersionedContractStorage>::deserialize",
78        serialize_with = "As::<MaybeVersionedContractStorage>::serialize"
79    )]
80    storage: ContractStorage,
81
82    #[borsh(skip)]
83    runtime: Runtime,
84}
85
86#[derive(Debug)]
87#[autoimpl(Deref using self.state)]
88#[autoimpl(DerefMut using self.state)]
89#[near(serializers = [borsh])]
90pub struct ContractStorage {
91    accounts: Accounts,
92
93    state: ContractState,
94
95    relayer_keys: LookupSet<near_sdk::PublicKey>,
96}
97
98#[derive(Debug, Default)]
99pub struct Runtime {
100    pub postponed_burns: PostponedMtBurnEvents,
101}
102
103#[near]
104impl Contract {
105    #[must_use]
106    #[init]
107    #[allow(clippy::use_self)] // Clippy seems to not play well with near-sdk, or there is a bug in clippy - seen in shared security analysis
108    pub fn new(config: DefuseConfig) -> Self {
109        let mut contract = Self {
110            storage: ContractStorage {
111                accounts: Accounts::new(Prefix::Accounts),
112                state: ContractState::new(Prefix::State, config.wnear_id, config.fees),
113                relayer_keys: LookupSet::new(Prefix::RelayerKeys),
114            },
115            runtime: Runtime::default(),
116        };
117        contract.init_acl(config.roles);
118        contract
119    }
120
121    fn init_acl(&mut self, roles: RolesConfig) {
122        let mut acl = self.acl_get_or_init();
123        require!(
124            roles
125                .super_admins
126                .into_iter()
127                .all(|super_admin| acl.add_super_admin_unchecked(&super_admin))
128                && roles
129                    .admins
130                    .into_iter()
131                    .flat_map(|(role, admins)| iter::repeat(role).zip(admins))
132                    .all(|(role, admin)| acl.add_admin_unchecked(role, &admin))
133                && roles
134                    .grantees
135                    .into_iter()
136                    .flat_map(|(role, grantees)| iter::repeat(role).zip(grantees))
137                    .all(|(role, grantee)| acl.grant_role_unchecked(role, &grantee)),
138            "failed to set roles"
139        );
140    }
141}
142
143#[near]
144impl Defuse for Contract {}
145
146#[derive(BorshStorageKey)]
147#[near(serializers = [borsh])]
148enum Prefix {
149    Accounts,
150    State,
151    RelayerKeys,
152}
153
154pub trait MigrateStorageWithPrefix<T>: Sized {
155    fn migrate<S>(val: T, prefix: S) -> Self
156    where
157        S: IntoStorageKey;
158}