Перейти к содержанию

Аккаунты

Аккаунт в MONO - это составной объект с информацией о пользователе. Он составляется из информации, которая описывает состояние пользователя на разных уровнях работы платформы с идентификацией по username - имени аккаунта пользователя в блокчейне.

username — это уникальный идентификатор пользователя в системе. Обычно он состоит из 12 латинских букв без пробелов и специальных символов. С помощью него производится сквозная идентификация принадлежности аккаунта к пользователю во всех кооперативных системах платформы. У всех пайщиков, кооперативов и кооперативных участков есть свои уникальные username.

Временные ограничения

Блокчейн позволяет регистрировать и более короткие имена аккаунтов длинной до 12 символов a-z и цифрами от 1 до 5, но на текущий момент эта функциональность недоступна.

Объект аккаунта

У каждого аккаунта есть 4 уровня хранения информации, которые удобно проследить по объекту Account в GraphQL-API:

BlockchainAccount - объект аккаунта в блокчейне содержит системную информацию, такую как публичные ключи доступа, доступные вычислительные ресурсы, информация об установленном смарт-контракте, и т.д. и т.п. Это системный уровень обслуживания, где у каждого пайщика есть аккаунт, но не каждый аккаунт может быть пайщиком в каком-либо кооперативе. Все смарт-контракты устанавливаются и исполняются на этом уровне.

UserAccount - объект пользователя кооперативной экономики содержит в блокчейне информацию о типе аккаунта пайщика (физическое лицо, ИП или организация), а также, обезличенные публичные данные (хэши) для верификации пайщиков между кооперативами. Этот уровень предназначен для хранения информации пайщика, которая необходима всем кооперативам, но не относится к какому-либо из них конкретно.

ParticipantAccount - объект пайщика кооператива в таблице блокчейна, который определяет членство пайщика в конкретном кооперативе. Поскольку MONO обслуживает только один кооператив, то в participant_account обычно содержится информация, которая описывает членство пайщика в том кооперативе, для которого развернут MONO. Этот объект обезличен, публичен, и хранится в блокчейне.

ProviderAccount - объект аккаунта в системе учёта провайдера, т.е. MONO. Здесь хранится приватная информация о пайщике кооператива, которая содержит его приватные данные. Эти данные не публикуются в блокчейне и не выходят за пределы базы данных провайдера. Они используются для заполнения шаблонов документов при нажатии соответствующих кнопок на платформе. Также, эта информация используется провайдером для организации входа пользователя в систему по наличию соответстветсвия между email и username в его базе данных и блокчейне (подробнее)

Зарегистрировать аккаунт

🛠️ SDK: Mutations.Accounts.RegisterAccount | 🔗 GraphQL API: Mutation.registerAccount

Вызов мутации регистрации аккаунта создаёт объект аккаунта в системе учета провайдера (но не регистрирует его в блокчейне). После вызова, пользователь получает возможность входа в систему на основании предоставленного при регистрации публичного ключа и адреса электронной почты.

Успешный вызов мутации НЕ означает, что пользователь становится пайщиком и получает блокчейн-аккаунт. При вызове мутации пользователь получает только объект ProviderAccount, а для его перехода в пайщики необходимо пройти дополнительный путь, описанный в разделе Регистрация Пайщика.

Зарегистрировать аккаунт пользователя в системе

Регистрация аккаунта производится мутацией RegisterAccount, где в качестве переменных принимаются данные пользователя одного из трех типов, определяемых в перечислении AccountType: Individual (физическое лицо), Organization (юридическое лицо) или Entrepreneur (индивидуальный предприниматель).

При указании типа пользователя Individual необходимо передать объект individual_data с данными физлица, при регистрации Organization - ожидается обязательная передача organization_data, при указании типа Entrepreneur - entrepreneur_data.

Имя пользователя username и публичный ключ public_key генерируются при создании экземпляра класса Account:

import { Classes } from '@coopenomics/sdk'

//генерируем новый аккант
const account = new Classes.Account()

В объекте account после генерации аккаунта содержится имя пользователя, приватный и публичный ключ. Сгенерированный приватный ключ из объекта аккаунта будет требоваться для входа в систему после регистрации.

// console.log(account)
{
  name: "randomuserr",
  private_key: "5JxyzABC1234567890defGHIJKLMNopqRSTUV",
  public_key: "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5SozEZ8i8jUBS6yX79y6"
}

Используя сгенерированный объект аккаунта с ключами, проводим регистрацию в системе учёта провайдера, вызывая мутацию:

import { Mutations } from '@coopenomics/sdk';

const variables: Mutations.Accounts.RegisterAccount.IInput = {
  data: {
    email: <string>; // Электронная почта
    entrepreneur_data?: <null | {
        bank_account: {
          account_number: <string>; // Номер банковского счета
          bank_name: <string>; // Название банка
          card_number?: <null | string>; // Номер карты
          currency: <string>; // Валюта счета
          details: {
            bik: <string>; // БИК банка
            corr: <string>; // Корреспондентский счет
            kpp: <string>; // КПП банка
          };
        };
        birthdate: <string>; // Дата рождения
        city: <string>; // Город
        country: <Russia>; // Страна
        details: {
          inn: <string>; // ИНН
          ogrn: <string>; // ОГРН
        };
        first_name: <string>; // Имя
        full_address: <string>; // Полный адрес
        last_name: <string>; // Фамилия
        middle_name: <string>; // Отчество
        phone: <string>; // Телефон
      }>; // Данные индивидуального предпринимателя
    individual_data?: <null | {
        birthdate: <string>; // Дата рождения
        first_name: <string>; // Имя
        full_address: <string>; // Полный адрес
        last_name: <string>; // Фамилия
        middle_name: <string>; // Отчество
        passport?: <null | {
            code: <string>;
            issued_at: <string>;
            issued_by: <string>;
            number: <number>;
            series: <number>;
          }>; // Данные паспорта
        phone: <string>; // Телефон
      }>; // Данные физического лица
    organization_data?: <null | {
        bank_account: {
          account_number: <string>; // Номер банковского счета
          bank_name: <string>; // Название банка
          card_number?: <null | string>; // Номер карты
          currency: <string>; // Валюта счета
          details: {
            bik: <string>; // БИК банка
            corr: <string>; // Корреспондентский счет
            kpp: <string>; // КПП банка
          };
        };
        city: <string>; // Город
        country: <string>; // Страна
        details: {
          inn: <string>;
          kpp: <string>;
          ogrn: <string>;
        };
        fact_address: <string>; // Фактический адрес
        full_address: <string>; // Полный адрес
        full_name: <string>; // Полное наименование организации
        phone: <string>; // Телефон
        represented_by: {
          based_on: <string>;
          first_name: <string>;
          last_name: <string>;
          middle_name: <string>;
          position: <string>;
        };
        short_name: <string>; // Краткое наименование организации
        type: <OrganizationType>; // Тип организации
      }>; // Данные организации
    public_key?: <null | string>; // Публичный ключ
    referer?: <null | string>; // Имя аккаунта реферера
    type: <AccountType>; // Тип аккаунта
    username: <string>; // Имя пользователя
  };
};

const { [Mutations.Accounts.RegisterAccount.name]: result } = await client.Mutation(
  Mutations.Accounts.RegisterAccount.mutation,
  { variables }
);

По-умолчанию, каждый пайщик регистрирует новый аккаунт в том кооперативе, в который вступает. Кооператив, при этом, самостоятельно управляет процессом восстановления ключей доступа, в случае их утери. Другими словами, обычно у одного пайщика, который состоит в несколькоих кооперативах - несколько аккаунтов.

Карта пайщика

Карта пайщика (CARDCOOP) - это сервис хранения ключей и данных пайщиков, который позволяет входить в несколько кооперативов без повторной регистрации в них, используя т.н. "быстрый вход" или "войти с помощью CARDCOOP". На текущий момент сервис в разработке. Однако, каждое имя аккаунта, связанные с ним приватные данные пайщика и ключи, могут рассматриваться как виртуальная карта пайщика в кооперативе.

Извлечь аккаунт

🛠️ SDK: Queries.Accounts.GetAccount | 🔗 GraphQL API: Query.getAccount

Запрос извлечения объекта аккаунта по username может быть выполнен председателем или членом совета на любой аккаунт, или пользователем, если он получает свои собственные данные.

Аккаунты извлекаются по именам пользователей с помощью запроса GetAccount. В случае, если у username нет какого-либо уровня аккаунта из представленных выше, то они будут null.

import { Queries } from '@coopenomics/sdk';

const variables: Queries.Accounts.GetAccount.IInput = {
  data: {
    username: <string>; // Имя аккаунта пользователя
  };
};

const { [Queries.Accounts.GetAccount.name]: result } = await client.Query(
  Queries.Accounts.GetAccount.query,
  { variables }
);

Извлечь лист аккаунтов

🛠️ SDK: Queries.Accounts.GetAccounts | 🔗 GraphQL API: Query.getAccounts

Получить лист аккаунтов списком с пагинацией. В качестве пераметров можно передать роль аккаунта, тип сортировки, направление, страницу и лимит.

import { Queries } from '@coopenomics/sdk';

const variables: Queries.Accounts.GetAccounts.IInput = {
  data?: {
    role?: <null | string>;
  };
  options?: {
    limit: <number>; // Количество элементов на странице
    page: <number>; // Номер страницы
    sortBy?: <null | string>; // Ключ сортировки (например, "name")
    sortOrder: <string>; // Направление сортировки ("ASC" или "DESC")
  };
};

const { [Queries.Accounts.GetAccounts.name]: result } = await client.Query(
  Queries.Accounts.GetAccounts.query,
  { variables }
);

Результат будет представлен в виде объекта с массивом items, где каждый элемент массива - это составной объект аккаунта.

interface IOutput {
  getAccounts: {
    currentPage: <number>; // Текущая страница
    items: <{
        blockchain_account?: {
          account_name: <string>; // Имя аккаунта
          core_liquid_balance?: <null | string>; // Баланс
          cpu_limit: {
            available: <string>; // Доступные ресурсы
            current_used?: <null | string>; // Текущее использование ресурсов
            last_usage_update_time?: <null | string>; // Время последнего обновления использования ресурсов
            max: <string>; // Максимальное количество ресурсов
            used: <string>; // Использовано ресурсов
          };
          cpu_weight: <string>; // Вес CPU
          created: <string>; // Дата создания
          head_block_num: <number>; // Номер последнего блока
          head_block_time: <string>; // Время последнего блока
          last_code_update: <string>; // Время последнего обновления кода
          net_limit: {
            available: <string>; // Доступные ресурсы
            current_used?: <null | string>; // Текущее использование ресурсов
            last_usage_update_time?: <null | string>; // Время последнего обновления использования ресурсов
            max: <string>; // Максимальное количество ресурсов
            used: <string>; // Использовано ресурсов
          };
          net_weight: <string>; // Вес сети
          permissions: <{
              parent: <string>; // Родительское разрешение
              perm_name: <string>; // Имя разрешения
              required_auth: {
                accounts: <{
                    permission: {
                      actor: <...>; // Актор
                      permission: <...>; // Разрешение
                    };
                    weight: <number>; // Вес
                  }[]>; // Уровни разрешений
                keys: <{
                    key: <string>; // Ключ
                    weight: <number>; // Вес
                  }[]>; // Ключи
                threshold: <number>; // Порог
                waits: <{
                    wait_sec: <number>; // Время ожидания в секундах
                    weight: <number>; // Вес
                  }[]>; // Вес ожидания
              };
            }[]>; // Разрешения
          privileged: <boolean>; // Флаг привилегий
          ram_quota: <number>; // Квота RAM
          ram_usage: <number>; // Использование RAM
          refund_request?: {
            cpu_amount: <string>; // Сумма CPU
            net_amount: <string>; // Сумма сети
            owner: <string>; // Владелец
            request_time: <string>; // Время запроса
          };
          rex_info?: <null | string>; // Информация о REX
          self_delegated_bandwidth?: {
            cpu_weight: <string>; // Вес CPU
            from: <string>; // Отправитель
            net_weight: <string>; // Вес сети
            to: <string>; // Получатель
          };
          total_resources?: {
            cpu_weight: <string>; // Вес CPU
            net_weight: <string>; // Вес сети
            owner: <string>; // Владелец
            ram_bytes: <number>; // Используемая RAM
          };
          voter_info?: <null | string>; // Информация о голосовании
        };
        participant_account?: {
          braname?: <null | string>; // Имя кооперативного участка
          created_at: <unknown>; // Время создания записи о члене
          has_vote: <boolean>; // LEGACY Флаг, имеет ли член право голоса
          is_initial: <boolean>; // LEGACY Флаг, внесен ли регистрационный взнос
          is_minimum: <boolean>; // LEGACY Флаг, внесен ли минимальный паевый взнос
          last_min_pay: <unknown>; // Время последнего минимального платежа
          last_update: <unknown>; // Время последнего обновления информации о члене
          status: <string>; // Статус члена кооператива (accepted | blocked)
          type?: <null | string>; // Тип участника (individual | entrepreneur | organization)
          username: <string>; // Уникальное имя члена кооператива
        };
        provider_account?: {
          email: <string>; // Электронная почта пользователя
          has_account: <boolean>; // Есть ли у пользователя аккаунт
          initial_order?: <null | string>; // ID начального заказа
          is_email_verified: <boolean>; // Подтверждена ли электронная почта
          is_registered: <boolean>; // Зарегистрирован ли пользователь
          message?: <null | string>; // Сообщение
          public_key: <string>; // Публичный ключ пользователя
          referer: <string>; // Реферер пользователя
          role: <string>; // Роль пользователя
          status: <UserStatus>; // Статус пользователя
          type: <string>; // Тип пользователя
          username: <string>; // Имя пользователя
        };
        user_account?: {
          meta: <string>; // Метаинформация
          referer: <string>; // Реферал
          registered_at: <string>; // Дата регистрации
          registrator: <string>; // Регистратор
          status: <string>; // Статус аккаунта
          storages: <string[]>; // Список хранилищ
          type: <string>; // Тип учетной записи
          username: <string>; // Имя аккаунта
          verifications: <{
              created_at: <string>; // Дата создания верификации
              is_verified: <boolean>; // Флаг верификации
              last_update: <string>; // Дата последнего обновления верификации
              notice: <string>; // Заметка верификации
              procedure: <string>; // Процедура верификации
              verificator: <string>; // Имя верификатора
            }[]>; // Дата регистрации
        };
        username: <string>; // Имя аккаунта кооператива
      }[]>; // Элементы текущей страницы
    totalCount: <number>; // Общее количество элементов
    totalPages: <number>; // Общее количество страниц
  };
}

Обновить аккаунт

🛠️ SDK: Mutations.Accounts.UpdateAccount | 🔗 GraphQL API: Mutation.updateAccount

Обновление аккаунта пользователя производится по username. Мутация позволяет изменить приватные данные пользователя, а также, адрес электронной почты в MONO. Использовать мутацию может только председатель совета.

import { Mutations } from '@coopenomics/sdk';

const variables: Mutations.Accounts.UpdateAccount.IInput = {
  data: {
    email: <string>; // Электронная почта
    entrepreneur_data?: <null | {
        birthdate: <string>; // Дата рождения
        city: <string>; // Город
        country: <Russia>; // Страна
        details: {
          inn: <string>; // ИНН
          ogrn: <string>; // ОГРН
        };
        first_name: <string>; // Имя
        full_address: <string>; // Полный адрес
        last_name: <string>; // Фамилия
        middle_name: <string>; // Отчество
        phone: <string>; // Телефон
      }>; // Данные индивидуального предпринимателя
    individual_data?: <null | {
        birthdate: <string>; // Дата рождения
        first_name: <string>; // Имя
        full_address: <string>; // Полный адрес
        last_name: <string>; // Фамилия
        middle_name: <string>; // Отчество
        passport?: <null | {
            code: <string>;
            issued_at: <string>;
            issued_by: <string>;
            number: <number>;
            series: <number>;
          }>; // Данные паспорта
        phone: <string>; // Телефон
      }>; // Данные физического лица
    organization_data?: <null | {
        city: <string>; // Город
        country: <string>; // Страна
        details: {
          inn: <string>;
          kpp: <string>;
          ogrn: <string>;
        };
        fact_address: <string>; // Фактический адрес
        full_address: <string>; // Полный адрес
        full_name: <string>; // Полное наименование организации
        phone: <string>; // Телефон
        represented_by: {
          based_on: <string>;
          first_name: <string>;
          last_name: <string>;
          middle_name: <string>;
          position: <string>;
        };
        short_name: <string>; // Краткое наименование организации
        type: <OrganizationType>; // Тип организации
      }>; // Данные организации
    public_key?: <null | string>; // Публичный ключ
    referer?: <null | string>; // Имя аккаунта реферера
    role: <User>; // Роль пользователя
    type: <AccountType>; // Тип аккаунта
    username: <string>; // Имя пользователя
  };
};

const { [Mutations.Accounts.UpdateAccount.name]: result } = await client.Mutation(
  Mutations.Accounts.UpdateAccount.mutation,
  { variables }
);

Обновление персональных данных в базе MONO производится добавлением нового объекта с указанием последнего необратимого блока в блокчейне, что позволяет использовать исторические данные для сверки и восстановления документов. Вся история изменений данных аккаунтов сохраняется.

Восстановить доступ к аккаунту

В случае утери ключа доступа к аккаунту - ключ возможно заменить. Для этого, необходимо последовательно применить две мутации: StartResetKey - для получения токена на замену ключа и ResetKey - мутация для непосредственной замены ключа доступа для аккаунта.

Получить токен для замены

🛠️ SDK: Mutations.Accounts.StartResetKey | 🔗 GraphQL API: Mutation.startResetKey

Метод запуска процесса замены ключа. Замена ключа в блокчейне производится за подписью кооператива. Для того, чтобы произвести замену, пайщику необходим токен доступа, который будет отправлен ему на почту после вызова этой мутации.

import { Mutations } from '@coopenomics/sdk';

const variables: Mutations.Accounts.StartResetKey.IInput = {
  data: {
    email: <string>; // Электронная почта
  };
};

const { [Mutations.Accounts.StartResetKey.name]: result } = await client.Mutation(
  Mutations.Accounts.StartResetKey.mutation,
  { variables }
);

Заменить ключ

🛠️ SDK: Mutations.Accounts.ResetKey | 🔗 GraphQL API: Mutation.resetKey

Метод производит замену публичного ключа аккаунта в блокчейне на переданный. На текущий момент здесь не используется каких-либо дополнительных децентрализованных сценариев проверки. Считаем, что если кооператив меняет ключ пайщика - значит так надо, т.к. обычно сейчас один пайщик только с одним аккаунтом и является пайщиком только одного кооператива.

Однако, в дальнейшем, при введении функционала "карты пайщика", которая сделает допустимым "быстрый вход" для пайщиков одного кооператива - в другой, система замены ключа будет требовать подтверждения нескольких кооперативов.

import { Mutations } from '@coopenomics/sdk';

const variables: Mutations.Accounts.ResetKey.IInput = {
  data: {
    public_key: <string>; // Публичный ключ для замены
    token: <string>; // Токен авторизации для замены ключа, полученный по email
  };
};

const { [Mutations.Accounts.ResetKey.name]: result } = await client.Mutation(
  Mutations.Accounts.ResetKey.mutation,
  { variables }
);