chore: зафиксированы все текущие изменения проекта

This commit is contained in:
AidarKC
2026-05-25 23:46:54 +03:00
parent 8c5de781ea
commit 8941582d54
53 changed files with 2303 additions and 106 deletions
+32 -4
View File
@@ -1,14 +1,42 @@
# Будущие фичи
Эта папка хранит задачи, которые сознательно отложены и сейчас не должны попадать в активную разработку или ручную проверку.
Эта папка хранит задачи, которые сознательно отложены и сейчас не должны попадать в активную разработку или ручную проверку без отдельной команды пользователя.
## Горизонты планирования
- `near/` - ближайшие планы: задачи, к которым можно вернуться сегодня или завтра.
- `medium/` - среднесрочные планы: задачи на ближайшие недели или 1-2 месяца.
- `far/` - дальнее будущее: идеи без понятного срока возврата.
Если пользователь спрашивает, какие есть планы, агент должен смотреть эти три папки и кратко перечислять задачи по горизонтам.
## Как использовать
1. Каждая будущая фича описывается отдельным markdown-файлом.
1. Каждая будущая фича описывается отдельным markdown-файлом в одном из горизонтов.
2. В файле нужно фиксировать:
- зачем нужна фича;
- что уже было сделано в коде;
- что временно отключено или закомментировано;
- к какому сроку или горизонту она относится;
- что нужно сделать;
- какие вопросы нужно уточнить перед реализацией;
- что уже было сделано в коде, если фича частично реализована;
- что временно отключено или закомментировано, если применимо;
- какие документы нужно обновить при возврате к задаче;
- с какого места продолжать разработку.
3. Агент не должен начинать реализацию файлов из этой папки без явной просьбы пользователя.
## Текущие планы
### Ближайшие
- `near/2026-05-25_1106_telegram_agent_players.md` - разрешённые пользователи Telegram для агента, отдельные папки игроков, персональные истории и публикация краткого вопроса/ответа в общий канал.
- `near/2026-05-25_1106_wallet_topup_solana_arweave.md` - пополнение Solana и Arweave через внешний сервис покупки с подсказкой и копированием адреса.
- `near/2026-05-25_1106_channels_my_create_button.md` - поправить экран каналов: создание канала показывать только в разделе `Мои каналы`.
### Среднесрочные
- `medium/2026-05-24_1140_репосты_в_каналах_и_тредах.md` - репосты в каналах и тредах.
- `medium/2026-05-25_1106_shine_balance_wallet.md` - кошелёк и пополнение баланса сияния через блокчейн.
### Дальнее будущее
- Сейчас задач нет.
+5
View File
@@ -0,0 +1,5 @@
# Дальнее будущее
Сейчас в этом горизонте нет активных идей.
Сюда переносить задачи, у которых нет понятного срока возврата и которые не нужно учитывать в ближайшем или среднесрочном планировании.
@@ -3,6 +3,12 @@
- Статус:
`future`
- Горизонт:
`medium`
- Ориентир:
1-2 месяца
- Решение от 2026-05-24:
Репосты временно убраны из активной разработки. Фича уже была частично реализована, но не доведена до финальной проверки. Чтобы она не мешала запуску проекта, пользовательский вход в репосты отключён в UI, а сервер больше не принимает новые `TEXT_REPOST` через `AddBlock`.
@@ -0,0 +1,62 @@
# Кошелёк и пополнение баланса сияния
- Горизонт:
`medium`
- Ориентир:
среднесрочно
- Статус:
`proposal`
## Кратко
Нужно добавить кошелёк для внутреннего баланса сияния и пополнение этого баланса через блокчейн-логику проекта. Задача связана с регистрацией пользователя и будущим учётом баланса.
## Предполагаемый сценарий
1. Пользователь регистрируется и получает/подключает нужные кошельки.
2. В интерфейсе появляется баланс сияния.
3. Пользователь открывает пополнение баланса сияния.
4. Система создаёт или принимает блокчейн-операцию пополнения.
5. После подтверждения баланса UI обновляет значение.
## Что нужно продумать
1. Что именно является единицей баланса сияния.
2. Где хранится состояние баланса: в существующем блокчейне SHiNE, Solana-модуле или комбинированно.
3. Какая операция отвечает за пополнение.
4. Нужно ли делать отдельную регистрацию кошелька сияния или использовать существующую регистрацию пользователя.
5. Как баланс восстанавливается после перезагрузки клиента.
6. Какие права нужны для пополнения и списания.
7. Нужна ли история операций баланса.
## Вопросы перед реализацией
1. Пополнение баланса сияния должно идти через основной блокчейн SHiNE или через Solana-программу.
2. Нужна ли конвертация из SOL/AR в сияние.
3. Кто может выпускать или начислять сияние.
4. Нужно ли поддерживать перевод сияния между пользователями.
5. Нужны ли лимиты, комиссии или статусы подтверждения.
6. Какой экран должен показывать баланс: регистрация, профиль, кошелёк или отдельная страница.
7. Нужно ли отображать неподтверждённый баланс отдельно от подтверждённого.
## Важное ограничение
Если для баланса сияния потребуется новый формат блокчейн-блока или изменение существующего формата, перед реализацией нужно отдельно предупредить пользователя и получить явное подтверждение на изменение формата блокчейна.
Если потребуется новый серверный API или изменение существующих `op`, перед реализацией нужно отдельно предупредить пользователя и получить явное подтверждение на изменение API.
## Документы, которые обновить при реализации
- `Dev_Docs/Blockchain/`, если появятся или изменятся блоки баланса.
- `Dev_Docs/Blockchain/CHANGELOG.md`, если меняется блокчейн-формат.
- `Dev_Docs/API/`, если меняется серверный API.
- `Dev_Docs/Pending_Features/` - добавить файл ручной проверки после реализации.
- Документацию Solana-регистрации, если баланс будет связан с Solana-модулем.
## Минимальная проверка в будущем
1. Новый пользователь видит корректный начальный баланс.
2. Пополнение создаёт правильную операцию.
3. Баланс обновляется после подтверждения.
4. После перезагрузки UI баланс остаётся корректным.
5. Ошибочные или повторные операции не начисляют баланс дважды.
@@ -0,0 +1,67 @@
# Кнопка создания канала только в разделе «Мои каналы»
- Горизонт:
`near`
- Ориентир:
сегодня/завтра
- Статус:
`proposal`
## Кратко
Нужно поправить экран каналов: в общем списке должна быть простая надпись `Каналы` и переход к своим каналам, а кнопка создания канала должна появляться только в разделе `Мои каналы`.
## Ожидаемый сценарий
### Общий список
1. Заголовок: `Каналы`.
2. Есть кнопка перехода в свои каналы: `Мои` или `Мои каналы`.
3. Кнопки создания канала в общем списке нет.
### Раздел своих каналов
1. Заголовок: `Мои каналы`.
2. Слева есть переход назад или кнопка `Ко всем каналам`.
3. Справа от заголовка или в правой части шапки есть маленькая кнопка `+` для создания канала.
4. Кнопка создания канала работает только здесь.
## Что нужно сделать
1. Найти компонент/страницу списка каналов в UI.
2. Разделить состояние общего списка и списка своих каналов.
3. Убрать кнопку создания канала из общего списка.
4. Добавить или оставить кнопку создания канала в `Мои каналы`.
5. Поправить заголовки:
- общий список: `Каналы`;
- свой список: `Мои каналы`.
6. Проверить расположение кнопок на мобильном и desktop-экране.
7. Убедиться, что существующее создание канала не ломается.
## Вопросы перед реализацией
1. Какой текст кнопки перехода лучше: `Мои`, `Мои каналы` или иконка с подписью.
2. Нужна ли отдельная стрелка назад, если уже есть кнопка `Ко всем каналам`.
3. Кнопка `+` должна быть иконкой без текста или `Создать канал`.
4. Нужно ли сохранять выбранный режим после перезагрузки страницы.
## Риски и ограничения
- Нужно проверить, не завязаны ли текущие обработчики создания канала на общий экран.
- На узких экранах заголовок, кнопка назад и `+` могут конфликтовать по ширине.
- Если есть роутинг по query/state, важно не сломать прямые переходы.
## Документы, которые обновить при реализации
- `Dev_Docs/Pending_Features/` - добавить файл ручной проверки после реализации.
- UI-документацию, если в проекте есть отдельное описание экранов каналов.
## Минимальная проверка
1. В общем списке виден заголовок `Каналы`.
2. В общем списке нет кнопки создания канала.
3. Переход в `Мои каналы` работает.
4. В `Мои каналы` видна кнопка создания канала.
5. Создание канала из `Мои каналы` работает.
6. Возврат ко всем каналам работает.
7. На мобильном экране шапка не ломается.
@@ -0,0 +1,94 @@
# Telegram-агент для разрешённых игроков
- Горизонт:
`near`
- Ориентир:
сегодня/завтра
- Статус:
`proposal`
## Кратко
Нужно расширить `SHiNE-agent-bot-coder`, чтобы агент мог принимать личные сообщения от заранее разрешённых пользователей, вести по каждому отдельную рабочую папку и историю, помогать им с обсуждениями/документами без изменения кода, а краткий результат публиковать в общий канал.
## Пользовательский сценарий
1. Разрешённый пользователь пишет агенту в личные сообщения текстом или голосом.
2. Голосовое сообщение распознаётся так же, как сейчас распознаются voice/audio-задачи.
3. Сервис определяет пользователя по разрешённому списку логинов.
4. Для пользователя используется отдельная папка в `Players/`.
5. Codex запускается с системным контекстом: от имени какого человека он работает, где лежит его папка, какие у него локальные инструкции.
6. Агент может читать код и документацию проекта, но писать должен только в папку этого пользователя, если нет отдельного согласования на изменение общего проекта.
7. После ответа пользователю агент отправляет в общий канал короткую сводку двумя сообщениями или двумя блоками: вопрос пользователя и полученный ответ.
8. Команда `/new` или `New` сбрасывает только сессию этого пользователя.
## Предлагаемая структура
- `Players/`
- `Ivan/`
- `AGENTS.md`
- `history/`
- `files/`
- `Sergey/`
- `AGENTS.md`
- `history/`
- `files/`
- `Milana/`
- `AGENTS.md`
- `history/`
- `files/`
Имена папок можно уточнить после получения точных Telegram-логинов.
## Что нужно сделать
1. Добавить конфигурацию разрешённых Telegram-пользователей.
2. Описать соответствие `telegram username -> имя игрока -> папка`.
3. Создавать или использовать отдельную историю диалога для каждого игрока.
4. Поддержать личные сообщения от разрешённых пользователей.
5. Запретить постановку задач от неизвестных пользователей.
6. Для групп/каналов оставить текущую логику: команды Айдара имеют приоритет.
7. При запуске Codex для игрока добавлять отдельный системный контекст:
- имя пользователя;
- путь к его папке;
- правило записи только в эту папку;
- путь к персональному `AGENTS.md`.
8. После ответа игроку отправлять краткую сводку в общий канал.
9. Поддержать `/new`/`New` как сброс только персональной сессии игрока.
10. Добавить защиту от случайного изменения общего кода в режиме игрока.
## Вопросы перед реализацией
1. Точные Telegram-логины Ивана, Сергея и Миланы.
2. Какой общий канал использовать для сводок: текущий `@shine_writing` или отдельный чат.
3. Нужно ли отправлять в общий канал полный текст вопроса/ответа или краткую выжимку.
4. Нужно ли пересылать вложения игроков в общий канал или только текстовые сводки.
5. Разрешить ли игрокам читать все документы проекта, включая технические заметки деплоя.
6. Что делать, если пользователь просит изменить код: отказать, создать предложение в своей папке или просить подтверждение Айдара.
7. Нужны ли русские имена папок (`Иван`, `Сергей`, `Милана`) или ASCII-имена (`Ivan`, `Sergey`, `Milana`).
8. Нужно ли хранить истории игроков в общей папке сервиса или внутри `Players/<name>/history/`.
## Риски и ограничения
- Нужно аккуратно разделить режим Айдара и режим игрока, чтобы игроки не могли случайно запустить изменение общего кода.
- Нужно не смешать истории разных пользователей.
- Нужно ограничить публикацию в общий канал, чтобы не утекали личные или слишком длинные ответы.
- Нужна проверка Telegram-идентификации: username может меняться, поэтому желательно хранить и `user_id`.
## Документы, которые обновить при реализации
- `SHiNE-agent-bot-coder/AGENTS.md`
- `SHiNE-agent-bot-coder/AGENT.md`
- `SHiNE-agent-bot-coder/README.md`
- `Dev_Docs/deploy/agent-bot-coder-local-systemd.md`, если появятся новые переменные окружения или настройки сервиса.
## Минимальная проверка
1. Айдар по-прежнему может ставить задачи из `@shine_writing`.
2. Неизвестный пользователь не ставит задачу в очередь.
3. Разрешённый игрок пишет личное текстовое сообщение и получает ответ.
4. Разрешённый игрок отправляет voice, оно распознаётся и обрабатывается.
5. История одного игрока не попадает в историю другого.
6. `/new` сбрасывает только историю текущего игрока.
7. Сводка вопрос/ответ появляется в общем канале.
8. В режиме игрока агент не пишет за пределы `Players/<name>/` без отдельного подтверждения.
@@ -0,0 +1,71 @@
# Пополнение Solana и Arweave через внешний сервис покупки
- Горизонт:
`near`
- Ориентир:
сегодня/завтра
- Статус:
`proposal`
## Кратко
Нужно добавить удобное пополнение кошельков на экране регистрации/кошелька: для Solana и Arweave дать отдельные действия `Пополнить`, которые ведут на международный сервис покупки криптовалюты с карты и помогают пользователю скопировать адрес кошелька.
## Пользовательский сценарий
1. Пользователь видит адрес кошелька Solana или Arweave.
2. Нажимает `Пополнить`.
3. Открывается промежуточное окно с инструкцией:
- сейчас пользователь перейдёт на страницу покупки/пополнения;
- нужно указать или проверить адрес кошелька;
- после оплаты нужно закрыть внешнюю страницу и вернуться назад;
- Solana обычно приходит быстро, ориентир 10-15 секунд после подтверждения сети;
- Arweave может идти дольше, точное время нужно уточнить по выбранному сервису.
4. В окне есть кнопки:
- `Скопировать адрес и перейти`;
- `Перейти без копирования`.
5. Для Solana и Arweave используются разные окна/инструкции и, возможно, разные внешние ссылки.
## Что нужно сделать
1. Найти текущий экран, где показываются кошельки при регистрации и пополнении.
2. Найти текущую ссылку покупки Arweave, если она уже есть в UI.
3. Выбрать международный сервис покупки Solana с карты, не российский.
4. Проверить, поддерживает ли сервис deep link с предзаполненным адресом кошелька.
5. Если deep link невозможен, реализовать промежуточное окно с копированием адреса.
6. Добавить отдельные действия для Solana и Arweave.
7. Сделать текст инструкции коротким и понятным.
8. Проверить, что адрес копируется в буфер обмена в браузере.
9. Проверить мобильный сценарий и desktop-сценарий.
## Вопросы перед реализацией
1. Какой сервис покупки Solana использовать: тот же провайдер, что для Arweave, или другой международный on-ramp.
2. Нужно ли разрешать покупку только SOL или также USDC/SPL-токены на Solana.
3. Где именно показывать кнопку `Пополнить`: только регистрация, настройки кошелька или оба места.
4. Нужно ли показывать предупреждение о комиссиях и стороннем сервисе.
5. Нужно ли открывать внешнюю страницу в новой вкладке или в текущем окне.
6. Нужно ли логировать факт нажатия `Пополнить` на сервере.
7. Какой точный текст использовать для времени прихода Arweave.
## Риски и ограничения
- On-ramp-сервисы меняют ссылки и параметры, поэтому deep link нужно проверять перед реализацией.
- Clipboard API может требовать HTTPS и пользовательский жест.
- Нельзя обещать точное время поступления средств: лучше писать ориентир и зависимость от сети/провайдера.
- Внешний сервис может быть недоступен в отдельных странах или для отдельных карт.
## Документы, которые обновить при реализации
- Документацию UI/кошельков, если такая есть.
- `Dev_Docs/Pending_Features/` - добавить файл ручной проверки после реализации.
- `Dev_Docs/API/`, только если появится новый серверный API или логирование.
## Минимальная проверка
1. На Solana-кошельке открывается правильное окно пополнения.
2. Кнопка `Скопировать адрес и перейти` копирует Solana-адрес и открывает внешний сервис.
3. Кнопка `Перейти без копирования` открывает внешний сервис без копирования.
4. Аналогичный сценарий работает для Arweave.
5. На мобильном экране текст и кнопки не перекрываются.
6. Возврат назад в приложение не ломает состояние регистрации/кошелька.
@@ -0,0 +1,26 @@
# Solana: init регистрации + деплой обязательных программ
- дата: 2026-05-24 20:35 (Europe/Moscow)
- статус: `pending`
## Кратко
Добавлена dev-страница в UI для вызова `init_users_economy_config` программы `shine_users` через подключённый кошелёк Phantom.
Задеплоены и зафиксированы адреса двух обязательных программ регистрации: `shine_users` и `shine_login_guard`.
## Что проверять вручную
1. Открыть UI и перейти в `Настройки разработчика`.
2. Нажать `Solana: init регистрации`.
3. Подключить Phantom devnet-кошелёк.
4. Выполнить `init_users_economy_config`.
5. Проверить отображение статуса и хэша транзакции.
6. Повторно нажать init и убедиться, что корректно показывается "уже инициализировано".
7. Выполнить тестовую регистрацию пользователя и убедиться, что CPI-вызов `shine_login_guard` не падает.
## Ожидаемый результат
- Первая транзакция выполняется успешно (если PDA ещё не создан).
- Вторая попытка возвращает ожидаемую ошибку о повторной инициализации.
- UI не падает, статус понятный, Program ID отображается корректно.
- Регистрация пользователя проходит с подключённым `shine_login_guard`.
@@ -0,0 +1,26 @@
# Отчёт private-запросов агента в группу
## Что сделано
После успешной обработки задачи из личного чата Айдара Telegram-бот агента отправляет итоговую копию в группу `@shine_writing`:
- первым сообщением исходный запрос;
- вторым сообщением, reply на первое, финальный ответ Codex.
Промежуточные статусы выполнения в группу не дублируются.
## Что проверять
1. Отправить боту личный текстовый запрос.
2. Дождаться полного ответа в личном чате.
3. Проверить, что в `@shine_writing` появилось сообщение с запросом.
4. Проверить, что итоговый ответ опубликован reply на это сообщение.
5. Отправить личный voice-запрос и проверить, что в отчёте есть распознанный текст.
## Ожидаемый результат
Личный чат работает как раньше, а группа получает только итоговую пару сообщений по завершённой задаче.
## Статус
pending
@@ -0,0 +1,23 @@
# Отчёт voice/audio-запросов с исходным файлом
## Краткое описание
Публичный отчёт по приватным voice/audio-запросам агента должен отправлять исходный Telegram voice/audio-файл с подписью, где указан распознанный текст. `file_id` не должен показываться пользователям в тексте отчёта.
## Что проверить
1. Отправить боту приватный voice-запрос от Айдара.
2. Дождаться обработки Codex.
3. Проверить группу/канал публичных отчётов.
4. Повторить сценарий для audio-файла, если он используется.
## Ожидаемый результат
- В публичном отчёте появляется исходное голосовое/audio-сообщение.
- В подписи к нему есть распознанный текст.
- В отчёте нет строки `Голосовой file_id` и самого `file_id`.
- Итоговый ответ Codex отправляется ответом на сообщение с исходным файлом.
## Статус
pending
@@ -0,0 +1,19 @@
# Улучшенная обработка длинных voice/audio
## Что сделано
- Увеличены и вынесены в `.env` тайм-ауты скачивания Telegram-файла и OpenAI-распознавания.
- Добавлены подробные логи стадий распознавания: старт, скачивание, размер файла, завершение, причина ошибки.
- Ошибки voice/audio теперь показываются пользователю как ошибка распознавания с понятной причиной.
## Как проверять
- Отправить короткое голосовое сообщение и убедиться, что оно распознаётся и передаётся в Codex.
- Отправить длинное голосовое сообщение и убедиться, что сервис не падает на прежнем коротком тайм-ауте.
- Смоделировать ошибку распознавания или временно указать неверный OpenAI-ключ и проверить текст ошибки в Telegram.
## Ожидаемый результат
- При успешном распознавании пользователь видит распознанный текст и задача уходит в Codex.
- При ошибке пользователь видит, что не удалось именно распознать voice/audio, и получает конкретную причину.
- В логах сервиса видны стадия и техническая причина сбоя.
## Статус
pending
+3 -3
View File
@@ -51,7 +51,7 @@
| N | Поле | Тип | Размер | Правило |
|---|------|-----|--------|---------|
| 1 | `magic` | bytes | 5 | Всегда `SHiNE`. |
| 2 | `format_major` | `u8` | 1 | Для нового формата: `2`. |
| 2 | `format_major` | `u8` | 1 | Для первого формата: `1`. |
| 3 | `format_minor` | `u8` | 1 | Для первой версии нового формата: `0`. |
| 4 | `record_len` | `u16` | 2 | Длина полезной записи от `magic` до `signature` включительно, без padding. |
| 5 | `created_at_ms` | `u64` | 8 | Время создания записи, Unix time в миллисекундах. Не меняется. |
@@ -63,7 +63,7 @@
После первых 9 полей идет набор типизированных блоков:
```text
UserPdaRecordV2
UserPdaRecordV1
- fixed_header: поля 1..9
- blocks_count: u8
- blocks: TypedBlock[blocks_count]
@@ -89,7 +89,7 @@ UserPdaRecordV2
Правила:
- неизвестный `block_type` в `format_major = 2` считается ошибкой;
- неизвестный `block_type` в `format_major = 1` считается ошибкой;
- обязательные блоки: `RootKeyBlock`, `DeviceKeyBlock`, `BlockchainRegistryBlock`;
- необязательные блоки: `ServerProfileBlock`, `AccessServersBlock`, `TrustedStateBlock`;
- каждый обязательный блок должен встречаться ровно один раз;
+166
View File
@@ -0,0 +1,166 @@
# Архитектура Solana-программ SHiNE
Документ описывает рабочую архитектуру Solana-части SHiNE: три Anchor-программы, DAO, ключи управления, PDA-счета и движение денег.
Это архитектурная справка. Она не меняет код, формат PDA-записи пользователя, серверный API или формат блокчейна SHiNE.
Статус: актуализировано по коду `shine-solana/shine/programs/*` на 2026-05-25.
Связанные документы:
- `Dev_Docs/Инициализация_Solana_регистрации/README.md` — single source of truth по деплою и первичной инициализации регистрации пользователей.
- `shine-solana/shine/doc/SHiNE-user-format-v.1.0.md` — точный формат `user_pda` для `shine_users`.
- `shine-solana/shine/doc/FUNDS_FLOW.md` — короткая справка по денежным потокам внутри Solana-модуля.
## Кратко
В Solana-модуле сейчас три основные программы:
1. `shine_login_guard` — проверяет логин и возвращает класс логина: обычный, premium или trademark.
2. `shine_users` — создает и обновляет пользовательскую PDA-запись, проверяет подписи и берет оплату за регистрацию/увеличение лимита.
3. `shine_payments` — принимает входящий поток средств в `inflow_vault`, ведет очереди тикетов, позволяет DAO выдавать лимиты менеджерам и выполняет выплаты.
DAO в текущем виде не является отдельной Anchor-программой SHiNE внутри `programs/`. Это управляющая модель поверх кошельков, governance-скриптов и authority-адресов. Для проектирования ее удобно считать отдельным управляющим блоком: DAO голосует, назначает управляющие ключи, управляет казной и вызывает защищенные методы второй и третьей программ.
## Общая схема
Редактируемая Mermaid-схема находится в [schemes/architecture.mmd](schemes/architecture.mmd).
Картинки:
- [schemes/architecture.svg](schemes/architecture.svg)
- [schemes/architecture.png](schemes/architecture.png)
## Программы и функции
| Блок | Папка/имя | Текущие функции из кода | Основной смысл |
| --- | --- | --- | --- |
| 1 | `shine_login_guard` | `classify_login` | Проверка логина перед регистрацией. |
| 2 | `shine_users` | `init_users_economy_config`, `update_users_economy_config`, `create_user_pda`, `update_user_pda` | Регистрация пользователя, обновление записи, экономика лимита. |
| 3 | `shine_payments` | `init`, `update_coef_limit`, `grant_manager_limits`, `buy_ticket`, `buy_ticket_usd`, `buy_ticket_sol`, `manager_add_ticket`, `step_payout`, `change_ticket_recipient` | Vault, билеты, очереди, выплаты, DAO-настройки, лимиты менеджеров. |
| DAO | governance/authority | Вызовы через governance и управляющие ключи | Управление правами, казной, настройками и будущими обновлениями программ. |
## Актуальные program id
Актуальные адреса заданы одновременно в `Anchor.toml`, `declare_id!` программ и `programs/common/src/deploy_config.rs`:
| Программа | Program ID |
| --- | --- |
| `shine_login_guard` | `3xkopA7cXagxzMFrKdv3NCBfV6BKiRJCk69kr27M2sRo` |
| `shine_users` | `FZS1YctoeEhCkZ5VTjsysUFAXR8CqxYztcLboXcg2Rpm` |
| `shine_payments` | `m48pWRGWrMj3TEHjuU4zsp5Gju4e7ZaPovk8RcVt7kR` |
Если эти адреса меняются, нужно синхронно обновить:
1. `shine-solana/shine/Anchor.toml`
2. `declare_id!` в `programs/*/src/lib.rs`
3. `programs/common/src/deploy_config.rs`
4. UI/серверные константы, перечисленные в `Dev_Docs/Инициализация_Solana_регистрации/README.md`
## Ключи и authority
Для удобного понимания на старте можно считать, что есть четыре группы ключей:
1. `key_1` / authority программы `shine_login_guard`.
- Сейчас программа только классифицирует логин.
- На первом этапе ее можно оставить под отдельным ключом.
- В будущем право обновления можно передать DAO.
2. `key_2` / authority программы `shine_users`.
- Отвечает за деплой/upgrade второй программы.
- Защищенное обновление economy-конфига в коде уже проверяет `DAO_AUTHORITY`.
- В целевой модели upgrade-authority второй программы нужно передать DAO.
3. `key_3` / authority программы `shine_payments`.
- Отвечает за деплой/upgrade третьей программы.
- Защищенные методы `update_coef_limit` и `grant_manager_limits` проверяют `dao_wallet` из `ConfigState`.
- В целевой модели upgrade-authority третьей программы нужно передать DAO.
4. DAO-ключи.
- Это управляющие кошельки/токены/realm governance.
- DAO может добавлять и отзывать управляющие ключи по голосованию.
- DAO-казна получает деньги от покупки тикетов и DAO-часть выплат из `inflow_vault`.
Адреса program id сейчас берутся из `programs/common/src/deploy_config.rs`. Для production/devnet можно подбирать vanity-адреса с понятным началом вроде `SHi...`, но это отдельная операция генерации ключей и деплоя.
## Счета и PDA
Постоянные PDA и счета:
1. `shine_users`
- `user_pda` — пользовательская запись по seed `login=<login>`, создается для каждого логина.
- `users_economy_config_pda` — общие параметры экономики регистрации и лимита.
2. `shine_payments`
- `config_pda` — хранит `dao_wallet` и адрес `inflow_vault`.
- `coef_limit_pda` — хранит коэффициент выплат, лимит очереди и награду вызывающему `step_payout`.
- `queues_pda` — агрегаты очередей выплат.
- `inflow_vault_pda` — PDA-вольт, куда `shine_users` переводит оплату регистрации и увеличения лимита.
- `ticket_pda` — отдельная PDA-запись тикета на каждую покупку/менеджерскую выдачу.
- `manager_allowance_pda` — PDA лимитов конкретного менеджера.
3. DAO
- `dao_wallet` / treasury — казна DAO.
- governance-аккаунты DAO — realm, governance, proposal/vote records и связанные аккаунты SPL Governance, если используется эта модель.
## Правило разделения с основным сервером
Solana-модуль лежит в основном репозитории как отдельная папка `shine-solana/shine/`, но не подключается автоматически к сборке или деплою основного сервера SHiNE. Команды `deployServer` и `deployUI` не должны деплоить Anchor-программы. Solana build/deploy выполняется отдельно из папки `shine-solana/shine/` по локальным правилам модуля.
## Движение денег
Основные потоки:
1. Регистрация пользователя через `shine_users::create_user_pda`.
- Платит `signer`.
- Деньги идут в `shine_payments::inflow_vault_pda`.
- Сумма состоит из регистрационной комиссии и оплаты дополнительного лимита.
2. Увеличение лимита через `shine_users::update_user_pda`.
- Платит `signer`.
- Деньги идут в тот же `inflow_vault_pda`.
- Сумма равна оплате дополнительного лимита.
3. Покупка тикета через `shine_payments::buy_ticket*`.
- Платит покупатель.
- Деньги сразу идут в `dao_wallet`.
- Одновременно создается тикет на выплату.
4. Выплата через `shine_payments::step_payout`.
- Вызвать может любой подписант.
- Деньги берутся из `inflow_vault_pda`.
- Часть идет получателю тикета.
- Часть идет в `dao_wallet`.
- Небольшая награда идет вызвавшему шаг выплат.
- Если очереди пустые, весь доступный остаток `inflow_vault_pda` переводится в DAO.
## Передача прав DAO
Минимальная целевая модель:
1. `shine_login_guard`
- Пока оставить на отдельном ключе `key_1`.
- Передачу DAO сделать позже, когда логика premium/trademark стабилизируется.
2. `shine_users`
- Economy-настройки уже должны обновляться DAO-authority.
- Upgrade-authority программы после проверки можно передать DAO.
3. `shine_payments`
- DAO уже управляет настройками выплат и лимитами менеджеров через `dao_wallet`.
- Upgrade-authority программы после проверки можно передать DAO.
4. DAO
- Управляет казной.
- Принимает решения голосованием.
- Добавляет/отзывает управляющие ключи.
- Вызывает защищенные методы второй и третьей программ.
- В будущем может принять управление первой программой.
## Детальные файлы
- [details/shine_login_guard.md](details/shine_login_guard.md)
- [details/shine_users.md](details/shine_users.md)
- [details/shine_payments.md](details/shine_payments.md)
- [details/shine_dao.md](details/shine_dao.md)
- [details/accounts_and_money_flow.md](details/accounts_and_money_flow.md)
@@ -0,0 +1,110 @@
# Счета, ключи и движение денег
## Кратко
В архитектуре есть три типа объектов:
1. Ключи программ и DAO.
2. PDA-счета состояния.
3. Денежные счета, через которые проходят SOL/lamports.
## Ключи
Минимальный набор для понимания:
1. `key_1` — deploy/upgrade authority `shine_login_guard`.
2. `key_2` — deploy/upgrade authority `shine_users`.
3. `key_3` — deploy/upgrade authority `shine_payments`.
4. `DAO_AUTHORITY` — адрес, который имеет право менять защищенные настройки.
5. `DAO_TREASURY_WALLET` / `dao_wallet` — казна DAO.
6. `manager_wallet` — кошелек менеджера, которому DAO выдает лимиты на создание тикетов.
7. `user root_key` — корневой ключ пользователя для подписи пользовательской записи.
8. `user device_key` — ключ устройства пользователя.
9. `server_key` — ключ сервера пользователя, если пользователь является сервером.
Текущие адреса из `programs/common/src/deploy_config.rs`:
| Роль | Адрес |
| --- | --- |
| `SHINE_LOGIN_GUARD_PROGRAM_ID` | `3xkopA7cXagxzMFrKdv3NCBfV6BKiRJCk69kr27M2sRo` |
| `SHINE_USERS_PROGRAM_ID` | `FZS1YctoeEhCkZ5VTjsysUFAXR8CqxYztcLboXcg2Rpm` |
| `SHINE_PAYMENTS_PROGRAM_ID` | `m48pWRGWrMj3TEHjuU4zsp5Gju4e7ZaPovk8RcVt7kR` |
| `DAO_AUTHORITY` | `FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P` |
| `DAO_TREASURY_WALLET` | `FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P` |
## Постоянные PDA
`shine_users`:
- `user_pda` — создается для каждого логина, seed `login=` + normalized login.
- `users_economy_config_pda` — один PDA с экономикой регистрации, seed `shine_users_economy_config`.
`shine_payments`:
- `config_pda` — один PDA конфига, seed `shine_payments_config`.
- `coef_limit_pda` — один PDA коэффициента/лимита/награды, seed `shine_payments_coef_limit`.
- `queues_pda` — один PDA агрегатов очередей, seed `shine_payments_queues`.
- `inflow_vault_pda` — один PDA-вольт входящих средств, seed `shine_payments_inflow_vault`.
- `ticket_pda` — много PDA, по одному на тикет, seed `shine_payments_q1_ticket` или `shine_payments_q2_ticket` + индекс.
- `manager_allowance_pda` — много PDA, по одному на менеджера, seed `shine_p_manager_allow` + адрес менеджера.
## Денежные потоки
### Регистрация
```text
user signer -> shine_users::create_user_pda -> shine_payments::inflow_vault_pda
```
Состав платежа:
- регистрационная комиссия;
- оплата `additional_limit`.
### Увеличение лимита
```text
user signer -> shine_users::update_user_pda -> shine_payments::inflow_vault_pda
```
Состав платежа:
- только оплата `additional_limit`.
### Покупка тикета
```text
buyer signer -> shine_payments::buy_ticket* -> dao_wallet
```
При этом создается `ticket_pda`, но деньги в `inflow_vault_pda` на этом шаге не идут.
### Выплата
```text
shine_payments::inflow_vault_pda -> ticket_recipient_wallet
shine_payments::inflow_vault_pda -> dao_wallet
shine_payments::inflow_vault_pda -> step_payout caller
```
Если очереди пустые:
```text
shine_payments::inflow_vault_pda -> dao_wallet
```
## Что нужно создать на старте
Минимально:
1. Три program id для `shine_login_guard`, `shine_users`, `shine_payments`.
2. Три upgrade-authority ключа или один временный deploy-ключ с четким планом передачи прав.
3. DAO authority/treasury.
4. `users_economy_config_pda`.
5. `shine_payments` PDA: `config_pda`, `coef_limit_pda`, `queues_pda`, `inflow_vault_pda`.
Динамически будут создаваться:
- `user_pda` на каждого пользователя;
- `ticket_pda` на каждый тикет;
- `manager_allowance_pda` на каждого менеджера.
@@ -0,0 +1,74 @@
# SHiNE DAO
## Кратко
DAO — управляющий слой Solana-части SHiNE. В текущем коде это не отдельная Anchor-программа в `programs/`, а модель управления через DAO-кошелек, DAO-authority, governance-скрипты и будущую передачу upgrade-authority программ.
## Что DAO должно уметь
1. Управлять казной.
- Принимать средства на `dao_wallet`.
- Выплачивать средства со счета DAO по решениям голосования.
2. Управлять настройками `shine_users`.
- Обновлять регистрационную комиссию.
- Обновлять цену шага лимита.
- Обновлять стартовый бонус лимита.
3. Управлять настройками `shine_payments`.
- Обновлять коэффициент выплат.
- Обновлять лимит очереди.
- Обновлять награду за вызов `step_payout`.
4. Управлять менеджерами.
- Выдавать менеджеру лимит на добавление тикетов.
- Отдельно учитывать лимиты Q1 и Q2.
5. Управлять правами программ.
- Принять upgrade-authority `shine_users`.
- Принять upgrade-authority `shine_payments`.
- Позже принять upgrade-authority `shine_login_guard`, если это потребуется.
6. Управлять ключами DAO.
- Добавлять управляющие ключи.
- Отзывать или сжигать управляющие ключи.
- Делать это через голосование, а не вручную одним админом.
7. Фиксировать решения.
- Делать заявления/решения через governance-механику.
- Привязывать важные изменения к proposal/vote/execute.
## Текущие адреса управления
В общем deploy-конфиге сейчас есть два важных адреса:
- `DAO_AUTHORITY` — используется `shine_users` для проверки права менять economy-конфиг.
- `DAO_TREASURY_WALLET` — используется `shine_payments` как `dao_wallet`.
Сейчас они могут совпадать. В целевой DAO-модели их лучше рассматривать как разные роли:
- authority/governance signer — кто имеет право исполнять управленческие инструкции;
- treasury wallet — счет, куда приходят деньги DAO.
## Передача прав
Рекомендуемый порядок:
1. Сначала стабилизировать и проверить `shine_users` и `shine_payments`.
2. Передать DAO право обновлять настройки, если оно еще не передано.
3. Передать DAO upgrade-authority второй и третьей программ.
4. Оставить `shine_login_guard` на отдельном ключе до стабилизации словарей и правил логинов.
5. После стабилизации решить отдельным голосованием, передавать ли первую программу DAO.
## Важное разделение
Есть два разных типа прав:
1. Право вызвать защищенную функцию программы.
- Например, `update_coef_limit` или `grant_manager_limits`.
- Проверяется внутри программы по `dao_wallet` или `DAO_AUTHORITY`.
2. Право обновить саму программу.
- Это upgrade-authority Solana ProgramData.
- Оно передается отдельной Solana-командой/DAO-транзакцией и не равно обычному PDA-счету.
@@ -0,0 +1,58 @@
# `shine_login_guard`
## Кратко
`shine_login_guard` — первая программа Solana-модуля SHiNE. Она проверяет логин перед регистрацией пользователя и возвращает класс логина.
Папка программы: `shine-solana/shine/programs/shine_login_guard/`.
## Текущая функция
1. `classify_login(login: String)`
- Нормализует логин.
- Проверяет длину и допустимые символы.
- Сравнивает части логина со словарями premium/trademark.
- Возвращает результат через `set_return_data`.
Классы результата:
- `0` — обычный логин, регистрацию можно продолжать.
- `1` — premium-логин.
- `2` — trademark-логин, нужна отдельная проверка/разрешение.
## Правила нормализации и классификации
Текущая логика из `programs/shine_login_guard/src/lib.rs`:
- пустой логин или логин длиннее 20 символов получает класс `premium`;
- `_` при нормализации удаляется;
- допустимы только ASCII-буквы и цифры, остальные символы дают класс `premium`;
- после удаления `_` результат приводится к нижнему регистру;
- логины длиной 7 символов или меньше считаются `premium`;
- логин разбивается максимум на 3 словарных фрагмента;
- если среди найденных фрагментов есть trademark-слово, результат `trademark`;
- если найдены только premium-слова, результат `premium`;
- если разбиение по словарям не найдено, результат `free`.
Словари собираются на этапе build из файлов:
- `programs/shine_login_guard/src/dictionaries/premium/*.txt`
- `programs/shine_login_guard/src/dictionaries/trademarks/*.txt`
## Роль в общей схеме
`shine_users::create_user_pda` вызывает `shine_login_guard` через CPI и продолжает регистрацию только если логин получил класс `0`.
## Ключи и управление
На старте удобно считать, что у программы есть отдельный управляющий ключ `key_1`.
Текущая рекомендация:
- пока оставить `shine_login_guard` под отдельным ключом;
- не передавать ее DAO до стабилизации правил premium/trademark;
- позже можно передать upgrade-authority DAO, чтобы изменения словарей и правил проходили через голосование.
## Счета
Собственных постоянных PDA-счетов у программы сейчас нет. Для проверки нужен только подписант транзакции в `ClassifyLogin`.
@@ -0,0 +1,173 @@
# `shine_payments`
## Кратко
`shine_payments` — третья программа Solana-модуля SHiNE. Она отвечает за vault входящих средств, DAO-казну, покупку тикетов, менеджерские лимиты, очереди выплат и пошаговое исполнение выплат.
Папка программы: `shine-solana/shine/programs/shine_payments/`.
## Текущие функции
1. `init`
- Создает основные PDA: `config_pda`, `coef_limit_pda`, `queues_pda`, `inflow_vault_pda`.
- Записывает `dao_wallet` и стартовые параметры выплат.
2. `update_coef_limit`
- Обновляет коэффициент выплаты, лимит очереди и награду вызвавшему `step_payout`.
- Требует подпись DAO-кошелька из `ConfigState`.
3. `grant_manager_limits`
- DAO выдает менеджеру лимиты на создание тикетов в очередях Q1/Q2.
- Создает или обновляет `manager_allowance_pda`.
4. `buy_ticket`
- Покупка тикета с суммой в lamports, пересчетом через Pyth SOL/USD.
5. `buy_ticket_usd`
- Покупка тикета от USD-центов с защитой по максимальному платежу в lamports.
6. `buy_ticket_sol`
- Покупка тикета в lamports с проверкой минимального ожидаемого USD-эквивалента.
7. `manager_add_ticket`
- Менеджер создает тикет за счет выданного ему DAO-лимита.
8. `step_payout`
- Любой подписант может вызвать шаг выплат.
- Программа выплачивает следующий тикет, DAO-часть и награду вызывающему.
9. `change_ticket_recipient`
- Текущий получатель тикета может поменять адрес получателя, если тикет еще не следующий на выплату.
## Аргументы инструкций
`init` аргументов не принимает.
`update_coef_limit`:
- `coef_ppm: u64`
- `limit_usd_cents: u64`
- `call_reward_lamports: u64`
`grant_manager_limits`:
- `manager_wallet: Pubkey`
- `add_q1_usd_cents: u64`
- `add_q2_usd_cents: u64`
`buy_ticket`:
- `amount_lamports: u64`
- `recipient_wallet: Pubkey`
`buy_ticket_usd`:
- `amount_usd_cents: u64`
- `max_pay_lamports: u64`
- `recipient_wallet: Pubkey`
`buy_ticket_sol`:
- `amount_lamports: u64`
- `min_expected_usd_cents: u64`
- `recipient_wallet: Pubkey`
`manager_add_ticket`:
- `queue_id: u8` — только `1` или `2`
- `recipient_wallet: Pubkey`
- `payout_usd_cents: u64`
`change_ticket_recipient`:
- `new_recipient_wallet: Pubkey`
## Главные PDA
1. `config_pda`
- Seed: `shine_payments_config`.
- Хранит `dao_wallet` и `inflow_vault`.
- Размер PDA: `8 + 160` байт.
2. `coef_limit_pda`
- Seed: `shine_payments_coef_limit`.
- Хранит коэффициент выплат, лимит и награду `step_payout`.
- Размер PDA: `8 + 96` байт.
3. `queues_pda`
- Seed: `shine_payments_queues`.
- Хранит агрегаты очередей Q1/Q2.
- Размер PDA: `8 + 192` байт.
4. `inflow_vault_pda`
- Seed: `shine_payments_inflow_vault`.
- Принимает деньги от `shine_users`.
- Из него выполняются выплаты тикетам, DAO и вызывающему `step_payout`.
- Размер PDA: `8 + 32` байт.
5. `ticket_pda`
- Seed зависит от очереди и индекса тикета.
- Отдельная PDA-запись на каждый тикет.
- Q1 seed: `shine_payments_q1_ticket` + `ticket_index`.
- Q2 seed: `shine_payments_q2_ticket` + `ticket_index`.
- Размер PDA: `8 + 160` байт.
6. `manager_allowance_pda`
- Seed: `shine_p_manager_allow` + адрес менеджера.
- Хранит доступный лимит менеджера по Q1/Q2.
- Размер PDA: `8 + 128` байт.
## Текущие параметры
Параметры initial config из `programs/shine_payments/src/settings.rs`:
| Поле | Значение | Смысл |
| --- | --- | --- |
| `START_COEF_PPM` | `5_000_000` | коэффициент 5.0x в ppm-масштабе |
| `START_LIMIT_USD_CENTS` | `1_000_000` | стартовый лимит Q1: 10_000 USD |
| `START_CALL_REWARD_LAMPORTS` | `8_000_000` | награда вызвавшему `step_payout`, 0.008 SOL |
| `MAX_CALL_REWARD_LAMPORTS` | `10_000_000` | максимум награды, 0.01 SOL |
| `ORACLE_MAX_AGE_SECS` | `120` | максимальный возраст цены Pyth |
Для расчетов используется Pyth SOL/USD:
- feed id: `0xef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d`
- price update account: `7UVimffxr9ow1uXYxsr4LHAcV58mLzhmwaeKvJ1pjLiE`
## Деньги
Входы:
- из `shine_users` в `inflow_vault_pda` при регистрации и увеличении лимита;
- от покупателя тикета сразу в `dao_wallet` при `buy_ticket*`.
Выходы:
- из `inflow_vault_pda` получателю тикета;
- из `inflow_vault_pda` в `dao_wallet`;
- из `inflow_vault_pda` вызвавшему `step_payout`;
- если очереди пустые, весь доступный остаток `inflow_vault_pda` переводится в DAO.
## Очереди и выплаты
Выплаты идут строго пошагово:
- если есть невыплаченные Q1-тикеты, `step_payout` берет следующий Q1;
- если Q1 пустая, берется следующий Q2;
- для Q1 DAO-часть равна сумме тикета в USD;
- для Q2 DAO-часть равна двойной сумме тикета в USD;
- перед выплатой суммы пересчитываются из USD-центов в lamports по Pyth SOL/USD;
- если в `inflow_vault_pda` не хватает средств на тикет, DAO-часть и награду вызвавшему, шаг отклоняется.
`change_ticket_recipient` запрещает менять получателя у тикета, который является следующим на выплату.
## Ключи и управление
На старте удобно считать, что у программы есть отдельный управляющий ключ `key_3`.
Целевая модель:
- `update_coef_limit` вызывает DAO;
- `grant_manager_limits` вызывает DAO;
- upgrade-authority программы после проверки передается DAO;
- `step_payout` остается открытым для любого подписанта, чтобы выплаты не зависели от одного оператора.
@@ -0,0 +1,136 @@
# `shine_users`
## Кратко
`shine_users` — вторая программа Solana-модуля SHiNE. Она отвечает за создание и обновление пользовательской PDA-записи, проверку подписи записи, проверку логина через `shine_login_guard` и оплату регистрации/дополнительного лимита.
Папка программы: `shine-solana/shine/programs/shine_users/`.
## Текущие функции
1. `init_users_economy_config`
- Создает PDA с экономическими настройками пользователей.
- Записывает стартовую регистрационную комиссию, цену шага лимита и стартовый бонус лимита.
2. `update_users_economy_config`
- Обновляет экономические настройки.
- Требует подпись `DAO_AUTHORITY` из общего deploy-конфига.
3. `create_user_pda`
- Проверяет логин через `shine_login_guard`.
- Проверяет структуру полей пользователя.
- Проверяет подпись записи root-ключом пользователя.
- Создает `user_pda` по seed `login=<normalized_login>`.
- Переводит оплату регистрации и дополнительного лимита в `shine_payments::inflow_vault_pda`.
4. `update_user_pda`
- Проверяет неизменяемые поля пользователя.
- Проверяет `prev_hash`, новую подпись и новое состояние последнего блока.
- При необходимости расширяет PDA.
- Переводит оплату дополнительного лимита в `shine_payments::inflow_vault_pda`.
## Аргументы инструкций
`init_users_economy_config` аргументов не принимает.
`update_users_economy_config`:
- `registration_fee_lamports: u64`
- `lamports_per_limit_step: u64`
- `start_bonus_limit: u64`
`create_user_pda`:
- `login: String`
- `root_key: Pubkey`
- `created_at_ms: u64`
- `additional_limit: u64`
- `fields: UserMutableFields`
- `signature: Vec<u8>`
`update_user_pda`:
- `login: String`
- `root_key: Pubkey`
- `created_at_ms: u64`
- `updated_at_ms: u64`
- `version: u32`
- `prev_hash: Vec<u8>`
- `additional_limit: u64`
- `fields: UserMutableFields`
- `signature: Vec<u8>`
`UserMutableFields`:
- `device_key: Pubkey`
- `blockchain_public_key: Pubkey`
- `blockchain_name: String`
- `used_bytes: u64`
- `last_block_number: u32`
- `last_block_hash: Vec<u8>` — ровно 32 байта
- `last_block_signature: Vec<u8>` — ровно 64 байта
- `arweave_tx_id: String`
- `is_server: bool`
- `server_key: Pubkey`
- `server_address: String`
- `sync_servers: Vec<String>`
- `access_servers: Vec<String>`
- `trusted_count: u8`
## Главные PDA
1. `user_pda`
- PDA записи пользователя.
- Seed: `login=<normalized_login>`.
- Создается отдельно для каждого логина.
- Стартовый размер: `768` байт.
- При обновлении может расширяться через `realloc`, но один auto-realloc ограничен `10_000` байт.
2. `users_economy_config_pda`
- PDA с настройками экономики.
- Seed: `shine_users_economy_config`.
- Хранит регистрационную комиссию, цену шага лимита и стартовый бонус.
- Размер PDA: `8 + 96` байт.
## Текущие параметры экономики
Параметры initial config из `programs/shine_users/src/settings.rs`:
| Поле | Значение | Смысл |
| --- | --- | --- |
| `START_REGISTRATION_FEE_LAMPORTS` | `10_000_000` | стартовая комиссия регистрации, 0.01 SOL |
| `LIMIT_STEP` | `10_000` | шаг `additional_limit` |
| `START_LAMPORTS_PER_LIMIT_STEP` | `100_000` | 0.0001 SOL за один шаг лимита |
| `START_BONUS_LIMIT` | `100_000` | стартовый бесплатный лимит при регистрации |
`additional_limit` в create/update должен быть кратен `LIMIT_STEP`.
## Связь с другими программами
`shine_users` зависит от:
- `shine_login_guard` — для проверки логина при создании пользователя;
- `shine_payments` — для вычисления и проверки `inflow_vault_pda`, куда уходят платежи.
`create_user_pda` делает CPI-вызов `shine_login_guard::classify_login` и принимает только результат `0`. Premium/trademark логины сейчас отклоняются ошибками `PremiumLogin` или `TrademarkLoginRequiresReview`.
Подпись `user_pda` и подпись состояния последнего блока проверяются через встроенную Solana Ed25519-инструкцию, которая должна идти раньше инструкции `shine_users` в той же транзакции.
## Деньги
Деньги из `shine_users` идут только в `inflow_vault_pda` программы `shine_payments`.
Потоки:
- `create_user_pda`: регистрационная комиссия + оплата `additional_limit`;
- `update_user_pda`: оплата `additional_limit`, если она больше нуля.
## Ключи и управление
На старте удобно считать, что у программы есть отдельный управляющий ключ `key_2`.
Целевая модель:
- economy-настройки меняет DAO-authority;
- upgrade-authority программы после проверки передается DAO;
- пользовательские операции `create_user_pda` и `update_user_pda` остаются доступными обычным пользователям при корректных подписях и оплате.
@@ -0,0 +1,54 @@
flowchart LR
U[Пользователь / signer]
B[Покупатель тикета]
M[Менеджер]
C[Любой caller step_payout]
LG[1. shine_login_guard<br/>classify_login]
USERS[2. shine_users<br/>create_user_pda / update_user_pda]
PAY[3. shine_payments<br/>vault / tickets / payouts]
DAO[SHiNE DAO<br/>governance / authority / treasury]
USERPDA[(user_pda<br/>по login)]
ECON[(users_economy_config_pda)]
CONFIG[(config_pda)]
COEF[(coef_limit_pda)]
QUEUES[(queues_pda)]
VAULT[(inflow_vault_pda)]
TICKET[(ticket_pda)]
ALLOW[(manager_allowance_pda)]
U -->|логин| USERS
USERS -->|CPI проверка| LG
USERS -->|создает/обновляет| USERPDA
USERS -->|читает экономику| ECON
U -->|регистрация / лимит| VAULT
DAO -->|update economy| USERS
DAO -->|update coef/limit| PAY
DAO -->|grant manager limits| PAY
DAO -->|создает/отзывает ключи| DAO
PAY --> CONFIG
PAY --> COEF
PAY --> QUEUES
PAY --> VAULT
PAY --> TICKET
PAY --> ALLOW
B -->|buy_ticket*| PAY
B -->|оплата покупки тикета| DAO
PAY -->|создает тикет| TICKET
M -->|manager_add_ticket| PAY
ALLOW -->|лимиты Q1/Q2| M
C -->|step_payout| PAY
VAULT -->|выплата тикета| U
VAULT -->|DAO-часть| DAO
VAULT -->|call reward| C
DAO -. upgrade authority после передачи .-> USERS
DAO -. upgrade authority после передачи .-> PAY
DAO -. позже возможно .-> LG
Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

@@ -0,0 +1,139 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1400" height="900" viewBox="0 0 1400 900" role="img" aria-labelledby="title desc">
<title id="title">Архитектура Solana-программ SHiNE</title>
<desc id="desc">Схема трех программ, DAO, PDA-счетов и движения денег.</desc>
<defs>
<marker id="arrow" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#2f3a45"/>
</marker>
<marker id="moneyArrow" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#0a7f62"/>
</marker>
<style>
.bg { fill: #f7f8fa; }
.title { font: 700 30px Arial, sans-serif; fill: #1f2933; }
.subtitle { font: 400 16px Arial, sans-serif; fill: #52606d; }
.box { fill: #ffffff; stroke: #9aa5b1; stroke-width: 2; rx: 8; }
.program { fill: #e8f1ff; stroke: #3465a4; }
.dao { fill: #fff3d6; stroke: #b7791f; }
.pda { fill: #edf7ed; stroke: #2f855a; }
.actor { fill: #f3e8ff; stroke: #805ad5; }
.txt { font: 700 17px Arial, sans-serif; fill: #1f2933; }
.small { font: 400 13px Arial, sans-serif; fill: #3e4c59; }
.line { stroke: #2f3a45; stroke-width: 2.2; fill: none; marker-end: url(#arrow); }
.money { stroke: #0a7f62; stroke-width: 3; fill: none; marker-end: url(#moneyArrow); }
.dashed { stroke-dasharray: 8 7; }
.legend { font: 400 14px Arial, sans-serif; fill: #3e4c59; }
</style>
</defs>
<rect class="bg" x="0" y="0" width="1400" height="900"/>
<text class="title" x="52" y="54">SHiNE Solana: программы, DAO, счета и движение денег</text>
<text class="subtitle" x="52" y="82">Текущая модель: три Anchor-программы, DAO/authority как управляющий слой, inflow vault и DAO treasury.</text>
<rect class="box actor" x="52" y="150" width="210" height="78"/>
<text class="txt" x="72" y="181">Пользователь</text>
<text class="small" x="72" y="206">signer, root_key, device_key</text>
<rect class="box actor" x="52" y="310" width="210" height="78"/>
<text class="txt" x="72" y="341">Покупатель тикета</text>
<text class="small" x="72" y="366">buy_ticket*</text>
<rect class="box actor" x="52" y="470" width="210" height="78"/>
<text class="txt" x="72" y="501">Менеджер</text>
<text class="small" x="72" y="526">manager_add_ticket</text>
<rect class="box actor" x="52" y="630" width="210" height="78"/>
<text class="txt" x="72" y="661">Любой caller</text>
<text class="small" x="72" y="686">step_payout</text>
<rect class="box program" x="360" y="126" width="270" height="96"/>
<text class="txt" x="382" y="160">1. shine_login_guard</text>
<text class="small" x="382" y="186">classify_login</text>
<text class="small" x="382" y="205">free / premium / trademark</text>
<rect class="box program" x="360" y="286" width="270" height="112"/>
<text class="txt" x="382" y="320">2. shine_users</text>
<text class="small" x="382" y="346">create_user_pda</text>
<text class="small" x="382" y="365">update_user_pda</text>
<text class="small" x="382" y="384">economy config</text>
<rect class="box program" x="360" y="518" width="270" height="122"/>
<text class="txt" x="382" y="552">3. shine_payments</text>
<text class="small" x="382" y="578">vault, tickets, queues</text>
<text class="small" x="382" y="597">grant_manager_limits</text>
<text class="small" x="382" y="616">step_payout</text>
<rect class="box dao" x="776" y="126" width="270" height="122"/>
<text class="txt" x="798" y="160">SHiNE DAO</text>
<text class="small" x="798" y="186">governance / authority</text>
<text class="small" x="798" y="205">treasury dao_wallet</text>
<text class="small" x="798" y="224">ключи через голосование</text>
<rect class="box pda" x="776" y="306" width="270" height="84"/>
<text class="txt" x="798" y="340">shine_users PDA</text>
<text class="small" x="798" y="365">user_pda, economy_config</text>
<rect class="box pda" x="776" y="500" width="270" height="150"/>
<text class="txt" x="798" y="534">shine_payments PDA</text>
<text class="small" x="798" y="560">config_pda, coef_limit_pda</text>
<text class="small" x="798" y="579">queues_pda</text>
<text class="small" x="798" y="598">inflow_vault_pda</text>
<text class="small" x="798" y="617">ticket_pda, manager_allowance</text>
<rect class="box pda" x="1134" y="500" width="214" height="88"/>
<text class="txt" x="1156" y="534">inflow_vault</text>
<text class="small" x="1156" y="560">деньги регистрации</text>
<rect class="box dao" x="1134" y="170" width="214" height="88"/>
<text class="txt" x="1156" y="204">DAO treasury</text>
<text class="small" x="1156" y="230">dao_wallet</text>
<path class="line" d="M262 189 C300 189, 318 334, 360 334"/>
<text class="small" x="270" y="286">регистрация / update</text>
<path class="line" d="M360 314 C322 250, 320 176, 360 174"/>
<text class="small" x="330" y="250">CPI login</text>
<path class="line" d="M630 342 L776 342"/>
<text class="small" x="646" y="329">создает/обновляет</text>
<path class="money" d="M262 205 C438 432, 1010 390, 1134 530"/>
<text class="small" x="430" y="430">регистрация и лимит -> inflow_vault</text>
<path class="money" d="M262 349 C540 260, 870 244, 1134 214"/>
<text class="small" x="538" y="270">покупка тикета -> DAO treasury</text>
<path class="line" d="M262 509 L360 579"/>
<text class="small" x="276" y="540">создать тикет</text>
<path class="line" d="M630 579 L776 575"/>
<text class="small" x="648" y="562">PDA состояния</text>
<path class="line" d="M1046 575 L1134 548"/>
<path class="money" d="M1134 560 C970 700, 580 728, 262 669"/>
<text class="small" x="650" y="720">call reward caller</text>
<path class="money" d="M1134 536 C860 754, 426 238, 262 194"/>
<text class="small" x="632" y="760">выплата получателю тикета</text>
<path class="money" d="M1241 500 L1241 258"/>
<text class="small" x="1254" y="380">DAO-часть выплат</text>
<path class="line" d="M776 188 L630 342"/>
<text class="small" x="642" y="250">update economy</text>
<path class="line" d="M776 216 C690 290, 666 516, 630 558"/>
<text class="small" x="654" y="438">settings / managers</text>
<path class="line dashed" d="M910 248 C850 702, 620 720, 520 640"/>
<text class="small" x="690" y="690">upgrade-authority: users/payments; login_guard позже</text>
<rect class="box" x="52" y="808" width="1296" height="54"/>
<line x1="74" y1="835" x2="132" y2="835" class="line"/>
<text class="legend" x="146" y="840">логические вызовы и управление</text>
<line x1="374" y1="835" x2="432" y2="835" class="money"/>
<text class="legend" x="446" y="840">движение SOL/lamports</text>
<line x1="682" y1="835" x2="740" y2="835" class="line dashed"/>
<text class="legend" x="754" y="840">будущая передача upgrade-authority DAO</text>
</svg>

After

Width:  |  Height:  |  Size: 7.1 KiB

@@ -0,0 +1,91 @@
# Деплой и инициализация Solana-регистрации (две обязательные программы)
## Коротко
Для рабочей регистрации пользователя нужны **обе** программы:
1. `shine_users` — хранение и обновление `user_pda`, economy-конфиг, логика регистрации.
2. `shine_login_guard` — проверка/классификация логина (CPI из `shine_users`).
Если задеплоена только одна из них — регистрация неработоспособна.
## Актуальные адреса (devnet)
- `shine_users` (регистрация пользователей):
`FZS1YctoeEhCkZ5VTjsysUFAXR8CqxYztcLboXcg2Rpm`
- `shine_login_guard`:
`3xkopA7cXagxzMFrKdv3NCBfV6BKiRJCk69kr27M2sRo`
- `shine_payments`:
`m48pWRGWrMj3TEHjuU4zsp5Gju4e7ZaPovk8RcVt7kR`
## Подтверждение деплоя
- Сеть: `https://api.devnet.solana.com`
- `shine_users`:
- `Program ID`: `FZS1YctoeEhCkZ5VTjsysUFAXR8CqxYztcLboXcg2Rpm`
- TX deploy: `5VzfpSirFCRqPUZfvAt3eADY9KnowW79PKZ1pCQAa2DJGiztj4dUYYXrSQNmWEhPVu6mPSDfcuHzFyEVmoKLa9DM`
- `shine_login_guard`:
- `Program ID`: `3xkopA7cXagxzMFrKdv3NCBfV6BKiRJCk69kr27M2sRo`
- TX deploy: `5iptngPYrLLjPE3Xby24zyNW3edVUnBNLBx785vjojMoq5JNLFNQvLNAm3jNYHbpf2B36qtbpTNzcvUNyRDqm1Mf`
## Порядок деплоя (devnet)
1. Убедиться, что CLI смотрит в devnet и у кошелька есть SOL.
2. Собрать и задеплоить `shine_login_guard`.
3. Собрать и задеплоить `shine_users`.
4. Проверить, что адреса совпадают между:
- `Anchor.toml`
- `declare_id!` в `programs/*/src/lib.rs`
- UI/серверными константами.
5. Выполнить `init_users_economy_config` (один раз на программу `shine_users`).
Пример команд:
```bash
cd shine-solana/shine
solana config get
solana balance
anchor build -p shine_login_guard
anchor deploy -p shine_login_guard
anchor build -p shine_users
anchor deploy -p shine_users
```
## Куда вписаны адреса в проекте
### UI
- Общие Solana-константы:
- `shine-UI/js/solana-programs.js`
- Страница инициализации:
- `shine-UI/js/pages/solana-users-init-view.js`
- Переход на страницу:
- `shine-UI/js/pages/developer-settings-view.js`
### Сервер
- Серверные константы Solana:
- `shine-server-config/src/main/java/utils/config/SolanaProgramsConfig.java`
## Как запустить инициализацию economy PDA
1. Открыть UI.
2. Перейти: `Профиль -> Настройки -> Настройки разработчика -> Solana: init регистрации`.
3. Подключить кошелёк (Phantom, devnet).
4. Нажать `Запустить init_users_economy_config`.
5. Дождаться статуса `Успешно`.
Страница сама вычисляет PDA `users_economy_config` по seed:
- seed: `shine_users_economy_config`
- program: `FZS1YctoeEhCkZ5VTjsysUFAXR8CqxYztcLboXcg2Rpm`
## Важно
- `init_users_economy_config` выполняется один раз на программу.
Если PDA уже создан, повторный вызов вернёт ошибку "already initialized" (это нормальное поведение).
- Серверные приватные ключи для Solana не используются: подписание делается кошельком пользователя в UI.
- `shine_users` внутри `create_user_pda` требует корректный адрес `shine_login_guard` для CPI-классификации логина.
Несовпадение адреса приведёт к ошибке регистрации.