diff --git a/DEBUG_CONNECTION_TESTING.md b/DEBUG_CONNECTION_TESTING.md deleted file mode 100644 index 22b7b5c..0000000 --- a/DEBUG_CONNECTION_TESTING.md +++ /dev/null @@ -1,92 +0,0 @@ -# DEBUG: тестирование сетевого соединения между двумя клиентами - -Документ описывает временный debug-контур для проверки WebRTC соединения между двумя активными WS-сессиями. - -## 1) Подготовка - - -0. Убедись, что в `application.properties` включен параметр: - `debug.tempApi.enabled=true` -1. Создай файл `.debug-token` в корне проекта на основе `debug-token.example`. -2. В `.debug-token` должна быть одна строка: секретный токен. -3. Перезапусти сервер. - -## 2) API debug - -Базовый заголовок для всех запросов: - -```bash --H "Authorization: Bearer " -``` - -### 2.1 Получить список живых клиентов - -```bash -curl -s \ - -H "Authorization: Bearer " \ - http://localhost:7070/debug/ws/clients | jq -``` - -Ответ содержит `sessionId`, `login`, `ip`, `userAgent`, и клиентскую информацию. - -### 2.2 Запустить debug-соединение между двумя сессиями - -```bash -curl -s -X POST \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - -d '{ - "initiatorSessionId": "SESSION_ID_A", - "responderSessionId": "SESSION_ID_B", - "clearDebugLog": false - }' \ - http://localhost:7070/debug/ws/connect | jq -``` - -В ответе придёт `runId`. Его используй для фильтра логов. - -### 2.3 Читать последние N debug-логов - -```bash -curl -s \ - -H "Authorization: Bearer " \ - "http://localhost:7070/debug/ws/logs?limit=200&runId=" | jq -``` - -## 3) Операционный сценарий “Codex + пользователь” - -1. Codex поднимает сервер и сообщает пользователю ссылку на UI. -2. Codex пишет пользователю: **«Запусти двух клиентов и скажи “продолжай”»**. -3. Пользователь запускает два клиента (лучше под разными логинами). -4. Пользователь пишет: **«продолжай»**. -5. Codex: - - вызывает `/debug/ws/clients`, - - выбирает 2 сессии, - - вызывает `/debug/ws/connect`, - - получает `runId`, - - читает `/debug/ws/logs?runId=...` и сообщает прогресс. -6. Если соединение не удалось: - - Codex сообщает ошибки по логам, - - при необходимости просит перезапустить 2 клиента, - - повторяет запуск debug-run. - -## 4) Какие сообщения считать успехом - -- `peer_connection_connected` -- `debug_connection_success` -- `signal_sent_200/210/220` без ошибок - -## 5) Что говорить пользователю в ходе прогона (через «колонку»/чат) - -Рекомендуемые фразы: - -- «Сервер запущен. Запусти двух клиентов и напиши “продолжай”.» -- «Вижу 2 активные сессии, запускаю тест соединения.» -- «Тест запущен, runId=... Сейчас проверяю логи.» -- «Соединение установлено / не установлено. Ниже причины и следующий шаг.» - -## 6) Ограничения - -- Механизм временный, не для production-эксплуатации. -- Доступ к debug API имеет любой, кто знает токен. -- Рекомендуется тестить между разными логинами. diff --git a/Dev_Docs/Pending_Features/2026-07-01_1945_mainnet-solana-addresses-sync.md b/Dev_Docs/Pending_Features/2026-07-01_1945_mainnet-solana-addresses-sync.md new file mode 100644 index 0000000..9cbaf1e --- /dev/null +++ b/Dev_Docs/Pending_Features/2026-07-01_1945_mainnet-solana-addresses-sync.md @@ -0,0 +1,45 @@ +# Синхронизация mainnet-адресов Solana по UI/серверу/ESP32 + +- статус: `pending` + +## Кратко + +Во все основные клиентские и серверные точки проекта протянуты актуальные mainnet-адреса программ: + +- `shine_login_guard`: `SHiGxGsXGioQYCYhchQ5R7KWoxN5UjFAFsucPf6sfnh` +- `shine_users`: `SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6` +- `shine_payments`: `SHiPmXbM9Fs9khzRUW3TGKsS2W84aqaXTxs3ZkajW9v` + +Также по умолчанию переключены основные RPC defaults на `https://api.mainnet-beta.solana.com` там, где это нужно для рабочего mainnet-сценария. + +## Что проверять + +1. Основной UI: + - отображение и использование новых program id; + - регистрация/чтение Solana PDA в mainnet; + - экран кошелька и покупка лимита. + +2. Server UI: + - создание server PDA в mainnet; + - чтение PDA; + - обновление PDA; + - корректная блокировка devnet-автопополнения при mainnet endpoint. + +3. Browser plugin wallet: + - резолв `user_pda` и `server PDA` через mainnet RPC. + +4. Web UI `shine_payments`: + - чтение очередей; + - покупка билета; + - admin/manager/dao tools открываются с новыми program id и mainnet RPC. + +5. ESP32 homeserver UI: + - отображаются новые program id; + - дефолтный Solana RPC теперь mainnet. + +## Ожидаемый результат + +- Во всех перечисленных поверхностях используются новые mainnet program id. +- Запросы по умолчанию идут в mainnet, кроме явно devnet-утилит. +- Нет обращений к старым devnet program id. + diff --git a/Dev_Docs/Solana_Architecture/README.md b/Dev_Docs/Solana_Architecture/README.md index e28e129..31a1f6f 100644 --- a/Dev_Docs/Solana_Architecture/README.md +++ b/Dev_Docs/Solana_Architecture/README.md @@ -46,9 +46,9 @@ DAO в текущем виде не является отдельной Anchor- | Программа | Program ID | | --- | --- | -| `shine_login_guard` | `3xkopA7cXagxzMFrKdv3NCBfV6BKiRJCk69kr27M2sRo` | -| `shine_users` | `3bYrnXwLc56oVPUBAjY8zTMLwHCYq29b5rUMu3b64SQJ` | -| `shine_payments` | `c4yTa4JT9EtQDCBX9LmWFK6T2gp4JGsuymFbom2EudW` | +| `shine_login_guard` | `SHiGxGsXGioQYCYhchQ5R7KWoxN5UjFAFsucPf6sfnh` | +| `shine_users` | `SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6` | +| `shine_payments` | `SHiPmXbM9Fs9khzRUW3TGKsS2W84aqaXTxs3ZkajW9v` | Если эти адреса меняются, нужно синхронно обновить: diff --git a/Dev_Docs/Solana_Architecture/details/accounts_and_money_flow.md b/Dev_Docs/Solana_Architecture/details/accounts_and_money_flow.md index 6fbe6c6..43786e7 100644 --- a/Dev_Docs/Solana_Architecture/details/accounts_and_money_flow.md +++ b/Dev_Docs/Solana_Architecture/details/accounts_and_money_flow.md @@ -26,11 +26,11 @@ | Роль | Адрес | | --- | --- | -| `SHINE_LOGIN_GUARD_PROGRAM_ID` | `3xkopA7cXagxzMFrKdv3NCBfV6BKiRJCk69kr27M2sRo` | -| `SHINE_USERS_PROGRAM_ID` | `3bYrnXwLc56oVPUBAjY8zTMLwHCYq29b5rUMu3b64SQJ` | -| `SHINE_PAYMENTS_PROGRAM_ID` | `c4yTa4JT9EtQDCBX9LmWFK6T2gp4JGsuymFbom2EudW` | -| `DAO_AUTHORITY` | `FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P` | -| `DAO_TREASURY_WALLET` | `FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P` | +| `SHINE_LOGIN_GUARD_PROGRAM_ID` | `SHiGxGsXGioQYCYhchQ5R7KWoxN5UjFAFsucPf6sfnh` | +| `SHINE_USERS_PROGRAM_ID` | `SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6` | +| `SHINE_PAYMENTS_PROGRAM_ID` | `SHiPmXbM9Fs9khzRUW3TGKsS2W84aqaXTxs3ZkajW9v` | +| `DAO_AUTHORITY` | `aiShm43fZjm3YkMs22sYL1bpXaL3bVxv7SSraPHzVgq` | +| `DAO_TREASURY_WALLET` | `aiShm43fZjm3YkMs22sYL1bpXaL3bVxv7SSraPHzVgq` | ## Постоянные PDA diff --git a/Dev_Docs/Инициализация_Solana_регистрации/README.md b/Dev_Docs/Инициализация_Solana_регистрации/README.md index b9457b2..81eba6f 100644 --- a/Dev_Docs/Инициализация_Solana_регистрации/README.md +++ b/Dev_Docs/Инициализация_Solana_регистрации/README.md @@ -1,125 +1,103 @@ -# Деплой и инициализация Solana-регистрации (две обязательные программы) +# Деплой и инициализация Solana-регистрации ## Коротко -Для рабочей регистрации пользователя нужны **обе** программы: +Для рабочей регистрации пользователя нужны три программы: 1. `shine_users` — хранение и обновление `user_pda`, economy-конфиг, логика регистрации. -2. `shine_login_guard` — проверка/классификация логина (CPI из `shine_users`). +2. `shine_login_guard` — проверка/классификация логина через CPI из `shine_users`. +3. `shine_payments` — приём платежей за регистрацию и докупку лимита через `inflow_vault PDA`. -Если задеплоена только одна из них — регистрация неработоспособна. +Если `shine_users` или `shine_login_guard` не совпадают по адресам с UI/сервером, регистрация неработоспособна. -## Актуальные адреса (devnet) +## Актуальные адреса (mainnet) -- `shine_users` (регистрация пользователей): - `3bYrnXwLc56oVPUBAjY8zTMLwHCYq29b5rUMu3b64SQJ` +- `shine_users`: + `SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6` - `shine_login_guard`: - `3xkopA7cXagxzMFrKdv3NCBfV6BKiRJCk69kr27M2sRo` + `SHiGxGsXGioQYCYhchQ5R7KWoxN5UjFAFsucPf6sfnh` - `shine_payments`: - `c4yTa4JT9EtQDCBX9LmWFK6T2gp4JGsuymFbom2EudW` + `SHiPmXbM9Fs9khzRUW3TGKsS2W84aqaXTxs3ZkajW9v` -## Подтверждение деплоя +Временный on-chain admin для `shine_users` и `shine_payments` до перевода управления на DAO: -- Сеть: `https://api.devnet.solana.com` -- `shine_users`: - - `Program ID`: `3bYrnXwLc56oVPUBAjY8zTMLwHCYq29b5rUMu3b64SQJ` - - TX deploy: `5VzfpSirFCRqPUZfvAt3eADY9KnowW79PKZ1pCQAa2DJGiztj4dUYYXrSQNmWEhPVu6mPSDfcuHzFyEVmoKLa9DM` +- `aiShm43fZjm3YkMs22sYL1bpXaL3bVxv7SSraPHzVgq` + +## Подтверждение текущего mainnet deploy + +- Сеть: `https://api.mainnet-beta.solana.com` - `shine_login_guard`: - - `Program ID`: `3xkopA7cXagxzMFrKdv3NCBfV6BKiRJCk69kr27M2sRo` - - TX deploy: `5iptngPYrLLjPE3Xby24zyNW3edVUnBNLBx785vjojMoq5JNLFNQvLNAm3jNYHbpf2B36qtbpTNzcvUNyRDqm1Mf` + - `Program ID`: `SHiGxGsXGioQYCYhchQ5R7KWoxN5UjFAFsucPf6sfnh` + - TX deploy: `5gCxrw5gj7ig5C3HFf4AUrHq2pMW1LxgfLUj4jtmQvTqXnR7xp1iySf7m63S3MaimpeUyYhQR1zpPFJyPi6NTAv7` +- `shine_users`: + - `Program ID`: `SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6` + - TX deploy: `5Cwp53EegPv6aV69Ztxj7nXomHt9H9ZaXSgWvDfLPeR5EAaU38ADsQjn9h8xShU34AgttYGwWaVWiicF2vLwJXU9` +- `shine_payments`: + - `Program ID`: `SHiPmXbM9Fs9khzRUW3TGKsS2W84aqaXTxs3ZkajW9v` + - TX deploy: `2vzsM85bTwSxdYvzWKZMpAg7Gqxs8R7CC5T5LTm7cRFizhr11QZvgDKqP77b8n2mcmA6DF4RqwrUrPzQ8bsQhvCn` -## Порядок деплоя (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 -``` +- `shine_users` внутри кода вызывает `shine_login_guard`. +- `shine_users` внутри кода знает `shine_payments` и переводит оплату в `inflow_vault PDA` этой программы. +- `shine_users` и `shine_payments` пока управляются временным адресом `aiShm...`. +- Позже эти hardcoded authority-адреса планируется заменить на адреса DAO через upgrade программ. ## Куда вписаны адреса в проекте ### 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` +- `shine-UI/js/solana-programs.js` +- `shine-UI/js/pages/wallet-view.js` +- `shine-UI/js/services/solana-register-service.js` +- `shine-UI/server-ui/*.html` ### Browser plugin wallet -- Резолвер PDA и проверка адресов: - - `SHiNE-browser-plugin-wallet/js/lib/shine-server-resolver.js` -- Проверка текущего wallet по PDA: - - `SHiNE-browser-plugin-wallet/background.js` -- Отображение состояния в popup: - - `SHiNE-browser-plugin-wallet/popup.js` +- `SHiNE-browser-plugin-wallet/js/lib/shine-server-resolver.js` ### Сервер -- Серверные константы Solana: - - `shine-server-config/src/main/java/utils/config/SolanaProgramsConfig.java` +- `SHiNE-server/shine-server-config/src/main/java/utils/config/SolanaProgramsConfig.java` ### Solana / Anchor - `shine-solana/shine/Anchor.toml` -- `shine-solana/shine/programs/shine_users/src/lib.rs` (`declare_id!`) -- `shine-solana/shine/programs/shine_login_guard/src/lib.rs` (`declare_id!`) -- `shine-solana/shine/programs/shine_payments/src/lib.rs` (`declare_id!`) +- `shine-solana/shine/programs/shine_users/src/lib.rs` +- `shine-solana/shine/programs/shine_users/src/settings.rs` +- `shine-solana/shine/programs/shine_login_guard/src/lib.rs` +- `shine-solana/shine/programs/shine_payments/src/lib.rs` +- `shine-solana/shine/programs/shine_payments/src/settings.rs` -### Где ещё нужно синхронизировать адреса после нового deploy +### Дополнительные потребители -- UI-константы и все потребители в `shine-UI/js/*` -- browser-plugin-wallet -- серверный `SolanaProgramsConfig.java` -- Anchor-конфиг и `declare_id!` в Solana-модуле -- документы: - - `Dev_Docs/Solana/user_pda/README.md` - - `shine-solana/shine/doc/programs/shine_users.md` - - `shine-solana/shine/doc/devnet_keys_and_deploy.md` +- `shine-solana/shine/programs/shine_payments/web/*.html` +- `ESP32/.../shine_homeserver_main.ino` +- `ESP32/.../shine_homeserver_ui.ino` ## Как запустить инициализацию economy PDA 1. Открыть UI. 2. Перейти: `Профиль -> Настройки -> Настройки разработчика -> Solana: init регистрации`. -3. Подключить кошелёк (Phantom, devnet). +3. Подключить mainnet-кошелёк. 4. Нажать `Запустить init_users_economy_config`. 5. Дождаться статуса `Успешно`. -Страница сама вычисляет PDA `users_economy_config` по seed: +Страница вычисляет PDA `users_economy_config` по seed: - seed: `shine_users_economy_config` -- program: `3bYrnXwLc56oVPUBAjY8zTMLwHCYq29b5rUMu3b64SQJ` +- program: `SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6` ## Кто оплачивает create/update user_pda - И обычная регистрация `create_user_pda`, и последующее `update_user_pda` оплачиваются с `clientKey`. -- В UI это означает, что Solana fee payer всегда берётся из `device`-ключа пользователя/сервера. +- В UI это означает, что Solana fee payer всегда берётся из `device`-ключа пользователя или сервера. - `rootKey` нужен для подписи unsigned PDA-записи, но не оплачивает транзакцию. - Для server UI это особенно важно: перед `create` и `update` нужно пополнять именно Solana-адрес `clientKey`. ## Важно -- `init_users_economy_config` выполняется один раз на программу. - Если PDA уже создан, повторный вызов вернёт ошибку "already initialized" (это нормальное поведение). -- Серверные приватные ключи для Solana не используются как отдельный backend-wallet: в UI/server UI транзакцию оплачивает именно `clientKey`, а содержимое записи подписывает `rootKey`. -- `shine_users` внутри `create_user_pda` требует корректный адрес `shine_login_guard` для CPI-классификации логина. - Несовпадение адреса приведёт к ошибке регистрации. +- `init_users_economy_config` выполняется один раз на программу. Если PDA уже создан, повторный вызов вернёт ошибку `already initialized`. +- Серверные приватные ключи для Solana не используются как отдельный backend-wallet: транзакцию оплачивает `clientKey`, а содержимое записи подписывает `rootKey`. +- `shine_users` внутри `create_user_pda` требует корректный адрес `shine_login_guard` для CPI-классификации логина. +- При новом devnet deploy планируется использовать те же program keypair, чтобы `program id` на devnet совпадали с mainnet. diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/main-device/shine_homeserver_main/shine_homeserver_main.ino b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/main-device/shine_homeserver_main/shine_homeserver_main.ino index df9c214..7d2616e 100644 --- a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/main-device/shine_homeserver_main/shine_homeserver_main.ino +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/main-device/shine_homeserver_main/shine_homeserver_main.ino @@ -70,10 +70,10 @@ int ge25519_frombytes(ge25519_p3 *h, const unsigned char *s); #define TEXT_EDIT_PANEL_H 330 #define TEST_VERSION "SHiNE homeserver (v.0.18)" -static const char *kShineUsersProgramId = "3bYrnXwLc56oVPUBAjY8zTMLwHCYq29b5rUMu3b64SQJ"; +static const char *kShineUsersProgramId = "SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6"; static const char *kShineUsersUserPdaSeedPrefix = "user_login="; -static const char *kShineLoginGuardProgramId = "3xkopA7cXagxzMFrKdv3NCBfV6BKiRJCk69kr27M2sRo"; -static const char *kShinePaymentsProgramId = "c4yTa4JT9EtQDCBX9LmWFK6T2gp4JGsuymFbom2EudW"; +static const char *kShineLoginGuardProgramId = "SHiGxGsXGioQYCYhchQ5R7KWoxN5UjFAFsucPf6sfnh"; +static const char *kShinePaymentsProgramId = "SHiPmXbM9Fs9khzRUW3TGKsS2W84aqaXTxs3ZkajW9v"; static const char *kSystemProgramId = "11111111111111111111111111111111"; static const char *kEd25519ProgramId = "Ed25519SigVerify111111111111111111111111111"; static const char *kSysvarInstructionsId = "Sysvar1nstructions1111111111111111111111111"; @@ -335,7 +335,7 @@ static String gScanResults[MAX_SCAN_RESULTS]; static int gScanResultCount = 0; static WifiViewMode gWifiViewMode = WIFI_VIEW_OVERVIEW; -static String gSolanaRpcUrl = "https://api.devnet.solana.com"; +static String gSolanaRpcUrl = "https://api.mainnet-beta.solana.com"; static String gShineServerLogin = kDefaultShineServerLogin; static String gShineServerUrl; static String gResolvedShineServerLogin; @@ -5271,7 +5271,7 @@ static void loadPrefs() { if (!gWifiSavedSsid.isEmpty() && findKnownWifiIndex(gWifiSavedSsid) < 0) { upsertKnownWifi(gWifiSavedSsid, gWifiSavedPassword); } - gSolanaRpcUrl = gPrefs.getString("solana_rpc", "https://api.devnet.solana.com"); + gSolanaRpcUrl = gPrefs.getString("solana_rpc", "https://api.mainnet-beta.solana.com"); String storedShineServerLogin = normalizeLoginValue(gPrefs.getString("shine_server_login", "")); if (!isValidShineServerLoginValue(storedShineServerLogin)) { String legacyShineServer = normalizeLoginValue(gPrefs.getString("shine_server", "")); diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/main-device/shine_homeserver_ui/shine_homeserver_ui.ino b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/main-device/shine_homeserver_ui/shine_homeserver_ui.ino index edf47cc..fc9e699 100644 --- a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/main-device/shine_homeserver_ui/shine_homeserver_ui.ino +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/main-device/shine_homeserver_ui/shine_homeserver_ui.ino @@ -239,9 +239,9 @@ static DerivedKeyState gDerivedKeys = {}; static const char *kSystemProgramId = "11111111111111111111111111111111"; static const char *kEd25519ProgramId = "Ed25519SigVerify111111111111111111111111111"; static const char *kSysvarInstructionsId = "Sysvar1nstructions1111111111111111111111111"; -static const char *kShineUsersProgramId = "3bYrnXwLc56oVPUBAjY8zTMLwHCYq29b5rUMu3b64SQJ"; -static const char *kShineLoginGuardProgramId = "3xkopA7cXagxzMFrKdv3NCBfV6BKiRJCk69kr27M2sRo"; -static const char *kShinePaymentsProgramId = "c4yTa4JT9EtQDCBX9LmWFK6T2gp4JGsuymFbom2EudW"; +static const char *kShineUsersProgramId = "SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6"; +static const char *kShineLoginGuardProgramId = "SHiGxGsXGioQYCYhchQ5R7KWoxN5UjFAFsucPf6sfnh"; +static const char *kShinePaymentsProgramId = "SHiPmXbM9Fs9khzRUW3TGKsS2W84aqaXTxs3ZkajW9v"; static const char *kUsersSeedPrefix = "user_login="; static const char *kUsersEconomyConfigSeed = "shine_users_economy_config"; static const char *kPaymentsInflowSeed = "shine_payments_inflow_vault"; @@ -1314,7 +1314,7 @@ static String defaultApiUrl() { } static String defaultRpcUrl() { - return "https://api.devnet.solana.com"; + return "https://api.mainnet-beta.solana.com"; } static String defaultWsUrl() { diff --git a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/reference/shine_homeserver_ui_nav_minimal_spec.md b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/reference/shine_homeserver_ui_nav_minimal_spec.md index a523ea6..b4a48bb 100644 --- a/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/reference/shine_homeserver_ui_nav_minimal_spec.md +++ b/ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/reference/shine_homeserver_ui_nav_minimal_spec.md @@ -154,7 +154,7 @@ - кнопку `SHiNE SERVER LOGIN`, если обычный `user PDA` ещё не зарегистрирован. Значения по умолчанию: -- Solana RPC: `https://api.devnet.solana.com` +- Solana RPC: `https://api.mainnet-beta.solana.com` - SHiNE server login: `shineupme` Нажатие на любую из двух кнопок открывает `TEXT_EDIT_SCREEN`. diff --git a/SHiNE-browser-plugin-wallet/js/lib/shine-server-resolver.js b/SHiNE-browser-plugin-wallet/js/lib/shine-server-resolver.js index 284363f..83ceba0 100644 --- a/SHiNE-browser-plugin-wallet/js/lib/shine-server-resolver.js +++ b/SHiNE-browser-plugin-wallet/js/lib/shine-server-resolver.js @@ -1,8 +1,8 @@ import { base64ToBytes } from './crypto-utils.js'; import { PublicKey } from './vendor/solana-publickey-bundle.js'; -const SOLANA_ENDPOINT_DEFAULT = 'https://api.devnet.solana.com'; -const SHINE_USERS_PROGRAM_ID = '3bYrnXwLc56oVPUBAjY8zTMLwHCYq29b5rUMu3b64SQJ'; +const SOLANA_ENDPOINT_DEFAULT = 'https://api.mainnet-beta.solana.com'; +const SHINE_USERS_PROGRAM_ID = 'SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6'; const SHINE_USERS_USER_PDA_SEED_PREFIX = 'user_login='; const DEFAULT_SHINE_SERVER_LOGIN = 'shineupme'; const DEFAULT_SHINE_SERVER_ADDRESS = 'shineup.me'; diff --git a/SHiNE-server/AGENTS.md b/SHiNE-server/AGENTS.md index dff81c0..5976df1 100644 --- a/SHiNE-server/AGENTS.md +++ b/SHiNE-server/AGENTS.md @@ -42,8 +42,8 @@ shine-UI/server-ui.html Для обновления — только root + device (blockchain-ключ не нужен). Актуальные адреса программ Solana (devnet): -- `shine_users`: `3bYrnXwLc56oVPUBAjY8zTMLwHCYq29b5rUMu3b64SQJ` -- `shine_payments`: `c4yTa4JT9EtQDCBX9LmWFK6T2gp4JGsuymFbom2EudW` +- `shine_users`: `SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6` +- `shine_payments`: `SHiPmXbM9Fs9khzRUW3TGKsS2W84aqaXTxs3ZkajW9v` Подробнее: `Dev_Docs/Инициализация_Solana_регистрации/README.md` diff --git a/SHiNE-server/shine-server-config/src/main/java/utils/config/SolanaProgramsConfig.java b/SHiNE-server/shine-server-config/src/main/java/utils/config/SolanaProgramsConfig.java index e540c25..3901c18 100644 --- a/SHiNE-server/shine-server-config/src/main/java/utils/config/SolanaProgramsConfig.java +++ b/SHiNE-server/shine-server-config/src/main/java/utils/config/SolanaProgramsConfig.java @@ -1,20 +1,20 @@ package utils.config; /** - * Публичные адреса Solana-программ SHiNE (жёстко зафиксированы для devnet). + * Публичные адреса Solana-программ SHiNE (жёстко зафиксированы для mainnet). * Секреты на сервере не хранятся. */ public final class SolanaProgramsConfig { private SolanaProgramsConfig() {} - public static final String SOLANA_CLUSTER = "devnet"; - public static final String SOLANA_RPC_URL = "https://api.devnet.solana.com"; + public static final String SOLANA_CLUSTER = "mainnet-beta"; + public static final String SOLANA_RPC_URL = "https://api.mainnet-beta.solana.com"; - // Программа регистрации пользователей (shine_users), задеплоена в devnet. - public static final String SHINE_USERS_PROGRAM_ID = "3bYrnXwLc56oVPUBAjY8zTMLwHCYq29b5rUMu3b64SQJ"; + // Программа регистрации пользователей (shine_users), задеплоена в mainnet. + public static final String SHINE_USERS_PROGRAM_ID = "SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6"; // Отдельно фиксируем адреса связанной инфраструктуры, чтобы UI/сервер ссылались одинаково. - public static final String SHINE_LOGIN_GUARD_PROGRAM_ID = "3xkopA7cXagxzMFrKdv3NCBfV6BKiRJCk69kr27M2sRo"; - public static final String SHINE_PAYMENTS_PROGRAM_ID = "c4yTa4JT9EtQDCBX9LmWFK6T2gp4JGsuymFbom2EudW"; + public static final String SHINE_LOGIN_GUARD_PROGRAM_ID = "SHiGxGsXGioQYCYhchQ5R7KWoxN5UjFAFsucPf6sfnh"; + public static final String SHINE_PAYMENTS_PROGRAM_ID = "SHiPmXbM9Fs9khzRUW3TGKsS2W84aqaXTxs3ZkajW9v"; } diff --git a/VERSION.properties b/VERSION.properties index aa8d08c..823b80c 100644 --- a/VERSION.properties +++ b/VERSION.properties @@ -1,2 +1,2 @@ -client.version=1.2.293 -server.version=1.2.273 +client.version=1.2.294 +server.version=1.2.274 diff --git a/scripts/devnet/test_login_guard_precheck.js b/scripts/devnet/test_login_guard_precheck.js index 43838a4..050cca7 100644 --- a/scripts/devnet/test_login_guard_precheck.js +++ b/scripts/devnet/test_login_guard_precheck.js @@ -5,8 +5,8 @@ const crypto = require('crypto'); const web3 = require('@solana/web3.js'); const RPC = process.env.SOLANA_RPC || 'https://api.devnet.solana.com'; -const LOGIN_GUARD_PROGRAM_ID = '3xkopA7cXagxzMFrKdv3NCBfV6BKiRJCk69kr27M2sRo'; -const SIM_PAYER = 'FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P'; +const LOGIN_GUARD_PROGRAM_ID = 'SHiGxGsXGioQYCYhchQ5R7KWoxN5UjFAFsucPf6sfnh'; +const SIM_PAYER = 'SHiJ58FvbJQJnPEUa5mZNVpWd1ym9CkjjPgeHYzUJmh'; const login = String(process.argv[2] || '').trim().toLowerCase(); if (!login) { @@ -84,4 +84,3 @@ main().catch((e) => { console.error(e?.stack || e?.message || String(e)); process.exit(10); }); - diff --git a/shine-UI/js/pages/wallet-view.js b/shine-UI/js/pages/wallet-view.js index 1226622..c50dd6b 100644 --- a/shine-UI/js/pages/wallet-view.js +++ b/shine-UI/js/pages/wallet-view.js @@ -63,7 +63,7 @@ function sessionArgsOrThrow() { return { login, storagePwd }; } -const SHINE_PAYMENTS_PROGRAM_ID = 'c4yTa4JT9EtQDCBX9LmWFK6T2gp4JGsuymFbom2EudW'; +const SHINE_PAYMENTS_PROGRAM_ID = 'SHiPmXbM9Fs9khzRUW3TGKsS2W84aqaXTxs3ZkajW9v'; const SHINE_PAYMENTS_ORACLE_ACCOUNT = '7UVimffxr9ow1uXYxsr4LHAcV58mLzhmwaeKvJ1pjLiE'; const SHINE_PAYMENTS_SEEDS = { config: 'shine_payments_config', diff --git a/shine-UI/js/services/solana-register-service.js b/shine-UI/js/services/solana-register-service.js index a65d1cc..3c91fef 100644 --- a/shine-UI/js/services/solana-register-service.js +++ b/shine-UI/js/services/solana-register-service.js @@ -6,7 +6,7 @@ import { import { loadSolanaWeb3 } from '../vendor/solana-web3-loader.js'; const CLASSIFY_LOGIN_INSTRUCTION_TAG = 1; -const PRECHECK_SIM_PAYER = 'FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P'; +const PRECHECK_SIM_PAYER = 'SHiJ58FvbJQJnPEUa5mZNVpWd1ym9CkjjPgeHYzUJmh'; const SHINE_USERS_USER_PDA_SEED_PREFIX = 'user_login='; let solanaLibPromise = null; diff --git a/shine-UI/js/solana-programs.js b/shine-UI/js/solana-programs.js index 922af48..e9293ef 100644 --- a/shine-UI/js/solana-programs.js +++ b/shine-UI/js/solana-programs.js @@ -1,9 +1,9 @@ -export const SOLANA_CLUSTER = 'devnet'; -export const SOLANA_ENDPOINT_DEFAULT = 'https://api.devnet.solana.com'; +export const SOLANA_CLUSTER = 'mainnet-beta'; +export const SOLANA_ENDPOINT_DEFAULT = 'https://api.mainnet-beta.solana.com'; // Единый файл актуальных Solana Program ID для UI-части SHiNE. // При смене адресов обновлять этот файл и все его потребители внутри shine-UI. -export const SHINE_USERS_PROGRAM_ID = '3bYrnXwLc56oVPUBAjY8zTMLwHCYq29b5rUMu3b64SQJ'; +export const SHINE_USERS_PROGRAM_ID = 'SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6'; export const SHINE_USERS_ECONOMY_CONFIG_SEED = 'shine_users_economy_config'; -export const SHINE_LOGIN_GUARD_PROGRAM_ID = '3xkopA7cXagxzMFrKdv3NCBfV6BKiRJCk69kr27M2sRo'; -export const SHINE_PAYMENTS_PROGRAM_ID = 'c4yTa4JT9EtQDCBX9LmWFK6T2gp4JGsuymFbom2EudW'; +export const SHINE_LOGIN_GUARD_PROGRAM_ID = 'SHiGxGsXGioQYCYhchQ5R7KWoxN5UjFAFsucPf6sfnh'; +export const SHINE_PAYMENTS_PROGRAM_ID = 'SHiPmXbM9Fs9khzRUW3TGKsS2W84aqaXTxs3ZkajW9v'; diff --git a/shine-UI/server-ui/create-server-pda.html b/shine-UI/server-ui/create-server-pda.html index df4ed05..9e725f1 100644 --- a/shine-UI/server-ui/create-server-pda.html +++ b/shine-UI/server-ui/create-server-pda.html @@ -48,8 +48,8 @@

Параметры Solana

- -
devnet: https://api.devnet.solana.com · mainnet: https://api.mainnet-beta.solana.com
+ +
mainnet: https://api.mainnet-beta.solana.com · devnet: https://api.devnet.solana.com
@@ -121,7 +121,7 @@
Положите SOL на этот адрес перед регистрацией:
Это Solana-адрес device-ключа (public key в base58 = Solana-адрес). С него оплачиваются и создание, и обновление PDA.
- +
Баланс device ещё не запрашивался.
diff --git a/shine-UI/server-ui/js/create-server-pda-page.js b/shine-UI/server-ui/js/create-server-pda-page.js index beaaab0..25c6e8f 100644 --- a/shine-UI/server-ui/js/create-server-pda-page.js +++ b/shine-UI/server-ui/js/create-server-pda-page.js @@ -40,6 +40,9 @@ $('serverAddress').placeholder = defaultServerAddress; $('btnTopupDevnet').addEventListener('click', () => { try { + if (!String($('endpoint').value || '').includes('devnet')) { + throw new Error('Автопополнение доступно только для devnet. Для mainnet пополните device-кошелёк вручную.'); + } openDevnetTopup($('devPub').value); } catch (error) { setStatus($('status'), error?.message || String(error), 'error'); diff --git a/shine-UI/server-ui/js/update-server-pda-page.js b/shine-UI/server-ui/js/update-server-pda-page.js index 4207e30..d86a67d 100644 --- a/shine-UI/server-ui/js/update-server-pda-page.js +++ b/shine-UI/server-ui/js/update-server-pda-page.js @@ -104,6 +104,9 @@ resetExpectedKeysUi(); $('btnTopupDevnet').addEventListener('click', () => { try { + if (!String($('endpoint').value || '').includes('devnet')) { + throw new Error('Автопополнение доступно только для devnet. Для mainnet пополните device-кошелёк вручную.'); + } openDevnetTopup($('devPub').value); } catch (error) { setStatus($('status'), error?.message || String(error), 'error'); diff --git a/shine-UI/server-ui/read-pda.html b/shine-UI/server-ui/read-pda.html index 08fcb27..378429d 100644 --- a/shine-UI/server-ui/read-pda.html +++ b/shine-UI/server-ui/read-pda.html @@ -52,7 +52,7 @@

Параметры Solana

- +
diff --git a/shine-UI/server-ui/update-server-pda.html b/shine-UI/server-ui/update-server-pda.html index 2c5812d..26eb38f 100644 --- a/shine-UI/server-ui/update-server-pda.html +++ b/shine-UI/server-ui/update-server-pda.html @@ -56,7 +56,7 @@

Параметры Solana

- +
@@ -142,7 +142,7 @@
Положите SOL на этот адрес перед обновлением:
Это Solana-адрес (base58) device-ключа. С него оплачивается транзакция.
- +
Баланс device ещё не запрашивался.
diff --git a/shine-solana/shine/Anchor.toml b/shine-solana/shine/Anchor.toml index 8ee249d..0846c26 100644 --- a/shine-solana/shine/Anchor.toml +++ b/shine-solana/shine/Anchor.toml @@ -6,21 +6,26 @@ resolution = true skip-lint = false [programs.devnet] -shine_payments = "c4yTa4JT9EtQDCBX9LmWFK6T2gp4JGsuymFbom2EudW" -shine_users = "3bYrnXwLc56oVPUBAjY8zTMLwHCYq29b5rUMu3b64SQJ" -shine_login_guard = "3xkopA7cXagxzMFrKdv3NCBfV6BKiRJCk69kr27M2sRo" +shine_payments = "SHiPmXbM9Fs9khzRUW3TGKsS2W84aqaXTxs3ZkajW9v" +shine_users = "SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6" +shine_login_guard = "SHiGxGsXGioQYCYhchQ5R7KWoxN5UjFAFsucPf6sfnh" [programs.localnet] -shine_payments = "c4yTa4JT9EtQDCBX9LmWFK6T2gp4JGsuymFbom2EudW" -shine_users = "3bYrnXwLc56oVPUBAjY8zTMLwHCYq29b5rUMu3b64SQJ" -shine_login_guard = "3xkopA7cXagxzMFrKdv3NCBfV6BKiRJCk69kr27M2sRo" +shine_payments = "SHiPmXbM9Fs9khzRUW3TGKsS2W84aqaXTxs3ZkajW9v" +shine_users = "SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6" +shine_login_guard = "SHiGxGsXGioQYCYhchQ5R7KWoxN5UjFAFsucPf6sfnh" + +[programs.mainnet] +shine_login_guard = "SHiGxGsXGioQYCYhchQ5R7KWoxN5UjFAFsucPf6sfnh" +shine_users = "SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6" +shine_payments = "SHiPmXbM9Fs9khzRUW3TGKsS2W84aqaXTxs3ZkajW9v" [registry] url = "https://api.apr.dev" [provider] -cluster = "devnet" -wallet = "~/.config/solana/id.json" +cluster = "mainnet" +wallet = "/home/ai/work/SHiNE/keys_for_create_DAO/dao-issuer.json" [workspace] members = ["programs/shine_users", "programs/shine_payments", "programs/shine_login_guard"] diff --git a/shine-solana/shine/Cargo.lock b/shine-solana/shine/Cargo.lock index f660f0e..b92b97e 100644 --- a/shine-solana/shine/Cargo.lock +++ b/shine-solana/shine/Cargo.lock @@ -14,6 +14,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + [[package]] name = "anchor-attribute-access-control" version = "0.31.1" @@ -140,6 +149,7 @@ dependencies = [ "anchor-derive-accounts", "anchor-derive-serde", "anchor-derive-space", + "anchor-lang-idl", "base64 0.21.7", "bincode", "borsh 0.10.4", @@ -157,6 +167,7 @@ dependencies = [ "anchor-lang-idl-spec", "anyhow", "heck", + "regex", "serde", "serde_json", "sha2 0.10.9", @@ -180,6 +191,7 @@ checksum = "5dc7a6d90cc643df0ed2744862cdf180587d1e5d28936538c18fc8908489ed67" dependencies = [ "anyhow", "bs58", + "cargo_toml", "heck", "proc-macro2", "quote", @@ -398,6 +410,16 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "cargo_toml" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a98356df42a2eb1bd8f1793ae4ee4de48e384dd974ce5eac8eee802edb7492be" +dependencies = [ + "serde", + "toml 0.8.22", +] + [[package]] name = "cc" version = "1.2.23" @@ -900,7 +922,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" dependencies = [ - "toml", + "toml 0.5.11", ] [[package]] @@ -1044,6 +1066,35 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + [[package]] name = "rustc_version" version = "0.4.1" @@ -1118,6 +1169,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + [[package]] name = "sha2" version = "0.9.9" @@ -1156,6 +1216,7 @@ dependencies = [ name = "shine_login_guard" version = "0.1.0" dependencies = [ + "anchor-lang", "solana-program", ] @@ -2082,11 +2143,26 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + [[package]] name = "toml_datetime" version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -2095,10 +2171,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", + "serde", + "serde_spanned", "toml_datetime", + "toml_write", "winnow", ] +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "typenum" version = "1.18.0" diff --git a/shine-solana/shine/build.gradle b/shine-solana/shine/build.gradle index c0408e3..d6fa8d3 100644 --- a/shine-solana/shine/build.gradle +++ b/shine-solana/shine/build.gradle @@ -46,5 +46,5 @@ tasks.register("checkUiRemote", Exec) { "grep -n 'test-solana-tickets.shineup.me\\|test-solana-tickets.shiningpeople.ru' /home/player/SHiNE/caddy/Caddyfile; " + "echo; " + "echo 'Program ID в загруженных html:'; " + - "grep -R -n 'c4yTa4JT9EtQDCBX9LmWFK6T2gp4JGsuymFbom2EudW' /home/player/sites/test-solana-tickets.shineup.me/*.html" + "grep -R -n 'SHiPmXbM9Fs9khzRUW3TGKsS2W84aqaXTxs3ZkajW9v' /home/player/sites/test-solana-tickets.shineup.me/*.html" } diff --git a/shine-solana/shine/create-SHiNE-DAO/README.md b/shine-solana/shine/create-SHiNE-DAO/README.md index ebfd5d9..5cf889c 100644 --- a/shine-solana/shine/create-SHiNE-DAO/README.md +++ b/shine-solana/shine/create-SHiNE-DAO/README.md @@ -1,6 +1,6 @@ # create-SHiNE-DAO -Чистый актуальный комплект для создания SHiNE DAO с governance NFT и для работы с NFT, которые: +Чистый актуальный комплект для создания SHiNE DAO с переносимым governance mint (`Liquid`) и для работы с NFT, которые: 1. можно выпускать через голосование DAO; 2. можно сжигать через голосование DAO; @@ -15,16 +15,18 @@ ## Что внутри -- `dao.config.env.example` - шаблон конфига для создания самого DAO. +- `dao.config.env.example` - шаблон конфига для создания mint и DAO в три шага. +- `scripts/01_create_governance_mint.js` - создание governance mint и metadata без депозита в Realm. +- `scripts/02_create_dao_with_governance_nfts.js` - создание `Realm / TokenOwnerRecord / Governance / Treasury` без deposit и без передачи authority. +- `scripts/03_transfer_dao_authorities.js` - передача `mint authority`, `freeze authority`, `realm authority` и `metadata update authority` на DAO. - `nft-flow.config.env.example` - шаблон конфига для выпуска и burn NFT через уже созданное DAO. -- `scripts/01_check_dao_config.sh` - предстаровая проверка конфига DAO. -- `scripts/02_create_dao_with_governance_nfts.js` - создание DAO с governance NFT. -- `scripts/03_prepare_second_voter.js` - подготовка второго голосующего. -- `scripts/04_create_empty_nft_template_with_metadata.js` - создание NFT mint-шаблона с metadata и картинкой. -- `scripts/05_propose_vote_mint_nft.js` - proposal/sign/vote на mint NFT. -- `scripts/06_execute_mint_nft.js` - execute mint proposal. -- `scripts/07_propose_vote_burn_nft.js` - proposal/sign/vote на burn NFT. -- `scripts/08_execute_burn_nft.js` - execute burn proposal. +- `scripts/01_check_dao_config.sh` - проверка конфига перед созданием DAO из существующего mint. +- `scripts/04_prepare_second_voter.js` - подготовка второго голосующего. +- `scripts/05_create_empty_nft_template_with_metadata.js` - создание NFT mint-шаблона с metadata и картинкой. +- `scripts/06_propose_vote_mint_nft.js` - proposal/sign/vote на mint NFT. +- `scripts/07_execute_mint_nft.js` - execute mint proposal. +- `scripts/08_propose_vote_burn_nft.js` - proposal/sign/vote на burn NFT. +- `scripts/09_execute_burn_nft.js` - execute burn proposal. - `docs/FUTURE_FAST_BURN_EXECUTOR.md` - описание будущей программы для быстрого burn одного конкретного `mint`. ## Подтвержденная база, на которой собран комплект @@ -74,9 +76,9 @@ npm install - `solana-keygen` - `node` -## Быстрый сценарий: создать DAO и запустить NFT-flow +## Быстрый сценарий: создать mint, раздать токены, создать DAO и затем передать все права на DAO -### 1. Создать конфиг DAO +### 1. Создать конфиг ```bash cp create-SHiNE-DAO/dao.config.env.example create-SHiNE-DAO/dao.config.env @@ -86,29 +88,116 @@ cp create-SHiNE-DAO/dao.config.env.example create-SHiNE-DAO/dao.config.env - имя DAO; - путь к keypair создателя; -- имя и символ governance NFT; -- `DAO_GOV_NFT_SUPPLY` (обычно `10`); +- имя и символ governance mint; +- при необходимости `DAO_GOV_MINT_KEYPAIR` для красивого или фиксированного mint address; - `DAO_GOV_TOKEN_METADATA_URI` и `DAO_GOV_TOKEN_IMAGE_URL`. -### 2. Проверить конфиг DAO +Обычно используются два keypair-файла: + +- `DAO_ISSUER_KEYPAIR` - временный кошелёк создателя/оператора; +- `DAO_GOV_MINT_KEYPAIR` - keypair самого mint, если нужен красивый адрес governance mint. + +Сгенерировать их можно так: + +```bash +solana-keygen new --outfile ./dao-issuer.json +solana-keygen new --outfile ./dao-gov-mint.json +``` + +Публичные ключи посмотреть так: + +```bash +solana-keygen pubkey ./dao-issuer.json +solana-keygen pubkey ./dao-gov-mint.json +``` + +Практическое правило: + +- первый keypair обычно временный, его красивый адрес не важен; +- красивым обычно имеет смысл делать именно `DAO_GOV_MINT_KEYPAIR`, потому что это постоянный публичный адрес governance mint. + +### 2. Создать governance mint + +```bash +node create-SHiNE-DAO/scripts/01_create_governance_mint.js ./create-SHiNE-DAO/dao.config.env +``` + +Этот шаг: + +1. создаёт mint; +2. прикрепляет metadata на Arweave; +3. оставляет у кошелька создателя: + - `mint authority` + - `freeze authority` + - `metadata update authority` + +Актуальные Arweave-ссылки для governance mint сейчас такие: + +- image: `https://arweave.net/rPGo-uYIFoLxMBVTGyPr5K6fuF0y9xL-8TlAaZyBJgY` +- metadata: `https://arweave.net/PdupvtRhj_DTBjTLcjD7Z5GSiEcFUf01GHaYMDs6gGw` + +### 3. Раздать токены вручную + +```bash +spl-token mint 1 +spl-token mint 1 +``` + +После этого можно руками наминтить управляющие токены на нужные кошельки. +На этом этапе токен ещё обычный и переносимый. + +### 4. Проверить конфиг DAO ```bash ./create-SHiNE-DAO/scripts/01_check_dao_config.sh ./create-SHiNE-DAO/dao.config.env ``` -### 3. Создать DAO +Перед проверкой в `dao.config.env` нужно указать: + +- `DAO_GOV_TOKEN_MINT` - уже созданный mint; +- `DAO_REALM_NAME` - новое имя DAO; +- `DAO_ISSUER_KEYPAIR` - кошелёк создателя; +- `DAO_VOTING_TIME_SEC` - по умолчанию `86400` секунд (1 день); +- `DAO_APPROVAL_THRESHOLD_PERCENT`. + +### 5. Создать DAO ```bash node create-SHiNE-DAO/scripts/02_create_dao_with_governance_nfts.js ./create-SHiNE-DAO/dao.config.env ``` +Этот шаг: + +- создаёт `Realm`; +- создаёт `TokenOwnerRecord` для кошелька создателя без deposit; +- создаёт `Governance` и `Native Treasury`; +- задаёт community token type = `Liquid`; +- не делает deposit токенов; +- не передаёт authority. + После этого из отчета взять: - `realm` - `governance` - `communityMint` +- `tokenOwnerRecord` -### 4. Создать конфиг NFT-flow +### 6. Передать все authority на DAO + +```bash +node create-SHiNE-DAO/scripts/03_transfer_dao_authorities.js ./create-SHiNE-DAO/dao.config.env +``` + +Этот шаг передаёт на DAO: + +- `mint authority` +- `freeze authority` +- `realm authority` +- `metadata update authority` + +После этого у создателя не должно остаться административных прав на governance mint и Realm. + +### 7. Создать конфиг NFT-flow ```bash cp create-SHiNE-DAO/nft-flow.config.env.example create-SHiNE-DAO/nft-flow.config.env @@ -123,33 +212,33 @@ cp create-SHiNE-DAO/nft-flow.config.env.example create-SHiNE-DAO/nft-flow.config - `VOTER2_KEYPAIR` - metadata URI и картинку для целевого NFT. -### 5. Подготовить второго голосующего +### 8. Подготовить второго голосующего ```bash -node create-SHiNE-DAO/scripts/03_prepare_second_voter.js ./create-SHiNE-DAO/nft-flow.config.env +node create-SHiNE-DAO/scripts/04_prepare_second_voter.js ./create-SHiNE-DAO/nft-flow.config.env ``` -### 6. Создать NFT mint-шаблон с metadata +### 9. Создать NFT mint-шаблон с metadata ```bash -node create-SHiNE-DAO/scripts/04_create_empty_nft_template_with_metadata.js ./create-SHiNE-DAO/nft-flow.config.env +node create-SHiNE-DAO/scripts/05_create_empty_nft_template_with_metadata.js ./create-SHiNE-DAO/nft-flow.config.env ``` Скрипт вернет `mint` будущего NFT. -### 7. Поднять proposal на mint NFT +### 10. Поднять proposal на mint NFT ```bash -node create-SHiNE-DAO/scripts/05_propose_vote_mint_nft.js \ +node create-SHiNE-DAO/scripts/06_propose_vote_mint_nft.js \ ./create-SHiNE-DAO/nft-flow.config.env \ \ ``` -### 8. Выполнить mint proposal +### 11. Выполнить mint proposal ```bash -node create-SHiNE-DAO/scripts/06_execute_mint_nft.js \ +node create-SHiNE-DAO/scripts/07_execute_mint_nft.js \ ./create-SHiNE-DAO/nft-flow.config.env \ \ \ @@ -157,19 +246,19 @@ node create-SHiNE-DAO/scripts/06_execute_mint_nft.js \ ``` -### 9. Поднять proposal на burn NFT +### 12. Поднять proposal на burn NFT ```bash -node create-SHiNE-DAO/scripts/07_propose_vote_burn_nft.js \ +node create-SHiNE-DAO/scripts/08_propose_vote_burn_nft.js \ ./create-SHiNE-DAO/nft-flow.config.env \ \ ``` -### 10. Выполнить burn proposal +### 13. Выполнить burn proposal ```bash -node create-SHiNE-DAO/scripts/08_execute_burn_nft.js \ +node create-SHiNE-DAO/scripts/09_execute_burn_nft.js \ ./create-SHiNE-DAO/nft-flow.config.env \ \ \ @@ -190,12 +279,18 @@ node create-SHiNE-DAO/scripts/08_execute_burn_nft.js \ ## Что важно про стартовые governance NFT -На старте governance NFT mint'ятся создателю DAO, а затем депонируются в Realm. +В новом трёхшаговом flow governance mint создается отдельно, токены можно распределить вручную до создания DAO, а затем отдельным шагом DAO получает права на: -Практически это означает: +- `mint authority` +- `freeze authority` +- `realm authority` +- `metadata update authority` -- первоначальный voting power принадлежит создателю DAO; -- затем governance NFT можно дополнительно распределять между другими участниками. +Это сделано специально, чтобы: + +1. сначала спокойно распределить токены; +2. затем создать Realm без блокировки стартовых токенов; +3. и только потом окончательно передать контроль DAO. ## Future diff --git a/shine-solana/shine/create-SHiNE-DAO/dao.config.env.example b/shine-solana/shine/create-SHiNE-DAO/dao.config.env.example index 22d03ab..2057770 100644 --- a/shine-solana/shine/create-SHiNE-DAO/dao.config.env.example +++ b/shine-solana/shine/create-SHiNE-DAO/dao.config.env.example @@ -4,28 +4,33 @@ DAO_CLUSTER="devnet" # Для каждого нового запуска должен быть новый realm name. -DAO_REALM_NAME="SHiNE NFT DAO 2026-06-30" +DAO_REALM_NAME="SHiNE DAO2" # Governance NFT коллекция DAO DAO_GOV_NFT_NAME="SHiNE Governance NFT" DAO_GOV_NFT_SYMBOL="SHINE-GOV" -DAO_GOV_TOKEN_DESCRIPTION="Governance NFT for SHiNE DAO" +DAO_GOV_TOKEN_DESCRIPTION="Токен для голосования в SHiNE DAO. Voting token for SHiNE DAO." -# Metadata JSON governance mint -DAO_GOV_TOKEN_METADATA_URI="https://arweave.net/CHANGE_ME_METADATA_JSON" -DAO_GOV_TOKEN_IMAGE_URL="https://arweave.net/CHANGE_ME_IMAGE" +# Шаг 1: создание mint +# Первый keypair временный: он нужен только как оператор для старта. +# Красивым обычно имеет смысл делать именно mint keypair. +DAO_GOV_MINT_KEYPAIR="/home/ai/work/SHiNE/keys_for_create_DAO/dao-gov-mint.json" -# Стартовое число governance NFT -DAO_GOV_NFT_SUPPLY="10" +# Metadata для governance mint +DAO_GOV_TOKEN_METADATA_URI="https://arweave.net/PdupvtRhj_DTBjTLcjD7Z5GSiEcFUf01GHaYMDs6gGw" +DAO_GOV_TOKEN_IMAGE_URL="https://arweave.net/rPGo-uYIFoLxMBVTGyPr5K6fuF0y9xL-8TlAaZyBJgY" -# Минимум для тестов Realms - 3600 -DAO_VOTING_TIME_SEC="3600" +# Шаг 2: создание DAO из уже существующего mint +DAO_GOV_TOKEN_MINT="" + +# Дефолт для новых DAO - 86400 секунд (1 день) +DAO_VOTING_TIME_SEC="86400" # 51..100 DAO_APPROVAL_THRESHOLD_PERCENT="51" -# Создатель DAO -DAO_ISSUER_KEYPAIR="$HOME/.config/solana/phantomWallet.json" +# Создатель DAO / временный оператор до передачи authority на DAO +DAO_ISSUER_KEYPAIR="/home/ai/work/SHiNE/keys_for_create_DAO/dao-issuer.json" # Governance program (Realms) SPL_GOVERNANCE_PROGRAM_ID="GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw" diff --git a/shine-solana/shine/create-SHiNE-DAO/scripts/01_check_dao_config.sh b/shine-solana/shine/create-SHiNE-DAO/scripts/01_check_dao_config.sh index 0dbc020..ea3bf78 100755 --- a/shine-solana/shine/create-SHiNE-DAO/scripts/01_check_dao_config.sh +++ b/shine-solana/shine/create-SHiNE-DAO/scripts/01_check_dao_config.sh @@ -23,7 +23,7 @@ require_cmd solana require_cmd solana-keygen require_cmd node -if [[ -z "${DAO_REALM_NAME:-}" || -z "${DAO_CLUSTER:-}" || -z "${DAO_ISSUER_KEYPAIR:-}" || -z "${SPL_GOVERNANCE_PROGRAM_ID:-}" ]]; then +if [[ -z "${DAO_REALM_NAME:-}" || -z "${DAO_CLUSTER:-}" || -z "${DAO_ISSUER_KEYPAIR:-}" || -z "${SPL_GOVERNANCE_PROGRAM_ID:-}" || -z "${DAO_GOV_TOKEN_MINT:-}" ]]; then echo "Ошибка: обязательные поля конфига пустые" exit 1 fi @@ -33,7 +33,7 @@ if [[ ! -f "$DAO_ISSUER_KEYPAIR" ]]; then exit 1 fi -if ! [[ "${DAO_VOTING_TIME_SEC}" =~ ^[0-9]+$ ]] || ! [[ "${DAO_GOV_NFT_SUPPLY}" =~ ^[0-9]+$ ]] || ! [[ "${DAO_APPROVAL_THRESHOLD_PERCENT}" =~ ^[0-9]+$ ]]; then +if ! [[ "${DAO_VOTING_TIME_SEC}" =~ ^[0-9]+$ ]] || ! [[ "${DAO_APPROVAL_THRESHOLD_PERCENT}" =~ ^[0-9]+$ ]]; then echo "Ошибка: числовые параметры заданы некорректно" exit 1 fi @@ -45,6 +45,12 @@ fi ISSUER_PUBKEY="$(solana-keygen pubkey "$DAO_ISSUER_KEYPAIR")" ISSUER_BALANCE="$(solana balance "$ISSUER_PUBKEY" --url "$DAO_CLUSTER" 2>/dev/null || true)" +MINT_PUBKEY="$DAO_GOV_TOKEN_MINT" + +if ! node -e "const { PublicKey } = require('@solana/web3.js'); new PublicKey(process.argv[1])" "$MINT_PUBKEY" >/dev/null 2>&1; then + echo "Ошибка: DAO_GOV_TOKEN_MINT не является валидным pubkey" + exit 1 +fi REALM_PDA="$(node - "$DAO_REALM_NAME" "$SPL_GOVERNANCE_PROGRAM_ID" <<'NODE' const { PublicKey } = require("@solana/web3.js"); @@ -63,6 +69,11 @@ if solana account "$REALM_PDA" --url "$DAO_CLUSTER" >/dev/null 2>&1; then REALM_EXISTS="yes" fi +MINT_EXISTS="no" +if solana account "$MINT_PUBKEY" --url "$DAO_CLUSTER" >/dev/null 2>&1; then + MINT_EXISTS="yes" +fi + cat < assertRequired(cfg, k)); + + const cluster = cfg.DAO_CLUSTER; + const connection = new Connection(clusterUrl(cluster), "confirmed"); + const issuer = loadKeypair(path.resolve(cfg.DAO_ISSUER_KEYPAIR)); + const mintKeypair = cfg.DAO_GOV_MINT_KEYPAIR + ? loadKeypair(path.resolve(cfg.DAO_GOV_MINT_KEYPAIR)) + : Keypair.generate(); + const governanceProgramId = new PublicKey(cfg.SPL_GOVERNANCE_PROGRAM_ID); + + const startBalance = await connection.getBalance(issuer.publicKey, "confirmed"); + console.log("============================================================"); + console.log("CREATE SHiNE GOVERNANCE MINT"); + console.log("------------------------------------------------------------"); + console.log("Сеть: ", cluster); + console.log("Mint: ", mintKeypair.publicKey.toBase58()); + console.log("Issuer: ", issuer.publicKey.toBase58()); + console.log("Governance program:", governanceProgramId.toBase58()); + console.log("Баланс до: ", `${lamportsToSol(startBalance)} SOL`); + console.log("Name: ", cfg.DAO_GOV_NFT_NAME); + console.log("Symbol: ", cfg.DAO_GOV_NFT_SYMBOL); + console.log("Metadata URI: ", cfg.DAO_GOV_TOKEN_METADATA_URI); + console.log("Image URL: ", cfg.DAO_GOV_TOKEN_IMAGE_URL); + console.log("============================================================"); + + const mintLen = getMintLen([]); + const mintRent = await connection.getMinimumBalanceForRentExemption(mintLen); + const txMint = new Transaction().add( + SystemProgram.createAccount({ + fromPubkey: issuer.publicKey, + newAccountPubkey: mintKeypair.publicKey, + space: mintLen, + lamports: mintRent, + programId: TOKEN_PROGRAM_ID, + }), + createInitializeMintInstruction(mintKeypair.publicKey, 0, issuer.publicKey, issuer.publicKey, TOKEN_PROGRAM_ID) + ); + const sigMint = await sendAndConfirmTransaction(connection, txMint, [issuer, mintKeypair], { + commitment: "confirmed", + }); + + const sigMetadata = await attachGovernanceMetadata(cfg, cluster, issuer, mintKeypair.publicKey, mintKeypair); + + const report = { + createdAt: new Date().toISOString(), + cluster, + mint: mintKeypair.publicKey.toBase58(), + issuer: issuer.publicKey.toBase58(), + mintAuthority: issuer.publicKey.toBase58(), + freezeAuthority: issuer.publicKey.toBase58(), + metadataUpdateAuthority: issuer.publicKey.toBase58(), + governanceProgramId: governanceProgramId.toBase58(), + metadataUri: cfg.DAO_GOV_TOKEN_METADATA_URI, + imageUrl: cfg.DAO_GOV_TOKEN_IMAGE_URL, + txMint: sigMint, + txMetadata: sigMetadata, + }; + const runsDir = path.resolve(__dirname, "..", "local", "runs"); + ensureDir(runsDir); + const reportPath = path.join(runsDir, `${nowStamp()}_governance_mint.json`); + fs.writeFileSync(reportPath, JSON.stringify(report, null, 2)); + + console.log("Готово. Governance mint создан."); + console.log("mint:", mintKeypair.publicKey.toBase58()); + console.log("report:", reportPath); +} + +main().catch((e) => { + console.error("CreateMint error:", e?.message || e); + process.exit(1); +}); diff --git a/shine-solana/shine/create-SHiNE-DAO/scripts/01a_mint_governance_token.js b/shine-solana/shine/create-SHiNE-DAO/scripts/01a_mint_governance_token.js new file mode 100644 index 0000000..d8a52fa --- /dev/null +++ b/shine-solana/shine/create-SHiNE-DAO/scripts/01a_mint_governance_token.js @@ -0,0 +1,68 @@ +#!/usr/bin/env node +"use strict"; + +const path = require("path"); +const { Connection, PublicKey, Transaction, sendAndConfirmTransaction } = require("@solana/web3.js"); +const { + TOKEN_PROGRAM_ID, + getAssociatedTokenAddressSync, + createAssociatedTokenAccountIdempotentInstruction, + createMintToInstruction, + getMint, +} = require("@solana/spl-token"); +const { clusterUrl, loadKeypair, parseEnvConfig, resolveConfigPath } = require("./js_common"); + +async function main() { + const targetWallet = process.argv[3]; + const amountRaw = process.argv[4] || "1"; + if (!targetWallet) { + throw new Error("Usage: node 01a_mint_governance_token.js [amount]"); + } + + const cfg = parseEnvConfig(resolveConfigPath(process.argv[2], "dao.config.env")); + if (!cfg.DAO_GOV_TOKEN_MINT) throw new Error("В конфиге пустой DAO_GOV_TOKEN_MINT"); + if (!cfg.DAO_ISSUER_KEYPAIR) throw new Error("В конфиге пустой DAO_ISSUER_KEYPAIR"); + + const conn = new Connection(clusterUrl(cfg.DAO_CLUSTER || "devnet"), "confirmed"); + const issuer = loadKeypair(path.resolve(cfg.DAO_ISSUER_KEYPAIR)); + const mint = new PublicKey(cfg.DAO_GOV_TOKEN_MINT); + const target = new PublicKey(targetWallet); + const amount = BigInt(amountRaw); + + const mintInfo = await getMint(conn, mint, "confirmed", TOKEN_PROGRAM_ID); + if (mintInfo.decimals !== 0) { + throw new Error(`Ожидался governance mint с decimals=0, а получено ${mintInfo.decimals}`); + } + + const ata = getAssociatedTokenAddressSync(mint, target, false, TOKEN_PROGRAM_ID); + const tx = new Transaction().add( + createAssociatedTokenAccountIdempotentInstruction( + issuer.publicKey, + ata, + target, + mint, + TOKEN_PROGRAM_ID + ), + createMintToInstruction( + mint, + ata, + issuer.publicKey, + amount, + [], + TOKEN_PROGRAM_ID + ) + ); + + const sig = await sendAndConfirmTransaction(conn, tx, [issuer], { commitment: "confirmed" }); + + console.log("Mint done."); + console.log("target:", target.toBase58()); + console.log("ata:", ata.toBase58()); + console.log("amount:", amount.toString()); + console.log("signature:", sig); +} + +main().catch((e) => { + console.error("MintGovernanceToken error:", e?.message || e); + process.exit(1); +}); diff --git a/shine-solana/shine/create-SHiNE-DAO/scripts/02_create_dao_with_governance_nfts.js b/shine-solana/shine/create-SHiNE-DAO/scripts/02_create_dao_with_governance_nfts.js index 6cb94bb..b28f258 100644 --- a/shine-solana/shine/create-SHiNE-DAO/scripts/02_create_dao_with_governance_nfts.js +++ b/shine-solana/shine/create-SHiNE-DAO/scripts/02_create_dao_with_governance_nfts.js @@ -7,20 +7,14 @@ const readline = require("readline"); const BN = require("bn.js"); const { Connection, - Keypair, - SystemProgram, + PublicKey, Transaction, sendAndConfirmTransaction, } = require("@solana/web3.js"); const { TOKEN_PROGRAM_ID, - AuthorityType, - getMintLen, - createInitializeMintInstruction, getAssociatedTokenAddressSync, - createAssociatedTokenAccountIdempotentInstruction, - createMintToInstruction, - createSetAuthorityInstruction, + getAccount, } = require("@solana/spl-token"); const { MintMaxVoteWeightSource, @@ -32,17 +26,11 @@ const { GoverningTokenConfigAccountArgs, GoverningTokenType, withCreateRealm, - withDepositGoverningTokens, + withCreateTokenOwnerRecord, withCreateGovernance, withCreateNativeTreasury, - withSetRealmAuthority, - SetRealmAuthorityAction, } = require("@solana/spl-governance"); -const { createUmi } = require("@metaplex-foundation/umi-bundle-defaults"); -const { createSignerFromKeypair, signerIdentity, percentAmount, none, some } = require("@metaplex-foundation/umi"); -const { fromWeb3JsKeypair, fromWeb3JsPublicKey } = require("@metaplex-foundation/umi-web3js-adapters"); -const { mplTokenMetadata, createV1, TokenStandard } = require("@metaplex-foundation/mpl-token-metadata"); -const { PublicKey, assertRequired, clusterUrl, ensureArweaveUri, ensureDir, loadKeypair, parseEnvConfig, resolveConfigPath, nowStamp } = require("./js_common"); +const { clusterUrl, ensureDir, loadKeypair, parseEnvConfig, resolveConfigPath, nowStamp } = require("./js_common"); function lamportsToSol(lamports) { return Number(lamports) / 1_000_000_000; @@ -57,43 +45,6 @@ async function askYes() { return answer.trim() === "YES"; } -async function attachGovernanceMetadata(cfg, cluster, issuer, mintPubkey, mintKeypair) { - ensureArweaveUri("DAO_GOV_TOKEN_METADATA_URI", cfg.DAO_GOV_TOKEN_METADATA_URI); - ensureArweaveUri("DAO_GOV_TOKEN_IMAGE_URL", cfg.DAO_GOV_TOKEN_IMAGE_URL); - - const umi = createUmi(clusterUrl(cluster)); - const issuerSigner = createSignerFromKeypair(umi, fromWeb3JsKeypair(issuer)); - const mintSigner = createSignerFromKeypair(umi, fromWeb3JsKeypair(mintKeypair)); - umi.use(signerIdentity(issuerSigner)); - umi.use(mplTokenMetadata()); - - const builder = createV1(umi, { - mint: mintSigner, - authority: issuerSigner, - payer: issuerSigner, - updateAuthority: issuerSigner, - name: cfg.DAO_GOV_NFT_NAME, - symbol: cfg.DAO_GOV_NFT_SYMBOL, - uri: cfg.DAO_GOV_TOKEN_METADATA_URI, - sellerFeeBasisPoints: percentAmount(0), - tokenStandard: TokenStandard.Fungible, - decimals: some(0), - creators: none(), - collection: none(), - uses: none(), - collectionDetails: none(), - ruleSet: none(), - printSupply: none(), - primarySaleHappened: false, - isMutable: true, - isCollection: false, - splTokenProgram: fromWeb3JsPublicKey(TOKEN_PROGRAM_ID), - }); - - const result = await builder.sendAndConfirm(umi); - return Buffer.from(result.signature).toString("base64"); -} - async function main() { const configPath = resolveConfigPath(process.argv[2], "dao.config.env"); if (!fs.existsSync(configPath)) throw new Error(`Конфиг не найден: ${configPath}`); @@ -104,26 +55,31 @@ async function main() { "DAO_REALM_NAME", "DAO_GOV_NFT_NAME", "DAO_GOV_NFT_SYMBOL", - "DAO_GOV_NFT_SUPPLY", - "DAO_VOTING_TIME_SEC", - "DAO_APPROVAL_THRESHOLD_PERCENT", "DAO_ISSUER_KEYPAIR", "SPL_GOVERNANCE_PROGRAM_ID", - "DAO_GOV_TOKEN_METADATA_URI", - "DAO_GOV_TOKEN_IMAGE_URL", - ].forEach((k) => assertRequired(cfg, k)); + "DAO_GOV_TOKEN_MINT", + ].forEach((k) => { + if (!cfg[k]) throw new Error(`В конфиге отсутствует обязательный параметр: ${k}`); + }); const cluster = cfg.DAO_CLUSTER; const connection = new Connection(clusterUrl(cluster), "confirmed"); const issuer = loadKeypair(path.resolve(cfg.DAO_ISSUER_KEYPAIR)); const governanceProgramId = new PublicKey(cfg.SPL_GOVERNANCE_PROGRAM_ID); - const supply = Number(cfg.DAO_GOV_NFT_SUPPLY); - const votingTimeSec = Number(cfg.DAO_VOTING_TIME_SEC); - const thresholdPct = Number(cfg.DAO_APPROVAL_THRESHOLD_PERCENT); + const mint = new PublicKey(cfg.DAO_GOV_TOKEN_MINT); - if (!Number.isInteger(supply) || supply <= 0) throw new Error("DAO_GOV_NFT_SUPPLY должен быть > 0"); - if (!Number.isInteger(votingTimeSec) || votingTimeSec < 3600) throw new Error("DAO_VOTING_TIME_SEC должен быть >= 3600"); - if (!Number.isInteger(thresholdPct) || thresholdPct < 51 || thresholdPct > 100) throw new Error("DAO_APPROVAL_THRESHOLD_PERCENT должен быть в диапазоне 51..100"); + const mintInfoBefore = await connection.getParsedAccountInfo(mint, "confirmed"); + if (!mintInfoBefore.value) throw new Error(`Governing mint не найден: ${mint.toBase58()}`); + const mintAccountBefore = await connection.getAccountInfo(mint, "confirmed"); + if (!mintAccountBefore || !mintAccountBefore.owner.equals(TOKEN_PROGRAM_ID)) { + throw new Error(`Mint должен быть классическим SPL Token mint: ${mint.toBase58()}`); + } + + const mintParsed = mintInfoBefore.value.data.parsed.info; + const mintAuthorityBefore = mintParsed.mintAuthority || null; + const freezeAuthorityBefore = mintParsed.freezeAuthority || null; + const issuerAta = getAssociatedTokenAddressSync(mint, issuer.publicKey, false, TOKEN_PROGRAM_ID); + const issuerAtaInfo = await getAccount(connection, issuerAta, "confirmed").catch(() => null); const [realmPda] = PublicKey.findProgramAddressSync( [Buffer.from("governance"), Buffer.from(cfg.DAO_REALM_NAME, "utf8")], @@ -135,18 +91,19 @@ async function main() { const startBalance = await connection.getBalance(issuer.publicKey, "confirmed"); console.log("============================================================"); - console.log("CREATE SHiNE DAO WITH GOVERNANCE NFT"); + console.log("CREATE SHiNE DAO FROM EXISTING GOVERNANCE MINT"); console.log("------------------------------------------------------------"); console.log("Сеть: ", cluster); console.log("Realm name: ", cfg.DAO_REALM_NAME); console.log("Realm PDA: ", realmPda.toBase58()); console.log("Issuer: ", issuer.publicKey.toBase58()); - console.log("Supply: ", supply); - console.log("Voting time: ", votingTimeSec); - console.log("Threshold %: ", thresholdPct); + console.log("Governance mint: ", mint.toBase58()); + console.log("Issuer ATA: ", issuerAta.toBase58()); + console.log("Issuer ATA есть: ", issuerAtaInfo ? "yes" : "no"); + console.log("Issuer ATA amount:", issuerAtaInfo ? issuerAtaInfo.amount.toString() : "0"); console.log("Баланс до: ", `${lamportsToSol(startBalance)} SOL`); - console.log("Metadata URI: ", cfg.DAO_GOV_TOKEN_METADATA_URI); - console.log("Image URL: ", cfg.DAO_GOV_TOKEN_IMAGE_URL); + console.log("Mint authority: ", mintAuthorityBefore || "null"); + console.log("Freeze authority:", freezeAuthorityBefore || "null"); console.log("============================================================"); if (!(await askYes())) { @@ -154,40 +111,11 @@ async function main() { return; } - const mintKeypair = Keypair.generate(); - const mintLen = getMintLen([]); - const mintRent = await connection.getMinimumBalanceForRentExemption(mintLen); - const issuerAta = getAssociatedTokenAddressSync(mintKeypair.publicKey, issuer.publicKey, false, TOKEN_PROGRAM_ID); - - const txMint = new Transaction().add( - SystemProgram.createAccount({ - fromPubkey: issuer.publicKey, - newAccountPubkey: mintKeypair.publicKey, - space: mintLen, - lamports: mintRent, - programId: TOKEN_PROGRAM_ID, - }), - createInitializeMintInstruction(mintKeypair.publicKey, 0, issuer.publicKey, issuer.publicKey, TOKEN_PROGRAM_ID), - createAssociatedTokenAccountIdempotentInstruction( - issuer.publicKey, - issuerAta, - issuer.publicKey, - mintKeypair.publicKey, - TOKEN_PROGRAM_ID - ), - createMintToInstruction(mintKeypair.publicKey, issuerAta, issuer.publicKey, supply, [], TOKEN_PROGRAM_ID) - ); - const sigMint = await sendAndConfirmTransaction(connection, txMint, [issuer, mintKeypair], { - commitment: "confirmed", - }); - - const sigMetadata = await attachGovernanceMetadata(cfg, cluster, issuer, mintKeypair.publicKey, mintKeypair); - const ixRealm = []; const communityTokenConfig = new GoverningTokenConfigAccountArgs({ voterWeightAddin: undefined, maxVoterWeightAddin: undefined, - tokenType: GoverningTokenType.Membership, + tokenType: GoverningTokenType.Liquid, }); const realmPk = await withCreateRealm( ixRealm, @@ -195,7 +123,7 @@ async function main() { PROGRAM_VERSION_V3, cfg.DAO_REALM_NAME, issuer.publicKey, - mintKeypair.publicKey, + mint, issuer.publicKey, undefined, MintMaxVoteWeightSource.FULL_SUPPLY_FRACTION, @@ -203,29 +131,32 @@ async function main() { communityTokenConfig, undefined ); - const sigRealm = await sendAndConfirmTransaction(connection, new Transaction().add(...ixRealm), [issuer], { commitment: "confirmed" }); + const sigRealm = await sendAndConfirmTransaction(connection, new Transaction().add(...ixRealm), [issuer], { + commitment: "confirmed", + }); - const ixDeposit = []; - const tokenOwnerRecordPk = await withDepositGoverningTokens( - ixDeposit, + const ixTokenOwnerRecord = []; + const tokenOwnerRecordPk = await withCreateTokenOwnerRecord( + ixTokenOwnerRecord, governanceProgramId, PROGRAM_VERSION_V3, realmPk, - issuerAta, - mintKeypair.publicKey, issuer.publicKey, - issuer.publicKey, - issuer.publicKey, - new BN(supply), - true + mint, + issuer.publicKey + ); + const sigTokenOwnerRecord = await sendAndConfirmTransaction( + connection, + new Transaction().add(...ixTokenOwnerRecord), + [issuer], + { commitment: "confirmed" } ); - const sigDeposit = await sendAndConfirmTransaction(connection, new Transaction().add(...ixDeposit), [issuer], { commitment: "confirmed" }); const governanceConfig = new GovernanceConfig({ - communityVoteThreshold: new VoteThreshold({ type: VoteThresholdType.YesVotePercentage, value: thresholdPct }), + communityVoteThreshold: new VoteThreshold({ type: VoteThresholdType.YesVotePercentage, value: Number(cfg.DAO_APPROVAL_THRESHOLD_PERCENT || 51) }), minCommunityTokensToCreateProposal: new BN(1), minInstructionHoldUpTime: 0, - baseVotingTime: votingTimeSec, + baseVotingTime: Number(cfg.DAO_VOTING_TIME_SEC || 86400), communityVoteTipping: VoteTipping.Early, minCouncilTokensToCreateProposal: new BN(0), councilVoteThreshold: new VoteThreshold({ type: VoteThresholdType.Disabled }), @@ -249,82 +180,45 @@ async function main() { issuer.publicKey ); const treasuryPk = await withCreateNativeTreasury(ixGov, governanceProgramId, PROGRAM_VERSION_V3, governancePk, issuer.publicKey); - const sigGov = await sendAndConfirmTransaction(connection, new Transaction().add(...ixGov), [issuer], { commitment: "confirmed" }); + const sigGov = await sendAndConfirmTransaction(connection, new Transaction().add(...ixGov), [issuer], { + commitment: "confirmed", + }); - const ixSetMintAuthority = [ - createSetAuthorityInstruction( - mintKeypair.publicKey, - issuer.publicKey, - AuthorityType.MintTokens, - governancePk, - [], - TOKEN_PROGRAM_ID - ), - ]; - const sigSetMintAuthority = await sendAndConfirmTransaction(connection, new Transaction().add(...ixSetMintAuthority), [issuer], { commitment: "confirmed" }); - - const ixRealmAuthority = []; - withSetRealmAuthority( - ixRealmAuthority, - governanceProgramId, - PROGRAM_VERSION_V3, - realmPk, - issuer.publicKey, - governancePk, - SetRealmAuthorityAction.SetChecked - ); - const sigSetRealmAuthority = await sendAndConfirmTransaction(connection, new Transaction().add(...ixRealmAuthority), [issuer], { commitment: "confirmed" }); - - const endBalance = await connection.getBalance(issuer.publicKey, "confirmed"); - const spentLamports = startBalance - endBalance; const report = { createdAt: new Date().toISOString(), cluster, - configPath, realmName: cfg.DAO_REALM_NAME, governanceProgramId: governanceProgramId.toBase58(), + governingMint: mint.toBase58(), issuer: issuer.publicKey.toBase58(), - communityMint: mintKeypair.publicKey.toBase58(), issuerAta: issuerAta.toBase58(), realm: realmPk.toBase58(), - tokenOwnerRecord: tokenOwnerRecordPk.toBase58(), governance: governancePk.toBase58(), nativeTreasury: treasuryPk.toBase58(), - metadataUri: cfg.DAO_GOV_TOKEN_METADATA_URI, - imageUrl: cfg.DAO_GOV_TOKEN_IMAGE_URL, - txMint: sigMint, - txMetadata: sigMetadata, + tokenOwnerRecord: tokenOwnerRecordPk.toBase58(), + mintAuthorityBefore, + freezeAuthorityBefore, txRealm: sigRealm, - txDeposit: sigDeposit, + txTokenOwnerRecord: sigTokenOwnerRecord, txGovernanceTreasury: sigGov, - txSetMintAuthorityToGovernance: sigSetMintAuthority, - txSetRealmAuthority: sigSetRealmAuthority, - tokenSupply: supply, - votingTimeSec, - thresholdPercent: thresholdPct, - startBalanceSol: lamportsToSol(startBalance), - endBalanceSol: lamportsToSol(endBalance), - spentSol: lamportsToSol(spentLamports), + communityTokenType: "Liquid", }; + const runsDir = path.resolve(__dirname, "..", "local", "runs"); + ensureDir(runsDir); + const reportPath = path.join(runsDir, `${nowStamp()}_create_dao_from_existing_mint.json`); + fs.writeFileSync(reportPath, JSON.stringify(report, null, 2)); - const reportsDir = path.resolve(__dirname, "..", "local", "runs"); - ensureDir(reportsDir); - const base = `${nowStamp()}_${cfg.DAO_REALM_NAME.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 80)}_dao`; - const jsonPath = path.join(reportsDir, `${base}.json`); - const txtPath = path.join(reportsDir, `${base}.txt`); - fs.writeFileSync(jsonPath, JSON.stringify(report, null, 2)); - fs.writeFileSync(txtPath, Object.entries(report).map(([k, v]) => `${k}: ${v}`).join("\n") + "\n"); - - console.log("DAO создан."); - console.log("communityMint:", report.communityMint); - console.log("realm:", report.realm); - console.log("governance:", report.governance); - console.log("nativeTreasury:", report.nativeTreasury); - console.log("spentSol:", report.spentSol); - console.log("report:", jsonPath); + console.log("DAO created."); + console.log("communityMint:", mint.toBase58()); + console.log("realm:", realmPk.toBase58()); + console.log("governance:", governancePk.toBase58()); + console.log("nativeTreasury:", treasuryPk.toBase58()); + console.log("tokenOwnerRecord:", tokenOwnerRecordPk.toBase58()); + console.log("spentSol:", "see report via tx fees + rent"); + console.log("report:", reportPath); } main().catch((e) => { - console.error("Ошибка создания DAO:", e?.message || e); + console.error("CreateDAO error:", e?.message || e); process.exit(1); }); diff --git a/shine-solana/shine/create-SHiNE-DAO/scripts/03_transfer_dao_authorities.js b/shine-solana/shine/create-SHiNE-DAO/scripts/03_transfer_dao_authorities.js new file mode 100644 index 0000000..a98fe6f --- /dev/null +++ b/shine-solana/shine/create-SHiNE-DAO/scripts/03_transfer_dao_authorities.js @@ -0,0 +1,268 @@ +#!/usr/bin/env node +"use strict"; + +const fs = require("fs"); +const path = require("path"); +const readline = require("readline"); +const { + Connection, + PublicKey, + Transaction, + sendAndConfirmTransaction, +} = require("@solana/web3.js"); +const { + TOKEN_PROGRAM_ID, + AuthorityType, + createSetAuthorityInstruction, +} = require("@solana/spl-token"); +const { + PROGRAM_VERSION_V3, + getRealm, + withSetRealmAuthority, + SetRealmAuthorityAction, +} = require("@solana/spl-governance"); +const { createUmi } = require("@metaplex-foundation/umi-bundle-defaults"); +const { + createSignerFromKeypair, + signerIdentity, + none, + some, +} = require("@metaplex-foundation/umi"); +const { fromWeb3JsKeypair, fromWeb3JsPublicKey } = require("@metaplex-foundation/umi-web3js-adapters"); +const { + mplTokenMetadata, + fetchMetadataFromSeeds, + updateV1, +} = require("@metaplex-foundation/mpl-token-metadata"); +const { + clusterUrl, + ensureDir, + loadKeypair, + nowStamp, + parseEnvConfig, + resolveConfigPath, +} = require("./js_common"); + +function lamportsToSol(lamports) { + return Number(lamports) / 1_000_000_000; +} + +async function askYes() { + const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); + const answer = await new Promise((resolve) => + rl.question("Введите YES для передачи authority на DAO: ", resolve) + ); + rl.close(); + return answer.trim() === "YES"; +} + +async function fetchMetadataState(cluster, issuer, mint) { + const umi = createUmi(clusterUrl(cluster)); + const issuerSigner = createSignerFromKeypair(umi, fromWeb3JsKeypair(issuer)); + umi.use(signerIdentity(issuerSigner)); + umi.use(mplTokenMetadata()); + + const metadata = await fetchMetadataFromSeeds(umi, { mint: fromWeb3JsPublicKey(mint) }); + return { + umi, + issuerSigner, + metadata, + }; +} + +async function fetchMetadataUpdateAuthority(cluster, mint) { + const umi = createUmi(clusterUrl(cluster)); + umi.use(mplTokenMetadata()); + const metadata = await fetchMetadataFromSeeds(umi, { mint: fromWeb3JsPublicKey(mint) }); + return metadata.updateAuthority.toString(); +} + +async function main() { + const configPath = resolveConfigPath(process.argv[2], "dao.config.env"); + if (!fs.existsSync(configPath)) throw new Error(`Конфиг не найден: ${configPath}`); + + const cfg = parseEnvConfig(configPath); + [ + "DAO_CLUSTER", + "DAO_REALM_NAME", + "DAO_ISSUER_KEYPAIR", + "SPL_GOVERNANCE_PROGRAM_ID", + "DAO_GOV_TOKEN_MINT", + ].forEach((k) => { + if (!cfg[k]) throw new Error(`В конфиге отсутствует обязательный параметр: ${k}`); + }); + + const cluster = cfg.DAO_CLUSTER; + const connection = new Connection(clusterUrl(cluster), "confirmed"); + const issuer = loadKeypair(path.resolve(cfg.DAO_ISSUER_KEYPAIR)); + const governanceProgramId = new PublicKey(cfg.SPL_GOVERNANCE_PROGRAM_ID); + const mint = new PublicKey(cfg.DAO_GOV_TOKEN_MINT); + + const [realmPk] = PublicKey.findProgramAddressSync( + [Buffer.from("governance"), Buffer.from(cfg.DAO_REALM_NAME, "utf8")], + governanceProgramId + ); + const [governancePk] = PublicKey.findProgramAddressSync( + [Buffer.from("account-governance"), realmPk.toBuffer(), realmPk.toBuffer()], + governanceProgramId + ); + + const realmInfo = await connection.getAccountInfo(realmPk, "confirmed"); + if (!realmInfo) throw new Error(`Realm не найден: ${realmPk.toBase58()}. Сначала выполните шаг 2.`); + + const governanceInfo = await connection.getAccountInfo(governancePk, "confirmed"); + if (!governanceInfo) { + throw new Error(`Governance PDA не найден: ${governancePk.toBase58()}. Сначала выполните шаг 2.`); + } + const realmBefore = await getRealm(connection, realmPk); + const realmAuthorityBefore = realmBefore.account.authority?.toBase58?.() || null; + if (realmAuthorityBefore !== issuer.publicKey.toBase58()) { + throw new Error(`Realm authority уже не у issuer: ${realmAuthorityBefore}`); + } + + const mintInfoBefore = await connection.getParsedAccountInfo(mint, "confirmed"); + if (!mintInfoBefore.value) throw new Error(`Mint не найден: ${mint.toBase58()}`); + const mintParsedBefore = mintInfoBefore.value.data.parsed.info; + const mintAuthorityBefore = mintParsedBefore.mintAuthority || null; + const freezeAuthorityBefore = mintParsedBefore.freezeAuthority || null; + + if (mintAuthorityBefore && mintAuthorityBefore !== issuer.publicKey.toBase58()) { + throw new Error(`Mint authority уже не у issuer: ${mintAuthorityBefore}`); + } + if (freezeAuthorityBefore && freezeAuthorityBefore !== issuer.publicKey.toBase58()) { + throw new Error(`Freeze authority уже не у issuer: ${freezeAuthorityBefore}`); + } + + const { umi, metadata, issuerSigner } = await fetchMetadataState(cluster, issuer, mint); + const metadataUpdateAuthorityBefore = metadata.updateAuthority.toString(); + if (metadataUpdateAuthorityBefore !== issuer.publicKey.toBase58()) { + throw new Error(`Metadata update authority уже не у issuer: ${metadataUpdateAuthorityBefore}`); + } + + const startBalance = await connection.getBalance(issuer.publicKey, "confirmed"); + console.log("============================================================"); + console.log("TRANSFER DAO AUTHORITIES"); + console.log("------------------------------------------------------------"); + console.log("Сеть: ", cluster); + console.log("Realm: ", realmPk.toBase58()); + console.log("Governance PDA: ", governancePk.toBase58()); + console.log("Issuer: ", issuer.publicKey.toBase58()); + console.log("Governance mint: ", mint.toBase58()); + console.log("Баланс до: ", `${lamportsToSol(startBalance)} SOL`); + console.log("Mint authority до: ", mintAuthorityBefore || "null"); + console.log("Freeze authority до: ", freezeAuthorityBefore || "null"); + console.log("Realm authority до: ", realmAuthorityBefore || "null"); + console.log("Metadata authority до: ", metadataUpdateAuthorityBefore); + console.log("============================================================"); + + if (!(await askYes())) { + console.log("Отменено."); + return; + } + + const ixTransferAuthorities = [ + createSetAuthorityInstruction( + mint, + issuer.publicKey, + AuthorityType.MintTokens, + governancePk, + [], + TOKEN_PROGRAM_ID + ), + createSetAuthorityInstruction( + mint, + issuer.publicKey, + AuthorityType.FreezeAccount, + governancePk, + [], + TOKEN_PROGRAM_ID + ), + ]; + const sigTransferAuthorities = await sendAndConfirmTransaction( + connection, + new Transaction().add(...ixTransferAuthorities), + [issuer], + { commitment: "confirmed" } + ); + + const ixSetRealmAuthority = []; + withSetRealmAuthority( + ixSetRealmAuthority, + governanceProgramId, + PROGRAM_VERSION_V3, + realmPk, + issuer.publicKey, + governancePk, + SetRealmAuthorityAction.SetChecked + ); + const sigSetRealmAuthority = await sendAndConfirmTransaction( + connection, + new Transaction().add(...ixSetRealmAuthority), + [issuer], + { commitment: "confirmed" } + ); + + const metadataTx = await updateV1(umi, { + authority: issuerSigner, + mint: fromWeb3JsPublicKey(mint), + newUpdateAuthority: some(fromWeb3JsPublicKey(governancePk)), + data: none(), + primarySaleHappened: none(), + isMutable: none(), + collection: { __kind: "None" }, + collectionDetails: { __kind: "None" }, + uses: { __kind: "None" }, + ruleSet: { __kind: "None" }, + authorizationData: none(), + }).sendAndConfirm(umi); + const sigMetadataAuthority = Buffer.from(metadataTx.signature).toString("base64"); + + const mintInfoAfter = await connection.getParsedAccountInfo(mint, "confirmed"); + const mintParsedAfter = mintInfoAfter.value.data.parsed.info; + const mintAuthorityAfter = mintParsedAfter.mintAuthority || null; + const freezeAuthorityAfter = mintParsedAfter.freezeAuthority || null; + const realmAfter = await getRealm(connection, realmPk); + const realmAuthorityAfter = realmAfter.account.authority?.toBase58?.() || null; + + const metadataUpdateAuthorityAfter = await fetchMetadataUpdateAuthority(cluster, mint); + + const report = { + createdAt: new Date().toISOString(), + cluster, + realmName: cfg.DAO_REALM_NAME, + governanceProgramId: governanceProgramId.toBase58(), + issuer: issuer.publicKey.toBase58(), + governingMint: mint.toBase58(), + realm: realmPk.toBase58(), + governance: governancePk.toBase58(), + mintAuthorityBefore, + freezeAuthorityBefore, + metadataUpdateAuthorityBefore, + realmAuthorityBefore, + mintAuthorityAfter, + freezeAuthorityAfter, + realmAuthorityAfter, + metadataUpdateAuthorityAfter, + txTransferAuthorities: sigTransferAuthorities, + txSetRealmAuthority: sigSetRealmAuthority, + txSetMetadataUpdateAuthority: sigMetadataAuthority, + }; + const runsDir = path.resolve(__dirname, "..", "local", "runs"); + ensureDir(runsDir); + const reportPath = path.join(runsDir, `${nowStamp()}_transfer_dao_authorities.json`); + fs.writeFileSync(reportPath, JSON.stringify(report, null, 2)); + + console.log("Все authority переданы на DAO."); + console.log("realm:", realmPk.toBase58()); + console.log("governance:", governancePk.toBase58()); + console.log("mintAuthority после:", mintAuthorityAfter || "null"); + console.log("freezeAuthority после:", freezeAuthorityAfter || "null"); + console.log("realmAuthority после:", realmAuthorityAfter || "null"); + console.log("metadataAuthority после:", metadataUpdateAuthorityAfter); + console.log("report:", reportPath); +} + +main().catch((e) => { + console.error("TransferAuthorities error:", e?.message || e); + process.exit(1); +}); diff --git a/shine-solana/shine/create-SHiNE-DAO/scripts/03_prepare_second_voter.js b/shine-solana/shine/create-SHiNE-DAO/scripts/04_prepare_second_voter.js similarity index 100% rename from shine-solana/shine/create-SHiNE-DAO/scripts/03_prepare_second_voter.js rename to shine-solana/shine/create-SHiNE-DAO/scripts/04_prepare_second_voter.js diff --git a/shine-solana/shine/create-SHiNE-DAO/scripts/04_create_empty_nft_template_with_metadata.js b/shine-solana/shine/create-SHiNE-DAO/scripts/05_create_empty_nft_template_with_metadata.js similarity index 100% rename from shine-solana/shine/create-SHiNE-DAO/scripts/04_create_empty_nft_template_with_metadata.js rename to shine-solana/shine/create-SHiNE-DAO/scripts/05_create_empty_nft_template_with_metadata.js diff --git a/shine-solana/shine/create-SHiNE-DAO/scripts/05_propose_vote_mint_nft.js b/shine-solana/shine/create-SHiNE-DAO/scripts/06_propose_vote_mint_nft.js similarity index 98% rename from shine-solana/shine/create-SHiNE-DAO/scripts/05_propose_vote_mint_nft.js rename to shine-solana/shine/create-SHiNE-DAO/scripts/06_propose_vote_mint_nft.js index 3be7be1..58f0220 100644 --- a/shine-solana/shine/create-SHiNE-DAO/scripts/05_propose_vote_mint_nft.js +++ b/shine-solana/shine/create-SHiNE-DAO/scripts/06_propose_vote_mint_nft.js @@ -22,7 +22,7 @@ const { clusterUrl, ensureDir, loadKeypair, nowStamp, parseEnvConfig, resolveCon async function main() { const targetWallet = process.argv[3]; const nftMintStr = process.argv[4]; - if (!targetWallet || !nftMintStr) throw new Error("Usage: node 05_propose_vote_mint_nft.js "); + if (!targetWallet || !nftMintStr) throw new Error("Usage: node 06_propose_vote_mint_nft.js "); const cfg = parseEnvConfig(resolveConfigPath(process.argv[2], "nft-flow.config.env")); const conn = new Connection(clusterUrl(cfg.CLUSTER), "confirmed"); diff --git a/shine-solana/shine/create-SHiNE-DAO/scripts/06_execute_mint_nft.js b/shine-solana/shine/create-SHiNE-DAO/scripts/07_execute_mint_nft.js similarity index 96% rename from shine-solana/shine/create-SHiNE-DAO/scripts/06_execute_mint_nft.js rename to shine-solana/shine/create-SHiNE-DAO/scripts/07_execute_mint_nft.js index d656064..a556b9c 100644 --- a/shine-solana/shine/create-SHiNE-DAO/scripts/06_execute_mint_nft.js +++ b/shine-solana/shine/create-SHiNE-DAO/scripts/07_execute_mint_nft.js @@ -13,7 +13,7 @@ async function main() { const proposalTx = new PublicKey(process.argv[4]); const target = new PublicKey(process.argv[5]); const nftMint = new PublicKey(process.argv[6]); - if (!process.argv[6]) throw new Error("Usage: node 06_execute_mint_nft.js "); + if (!process.argv[6]) throw new Error("Usage: node 07_execute_mint_nft.js "); const conn = new Connection(clusterUrl(cfg.CLUSTER), "confirmed"); const main = loadKeypair(path.resolve(__dirname, "..", cfg.MAIN_KEYPAIR)); diff --git a/shine-solana/shine/create-SHiNE-DAO/scripts/07_propose_vote_burn_nft.js b/shine-solana/shine/create-SHiNE-DAO/scripts/08_propose_vote_burn_nft.js similarity index 98% rename from shine-solana/shine/create-SHiNE-DAO/scripts/07_propose_vote_burn_nft.js rename to shine-solana/shine/create-SHiNE-DAO/scripts/08_propose_vote_burn_nft.js index 6a9428f..8f736ce 100644 --- a/shine-solana/shine/create-SHiNE-DAO/scripts/07_propose_vote_burn_nft.js +++ b/shine-solana/shine/create-SHiNE-DAO/scripts/08_propose_vote_burn_nft.js @@ -21,7 +21,7 @@ const { clusterUrl, ensureDir, loadKeypair, nowStamp, parseEnvConfig, resolveCon async function main() { const currentOwnerWallet = process.argv[3]; const nftMintStr = process.argv[4]; - if (!currentOwnerWallet || !nftMintStr) throw new Error("Usage: node 07_propose_vote_burn_nft.js "); + if (!currentOwnerWallet || !nftMintStr) throw new Error("Usage: node 08_propose_vote_burn_nft.js "); const cfg = parseEnvConfig(resolveConfigPath(process.argv[2], "nft-flow.config.env")); const conn = new Connection(clusterUrl(cfg.CLUSTER), "confirmed"); diff --git a/shine-solana/shine/create-SHiNE-DAO/scripts/08_execute_burn_nft.js b/shine-solana/shine/create-SHiNE-DAO/scripts/09_execute_burn_nft.js similarity index 96% rename from shine-solana/shine/create-SHiNE-DAO/scripts/08_execute_burn_nft.js rename to shine-solana/shine/create-SHiNE-DAO/scripts/09_execute_burn_nft.js index d7deb09..dedc3a6 100644 --- a/shine-solana/shine/create-SHiNE-DAO/scripts/08_execute_burn_nft.js +++ b/shine-solana/shine/create-SHiNE-DAO/scripts/09_execute_burn_nft.js @@ -13,7 +13,7 @@ async function main() { const proposalTx = new PublicKey(process.argv[4]); const currentOwner = new PublicKey(process.argv[5]); const nftMint = new PublicKey(process.argv[6]); - if (!process.argv[6]) throw new Error("Usage: node 08_execute_burn_nft.js "); + if (!process.argv[6]) throw new Error("Usage: node 09_execute_burn_nft.js "); const conn = new Connection(clusterUrl(cfg.CLUSTER), "confirmed"); const main = loadKeypair(path.resolve(__dirname, "..", cfg.MAIN_KEYPAIR)); diff --git a/shine-solana/shine/doc/programs/shine_login_guard.md b/shine-solana/shine/doc/programs/shine_login_guard.md index 32135df..3e9e75e 100644 --- a/shine-solana/shine/doc/programs/shine_login_guard.md +++ b/shine-solana/shine/doc/programs/shine_login_guard.md @@ -22,7 +22,7 @@ Текущий program id: -- `3xkopA7cXagxzMFrKdv3NCBfV6BKiRJCk69kr27M2sRo` +- `SHiGxGsXGioQYCYhchQ5R7KWoxN5UjFAFsucPf6sfnh` ## 3. Состояние diff --git a/shine-solana/shine/doc/programs/shine_payments.md b/shine-solana/shine/doc/programs/shine_payments.md index 607aa0b..a3542b0 100644 --- a/shine-solana/shine/doc/programs/shine_payments.md +++ b/shine-solana/shine/doc/programs/shine_payments.md @@ -22,7 +22,11 @@ Текущий program id: -- `c4yTa4JT9EtQDCBX9LmWFK6T2gp4JGsuymFbom2EudW` +- `SHiPmXbM9Fs9khzRUW3TGKsS2W84aqaXTxs3ZkajW9v` + +Текущий зашитый административный кошелек: + +- `DAO_WALLET`: `aiShm43fZjm3YkMs22sYL1bpXaL3bVxv7SSraPHzVgq` ## 2. Основная модель diff --git a/shine-solana/shine/doc/programs/shine_users.md b/shine-solana/shine/doc/programs/shine_users.md index 4dd0ed7..f65b07a 100644 --- a/shine-solana/shine/doc/programs/shine_users.md +++ b/shine-solana/shine/doc/programs/shine_users.md @@ -35,9 +35,9 @@ ## 2. Program ID и внешние зависимости -Текущий program id devnet/localnet: +Текущий program id: -- `3bYrnXwLc56oVPUBAjY8zTMLwHCYq29b5rUMu3b64SQJ` +- `SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6` Внешние зависимости по логике: @@ -48,6 +48,12 @@ - системная программа Solana; - sysvar `instructions`. +Текущие зашитые внешние адреса: + +- `DAO_AUTHORITY`: `aiShm43fZjm3YkMs22sYL1bpXaL3bVxv7SSraPHzVgq` +- `shine_payments`: `SHiPmXbM9Fs9khzRUW3TGKsS2W84aqaXTxs3ZkajW9v` +- `shine_login_guard`: `SHiGxGsXGioQYCYhchQ5R7KWoxN5UjFAFsucPf6sfnh` + ## 3. PDA и seed-правила ### 3.1. Пользовательская PDA diff --git a/shine-solana/shine/programs/shine_login_guard/Cargo.toml b/shine-solana/shine/programs/shine_login_guard/Cargo.toml index 25b325e..2426b10 100644 --- a/shine-solana/shine/programs/shine_login_guard/Cargo.toml +++ b/shine-solana/shine/programs/shine_login_guard/Cargo.toml @@ -18,3 +18,8 @@ solana-program = "2.1.21" default = [] no-entrypoint = [] cpi = [] +idl-build = ["anchor-lang/idl-build"] + +[dependencies.anchor-lang] +version = "0.31.1" +optional = true diff --git a/shine-solana/shine/programs/shine_login_guard/src/lib.rs b/shine-solana/shine/programs/shine_login_guard/src/lib.rs index 91111a2..8014a2d 100644 --- a/shine-solana/shine/programs/shine_login_guard/src/lib.rs +++ b/shine-solana/shine/programs/shine_login_guard/src/lib.rs @@ -18,7 +18,7 @@ pub const CLASS_TRADEMARK: u32 = 2; pub const MAX_WORDS_PER_LOGIN: usize = 3; pub const MAX_LOGIN_LEN: usize = 20; -solana_program::declare_id!("3xkopA7cXagxzMFrKdv3NCBfV6BKiRJCk69kr27M2sRo"); +solana_program::declare_id!("SHiGxGsXGioQYCYhchQ5R7KWoxN5UjFAFsucPf6sfnh"); entrypoint!(process_instruction); diff --git a/shine-solana/shine/programs/shine_payments/src/lib.rs b/shine-solana/shine/programs/shine_payments/src/lib.rs index 481a854..b905bd7 100644 --- a/shine-solana/shine/programs/shine_payments/src/lib.rs +++ b/shine-solana/shine/programs/shine_payments/src/lib.rs @@ -17,7 +17,7 @@ use std::str::FromStr; pub mod settings; -solana_program::declare_id!("c4yTa4JT9EtQDCBX9LmWFK6T2gp4JGsuymFbom2EudW"); +solana_program::declare_id!("SHiPmXbM9Fs9khzRUW3TGKsS2W84aqaXTxs3ZkajW9v"); entrypoint!(process_instruction); const IX_INIT: u8 = 1; diff --git a/shine-solana/shine/programs/shine_payments/src/settings.rs b/shine-solana/shine/programs/shine_payments/src/settings.rs index 34f875a..e410ba7 100644 --- a/shine-solana/shine/programs/shine_payments/src/settings.rs +++ b/shine-solana/shine/programs/shine_payments/src/settings.rs @@ -55,4 +55,4 @@ pub const PYTH_SOL_USD_FEED_ID: &str = pub const PYTH_SOL_USD_ACCOUNT: &str = "7UVimffxr9ow1uXYxsr4LHAcV58mLzhmwaeKvJ1pjLiE"; /// `DAO_WALLET` — адрес кошелька DAO-казны для `shine_payments`. -pub const DAO_WALLET: &str = "FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P"; +pub const DAO_WALLET: &str = "aiShm43fZjm3YkMs22sYL1bpXaL3bVxv7SSraPHzVgq"; diff --git a/shine-solana/shine/programs/shine_payments/web/admin_tools.html b/shine-solana/shine/programs/shine_payments/web/admin_tools.html index 526aa4d..88bff3f 100644 --- a/shine-solana/shine/programs/shine_payments/web/admin_tools.html +++ b/shine-solana/shine/programs/shine_payments/web/admin_tools.html @@ -110,9 +110,9 @@