SHA256
chore: зафиксированы все текущие изменения проекта
This commit is contained in:
@@ -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 |
Reference in New Issue
Block a user