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
129
130
//! # 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);
//!
//! ```

pub mod macros;

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),
    }
  }
}