1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! # multi-ownable
//!
//! A NEAR plugin enables multiple Accounts to share ownership of a contract.
//! Calls are stored in hashed form, so storage requirements are very low.
//!
//! ### test
//!
//! cargo test -- --nocapture
//!
//! ### usage
//!
//! `multi-ownable` can be addeded to your contract via a macro:
//!
//! ```rust
//! // Arguments:
//! // 1. name of your contract
//! // 2. name of the field where the multi ownable state is stored
//! // 3. enum type for possible multisig calls
//! // 4. callback function to process completed multisig calls
//! crate::impl_multi_ownable!(Contract, multi_ownable, MultiOwnableCall, on_call);
//! ```
//!
//! Here is a full working example of how to use `multi-ownable`
//!
//! ```rust
//! // import "impl_multi_ownable" and "MultiOwnableData"
//! use multi_ownable::{impl_multi_ownable, MultiOwnableData};
//! use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
//! use near_sdk::{env, near_bindgen, require, AccountId, BorshStorageKey, PanicOnDefault};
//!
//! #[near_bindgen]
//! #[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)]
//! pub struct Contract {
//!   multi_ownable: MultiOwnableData, // add this to the Contract
//!   number: u64,
//! }
//! #[derive(BorshSerialize, BorshStorageKey)]
//! enum StorageKey {
//!   Owners,
//!   MultiOwnableCalls,
//! }
//!
//! #[near_bindgen]
//! impl Contract {
//!   #[init]
//!   pub fn new(owner_id: AccountId) -> Self {
//!     let mut this = Self {
//!       number: 0,
//!       multi_ownable: MultiOwnableData::new(StorageKey::Owners, StorageKey::MultiOwnableCalls),
//!     };
//!     // initialize multi_ownable in the "new" func of your Contract
//!     this.init_multi_ownable(vec![owner_id.clone()], 1);
//!     this
//!   }
//!
//!   pub fn get_number(&self) -> u64 {
//!     self.number
//!   }
//!
//!   // arguments are received as a json string
//!   fn on_call(&mut self, call_name: MultiOwnableCall, arguments: &str) {
//!     match call_name {
//!       MultiOwnableCall::UpdateNumber => self._update_number(arguments),
//!       MultiOwnableCall::DoSomethingElse => self._do_something_else(arguments),
//!     }
//!   }
//!
//!   #[private]
//!   fn _update_number(&mut self, args: &str) {
//!     // first, deserialize your arguments
//!     let UpdateNumberArgs { number } =
//!       near_sdk::serde_json::from_str(&args).expect("Invalid SetRewardRateArgs");
//!     self.number = number;
//!   }
//!
//!   #[private]
//!   fn _do_something_else(&self, _args: &str) {
//!     // do something else
//!   }
//! }
//!
//! // an argument struct for "update_number" call
//! #[derive(Serialize, Deserialize, Clone)]
//! #[serde(crate = "near_sdk::serde")]
//! pub struct UpdateNumberArgs {
//!   pub number: u64,
//! }
//!
//! // create an enum to match possible multisig calls
//! // make sure to both "rename" and "alias" your fields to be snake_case
//! #[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, Clone)]
//! #[serde(crate = "near_sdk::serde")]
//! pub enum MultiOwnableCall {
//!   #[serde(rename = "update_number", alias = "update_number")]
//!   UpdateNumber,
//!   #[serde(rename = "do_something_else", alias = "do_something_else")]
//!   DoSomethingElse,
//! }
//!
//! crate::impl_multi_ownable!(Contract, multi_ownable, MultiOwnableCall, on_call);
//!
//! ```

use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
use near_sdk::collections::{UnorderedMap, UnorderedSet};
use near_sdk::{AccountId, IntoStorageKey};
pub use sha2;

#[derive(BorshDeserialize, BorshSerialize)]
pub struct MultiOwnableData {
  pub owners: UnorderedSet<AccountId>,
  pub threshold: u32,
  pub calls: UnorderedMap<Vec<u8>, Vec<AccountId>>,
}

impl MultiOwnableData {
  pub fn new<S, T>(owners_key: S, calls_key: T) -> Self
  where
    S: IntoStorageKey,
    T: IntoStorageKey,
  {
    Self {
      threshold: 0,
      owners: UnorderedSet::new(owners_key),
      calls: UnorderedMap::new(calls_key),
    }
  }
}