Compare commits
212 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a450e2ac6 | |||
| 9fc87a705f | |||
| 305864c1ed | |||
| ad83d9cc77 | |||
| 226c7f9473 | |||
| 137d1ca8a4 | |||
| 59744e9d23 | |||
| ac8836628e | |||
| 1f649c8951 | |||
| 310468fb69 | |||
| 7f782e0a92 | |||
| d0c9f8bffc | |||
| 60a35f6f76 | |||
| a3a2923751 | |||
| 32e6a45f05 | |||
| 59665c6df4 | |||
| d23b4e3aac | |||
| 20897cf199 | |||
| 0b57232606 | |||
| 1bd8f003f7 | |||
| fcb614cd90 | |||
| 1c376c003a | |||
| a433ceaf1d | |||
| 850478b7f7 | |||
| 11a891392e | |||
| 7930ef8ef6 | |||
| 8484a14905 | |||
| e7a0968b94 | |||
| 87241b2de0 | |||
| 4a5fb2af8b | |||
| b9acc67818 | |||
| 5be9fa6ae8 | |||
| e0f8d6a64c | |||
| 2276ab396e | |||
| 9de84afc29 | |||
| 747e567666 | |||
| 675376079e | |||
| 1fa0cee816 | |||
| 6a7f71eb5a | |||
| 6bda6948bf | |||
| abb43b4221 | |||
| 4193c45205 | |||
| 36eb50fef9 | |||
| ad0ebf82ee | |||
| 54d55e288f | |||
| 9fb51b00bd | |||
| d56dcc153a | |||
| d9f421acbd | |||
| 01ec687ba0 | |||
| 0ee1efac4e | |||
| fbf9d514a0 | |||
| be7a9e9e3a | |||
| bc3b3f701f | |||
| 0d8be7a489 | |||
| 2fdeaed3a0 | |||
| 13481d8f95 | |||
| f4b87baa2b | |||
| a337dbceb8 | |||
| 04592a9908 | |||
| 9d204ba496 | |||
| 64dd48b408 | |||
| 515a374f86 | |||
| ddae8d6a53 | |||
| 6e87047e1d | |||
| 62e13853c5 | |||
| a09959d751 | |||
| e2678df443 | |||
| fedec69e44 | |||
| 4ccf2c743c | |||
| 418d43af58 | |||
| 869a75f080 | |||
| c7bc2d1a20 | |||
| 3ea4cd6d73 | |||
| 6a955737e0 | |||
| 51bf49b063 | |||
| 12468b16c8 | |||
| 5ebd28d181 | |||
| defe482ccd | |||
| e99d141f28 | |||
| aa0a6731de | |||
| 46f7f10dce | |||
| 552333faf6 | |||
| e23589d4d3 | |||
| 104b08e1ba | |||
| 1b81cfc0a9 | |||
| 6e51433ae9 | |||
| 421f356987 | |||
| abd9f1931d | |||
| 2b87034abb | |||
| 8d7c919332 | |||
| 4785ea2886 | |||
| 4daa06dac1 | |||
| abc253f47d | |||
| 5fea2f8f34 | |||
| c29e224f2f | |||
| b930543c2b | |||
| 2768d72e90 | |||
| 8ccefe8fac | |||
| c93e59908d | |||
| 22f0fd8a10 | |||
| 8e17d96529 | |||
| 494a164ee8 | |||
| ee28ae1719 | |||
| 3266061359 | |||
| f69d849c4f | |||
| 2628120e35 | |||
| edf7d9ab62 | |||
| 2095e448e5 | |||
| beff507f4a | |||
| bf4fbfabac | |||
| 2f1d88a74c | |||
| b444004f4a | |||
| 6862f2881a | |||
| 73afcabee2 | |||
| b4168a4053 | |||
| 1fe14d5811 | |||
| 89e5c97ef8 | |||
| 258b8304ff | |||
| 69fe9c1348 | |||
| fa4af94fa0 | |||
| 2ac84a6910 | |||
| 3f79cf9a1b | |||
| c155605ca8 | |||
| 80a94f79da | |||
| 89ba6cb356 | |||
| c7bd0326ba | |||
| 2c551a0d9e | |||
| ba103a969e | |||
| 0d9177f98f | |||
| 60c32be299 | |||
| 83c107b8b2 | |||
| b6d582fb2c | |||
| a5818bdc53 | |||
| de0fe5465f | |||
| d43b188a30 | |||
| bfb2be2a72 | |||
| 73c2cd9cb3 | |||
| 61cfc442ef | |||
| 0ed04c2c27 | |||
| 6773509ede | |||
| acd86df785 | |||
| c554b167b3 | |||
| 18cf7b61e3 | |||
| e42b396a68 | |||
| c47e61d42a | |||
| 92559370fa | |||
| c3a2ec46b3 | |||
| fe55d23e65 | |||
| b0d4f5d542 | |||
| be48f7a158 | |||
| 83bd744100 | |||
| ff1615f7b1 | |||
| 696e1a0d2c | |||
| 397670473a | |||
| cab8061bc3 | |||
| 3c2b9bcb4c | |||
| 7a274b7ab1 | |||
| e524244bab | |||
| 14ecaf259a | |||
| 749c13dc91 | |||
| ab2ee4cc92 | |||
| 2907352d61 | |||
| 390b995eb6 | |||
| 24b3dd3c60 | |||
| 55444a4b72 | |||
| 76854207a7 | |||
| 4fcd700e39 | |||
| 05dfcced1b | |||
| 931c366907 | |||
| 71d35b74d7 | |||
| 21fac5c7df | |||
| 0ba9d55081 | |||
| b5b388e95d | |||
| 9c643395b3 | |||
| b989074170 | |||
| 261f010a49 | |||
| af46867939 | |||
| be2355e5c5 | |||
| 8b4a20ed3f | |||
| 1ab1e1d3c5 | |||
| c9aab02aab | |||
| 0643604778 | |||
| fe79f3ff98 | |||
| f919a14927 | |||
| dda389ad75 | |||
| aa42864029 | |||
| 41bb17224a | |||
| fd6f628c43 | |||
| 28ca615d96 | |||
| 76203a88d8 | |||
| aa891adddf | |||
| ecdac6fff9 | |||
| bcba24ea5e | |||
| 44b1f423cc | |||
| 85cc7361d7 | |||
| e5e28ff652 | |||
| 4957182480 | |||
| 84927e943a | |||
| 0d06114f00 | |||
| 2654760e16 | |||
| d02e7ba2be | |||
| e18fd94336 | |||
| 03e2410556 | |||
| 0f1f34f621 | |||
| c08473618f | |||
| 3b4fb195bb | |||
| 827eb10cc2 | |||
| 6e7c60de6e | |||
| 54a4449e0b | |||
| a6011f5d46 | |||
| a1300e2b3c | |||
| 8f58672403 |
@@ -1,8 +0,0 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
ВАЖНО!!! Мы работаем в МОНО-репозитории pnpm, любая установка пакетов производится ЧЕРЕЗ ФИЛЬТР компонента в корне: pnpm add glob --filter notificator2.
|
||||
|
||||
ВАЖНО!!! НИКОГДА НЕ ЗАПУСКАЙ ПРИЛОЖЕНИЕ ИЛИ ЕГО СБОРКУ!!! Просто отчитайся что всё сделал.
|
||||
@@ -1,6 +1,9 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# DESKTOP
|
||||
|
||||
- Пишем код фронтенда в архитектуре Feature Sliced Design (FSD).
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
Файл [main.py](mdc:monocoop/monocoop/monocoop/components/docs/main.py) автоматически генерит ссылки на документацию SDK и GraphQL, которые формируются и публикуются автоматически. При создании документации к методам всегда применяй ссылки на SDK и GraphQL по форме:
|
||||
{{ get_sdk_doc("Mutations", "Accounts", "RegisterAccount") }} | {{ get_graphql_doc("Mutation.registerAccount") }}
|
||||
|
||||
### 🎯 Основные паттерны использования
|
||||
|
||||
**1. Стандартная структура для методов API:**
|
||||
```markdown
|
||||
## Название действия
|
||||
{{ get_sdk_doc("Namespace", "Module", "Method") }} | {{ get_graphql_doc("Type.methodName") }}
|
||||
|
||||
{{ get_typedoc_input("Namespace.Module.Method") }}
|
||||
|
||||
Результат:
|
||||
{{ get_typedoc_definition("Namespace.Module.Method", "IOutput") }}
|
||||
```
|
||||
|
||||
**2. Описание + пример (для сложных методов):**
|
||||
```markdown
|
||||
{{ get_typedoc_desc("Namespace.Module.Method") }}
|
||||
|
||||
{{ get_typedoc_input("Namespace.Module.Method") }}
|
||||
```
|
||||
|
||||
**3. Ссылки на типы данных:**
|
||||
```markdown
|
||||
У каждого аккаунта есть объект {{ get_graphql_definition("Account") }} в GraphQL-API
|
||||
```
|
||||
|
||||
### 🔧 Макросы и их применение
|
||||
|
||||
| Макрос | Назначение | Пример использования |
|
||||
|--------|------------|---------------------|
|
||||
| `get_sdk_doc` | Ссылка на SDK документацию | `{{ get_sdk_doc("Mutations", "Payments", "CreateDeposit") }}` |
|
||||
| `get_graphql_doc` | Ссылка на GraphQL операцию | `{{ get_graphql_doc("Query.getAccount") }}` |
|
||||
| `get_graphql_definition` | Ссылка на GraphQL тип | `{{ get_graphql_definition("Account") }}` |
|
||||
| `get_class_doc` | Ссылка на класс/метод SDK | `{{ get_class_doc("Document", "sign") }}` |
|
||||
| `get_typedoc_input` | **Полный пример** TypeScript вызова | `{{ get_typedoc_input("Mutations.Auth.Login") }}` |
|
||||
| `get_typedoc_definition` | **Структура интерфейса** | `{{ get_typedoc_definition("Mutations.Auth.Login", "IOutput") }}` |
|
||||
| `get_typedoc_desc` | Описание + примеры | `{{ get_typedoc_desc("Mutations.Auth.Login") }}` |
|
||||
| `get_typedoc_value` | Значение константы | `{{ get_typedoc_value("Constants.API_VERSION") }}` |
|
||||
|
||||
### 📝 Правила оформления
|
||||
|
||||
**ОБЯЗАТЕЛЬНО:**
|
||||
- Всегда используй `get_typedoc_input` для демонстрации **КАК вызывать** метод
|
||||
- Всегда используй `get_typedoc_definition` для показа **ЧТО возвращается**
|
||||
- Комбинируй SDK и GraphQL ссылки через ` | `: `{{ get_sdk_doc(...) }} | {{ get_graphql_doc(...) }}`
|
||||
|
||||
**ЖЕЛАТЕЛЬНО:**
|
||||
- Для сложных методов добавляй `get_typedoc_desc` в начало
|
||||
- Используй `get_graphql_definition` для ссылок на типы данных в тексте
|
||||
- Группируй связанные операции в одном разделе
|
||||
|
||||
**ФОРМАТ ССЫЛОК:**
|
||||
- SDK: `"Namespace", "Module", "Method"` (3 аргумента)
|
||||
- GraphQL: `"Type.methodName"` (точка между типом и методом)
|
||||
- Определения: просто имя типа `"TypeName"`
|
||||
@@ -76,14 +76,7 @@ jobs:
|
||||
echo "CMD [\"pnpm\", \"-F\", \"coop-notificator\", \"run\", \"start\"]" >> Dockerfile.notificator
|
||||
docker build -t dicoop/notificator:${{ env.DOCKER_TAG }} -f Dockerfile.notificator .
|
||||
docker push dicoop/notificator:${{ env.DOCKER_TAG }}
|
||||
|
||||
- name: Build notifications image
|
||||
run: |
|
||||
echo "FROM dicoop/mono-base:${{ env.DOCKER_TAG }}" > Dockerfile.notifications
|
||||
echo "CMD [\"pnpm\", \"-F\", \"@coopenomics/notifications\", \"run\", \"sync\"]" >> Dockerfile.notifications
|
||||
docker build -t dicoop/notifications:${{ env.DOCKER_TAG }} -f Dockerfile.notifications .
|
||||
docker push dicoop/notifications:${{ env.DOCKER_TAG }}
|
||||
|
||||
|
||||
# Отправка хука для деплоя
|
||||
- name: Trigger deployment webhook
|
||||
if: ${{ success() }}
|
||||
@@ -103,20 +96,4 @@ jobs:
|
||||
curl -X POST ${{ vars.TESTNET_WEBHOOK_URL }} \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '${{ env.DOCKER_TAG }}'
|
||||
fi
|
||||
|
||||
# Уведомление в Telegram об успехе
|
||||
- name: Telegram notify success
|
||||
if: ${{ success() }}
|
||||
run: |
|
||||
curl -s -X POST https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage \
|
||||
-d chat_id=${{ secrets.TELEGRAM_CHAT_ID }} \
|
||||
-d text="✅ [GITHUB MONO] Успешная сборка контейнеров: $GITHUB_REPOSITORY ($GITHUB_REF) [${{ env.DOCKER_TAG }}]"
|
||||
|
||||
# Уведомление в Telegram об ошибке
|
||||
- name: Telegram notify failure
|
||||
if: ${{ failure() }}
|
||||
run: |
|
||||
curl -s -X POST https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage \
|
||||
-d chat_id=${{ secrets.TELEGRAM_CHAT_ID }} \
|
||||
-d text="❌ [GITHUB MONO] Ошибка при сборке контейнеров: $GITHUB_REPOSITORY ($GITHUB_REF) [${{ env.DOCKER_TAG }}]"
|
||||
fi
|
||||
@@ -1,18 +0,0 @@
|
||||
# .github/workflows/trigger-coopenomics.yml
|
||||
name: Trigger Contracts Docs Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [dev, testnet, main, capital] # или когда нужно триггерить
|
||||
|
||||
jobs:
|
||||
trigger-coopenomics:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Trigger Coopenomics deployment
|
||||
uses: peter-evans/repository-dispatch@v2
|
||||
with:
|
||||
token: ${{ secrets.COOPENOMICS_PAT }}
|
||||
repository: coopenomics/coopenomics # укажи правильный owner/repo
|
||||
event-type: deploy_from_mono
|
||||
client-payload: '{"repository": "${{ github.repository }}", "sha": "${{ github.sha }}", "ref": "${{ github.ref }}", "actor": "${{ github.actor }}"}'
|
||||
@@ -5,7 +5,3 @@ blockchain-data/
|
||||
scripts/changelog-prompt.md
|
||||
scripts/changelog-release.md
|
||||
scripts/release-info.md
|
||||
components/docs/docs/sdk
|
||||
dist/
|
||||
.env
|
||||
.DS_Store
|
||||
|
||||
Vendored
+7
-47
@@ -3,57 +3,17 @@
|
||||
"editor.guides.bracketPairs": true,
|
||||
"editor.tabCompletion": "onlySnippets",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
// "editor.formatOnSave": true,
|
||||
// "editor.codeActionsOnSave": {
|
||||
// "source.addMissingImports.ts": "explicit",
|
||||
// "source.fixAll.ts": "explicit",
|
||||
// "source.fixAll.eslint": "explicit"
|
||||
// },
|
||||
"eslint.run": "onType",
|
||||
"eslint.validate": ["javascript", "typescript", "vue"],
|
||||
"i18n-ally.localesPaths": ["src/i18n"],
|
||||
"typescript.format.enable": true,
|
||||
"typescript.format.indentSwitchCase": false,
|
||||
"typescript.validate.enable": true,
|
||||
"notebook.defaultFormatter": "Vue.volar",
|
||||
|
||||
// Оптимизация для монорепозитория
|
||||
"typescript.preferences.useAliasesForRenames": false,
|
||||
"typescript.preferences.include": false,
|
||||
"typescript.disableAutomaticTypeAcquisition": true,
|
||||
"typescript.preferences.includePackageJsonAutoImports": "off",
|
||||
"typescript.suggest.autoImports": true,
|
||||
"typescript.suggest.paths": true,
|
||||
"typescript.updateImportsOnFileMove.enabled": "always",
|
||||
"typescript.workspaceSymbols.scope": "currentProject",
|
||||
|
||||
// Настройки для снижения нагрузки
|
||||
"files.watcherExclude": {
|
||||
"**/node_modules/**": true,
|
||||
"**/dist/**": true,
|
||||
"**/.git/objects/**": true,
|
||||
"**/.git/subtree-cache/**": true,
|
||||
"**/node_modules/*/**": true,
|
||||
"**/.cache/**": true,
|
||||
"**/.quasar/**": true,
|
||||
"**/*.tsbuildinfo": true
|
||||
},
|
||||
|
||||
"search.exclude": {
|
||||
"**/node_modules": true,
|
||||
"**/dist": true,
|
||||
"**/.cache": true,
|
||||
"**/.quasar": true,
|
||||
"**/*.tsbuildinfo": true
|
||||
},
|
||||
|
||||
"files.exclude": {
|
||||
"**/.cache": true,
|
||||
"**/*.tsbuildinfo": true,
|
||||
"**/node_modules/.cache": true
|
||||
},
|
||||
|
||||
// TypeScript server настройки
|
||||
"typescript.tsserver.maxTsServerMemory": 8192,
|
||||
"typescript.tsserver.watchOptions": {
|
||||
"excludeFiles": [
|
||||
"**/node_modules/**/*",
|
||||
"**/dist/**/*",
|
||||
"**/.cache/**/*"
|
||||
]
|
||||
}
|
||||
"notebook.defaultFormatter": "Vue.volar"
|
||||
}
|
||||
|
||||
@@ -1,23 +1,3 @@
|
||||
# v2025.9.1
|
||||
|
||||
В системе Кооперативной Экономики развернут смарт-контракт CAPITAL v0.2 для генерации и капитализации результатов интеллектуальной деятельности. Контракт описывает и обеспечивает:
|
||||
- бизнес-процесс производства результатов интеллектуальной деятельности в кооперативах создателями, авторами, инвесторами, координаторами, мастерами и собственниками имущества в кооперации на проектах;
|
||||
- приём результатов интеллектуальной деятельности в качестве паевых взносов в кооператив;
|
||||
- распределение потока членских взносов среди вкладчиков;
|
||||
- капитализацию результатов интеллектуальной деятельности новыми результатами по модели золотого сечения;
|
||||
- оценку вкладов авторов и создателей по методу Водянова;
|
||||
|
||||
В основе математической модели контракта лежит принцип распределения справедливой выгоды между вкладчиками согласно концепции "Общественно-полезного времени", разработанной в рамках теорий Кузнецова и академика Глушкова при работе над проектом общегосударственной автоматизированной системы учета и обработки информации (ОГАС).
|
||||
|
||||
Подробнее в документации: https://coopenomics.world/contracts/group__public__capital.html
|
||||
|
||||
✨ Новые функции
|
||||
- [#301](https://github.com/coopenomics/mono/issues/301): Реализация контракта "Капитализация" v0.2: регистрация вкладчиков, создание и управление проектами, поддержка инвестиций, ссуд, членских взносов, проведение голосований, расчет капитализации, интеграция с внешними контрактами, поддержка импорта данных.
|
||||
|
||||
#releases
|
||||
|
||||
---
|
||||
|
||||
# v2025.7.1-1
|
||||
|
||||
В этом релизе реализованы механизмы возврата паевого взноса пайщика и инструменты управления этим процессом для членов совета. Также внесены визуальные доработки интерфейсов для улучшения восприятия информации.
|
||||
|
||||
-129
@@ -1,129 +0,0 @@
|
||||
# 📋 Резюме создания компонентов Notifications
|
||||
|
||||
## ✅ Что создано
|
||||
|
||||
### 1. 📚 Библиотека `@monocoop/notifications`
|
||||
**Расположение:** `components/notifications/`
|
||||
|
||||
**Функции:**
|
||||
- ✅ Типизированные интерфейсы для workflow с Zod
|
||||
- ✅ Builder паттерн для создания workflow
|
||||
- ✅ Базовые шаблоны для email, in-app, push, SMS
|
||||
- ✅ Автоматическая конвертация Zod схем в JSON Schema для Novu
|
||||
- ✅ Экспорт всех workflow для использования в других пакетах
|
||||
|
||||
**Структура:**
|
||||
```
|
||||
src/
|
||||
├── types/ # Базовые типы и интерфейсы
|
||||
├── base/ # Утилиты и настройки по умолчанию
|
||||
├── workflows/ # Папки с workflow
|
||||
│ └── welcome/ # Пример приветственного workflow
|
||||
└── index.ts # Главный экспорт
|
||||
```
|
||||
|
||||
### 2. 🚀 NestJS приложение `@monocoop/notificator2`
|
||||
**Расположение:** `components/notificator2/`
|
||||
|
||||
**Функции:**
|
||||
- ✅ Автоматический upsert всех workflow в Novu при запуске
|
||||
- ✅ RESTful API для триггера уведомлений
|
||||
- ✅ Типизированная валидация payload
|
||||
- ✅ Health check endpoints
|
||||
- ✅ Использует библиотеку notifications для типов
|
||||
|
||||
**API Endpoints:**
|
||||
- `GET /api/notifications/health` - Health check
|
||||
- `GET /api/notifications/workflows` - Список workflow
|
||||
- `POST /api/notifications/trigger` - Универсальный триггер
|
||||
- `POST /api/notifications/trigger/welcome` - Триггер welcome workflow
|
||||
- `POST /api/notifications/workflows/upsert-all` - Принудительный upsert
|
||||
|
||||
## 🔧 Как использовать
|
||||
|
||||
### 1. Настройка библиотеки notifications
|
||||
```bash
|
||||
cd components/notifications
|
||||
pnpm install # или npm install
|
||||
pnpm build # для компиляции TypeScript
|
||||
```
|
||||
|
||||
### 2. Настройка notificator2
|
||||
```bash
|
||||
cd components/notificator2
|
||||
pnpm install # или npm install
|
||||
|
||||
# Настройка окружения
|
||||
cp .env.example .env
|
||||
# Добавить NOVU_API_KEY в .env
|
||||
|
||||
# Запуск
|
||||
pnpm start:dev
|
||||
```
|
||||
|
||||
### 3. Добавление нового workflow
|
||||
|
||||
**В библиотеке notifications:**
|
||||
```typescript
|
||||
// components/notifications/src/workflows/order/order-workflow.ts
|
||||
export const orderWorkflow = WorkflowBuilder
|
||||
.create<OrderPayload>()
|
||||
.name('Order Confirmation')
|
||||
.workflowId('order-confirmation')
|
||||
.payloadSchema(orderPayloadSchema)
|
||||
.addSteps([...])
|
||||
.build();
|
||||
```
|
||||
|
||||
**Регистрация в index.ts:**
|
||||
```typescript
|
||||
// components/notifications/src/workflows/index.ts
|
||||
import { orderWorkflow } from './order';
|
||||
|
||||
export const allWorkflows = [
|
||||
welcomeWorkflow,
|
||||
orderWorkflow, // ← добавить новый workflow
|
||||
];
|
||||
```
|
||||
|
||||
### 4. Использование API
|
||||
|
||||
**Триггер welcome workflow:**
|
||||
```bash
|
||||
curl -X POST http://localhost:3000/api/notifications/trigger/welcome \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"subscriberId": "user-123",
|
||||
"email": "user@example.com",
|
||||
"payload": {
|
||||
"userName": "Иван Иванов",
|
||||
"userEmail": "user@example.com",
|
||||
"age": 25
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
## 🎯 Преимущества архитектуры
|
||||
|
||||
1. **Типобезопасность** - Zod схемы обеспечивают валидацию на уровне TypeScript и runtime
|
||||
2. **Разделение ответственности** - Библиотека содержит только типы, сервер - логику
|
||||
3. **Расширяемость** - Легко добавлять новые workflow
|
||||
4. **Автоматизация** - Workflow автоматически синхронизируются с Novu
|
||||
5. **Переиспользование** - Типы можно использовать в других частях системы
|
||||
|
||||
## 🔄 Workflow при запуске
|
||||
|
||||
1. **Запуск notificator2** →
|
||||
2. **Чтение всех workflow из библиотеки** →
|
||||
3. **Проверка существования в Novu** →
|
||||
4. **Создание/обновление workflow** →
|
||||
5. **Готов к приёму запросов на триггеры**
|
||||
|
||||
## 📁 Структура как в testFramework2.ts
|
||||
|
||||
Вся логика из `testFramework2.ts` была перенесена в structured архитектуру:
|
||||
- ✅ `buildWorkflowData` → `WorkflowBuilder`
|
||||
- ✅ `baseSteps` → `createEmailStep`, `createInAppStep`, etc.
|
||||
- ✅ `upsertWorkflow` → `NovuService.upsertWorkflow`
|
||||
- ✅ `triggerWorkflow` → `NovuService.triggerWorkflow`
|
||||
- ✅ Типизация payload → Zod схемы + TypeScript типы
|
||||
Vendored
BIN
Binary file not shown.
-22
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/lib/**",
|
||||
"${workspaceFolder}/lib/eosiolib/contracts/**",
|
||||
"${workspaceFolder}/lib/eosiolib/core/**"
|
||||
],
|
||||
"defines": [
|
||||
"_DEBUG",
|
||||
"UNICODE",
|
||||
"_UNICODE",
|
||||
"int128_t=__int128_t",
|
||||
"uint128_t=__uint128_t"
|
||||
],
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++20"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
{
|
||||
// Enable the ESlint flat config support
|
||||
"prettier.enable": false,
|
||||
"editor.formatOnSave": true,
|
||||
// Auto fix
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
// "source.fixAll": "explicit",
|
||||
// "source.organizeImports": "never"
|
||||
},
|
||||
// Silent the stylistic rules in you IDE, but still auto fix them
|
||||
"eslint.rules.customizations": [
|
||||
{
|
||||
"rule": "style/*",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-indent",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-spacing",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-spaces",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-order",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-dangle",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-newline",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*quotes",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*semi",
|
||||
"severity": "off"
|
||||
}
|
||||
],
|
||||
// Enable eslint for all supported languages
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"typescriptreact",
|
||||
"vue",
|
||||
"html",
|
||||
"markdown",
|
||||
"json",
|
||||
"jsonc",
|
||||
"yaml"
|
||||
],
|
||||
// "[cpp]": {
|
||||
// "editor.defaultFormatter": "ms-vscode.cpptools",
|
||||
// },
|
||||
// "C_Cpp.vcFormat.indent.withinParentheses": "indent",
|
||||
"C_Cpp.errorSquiggles": "disabled"
|
||||
}
|
||||
@@ -9,7 +9,7 @@ export default antfu(
|
||||
},
|
||||
{
|
||||
rules: {
|
||||
'no-unused-vars': 'off',
|
||||
'no-unused-vars': 'warn',
|
||||
'unused-imports/no-unused-vars': 'off',
|
||||
'no-console': 'off',
|
||||
'unicorn/prefer-number-properties': 'off',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@coopenomics/boot",
|
||||
"type": "module",
|
||||
"version": "2025.9.1",
|
||||
"version": "2025.7.2",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@9.0.6",
|
||||
"description": "",
|
||||
|
||||
@@ -15,7 +15,6 @@ import EosApi from 'eosjs-api'
|
||||
// @ts-expect-error заменить на antelope
|
||||
import ecc from 'eosjs-ecc'
|
||||
import config from '../configs'
|
||||
import { consoleIt } from '../tests/shared/consoleIt'
|
||||
|
||||
import type { Contract, Feature, Keys, Network } from '../types'
|
||||
|
||||
@@ -876,7 +875,7 @@ export default class Blockchain {
|
||||
name: SovietContract.Actions.Boards.CreateBoard.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: params.coopname,
|
||||
actor: params.username,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
@@ -988,35 +987,4 @@ export default class Blockchain {
|
||||
|
||||
console.log('Программа установлена: ', params)
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет транзакцию и автоматически выводит консоль логи через consoleIt
|
||||
* @param actions - массив действий для транзакции
|
||||
* @param options - опции транзакции (по умолчанию стандартные)
|
||||
* @param options.blocksBehind - количество блоков позади для подтверждения
|
||||
* @param options.expireSeconds - время жизни транзакции в секундах
|
||||
* @returns результат транзакции
|
||||
*/
|
||||
async transactWithLogs(
|
||||
actions: any[],
|
||||
options: { blocksBehind?: number, expireSeconds?: number } = {},
|
||||
): Promise<any> {
|
||||
await this.update_pass_instance()
|
||||
|
||||
const defaultOptions = {
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
...options,
|
||||
}
|
||||
|
||||
const result = await this.api.transact(
|
||||
{ actions },
|
||||
defaultOptions,
|
||||
)
|
||||
|
||||
// Автоматически выводим консоль логи
|
||||
consoleIt(result)
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,16 +65,6 @@ export default [
|
||||
path: path.resolve(process.cwd(), '../contracts/build/contracts/fund'),
|
||||
target: 'fund',
|
||||
},
|
||||
{
|
||||
name: 'ledger',
|
||||
path: path.resolve(process.cwd(), '../contracts/build/contracts/ledger'),
|
||||
target: 'ledger',
|
||||
},
|
||||
{
|
||||
name: 'test',
|
||||
path: path.resolve(process.cwd(), '../contracts/build/contracts/test'),
|
||||
target: 'test',
|
||||
},
|
||||
{
|
||||
name: 'contributor',
|
||||
path: path.resolve(process.cwd(), '../contracts/build/contracts/contributor'),
|
||||
|
||||
@@ -91,14 +91,6 @@ export default {
|
||||
name: 'loan',
|
||||
code_permissions_to: ['loan'],
|
||||
},
|
||||
{
|
||||
name: 'ledger',
|
||||
code_permissions_to: ['ledger'],
|
||||
},
|
||||
{
|
||||
name: 'test',
|
||||
code_permissions_to: ['test'],
|
||||
},
|
||||
{
|
||||
name: 'meet',
|
||||
code_permissions_to: ['meet'],
|
||||
|
||||
@@ -3,7 +3,6 @@ import type { Keys } from '../types'
|
||||
import config, { GOVERN_SYMBOL, SYMBOL } from '../configs'
|
||||
import Blockchain from '../blockchain'
|
||||
import { generateRandomSHA256 } from '../utils/randomHash'
|
||||
import { processLastDecision } from '../tests/soviet/processLastDecision'
|
||||
|
||||
const test_hash
|
||||
= '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777'
|
||||
@@ -42,7 +41,7 @@ export class CooperativeClass {
|
||||
await this.blockchain.createProgram({
|
||||
coopname,
|
||||
username: coopname,
|
||||
type: 'marketplace',
|
||||
type: 'cooplace',
|
||||
title: 'КООПЛЕЙС',
|
||||
announce: '',
|
||||
description: '',
|
||||
@@ -91,36 +90,35 @@ export class CooperativeClass {
|
||||
income_hash: registration_hash,
|
||||
})
|
||||
|
||||
await processLastDecision(this.blockchain, config.provider)
|
||||
// console.log('Голосуем по решению в провайдере')
|
||||
console.log('Голосуем по решению в провайдере')
|
||||
|
||||
// await this.blockchain.votefor({
|
||||
// version: '1.0.0',
|
||||
// signed_at: '2025-05-14T12:22:26',
|
||||
// signed_hash: test_hash,
|
||||
// signature: test_sign,
|
||||
// public_key: test_pkey,
|
||||
// coopname: config.provider,
|
||||
// username: config.provider_chairman,
|
||||
// decision_id: 1,
|
||||
// })
|
||||
await this.blockchain.votefor({
|
||||
version: '1.0.0',
|
||||
signed_at: '2025-05-14T12:22:26',
|
||||
signed_hash: test_hash,
|
||||
signature: test_sign,
|
||||
public_key: test_pkey,
|
||||
coopname: config.provider,
|
||||
username: config.provider_chairman,
|
||||
decision_id: 1,
|
||||
})
|
||||
|
||||
// console.log('Утверждаем решение в провайдере')
|
||||
console.log('Утверждаем решение в провайдере')
|
||||
|
||||
// await this.blockchain.authorize({
|
||||
// coopname: config.provider,
|
||||
// chairman: config.provider_chairman,
|
||||
// decision_id: 1,
|
||||
// document,
|
||||
// })
|
||||
await this.blockchain.authorize({
|
||||
coopname: config.provider,
|
||||
chairman: config.provider_chairman,
|
||||
decision_id: 1,
|
||||
document,
|
||||
})
|
||||
|
||||
// console.log('Исполняем решение в провайдере')
|
||||
console.log('Исполняем решение в провайдере')
|
||||
|
||||
// await this.blockchain.exec({
|
||||
// executer: config.provider_chairman,
|
||||
// coopname: config.provider,
|
||||
// decision_id: 1,
|
||||
// })
|
||||
await this.blockchain.exec({
|
||||
executer: config.provider_chairman,
|
||||
coopname: config.provider,
|
||||
decision_id: 1,
|
||||
})
|
||||
|
||||
console.log('Отправляем подписанное положение о ЦПП Кошелька оператору')
|
||||
|
||||
|
||||
@@ -330,7 +330,7 @@ export async function startInfra() {
|
||||
await blockchain.transfer({
|
||||
from: 'eosio',
|
||||
to: config.provider,
|
||||
quantity: `1000.0000 ${config.token.symbol}`,
|
||||
quantity: `100.0000 ${config.token.symbol}`,
|
||||
memo: '',
|
||||
})
|
||||
|
||||
|
||||
@@ -6,96 +6,29 @@ import type { Account, Contract, Keys } from '../types'
|
||||
import config, { GOVERN_SYMBOL, SYMBOL } from '../configs'
|
||||
import Blockchain from '../blockchain'
|
||||
import { sendPostToCoopbackWithSecret, sleep } from '../utils'
|
||||
import { fakeDocument } from '../tests/shared/fakeDocument'
|
||||
|
||||
export class ParticipantsClass {
|
||||
// const test_hash
|
||||
// = '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777'
|
||||
// const test_sign
|
||||
// = 'SIG_K1_KmKWPBC8dZGGDGhbKEoZEzPr3h5crRrR2uLdGRF5DJbeibY1MY1bZ9sPwHsgmPfiGFv9psfoCVsXFh9TekcLuvaeuxRKA8'
|
||||
// const test_pkey = 'EOS5JhMfxbsNebajHcTEK8yC9uNN9Dit9hEmzE8ri8yMhhzxrLg3J'
|
||||
// const test_meta = JSON.stringify({})
|
||||
|
||||
// const document = {
|
||||
// hash: test_hash,
|
||||
// signature: test_sign,
|
||||
// public_key: test_pkey,
|
||||
// meta: test_meta,
|
||||
// }
|
||||
|
||||
export class CooperativeClass {
|
||||
public blockchain: Blockchain
|
||||
|
||||
constructor(blockchain: Blockchain) {
|
||||
this.blockchain = blockchain
|
||||
}
|
||||
|
||||
async addUser2(coopname: string, username: string, keys?: Keys, referer?: string) {
|
||||
const account = await this.blockchain.generateKeypair(
|
||||
username,
|
||||
keys,
|
||||
'Аккаунт участника',
|
||||
)
|
||||
console.log('Регистрируем аккаунт')
|
||||
|
||||
const data: RegistratorContract.Actions.AddUser.IAddUser = {
|
||||
coopname,
|
||||
referer: referer || '',
|
||||
username: account.username,
|
||||
type: 'individual',
|
||||
created_at: '2025-02-05T09:34:27',
|
||||
initial: '100.0000 RUB',
|
||||
minimum: '100.0000 RUB',
|
||||
spread_initial: false,
|
||||
meta: '',
|
||||
}
|
||||
|
||||
await this.blockchain.api.transact({
|
||||
actions: [
|
||||
{
|
||||
account: RegistratorContract.contractName.production,
|
||||
name: RegistratorContract.Actions.AddUser.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data,
|
||||
},
|
||||
],
|
||||
}, {
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
})
|
||||
|
||||
console.log('Регистрируем аккаунт как пользователя')
|
||||
|
||||
const changeKey: RegistratorContract.Actions.ChangeKey.IChangeKey = {
|
||||
coopname,
|
||||
username,
|
||||
public_key: account.publicKey,
|
||||
changer: coopname,
|
||||
}
|
||||
|
||||
await this.blockchain.api.transact({
|
||||
actions: [
|
||||
{
|
||||
account: RegistratorContract.contractName.production,
|
||||
name: RegistratorContract.Actions.ChangeKey.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: changeKey,
|
||||
},
|
||||
],
|
||||
}, {
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
})
|
||||
|
||||
// console.log('Отправляем подписанное положение о ЦПП Кошелька оператору')
|
||||
|
||||
// await this.blockchain.sendAgreement({
|
||||
// coopname: config.provider,
|
||||
// administrator: config.provider,
|
||||
// username: username!,
|
||||
// agreement_type: 'wallet',
|
||||
// document: fakeDocument,
|
||||
// })
|
||||
|
||||
// console.log('создаём кошелёк')
|
||||
|
||||
// console.log(`Арендуем ресурсы кооперативу`)
|
||||
|
||||
// await this.blockchain.powerup({
|
||||
// payer: 'eosio',
|
||||
// receiver: username,
|
||||
// days: config.powerup.days,
|
||||
// payment: `100.0000 ${config.token.symbol}`,
|
||||
// transfer: true,
|
||||
// })
|
||||
}
|
||||
|
||||
async addUser(username: string, keys?: Keys, referer?: string) {
|
||||
async addUser(username: string, keys?: Keys) {
|
||||
const account = await this.blockchain.generateKeypair(
|
||||
username,
|
||||
keys,
|
||||
@@ -105,7 +38,7 @@ export class ParticipantsClass {
|
||||
|
||||
const data: RegistratorContract.Actions.AddUser.IAddUser = {
|
||||
coopname: config.provider,
|
||||
referer: referer || '',
|
||||
referer: '',
|
||||
username: account.username,
|
||||
type: 'individual',
|
||||
created_at: '2025-02-05T09:34:27',
|
||||
@@ -159,7 +92,12 @@ export class ParticipantsClass {
|
||||
administrator: config.provider,
|
||||
username: username!,
|
||||
agreement_type: 'wallet',
|
||||
document: fakeDocument,
|
||||
document: { // отправляем произвольный документ с валидной подписью
|
||||
hash: '157192B276DA23CC84AB078FC8755C051C5F0430BF4802E55718221E6B76C777',
|
||||
public_key: 'PUB_K1_5JhMfxbsNebajHcTEK8yC9uNN9Dit9hEmzE8ri8yMhhzzEtUA4',
|
||||
signature: 'SIG_K1_KmKWPBC8dZGGDGhbKEoZEzPr3h5crRrR2uLdGRF5DJbeibY1MY1bZ9sPwHsgmPfiGFv9psfoCVsXFh9TekcLuvaeuxRKA8',
|
||||
meta: '{}',
|
||||
},
|
||||
})
|
||||
|
||||
console.log('создаём кошелёк')
|
||||
@@ -176,28 +114,15 @@ export class ParticipantsClass {
|
||||
}
|
||||
}
|
||||
|
||||
export async function addUser(username: string, referer?: string) {
|
||||
export async function addUser(username: string) {
|
||||
// инициализируем инстанс с ключами
|
||||
const blockchain = new Blockchain(config.network, config.private_keys)
|
||||
const participants = new ParticipantsClass(blockchain)
|
||||
const cooperative = new CooperativeClass(blockchain)
|
||||
|
||||
await participants.addUser(username, {
|
||||
await cooperative.addUser(username, {
|
||||
// eslint-disable-next-line node/prefer-global/process
|
||||
privateKey: process.env.EOSIO_PRV_KEY!,
|
||||
// eslint-disable-next-line node/prefer-global/process
|
||||
publicKey: process.env.EOSIO_PUB_KEY!,
|
||||
}, referer)
|
||||
}
|
||||
|
||||
export async function addUser2(coopname: string, username: string, referer?: string) {
|
||||
// инициализируем инстанс с ключами
|
||||
const blockchain = new Blockchain(config.network, config.private_keys)
|
||||
const participants = new ParticipantsClass(blockchain)
|
||||
|
||||
await participants.addUser2(coopname, username, {
|
||||
// eslint-disable-next-line node/prefer-global/process
|
||||
privateKey: process.env.EOSIO_PRV_KEY!,
|
||||
// eslint-disable-next-line node/prefer-global/process
|
||||
publicKey: process.env.EOSIO_PUB_KEY!,
|
||||
}, referer)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,450 +0,0 @@
|
||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
|
||||
import { CapitalContract, SovietContract } from 'cooptypes'
|
||||
import Blockchain from '../blockchain'
|
||||
import config from '../configs'
|
||||
import { getTotalRamUsage, globalRamStats } from '../utils/getTotalRamUsage'
|
||||
import { addUser, addUser2 } from '../init/participant'
|
||||
import { generateRandomUsername } from '../utils/randomUsername'
|
||||
import { generateRandomSHA256 } from '../utils/randomHash'
|
||||
import { sleep } from '../utils'
|
||||
import { CooperativeClass } from '../init/cooperative'
|
||||
import { getCirculationAccount, getCoopProgramWallet, getLedgerAccountById, getUserProgramWalletAmount } from './wallet/walletUtils'
|
||||
import { registerContributor } from './capital/registerContributor'
|
||||
import { signAppendix } from './capital/signAppendix'
|
||||
import { capitalProgramId, circulationAccountId, sourceProgramId } from './capital/consts'
|
||||
import { commitToResult } from './capital/commitToResult'
|
||||
import { refreshSegment } from './capital/refreshSegment'
|
||||
import { signWalletAgreement } from './wallet/signWalletAgreement'
|
||||
import { fakeDocument } from './shared/fakeDocument'
|
||||
import { depositToWallet } from './wallet/depositToWallet'
|
||||
import { signCapitalAgreement } from './capital/signCapitalAgreement'
|
||||
import { addAuthor } from './capital/addAuthor'
|
||||
import { getSegment } from './capital/getSegment'
|
||||
import { investInProject } from './capital/investInProject'
|
||||
import { processDebt } from './capital/processDebt'
|
||||
import { processCreateProjectProperty } from './capital/processCreateProjectProperty'
|
||||
import { processCreateProgramProperty } from './capital/processCreateProgramProperty'
|
||||
import { processStartVoting } from './capital/processStartVoting'
|
||||
import { createVoteDistribution, submitVote } from './capital/submitVote'
|
||||
import { processCompleteVoting } from './capital/processCompleteVoting'
|
||||
import { processPushResult } from './capital/processPushResult'
|
||||
import { processConvertSegment } from './capital/processConvertSegment'
|
||||
import { processCalculateVotes } from './capital/processCalculateVotes'
|
||||
import { processAddContributor } from './capital/processAddContributor'
|
||||
import { processDeleteProject } from './capital/processDeleteProject'
|
||||
import { getProject } from './capital/getProject'
|
||||
import { processFundProgram } from './capital/processFundProgram'
|
||||
import { processRefreshProg } from './capital/processRefreshProg'
|
||||
import { processFundProject } from './capital/processFundProject'
|
||||
import { processRefreshProj } from './capital/processRefreshProj'
|
||||
// const CLI_PATH = 'src/index.ts'
|
||||
|
||||
const blockchain = new Blockchain(config.network, config.private_keys)
|
||||
let metaProject: CapitalContract.Actions.CreateProject.ICreateProject
|
||||
let componentProject: CapitalContract.Actions.CreateProject.ICreateProject
|
||||
let newComponentProject: CapitalContract.Actions.CreateProject.ICreateProject
|
||||
let testCoop: string
|
||||
let chairman: string
|
||||
|
||||
let tester1: string
|
||||
let tester2: string
|
||||
let tester3: string
|
||||
let tester4: string
|
||||
let tester5: string
|
||||
|
||||
let investor1: string
|
||||
let investor2: string
|
||||
let investor3: string
|
||||
// let result1: CapitalContract.Actions.CreateResult.ICreateResult
|
||||
// let result2: CapitalContract.Actions.CreateResult.ICreateResult
|
||||
// let result3: CapitalContract.Actions.CreateResult.ICreateResult
|
||||
|
||||
const commits: string[] = []
|
||||
const tester1CommitHours = 10
|
||||
const tester2CommitHours = 20
|
||||
const tester3CommitHours = 100
|
||||
const investAmount1 = 100000
|
||||
const investAmount2 = 25000
|
||||
const investAmount3 = 25000
|
||||
|
||||
const totalToCapitalConvertAmount = 0
|
||||
const totalToProjectConvertAmount = 0
|
||||
// Хранение хэшей долгов для каждого пользователя
|
||||
let userDebtHashes: { [username: string]: string[] } = {}
|
||||
beforeAll(async () => {
|
||||
testCoop = generateRandomUsername()
|
||||
chairman = generateRandomUsername()
|
||||
|
||||
await blockchain.update_pass_instance()
|
||||
|
||||
const coop = new CooperativeClass(blockchain)
|
||||
|
||||
await coop.createCooperative(testCoop, {
|
||||
privateKey: process.env.EOSIO_PRV_KEY!,
|
||||
publicKey: process.env.EOSIO_PUB_KEY!,
|
||||
})
|
||||
|
||||
await blockchain.preInit({
|
||||
coopname: testCoop,
|
||||
username: config.provider,
|
||||
status: 'active',
|
||||
})
|
||||
|
||||
await addUser2(testCoop, chairman)
|
||||
|
||||
await blockchain.createBoard({
|
||||
coopname: testCoop,
|
||||
username: testCoop,
|
||||
type: 'soviet',
|
||||
members: [{
|
||||
username: chairman,
|
||||
is_voting: true,
|
||||
position_title: 'Председатель совета',
|
||||
position: 'chairman',
|
||||
}],
|
||||
name: 'Совет',
|
||||
description: '',
|
||||
})
|
||||
|
||||
console.log('testcoop: ', testCoop)
|
||||
|
||||
investor1 = generateRandomUsername()
|
||||
console.log('investor1: ', investor1)
|
||||
await addUser2(testCoop, investor1)
|
||||
|
||||
tester1 = generateRandomUsername()
|
||||
console.log('tester1: ', tester1)
|
||||
await addUser2(testCoop, tester1)
|
||||
|
||||
tester2 = generateRandomUsername()
|
||||
console.log('tester2: ', tester2)
|
||||
await addUser2(testCoop, tester2)
|
||||
|
||||
tester3 = generateRandomUsername()
|
||||
console.log('tester3: ', tester3)
|
||||
await addUser2(testCoop, tester3)
|
||||
|
||||
tester4 = generateRandomUsername()
|
||||
console.log('tester4: ', tester4)
|
||||
await addUser2(testCoop, tester4)
|
||||
|
||||
tester5 = generateRandomUsername()
|
||||
console.log('tester5: ', tester5)
|
||||
await addUser2(testCoop, tester5)
|
||||
|
||||
// Инициализируем массивы хэшей долгов для каждого пользователя
|
||||
userDebtHashes = {
|
||||
[tester1]: [],
|
||||
[tester2]: [],
|
||||
[tester3]: [],
|
||||
[tester4]: [],
|
||||
[tester5]: [],
|
||||
[investor1]: [],
|
||||
[investor2]: [],
|
||||
[investor3]: [],
|
||||
}
|
||||
|
||||
investor2 = generateRandomUsername()
|
||||
console.log('investor2: ', investor2)
|
||||
await addUser2(testCoop, investor2, tester4)
|
||||
|
||||
investor3 = generateRandomUsername()
|
||||
console.log('investor3: ', investor3)
|
||||
await addUser2(testCoop, investor3, tester5)
|
||||
|
||||
// const { stdout } = await execa('esno', [CLI_PATH, 'boot'], { stdio: 'inherit' })
|
||||
// expect(stdout).toContain('Boot process completed')
|
||||
}, 500_000)
|
||||
|
||||
afterAll(() => {
|
||||
console.log('\n📊 **RAM USAGE SUMMARY** 📊')
|
||||
let totalGlobalRam = 0
|
||||
|
||||
for (const [key, ramUsed] of Object.entries(globalRamStats)) {
|
||||
const ramKb = (ramUsed / 1024).toFixed(2)
|
||||
console.log(` ${key} = ${ramKb} kb`)
|
||||
totalGlobalRam += ramUsed
|
||||
}
|
||||
|
||||
console.log(`\n💾 **TOTAL RAM USED IN TESTS**: ${(totalGlobalRam / 1024).toFixed(2)} kb\n`)
|
||||
|
||||
console.log(`testCoop: ${testCoop}\ntester1: ${tester1}\ntester2: ${tester2}\ntester3: ${tester3}\ntester4: ${tester4}\ntester5: ${tester5}\ninvestor1: ${investor1}\ninvestor2: ${investor2}\ninvestor3: ${investor3}`)
|
||||
})
|
||||
|
||||
describe('тест контракта CAPITAL', () => {
|
||||
it('создаём программу генерации', async () => {
|
||||
const program = await getCoopProgramWallet(blockchain, testCoop, sourceProgramId)
|
||||
if (!program) {
|
||||
const data: SovietContract.Actions.Programs.CreateProgram.ICreateProgram = {
|
||||
coopname: testCoop,
|
||||
is_can_coop_spend_share_contributions: true,
|
||||
username: chairman,
|
||||
title: 'Договор УХД',
|
||||
announce: '',
|
||||
description: '',
|
||||
preview: '',
|
||||
images: '',
|
||||
calculation_type: 'free',
|
||||
fixed_membership_contribution: '0.0000 RUB',
|
||||
membership_percent_fee: '0',
|
||||
meta: '',
|
||||
type: 'source',
|
||||
}
|
||||
|
||||
const result = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: SovietContract.contractName.production,
|
||||
name: SovietContract.Actions.Programs.CreateProgram.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: chairman,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
getTotalRamUsage(result)
|
||||
expect(result.transaction_id).toBeDefined()
|
||||
}
|
||||
})
|
||||
|
||||
it('создаём программу капитализации', async () => {
|
||||
const program = await getCoopProgramWallet(blockchain, testCoop, capitalProgramId)
|
||||
|
||||
if (!program) {
|
||||
const data: SovietContract.Actions.Programs.CreateProgram.ICreateProgram = {
|
||||
coopname: testCoop,
|
||||
is_can_coop_spend_share_contributions: false,
|
||||
username: chairman,
|
||||
title: 'Капитализация',
|
||||
announce: '',
|
||||
description: '',
|
||||
preview: '',
|
||||
images: '',
|
||||
calculation_type: 'free',
|
||||
fixed_membership_contribution: '0.0000 RUB',
|
||||
membership_percent_fee: '0',
|
||||
meta: '',
|
||||
type: 'capital',
|
||||
}
|
||||
|
||||
const result = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: SovietContract.contractName.production,
|
||||
name: SovietContract.Actions.Programs.CreateProgram.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: chairman,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
getTotalRamUsage(result)
|
||||
expect(result.transaction_id).toBeDefined()
|
||||
}
|
||||
})
|
||||
|
||||
it('импорт вкладчика ДО установки конфига - должен работать', async () => {
|
||||
// Создаем тестового пользователя для импорта
|
||||
const importUsername = generateRandomUsername()
|
||||
console.log('importUsername: ', importUsername)
|
||||
await addUser2(testCoop, importUsername)
|
||||
|
||||
const contributorHash = generateRandomSHA256()
|
||||
const contributionAmount = '10000.0000 RUB'
|
||||
|
||||
// Получаем состояние паевого счета ДО импорта
|
||||
const shareFundBefore = await getCirculationAccount(blockchain, testCoop)
|
||||
const shareFundBalanceBefore = parseFloat(shareFundBefore.available)
|
||||
|
||||
console.log('Share fund before import:', shareFundBalanceBefore)
|
||||
|
||||
// Импортируем вкладчика
|
||||
const importData: CapitalContract.Actions.ImportContributor.IImportContributor = {
|
||||
coopname: testCoop,
|
||||
username: importUsername,
|
||||
contributor_hash: contributorHash,
|
||||
contribution_amount: contributionAmount,
|
||||
memo: 'договор №123 от такого то числа',
|
||||
}
|
||||
|
||||
const result = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.ImportContributor.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: testCoop,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data: importData,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
getTotalRamUsage(result)
|
||||
expect(result.transaction_id).toBeDefined()
|
||||
|
||||
// Проверяем, что вкладчик был создан
|
||||
const contributorTable = await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
testCoop,
|
||||
'contributors',
|
||||
)
|
||||
console.log('contributorTable: ', contributorTable)
|
||||
|
||||
expect(contributorTable.length).toBe(1)
|
||||
const contributor = contributorTable[0]
|
||||
expect(contributor.username).toBe(importUsername)
|
||||
expect(contributor.status).toBe('active')
|
||||
|
||||
// Проверяем, что кошелек программы был создан
|
||||
const wallet = await getUserProgramWalletAmount(blockchain, testCoop, importUsername, capitalProgramId)
|
||||
expect(parseFloat(wallet)).toBe(parseFloat(contributionAmount))
|
||||
|
||||
// Проверяем увеличение паевого счета
|
||||
const shareFundAfter = await getCirculationAccount(blockchain, testCoop)
|
||||
const shareFundBalanceAfter = parseFloat(shareFundAfter.available)
|
||||
console.log('Share fund after import:', shareFundBalanceAfter)
|
||||
|
||||
expect(shareFundBalanceAfter).toBe(shareFundBalanceBefore + parseFloat(contributionAmount))
|
||||
})
|
||||
|
||||
it('инициализируем контракт CAPITAL и сверяем конфиг', async () => {
|
||||
const data: CapitalContract.Actions.SetConfig.ISetConfig = {
|
||||
coopname: testCoop,
|
||||
config: {
|
||||
coordinator_bonus_percent: 4,
|
||||
expense_pool_percent: 100,
|
||||
coordinator_invite_validity_days: 30,
|
||||
voting_period_in_days: 7,
|
||||
authors_voting_percent: 38.2,
|
||||
creators_voting_percent: 38.2,
|
||||
},
|
||||
}
|
||||
|
||||
const result = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.SetConfig.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: testCoop,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
getTotalRamUsage(result)
|
||||
expect(result.transaction_id).toBeDefined()
|
||||
|
||||
const state = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
CapitalContract.contractName.production,
|
||||
'state',
|
||||
1,
|
||||
testCoop,
|
||||
testCoop,
|
||||
))[0]
|
||||
console.log('state: ', state)
|
||||
expect(state).toBeDefined()
|
||||
expect(state.coopname).toBe(testCoop)
|
||||
expect(parseFloat(state.config.coordinator_bonus_percent)).toBe(data.config.coordinator_bonus_percent)
|
||||
expect(parseFloat(state.config.expense_pool_percent)).toBe(data.config.expense_pool_percent)
|
||||
expect(parseFloat(state.config.coordinator_invite_validity_days)).toBe(data.config.coordinator_invite_validity_days)
|
||||
expect(parseFloat(state.config.voting_period_in_days)).toBe(data.config.voting_period_in_days)
|
||||
expect(parseFloat(state.config.authors_voting_percent)).toBe(data.config.authors_voting_percent)
|
||||
expect(parseFloat(state.config.creators_voting_percent)).toBe(data.config.creators_voting_percent)
|
||||
})
|
||||
|
||||
it('импорт вкладчика ПОСЛЕ установки конфига - должен провалиться', async () => {
|
||||
// Создаем еще одного тестового пользователя для импорта
|
||||
const importUsername2 = generateRandomUsername()
|
||||
console.log('importUsername2: ', importUsername2)
|
||||
await addUser(importUsername2)
|
||||
|
||||
const contributorHash2 = generateRandomSHA256()
|
||||
const contributionAmount2 = '5000.0000 RUB'
|
||||
|
||||
// Пытаемся импортировать вкладчика после установки конфига
|
||||
const importData2: CapitalContract.Actions.ImportContributor.IImportContributor = {
|
||||
coopname: testCoop,
|
||||
username: importUsername2,
|
||||
contributor_hash: contributorHash2,
|
||||
contribution_amount: contributionAmount2,
|
||||
memo: 'договор №123 от такого то числа',
|
||||
}
|
||||
|
||||
// Ожидаем, что транзакция провалится
|
||||
await expect(
|
||||
blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.ImportContributor.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: testCoop,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data: importData2,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
),
|
||||
).rejects.toThrow() // Ожидаем ошибку
|
||||
|
||||
// Проверяем, что вкладчик НЕ был создан
|
||||
const contributorTable = await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
testCoop,
|
||||
'contributors',
|
||||
)
|
||||
expect(contributorTable.length).toBe(1)
|
||||
|
||||
// Проверяем, что кошелек программы НЕ был создан
|
||||
const wallet = await getUserProgramWalletAmount(blockchain, testCoop, importUsername2, capitalProgramId)
|
||||
expect(wallet).toBe('0.0000 RUB')
|
||||
})
|
||||
})
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,75 +0,0 @@
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import { expect } from 'vitest'
|
||||
import type Blockchain from '../../blockchain'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { makeCombinedChecksum256NameIndexKey } from '../shared/combinedKeys'
|
||||
|
||||
export async function addAuthor(blockchain: Blockchain, coopname: string, project_hash: string, author: string) {
|
||||
const data: CapitalContract.Actions.AddAuthor.IAddAuthor = {
|
||||
coopname,
|
||||
project_hash,
|
||||
author,
|
||||
}
|
||||
|
||||
const result = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.AddAuthor.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: coopname,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
getTotalRamUsage(result)
|
||||
expect(result.transaction_id).toBeDefined()
|
||||
|
||||
const key = makeCombinedChecksum256NameIndexKey(project_hash, author)
|
||||
const rows = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
CapitalContract.Tables.Segments.tableName,
|
||||
1000,
|
||||
key,
|
||||
key,
|
||||
3,
|
||||
'i128',
|
||||
))
|
||||
|
||||
const segment = rows[0]
|
||||
|
||||
console.log('segment: ', segment)
|
||||
expect(segment).toBeDefined()
|
||||
expect(segment.username).toBe(author)
|
||||
expect(segment.project_hash).toBe(project_hash)
|
||||
expect(segment.is_author).toBe(1)
|
||||
|
||||
const project = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
CapitalContract.Tables.Projects.tableName,
|
||||
1000,
|
||||
project_hash,
|
||||
project_hash,
|
||||
3,
|
||||
'sha256',
|
||||
))[0]
|
||||
|
||||
expect(project).toBeDefined()
|
||||
|
||||
return {
|
||||
project,
|
||||
segment,
|
||||
}
|
||||
}
|
||||
@@ -2,20 +2,34 @@ import { expect } from 'vitest'
|
||||
import { CapitalContract, SovietContract } from 'cooptypes'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { generateRandomSHA256 } from '../../utils/randomHash'
|
||||
import { fakeDocument } from '../shared/fakeDocument'
|
||||
import { processMasterApprove } from './processMasterApprove'
|
||||
import { fakeDocument } from '../soviet/fakeDocument'
|
||||
import { processDecision } from '../soviet/processDecision'
|
||||
import { ratePerHour } from './consts'
|
||||
|
||||
export async function commitToResult(
|
||||
blockchain: any,
|
||||
coopname: string,
|
||||
resultHash: string,
|
||||
projectHash: string,
|
||||
creator: string,
|
||||
spendHours: number,
|
||||
) {
|
||||
const commitHash = generateRandomSHA256()
|
||||
console.log(`\n🔹 Начало коммита: ${commitHash}, проект: ${projectHash}, часы: ${spendHours}`)
|
||||
const totalSpended = `${(spendHours * parseFloat(ratePerHour)).toFixed(4)} RUB`
|
||||
console.log(`\n🔹 Начало коммита: ${commitHash}, часы: ${spendHours}, сумма: ${totalSpended}`)
|
||||
|
||||
// Получение текущих данных перед коммитом
|
||||
const prevResult = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'results',
|
||||
1,
|
||||
resultHash,
|
||||
resultHash,
|
||||
2,
|
||||
'sha256',
|
||||
))[0] || { spended: '0.0000 RUB', commits_count: 0 }
|
||||
|
||||
// Получение текущих данных проекта перед коммитом
|
||||
const prevProject = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
@@ -39,32 +53,21 @@ export async function commitToResult(
|
||||
))[0] || { contributed_hours: 0, available: '0.0000 RUB' }
|
||||
|
||||
console.log('📊 Балансы до коммита:')
|
||||
console.log('▶ Результат:', prevResult)
|
||||
console.log('▶ Проект:', prevProject)
|
||||
console.log('▶ Контрибьютор:', prevContributor)
|
||||
|
||||
// Обновим состояние проекта после возможной смены статуса
|
||||
const projectBefore = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'projects',
|
||||
1,
|
||||
projectHash,
|
||||
projectHash,
|
||||
3,
|
||||
'sha256',
|
||||
))[0]
|
||||
const prevTotalCommits = projectBefore?.counts?.total_commits ?? 0
|
||||
const prevCreatorsHours = projectBefore?.fact?.creators_hours ?? 0
|
||||
|
||||
// Создание коммита
|
||||
const commitData: CapitalContract.Actions.CreateCommit.ICommit = {
|
||||
coopname,
|
||||
username: creator,
|
||||
project_hash: projectHash,
|
||||
application: coopname,
|
||||
result_hash: resultHash,
|
||||
commit_hash: commitHash,
|
||||
creator_hours: spendHours,
|
||||
contributed_hours: spendHours,
|
||||
username: creator,
|
||||
}
|
||||
|
||||
console.log(`\n🚀 Отправка транзакции CreateCommit для результата ${resultHash}`)
|
||||
const createCommitResult = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
@@ -86,7 +89,7 @@ export async function commitToResult(
|
||||
expect(createCommitResult.transaction_id).toBeDefined()
|
||||
|
||||
// Проверка созданного коммита
|
||||
const blockchainCommit = (await blockchain.getTableRows(
|
||||
let blockchainCommit = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'commits',
|
||||
@@ -100,30 +103,39 @@ export async function commitToResult(
|
||||
console.log('🔍 Коммит после создания:', blockchainCommit)
|
||||
expect(blockchainCommit).toBeDefined()
|
||||
expect(blockchainCommit.commit_hash).toBe(commitHash)
|
||||
expect(blockchainCommit.project_hash).toBe(projectHash)
|
||||
expect(blockchainCommit.spended).toBe(totalSpended)
|
||||
expect(blockchainCommit.status).toBe('created')
|
||||
|
||||
// Получение данных проекта для определения мастера
|
||||
const projectData = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
// Утверждение коммита
|
||||
const approveCommitData: SovietContract.Actions.Approves.ConfirmApprove.IConfirmApprove = {
|
||||
coopname,
|
||||
'projects',
|
||||
1,
|
||||
projectHash,
|
||||
projectHash,
|
||||
3,
|
||||
'sha256',
|
||||
))[0]
|
||||
approval_hash: commitHash,
|
||||
approved_document: fakeDocument,
|
||||
}
|
||||
|
||||
expect(projectData).toBeDefined()
|
||||
expect(projectData.master).toBeDefined()
|
||||
console.log(`\n✅ Подтверждение коммита ${commitHash}`)
|
||||
const approveCommitResult = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: SovietContract.contractName.production,
|
||||
name: SovietContract.Actions.Approves.ConfirmApprove.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: approveCommitData,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
|
||||
// Утверждение коммита мастером проекта
|
||||
console.log(`\n✅ Подтверждение коммита ${commitHash} мастером ${projectData.master}`)
|
||||
const approveCommitResult = await processMasterApprove(blockchain, coopname, commitHash, projectData.master)
|
||||
getTotalRamUsage(approveCommitResult)
|
||||
expect(approveCommitResult.transaction_id).toBeDefined()
|
||||
|
||||
// Проверка утвержденного коммита
|
||||
const blockchainEmptyCommit = (await blockchain.getTableRows(
|
||||
blockchainCommit = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'commits',
|
||||
@@ -135,9 +147,96 @@ export async function commitToResult(
|
||||
))[0]
|
||||
|
||||
// Коммит удаляем после утверждения
|
||||
expect(blockchainEmptyCommit).toBeUndefined()
|
||||
expect(blockchainCommit).toBeUndefined()
|
||||
|
||||
// // Получение всех решений и выполнение последнего
|
||||
// const decisions = await blockchain.getTableRows(
|
||||
// SovietContract.contractName.production,
|
||||
// coopname,
|
||||
// 'decisions',
|
||||
// 1000,
|
||||
// )
|
||||
// const lastDecision = decisions[decisions.length - 1]
|
||||
|
||||
// console.log(`\n📜 Выполнение последнего решения: ${lastDecision.id}`)
|
||||
// await processDecision(blockchain, lastDecision.id)
|
||||
|
||||
// {
|
||||
// // Утверждение коммита
|
||||
// const act1Data: CapitalContract.Actions.SetAct1.ISetAct1 = {
|
||||
// coopname,
|
||||
// application: coopname,
|
||||
// username: creator,
|
||||
// commit_hash: commitHash,
|
||||
// act: fakeDocument,
|
||||
// }
|
||||
|
||||
// console.log(`\n✅ Установка акта1 по коммиту ${commitHash}`)
|
||||
// const act1Result = await blockchain.api.transact(
|
||||
// {
|
||||
// actions: [
|
||||
// {
|
||||
// account: CapitalContract.contractName.production,
|
||||
// name: CapitalContract.Actions.SetAct1.actionName,
|
||||
// authorization: [{ actor: coopname, permission: 'active' }],
|
||||
// data: act1Data,
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// blocksBehind: 3,
|
||||
// expireSeconds: 30,
|
||||
// },
|
||||
// )
|
||||
|
||||
// getTotalRamUsage(act1Result)
|
||||
// expect(act1Result.transaction_id).toBeDefined()
|
||||
// }
|
||||
|
||||
// {
|
||||
// // Утверждение коммита
|
||||
// const act2Data: CapitalContract.Actions.SetAct2.ISetAct2 = {
|
||||
// coopname,
|
||||
// application: coopname,
|
||||
// username: creator,
|
||||
// commit_hash: commitHash,
|
||||
// act: fakeDocument,
|
||||
// }
|
||||
|
||||
// console.log(`\n✅ Установка акта2 по коммиту ${commitHash}`)
|
||||
// const act2Result = await blockchain.api.transact(
|
||||
// {
|
||||
// actions: [
|
||||
// {
|
||||
// account: CapitalContract.contractName.production,
|
||||
// name: CapitalContract.Actions.SetAct2.actionName,
|
||||
// authorization: [{ actor: coopname, permission: 'active' }],
|
||||
// data: act2Data,
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// blocksBehind: 3,
|
||||
// expireSeconds: 30,
|
||||
// },
|
||||
// )
|
||||
|
||||
// getTotalRamUsage(act2Result)
|
||||
// expect(act2Result.transaction_id).toBeDefined()
|
||||
// }
|
||||
|
||||
// Проверка результата после утверждения коммита
|
||||
const finalResult = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'results',
|
||||
1,
|
||||
resultHash,
|
||||
resultHash,
|
||||
2,
|
||||
'sha256',
|
||||
))[0]
|
||||
|
||||
// Проверка проекта после утверждения коммита
|
||||
const finalProject = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
@@ -161,19 +260,23 @@ export async function commitToResult(
|
||||
))[0]
|
||||
|
||||
console.log('\n📊 Балансы после утверждения коммита:')
|
||||
console.log('▶ Результат:', finalResult)
|
||||
console.log('▶ Проект:', finalProject)
|
||||
console.log('▶ Контрибьютор:', finalContributor)
|
||||
|
||||
// Проверки по проекту
|
||||
expect(finalProject.counts.total_commits).toBe(prevTotalCommits + 1)
|
||||
expect(parseInt(finalProject.fact.creators_hours)).toBe(prevCreatorsHours + spendHours)
|
||||
// expect(parseFloat(finalResult.spended)).toBe(parseFloat(prevResult.spended) + parseFloat(totalSpended))
|
||||
// expect(parseFloat(finalProject.spended)).toBe(parseFloat(prevProject.spended))
|
||||
|
||||
// Проверка, что у контрибьютора увеличились contributed_hours и available
|
||||
// expect(parseFloat(finalContributor.contributed_hours)).toBe(parseFloat(prevContributor.contributed_hours) + spendHours)
|
||||
// expect(parseFloat(finalContributor.available)).toBe(parseFloat(prevContributor.available) + parseFloat(totalSpended))
|
||||
|
||||
console.log(`\n✅ Коммит ${commitHash} завершен успешно!`)
|
||||
|
||||
return {
|
||||
commitHash,
|
||||
commit: blockchainCommit,
|
||||
transactionId: approveCommitResult.transaction_id,
|
||||
transaction: approveCommitResult,
|
||||
finalResult,
|
||||
finalProject,
|
||||
finalContributor,
|
||||
}
|
||||
|
||||
@@ -4,4 +4,3 @@ export const walletProgramId = 1
|
||||
export const ratePerHour = '1000.0000 RUB'
|
||||
export const sourceProgramName = 'source'
|
||||
export const capitalProgramName = 'capital'
|
||||
export const circulationAccountId = 80
|
||||
@@ -1,27 +0,0 @@
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import type Blockchain from '../../blockchain'
|
||||
|
||||
/**
|
||||
* Получает кошелек капитализации пользователя
|
||||
* @param blockchain экземпляр блокчейна
|
||||
* @param coopname название кооператива
|
||||
* @param username имя пользователя
|
||||
* @returns кошелек капитализации или null если не найден
|
||||
*/
|
||||
export async function getCapitalWallet(
|
||||
blockchain: Blockchain,
|
||||
coopname: string,
|
||||
username: string,
|
||||
) {
|
||||
const capitalWallets = await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'capwallets',
|
||||
100,
|
||||
username,
|
||||
username,
|
||||
2, // by_username index
|
||||
)
|
||||
|
||||
return capitalWallets[0] || null
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import type Blockchain from '../../blockchain'
|
||||
import { makeCombinedChecksum256NameIndexKey } from '../shared/combinedKeys'
|
||||
|
||||
export async function getProject(blockchain: Blockchain, coopname: string, project_hash: string): Promise<CapitalContract.Tables.Projects.IProject> {
|
||||
const rows = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
CapitalContract.Tables.Projects.tableName,
|
||||
1000,
|
||||
project_hash,
|
||||
project_hash,
|
||||
3,
|
||||
'sha256',
|
||||
))
|
||||
|
||||
return rows[0]
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import type Blockchain from '../../blockchain'
|
||||
|
||||
/**
|
||||
* Получает кошелек проекта пользователя
|
||||
* @param blockchain экземпляр блокчейна
|
||||
* @param coopname название кооператива
|
||||
* @param projectHash хеш проекта
|
||||
* @param username имя пользователя
|
||||
* @returns кошелек проекта или null если не найден
|
||||
*/
|
||||
export async function getProjectWallet(
|
||||
blockchain: Blockchain,
|
||||
coopname: string,
|
||||
projectHash: string,
|
||||
username: string,
|
||||
) {
|
||||
const projectWallets = await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'projwallets',
|
||||
100,
|
||||
username,
|
||||
username,
|
||||
3, // by_username index
|
||||
)
|
||||
|
||||
// Ищем кошелек для конкретного проекта
|
||||
return projectWallets.find((wallet: any) => wallet.project_hash === projectHash) || null
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import type Blockchain from '../../blockchain'
|
||||
import { makeCombinedChecksum256NameIndexKey } from '../shared/combinedKeys'
|
||||
|
||||
export async function getSegment(blockchain: Blockchain, coopname: string, project_hash: string, author: string): Promise<CapitalContract.Tables.Segments.ISegment> {
|
||||
const key = makeCombinedChecksum256NameIndexKey(project_hash, author)
|
||||
const rows = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
CapitalContract.Tables.Segments.tableName,
|
||||
1000,
|
||||
key,
|
||||
key,
|
||||
3,
|
||||
'i128',
|
||||
))
|
||||
|
||||
return rows[0]
|
||||
}
|
||||
@@ -4,9 +4,7 @@ import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { generateRandomSHA256 } from '../../utils/randomHash'
|
||||
import { getCoopProgramWallet, getUserProgramWallet } from '../wallet/walletUtils'
|
||||
import { processDecision } from '../soviet/processDecision'
|
||||
import { processApprove } from './processApprove'
|
||||
import { getSegment } from './getSegment'
|
||||
import { sourceProgramId } from './consts'
|
||||
import { capitalProgramId, sourceProgramId } from './consts'
|
||||
|
||||
export async function investInProject(
|
||||
blockchain: any,
|
||||
@@ -19,6 +17,18 @@ export async function investInProject(
|
||||
const investHash = generateRandomSHA256()
|
||||
console.log(`\n🔹 Начало инвестиции: ${investHash}`)
|
||||
|
||||
// Получение текущих данных перед инвестицией
|
||||
const prevContributor = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'contributors',
|
||||
1,
|
||||
investor,
|
||||
investor,
|
||||
2,
|
||||
'i64',
|
||||
))[0] || { invested: '0.0000 RUB' }
|
||||
|
||||
const prevProject = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
@@ -30,17 +40,19 @@ export async function investInProject(
|
||||
'sha256',
|
||||
))[0] || { invested: '0.0000 RUB', available: '0.0000 RUB' }
|
||||
|
||||
const prevUserWallet = await getUserProgramWallet(blockchain, coopname, investor, sourceProgramId) || { blocked: '0.0000 RUB' }
|
||||
const prevProgramWallet = await getCoopProgramWallet(blockchain, coopname, sourceProgramId) || { blocked: '0.0000 RUB', share_contributions: '0.0000 RUB' }
|
||||
const prevUserWallet = await getUserProgramWallet(blockchain, coopname, investor, capitalProgramId) || { blocked: '0.0000 RUB' }
|
||||
const prevProgramWallet = await getCoopProgramWallet(blockchain, coopname, capitalProgramId) || { blocked: '0.0000 RUB', share_contributions: '0.0000 RUB' }
|
||||
|
||||
console.log('📊 Балансы до инвестиции:')
|
||||
console.log('▶ Контрибьютор:', prevContributor)
|
||||
console.log('▶ Проект:', prevProject)
|
||||
console.log('▶ Кошелек пользователя:', prevUserWallet)
|
||||
console.log('▶ Кошелек программы:', prevProgramWallet)
|
||||
|
||||
// Создание инвестиции
|
||||
const createInvestData: CapitalContract.Actions.CreateProjectInvest.ICreateInvest = {
|
||||
const createInvestData: CapitalContract.Actions.CreateInvest.ICreateInvest = {
|
||||
coopname,
|
||||
application: coopname,
|
||||
project_hash: projectHash,
|
||||
username: investor,
|
||||
invest_hash: investHash,
|
||||
@@ -54,7 +66,7 @@ export async function investInProject(
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.CreateProjectInvest.actionName,
|
||||
name: CapitalContract.Actions.CreateInvest.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: createInvestData,
|
||||
},
|
||||
@@ -69,7 +81,7 @@ export async function investInProject(
|
||||
getTotalRamUsage(createInvestResult)
|
||||
expect(createInvestResult.transaction_id).toBeDefined()
|
||||
|
||||
const blockchainInvest = (await blockchain.getTableRows(
|
||||
let blockchainInvest = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'invests',
|
||||
@@ -85,11 +97,36 @@ export async function investInProject(
|
||||
expect(blockchainInvest.status).toBe('created')
|
||||
|
||||
// Утверждение инвестиции
|
||||
console.log(`\n✅ Подтверждение инвестиции ${investHash}`)
|
||||
const approveInvestResult = await processApprove(blockchain, coopname, investHash)
|
||||
const approveInvestData: CapitalContract.Actions.ApproveInvest.IApproveInvest = {
|
||||
coopname,
|
||||
application: coopname,
|
||||
approver: 'ant',
|
||||
invest_hash: investHash,
|
||||
approved_statement: fakeDocument,
|
||||
}
|
||||
|
||||
// Проверка утвержденной инвестиции
|
||||
const blockchainEmptyInvest = (await blockchain.getTableRows(
|
||||
console.log(`\n✅ Подтверждение инвестиции ${investHash}`)
|
||||
const approveInvestResult = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.ApproveInvest.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: approveInvestData,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
|
||||
getTotalRamUsage(approveInvestResult)
|
||||
expect(approveInvestResult.transaction_id).toBeDefined()
|
||||
|
||||
blockchainInvest = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'invests',
|
||||
@@ -100,10 +137,49 @@ export async function investInProject(
|
||||
'sha256',
|
||||
))[0]
|
||||
|
||||
// Инвестиция удаляется после утверждения
|
||||
expect(blockchainEmptyInvest).toBeUndefined()
|
||||
console.log('🔍 Инвестиция после утверждения:', blockchainInvest)
|
||||
expect(blockchainInvest).toBeDefined()
|
||||
expect(blockchainInvest.status).toBe('approved')
|
||||
|
||||
const finalProject = (await blockchain.getTableRows(
|
||||
// Получение всех решений и выполнение последнего
|
||||
const decisions = await blockchain.getTableRows(
|
||||
SovietContract.contractName.production,
|
||||
coopname,
|
||||
'decisions',
|
||||
1000,
|
||||
)
|
||||
const lastDecision = decisions[decisions.length - 1]
|
||||
|
||||
console.log(`\n📜 Выполнение последнего решения: ${lastDecision.id}`)
|
||||
await processDecision(blockchain, lastDecision.id)
|
||||
|
||||
blockchainInvest = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'invests',
|
||||
1,
|
||||
investHash,
|
||||
investHash,
|
||||
2,
|
||||
'sha256',
|
||||
))[0]
|
||||
|
||||
console.log('📌 Инвестиция после завершения:', blockchainInvest)
|
||||
expect(blockchainInvest).toBeUndefined()
|
||||
|
||||
// Получение новых данных после инвестиции
|
||||
const contributor = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'contributors',
|
||||
1,
|
||||
investor,
|
||||
investor,
|
||||
2,
|
||||
'i64',
|
||||
))[0]
|
||||
|
||||
const project = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'projects',
|
||||
@@ -114,30 +190,24 @@ export async function investInProject(
|
||||
'sha256',
|
||||
))[0]
|
||||
|
||||
const finalUserWallet = await getUserProgramWallet(blockchain, coopname, investor, sourceProgramId)
|
||||
const finalProgramWallet = await getCoopProgramWallet(blockchain, coopname, sourceProgramId)
|
||||
|
||||
// Получение сегмента инвестора для данного проекта
|
||||
const segment = await getSegment(blockchain, coopname, projectHash, investor)
|
||||
const userWallet = await getUserProgramWallet(blockchain, coopname, investor, capitalProgramId)
|
||||
const programWallet = await getCoopProgramWallet(blockchain, coopname, capitalProgramId)
|
||||
|
||||
console.log('\n📊 Балансы после инвестиции:')
|
||||
console.log('▶ Проект:', finalProject)
|
||||
console.log('▶ Сегмент:', segment)
|
||||
console.log('▶ Кошелек пользователя:', finalUserWallet)
|
||||
console.log('▶ Кошелек программы:', finalProgramWallet)
|
||||
console.log('▶ Контрибьютор:', contributor)
|
||||
console.log('▶ Проект:', project)
|
||||
console.log('▶ Кошелек пользователя:', userWallet)
|
||||
console.log('▶ Кошелек программы:', programWallet)
|
||||
|
||||
// Проверка изменения балансов
|
||||
expect(parseFloat(finalUserWallet.blocked)).toBeCloseTo(parseFloat(prevUserWallet.blocked) + parseFloat(investAmount), 1)
|
||||
expect(parseFloat(finalProgramWallet.blocked)).toBeCloseTo(parseFloat(prevProgramWallet.blocked) + parseFloat(investAmount), 1)
|
||||
expect(parseFloat(contributor.invested)).toBe(parseFloat(prevContributor.invested) + parseFloat(investAmount))
|
||||
expect(parseFloat(project.invested)).toBe(parseFloat(prevProject.invested) + parseFloat(investAmount))
|
||||
expect(parseFloat(project.available)).toBe(parseFloat(prevProject.available) + parseFloat(investAmount))
|
||||
expect(parseFloat(userWallet.blocked)).toBe(parseFloat(prevUserWallet.blocked) + parseFloat(investAmount))
|
||||
expect(parseFloat(programWallet.blocked)).toBe(parseFloat(prevProgramWallet.blocked) + parseFloat(investAmount))
|
||||
expect(parseFloat(programWallet.share_contributions)).toBe(parseFloat(prevProgramWallet.share_contributions) + parseFloat(investAmount))
|
||||
|
||||
console.log(`\n✅ Инвестирование на ${investAmount} завершено успешно!`)
|
||||
|
||||
return {
|
||||
investHash,
|
||||
invest: blockchainInvest,
|
||||
transactionId: approveInvestResult.transaction_id,
|
||||
prevProject,
|
||||
project: finalProject,
|
||||
segment,
|
||||
}
|
||||
return { investHash, transactionId: approveInvestResult.transaction_id }
|
||||
}
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import type Blockchain from '../../blockchain'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { getSegment } from './getSegment'
|
||||
|
||||
export async function processAddContributor(
|
||||
blockchain: Blockchain,
|
||||
coopname: string,
|
||||
project_hash: string,
|
||||
username: string,
|
||||
) {
|
||||
// Состояние сегмента до добавления (может не существовать)
|
||||
let segmentBefore: any = null
|
||||
try {
|
||||
segmentBefore = await getSegment(blockchain, coopname, project_hash, username)
|
||||
}
|
||||
catch (error) {
|
||||
// Сегмент может не существовать - это нормально
|
||||
}
|
||||
|
||||
const data: CapitalContract.Actions.RegisterShare.IRegisterShare = {
|
||||
coopname,
|
||||
project_hash,
|
||||
username,
|
||||
}
|
||||
|
||||
// Выполняем добавление участника в проект
|
||||
const txAdd = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.RegisterShare.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data,
|
||||
},
|
||||
],
|
||||
},
|
||||
{ blocksBehind: 3, expireSeconds: 30 },
|
||||
)
|
||||
getTotalRamUsage(txAdd)
|
||||
expect(txAdd.transaction_id).toBeDefined()
|
||||
|
||||
// Состояние сегмента после добавления
|
||||
const segmentAfter = await getSegment(blockchain, coopname, project_hash, username)
|
||||
|
||||
return {
|
||||
transactionId: txAdd.transaction_id,
|
||||
segmentBefore,
|
||||
segmentAfter,
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import { expect } from 'vitest'
|
||||
import { SovietContract } from 'cooptypes'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { fakeDocument } from '../shared/fakeDocument'
|
||||
|
||||
export async function processApprove(blockchain: any, coopname: string, approvalHash: string) {
|
||||
fakeDocument.signatures[0].signer = 'ant'
|
||||
|
||||
const data: SovietContract.Actions.Approves.ConfirmApprove.IConfirmApprove = {
|
||||
coopname,
|
||||
approval_hash: approvalHash,
|
||||
approved_document: fakeDocument,
|
||||
}
|
||||
|
||||
const result = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: SovietContract.contractName.production,
|
||||
name: SovietContract.Actions.Approves.ConfirmApprove.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
|
||||
getTotalRamUsage(result)
|
||||
expect(result.transaction_id).toBeDefined()
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import type Blockchain from '../../blockchain'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { makeCombinedChecksum256NameIndexKey } from '../shared/combinedKeys'
|
||||
|
||||
/**
|
||||
* Функция для расчета голосов участника по методу Водянова
|
||||
* @param blockchain экземпляр блокчейна
|
||||
* @param data данные для расчета голосов
|
||||
*/
|
||||
export async function processCalculateVotes(
|
||||
blockchain: Blockchain,
|
||||
data: CapitalContract.Actions.CalculateVotes.IFinalVoting,
|
||||
) {
|
||||
const { coopname, username, project_hash } = data
|
||||
|
||||
console.log(`\n🧮 Начало расчета голосов для участника ${username} в проекте ${project_hash}`)
|
||||
|
||||
// Состояние до: данные проекта
|
||||
const projectBefore = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'projects',
|
||||
1,
|
||||
project_hash,
|
||||
project_hash,
|
||||
3,
|
||||
'sha256',
|
||||
))[0]
|
||||
|
||||
// Состояние до: данные сегмента участника
|
||||
const segmentKey = makeCombinedChecksum256NameIndexKey(project_hash, username)
|
||||
const segmentBefore = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'segments',
|
||||
1,
|
||||
segmentKey,
|
||||
segmentKey,
|
||||
3,
|
||||
'i128',
|
||||
))[0]
|
||||
|
||||
// console.log('📊 Состояние до расчета голосов:')
|
||||
// console.log('▶ Статус проекта:', projectBefore.status)
|
||||
// console.log('▶ Голосов получено:', projectBefore.voting.votes_received)
|
||||
// console.log('▶ Всего участников голосования:', projectBefore.voting.total_voters)
|
||||
// console.log(`▶ Сегмент участника ${username}:`)
|
||||
// console.log(' - voting_bonus:', segmentBefore.voting_bonus)
|
||||
// console.log(' - equal_author_bonus:', segmentBefore.equal_author_bonus)
|
||||
// console.log(' - direct_creator_bonus:', segmentBefore.direct_creator_bonus)
|
||||
// console.log(' - total_segment_cost:', segmentBefore.total_segment_cost)
|
||||
|
||||
// Отправляем транзакцию расчета голосов
|
||||
const txResult = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.CalculateVotes.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data,
|
||||
},
|
||||
],
|
||||
},
|
||||
{ blocksBehind: 3, expireSeconds: 30 },
|
||||
)
|
||||
getTotalRamUsage(txResult)
|
||||
expect(txResult.transaction_id).toBeDefined()
|
||||
|
||||
// Состояние после: данные проекта
|
||||
const projectAfter = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'projects',
|
||||
1,
|
||||
project_hash,
|
||||
project_hash,
|
||||
3,
|
||||
'sha256',
|
||||
))[0]
|
||||
|
||||
// Состояние после: данные сегмента участника
|
||||
const segmentAfter = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'segments',
|
||||
1,
|
||||
segmentKey,
|
||||
segmentKey,
|
||||
3,
|
||||
'i128',
|
||||
))[0]
|
||||
|
||||
console.log('📊 Состояние после расчета голосов:')
|
||||
console.log('▶ Статус проекта:', projectAfter.status)
|
||||
console.log(`▶ Сегмент участника ${username}:`)
|
||||
console.log(' - voting_bonus:', segmentAfter.voting_bonus)
|
||||
console.log(' - total_voting_pool:', projectAfter.voting.amounts.total_voting_pool)
|
||||
console.log(' - active_voting_amount:', projectAfter.voting.amounts.active_voting_amount)
|
||||
console.log(' - equal_voting_amount:', projectAfter.voting.amounts.equal_voting_amount)
|
||||
|
||||
return {
|
||||
txId: txResult.transaction_id,
|
||||
projectBefore,
|
||||
projectAfter,
|
||||
segmentBefore,
|
||||
segmentAfter,
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import type Blockchain from '../../blockchain'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
|
||||
/**
|
||||
* Функция для завершения голосования по проекту
|
||||
* @param blockchain экземпляр блокчейна
|
||||
* @param data данные для завершения голосования
|
||||
*/
|
||||
export async function processCompleteVoting(
|
||||
blockchain: Blockchain,
|
||||
data: CapitalContract.Actions.CompleteVoting.ICompleteVoting,
|
||||
) {
|
||||
const { coopname, project_hash } = data
|
||||
|
||||
console.log(`\n✅ Начало завершения голосования для проекта ${project_hash}`)
|
||||
|
||||
// Состояние до: данные проекта
|
||||
const projectBefore = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'projects',
|
||||
1,
|
||||
project_hash,
|
||||
project_hash,
|
||||
3,
|
||||
'sha256',
|
||||
))[0]
|
||||
|
||||
console.log('📊 Состояние до завершения голосования:')
|
||||
console.log('▶ Статус проекта:', projectBefore.status)
|
||||
console.log('▶ Голосов получено:', projectBefore.voting.votes_received)
|
||||
console.log('▶ Всего участников голосования:', projectBefore.voting.total_voters)
|
||||
console.log('▶ Дедлайн голосования:', projectBefore.voting.voting_deadline)
|
||||
|
||||
// Отправляем транзакцию завершения голосования
|
||||
const txResult = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.CompleteVoting.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data,
|
||||
},
|
||||
],
|
||||
},
|
||||
{ blocksBehind: 3, expireSeconds: 30 },
|
||||
)
|
||||
getTotalRamUsage(txResult)
|
||||
expect(txResult.transaction_id).toBeDefined()
|
||||
|
||||
// Состояние после: данные проекта
|
||||
const projectAfter = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'projects',
|
||||
1,
|
||||
project_hash,
|
||||
project_hash,
|
||||
3,
|
||||
'sha256',
|
||||
))[0]
|
||||
|
||||
console.log('📊 Состояние после завершения голосования:')
|
||||
console.log('▶ Статус проекта:', projectAfter.status)
|
||||
console.log('▶ Голосов получено:', projectAfter.voting.votes_received)
|
||||
console.log('▶ Всего участников голосования:', projectAfter.voting.total_voters)
|
||||
|
||||
// Проверяем что статус изменился на completed
|
||||
expect(projectAfter.status).toBe('completed')
|
||||
|
||||
console.log(`\n✅ Голосование для проекта ${project_hash} успешно завершено!`)
|
||||
|
||||
return {
|
||||
projectHash: project_hash,
|
||||
txId: txResult.transaction_id,
|
||||
projectBefore,
|
||||
projectAfter,
|
||||
}
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { generateRandomSHA256 } from '../../utils/randomHash'
|
||||
import { fakeDocument } from '../shared/fakeDocument'
|
||||
import type Blockchain from '../../blockchain'
|
||||
import { getCoopProgramWallet, getUserProgramWallet } from '../wallet/walletUtils'
|
||||
import { getSegment } from './getSegment'
|
||||
import { getProjectWallet } from './getProjectWallet'
|
||||
import { capitalProgramId, walletProgramId } from './consts'
|
||||
import { getProject } from './getProject'
|
||||
|
||||
/**
|
||||
* Функция для конвертации сегмента участника
|
||||
* @param blockchain экземпляр блокчейна
|
||||
* @param coopname название кооператива
|
||||
* @param username имя пользователя
|
||||
* @param projectHash хеш проекта
|
||||
* @param walletAmount сумма для конвертации в главный кошелек
|
||||
* @param capitalAmount сумма для конвертации в капитализацию
|
||||
* @param projectAmount сумма для конвертации в кошелек проекта
|
||||
*/
|
||||
export async function processConvertSegment(
|
||||
blockchain: Blockchain,
|
||||
coopname: string,
|
||||
username: string,
|
||||
projectHash: string,
|
||||
walletAmount: string,
|
||||
capitalAmount: string,
|
||||
projectAmount: string,
|
||||
) {
|
||||
const convertHash = generateRandomSHA256()
|
||||
console.log(`\n🔹 Начало процесса конвертации сегмента: ${convertHash}`)
|
||||
|
||||
// Получаем сегмент до конвертации
|
||||
const segmentBefore = await getSegment(blockchain, coopname, projectHash, username)
|
||||
console.log('📊 Сегмент до конвертации:', segmentBefore)
|
||||
|
||||
// Получаем кошельки до конвертации
|
||||
const mainWalletBefore = await getCoopProgramWallet(blockchain, coopname, walletProgramId)
|
||||
const capitalWalletBefore = await getCoopProgramWallet(blockchain, coopname, capitalProgramId)
|
||||
const userCapitalWalletBefore = await getUserProgramWallet(blockchain, coopname, username, capitalProgramId)
|
||||
const projectWalletBefore = await getProjectWallet(blockchain, coopname, projectHash, username)
|
||||
const projectBefore = await getProject(blockchain, coopname, projectHash)
|
||||
|
||||
console.log('💰 Кошельки до конвертации:')
|
||||
console.log(' ▶ Главный кошелек программы:', mainWalletBefore)
|
||||
console.log(' ▶ Глобальный кошелек программы капитализации:', capitalWalletBefore)
|
||||
console.log(' ▶ Кошелек пользователя в программе капитализации:', userCapitalWalletBefore)
|
||||
console.log(' ▶ Кошелек проекта:', projectWalletBefore)
|
||||
console.log(' ▶ Проект до конвертации: ', projectBefore)
|
||||
|
||||
// 1. Конвертируем сегмент
|
||||
const convertSegmentData: CapitalContract.Actions.ConvertSegment.IConvertSegment = {
|
||||
coopname,
|
||||
username,
|
||||
project_hash: projectHash,
|
||||
convert_hash: convertHash,
|
||||
wallet_amount: walletAmount,
|
||||
capital_amount: capitalAmount,
|
||||
project_amount: projectAmount,
|
||||
convert_statement: fakeDocument,
|
||||
}
|
||||
|
||||
console.log(`\n🚀 Отправка транзакции ConvertSegment для ${username}`)
|
||||
console.log(` В главный кошелек: ${walletAmount}`)
|
||||
console.log(` В капитализацию: ${capitalAmount}`)
|
||||
console.log(` В проект: ${projectAmount}`)
|
||||
|
||||
const convertResult = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.ConvertSegment.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: convertSegmentData,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
|
||||
getTotalRamUsage(convertResult)
|
||||
expect(convertResult.transaction_id).toBeDefined()
|
||||
console.log('✅ Сегмент конвертирован:', convertHash)
|
||||
|
||||
// Получаем сегмент после конвертации (должен быть удален)
|
||||
const segmentAfter = await getSegment(blockchain, coopname, projectHash, username)
|
||||
|
||||
// Получаем кошельки после конвертации
|
||||
const mainWalletAfter = await getCoopProgramWallet(blockchain, coopname, walletProgramId)
|
||||
const capitalWalletAfter = await getCoopProgramWallet(blockchain, coopname, capitalProgramId)
|
||||
const userCapitalWalletAfter = await getUserProgramWallet(blockchain, coopname, username, capitalProgramId)
|
||||
const projectWalletAfter = await getProjectWallet(blockchain, coopname, projectHash, username)
|
||||
|
||||
console.log('💰 Кошельки после конвертации:')
|
||||
console.log(' ▶ Главный кошелек программы:', mainWalletAfter)
|
||||
console.log(' ▶ Глобальный кошелек программы капитализации:', capitalWalletAfter)
|
||||
console.log(' ▶ Кошелек пользователя в программе капитализации:', userCapitalWalletAfter)
|
||||
console.log(' ▶ Кошелек проекта:', projectWalletAfter)
|
||||
|
||||
// Проверяем что сегмент удален после конвертации
|
||||
expect(segmentAfter).toBeUndefined()
|
||||
console.log('🔍 Сегмент после конвертации: ', segmentAfter)
|
||||
|
||||
// Проверяем изменения в кошельках
|
||||
console.log('\n🔍 Проверка изменений в кошельках:')
|
||||
|
||||
// Проверяем главный кошелек программы (wallet_amount)
|
||||
if (walletAmount !== '0.0000 RUB') {
|
||||
const walletAmountValue = parseFloat(walletAmount.split(' ')[0])
|
||||
const beforeAvailable = mainWalletBefore ? parseFloat(mainWalletBefore.available.split(' ')[0]) : 0
|
||||
const afterAvailable = mainWalletAfter ? parseFloat(mainWalletAfter.available.split(' ')[0]) : 0
|
||||
const expectedIncrease = beforeAvailable + walletAmountValue
|
||||
console.log(`✅ Главный кошелек программы: ${beforeAvailable} → ${afterAvailable} (+${walletAmountValue})`)
|
||||
expect(afterAvailable).toBeCloseTo(expectedIncrease, 1)
|
||||
}
|
||||
|
||||
// Проверяем глобальный кошелек программы капитализации (capital_amount)
|
||||
if (capitalAmount !== '0.0000 RUB') {
|
||||
const capitalAmountValue = parseFloat(capitalAmount.split(' ')[0])
|
||||
const beforeBlocked = capitalWalletBefore ? parseFloat(capitalWalletBefore.blocked.split(' ')[0]) : 0
|
||||
const afterBlocked = capitalWalletAfter ? parseFloat(capitalWalletAfter.blocked.split(' ')[0]) : 0
|
||||
const expectedIncrease = beforeBlocked + capitalAmountValue
|
||||
console.log(`✅ Глобальный кошелек программы капитализации: ${beforeBlocked} → ${afterBlocked} (+${capitalAmountValue})`)
|
||||
expect(afterBlocked).toBeCloseTo(expectedIncrease, 1)
|
||||
}
|
||||
|
||||
// Проверяем кошелек пользователя в программе капитализации (capital_amount)
|
||||
if (capitalAmount !== '0.0000 RUB') {
|
||||
const capitalAmountValue = parseFloat(capitalAmount.split(' ')[0])
|
||||
const beforeBlocked = userCapitalWalletBefore ? parseFloat(userCapitalWalletBefore.blocked.split(' ')[0]) : 0
|
||||
const afterBlocked = userCapitalWalletAfter ? parseFloat(userCapitalWalletAfter.blocked.split(' ')[0]) : 0
|
||||
const expectedIncrease = beforeBlocked + capitalAmountValue
|
||||
console.log(`✅ Кошелек пользователя в программе капитализации: ${beforeBlocked} → ${afterBlocked} (+${capitalAmountValue})`)
|
||||
expect(afterBlocked).toBeCloseTo(expectedIncrease, 1)
|
||||
}
|
||||
|
||||
// Проверяем кошелек проекта (project_amount)
|
||||
if (projectAmount !== '0.0000 RUB' && projectBefore.parent_hash.includes('0000000000')) {
|
||||
const projectAmountValue = parseFloat(projectAmount.split(' ')[0])
|
||||
const beforeShares = projectWalletBefore ? parseFloat(projectWalletBefore.shares.split(' ')[0]) : 0
|
||||
const afterShares = projectWalletAfter ? parseFloat(projectWalletAfter.shares.split(' ')[0]) : 0
|
||||
const expectedIncrease = beforeShares + projectAmountValue
|
||||
console.log(`✅ Кошелек проекта: ${beforeShares} → ${afterShares} (+${projectAmountValue})`)
|
||||
expect(afterShares).toBeCloseTo(expectedIncrease, 1)
|
||||
}
|
||||
|
||||
console.log('\n📊 Результаты после конвертации сегмента:')
|
||||
console.log('▶ Сегмент до:', segmentBefore)
|
||||
console.log('▶ Сегмент после удалён: ', segmentAfter)
|
||||
|
||||
console.log(`\n✅ Процесс конвертации сегмента завершен успешно!`)
|
||||
|
||||
return {
|
||||
convertHash,
|
||||
segmentBefore,
|
||||
segmentAfter,
|
||||
mainWalletBefore,
|
||||
mainWalletAfter,
|
||||
capitalWalletBefore,
|
||||
capitalWalletAfter,
|
||||
userCapitalWalletBefore,
|
||||
userCapitalWalletAfter,
|
||||
projectWalletBefore,
|
||||
projectWalletAfter,
|
||||
transactionId: convertResult.transaction_id,
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import type Blockchain from '../../blockchain'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { getCoopProgramWallet, getLedgerAccountById } from '../wallet/walletUtils'
|
||||
import { processLastDecision } from '../soviet/processLastDecision'
|
||||
import { processApprove } from './processApprove'
|
||||
import { capitalProgramId, circulationAccountId } from './consts'
|
||||
|
||||
export async function processCreateProgramProperty(
|
||||
blockchain: Blockchain,
|
||||
data: CapitalContract.Actions.CreateProgramProperty.ICreateProgramProperty,
|
||||
fakeDocument: any,
|
||||
) {
|
||||
const { coopname, username, property_hash } = data
|
||||
|
||||
// Состояния до: кошелек программы капитализации и паевой счет в бухгалтерии
|
||||
const programWalletBefore = await getCoopProgramWallet(blockchain, coopname, capitalProgramId)
|
||||
const ledgerShareBefore = await getLedgerAccountById(blockchain, coopname, circulationAccountId)
|
||||
|
||||
// 1) Создаём программное предложение имущественного взноса
|
||||
const txCreate = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.CreateProgramProperty.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data,
|
||||
},
|
||||
],
|
||||
},
|
||||
{ blocksBehind: 3, expireSeconds: 30 },
|
||||
)
|
||||
getTotalRamUsage(txCreate)
|
||||
expect(txCreate.transaction_id).toBeDefined()
|
||||
|
||||
// 2) Подтверждение одобрения председателем (через soviet)
|
||||
await processApprove(blockchain as any, coopname, property_hash as unknown as string)
|
||||
|
||||
// 3) Обработка решения совета (AUTHORIZE_PROGRAM_PROPERTY)
|
||||
await processLastDecision(blockchain, coopname)
|
||||
|
||||
// 4) Акт 1 — подписывает участник-вноситель (username)
|
||||
fakeDocument.signatures[0].signer = username
|
||||
const act1Data = {
|
||||
coopname,
|
||||
username,
|
||||
property_hash,
|
||||
act: fakeDocument,
|
||||
} as CapitalContract.Actions.Act1ProgramProperty.IAct1ProgramProperty
|
||||
|
||||
const txAct1 = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.Act1ProgramProperty.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: act1Data,
|
||||
},
|
||||
],
|
||||
},
|
||||
{ blocksBehind: 3, expireSeconds: 30 },
|
||||
)
|
||||
getTotalRamUsage(txAct1)
|
||||
expect(txAct1.transaction_id).toBeDefined()
|
||||
|
||||
// 5) Акт 2 — подписывает председатель совета кооператива ('ant')
|
||||
fakeDocument.signatures[0].signer = 'ant'
|
||||
|
||||
const act2Data = {
|
||||
coopname,
|
||||
property_hash,
|
||||
username: 'ant',
|
||||
act: fakeDocument,
|
||||
} as CapitalContract.Actions.Act2ProgramProperty.IAct2ProgramProperty
|
||||
|
||||
const txAct2 = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.Act2ProgramProperty.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: act2Data,
|
||||
},
|
||||
],
|
||||
},
|
||||
{ blocksBehind: 3, expireSeconds: 30 },
|
||||
)
|
||||
getTotalRamUsage(txAct2)
|
||||
expect(txAct2.transaction_id).toBeDefined()
|
||||
|
||||
// Состояния после
|
||||
const programWalletAfter = await getCoopProgramWallet(blockchain, coopname, capitalProgramId)
|
||||
const ledgerShareAfter = await getLedgerAccountById(blockchain, coopname, circulationAccountId)
|
||||
|
||||
return {
|
||||
propertyHash: property_hash,
|
||||
txCreateId: txCreate.transaction_id,
|
||||
txAct1Id: txAct1.transaction_id,
|
||||
txAct2Id: txAct2.transaction_id,
|
||||
programWalletBefore,
|
||||
programWalletAfter,
|
||||
ledgerShareBefore,
|
||||
ledgerShareAfter,
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import type Blockchain from '../../blockchain'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { processLastDecision } from '../soviet/processLastDecision'
|
||||
import { processApprove } from './processApprove'
|
||||
import { getSegment } from './getSegment'
|
||||
|
||||
export async function processCreateProjectProperty(
|
||||
blockchain: Blockchain,
|
||||
data: CapitalContract.Actions.CreateProjectProperty.ICreateProjectProperty,
|
||||
) {
|
||||
const { coopname, username, project_hash, property_hash } = data
|
||||
|
||||
// Состояния до: сегмент проекта и данные проекта
|
||||
const segmentBefore = await getSegment(blockchain, coopname, project_hash, username)
|
||||
const projectBefore = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'projects',
|
||||
1,
|
||||
project_hash,
|
||||
project_hash,
|
||||
3,
|
||||
'sha256',
|
||||
))[0]
|
||||
|
||||
// 1) Создаём проектное предложение имущественного взноса
|
||||
const txCreate = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.CreateProjectProperty.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data,
|
||||
},
|
||||
],
|
||||
},
|
||||
{ blocksBehind: 3, expireSeconds: 30 },
|
||||
)
|
||||
getTotalRamUsage(txCreate)
|
||||
expect(txCreate.transaction_id).toBeDefined()
|
||||
|
||||
// 2) Подтверждение одобрения председателем (через soviet)
|
||||
await processApprove(blockchain as any, coopname, property_hash as unknown as string)
|
||||
|
||||
// Состояния после: сегмент проекта и данные проекта
|
||||
const segmentAfter = await getSegment(blockchain, coopname, project_hash, username)
|
||||
const projectAfter = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'projects',
|
||||
1,
|
||||
project_hash,
|
||||
project_hash,
|
||||
3,
|
||||
'sha256',
|
||||
))[0]
|
||||
|
||||
return {
|
||||
propertyHash: property_hash,
|
||||
txCreateId: txCreate.transaction_id,
|
||||
segmentBefore,
|
||||
segmentAfter,
|
||||
projectBefore,
|
||||
projectAfter,
|
||||
}
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract, GatewayContract } from 'cooptypes'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { generateRandomSHA256 } from '../../utils/randomHash'
|
||||
import { processLastDecision } from '../soviet/processLastDecision'
|
||||
import type Blockchain from '../../blockchain'
|
||||
import { formatDateToISOString } from '../shared/formatDateToISOString'
|
||||
import { processApprove } from './processApprove'
|
||||
import { getSegment } from './getSegment'
|
||||
|
||||
export async function processDebt(
|
||||
blockchain: Blockchain,
|
||||
coopname: string,
|
||||
username: string,
|
||||
projectHash: string,
|
||||
debtAmount: string,
|
||||
fakeDocument: any,
|
||||
repaidAtDate: Date,
|
||||
) {
|
||||
const debtHash = generateRandomSHA256()
|
||||
console.log(`\n🔹 Начало процесса создания долга: ${debtHash}`)
|
||||
|
||||
// Дата возврата через месяц
|
||||
const repaidAt = formatDateToISOString(repaidAtDate)
|
||||
|
||||
// Получаем сегмент до создания долга
|
||||
const segmentBefore = await getSegment(blockchain, coopname, projectHash, username)
|
||||
console.log('📊 Сегмент до долга:', segmentBefore)
|
||||
|
||||
// Получаем контрибьютора до создания долга
|
||||
const contributorsBefore = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'contributors',
|
||||
1,
|
||||
username,
|
||||
username,
|
||||
2,
|
||||
'i64',
|
||||
))
|
||||
const contributorBefore = contributorsBefore[0]
|
||||
console.log('📊 Контрибьютор до долга:', contributorBefore)
|
||||
|
||||
fakeDocument.signatures[0].signer = username
|
||||
|
||||
// 1. Создаем долг
|
||||
const createDebtData: CapitalContract.Actions.CreateDebt.ICreateDebt = {
|
||||
coopname,
|
||||
username,
|
||||
debt_hash: debtHash,
|
||||
project_hash: projectHash,
|
||||
amount: debtAmount,
|
||||
repaid_at: repaidAt,
|
||||
statement: fakeDocument,
|
||||
}
|
||||
|
||||
console.log(`\n🚀 Отправка транзакции CreateDebt для ${username} на сумму ${debtAmount}`)
|
||||
const createDebtResult = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.CreateDebt.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: createDebtData,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
|
||||
getTotalRamUsage(createDebtResult)
|
||||
expect(createDebtResult.transaction_id).toBeDefined()
|
||||
console.log('✅ Долг создан:', debtHash)
|
||||
|
||||
// Проверяем что долг создался
|
||||
const debts = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'debts',
|
||||
1,
|
||||
debtHash,
|
||||
debtHash,
|
||||
3,
|
||||
'sha256',
|
||||
))
|
||||
const createdDebt = debts[0]
|
||||
console.log('🔍 Долг в блокчейне:', createdDebt)
|
||||
expect(createdDebt).toBeDefined()
|
||||
expect(createdDebt.status).toBe('created')
|
||||
|
||||
// 2. Одобряем долг через processApprove (soviet контракт)
|
||||
console.log(`\n✅ Подтверждение долга ${debtHash} через soviet`)
|
||||
await processApprove(blockchain, coopname, debtHash)
|
||||
console.log('✅ Долг одобрен советом')
|
||||
|
||||
// 3. Процессим решение совета
|
||||
await processLastDecision(blockchain, coopname)
|
||||
console.log('✅ Решение совета принято')
|
||||
|
||||
// 5. Подтверждаем завершение вывода (gateway сам вызовет callback на capital)
|
||||
const confirmOutcomeData: GatewayContract.Actions.CompleteOutcome.ICompleteOutcome = {
|
||||
coopname,
|
||||
outcome_hash: debtHash,
|
||||
}
|
||||
|
||||
console.log(`\n✅ Подтверждение завершения вывода`)
|
||||
const confirmResult = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: GatewayContract.contractName.production,
|
||||
name: GatewayContract.Actions.CompleteOutcome.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: confirmOutcomeData,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
|
||||
getTotalRamUsage(confirmResult)
|
||||
expect(confirmResult.transaction_id).toBeDefined()
|
||||
console.log('✅ Оплата долга подтверждена')
|
||||
|
||||
// Получаем финальные состояния
|
||||
const segmentAfter = await getSegment(blockchain, coopname, projectHash, username)
|
||||
|
||||
const contributorsAfter = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'contributors',
|
||||
1,
|
||||
username,
|
||||
username,
|
||||
2,
|
||||
'i64',
|
||||
))
|
||||
const contributorAfter = contributorsAfter[0]
|
||||
|
||||
// Получаем долг после подтверждения оплаты
|
||||
const debtsAfter = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'debts',
|
||||
1,
|
||||
debtHash,
|
||||
debtHash,
|
||||
3,
|
||||
'sha256',
|
||||
))
|
||||
const debtAfter = debtsAfter[0]
|
||||
|
||||
console.log('\n📊 Результаты после обработки долга:')
|
||||
console.log('▶ Сегмент до:', segmentBefore)
|
||||
console.log('▶ Сегмент после:', segmentAfter)
|
||||
console.log('▶ Контрибьютор до:', contributorBefore)
|
||||
console.log('▶ Контрибьютор после:', contributorAfter)
|
||||
console.log('▶ Долг после оплаты:', debtAfter)
|
||||
|
||||
console.log(`\n✅ Процесс долга на ${debtAmount} завершен успешно!`)
|
||||
|
||||
return {
|
||||
debtHash,
|
||||
debt: createdDebt,
|
||||
debtAfter,
|
||||
segmentBefore,
|
||||
segmentAfter,
|
||||
contributorBefore,
|
||||
contributorAfter,
|
||||
transactionId: confirmResult.transaction_id,
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import type Blockchain from '../../blockchain'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
|
||||
export async function processDeleteProject(
|
||||
blockchain: Blockchain,
|
||||
coopname: string,
|
||||
project_hash: string,
|
||||
) {
|
||||
// Состояние проекта до удаления
|
||||
const projectBefore = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'projects',
|
||||
1,
|
||||
project_hash,
|
||||
project_hash,
|
||||
3,
|
||||
'sha256',
|
||||
))[0]
|
||||
|
||||
const data: CapitalContract.Actions.DeleteProject.IDeleteProject = {
|
||||
coopname,
|
||||
project_hash,
|
||||
}
|
||||
|
||||
// Выполняем удаление проекта
|
||||
const txDelete = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.DeleteProject.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data,
|
||||
},
|
||||
],
|
||||
},
|
||||
{ blocksBehind: 3, expireSeconds: 30 },
|
||||
)
|
||||
getTotalRamUsage(txDelete)
|
||||
expect(txDelete.transaction_id).toBeDefined()
|
||||
|
||||
// Проверяем что проект удален
|
||||
const projectsAfter = await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'projects',
|
||||
1,
|
||||
project_hash,
|
||||
project_hash,
|
||||
3,
|
||||
'sha256',
|
||||
)
|
||||
|
||||
return {
|
||||
transactionId: txDelete.transaction_id,
|
||||
projectBefore,
|
||||
projectAfter: projectsAfter[0] || null, // null если удален
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { getCapitalWallet } from './getCapitalWallet'
|
||||
|
||||
export async function processFundProgram(
|
||||
blockchain: any,
|
||||
coopname: string,
|
||||
amount: string,
|
||||
memo: string = 'Тестовое финансирование программы',
|
||||
) {
|
||||
console.log(`\n🔹 Начало финансирования программы капитализации: сумма=${amount}, memo=${memo}`)
|
||||
|
||||
// Получение данных ДО финансирования
|
||||
const programWalletBefore = await getCapitalWallet(blockchain, coopname, coopname)
|
||||
|
||||
console.log('📊 Состояние программы ДО финансирования:')
|
||||
console.log('▶ Кошелек программы:', programWalletBefore)
|
||||
|
||||
// Финансирование программы капитализации
|
||||
const fundData: CapitalContract.Actions.FundProgram.IFundProgram = {
|
||||
coopname,
|
||||
amount,
|
||||
memo,
|
||||
}
|
||||
|
||||
const fundResult = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.FundProgram.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: fundData,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
|
||||
getTotalRamUsage(fundResult)
|
||||
expect(fundResult.transaction_id).toBeDefined()
|
||||
|
||||
// Получение данных ПОСЛЕ финансирования
|
||||
const programWalletAfter = await getCapitalWallet(blockchain, coopname, coopname)
|
||||
|
||||
console.log('📊 Состояние программы ПОСЛЕ финансирования:')
|
||||
console.log('▶ Кошелек программы:', programWalletAfter)
|
||||
|
||||
console.log(`\n✅ Финансирование программы капитализации завершено успешно!`)
|
||||
|
||||
return {
|
||||
transactionId: fundResult.transaction_id,
|
||||
transaction: fundResult,
|
||||
programWalletBefore,
|
||||
programWalletAfter,
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { getProjectWallet } from './getProjectWallet'
|
||||
|
||||
export async function processFundProject(
|
||||
blockchain: any,
|
||||
coopname: string,
|
||||
projectHash: string,
|
||||
amount: string,
|
||||
memo: string = 'Тестовое финансирование проекта',
|
||||
) {
|
||||
console.log(`\n🔹 Начало финансирования проекта: проект=${projectHash}, сумма=${amount}, memo=${memo}`)
|
||||
|
||||
// Получение данных ДО финансирования
|
||||
const projectWalletBefore = await getProjectWallet(blockchain, coopname, projectHash, coopname)
|
||||
|
||||
console.log('📊 Состояние проекта ДО финансирования:')
|
||||
console.log('▶ Кошелек проекта:', projectWalletBefore)
|
||||
|
||||
// Финансирование проекта
|
||||
const fundData: CapitalContract.Actions.FundProject.IFundProject = {
|
||||
coopname,
|
||||
project_hash: projectHash,
|
||||
amount,
|
||||
memo,
|
||||
}
|
||||
|
||||
const fundResult = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.FundProject.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: fundData,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
|
||||
getTotalRamUsage(fundResult)
|
||||
expect(fundResult.transaction_id).toBeDefined()
|
||||
|
||||
// Получение данных ПОСЛЕ финансирования
|
||||
const projectWalletAfter = await getProjectWallet(blockchain, coopname, projectHash, coopname)
|
||||
|
||||
console.log('📊 Состояние проекта ПОСЛЕ финансирования:')
|
||||
console.log('▶ Кошелек проекта:', projectWalletAfter)
|
||||
|
||||
console.log(`\n✅ Финансирование проекта завершено успешно!`)
|
||||
|
||||
return {
|
||||
transactionId: fundResult.transaction_id,
|
||||
transaction: fundResult,
|
||||
projectWalletBefore,
|
||||
projectWalletAfter,
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
|
||||
export async function processMasterApprove(
|
||||
blockchain: any,
|
||||
coopname: string,
|
||||
commitHash: string,
|
||||
master: string,
|
||||
) {
|
||||
const data: CapitalContract.Actions.CommitApprove.ICommitApprove = {
|
||||
coopname,
|
||||
master,
|
||||
commit_hash: commitHash,
|
||||
}
|
||||
|
||||
const result = await blockchain.transactWithLogs([
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.CommitApprove.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data,
|
||||
},
|
||||
])
|
||||
|
||||
getTotalRamUsage(result)
|
||||
expect(result.transaction_id).toBeDefined()
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract, SovietContract } from 'cooptypes'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { generateRandomSHA256 } from '../../utils/randomHash'
|
||||
import { setDocumentSignatures } from '../../utils/setDocumentSignatures'
|
||||
import { processLastDecision } from '../soviet/processLastDecision'
|
||||
import { fakeDocument } from '../shared/fakeDocument'
|
||||
import type Blockchain from '../../blockchain'
|
||||
import { getSegment } from './getSegment'
|
||||
import { processApprove } from './processApprove'
|
||||
|
||||
/**
|
||||
* Функция для внесения результата в кооператив
|
||||
* @param blockchain экземпляр блокчейна
|
||||
* @param coopname название кооператива
|
||||
* @param username имя пользователя
|
||||
* @param projectHash хеш проекта
|
||||
* @param contributionAmount сумма взноса
|
||||
* @param debtAmount сумма долга к погашению
|
||||
* @param debtHashes вектор хэшей долгов для погашения
|
||||
*/
|
||||
export async function processPushResult(
|
||||
blockchain: Blockchain,
|
||||
coopname: string,
|
||||
username: string,
|
||||
projectHash: string,
|
||||
contributionAmount: string,
|
||||
debtAmount: string,
|
||||
debtHashes: string[] = [],
|
||||
) {
|
||||
const resultHash = generateRandomSHA256()
|
||||
console.log(`\n🔹 Начало процесса внесения результата: ${resultHash}`)
|
||||
|
||||
// Получаем сегмент до внесения результата
|
||||
const segmentBefore = await getSegment(blockchain, coopname, projectHash, username)
|
||||
console.log('📊 Сегмент до внесения результата:', segmentBefore)
|
||||
|
||||
// Устанавливаем подписи документа
|
||||
const documentWithSignatures = setDocumentSignatures([username])
|
||||
|
||||
// 1. Вносим результат
|
||||
const pushResultData: CapitalContract.Actions.PushResult.IPushResult = {
|
||||
coopname,
|
||||
username,
|
||||
project_hash: projectHash,
|
||||
result_hash: resultHash,
|
||||
contribution_amount: contributionAmount,
|
||||
debt_amount: debtAmount,
|
||||
statement: documentWithSignatures,
|
||||
debt_hashes: debtHashes,
|
||||
}
|
||||
|
||||
console.log(`\n🚀 Отправка транзакции PushResult для ${username} на сумму ${contributionAmount}`)
|
||||
const pushResult = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.PushResult.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: pushResultData,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
|
||||
getTotalRamUsage(pushResult)
|
||||
expect(pushResult.transaction_id).toBeDefined()
|
||||
console.log('✅ Результат внесен:', resultHash)
|
||||
|
||||
// Проверяем что результат создался
|
||||
const results = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'results',
|
||||
1,
|
||||
resultHash,
|
||||
resultHash,
|
||||
3,
|
||||
'sha256',
|
||||
))
|
||||
const createdResult = results[0]
|
||||
console.log('🔍 Результат в блокчейне:', createdResult)
|
||||
expect(createdResult).toBeDefined()
|
||||
expect(createdResult.status).toBe('created')
|
||||
|
||||
// 2. Одобряем результат через processApprove (soviet контракт)
|
||||
console.log(`\n✅ Одобрение результата ${resultHash} через soviet`)
|
||||
await processApprove(blockchain, coopname, resultHash)
|
||||
console.log('✅ Результат одобрен советом')
|
||||
|
||||
// 3. Процессим решение совета
|
||||
await processLastDecision(blockchain, coopname)
|
||||
console.log('✅ Решение совета принято')
|
||||
|
||||
console.log('🔍 Подписываем акт №1 вкладчиком', documentWithSignatures)
|
||||
// 4. Подписываем акт №1 вкладчиком
|
||||
const signAct1Data: CapitalContract.Actions.SignAct1.ISignAct1 = {
|
||||
coopname,
|
||||
username,
|
||||
result_hash: resultHash,
|
||||
act: documentWithSignatures,
|
||||
}
|
||||
|
||||
console.log(`\n✅ Подписание акта №1 вкладчиком ${username}`)
|
||||
const signAct1Result = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.SignAct1.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: signAct1Data,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
|
||||
getTotalRamUsage(signAct1Result)
|
||||
expect(signAct1Result.transaction_id).toBeDefined()
|
||||
console.log('✅ Акт №1 подписан вкладчиком')
|
||||
|
||||
// Создаем документ с подписями вкладчика и председателя
|
||||
const documentWithChairmanSignature = setDocumentSignatures([username, 'ant'])
|
||||
|
||||
// 5. Подписываем акт №2 председателем
|
||||
const signAct2Data: CapitalContract.Actions.SignAct2.ISignAct2 = {
|
||||
coopname,
|
||||
result_hash: resultHash,
|
||||
act: documentWithChairmanSignature,
|
||||
chairman: 'ant',
|
||||
}
|
||||
|
||||
console.log(`\n✅ Подписание акта №2 председателем`, documentWithChairmanSignature)
|
||||
const signAct2Result = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.SignAct2.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: signAct2Data,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
|
||||
getTotalRamUsage(signAct2Result)
|
||||
expect(signAct2Result.transaction_id).toBeDefined()
|
||||
console.log('✅ Акт №2 подписан председателем')
|
||||
|
||||
// Получаем финальные состояния
|
||||
const segmentAfter = await getSegment(blockchain, coopname, projectHash, username)
|
||||
|
||||
console.log('\n📊 Результаты после внесения результата:')
|
||||
console.log('▶ Сегмент до:', segmentBefore)
|
||||
console.log('▶ Сегмент после:', segmentAfter)
|
||||
|
||||
console.log(`\n✅ Процесс внесения результата на ${contributionAmount} завершен успешно!`)
|
||||
|
||||
return {
|
||||
resultHash,
|
||||
result: createdResult,
|
||||
segmentBefore,
|
||||
segmentAfter,
|
||||
transactionId: signAct2Result.transaction_id,
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { getCapitalWallet } from './getCapitalWallet'
|
||||
|
||||
export async function processRefreshProg(
|
||||
blockchain: any,
|
||||
coopname: string,
|
||||
username: string,
|
||||
) {
|
||||
console.log(`\n🔹 Начало обновления CRPS пайщика в программе капитализации: пользователь=${username}`)
|
||||
|
||||
// Получение данных ДО обновления
|
||||
const programWalletBefore = await getCapitalWallet(blockchain, coopname, username)
|
||||
|
||||
console.log('📊 Состояние пайщика ДО обновления CRPS:')
|
||||
console.log('▶ Кошелек пайщика:', programWalletBefore)
|
||||
|
||||
// Обновление CRPS пайщика в программе капитализации
|
||||
const refreshData: CapitalContract.Actions.RefreshProgram.IRefreshProgram = {
|
||||
coopname,
|
||||
username,
|
||||
}
|
||||
|
||||
const refreshResult = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.RefreshProgram.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: refreshData,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
|
||||
getTotalRamUsage(refreshResult)
|
||||
expect(refreshResult.transaction_id).toBeDefined()
|
||||
|
||||
// Получение данных ПОСЛЕ обновления
|
||||
const programWalletAfter = await getCapitalWallet(blockchain, coopname, username)
|
||||
|
||||
console.log('📊 Состояние пайщика ПОСЛЕ обновления CRPS:')
|
||||
console.log('▶ Кошелек пайщика:', programWalletAfter)
|
||||
|
||||
console.log(`\n✅ Обновление CRPS пайщика в программе капитализации завершено успешно!`)
|
||||
|
||||
return {
|
||||
transactionId: refreshResult.transaction_id,
|
||||
transaction: refreshResult,
|
||||
programWalletBefore,
|
||||
programWalletAfter,
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { getProjectWallet } from './getProjectWallet'
|
||||
|
||||
export async function processRefreshProj(
|
||||
blockchain: any,
|
||||
coopname: string,
|
||||
projectHash: string,
|
||||
username: string,
|
||||
) {
|
||||
console.log(`\n🔹 Начало обновления CRPS пайщика в проекте: проект=${projectHash}, пользователь=${username}`)
|
||||
|
||||
// Получение данных ДО обновления
|
||||
const projectWalletBefore = await getProjectWallet(blockchain, coopname, projectHash, username)
|
||||
|
||||
console.log('📊 Состояние кошелька проекта ДО обновления CRPS:')
|
||||
console.log('▶ Кошелек проекта:', projectWalletBefore)
|
||||
|
||||
// Обновление CRPS пайщика в проекте
|
||||
const refreshData: CapitalContract.Actions.RefreshProject.IRefreshProject = {
|
||||
coopname,
|
||||
project_hash: projectHash,
|
||||
username,
|
||||
}
|
||||
|
||||
const refreshResult = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.RefreshProject.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: refreshData,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
|
||||
getTotalRamUsage(refreshResult)
|
||||
expect(refreshResult.transaction_id).toBeDefined()
|
||||
|
||||
// Получение данных ПОСЛЕ обновления
|
||||
const projectWalletAfter = await getProjectWallet(blockchain, coopname, projectHash, username)
|
||||
|
||||
console.log('📊 Состояние кошелька проекта ПОСЛЕ обновления CRPS:')
|
||||
console.log('▶ Кошелек проекта:', projectWalletAfter)
|
||||
|
||||
console.log(`\n✅ Обновление CRPS пайщика в проекте завершено успешно!`)
|
||||
|
||||
return {
|
||||
transactionId: refreshResult.transaction_id,
|
||||
transaction: refreshResult,
|
||||
projectWalletBefore,
|
||||
projectWalletAfter,
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import type Blockchain from '../../blockchain'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
|
||||
export async function processStartVoting(
|
||||
blockchain: Blockchain,
|
||||
data: CapitalContract.Actions.StartVoting.IStartVoting,
|
||||
) {
|
||||
const { coopname, project_hash } = data
|
||||
|
||||
// Состояние до: данные проекта
|
||||
const projectBefore = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'projects',
|
||||
1,
|
||||
project_hash,
|
||||
project_hash,
|
||||
3,
|
||||
'sha256',
|
||||
))[0]
|
||||
|
||||
// 1) Запускаем голосование по проекту
|
||||
const txStart = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.StartVoting.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data,
|
||||
},
|
||||
],
|
||||
},
|
||||
{ blocksBehind: 3, expireSeconds: 30 },
|
||||
)
|
||||
getTotalRamUsage(txStart)
|
||||
expect(txStart.transaction_id).toBeDefined()
|
||||
|
||||
// Состояние после: данные проекта
|
||||
const projectAfter = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'projects',
|
||||
1,
|
||||
project_hash,
|
||||
project_hash,
|
||||
3,
|
||||
'sha256',
|
||||
))[0]
|
||||
|
||||
return {
|
||||
projectHash: project_hash,
|
||||
txStartId: txStart.transaction_id,
|
||||
projectBefore,
|
||||
projectAfter,
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import type Blockchain from '../../blockchain'
|
||||
|
||||
export async function refreshSegment(
|
||||
blockchain: Blockchain,
|
||||
coopname: string,
|
||||
projectHash: string,
|
||||
username: string,
|
||||
) {
|
||||
console.log(`\n🔄 Обновляем сегмент CRPS для ${username} в проекте ${projectHash}`)
|
||||
|
||||
// Получение данных сегмента перед обновлением
|
||||
const prevSegment = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
CapitalContract.Tables.Segments.tableName,
|
||||
1000,
|
||||
)).filter((s: any) => s.project_hash === projectHash && s.username === username)[0]
|
||||
|
||||
// console.log('📊 Сегмент до обновления:', prevSegment)
|
||||
|
||||
// Обновление сегмента через rfrshsegment
|
||||
const refreshSegmentData: CapitalContract.Actions.RefreshSegment.IRefreshSegment = {
|
||||
coopname,
|
||||
project_hash: projectHash,
|
||||
username,
|
||||
}
|
||||
|
||||
const refreshResult = await blockchain.transactWithLogs(
|
||||
[
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.RefreshSegment.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data: refreshSegmentData,
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
getTotalRamUsage(refreshResult)
|
||||
expect(refreshResult.transaction_id).toBeDefined()
|
||||
|
||||
// Получение обновленного сегмента
|
||||
const updatedSegment = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
CapitalContract.Tables.Segments.tableName,
|
||||
1000,
|
||||
)).filter((s: any) => s.project_hash === projectHash && s.username === username)[0]
|
||||
|
||||
// console.log('📊 Сегмент после обновления:', updatedSegment)
|
||||
console.log(`✅ Сегмент для ${username} обновлен успешно!`)
|
||||
|
||||
return {
|
||||
transactionId: refreshResult.transaction_id,
|
||||
prevSegment,
|
||||
updatedSegment,
|
||||
}
|
||||
}
|
||||
@@ -1,64 +1,3 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import { fakeDocument } from '../shared/fakeDocument'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { processApprove } from './processApprove'
|
||||
export function registerContributor() {
|
||||
|
||||
export async function registerContributor(
|
||||
blockchain: any,
|
||||
coopname: string,
|
||||
username: string,
|
||||
contributorHash: string,
|
||||
ratePerHour: string,
|
||||
) {
|
||||
const contract = fakeDocument
|
||||
contract.signatures[0].signer = username
|
||||
const data: CapitalContract.Actions.RegisterContributor.IRegisterContributor = {
|
||||
coopname,
|
||||
username,
|
||||
contributor_hash: contributorHash,
|
||||
rate_per_hour: ratePerHour,
|
||||
is_external_contract: false,
|
||||
contract,
|
||||
}
|
||||
|
||||
const result = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.RegisterContributor.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
getTotalRamUsage(result)
|
||||
expect(result.transaction_id).toBeDefined()
|
||||
|
||||
// Аппрув председателя (через совет) по хэшу договора УХД
|
||||
await processApprove(blockchain, coopname, contributorHash)
|
||||
|
||||
// Проверяем, что контрибьютор активирован и договор сохранён
|
||||
const contributor = (
|
||||
await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'contributors',
|
||||
1,
|
||||
username,
|
||||
username,
|
||||
2,
|
||||
'i64',
|
||||
)
|
||||
)[0]
|
||||
|
||||
expect(contributor).toBeDefined()
|
||||
expect(contributor.status).toBe('active')
|
||||
expect(contributor.contract).toBeDefined()
|
||||
}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import { fakeDocument } from '../shared/fakeDocument'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { processApprove } from './processApprove'
|
||||
|
||||
export async function signAppendix(
|
||||
blockchain: any,
|
||||
coopname: string,
|
||||
username: string,
|
||||
projectHash: string,
|
||||
appendixHash: string,
|
||||
) {
|
||||
// Подписание приложения пайщиком (через кооператив, как и в контракте)
|
||||
const data: CapitalContract.Actions.GetClearance.IGetClearance = {
|
||||
coopname,
|
||||
username,
|
||||
project_hash: projectHash,
|
||||
appendix_hash: appendixHash,
|
||||
document: fakeDocument,
|
||||
}
|
||||
|
||||
const result = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.GetClearance.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
},
|
||||
)
|
||||
|
||||
getTotalRamUsage(result)
|
||||
expect(result.transaction_id).toBeDefined()
|
||||
|
||||
// Аппрув председателя (через совет) по хэшу приложения
|
||||
await processApprove(blockchain, coopname, appendixHash)
|
||||
|
||||
// Проверяем, что приложение применилось к контрибьютору, а запись удалена
|
||||
const contributor = (
|
||||
await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'contributors',
|
||||
1,
|
||||
username,
|
||||
username,
|
||||
2,
|
||||
'i64',
|
||||
)
|
||||
)[0]
|
||||
|
||||
expect(contributor).toBeDefined()
|
||||
expect(contributor.appendixes).toContain(projectHash)
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
import { expect } from 'vitest'
|
||||
import { CapitalContract } from 'cooptypes'
|
||||
import type Blockchain from '../../blockchain'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
|
||||
/**
|
||||
* Интерфейс для распределения голосов
|
||||
*/
|
||||
export interface VoteInput {
|
||||
recipient: string
|
||||
amount: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает равномерное распределение голосующей суммы между участниками (кроме самого голосующего)
|
||||
* @param voters список всех участников голосования
|
||||
* @param voter участник который голосует (исключается из распределения)
|
||||
* @param votingAmount общая голосующая сумма для распределения
|
||||
* @returns массив распределения голосов
|
||||
*/
|
||||
export function createVoteDistribution(
|
||||
voters: string[],
|
||||
voter: string,
|
||||
votingAmount: string,
|
||||
): VoteInput[] {
|
||||
const otherVoters = voters.filter(v => v !== voter)
|
||||
const votingAmountFloat = parseFloat(votingAmount.split(' ')[0])
|
||||
const currency = votingAmount.split(' ')[1]
|
||||
|
||||
// Равномерно распределяем сумму между остальными участниками
|
||||
const amountPerVoter = votingAmountFloat / otherVoters.length
|
||||
|
||||
return otherVoters.map(recipient => ({
|
||||
recipient,
|
||||
amount: `${amountPerVoter.toFixed(4)} ${currency}`,
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* Функция для голосования по методу Водянова
|
||||
* @param blockchain экземпляр блокчейна
|
||||
* @param coopname имя кооператива
|
||||
* @param voter участник который голосует
|
||||
* @param projectHash хэш проекта
|
||||
* @param voteDistribution массив голосов для распределения
|
||||
*/
|
||||
export async function submitVote(
|
||||
blockchain: Blockchain,
|
||||
coopname: string,
|
||||
voter: string,
|
||||
projectHash: string,
|
||||
voteDistribution: VoteInput[],
|
||||
) {
|
||||
console.log(`\n🗳️ Начало голосования участника ${voter}`)
|
||||
|
||||
// Состояние до: данные проекта
|
||||
const projectBefore = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'projects',
|
||||
1,
|
||||
projectHash,
|
||||
projectHash,
|
||||
3,
|
||||
'sha256',
|
||||
))[0]
|
||||
|
||||
// Получаем все голоса по проекту до голосования
|
||||
const votesBefore = await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'votes',
|
||||
100,
|
||||
projectHash,
|
||||
projectHash,
|
||||
2,
|
||||
'sha256',
|
||||
)
|
||||
|
||||
// Получаем голоса конкретного участника до голосования
|
||||
const voterVotesBefore = votesBefore.filter((vote: any) =>
|
||||
vote.project_hash === projectHash && vote.voter === voter,
|
||||
)
|
||||
|
||||
console.log('📊 Состояние до голосования:')
|
||||
console.log('▶ Проект votes_received:', projectBefore.voting.votes_received)
|
||||
console.log('▶ Всего голосов в таблице:', votesBefore.filter((v: any) => v.project_hash === projectHash).length)
|
||||
console.log('▶ Голосов от участника:', voterVotesBefore.length)
|
||||
|
||||
// Формируем данные для транзакции
|
||||
const votes = voteDistribution.map(v => ({
|
||||
recipient: v.recipient,
|
||||
amount: v.amount,
|
||||
}))
|
||||
|
||||
const data: any = {
|
||||
coopname,
|
||||
voter,
|
||||
project_hash: projectHash,
|
||||
votes,
|
||||
}
|
||||
|
||||
console.log('📝 Данные голосования:', JSON.stringify(data, null, 2))
|
||||
|
||||
// Отправляем транзакцию голосования
|
||||
const txResult = await blockchain.api.transact(
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
account: CapitalContract.contractName.production,
|
||||
name: CapitalContract.Actions.SubmitVote.actionName,
|
||||
authorization: [{ actor: coopname, permission: 'active' }],
|
||||
data,
|
||||
},
|
||||
],
|
||||
},
|
||||
{ blocksBehind: 3, expireSeconds: 30 },
|
||||
)
|
||||
getTotalRamUsage(txResult)
|
||||
expect(txResult.transaction_id).toBeDefined()
|
||||
|
||||
// Состояние после: данные проекта
|
||||
const projectAfter = (await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'projects',
|
||||
1,
|
||||
projectHash,
|
||||
projectHash,
|
||||
3,
|
||||
'sha256',
|
||||
))[0]
|
||||
|
||||
// Получаем все голоса по проекту после голосования
|
||||
const votesAfter = await blockchain.getTableRows(
|
||||
CapitalContract.contractName.production,
|
||||
coopname,
|
||||
'votes',
|
||||
100,
|
||||
projectHash,
|
||||
projectHash,
|
||||
2,
|
||||
'sha256',
|
||||
)
|
||||
|
||||
// Получаем голоса конкретного участника после голосования
|
||||
const voterVotesAfter = votesAfter.filter((vote: any) =>
|
||||
vote.project_hash === projectHash && vote.voter === voter,
|
||||
)
|
||||
|
||||
console.log('📊 Состояние после голосования:')
|
||||
console.log('▶ Проект votes_received:', projectAfter.voting.votes_received)
|
||||
console.log('▶ Всего голосов в таблице:', votesAfter.filter((v: any) => v.project_hash === projectHash).length)
|
||||
console.log('▶ Голосов от участника:', voterVotesAfter.length)
|
||||
|
||||
return {
|
||||
txId: txResult.transaction_id,
|
||||
projectBefore,
|
||||
projectAfter,
|
||||
votesBefore: votesBefore.filter((v: any) => v.project_hash === projectHash),
|
||||
votesAfter: votesAfter.filter((v: any) => v.project_hash === projectHash),
|
||||
voterVotesBefore,
|
||||
voterVotesAfter,
|
||||
voteInput: voteDistribution,
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
// Комбинирование индекса (project_hash + username) как в C++ combine_checksum_ids
|
||||
// Возвращает десятичную строку uint128 для использования с key_type 'i128'
|
||||
|
||||
function charToSymbol(c: string): number {
|
||||
if (c === '.') return 0
|
||||
if (c >= '1' && c <= '5') return c.charCodeAt(0) - '1'.charCodeAt(0) + 1
|
||||
if (c >= 'a' && c <= 'z') return c.charCodeAt(0) - 'a'.charCodeAt(0) + 6
|
||||
return 0
|
||||
}
|
||||
|
||||
function nameToUint64(name: string): bigint {
|
||||
let value = 0n
|
||||
let i = 0
|
||||
for (; i < 12 && i < name.length; i++) {
|
||||
value <<= 5n
|
||||
value |= BigInt(charToSymbol(name[i]))
|
||||
}
|
||||
value <<= 4n + 5n * BigInt(12 - i)
|
||||
if (i < name.length) {
|
||||
value |= BigInt(charToSymbol(name[i])) & 0x0fn
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
function hexToBytes(hex: string): Uint8Array {
|
||||
const clean = hex.startsWith('0x') ? hex.slice(2) : hex
|
||||
if (clean.length !== 64) throw new Error('Invalid sha256 hex')
|
||||
const out = new Uint8Array(32)
|
||||
for (let i = 0; i < 32; i++) {
|
||||
out[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
function readBigUInt64LE(bytes: Uint8Array, offset = 0): bigint {
|
||||
let result = 0n
|
||||
for (let i = 0; i < 8; i++) {
|
||||
result |= BigInt(bytes[offset + i]) << (8n * BigInt(i))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export function makeCombinedChecksum256NameIndexKey(projectHashHex: string, username: string): string {
|
||||
const bytes = hexToBytes(projectHashHex)
|
||||
const truncatedHashLE = readBigUInt64LE(bytes, 0)
|
||||
const nameValue = nameToUint64(username)
|
||||
const key128 = (truncatedHashLE << 64n) | nameValue
|
||||
return key128.toString()
|
||||
}
|
||||
|
||||
export function makeCombinedChecksum256Uint64IndexKey(projectHashHex: string, id: number): string {
|
||||
const bytes = hexToBytes(projectHashHex)
|
||||
const truncatedHashLE = readBigUInt64LE(bytes, 0)
|
||||
const idValue = BigInt(id)
|
||||
const key128 = (truncatedHashLE << 64n) | idValue
|
||||
return key128.toString()
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
interface ActionTrace {
|
||||
act?: {
|
||||
account: string
|
||||
name: string
|
||||
authorization?: { actor: string }[]
|
||||
}
|
||||
console?: string
|
||||
inline_traces?: ActionTrace[]
|
||||
}
|
||||
|
||||
interface TransactionResult {
|
||||
processed?: {
|
||||
action_traces?: ActionTrace[]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Извлекает и печатает все консоль логи из результата транзакции
|
||||
* @param transaction - результат транзакции blockchain.api.transact
|
||||
*/
|
||||
export function consoleIt(transaction: TransactionResult): void {
|
||||
if (!transaction?.processed?.action_traces) {
|
||||
console.log('❌ Нет данных о транзакции или action traces')
|
||||
return
|
||||
}
|
||||
|
||||
console.log('\n📋 КОНСОЛЬ ЛОГИ ТРАНЗАКЦИИ:')
|
||||
console.log('='.repeat(50))
|
||||
|
||||
let hasConsoleLogs = false
|
||||
|
||||
function processActionTrace(trace: ActionTrace, depth = 0): void {
|
||||
const indent = ' '.repeat(depth)
|
||||
|
||||
// Получаем информацию о действии
|
||||
const account = trace.act?.account || 'unknown'
|
||||
const action = trace.act?.name || 'unknown'
|
||||
const actor = trace.act?.authorization?.[0]?.actor || 'unknown'
|
||||
|
||||
// Печатаем заголовок действия
|
||||
console.log(`${indent}🔧 ${account}:${action} (actor: ${actor})`)
|
||||
|
||||
// Если есть консоль логи, печатаем их
|
||||
if (trace.console) {
|
||||
hasConsoleLogs = true
|
||||
const logs = trace.console.split('\n').filter(log => log.trim())
|
||||
|
||||
if (logs.length > 0) {
|
||||
console.log(`${indent}📝 Console output:`)
|
||||
logs.forEach((log) => {
|
||||
console.log(`${indent} ${log}`)
|
||||
})
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log(`${indent} (нет консоль логов)`)
|
||||
}
|
||||
|
||||
// Обрабатываем inline traces рекурсивно
|
||||
if (trace.inline_traces?.length) {
|
||||
console.log(`${indent} ↳ Inline actions:`)
|
||||
trace.inline_traces.forEach((inlineTrace) => {
|
||||
processActionTrace(inlineTrace, depth + 1)
|
||||
})
|
||||
}
|
||||
|
||||
if (depth === 0) {
|
||||
console.log('') // Пустая строка между действиями верхнего уровня
|
||||
}
|
||||
}
|
||||
|
||||
// Обрабатываем все action traces
|
||||
transaction.processed.action_traces.forEach((trace, index) => {
|
||||
console.log(`\n📍 Action ${index + 1}:`)
|
||||
processActionTrace(trace, 0)
|
||||
})
|
||||
|
||||
if (!hasConsoleLogs) {
|
||||
console.log('ℹ️ В транзакции не найдено консоль логов')
|
||||
}
|
||||
|
||||
console.log('='.repeat(50))
|
||||
console.log('🏁 Конец логов транзакции\n')
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
export const fakeDocument = {
|
||||
version: '1.0.0',
|
||||
hash: '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777',
|
||||
doc_hash: '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777',
|
||||
meta_hash: '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777',
|
||||
meta: '{}',
|
||||
signatures: [{
|
||||
id: 1,
|
||||
signed_hash: '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777',
|
||||
signer: 'cooperative1',
|
||||
public_key: 'EOS5JhMfxbsNebajHcTEK8yC9uNN9Dit9hEmzE8ri8yMhhzxrLg3J',
|
||||
signature: 'SIG_K1_KmKWPBC8dZGGDGhbKEoZEzPr3h5crRrR2uLdGRF5DJbeibY1MY1bZ9sPwHsgmPfiGFv9psfoCVsXFh9TekcLuvaeuxRKA8',
|
||||
signed_at: '2025-05-14T12:22:26',
|
||||
meta: '{}',
|
||||
}],
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
export const fakeVote = {
|
||||
version: '1.0.0',
|
||||
coopname: 'voskhod',
|
||||
username: 'ant',
|
||||
decision_id: '1',
|
||||
signed_at: '2025-05-14T12:22:26.000',
|
||||
signed_hash: '157192B276DA23CC84AB078FC8755C051C5F0430BF4802E55718221E6B76C777',
|
||||
signature: 'SIG_K1_KmKWPBC8dZGGDGhbKEoZEzPr3h5crRrR2uLdGRF5DJbeibY1MY1bZ9sPwHsgmPfiGFv9psfoCVsXFh9TekcLuvaeuxRKA8',
|
||||
public_key: 'PUB_K1_5JhMfxbsNebajHcTEK8yC9uNN9Dit9hEmzE8ri8yMhhzzEtUA4',
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* Форматирует дату в строку формата 'YYYY-MM-DDTHH:mm:ss'
|
||||
* @param date - Дата для форматирования
|
||||
* @returns Строка в формате 'YYYY-MM-DDTHH:mm:ss'
|
||||
*/
|
||||
export function formatDateToISOString(date: Date): string {
|
||||
return date.toISOString().slice(0, 19);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
export * from './combinedKeys'
|
||||
export * from './consoleIt'
|
||||
export * from './fakeDocument'
|
||||
export * from './fakeVote'
|
||||
export * from './formatDateToISOString'
|
||||
@@ -1,12 +1,14 @@
|
||||
import { SovietContract } from 'cooptypes'
|
||||
import { expect } from 'vitest'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { fakeDocument } from '../shared/fakeDocument'
|
||||
import type Blockchain from '../../blockchain'
|
||||
import { fakeVote } from '../shared/fakeVote'
|
||||
import { fakeDocument } from './fakeDocument'
|
||||
|
||||
export async function processDecision(blockchain: Blockchain, decisionId: number) {
|
||||
const voteData: SovietContract.Actions.Decisions.VoteFor.IVoteForDecision = fakeVote
|
||||
voteData.decision_id = decisionId
|
||||
export async function processDecision(blockchain: any, decisionId: number) {
|
||||
const voteData: SovietContract.Actions.Decisions.VoteFor.IVoteForDecision = {
|
||||
coopname: 'voskhod',
|
||||
member: 'ant',
|
||||
decision_id: decisionId,
|
||||
}
|
||||
|
||||
const authData: SovietContract.Actions.Decisions.Authorize.IAuthorize = {
|
||||
coopname: 'voskhod',
|
||||
@@ -51,4 +53,5 @@ export async function processDecision(blockchain: Blockchain, decisionId: number
|
||||
)
|
||||
|
||||
getTotalRamUsage(result)
|
||||
expect(result.transaction_id).toBeDefined()
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import { SovietContract } from 'cooptypes'
|
||||
import type Blockchain from '../../blockchain'
|
||||
import { processDecision } from './processDecision'
|
||||
|
||||
export async function processLastDecision(blockchain: Blockchain, coopname: string) {
|
||||
// Получаем последнее решение для голосования
|
||||
const decisions = await blockchain.getTableRows(
|
||||
SovietContract.contractName.production,
|
||||
coopname,
|
||||
SovietContract.Tables.Decisions.tableName,
|
||||
1,
|
||||
)
|
||||
const lastDecision = decisions[0]
|
||||
|
||||
// 3. Голосуем за решение совета
|
||||
await processDecision(blockchain, lastDecision.id)
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import { GatewayContract, WalletContract } from 'cooptypes'
|
||||
import { expect } from 'vitest'
|
||||
import { getTotalRamUsage } from '../../utils/getTotalRamUsage'
|
||||
import { generateRandomSHA256 } from '../../utils/randomHash'
|
||||
import { compareTokenAmounts, getCirculationAccount, getCoopProgramWallet, getDeposit, getUserProgramWallet } from './walletUtils'
|
||||
import { compareTokenAmounts, getCoopProgramWallet, getCoopWallet, getDeposit, getUserProgramWallet } from './walletUtils'
|
||||
|
||||
export async function depositToWallet(blockchain: any, coopname: string, username: string, amount: number) {
|
||||
const depositId = randomInt(100000)
|
||||
@@ -48,7 +48,8 @@ export async function depositToWallet(blockchain: any, coopname: string, usernam
|
||||
const prevUserWallet = await getUserProgramWallet(blockchain, coopname, username, 1)
|
||||
const prevUserWalletAvailable = prevUserWallet?.available || '0.0000 RUB'
|
||||
|
||||
const prevCoopWalletAvailable = (await getCirculationAccount(blockchain, coopname)).available
|
||||
const prevCoopWallet = await getCoopWallet(blockchain, coopname)
|
||||
const prevCoopWalletAvailable = prevCoopWallet?.circulating_account?.available || '0.0000 RUB'
|
||||
|
||||
const data2: GatewayContract.Actions.CompleteIncome.ICompleteIncome = {
|
||||
coopname,
|
||||
@@ -84,7 +85,7 @@ export async function depositToWallet(blockchain: any, coopname: string, usernam
|
||||
|
||||
// Проверяем изменение балансов
|
||||
compareTokenAmounts(prevUserWalletAvailable, userWallet.available, amount)
|
||||
compareTokenAmounts(prevCoopWalletAvailable, (await getCirculationAccount(blockchain, coopname)).available, amount)
|
||||
compareTokenAmounts(prevCoopWalletAvailable, (await getCoopWallet(blockchain, coopname)).circulating_account.available, amount)
|
||||
|
||||
return { depositId, program, userWallet }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { FundContract, LedgerContract, SovietContract, WalletContract } from 'cooptypes'
|
||||
import { FundContract, SovietContract, WalletContract } from 'cooptypes'
|
||||
import { expect } from 'vitest'
|
||||
import type Blockchain from '../../blockchain'
|
||||
import { circulationAccountId } from '../capital/consts'
|
||||
|
||||
export async function getUserProgramWallet(blockchain: any, coopname: string, username: string, program_id: number) {
|
||||
const wallets = await blockchain.getTableRows(
|
||||
@@ -17,11 +16,6 @@ export async function getUserProgramWallet(blockchain: any, coopname: string, us
|
||||
return wallets.find((el: any) => el.program_id === program_id)
|
||||
}
|
||||
|
||||
export async function getUserProgramWalletAmount(blockchain: any, coopname: string, username: string, program_id: number) {
|
||||
const wallet = await getUserProgramWallet(blockchain, coopname, username, program_id)
|
||||
return wallet ? `${(parseFloat(wallet.available) + parseFloat(wallet.blocked)).toFixed(4)} RUB` : '0.0000 RUB'
|
||||
}
|
||||
|
||||
export async function getCoopProgramWallet(blockchain: any, coopname: string, program_id: number) {
|
||||
const program = await blockchain.getTableRows(
|
||||
SovietContract.contractName.production,
|
||||
@@ -35,25 +29,6 @@ export async function getCoopProgramWallet(blockchain: any, coopname: string, pr
|
||||
return program[0]
|
||||
}
|
||||
|
||||
export async function getLedgerAccounts(blockchain: Blockchain, coopname: string) {
|
||||
return (await blockchain.getTableRows(
|
||||
LedgerContract.contractName.production,
|
||||
coopname,
|
||||
LedgerContract.Tables.Laccount.tableName,
|
||||
100,
|
||||
))
|
||||
}
|
||||
|
||||
export async function getLedgerAccountById(blockchain: Blockchain, coopname: string, accountId: number) {
|
||||
const accounts = await getLedgerAccounts(blockchain, coopname)
|
||||
return accounts.find((account: any) => account.id === accountId)
|
||||
}
|
||||
|
||||
export async function getCirculationAccount(blockchain: Blockchain, coopname: string) {
|
||||
const accounts = await getLedgerAccounts(blockchain, coopname)
|
||||
return accounts.find((account: any) => account.id === circulationAccountId) || { available: '0.0000 RUB' }
|
||||
}
|
||||
|
||||
export async function getCoopWallet(blockchain: any, coopname: string) {
|
||||
return (await blockchain.getTableRows(
|
||||
FundContract.contractName.production,
|
||||
@@ -81,5 +56,5 @@ export function compareTokenAmounts(prevAmount: string, currentAmount: string, e
|
||||
const current = parseFloat(currentAmount.split(' ')[0])
|
||||
const expected = prev + expectedIncrease
|
||||
|
||||
expect(current).toBeCloseTo(expected, 4)
|
||||
expect(current).toBe(expected)
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import { fakeDocument } from '../tests/shared/fakeDocument'
|
||||
|
||||
/**
|
||||
* Устанавливает подписи документа на основе массива имен аккаунтов
|
||||
* @param usernames массив имен аккаунтов для подписи документа
|
||||
* @returns копия fakeDocument с установленными подписями
|
||||
*/
|
||||
export function setDocumentSignatures(usernames: string[]) {
|
||||
const document = JSON.parse(JSON.stringify(fakeDocument))
|
||||
|
||||
// Очищаем существующие подписи
|
||||
document.signatures = []
|
||||
|
||||
// Создаем подписи для каждого пользователя
|
||||
usernames.forEach((username, index) => {
|
||||
const signature = {
|
||||
id: index + 1,
|
||||
signed_hash: '157192b276da23cc84ab078fc8755c051c5f0430bf4802e55718221e6b76c777',
|
||||
signer: username,
|
||||
public_key: 'EOS5JhMfxbsNebajHcTEK8yC9uNN9Dit9hEmzE8ri8yMhhzxrLg3J',
|
||||
signature: 'SIG_K1_KmKWPBC8dZGGDGhbKEoZEzPr3h5crRrR2uLdGRF5DJbeibY1MY1bZ9sPwHsgmPfiGFv9psfoCVsXFh9TekcLuvaeuxRKA8',
|
||||
signed_at: '2025-05-14T12:22:26',
|
||||
meta: '{}',
|
||||
}
|
||||
document.signatures.push(signature)
|
||||
})
|
||||
|
||||
return document
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@coopenomics/cleos",
|
||||
"version": "2025.9.1",
|
||||
"version": "2025.7.2",
|
||||
"private": true,
|
||||
"description": "",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
./cleos.sh system newaccount eosio test "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" --stake-net "0.0001 AXON" --stake-cpu "0.0001 AXON" --buy-ram "0.0001 AXON"
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
./cleos.sh push action eosio powerup '[eosio, test, 1, "150.0000 AXON", true]' -p eosio
|
||||
Vendored
BIN
Binary file not shown.
@@ -1,10 +0,0 @@
|
||||
---
|
||||
alwaysApply: true
|
||||
---
|
||||
Мы следуем принципам чистой архитектуры. На примере контракта Capital:
|
||||
- app/ - уровень приложения. Содержит действия, которые вызываются контрактом, которые оркестрируют бизнес-логику сущностей и ядра. Подпапки содержат домены бизнес-логики, где каждый файл - одно действие.
|
||||
- domain/ - доменный уровень. Содержит сущности и корневую бизнес-логику, которая в своих функциях использует несколько сущностей. На уровне сущностей запрещено вызывать другие сущности.
|
||||
- entity/ - слой сущностей домена. Содержит выделенные сущности, структуры, индексы их таблиц, и inline реализации простейших бизнес-функций, которые по своим названиям помогают явно определить что происходит с сущностью.
|
||||
- core/ - слой корневой бизнес-логики, которая объединяет несколько сущностей. Если бизнес-функция сущности требует для своего исполнения нескольких операций с сущностями или она явно относится к доменному уровню приложения за счет своего применения, то такая функция должна быть вынесена в ядро.
|
||||
|
||||
Мы допускаем последовательное-повторное обновление сущностей по ходу исполнения действия контракта, если это необходимо в пользу ясности. Длинна типа name не должна превышать 12 символов и не должна содержать специальных знаков.
|
||||
@@ -1,43 +0,0 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
Мне нужно, чтобы ты прошелся по контракту и разнес все действия по группам так, как это сделано в примере. Там:
|
||||
- указано краткое описание того что происходит и к чему приводит
|
||||
- указаны параметры и их описание
|
||||
- указана группа public_actions и public_soviet_actions - для контракта совета, но у тебя может быть другой контракт
|
||||
- указан якорь для ссылок внешних в самом верху и с отступом
|
||||
- указана требуемая авторизация
|
||||
- если у действия есть проводки по кошельков или фондам - отметить их с помощью тега @post:
|
||||
- Проводки по кошельку Wallet::add_available_funds, add_available_funds, add_blocked_funds, sub_blocked_funds, block_funds, unblock_funds, pay_membership_fee, unpay_membership_fee
|
||||
- Проводки по фондам Ledger::add, sub, transfer, block, unblock, writeoff, writeoffcnsl, add_membership_fee, sub_membership_fee, block_membership_fee, unblock_membership_fee
|
||||
- если у действия есть входящий документ - отметить его также с помощью тега @post
|
||||
|
||||
Пример:
|
||||
/**
|
||||
|
||||
*
|
||||
* @brief Завершение голосования для конкретного участника по методу Водянова.
|
||||
* Рассчитывает полные итоговые суммы участника включая:
|
||||
* - Результаты голосования по Водянову: (сумма голосов + общая сумма распределения) / количество голосующих
|
||||
* - Фиксированные распределения: 61.8% авторских премий равно между авторами, 61.8% создательских премий прямо создателям
|
||||
* Обновляет сегмент участника с итоговыми суммами раздельно по пулам
|
||||
* @param coopname Наименование кооператива
|
||||
* @param username Наименование пользователя-участника голосования
|
||||
* @param project_hash Хеш проекта для которого завершается голосование
|
||||
* @ingroup public_actions
|
||||
* @ingroup public_capital_actions
|
||||
* @note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
void capital::calcvotes(name coopname, name username, checksum256 project_hash) {
|
||||
require_auth(coopname);
|
||||
|
||||
// Получаем проект и проверяем состояние
|
||||
auto project = Capital::Projects::get_project_or_fail(coopname, project_hash);
|
||||
eosio::check(project.status == Capital::Projects::Status::COMPLETED, "Проект не в статусе завершенного голосования");
|
||||
... и т.д.
|
||||
|
||||
|
||||
ВАЖНО: ВСЕ ДЕЙСТВИЯ НЕОБХОДИМО ДОКУМЕНТИРОВАТЬ В *.cpp файлах, не в .hpp!!!
|
||||
ВАЖНО2: ПОСЛЕ ПРЕДЛОЖЕНИЯ @BRIEF ДОЛЖНА БЫТЬ ТОЧКА.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
Тебе необходимо добавить все константы контракта в группу public_contractname_consts.
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
Добавь в главный заголовочный файл контракта определение групп как в примере, но вместо capital должен быть тот контракт, на который у тебя задание.
|
||||
|
||||
/**
|
||||
\defgroup public_capital Контракт CAPITAL
|
||||
|
||||
* Смарт-контракт капитализации результатов интеллектуальной деятельности предназначен для управления учётом и распределением вознаграждений за результаты интеллектуальной деятельности.
|
||||
*/
|
||||
|
||||
/**
|
||||
\defgroup public_capital_processes Процессы
|
||||
\ingroup public_capital
|
||||
*/
|
||||
|
||||
/**
|
||||
\defgroup public_capital_actions Действия
|
||||
\ingroup public_capital
|
||||
*/
|
||||
|
||||
/**
|
||||
\defgroup public_capital_tables Таблицы
|
||||
\ingroup public_capital
|
||||
*/
|
||||
|
||||
/**
|
||||
\defgroup public_capital_consts Константы
|
||||
\ingroup public_capital
|
||||
*/
|
||||
@@ -1,82 +0,0 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
На основе предоставленной тебе информации необходимо создать страницы или страницу с описанием процессов контракта, создав новую группу @defgroup public_<contract>_<process>_process добавив их в общую группу @ingroup public_<contract>_proccesses
|
||||
|
||||
Каждый процесс должен включать:
|
||||
- Человеко-читаемое название по своему бизнес-смыслу
|
||||
- Диаграмму последовательности действий mermaid
|
||||
- Пошаговое описание происходящего в процессе с обоснованием зачем это нужно
|
||||
- Каждый шаг - это одно действие в смарт-контракте, на которое необходимо ссылаться через @ref <наименование якоря> "заголовок ссылки".
|
||||
- ВАЖНО: @ref ссылки делать ТОЛЬКО на процессы (например, @ref capital::regcontrib "подача заявки"), а НЕ на внутренние сущности или функции (НЕ Capital::Contributors::create_contributor).
|
||||
- На каждом шаге, если таковое есть в самом действии или в вызываемых им функциях, необходимо дополнительно отмечать с помощью тегов @post человеко-читаемые эффекты БЕЗ ссылок на внутренние функции:
|
||||
- Проводки по кошельку (например, "создается программный кошелек", "блокируются средства")
|
||||
- Проводки по фондам (например, "списываются средства с фонда", "переводятся средства")
|
||||
- Передаваемые на вход документы
|
||||
- Изменения статусов (например, "статус меняется с PENDING на ACTIVE")
|
||||
- При необходимости делай ссылки на процессы через @ref <contract>::<action> "описание процесса"
|
||||
- При выделении жирным используй теги <b></b>
|
||||
- Пункты списков пиши текстом, а не списком. Руководствуйся принципом: "лучше объясни, чем коротко напиши".
|
||||
|
||||
## ПРАВИЛА ОФОРМЛЕНИЯ ДИАГРАММ
|
||||
|
||||
### Диаграммы последовательности:
|
||||
- Использовать ТОЛЬКО sequenceDiagram вместо graph или flowchart
|
||||
- Участники (participant) называть понятными русскими именами:
|
||||
- "Вкладчик" вместо "User" или "Участник"
|
||||
- "Capital Contract" вместо технических названий
|
||||
- "Soviet Contract", "Gateway Contract" для системных контрактов
|
||||
- В сообщениях использовать ЧЕЛОВЕКО-ЧИТАЕМЫЕ описания вместо технических имен методов:
|
||||
- "Подача заявления о расходе" вместо "createexpnse()"
|
||||
- "Запрос предварительного одобрения" вместо "approveexpns()"
|
||||
- "Создание исходящего платежа" вместо "gateway::createoutpay"
|
||||
- Использовать Note over для пояснений внутренней логики
|
||||
- Использовать alt/else для альтернативных путей
|
||||
|
||||
### Замена технических терминов:
|
||||
- Все технические термины в тексте заменять на смысловые описания с @ref ссылками:
|
||||
- used_expense_pool → "сумма использованных средств"
|
||||
- accumulated_expense_pool → "накопленные средства пула"
|
||||
- Gateway::createoutpay → @ref Gateway::createoutpay "создаёт исходящий платеж"
|
||||
- Soviet::create_agenda → @ref Soviet::create_agenda "направляет на рассмотрение советом"
|
||||
|
||||
|
||||
Пример оформления диаграммы
|
||||
\htmlonly
|
||||
<div class="mermaid">
|
||||
sequenceDiagram
|
||||
participant V as Вкладчик
|
||||
participant C as Capital Contract
|
||||
participant S as Soviet Contract
|
||||
|
||||
V->>C: 1. Подача заявления
|
||||
Note over C: Проверка условий
|
||||
C->>S: 2. Запрос одобрения
|
||||
alt Одобрение
|
||||
S->>C: 3. Авторизация
|
||||
else Отклонение
|
||||
S->>C: 3. Отклонение
|
||||
end
|
||||
</div>
|
||||
\endhtmlonly
|
||||
*/
|
||||
|
||||
Пример оформления процесса
|
||||
/**
|
||||
|
||||
@defgroup public_capital_registration_process Процесс регистрации вкладчиков
|
||||
@ingroup public_capital_processes
|
||||
|
||||
@pre предварительные условия
|
||||
|
||||
Процесс приёма договора УХД от пайщика, фиксации его статуса как вкладчика
|
||||
и обеспечения технической возможности участвовать в проектах и программе капитализации.
|
||||
|
||||
``` cpp
|
||||
some formula
|
||||
```
|
||||
|
||||
@post эффекты
|
||||
*/
|
||||
@@ -1,64 +0,0 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
Мне нужно, чтобы ты прошелся по всем таблицам контракта которые используются в нем. Они могут находиться также где-то в @lib/. Мне нужно, чтобы ты добавил все найденные таблицы в указанную по аналогии с тем как тебе предоставлено в примере.
|
||||
|
||||
А именно:
|
||||
- все описания полей должны быть сделаны через ///<
|
||||
- У таблицы должно быть описание
|
||||
- У таблицы должна быть общая группа всех таблиц и частная группа конкретного контракта
|
||||
- У таблицы должен быть якорь
|
||||
- У таблицы должна быть область памяти в которой хранятся данные
|
||||
- У таблицы должно быть указано имя.
|
||||
|
||||
Пример:
|
||||
|
||||
namespace Capital {
|
||||
/**
|
||||
* @brief Таблица проектов хранит информацию о проектах кооператива, которые станут результатами.
|
||||
* @ingroup public_tables
|
||||
* @ingroup public_capital_tables
|
||||
* @par Область памяти (scope): coopname
|
||||
* @par Имя таблицы (table): projects
|
||||
*/
|
||||
struct [[eosio::table, eosio::contract(CAPITAL)]] project {
|
||||
uint64_t id; ///< ID проекта (внутренний ключ)
|
||||
|
||||
name coopname; ///< Имя кооператива
|
||||
checksum256 project_hash; ///< Хэш проекта (внешний ключ)
|
||||
checksum256 parent_hash; ///< Хэш родительского проекта (если есть)
|
||||
|
||||
eosio::name status; ///< Статус проекта
|
||||
|
||||
bool is_opened; ///< Открыт ли проект для инвестиций
|
||||
|
||||
// Мастер проекта
|
||||
name master; ///< Мастер проекта
|
||||
|
||||
std::string title; ///< Название проекта
|
||||
std::string description; ///< Описание проекта
|
||||
std::string meta; ///< Метаданные проекта
|
||||
|
||||
counts_data counts; ///< Счетчики участников проекта
|
||||
|
||||
plan_pool plan; ///< Плановые показатели
|
||||
fact_pool fact; ///< Фактические показатели
|
||||
crps_data crps; ///< Данные CRPS для распределения наград
|
||||
|
||||
// Голосование по методу Водянова
|
||||
voting_data voting; ///< Данные голосования по методу Водянова
|
||||
|
||||
// Членские взносы
|
||||
membership_crps membership; ///< Данные CRPS для распределения членских взносов
|
||||
|
||||
// Время создания проекта
|
||||
time_point_sec created_at = current_time_point(); ///< Время создания проекта
|
||||
|
||||
uint64_t primary_key() const { return id; } ///< Первичный ключ (1)
|
||||
uint64_t by_created_at() const { return created_at.sec_since_epoch(); } ///< Индекс по времени создания (2)
|
||||
checksum256 by_hash() const { return project_hash; } ///< Индекс по хэшу проекта (3)
|
||||
checksum256 by_parent_hash() const { return parent_hash; } ///< Индекс по хэшу родительского проекта (4)
|
||||
};
|
||||
@@ -13,9 +13,5 @@ node_modules
|
||||
build/*
|
||||
blockchain-data/*
|
||||
wallet-data/*
|
||||
docs/*
|
||||
blocks/*
|
||||
docs/html
|
||||
docs/html2
|
||||
docs/json
|
||||
docs/node_nodules
|
||||
docs/xml
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
trigger: glob
|
||||
glob: *.cpp,*.hpp
|
||||
---
|
||||
Мы следуем принципам чистой архитектуры. На примере контракта Capital:
|
||||
- app/ - уровень приложения. Содержит действия, которые вызываются контрактом, которые оркестрируют бизнес-логику сущностей и ядра. Подпапки содержат домены бизнес-логики, где каждый файл - одно действие.
|
||||
- domain/ - доменный уровень. Содержит сущности и корневую бизнес-логику, которая в своих функциях использует несколько сущностей. На уровне сущностей запрещено вызывать другие сущности.
|
||||
- entity/ - слой сущностей домена. Содержит выделенные сущности, структуры, индексы их таблиц, и inline реализации простейших бизнес-функций, которые по своим названиям помогают явно определить что происходит с сущностью.
|
||||
- core/ - слой корневой бизнес-логики, которая объединяет несколько сущностей. Если бизнес-функция сущности требует для своего исполнения нескольких операций с сущностями или она явно относится к доменному уровню приложения за счет своего применения, то такая функция должна быть вынесена в ядро.
|
||||
|
||||
Мы допускаем последовательное-повторное обновление сущностей по ходу исполнения действия контракта, если это необходимо в пользу ясности. Длинна типа name не должна превышать 12 символов и не должна содержать специальных знаков.
|
||||
@@ -1,57 +0,0 @@
|
||||
---
|
||||
description:
|
||||
alwaysApply: false
|
||||
---
|
||||
Мне нужно, чтобы ты прошелся по контракту и разнес все действия по группам так, как это сделано в примере. Там:
|
||||
- указано краткое описание того что происходит и к чему приводит
|
||||
- указаны параметры и их описание
|
||||
- указана группа public_actions и public_soviet_actions - для контракта совета, но у тебя может быть другой контракт
|
||||
- указан якорь для ссылок внешних в самом верху и с отступом
|
||||
- указана требуемая авторизация
|
||||
- если у действия есть проводки по кошельков или фондам - отметить их с помощью тега @post:
|
||||
* | Метод | Иконки | Описание |
|
||||
* |:-----------------------|:---------------|:--------------------------------------|
|
||||
* | Wallet::add_available_funds | `💰➕✅` | Добавление доступных средств |
|
||||
* | Wallet::sub_available_funds | `💰➖✅` | Уменьшение доступных средств |
|
||||
* | Wallet::add_blocked_funds | `💰➕🔒` | Добавление заблокированных средств |
|
||||
* | Wallet::sub_blocked_funds | `💰➖🔒` | Уменьшение заблокированных средств |
|
||||
*
|
||||
|
||||
- Проводки по фондам - их тоже отметь с тегом @post
|
||||
*
|
||||
* | Метод | Иконки | Описание |
|
||||
* |:-----------------------|:---------------|:--------------------------------------|
|
||||
* | Ledger::add | `🏛️➕✅` | Добавление средств в счет |
|
||||
* | Ledger::sub | `🏛️➖✅` | Уменьшение средств на счете |
|
||||
* | Ledger::block | `🏛️🔒` | Блокировка средств на счёте |
|
||||
* | Ledger::unblock | `🏛️🔓` | Разблокирование средств на счёте |
|
||||
|
||||
- если у действия есть входящий документ - отметить его также с помощью тега @post
|
||||
|
||||
Пример:
|
||||
/**
|
||||
|
||||
*
|
||||
* @brief Завершение голосования для конкретного участника по методу Водянова.
|
||||
* Рассчитывает полные итоговые суммы участника включая:
|
||||
* - Результаты голосования по Водянову: (сумма голосов + общая сумма распределения) / количество голосующих
|
||||
* - Фиксированные распределения: 61.8% авторских премий равно между авторами, 61.8% создательских премий прямо создателям
|
||||
* Обновляет сегмент участника с итоговыми суммами раздельно по пулам
|
||||
* @param coopname Наименование кооператива
|
||||
* @param username Наименование пользователя-участника голосования
|
||||
* @param project_hash Хеш проекта для которого завершается голосование
|
||||
* @ingroup public_actions
|
||||
* @ingroup public_capital_actions
|
||||
* @note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
void capital::calcvotes(name coopname, name username, checksum256 project_hash) {
|
||||
require_auth(coopname);
|
||||
|
||||
// Получаем проект и проверяем состояние
|
||||
auto project = Capital::Projects::get_project_or_fail(coopname, project_hash);
|
||||
eosio::check(project.status == Capital::Projects::Status::COMPLETED, "Проект не в статусе завершенного голосования");
|
||||
... и т.д.
|
||||
|
||||
|
||||
ВАЖНО: ВСЕ ДЕЙСТВИЯ НЕОБХОДИМО ДОКУМЕНТИРОВАТЬ В *.cpp файлах, не в .hpp!!!
|
||||
ВАЖНО2: ПОСЛЕ ПРЕДЛОЖЕНИЯ @BRIEF ДОЛЖНА БЫТЬ ТОЧКА.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
Тебе необходимо добавить все константы контракта в группу public_contractname_consts.
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
Добавь в главный заголовочный файл контракта определение групп как в примере, но вместо capital должен быть тот контракт, на который у тебя задание.
|
||||
|
||||
/**
|
||||
\defgroup public_capital Контракт CAPITAL
|
||||
|
||||
* Смарт-контракт капитализации результатов интеллектуальной деятельности предназначен для управления учётом и распределением вознаграждений за результаты интеллектуальной деятельности.
|
||||
*/
|
||||
|
||||
/**
|
||||
\defgroup public_capital_processes Процессы
|
||||
\ingroup public_capital
|
||||
*/
|
||||
|
||||
/**
|
||||
\defgroup public_capital_actions Действия
|
||||
\ingroup public_capital
|
||||
*/
|
||||
|
||||
/**
|
||||
\defgroup public_capital_tables Таблицы
|
||||
\ingroup public_capital
|
||||
*/
|
||||
|
||||
/**
|
||||
\defgroup public_capital_consts Константы
|
||||
\ingroup public_capital
|
||||
*/
|
||||
@@ -1,40 +0,0 @@
|
||||
---
|
||||
trigger: manual
|
||||
---
|
||||
Используй следующий язык иконок для обозначения операций в тегах @pre действий или в тегах @post эффектов этих действий.
|
||||
Во всех действиях, в которых в аргументах есть документ - этот документ должен быть указан в @pre и иконкой документа.
|
||||
Во всех местах, где по коду или в по тексту есть указание на увеличение или уменьшение средств в кошельке - необходимо использовать соответствующую комбинацию символов.
|
||||
Во всех местах, где по коду или по тексту есть указание на проводку по счету - необходимо отмечать их языком иконок и писать соответствующий текст.
|
||||
|
||||
|
||||
* ## Язык иконок для обозначений
|
||||
* | Символ | Значение |
|
||||
* |:------:|:--------------------------|
|
||||
* | `📄` | **Документ чего-то** |
|
||||
* | `💰` | **Кошелек** |
|
||||
* | `🏛️` | **Фонд** |
|
||||
* | `➕` | **Добавление** средств |
|
||||
* | `➖` | **Списание** средств |
|
||||
* | `✅` | Статус: **Доступные** |
|
||||
* | `🔒` | Статус: **Заблокировано** |
|
||||
* | `🔓` | **Разблокирование** |
|
||||
*
|
||||
* ### Графический язык проводок по Кошельку (Wallet)
|
||||
*
|
||||
* | Метод | Иконки | Описание |
|
||||
* |:-----------------------|:---------------|:--------------------------------------|
|
||||
* | Wallet::add_available_funds | `💰➕✅` | Добавление доступных средств |
|
||||
* | Wallet::sub_available_funds | `💰➖✅` | Уменьшение доступных средств |
|
||||
* | Wallet::add_blocked_funds | `💰➕🔒` | Добавление заблокированных средств |
|
||||
* | Wallet::sub_blocked_funds | `💰➖🔒` | Уменьшение заблокированных средств |
|
||||
*
|
||||
* ### Графический язык проводок по счетам (Ledger)
|
||||
*
|
||||
* | Метод | Иконки | Описание |
|
||||
* |:-----------------------|:---------------|:--------------------------------------|
|
||||
* | Ledger::add | `🏛️➕✅` | Добавление средств в счет |
|
||||
* | Ledger::sub | `🏛️➖✅` | Уменьшение средств на счете |
|
||||
* | Ledger::block | `🏛️🔒` | Блокировка средств на счёте |
|
||||
* | Ledger::unblock | `🏛️🔓` | Разблокирование средств на счёте |
|
||||
*/
|
||||
*
|
||||
@@ -1,28 +0,0 @@
|
||||
---
|
||||
trigger: manual
|
||||
---
|
||||
Мне нужно, чтобы ты организовал следующий принцип доступа к файлами документации:
|
||||
|
||||
1. У каждого эффекта должен быть файл процесса, на который он ссылается.
|
||||
2. У каждого эффекта должен быть свой файл, который лежит рядом с файлом процесса (в той же директории) и называется вот так <name>-effect.dox
|
||||
3. Каждый файл эффекта должен содержать информацию о движении средств по кошелькам пользователя или кооператива типа такого (БЕЗ СПЕЦИАЛЬНОЙ DOXYGEN РАЗМЕТКИ!!!):
|
||||
➕🏛️ Ledger::add на счёт Ledger::accounts::SHARE_FUND на сумму паевого взноса за вычетом ссуды\n
|
||||
➕🔒 Wallet::add_blocked_funds на кошельке @ref _source_program на сумму паевого взноса за вычетом ссуды\n
|
||||
➖🏛️ Ledger::sub со счёта Ledger::accounts::LONG_TERM_LOANS на сумму погашаемой ссуды\n
|
||||
🫳 Loan::settle_debt на сумму погашаемой ссуды
|
||||
4. Все файлы эффектов должны собираться в единый файл для обзора, который находится в docs/effects-traces.dox вот так:
|
||||
/**
|
||||
* @defgroup deltas_overview Эффекты по процессам
|
||||
* @ingroup public_capital
|
||||
|
||||
@ref public_capital_result_process
|
||||
@include{doc} public_capital_result_process-effect.dox
|
||||
|
||||
4. Все файлы процессов должны включать в конце, но перед информацией о связанных процессах, файл с эффектом:
|
||||
# Эффекты процесса
|
||||
@include{doc} public_capital_result_process-effect.dox
|
||||
|
||||
5. Все пути могут быть без префиксов и директорий и определяться просто уникальным именем файла.
|
||||
|
||||
...
|
||||
*/
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
trigger: manual
|
||||
---
|
||||
Также мне надо, чтобы в каждом процессе в самом начале этого процесса было @note, которое аккумулирует в себе эффекты, что делает весь процесс.
|
||||
Там, где запрашивается approve из совета - там всегда приходит принятое заявление. Также в совете бывает что запрашивает протокол решения.
|
||||
|
||||
Например, в процесса внесения инвестиционного взноса в проект в начале будет:
|
||||
@note
|
||||
- 📄 заявление на паевой взнос
|
||||
- 📄 принятое председателем заявление
|
||||
- 💰➕🔒 добавление заблокированных средств в кошелек проекта
|
||||
- 🏛️➕✅ добавление доступных средств на счет паевого фонда
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
На основе предоставленной тебе информации необходимо создать страницы или страницу с описанием процессов контракта, создав новую группу @defgroup public_<contract>_<process>_process добавив их в общую группу @ingroup public_<contract>_proccesses
|
||||
|
||||
Каждый процесс должен включать:
|
||||
- Человеко-читаемое название по своему бизнес-смыслу
|
||||
- Диаграмму последовательности действий mermaid
|
||||
- Пошаговое описание происходящего в процессе с обоснованием зачем это нужно
|
||||
- Каждый шаг - это одно действие в смарт-контракте, на которое необходимо ссылаться через @ref <наименование якоря> "заголовок ссылки".
|
||||
- ВАЖНО: @ref ссылки делать ТОЛЬКО на процессы (например, @ref capital::regcontrib "подача заявки"), а НЕ на внутренние сущности или функции (НЕ Capital::Contributors::create_contributor).
|
||||
- На каждом шаге, если таковое есть в самом действии или в вызываемых им функциях, необходимо дополнительно отмечать с помощью тегов @post человеко-читаемые эффекты БЕЗ ссылок на внутренние функции:
|
||||
- Проводки по кошельку (например, "создается программный кошелек", "блокируются средства")
|
||||
- Проводки по фондам (например, "списываются средства с фонда", "переводятся средства")
|
||||
- Передаваемые на вход документы
|
||||
- Изменения статусов (например, "статус меняется с PENDING на ACTIVE")
|
||||
- При необходимости делай ссылки на процессы через @ref <contract>::<action> "описание процесса"
|
||||
- При выделении жирным используй теги <b></b>
|
||||
- Пункты списков пиши текстом, а не списком. Руководствуйся принципом: "лучше объясни, чем коротко напиши".
|
||||
|
||||
## ПРАВИЛА ОФОРМЛЕНИЯ ДИАГРАММ
|
||||
|
||||
### Диаграммы последовательности:
|
||||
- Использовать ТОЛЬКО sequenceDiagram вместо graph или flowchart
|
||||
- Участники (participant) называть понятными русскими именами:
|
||||
- "Вкладчик" вместо "User" или "Участник"
|
||||
- "Capital Contract" вместо технических названий
|
||||
- "Soviet Contract", "Gateway Contract" для системных контрактов
|
||||
- В сообщениях использовать ЧЕЛОВЕКО-ЧИТАЕМЫЕ описания вместо технических имен методов:
|
||||
- "Подача заявления о расходе" вместо "createexpnse()"
|
||||
- "Запрос предварительного одобрения" вместо "approveexpns()"
|
||||
- "Создание исходящего платежа" вместо "gateway::createoutpay"
|
||||
- Использовать Note over для пояснений внутренней логики
|
||||
- Использовать alt/else для альтернативных путей
|
||||
|
||||
### Замена технических терминов:
|
||||
- Все технические термины в тексте заменять на смысловые описания с @ref ссылками:
|
||||
- used_expense_pool → "сумма использованных средств"
|
||||
- accumulated_expense_pool → "накопленные средства пула"
|
||||
- Gateway::createoutpay → @ref Gateway::createoutpay "создаёт исходящий платеж"
|
||||
- Soviet::create_agenda → @ref Soviet::create_agenda "направляет на рассмотрение советом"
|
||||
|
||||
|
||||
Пример оформления диаграммы
|
||||
\htmlonly
|
||||
<div class="mermaid">
|
||||
sequenceDiagram
|
||||
participant V as Вкладчик
|
||||
participant C as Capital Contract
|
||||
participant S as Soviet Contract
|
||||
|
||||
V->>C: 1. Подача заявления
|
||||
Note over C: Проверка условий
|
||||
C->>S: 2. Запрос одобрения
|
||||
alt Одобрение
|
||||
S->>C: 3. Авторизация
|
||||
else Отклонение
|
||||
S->>C: 3. Отклонение
|
||||
end
|
||||
</div>
|
||||
\endhtmlonly
|
||||
*/
|
||||
|
||||
Пример оформления процесса
|
||||
/**
|
||||
|
||||
@defgroup public_capital_registration_process Процесс регистрации вкладчиков
|
||||
@ingroup public_capital_processes
|
||||
|
||||
@pre предварительные условия
|
||||
|
||||
Процесс приёма договора УХД от пайщика, фиксации его статуса как вкладчика
|
||||
и обеспечения технической возможности участвовать в проектах и программе капитализации.
|
||||
|
||||
``` cpp
|
||||
some formula
|
||||
```
|
||||
|
||||
@post эффекты
|
||||
*/
|
||||
@@ -1,64 +0,0 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
Мне нужно, чтобы ты прошелся по всем таблицам контракта которые используются в нем. Они могут находиться также где-то в @lib/. Мне нужно, чтобы ты добавил все найденные таблицы в указанную по аналогии с тем как тебе предоставлено в примере.
|
||||
|
||||
А именно:
|
||||
- все описания полей должны быть сделаны через ///<
|
||||
- У таблицы должно быть описание
|
||||
- У таблицы должна быть общая группа всех таблиц и частная группа конкретного контракта
|
||||
- У таблицы должен быть якорь
|
||||
- У таблицы должна быть область памяти в которой хранятся данные
|
||||
- У таблицы должно быть указано имя.
|
||||
|
||||
Пример:
|
||||
|
||||
namespace Capital {
|
||||
/**
|
||||
* @brief Таблица проектов хранит информацию о проектах кооператива, которые станут результатами.
|
||||
* @ingroup public_tables
|
||||
* @ingroup public_capital_tables
|
||||
* @par Область памяти (scope): coopname
|
||||
* @par Имя таблицы (table): projects
|
||||
*/
|
||||
struct [[eosio::table, eosio::contract(CAPITAL)]] project {
|
||||
uint64_t id; ///< ID проекта (внутренний ключ)
|
||||
|
||||
name coopname; ///< Имя кооператива
|
||||
checksum256 project_hash; ///< Хэш проекта (внешний ключ)
|
||||
checksum256 parent_hash; ///< Хэш родительского проекта (если есть)
|
||||
|
||||
eosio::name status; ///< Статус проекта
|
||||
|
||||
bool is_opened; ///< Открыт ли проект для инвестиций
|
||||
|
||||
// Мастер проекта
|
||||
name master; ///< Мастер проекта
|
||||
|
||||
std::string title; ///< Название проекта
|
||||
std::string description; ///< Описание проекта
|
||||
std::string meta; ///< Метаданные проекта
|
||||
|
||||
counts_data counts; ///< Счетчики участников проекта
|
||||
|
||||
plan_pool plan; ///< Плановые показатели
|
||||
fact_pool fact; ///< Фактические показатели
|
||||
crps_data crps; ///< Данные CRPS для распределения наград
|
||||
|
||||
// Голосование по методу Водянова
|
||||
voting_data voting; ///< Данные голосования по методу Водянова
|
||||
|
||||
// Членские взносы
|
||||
membership_crps membership; ///< Данные CRPS для распределения членских взносов
|
||||
|
||||
// Время создания проекта
|
||||
time_point_sec created_at = current_time_point(); ///< Время создания проекта
|
||||
|
||||
uint64_t primary_key() const { return id; } ///< Первичный ключ (1)
|
||||
uint64_t by_created_at() const { return created_at.sec_since_epoch(); } ///< Индекс по времени создания (2)
|
||||
checksum256 by_hash() const { return project_hash; } ///< Индекс по хэшу проекта (3)
|
||||
checksum256 by_parent_hash() const { return parent_hash; } ///< Индекс по хэшу родительского проекта (4)
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
trigger: always_on
|
||||
alwaysApply: true
|
||||
---
|
||||
ВСЕГДА ОТВЕЧАЙ НА РУССКОМ ЯЗЫКЕ. ДОКУМЕНТАЦИЮ ПИШИ КРАТКО - БЕЗ ФАНТАЗИЙ, ПО СУЩЕСТВУ.НЕ ИСПОЛЬЗУЙ СЛОВА, КОТОРЫЕ ВЫБРАСЫВАЮТСЯ БЕЗ ПОТЕРИ СМЫСЛА. НЕ ПИШИ МНЕ ДЛИННЫЕ ОТЧЕТЫ О ПРОДЕЛАННОЙ РАБОТЕ - ОНИ МНЕ НАФИГ НЕ НУЖНЫ!!
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/lib/**",
|
||||
"${workspaceFolder}/lib/eosiolib/contracts/**",
|
||||
"${workspaceFolder}/lib/eosiolib/core/**"
|
||||
],
|
||||
"defines": [
|
||||
"_DEBUG",
|
||||
"UNICODE",
|
||||
"_UNICODE",
|
||||
"int128_t=__int128_t",
|
||||
"uint128_t=__uint128_t"
|
||||
],
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++20"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
@@ -30,8 +30,6 @@ macro(add_contract_build name)
|
||||
endmacro()
|
||||
|
||||
# Добавляем все контракты
|
||||
add_contract_build(test)
|
||||
add_contract_build(ledger)
|
||||
add_contract_build(meet)
|
||||
add_contract_build(loan)
|
||||
add_contract_build(wallet)
|
||||
|
||||
@@ -32,19 +32,19 @@ DOXYFILE_ENCODING = UTF-8
|
||||
# title of most generated pages and in a few other places.
|
||||
# The default value is: My Project.
|
||||
|
||||
PROJECT_NAME = COOPENOMICS
|
||||
PROJECT_NAME = "Системные контракты"
|
||||
|
||||
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = v1
|
||||
PROJECT_NUMBER =
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
# quick idea about the purpose of the project. Keep the description short.
|
||||
|
||||
PROJECT_BRIEF = Кооперативная Экономика
|
||||
PROJECT_BRIEF =
|
||||
|
||||
# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
|
||||
# in the documentation. The maximum height of the logo should not exceed 55
|
||||
@@ -874,7 +874,7 @@ WARN_LOGFILE =
|
||||
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = cpp/ *.dox #README.md
|
||||
INPUT = README.md cpp/common/ cpp/registrator/ cpp/marketplace/ cpp/soviet/ cpp/system/ cpp/gateway/ cpp/fund/ cpp/draft cpp/contributor
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
@@ -905,7 +905,7 @@ INPUT_ENCODING = UTF-8
|
||||
|
||||
FILE_PATTERNS = *.cpp \
|
||||
*.hpp \
|
||||
*.dox
|
||||
|
||||
|
||||
# The RECURSIVE tag can be used to specify whether or not subdirectories should
|
||||
# be searched for input files as well.
|
||||
@@ -953,21 +953,21 @@ EXCLUDE_SYMBOLS =
|
||||
# that contain example code fragments that are included (see the \include
|
||||
# command).
|
||||
|
||||
EXAMPLE_PATH = .
|
||||
EXAMPLE_PATH =
|
||||
|
||||
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
|
||||
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
|
||||
# *.h) to filter out the source-files in the directories. If left blank all
|
||||
# files are included.
|
||||
|
||||
EXAMPLE_PATTERNS = *.dox
|
||||
EXAMPLE_PATTERNS = *
|
||||
|
||||
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
|
||||
# searched for input files to be used with the \include or \dontinclude commands
|
||||
# irrespective of the value of the RECURSIVE tag.
|
||||
# The default value is: NO.
|
||||
|
||||
EXAMPLE_RECURSIVE = YES
|
||||
EXAMPLE_RECURSIVE = NO
|
||||
|
||||
# The IMAGE_PATH tag can be used to specify one or more files or directories
|
||||
# that contain images that are to be included in the documentation (see the
|
||||
@@ -1029,7 +1029,7 @@ FILTER_SOURCE_PATTERNS =
|
||||
# (index.html). This can be useful if you have a project on for instance GitHub
|
||||
# and want to reuse the introduction page also for the doxygen output.
|
||||
|
||||
# USE_MDFILE_AS_MAINPAGE = README.md
|
||||
USE_MDFILE_AS_MAINPAGE = README.md
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to source browsing
|
||||
@@ -1178,7 +1178,7 @@ HTML_FILE_EXTENSION = .html
|
||||
# of the possible markers and block names see the documentation.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_HEADER = docs/css/docs/doxygen-custom/header.html
|
||||
HTML_HEADER =
|
||||
|
||||
# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
|
||||
# generated HTML page. If the tag is left blank doxygen will generate a standard
|
||||
@@ -1188,7 +1188,7 @@ HTML_HEADER = docs/css/docs/doxygen-custom/header.html
|
||||
# that doxygen normally uses.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_FOOTER = docs/css/docs/doxygen-custom/footer.html
|
||||
HTML_FOOTER =
|
||||
|
||||
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
|
||||
# sheet that is used by each HTML page. It can be used to fine-tune the look of
|
||||
@@ -1213,7 +1213,7 @@ HTML_STYLESHEET =
|
||||
# list). For an example see the documentation.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_EXTRA_STYLESHEET = docs/css/doxygen-awesome.css docs/css/docs/doxygen-custom/custom.css docs/css/doxygen-awesome-sidebar-only.css docs/css/doxygen-awesome-sidebar-only-darkmode-toggle.css
|
||||
HTML_EXTRA_STYLESHEET = docs/css/doxygen-awesome.css docs/css/doxygen-awesome-sidebar-only.css
|
||||
|
||||
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
|
||||
# other source files which should be copied to the HTML output directory. Note
|
||||
@@ -1223,9 +1223,9 @@ HTML_EXTRA_STYLESHEET = docs/css/doxygen-awesome.css docs/css/docs/doxygen-cust
|
||||
# files will be copied as-is; there are no commands or markers available.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_EXTRA_FILES = docs/css/doxygen-awesome-darkmode-toggle.js docs/css/mermaid.min.js docs/css/docs/doxygen-custom/custom.js
|
||||
HTML_EXTRA_FILES =
|
||||
|
||||
# HTML_COLORSTYLE = LIGHT # required with Doxygen >= 1.9.5
|
||||
HTML_COLORSTYLE = LIGHT # required with Doxygen >= 1.9.5
|
||||
|
||||
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
|
||||
# will adjust the colors in the style sheet and background images according to
|
||||
@@ -1554,7 +1554,7 @@ ENUM_VALUES_PER_LINE = 4
|
||||
# Minimum value: 0, maximum value: 1500, default value: 250.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
TREEVIEW_WIDTH = 200
|
||||
TREEVIEW_WIDTH = 250
|
||||
|
||||
# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
|
||||
# external symbols imported via tag files in a separate window.
|
||||
@@ -1616,7 +1616,7 @@ FORMULA_MACROFILE =
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
USE_MATHJAX = YES
|
||||
USE_MATHJAX = NO
|
||||
|
||||
# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.
|
||||
# Note that the different versions of MathJax have different requirements with
|
||||
@@ -2061,7 +2061,7 @@ MAN_LINKS = NO
|
||||
# captures the structure of the code including all documentation.
|
||||
# The default value is: NO.
|
||||
|
||||
GENERATE_XML = NO
|
||||
GENERATE_XML = YES
|
||||
|
||||
# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
|
||||
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Введение
|
||||
# Введение.
|
||||
|
||||
Репозиторий содержит в себе смарт-контракты операционной системы coopOS, образующих протокол COOPENOMICS.
|
||||
|
||||
|
||||
Vendored
BIN
Binary file not shown.
@@ -8,26 +8,11 @@
|
||||
|
||||
using namespace eosio;
|
||||
|
||||
/**
|
||||
* @brief Инициализация контракта кооперативных участков.
|
||||
* Выполняет начальную настройку контракта.
|
||||
* @ingroup public_actions
|
||||
* @ingroup public_branch_actions
|
||||
|
||||
* @note Авторизация требуется от аккаунта: @p _branch
|
||||
*/
|
||||
[[eosio::action]] void branch::migrate() {
|
||||
require_auth(_branch);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Инициализация контракта кооперативных участков.
|
||||
* Выполняет начальную настройку контракта.
|
||||
* @ingroup public_actions
|
||||
* @ingroup public_branch_actions
|
||||
|
||||
* @note Авторизация требуется от аккаунта: @p _system
|
||||
*/
|
||||
[[eosio::action]] void branch::init()
|
||||
{
|
||||
require_auth(_system);
|
||||
|
||||
@@ -8,40 +8,6 @@
|
||||
|
||||
#include "../lib/common.hpp"
|
||||
|
||||
/**
|
||||
\defgroup public_branch Контракт BRANCH
|
||||
|
||||
* Смарт-контракт управления кооперативными участками предназначен для создания, редактирования и удаления кооперативных участков, а также управления доверенными лицами.
|
||||
*/
|
||||
|
||||
/**
|
||||
\defgroup public_branch_processes Процессы
|
||||
\ingroup public_branch
|
||||
*/
|
||||
|
||||
/**
|
||||
\defgroup public_branch_actions Действия
|
||||
\ingroup public_branch
|
||||
*/
|
||||
|
||||
/**
|
||||
\defgroup public_branch_tables Таблицы
|
||||
\ingroup public_branch
|
||||
*/
|
||||
|
||||
// /**
|
||||
// \defgroup public_branch_consts Константы
|
||||
// \ingroup public_branch
|
||||
// */
|
||||
|
||||
/**
|
||||
* @ingroup public_consts
|
||||
* @ingroup public_branch_consts
|
||||
|
||||
* @brief Константы контракта кооперативных участков
|
||||
*/
|
||||
// Константы будут добавлены по мере необходимости
|
||||
|
||||
/**
|
||||
* \ingroup public_contracts
|
||||
*
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
/**
|
||||
* @brief Добавление доверенного лица в кооперативный участок.
|
||||
* Добавляет нового доверенного лица в существующий кооперативный участок.
|
||||
* Максимальное количество доверенных лиц на один участок - 3.
|
||||
* @param coopname Наименование кооператива
|
||||
* @param braname Наименование кооперативного участка
|
||||
* @param trusted Доверенное лицо для добавления (должно быть физическим лицом)
|
||||
* @ingroup public_actions
|
||||
* @ingroup public_branch_actions
|
||||
|
||||
* @note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
[[eosio::action]] void branch::addtrusted(eosio::name coopname, eosio::name braname, eosio::name trusted) {
|
||||
check_auth_or_fail(_branch, coopname, coopname, "addtrusted"_n);
|
||||
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
/**
|
||||
* @brief Создание нового кооперативного участка.
|
||||
* Создает новый кооперативный участок с указанным председателем.
|
||||
* При создании третьего участка автоматически активирует систему кооперативных участков.
|
||||
* @param coopname Наименование кооператива
|
||||
* @param braname Наименование кооперативного участка
|
||||
* @param trustee Председатель кооперативного участка (должен быть физическим лицом)
|
||||
* @ingroup public_actions
|
||||
* @ingroup public_branch_actions
|
||||
|
||||
* @note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
[[eosio::action]] void branch::createbranch(eosio::name coopname, eosio::name braname, eosio::name trustee) {
|
||||
check_auth_or_fail(_branch, coopname, coopname, "createbranch"_n);
|
||||
|
||||
|
||||
@@ -1,14 +1,3 @@
|
||||
/**
|
||||
* @brief Удаление кооперативного участка.
|
||||
* Удаляет существующий кооперативный участок и отключает его участников.
|
||||
* При удалении участка, когда остается менее 3 участков, автоматически отключает систему кооперативных участков.
|
||||
* @param coopname Наименование кооператива
|
||||
* @param braname Наименование кооперативного участка для удаления
|
||||
* @ingroup public_actions
|
||||
* @ingroup public_branch_actions
|
||||
|
||||
* @note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
[[eosio::action]] void branch::deletebranch(eosio::name coopname, eosio::name braname) {
|
||||
check_auth_or_fail(_branch, coopname, coopname, "deletebranch"_n);
|
||||
|
||||
|
||||
@@ -1,14 +1,3 @@
|
||||
/**
|
||||
* @brief Удаление доверенного лица из кооперативного участка.
|
||||
* Удаляет доверенное лицо из существующего кооперативного участка.
|
||||
* @param coopname Наименование кооператива
|
||||
* @param braname Наименование кооперативного участка
|
||||
* @param trusted Доверенное лицо для удаления
|
||||
* @ingroup public_actions
|
||||
* @ingroup public_branch_actions
|
||||
|
||||
* @note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
[[eosio::action]] void branch::deltrusted(eosio::name coopname, eosio::name braname, eosio::name trusted) {
|
||||
check_auth_or_fail(_branch, coopname, coopname, "deltrusted"_n);
|
||||
|
||||
|
||||
@@ -1,14 +1,3 @@
|
||||
/**
|
||||
* @brief Редактирование кооперативного участка.
|
||||
* Изменяет председателя существующего кооперативного участка.
|
||||
* @param coopname Наименование кооператива
|
||||
* @param braname Наименование кооперативного участка
|
||||
* @param trustee Новый председатель кооперативного участка (должен быть физическим лицом)
|
||||
* @ingroup public_actions
|
||||
* @ingroup public_branch_actions
|
||||
|
||||
* @note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
[[eosio::action]] void branch::editbranch(eosio::name coopname, eosio::name braname, eosio::name trustee) {
|
||||
check_auth_or_fail(_branch, coopname, coopname, "editbranch"_n);
|
||||
|
||||
|
||||
BIN
Binary file not shown.
-71
@@ -1,71 +0,0 @@
|
||||
/**
|
||||
|
||||
* @defgroup public_capital_configuration_process Конфигурация контракта для кооператива
|
||||
* @ingroup public_capital_processes
|
||||
*
|
||||
* Процесс установки и обновления глобальных параметров работы контракта
|
||||
* для конкретного кооператива, включая параметры голосования, распределения
|
||||
* премий и временных ограничений.
|
||||
*
|
||||
* ## Назначение процесса
|
||||
*
|
||||
* Конфигурация определяет ключевые параметры функционирования системы:
|
||||
* - Размеры премий и их распределение между участниками
|
||||
* - Временные рамки для различных операций
|
||||
* - Лимиты и ограничения на операции
|
||||
* - Процентные соотношения для расчетов
|
||||
*
|
||||
* \htmlonly
|
||||
* <div class="mermaid">
|
||||
* sequenceDiagram
|
||||
* participant Кооператив
|
||||
* participant Capital_Contract
|
||||
*
|
||||
* Кооператив->>Capital_Contract: 1. Установка конфигурации
|
||||
* Note over Capital_Contract: Проверка авторизации кооператива
|
||||
* Capital_Contract->>Capital_Contract: 2. Валидация параметров
|
||||
* Capital_Contract->>Capital_Contract: 3. Проверка существования конфигурации
|
||||
* alt Конфигурация не существует
|
||||
* Capital_Contract->>Capital_Contract: 4. Создание новой записи
|
||||
* else Конфигурация существует
|
||||
* Capital_Contract->>Capital_Contract: 4. Обновление существующей записи
|
||||
* end
|
||||
* Capital_Contract-->>Кооператив: Успешная установка конфигурации
|
||||
* </div>
|
||||
* \endhtmlonly
|
||||
*
|
||||
* ## Пошаговое описание процесса
|
||||
*
|
||||
* ### Шаг 1: Авторизация кооператива
|
||||
* <b>Действие:</b> Проверка авторизации от имени кооператива
|
||||
*
|
||||
* Кооператив должен быть авторизован для изменения своих параметров.
|
||||
* Это обеспечивает безопасность и предотвращает несанкционированные изменения конфигурации.
|
||||
*
|
||||
* ### Шаг 2: Валидация параметров конфигурации
|
||||
* <b>Действие:</b> @ref capital::setconfig "Установка конфигурации"
|
||||
*
|
||||
* Система проверяет корректность всех переданных параметров:
|
||||
* - Премия координатора должна быть в диапазоне от 0 до 10 процентов от инвестиций
|
||||
* - Процент доли пула расходов должен быть между 0 и 100 процентами от инвестиций
|
||||
* - Срок действия приглашения координатора должен быть не отрицательным числом дней
|
||||
* - Период голосования должен быть между 1 и 30 днями
|
||||
* - Процент премий авторов на голосовании должен быть между 0 и 100
|
||||
* - Процент премий создателей на голосовании должен быть между 0 и 100
|
||||
*
|
||||
* Валидация предотвращает установку некорректных значений, которые могут
|
||||
* нарушить экономическую модель или логику работы системы.
|
||||
*
|
||||
* ### Шаг 3: Создание или обновление конфигурации
|
||||
* <b>Действие:</b> Работа с таблицей @ref Capital::global_state
|
||||
*
|
||||
* Если конфигурация для кооператива не существует, создается новая запись.
|
||||
* Если конфигурация уже есть, она обновляется новыми значениями.
|
||||
*
|
||||
* @post
|
||||
* - Конфигурация сохранена в таблице @ref Capital::global_state контракта
|
||||
* - Установлены корректные параметры работы системы для кооператива
|
||||
* - Кооператив может использовать контракт CAPITAL
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -1,39 +0,0 @@
|
||||
/**
|
||||
* @brief Устанавливает конфигурацию кооператива
|
||||
* Обновляет глобальное состояние контракта для указанного кооператива:
|
||||
* - Валидирует параметры конфигурации (проценты, периоды, лимиты)
|
||||
* - Создает новую запись конфигурации если отсутствует
|
||||
* - Модифицирует существующую конфигурацию
|
||||
* @param coopname Имя кооператива
|
||||
* @param config Новая конфигурация кооператива
|
||||
* @ingroup public_actions
|
||||
* @ingroup public_capital_actions
|
||||
|
||||
* @note Авторизация требуется от аккаунта: @p coopname
|
||||
*/
|
||||
void capital::setconfig(eosio::name coopname, Capital::config config) {
|
||||
require_auth(coopname);
|
||||
|
||||
eosio::check(config.coordinator_bonus_percent >= 0.0 && config.coordinator_bonus_percent <= 10, "Координатор не может получить больше 0.1 (10%) от инвестиций");
|
||||
eosio::check(config.expense_pool_percent >= 0.0 && config.expense_pool_percent <= 100, "Процент инвестиций в пул расходов должен быть между 0 и 1 (100%)");
|
||||
eosio::check(config.coordinator_invite_validity_days >= 0, "Срок действия приглашения координатора должен быть не отрицательным числом дней");
|
||||
eosio::check(config.voting_period_in_days >= 1 && config.voting_period_in_days <= 30, "Период голосования должен быть между 1 и 30 днями");
|
||||
eosio::check(config.authors_voting_percent >= 0.0 && config.authors_voting_percent <= 100, "Процент премий авторов на голосовании должен быть между 0 и 100");
|
||||
eosio::check(config.creators_voting_percent >= 0.0 && config.creators_voting_percent <= 100, "Процент премий создателей на голосовании должен быть между 0 и 100");
|
||||
|
||||
Capital::global_state_table global_state_inst(_self, _self.value);
|
||||
auto itr = global_state_inst.find(coopname.value);
|
||||
|
||||
if (itr == global_state_inst.end()) {
|
||||
global_state_inst.emplace(coopname, [&](auto& s) {
|
||||
s.coopname = coopname;
|
||||
s.config = config;
|
||||
});
|
||||
} else {
|
||||
global_state_inst.modify(itr, coopname, [&](auto& s) {
|
||||
s.config = config;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* @brief Одобряет долг в проекте
|
||||
* Одобряет долг в проекте и создает повестку в совете:
|
||||
* - Получает долг
|
||||
* - Обновляет статус долга на approved
|
||||
* - Создает повестку в совете для рассмотрения долга
|
||||
* @param coopname Наименование кооператива
|
||||
* @param debt_hash Хеш долга для одобрения
|
||||
* @param approved_statement Одобренное заявление о долге
|
||||
* @ingroup public_actions
|
||||
* @ingroup public_capital_actions
|
||||
|
||||
* @note Авторизация требуется от аккаунта: @p _soviet
|
||||
*/
|
||||
void capital::approvedebt(eosio::name coopname, checksum256 debt_hash, document2 approved_statement)
|
||||
{
|
||||
require_auth(_soviet);
|
||||
|
||||
// Получаем долг
|
||||
auto exist_debt = Capital::Debts::get_debt_or_fail(coopname, debt_hash);
|
||||
|
||||
// Обновляем статус долга
|
||||
Capital::Debts::update_debt_status(coopname, debt_hash, Capital::Debts::Status::APPROVED,
|
||||
_soviet, approved_statement);
|
||||
|
||||
// Создаем агенду в совете
|
||||
Capital::Debts::create_debt_agenda(coopname, exist_debt.username, debt_hash, exist_debt.statement);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user