npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hlf-core/coin-chaincode

v4.0.1

Published

TypeScript library for token management on the Hyperledger Fabric chaincode side: accounts, emission, transfers, hold and burning with event dispatching

Readme

@hlf-core/coin-chaincode

TypeScript библиотека управления токенами на стороне chaincode: счета, эмиссия, переводы, удержание

npm version License: ISC

Модуль для управления токенами (монетами) и их счетами в блокчейн-среде Hyperledger Fabric. Реализует полный набор операций для создания, эмиссии, перевода, удержания и сжигания цифровых активов с поддержкой событий и строгой типизацией.

🚀 Возможности

  • Полный жизненный цикл токенов: создание, эмиссия, сжигание, перевод
  • Система удержания (hold): временная блокировка средств с возможностью разблокировки
  • Гибкая архитектура: модульная структура с возможностью расширения
  • Строгая типизация: полная поддержка TypeScript
  • Событийная модель: автоматическая генерация событий для интеграции
  • Обработка ошибок: кастомные исключения с детальной информацией
  • Атомарные операции: гарантия консистентности данных

📋 Содержание

🛠 Установка

npm install @hlf-core/coin-chaincode

Зависимости

{
  "@hlf-core/coin": "~3.2.33",
  "@hlf-core/chaincode": "~3.6.4"
}

Транзитивно подтягиваются @ts-core/common (v3.x, через @hlf-core/chaincode/@hlf-core/coin), @hlf-core/common, fabric-shim, reflect-metadata и lodash.

🚀 Быстрый старт

import { CoinService, CoinManager } from '@hlf-core/coin-chaincode';
import { ILogger, IStub } from '@ts-core/common';

// Создание сервиса
const logger: ILogger = /* ваш логгер */;
const service = new CoinService(logger);

// Создание менеджера
const stub: IStub = /* ваш stub */;
const manager = new CoinManager(logger, stub);

// Создание монеты
const coin = manager.create('MYCOIN', 18, 'owner-uid');

// Эмиссия токенов
await manager.emit(coin, 'user-uid', '1000');

// Перевод между пользователями
await manager.transfer(coin, 'user-uid', 'another-user-uid', '100');

🏗 Архитектура

Основные компоненты

1. CoinManager - Основной менеджер монет

class CoinManager<T extends ICoin = ICoin> extends EntityManagerImpl<T>
  • Управляет жизненным циклом монет
  • Реализует все операции с токенами
  • Работает с CoinAccountManager для управления счетами

2. CoinAccountManager - Менеджер счетов

class CoinAccountManager extends EntityManagerImpl<CoinAccount>
  • Управляет счетами пользователей для каждой монеты
  • Автоматически удаляет пустые счета
  • Предоставляет методы для работы с балансами

3. CoinService - Сервисный слой

class CoinService<H extends IStubHolder = IStubHolder> extends LoggerWrapper
  • Фасад для бизнес-логики
  • Валидация входных параметров
  • Диспатчинг событий
  • Обработка ошибок

4. Error Classes - Система ошибок

class CoinNotFoundError extends Error<string>
class CoinObjectNotFoundError extends Error<string>
class CoinFromToEqualsError extends Error<string>

Диаграмма архитектуры

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   CoinService   │────│   CoinManager    │────│ CoinAccountMgr  │
│                 │    │                  │    │                 │
│ • Валидация     │    │ • CRUD монет     │    │ • CRUD счетов   │
│ • События       │    │ • Операции       │    │ • Балансы       │
│ • Ошибки        │    │ • Переводы       │    │ • Удержания     │
└─────────────────┘    └──────────────────┘    └─────────────────┘
         │                       │                       │
         └───────────────────────┼───────────────────────┘
                                 │
                    ┌─────────────────┐
                    │   IStub/State   │
                    │                 │
                    │ • Блокчейн      │
                    │ • Персистентность│
                    └─────────────────┘

Хранение счетов в состоянии

Монета и её счета — разные записи состояния. Монета лежит по собственному uid, а каждый счёт — по составному ключу «пространство имён + монета + владелец + счёт»:

coin_owner1_8_BTC                              ← сама монета
→coin~account:coin_owner1_8_BTC~user1~OWNER    ← основной счёт user1
→coin~account:coin_owner1_8_BTC~user1~PAYOUT   ← второй счёт того же владельца
→coin~account:coin_owner1_8_BTC~user2~OWNER    ← счёт user2

Последняя часть ключа — идентификатор счёта. У одного владельца по одной монете может быть несколько счетов с разным назначением; менеджер их различает, но о смысле значений не знает — словарь задаёт приложение.

Такая схема выбрана потому, что в состоянии Hyperledger Fabric нет поиска по полям: найти запись можно только по точному ключу либо диапазонным запросом по началу ключа. Порядок частей выбран под запросы: монета перед владельцем — все счета одной монеты лежат подряд и выбираются одним диапазонным запросом; владелец перед счётом — так же подряд лежат все счета одного лица.

Ключи собирает CoinAccountUtil из @hlf-core/coin, менеджеры не склеивают строки самостоятельно:

// точный ключ одного счёта — используется в accountGet
CoinAccountUtil.createUid(coinUid, ownerUid, id);      // →coin~account:coin_owner1_8_BTC~user1~OWNER

// префикс всех счетов монеты — используется в accountList и accountsRemove
CoinAccountUtil.createPrefix(coinUid);                 // →coin~account:coin_owner1_8_BTC~

// префикс всех счетов одного владельца — используется в accountList с владельцем
CoinAccountUtil.createPrefix(coinUid, ownerUid);       // →coin~account:coin_owner1_8_BTC~user1~

Префикс обязательно заканчивается разделителем: без него диапазонный запрос захватил бы счета монет, чей идентификатор начинается так же (BTC и BTC2, базовая монета и её серийные выпуски).

Пустые счета не хранятся

CoinAccountManager.save удаляет счёт вместо записи, если он пуст:

public async save(item: CoinAccount): Promise<CoinAccount> {
    if (item.isEmpty()) {
        await this.remove(item);
        return item;
    }
    return super.save(item);
}

Поэтому состояние не засоряется нулевыми счетами, а accountList возвращает только те счета, где есть баланс. Обратная сторона: accountGet для адреса без токенов не найдёт записи и вернёт новый пустой счёт, а не null — он попадёт в состояние только после первой операции с ненулевой суммой.

Удаление монеты

CoinManager.remove удаляет монету вместе со всеми её счетами:

public async remove(item: UID): Promise<void> {
    await this.accountsRemove(item);   // все счета монеты по префиксу
    await super.remove(item);          // сама монета
}

Счета удаляются одной транзакцией, без разбиения на части. Для монеты с большим числом держателей объём изменений растёт линейно и может упереться в ограничения Fabric на размер набора записей или время подтверждения — в таком случае удаление монеты не пройдёт целиком, а не частично.

📚 API Документация

CoinManager

Основные операции

// Создание монеты
create(coinId: string, decimals: number, owner: UID): T

// Получение монеты
get(item: UID, details?: Array<keyof ICoin>): Promise<T>

// Сохранение монеты
save(item: T): Promise<T>

// Удаление монеты и всех связанных счетов
remove(item: UID): Promise<void>

Операции с токенами

// Эмиссия токенов
emit(coin: T | string, object: ICoinAccountObject, value: string): Promise<ICoinMovement>
emitHeld(coin: T | string, object: ICoinAccountObject, value: string): Promise<ICoinMovement>

// Сжигание токенов
burn(coin: T | string, object: ICoinAccountObject, value: string): Promise<ICoinMovement>
burnHeld(coin: T | string, object: ICoinAccountObject, value: string): Promise<ICoinMovement>

// Удержание токенов (hold)
hold(coin: T | string, object: ICoinAccountObject, value: string): Promise<ICoinMovement>
unhold(coin: T | string, object: ICoinAccountObject, value: string): Promise<ICoinMovement>

// Нулификация (обнуление баланса)
nullify(coin: T | string, object: ICoinAccountObject): Promise<ICoinNullify>
nullifyHeld(coin: T | string, object: ICoinAccountObject): Promise<ICoinNullify>

// Переводы
transfer(coin: T | string, object: ICoinAccountObject, target: ICoinAccountObject, value: string): Promise<ICoinTransfer>
transferToHeld(coin: T | string, object: ICoinAccountObject, target: ICoinAccountObject, value: string): Promise<ICoinTransfer>
transferFromHeld(coin: T | string, object: ICoinAccountObject, target: ICoinAccountObject, value: string): Promise<ICoinTransfer>
transferFromToHeld(coin: T | string, object: ICoinAccountObject, target: ICoinAccountObject, value: string): Promise<ICoinTransfer>

Параметры:

  • coin - UID монеты или объект монеты
  • object - адрес счёта, с которым выполняется операция: { ownerUid, id }
  • target - адрес счёта получателя (для transfer операций)
  • value - Сумма операции в строковом формате

Адрес счёта — пара «владелец + идентификатор счёта». В переводах их два подряд, и объект различает стороны структурой: позиционные строки здесь легко переставить местами, а компилятор бы промолчал.

Работа со счетами

// Получение счета
accountGet(coin: UID, object: ICoinAccountObject): Promise<ICoinAccount>

// Сохранение счета
accountSet(item: ICoinAccount): Promise<ICoinAccount>

// Список счетов монеты, либо только счета одного владельца
accountList(coin: UID, owner?: UID): Promise<Array<ICoinAccount>>

CoinService

Основные методы

// Эмиссия с валидацией и событиями
emit(holder: H, params: ICoinEmitDto, isDispatchEvent: boolean): Promise<void>
emitHeld(holder: H, params: ICoinEmitDto, isDispatchEvent: boolean): Promise<void>

// Сжигание с валидацией и событиями
burn(holder: H, params: ICoinBurnDto, isDispatchEvent: boolean): Promise<void>
burnHeld(holder: H, params: ICoinBurnDto, isDispatchEvent: boolean): Promise<void>

// Нулификация с валидацией и событиями
nullify(holder: H, params: ICoinNullifyDto, isDispatchEvent: boolean): Promise<void>
nullifyHeld(holder: H, params: ICoinNullifyDto, isDispatchEvent: boolean): Promise<void>

// Удержание с валидацией и событиями
hold(holder: H, params: ICoinHoldDto, isDispatchEvent: boolean): Promise<void>
unhold(holder: H, params: ICoinUnholdDto, isDispatchEvent: boolean): Promise<void>

// Переводы с валидацией и событиями
transfer(holder: H, params: ICoinTransferDto, isDispatchEvent: boolean): Promise<void>
transferToHeld(holder: H, params: ICoinTransferDto, isDispatchEvent: boolean): Promise<void>
transferFromHeld(holder: H, params: ICoinTransferDto, isDispatchEvent: boolean): Promise<void>
transferFromToHeld(holder: H, params: ICoinTransferDto, isDispatchEvent: boolean): Promise<void>

// Получение информации
get<T extends ICoin>(holder: H, params: ICoinGetDto): Promise<T>
balanceGet(holder: H, params: ICoinBalanceGetDto): Promise<ICoinBalanceGetDtoResponse>

Параметры:

  • holder - Объект с доступом к stub (IStubHolder)
  • params - DTO с параметрами операции
  • isDispatchEvent - Генерировать ли события

💡 Примеры использования

Создание и настройка токена

import { CoinService, CoinManager } from '@hlf-core/coin-chaincode';

const service = new CoinService(logger);
const manager = new CoinManager(logger, stub);

// Создание токена с 18 знаками после запятой
const token = manager.create('USDT', 18, 'issuer-uid');
await manager.save(token);

console.log(`Создан токен: ${token.id}, владелец: ${token.owner}`);

Эмиссия и распределение токенов

// Эмиссия 1,000,000 токенов на счет эмитента
await service.emit(holder, {
    coinUid: token.uid,
    object: { ownerUid: 'issuer-uid', id: 'OWNER' },
    value: '1000000'
}, true);

// Распределение токенов пользователям
const users = ['user1', 'user2', 'user3'];
for (const user of users) {
    await service.transfer(holder, {
        coinUid: token.uid,
        object: { ownerUid: 'issuer-uid', id: 'OWNER' },
        target: { ownerUid: user, id: 'OWNER' },
        value: '1000',
        initiatorUid: 'issuer-uid'
    }, true);
}

Система удержания (escrow)

// Удержание 100 токенов на счете пользователя
await service.hold(holder, {
    coinUid: token.uid,
    object: { ownerUid: 'user1', id: 'OWNER' },
    value: '100',
    initiatorUid: 'escrow-service'
}, true);

// Проверка баланса (включая удержанные средства)
const balance = await service.balanceGet(holder, {
    coinUid: token.uid,
    object: { ownerUid: 'user1', id: 'OWNER' }
});

console.log(`Доступно: ${balance.inUse}, удержано: ${balance.held}, всего: ${balance.total}`);

// Разблокировка удержанных средств
await service.unhold(holder, {
    coinUid: token.uid,
    object: { ownerUid: 'user1', id: 'OWNER' },
    value: '100',
    initiatorUid: 'escrow-service'
}, true);

Сложные переводы

// Перевод из удержанных средств в обычные
await service.transferFromHeld(holder, {
    coinUid: token.uid,
    object: { ownerUid: 'user1', id: 'OWNER' },
    target: { ownerUid: 'user2', id: 'OWNER' },
    value: '50',
    initiatorUid: 'payment-service'
}, true);

// Перевод в удержание
await service.transferToHeld(holder, {
    coinUid: token.uid,
    object: { ownerUid: 'user2', id: 'OWNER' },
    target: { ownerUid: 'user3', id: 'OWNER' },
    value: '25',
    initiatorUid: 'escrow-service'
}, true);

// Перевод из удержания отправителя в удержание получателя
await service.transferFromToHeld(holder, {
    coinUid: token.uid,
    object: { ownerUid: 'user3', id: 'OWNER' },
    target: { ownerUid: 'user1', id: 'OWNER' },
    value: '10',
    initiatorUid: 'escrow-service'
}, true);

Нулификация балансов

// Обнулить обычный баланс пользователя
await service.nullify(holder, {
    coinUid: token.uid,
    object: { ownerUid: 'user1', id: 'OWNER' }
}, true);

// Обнулить удержанный баланс
await service.nullifyHeld(holder, {
    coinUid: token.uid,
    object: { ownerUid: 'user1', id: 'OWNER' }
}, true);

Обработка ошибок

import { CoinNotFoundError, CoinFromToEqualsError } from '@hlf-core/coin-chaincode';

try {
    await service.transfer(holder, {
        coinUid: 'non-existent-coin',
        object: { ownerUid: 'user1', id: 'OWNER' },
        target: { ownerUid: 'user2', id: 'OWNER' },
        value: '100',
        initiatorUid: 'user1'
    }, true);
} catch (error) {
    if (CoinNotFoundError.instanceOf(error)) {
        console.error('Монета не найдена:', error.details);
    } else if (CoinFromToEqualsError.instanceOf(error)) {
        console.error('Отправитель и получатель одинаковые:', error.details);
    } else {
        console.error('Неизвестная ошибка:', error);
    }
}

📦 DTO Интерфейсы

ICoinTransferDto

interface ICoinTransferDto {
    coinUid: string;        // UID монеты
    object: ICoinAccountObject;  // адрес счёта отправителя
    target: ICoinAccountObject;  // адрес счёта получателя
    value: string;          // Сумма перевода
    initiatorUid?: string;  // UID инициатора операции
}

ICoinEmitDto / ICoinBurnDto

interface ICoinEmitDto {
    coinUid: string;        // UID монеты
    object: ICoinAccountObject;  // адрес счёта: получателя для emit, отправителя для burn
    value: string;          // Сумма эмиссии/сжигания
    initiatorUid?: string;  // UID инициатора операции
}

ICoinHoldDto / ICoinUnholdDto

interface ICoinHoldDto {
    coinUid: string;        // UID монеты
    object: ICoinAccountObject;  // адрес счёта, где удерживаются средства
    value: string;          // Сумма удержания/разблокировки
    initiatorUid?: string;  // UID инициатора операции
}

ICoinNullifyDto

interface ICoinNullifyDto {
    coinUid: string;        // UID монеты
    object: ICoinAccountObject;  // адрес счёта
}

ICoinBalanceGetDto

interface ICoinBalanceGetDto {
    coinUid: string;        // UID монеты
    object: ICoinAccountObject;  // адрес счёта
}

ICoinBalanceGetDtoResponse

interface ICoinBalanceGetDtoResponse {
    inUse: string;          // Доступный баланс
    held: string;           // Удержанный баланс
    total: string;          // Общий баланс (inUse + held)
}

📡 События

Библиотека генерирует события для интеграции с внешними системами:

CoinEmittedEvent

Генерируется при эмиссии токенов (emit/emitHeld).

{
    coinUid: string;
    object: ICoinAccountObject;
    value: string;
}

CoinBurnedEvent

Генерируется при сжигании токенов (burn/burnHeld).

{
    coinUid: string;
    object: ICoinAccountObject;
    value: string;
}

CoinTransferredEvent

Генерируется при переводе токенов.

{
    coinUid: string;
    object: ICoinAccountObject;
    target: ICoinAccountObject;
    value: string;
    initiatorUid?: string;
}

CoinHoldedEvent

Генерируется при удержании средств.

{
    coinUid: string;
    object: ICoinAccountObject;
    value: string;
    initiatorUid?: string;
}

CoinUnholdedEvent

Генерируется при разблокировке удержанных средств.

{
    coinUid: string;
    object: ICoinAccountObject;
    value: string;
    initiatorUid?: string;
}

CoinNullifiedEvent

Генерируется при нулификации баланса.

{
    coinUid: string;
    object: ICoinAccountObject;
}

📁 Структура проекта

src/
├── CoinManager.ts          # Основной менеджер монет
├── CoinAccountManager.ts   # Менеджер счетов
├── CoinService.ts          # Сервисный слой
├── ICoinManager.ts         # Интерфейс менеджера
├── Error.ts                # Кастомные ошибки
└── public-api.ts           # Публичный API

Основные файлы

  • CoinManager.ts - Реализация всех операций с монетами
  • CoinAccountManager.ts - Управление счетами пользователей
  • CoinService.ts - Бизнес-логика с валидацией и событиями
  • ICoinManager.ts - Контракт для менеджера монет
  • Error.ts - Система обработки ошибок

🔧 Разработка

Установка зависимостей

npm install

Сборка

npm run build

Тестирование

npm test

Линтинг

npm run lint

⚠️ Важные замечания

Безопасность

  • Библиотека не проверяет права доступа - это ответственность вызывающего кода
  • Все операции должны проходить через endorsement policy сети Hyperledger Fabric
  • Валидация amount/value выполняется на уровне CoinAccount

Атомарность

  • Операции transfer выполняют множественные read/write операции
  • Консистентность гарантируется через read-write sets механизм Hyperledger Fabric
  • Параллельные транзакции могут привести к MVCC_READ_CONFLICT

Производительность

  • Методы transfer делают 6+ операций с state
  • Рекомендуется батчинг операций где возможно
  • Пустые счета автоматически удаляются для экономии места
  • accountList и удаление монеты обходят все счета монеты в одной транзакции — стоимость линейна по числу держателей и на крупных выпусках упирается в лимиты Fabric

Лицензия

ISC

Автор

Ренат Губаев

Ссылки

Поддержка

Если вы нашли баг или у вас есть предложение по улучшению, пожалуйста, создайте issue в GitHub Issues.