members-module.md 10 KB

Members Module

Table Of Content

Overview

Manages the set of current members, their profile, status.

Module Name

Membership

Constants

These are constants that are only part of making this document legible, they are not part of the public interface of the module.

const DEFAULT_FIRST_MEMBER_ID: u64 = 1;
const FIRST_PAID_TERMS_ID: u64 = 1;
const DEFAULT_PAID_TERM_ID: u64 = 0;
const DEFAULT_PAID_TERM_FEE: u64 = 100;
const DEFAULT_PAID_TERM_TEXT: &str = "Default Paid Term TOS...";
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;

Trait Configuration

Trait Name

Trait

Base Traits

These are the traits which provide the interfaces to services external to this module, such as peer modules which must run in the same runtime for example.

Associated Types

Event

Event type. ???

MemberId

Member identifier type.

PaidTermId

Paid term identifier type.

SubscriptionId

Subscription identifier type.

Roles

Roles module type.

Public Types

These are the public type of the module, that is they are used in the storage system and in signature of public methods. All other types are omitted.

EntryMethod

pub enum EntryMethod<T: Trait> {
    Paid(T::PaidTermId),
    Screening(T::AccountId),
}

Profile

pub struct Profile<T: Trait> {
    pub id: T::MemberId,
    pub handle: Vec<u8>,
    pub avatar_uri: Vec<u8>,
    pub about: Vec<u8>,
    pub registered_at_block: T::BlockNumber,
    pub registered_at_time: T::Moment,
    pub entry: EntryMethod<T>,
    pub suspended: bool,
    pub subscription: Option<T::SubscriptionId>,
}

PaidMembershipTerms

pub struct PaidMembershipTerms<T: Trait> {
    /// Unique identifier - the term id
    pub id: T::PaidTermId,
    /// Quantity of native tokens which must be provably burned
    pub fee: BalanceOf<T>,
    /// String of capped length describing human readable conditions which are being agreed upon
    pub text: Vec<u8>,
}

UserInfo

pub struct UserInfo {
    pub handle: Option<Vec<u8>>,
    pub avatar_uri: Option<Vec<u8>>,
    pub about: Option<Vec<u8>>,
}

Storage

The following is the payload used with decl_storage.

/// MemberId's start at this value
pub FirstMemberId get(first_member_id) config(first_member_id): T::MemberId = T::MemberId::sa(DEFAULT_FIRST_MEMBER_ID);

/// MemberId to assign to next member that is added to the registry
pub NextMemberId get(next_member_id) build(|config: &GenesisConfig<T>| config.first_member_id): T::MemberId = T::MemberId::sa(DEFAULT_FIRST_MEMBER_ID);

/// Mapping of member ids to their corresponding primary accountid
pub AccountIdByMemberId get(account_id_by_member_id) : map T::MemberId => T::AccountId;

/// Mapping of members' account ids to their member id.
pub MemberIdByAccountId get(member_id_by_account_id) : map T::AccountId => Option<T::MemberId>;

/// Mapping of member's id to their membership profile
// Value is Option<Profile> because it is not meaningful to have a Default value for Profile
pub MemberProfile get(member_profile) : map T::MemberId => Option<Profile<T>>;

/// Registered unique handles and their mapping to their owner
pub Handles get(handles) : map Vec<u8> => Option<T::MemberId>;

/// Next paid membership terms id
pub NextPaidMembershipTermsId get(next_paid_membership_terms_id) : T::PaidTermId = T::PaidTermId::sa(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(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 mut terms: PaidMembershipTerms<T> = Default::default();
    terms.fee = config.default_paid_membership_fee;
    vec![(terms.id, terms)]
}) : map T::PaidTermId => Option<PaidMembershipTerms<T>>;

/// Active Paid membership terms
pub ActivePaidMembershipTerms get(active_paid_membership_terms) : Vec<T::PaidTermId> = vec![T::PaidTermId::sa(DEFAULT_PAID_TERM_ID)];

/// Is the platform is accepting new members or not
pub NewMembershipsAllowed get(new_memberships_allowed) : bool = true;

pub ScreeningAuthority get(screening_authority) : Option<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(min_handle_length) : u32 = DEFAULT_MIN_HANDLE_LENGTH;
pub MaxHandleLength get(max_handle_length) : u32 = DEFAULT_MAX_HANDLE_LENGTH;
pub MaxAvatarUriLength get(max_avatar_uri_length) : u32 = DEFAULT_MAX_AVATAR_URI_LENGTH;
pub MaxAboutTextLength get(max_about_text_length) : u32 = DEFAULT_MAX_ABOUT_TEXT_LENGTH;

Invariants

...

Events

The following is the payload used with decl_events.

pub enum Event<T> where
      <T as system::Trait>::AccountId,
      <T as Trait>::MemberId {
        MemberRegistered(MemberId, AccountId),
        MemberUpdatedAboutText(MemberId),
        MemberUpdatedAvatar(MemberId),
        MemberUpdatedHandle(MemberId),
}

Dispatchable Methods

buy_membership

Payload

origin: Origin, paid_terms_id: PaidTermId, user_info: UserInfo

Description

Establish new membership through payment.

Errors

1.
!ensure_signed(origin)
2. new members not allowed
!<NewMembershipsAllowed<T>>::get()
3. account already associated with a membership
<MemberIdByAccountId<T>>::exists(origin)
4. role key cannot be used for membership
T::Roles::is_role_account(origin)
5. paid terms id not active
!<ActivePaidMembershipTerms<T>>::iter().any(|x| x == paid_terms_id)
6. paid terms id not active

Note: This is a bug, should not be checked

!<PaidMembershipTermsById<T>>::exists(paid_terms_id)
7. not enough balance to buy membership
!T::Currency::can_slash(who, terms.fee)

where

let terms = <PaidMembershipTermsById<T>>::get(paid_terms_id);
8. missing handle
...
9. handle too short
...
10. handle too long
...
11. avatar uri too long
...
12. Handle occupied
...

Side effects

Membership established
Precondition

NO_ERROR

Side effect(s)

The following conditions must all hold

[T::Currency::balance]'(who) == T::Currency::balance(who) - <PaidMembershipTermsById<T>>::get(paid_terms_id).fee &&

[<MemberIdByAccountId<T>>]' == <MemberIdByAccountId<T>> [+] (who, <NextMemberId<T>>::get()) &&
[<AccountIdByMemberId<T>>]' == <AccountIdByMemberId<T>> [+] (<NextMemberId<T>>::get(), who)) == who &&
[<MemberProfile<T>>]' == <MemberProfile<T>> [+] (<NextMemberId<T>>::get(), profile) &&
[<Handles<T>>]' == <Handles<T>> [+] (user_info.handle.clone(), <NextMemberId<T>>::get()) &&
[<NextMemberId<T>>]' == <NextMemberId<T>> + 1

where

let profile = Profile {
  id: <NextMemberId<T>>::get(),
  handle: user_info.handle,
  avatar_uri: user_info.avatar_uri,
  about: user_info.about,
  registered_at_block: <system::Module<T>>::block_number(),
  registered_at_time: <timestamp::Module<T>>::now(),
  entry: EntryMethod::Paid(paid_terms_id)),
  suspended: false,
  subscription: None,
}
Result
Ok(<NextMemberId<T>>::get())
Event(s)
MemberRegistered(<NextMemberId<T>>::get(), who.clone())

change_member_about_text

Payload

origin: Origin, text: Vec<u8>

Description

Change about text on membership.

Errors

1.
!ensure_signed(origin)
2. no member id found for accountid
!<MemberIdByAccountId<T>>::exists(who)
3. not primary account
!<AccountIdByMemberId<T>>::exists(member_id)

where

let member_id = <MemberIdByAccountId<T>>::get(who)
4. member profile not found
!<MemberProfile<T>>::exists(member_id)

Side effects

About text updated
Precondition

NO_ERROR

Side effect(s)

The following conditions must all hold

<MemberProfile<T>>::get(member_id).text ===  text.truncate(Self::max_about_text_length() as usize);
Result
Ok()
Event(s)

RawEvent::MemberUpdatedAboutText(id)

change_member_avatar

fill in

change_member_handle

fill in

update_profile

fill in

add_screened_member

fill in

set_screening_authority

fill in

Non-dispatchable Methods

is_active_member

...

lookup_member_id

...

lookup_account_by_member_id