123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699 |
- // Ensure we're `no_std` when compiling for Wasm.
- #![cfg_attr(not(feature = "std"), no_std)]
- // Clippy linter requirement.
- // Disable it because of the substrate lib design
- // Example: pub PaidMembershipTermsById get(paid_membership_terms_by_id) build(|config: &GenesisConfig<T>| {}
- #![allow(clippy::redundant_closure_call)]
- pub mod genesis;
- pub(crate) mod mock;
- mod tests;
- use codec::{Codec, Decode, Encode};
- use frame_support::traits::Currency;
- use frame_support::{decl_event, decl_module, decl_storage, ensure, Parameter};
- use frame_system::{ensure_root, ensure_signed};
- use sp_arithmetic::traits::{BaseArithmetic, One};
- use sp_runtime::traits::{MaybeSerialize, Member};
- use sp_std::borrow::ToOwned;
- use sp_std::vec;
- use sp_std::vec::Vec;
- use common::currency::{BalanceOf, GovernanceCurrency};
- //TODO: Convert errors to the Substrate decl_error! macro.
- /// Result with string error message. This exists for backward compatibility purpose.
- pub type DispatchResult = Result<(), &'static str>;
- pub trait Trait: frame_system::Trait + GovernanceCurrency + pallet_timestamp::Trait {
- type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
- type MemberId: Parameter
- + Member
- + BaseArithmetic
- + Codec
- + Default
- + Copy
- + MaybeSerialize
- + PartialEq;
- type PaidTermId: Parameter
- + Member
- + BaseArithmetic
- + Codec
- + Default
- + Copy
- + MaybeSerialize
- + PartialEq;
- type SubscriptionId: Parameter
- + Member
- + BaseArithmetic
- + Codec
- + Default
- + Copy
- + MaybeSerialize
- + PartialEq;
- /// Describes the common type for the working group members (workers).
- type ActorId: Parameter
- + Member
- + BaseArithmetic
- + Codec
- + Default
- + Copy
- + MaybeSerialize
- + PartialEq
- + Ord;
- }
- const FIRST_PAID_TERMS_ID: u8 = 1;
- // Default paid membership terms
- pub const DEFAULT_PAID_TERM_ID: u8 = 0;
- // Default user info constraints
- const DEFAULT_MIN_HANDLE_LENGTH: u32 = 5;
- const DEFAULT_MAX_HANDLE_LENGTH: u32 = 40;
- const DEFAULT_MAX_AVATAR_URI_LENGTH: u32 = 1024;
- const DEFAULT_MAX_ABOUT_TEXT_LENGTH: u32 = 2048;
- /// Public membership object alias.
- pub type Membership<T> = MembershipObject<
- <T as frame_system::Trait>::BlockNumber,
- <T as pallet_timestamp::Trait>::Moment,
- <T as Trait>::PaidTermId,
- <T as Trait>::SubscriptionId,
- <T as frame_system::Trait>::AccountId,
- >;
- #[derive(Encode, Decode, Default)]
- /// Stored information about a registered user
- pub struct MembershipObject<BlockNumber, Moment, PaidTermId, SubscriptionId, AccountId> {
- /// The unique handle chosen by member
- pub handle: Vec<u8>,
- /// A Url to member's Avatar image
- pub avatar_uri: Vec<u8>,
- /// Short text chosen by member to share information about themselves
- pub about: Vec<u8>,
- /// Block number when member was registered
- pub registered_at_block: BlockNumber,
- /// Timestamp when member was registered
- pub registered_at_time: Moment,
- /// How the member was registered
- pub entry: EntryMethod<PaidTermId, AccountId>,
- /// Whether the member is suspended or not.
- pub suspended: bool,
- /// The type of subscription the member has purchased if any.
- pub subscription: Option<SubscriptionId>,
- /// Member's root account id. Only the root account is permitted to set a new root account
- /// and update the controller account. Other modules may only allow certain actions if
- /// signed with root account. It is intended to be an account that can remain offline and
- /// potentially hold a member's funds, and be a source for staking roles.
- pub root_account: AccountId,
- /// Member's controller account id. This account is intended to be used by
- /// a member to act under their identity in other modules. It will usually be used more
- /// online and will have less funds in its balance.
- pub controller_account: AccountId,
- }
- // Contains valid or default user details
- struct ValidatedUserInfo {
- handle: Vec<u8>,
- avatar_uri: Vec<u8>,
- about: Vec<u8>,
- }
- #[derive(Encode, Decode, Debug, PartialEq)]
- pub enum EntryMethod<PaidTermId, AccountId> {
- Paid(PaidTermId),
- Screening(AccountId),
- Genesis,
- }
- /// Must be default constructible because it indirectly is a value in a storage map.
- /// ***SHOULD NEVER ACTUALLY GET CALLED, IS REQUIRED TO DUE BAD STORAGE MODEL IN SUBSTRATE***
- impl<PaidTermId, AccountId> Default for EntryMethod<PaidTermId, AccountId> {
- fn default() -> Self {
- Self::Genesis
- }
- }
- #[derive(Encode, Decode, Eq, PartialEq, Default)]
- pub struct PaidMembershipTerms<Balance> {
- /// Quantity of native tokens which must be provably burned
- pub fee: Balance,
- /// String of capped length describing human readable conditions which are being agreed upon
- pub text: Vec<u8>,
- }
- decl_storage! {
- trait Store for Module<T: Trait> as Membership {
- /// MemberId to assign to next member that is added to the registry, and is also the
- /// total number of members created. MemberIds start at Zero.
- pub NextMemberId get(fn members_created) : T::MemberId;
- /// Mapping of member's id to their membership profile
- pub MembershipById get(fn membership) : map hasher(blake2_128_concat)
- T::MemberId => Membership<T>;
- /// Mapping of a root account id to vector of member ids it controls.
- pub(crate) MemberIdsByRootAccountId : map hasher(blake2_128_concat)
- T::AccountId => Vec<T::MemberId>;
- /// Mapping of a controller account id to vector of member ids it controls
- pub(crate) MemberIdsByControllerAccountId : map hasher(blake2_128_concat)
- T::AccountId => Vec<T::MemberId>;
- /// Registered unique handles and their mapping to their owner
- pub MemberIdByHandle get(fn handles) : map hasher(blake2_128_concat)
- Vec<u8> => T::MemberId;
- /// Next paid membership terms id
- pub NextPaidMembershipTermsId get(fn next_paid_membership_terms_id) :
- T::PaidTermId = T::PaidTermId::from(FIRST_PAID_TERMS_ID);
- /// Paid membership terms record
- // Remember to add _genesis_phantom_data: std::marker::PhantomData{} to membership
- // genesis config if not providing config() or extra_genesis
- pub PaidMembershipTermsById get(fn paid_membership_terms_by_id) build(|config: &GenesisConfig<T>| {
- // This method only gets called when initializing storage, and is
- // compiled as native code. (Will be called when building `raw` chainspec)
- // So it can't be relied upon to initialize storage for runtimes updates.
- // Initialization for updated runtime is done in run_migration()
- let terms = PaidMembershipTerms {
- fee: config.default_paid_membership_fee,
- text: Vec::default(),
- };
- vec![(T::PaidTermId::from(DEFAULT_PAID_TERM_ID), terms)]
- }) : map hasher(blake2_128_concat) T::PaidTermId => PaidMembershipTerms<BalanceOf<T>>;
- /// Active Paid membership terms
- pub ActivePaidMembershipTerms get(fn active_paid_membership_terms) :
- Vec<T::PaidTermId> = vec![T::PaidTermId::from(DEFAULT_PAID_TERM_ID)];
- /// Is the platform is accepting new members or not
- pub NewMembershipsAllowed get(fn new_memberships_allowed) : bool = true;
- pub ScreeningAuthority get(fn screening_authority) : T::AccountId;
- // User Input Validation parameters - do these really need to be state variables
- // I don't see a need to adjust these in future?
- pub MinHandleLength get(fn min_handle_length) : u32 = DEFAULT_MIN_HANDLE_LENGTH;
- pub MaxHandleLength get(fn max_handle_length) : u32 = DEFAULT_MAX_HANDLE_LENGTH;
- pub MaxAvatarUriLength get(fn max_avatar_uri_length) : u32 = DEFAULT_MAX_AVATAR_URI_LENGTH;
- pub MaxAboutTextLength get(fn max_about_text_length) : u32 = DEFAULT_MAX_ABOUT_TEXT_LENGTH;
- }
- add_extra_genesis {
- config(default_paid_membership_fee): BalanceOf<T>;
- config(members) : Vec<genesis::Member<T::MemberId, T::AccountId, T::Moment>>;
- build(|config: &GenesisConfig<T>| {
- for member in &config.members {
- let checked_user_info = <Module<T>>::check_user_registration_info(
- Some(member.handle.clone().into_bytes()),
- Some(member.avatar_uri.clone().into_bytes()),
- Some(member.about.clone().into_bytes())
- ).expect("Importing Member Failed");
- let member_id = <Module<T>>::insert_member(
- &member.root_account,
- &member.controller_account,
- &checked_user_info,
- EntryMethod::Genesis,
- T::BlockNumber::from(1),
- member.registered_at_time
- ).expect("Importing Member Failed");
- // ensure imported member id matches assigned id
- assert_eq!(member_id, member.member_id, "Import Member Failed: MemberId Incorrect");
- }
- });
- }
- }
- decl_event! {
- pub enum Event<T> where
- <T as frame_system::Trait>::AccountId,
- <T as Trait>::MemberId,
- {
- MemberRegistered(MemberId, AccountId),
- MemberUpdatedAboutText(MemberId),
- MemberUpdatedAvatar(MemberId),
- MemberUpdatedHandle(MemberId),
- MemberSetRootAccount(MemberId, AccountId),
- MemberSetControllerAccount(MemberId, AccountId),
- }
- }
- decl_module! {
- pub struct Module<T: Trait> for enum Call where origin: T::Origin {
- fn deposit_event() = default;
- /// Non-members can buy membership
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn buy_membership(
- origin,
- paid_terms_id: T::PaidTermId,
- handle: Option<Vec<u8>>,
- avatar_uri: Option<Vec<u8>>,
- about: Option<Vec<u8>>
- ) {
- let who = ensure_signed(origin)?;
- // make sure we are accepting new memberships
- ensure!(Self::new_memberships_allowed(), "new members not allowed");
- // ensure paid_terms_id is active
- let terms = Self::ensure_active_terms_id(paid_terms_id)?;
- // ensure enough free balance to cover terms fees
- ensure!(T::Currency::can_slash(&who, terms.fee), "not enough balance to buy membership");
- let user_info = Self::check_user_registration_info(handle, avatar_uri, about)?;
- let member_id = Self::insert_member(
- &who,
- &who,
- &user_info,
- EntryMethod::Paid(paid_terms_id),
- <frame_system::Module<T>>::block_number(),
- <pallet_timestamp::Module<T>>::now()
- )?;
- let _ = T::Currency::slash(&who, terms.fee);
- Self::deposit_event(RawEvent::MemberRegistered(member_id, who));
- }
- /// Change member's about text
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn change_member_about_text(origin, member_id: T::MemberId, text: Vec<u8>) {
- let sender = ensure_signed(origin)?;
- let membership = Self::ensure_membership(member_id)?;
- ensure!(membership.controller_account == sender, "only controller account can update member about text");
- Self::_change_member_about_text(member_id, &text)?;
- }
- /// Change member's avatar
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn change_member_avatar(origin, member_id: T::MemberId, uri: Vec<u8>) {
- let sender = ensure_signed(origin)?;
- let membership = Self::ensure_membership(member_id)?;
- ensure!(membership.controller_account == sender, "only controller account can update member avatar");
- Self::_change_member_avatar(member_id, &uri)?;
- }
- /// Change member's handle. Will ensure new handle is unique and old one will be available
- /// for other members to use.
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn change_member_handle(origin, member_id: T::MemberId, handle: Vec<u8>) {
- let sender = ensure_signed(origin)?;
- let membership = Self::ensure_membership(member_id)?;
- ensure!(membership.controller_account == sender, "only controller account can update member handle");
- Self::_change_member_handle(member_id, handle)?;
- }
- /// Update member's all or some of handle, avatar and about text.
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn update_membership(
- origin,
- member_id: T::MemberId,
- handle: Option<Vec<u8>>,
- avatar_uri: Option<Vec<u8>>,
- about: Option<Vec<u8>>
- ) {
- let sender = ensure_signed(origin)?;
- let membership = Self::ensure_membership(member_id)?;
- ensure!(membership.controller_account == sender, "only controller account can update member info");
- if let Some(uri) = avatar_uri {
- Self::_change_member_avatar(member_id, &uri)?;
- }
- if let Some(about) = about {
- Self::_change_member_about_text(member_id, &about)?;
- }
- if let Some(handle) = handle {
- Self::_change_member_handle(member_id, handle)?;
- }
- }
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn set_controller_account(origin, member_id: T::MemberId, new_controller_account: T::AccountId) {
- let sender = ensure_signed(origin)?;
- let mut membership = Self::ensure_membership(member_id)?;
- ensure!(membership.root_account == sender, "only root account can set new controller account");
- // only update if new_controller_account is different than current one
- if membership.controller_account != new_controller_account {
- <MemberIdsByControllerAccountId<T>>::mutate(&membership.controller_account, |ids| {
- ids.retain(|id| *id != member_id);
- });
- <MemberIdsByControllerAccountId<T>>::mutate(&new_controller_account, |ids| {
- ids.push(member_id);
- });
- membership.controller_account = new_controller_account.clone();
- <MembershipById<T>>::insert(member_id, membership);
- Self::deposit_event(RawEvent::MemberSetControllerAccount(member_id, new_controller_account));
- }
- }
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn set_root_account(origin, member_id: T::MemberId, new_root_account: T::AccountId) {
- let sender = ensure_signed(origin)?;
- let mut membership = Self::ensure_membership(member_id)?;
- ensure!(membership.root_account == sender, "only root account can set new root account");
- // only update if new root account is different than current one
- if membership.root_account != new_root_account {
- <MemberIdsByRootAccountId<T>>::mutate(&membership.root_account, |ids| {
- ids.retain(|id| *id != member_id);
- });
- <MemberIdsByRootAccountId<T>>::mutate(&new_root_account, |ids| {
- ids.push(member_id);
- });
- membership.root_account = new_root_account.clone();
- <MembershipById<T>>::insert(member_id, membership);
- Self::deposit_event(RawEvent::MemberSetRootAccount(member_id, new_root_account));
- }
- }
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn add_screened_member(
- origin,
- new_member_account: T::AccountId,
- handle: Option<Vec<u8>>,
- avatar_uri: Option<Vec<u8>>,
- about: Option<Vec<u8>>
- ) {
- // ensure sender is screening authority
- let sender = ensure_signed(origin)?;
- if <ScreeningAuthority<T>>::exists() {
- ensure!(sender == Self::screening_authority(), "not screener");
- } else {
- // no screening authority defined. Cannot accept this request
- return Err("no screening authority defined".into());
- }
- // make sure we are accepting new memberships
- ensure!(Self::new_memberships_allowed(), "new members not allowed");
- let user_info = Self::check_user_registration_info(handle, avatar_uri, about)?;
- let member_id = Self::insert_member(
- &new_member_account,
- &new_member_account,
- &user_info,
- EntryMethod::Screening(sender),
- <frame_system::Module<T>>::block_number(),
- <pallet_timestamp::Module<T>>::now()
- )?;
- Self::deposit_event(RawEvent::MemberRegistered(member_id, new_member_account));
- }
- #[weight = 10_000_000] // TODO: adjust weight
- pub fn set_screening_authority(origin, authority: T::AccountId) {
- ensure_root(origin)?;
- <ScreeningAuthority<T>>::put(authority);
- }
- }
- }
- /// Reason why a given member id does not have a given account as the controller account.
- pub enum ControllerAccountForMemberCheckFailed {
- NotMember,
- NotControllerAccount,
- }
- pub enum MemberControllerAccountDidNotSign {
- UnsignedOrigin,
- MemberIdInvalid,
- SignerControllerAccountMismatch,
- }
- pub enum MemberControllerAccountMismatch {
- MemberIdInvalid,
- SignerControllerAccountMismatch,
- }
- pub enum MemberRootAccountMismatch {
- MemberIdInvalid,
- SignerRootAccountMismatch,
- }
- impl<T: Trait> Module<T> {
- /// Provided that the member_id exists return its membership. Returns error otherwise.
- pub fn ensure_membership(id: T::MemberId) -> Result<Membership<T>, &'static str> {
- if <MembershipById<T>>::contains_key(&id) {
- Ok(Self::membership(&id))
- } else {
- Err("member profile not found")
- }
- }
- /// Ensure that given member has given account as the controller account
- pub fn ensure_is_controller_account_for_member(
- member_id: &T::MemberId,
- account: &T::AccountId,
- ) -> Result<Membership<T>, ControllerAccountForMemberCheckFailed> {
- if MembershipById::<T>::contains_key(member_id) {
- let membership = MembershipById::<T>::get(member_id);
- if membership.controller_account == *account {
- Ok(membership)
- } else {
- Err(ControllerAccountForMemberCheckFailed::NotControllerAccount)
- }
- } else {
- Err(ControllerAccountForMemberCheckFailed::NotMember)
- }
- }
- /// Returns true if account is either a member's root or controller account
- pub fn is_member_account(who: &T::AccountId) -> bool {
- <MemberIdsByRootAccountId<T>>::contains_key(who)
- || <MemberIdsByControllerAccountId<T>>::contains_key(who)
- }
- fn ensure_active_terms_id(
- terms_id: T::PaidTermId,
- ) -> Result<PaidMembershipTerms<BalanceOf<T>>, &'static str> {
- let active_terms = Self::active_paid_membership_terms();
- ensure!(
- active_terms.iter().any(|&id| id == terms_id),
- "paid terms id not active"
- );
- if <PaidMembershipTermsById<T>>::contains_key(terms_id) {
- Ok(Self::paid_membership_terms_by_id(terms_id))
- } else {
- Err("paid membership term id does not exist")
- }
- }
- #[allow(clippy::ptr_arg)] // cannot change to the "&[u8]" suggested by clippy
- fn ensure_unique_handle(handle: &Vec<u8>) -> DispatchResult {
- ensure!(
- !<MemberIdByHandle<T>>::contains_key(handle),
- "handle already registered"
- );
- Ok(())
- }
- fn validate_handle(handle: &[u8]) -> DispatchResult {
- ensure!(
- handle.len() >= Self::min_handle_length() as usize,
- "handle too short"
- );
- ensure!(
- handle.len() <= Self::max_handle_length() as usize,
- "handle too long"
- );
- Ok(())
- }
- fn validate_text(text: &[u8]) -> Vec<u8> {
- let mut text = text.to_owned();
- text.truncate(Self::max_about_text_length() as usize);
- text
- }
- fn validate_avatar(uri: &[u8]) -> DispatchResult {
- ensure!(
- uri.len() <= Self::max_avatar_uri_length() as usize,
- "avatar uri too long"
- );
- Ok(())
- }
- /// Basic user input validation
- fn check_user_registration_info(
- handle: Option<Vec<u8>>,
- avatar_uri: Option<Vec<u8>>,
- about: Option<Vec<u8>>,
- ) -> Result<ValidatedUserInfo, &'static str> {
- // Handle is required during registration
- let handle = handle.ok_or("handle must be provided during registration")?;
- Self::validate_handle(&handle)?;
- let about = Self::validate_text(&about.unwrap_or_default());
- let avatar_uri = avatar_uri.unwrap_or_default();
- Self::validate_avatar(&avatar_uri)?;
- Ok(ValidatedUserInfo {
- handle,
- avatar_uri,
- about,
- })
- }
- fn insert_member(
- root_account: &T::AccountId,
- controller_account: &T::AccountId,
- user_info: &ValidatedUserInfo,
- entry_method: EntryMethod<T::PaidTermId, T::AccountId>,
- registered_at_block: T::BlockNumber,
- registered_at_time: T::Moment,
- ) -> Result<T::MemberId, &'static str> {
- Self::ensure_unique_handle(&user_info.handle)?;
- let new_member_id = Self::members_created();
- let membership: Membership<T> = MembershipObject {
- handle: user_info.handle.clone(),
- avatar_uri: user_info.avatar_uri.clone(),
- about: user_info.about.clone(),
- registered_at_block,
- registered_at_time,
- entry: entry_method,
- suspended: false,
- subscription: None,
- root_account: root_account.clone(),
- controller_account: controller_account.clone(),
- };
- <MemberIdsByRootAccountId<T>>::mutate(root_account, |ids| {
- ids.push(new_member_id);
- });
- <MemberIdsByControllerAccountId<T>>::mutate(controller_account, |ids| {
- ids.push(new_member_id);
- });
- <MembershipById<T>>::insert(new_member_id, membership);
- <MemberIdByHandle<T>>::insert(user_info.handle.clone(), new_member_id);
- <NextMemberId<T>>::put(new_member_id + One::one());
- Ok(new_member_id)
- }
- fn _change_member_about_text(id: T::MemberId, text: &[u8]) -> DispatchResult {
- let mut membership = Self::ensure_membership(id)?;
- let text = Self::validate_text(text);
- membership.about = text;
- Self::deposit_event(RawEvent::MemberUpdatedAboutText(id));
- <MembershipById<T>>::insert(id, membership);
- Ok(())
- }
- fn _change_member_avatar(id: T::MemberId, uri: &[u8]) -> DispatchResult {
- let mut membership = Self::ensure_membership(id)?;
- Self::validate_avatar(uri)?;
- membership.avatar_uri = uri.to_owned();
- Self::deposit_event(RawEvent::MemberUpdatedAvatar(id));
- <MembershipById<T>>::insert(id, membership);
- Ok(())
- }
- fn _change_member_handle(id: T::MemberId, handle: Vec<u8>) -> DispatchResult {
- let mut membership = Self::ensure_membership(id)?;
- Self::validate_handle(&handle)?;
- Self::ensure_unique_handle(&handle)?;
- <MemberIdByHandle<T>>::remove(&membership.handle);
- <MemberIdByHandle<T>>::insert(handle.clone(), id);
- membership.handle = handle;
- Self::deposit_event(RawEvent::MemberUpdatedHandle(id));
- <MembershipById<T>>::insert(id, membership);
- Ok(())
- }
- pub fn ensure_member_controller_account_signed(
- origin: T::Origin,
- member_id: &T::MemberId,
- ) -> Result<T::AccountId, MemberControllerAccountDidNotSign> {
- // Ensure transaction is signed.
- let signer_account =
- ensure_signed(origin).map_err(|_| MemberControllerAccountDidNotSign::UnsignedOrigin)?;
- // Ensure member exists
- let membership = Self::ensure_membership(*member_id)
- .map_err(|_| MemberControllerAccountDidNotSign::MemberIdInvalid)?;
- ensure!(
- membership.controller_account == signer_account,
- MemberControllerAccountDidNotSign::SignerControllerAccountMismatch
- );
- Ok(signer_account)
- }
- pub fn ensure_member_controller_account(
- signer_account: &T::AccountId,
- member_id: &T::MemberId,
- ) -> Result<(), MemberControllerAccountMismatch> {
- // Ensure member exists
- let membership = Self::ensure_membership(*member_id)
- .map_err(|_| MemberControllerAccountMismatch::MemberIdInvalid)?;
- ensure!(
- membership.controller_account == *signer_account,
- MemberControllerAccountMismatch::SignerControllerAccountMismatch
- );
- Ok(())
- }
- pub fn ensure_member_root_account(
- signer_account: &T::AccountId,
- member_id: &T::MemberId,
- ) -> Result<(), MemberRootAccountMismatch> {
- // Ensure member exists
- let membership = Self::ensure_membership(*member_id)
- .map_err(|_| MemberRootAccountMismatch::MemberIdInvalid)?;
- ensure!(
- membership.root_account == *signer_account,
- MemberRootAccountMismatch::SignerRootAccountMismatch
- );
- Ok(())
- }
- }
|