diff --git a/shine/AGENTS.md b/shine/AGENTS.md index 9c545ea..259bbfd 100644 --- a/shine/AGENTS.md +++ b/shine/AGENTS.md @@ -4,9 +4,9 @@ В проекте есть спецификация пользовательской PDA-записи: -- `doc/SHINE_USER_PDA_V1.md` +- актуальные документы в `doc/`. -Если меняется формат записи, сериализация, правила подписи, `prev_hash`, экономика лимитов или связанные ограничения create/update, этот документ нужно обновлять в том же изменении. +Если меняется формат записи, сериализация, правила подписи, `prev_hash`, экономика лимитов или связанные ограничения create/update, соответствующую документацию в `doc/` нужно обновлять в том же изменении. ## Language Rule @@ -35,6 +35,7 @@ Push выполнять через `http.extraHeader` (Authorization) без в ## Rule: Commit Messages Текст commit message писать на русском языке. +Это обязательное правило для всех новых коммитов в этом репозитории. ## Rule: UI Deploy diff --git a/shine/doc/Deploy.txt b/shine/doc/Deploy.txt deleted file mode 100644 index b1242bd..0000000 --- a/shine/doc/Deploy.txt +++ /dev/null @@ -1,31 +0,0 @@ -Деплой в devnet (по умолчанию у тебя уже devnet): - обе программы сразу: -anchor deploy - или по одной: -anchor deploy --program-name shine_users -anchor deploy --program-name shine_payments - - Проверка деплоя -anchor keys list -solana program show 5dFcWDNp42Xn9Vv4oDMJzM4obBJ8hvDuAtPX54fT5L3t // -solana program show GcGFR47xF7o7ztXzN4MFThmxzHn4z6VmpmELgNk8smCm // - - Апгрейд в будущем - После изменений кода: -anchor build -anchor upgrade --program-name shine_users -anchor upgrade --program-name shine_payments - - посмотреть адрес кошелька - solana address -k /home/ai/.config/solana/id.jsonanchor build --program-name shine_payments - - - - - - -anchor deploy --program-name shine_payments - -solana program show 92sgkgx7KHpbhQu81mNGHaKa7skJB7esArVdPM7paDSW --url http://127.0.0.1:8899 - -solana address -k /home/ai/work/SOLANA/shine/target/deploy/shine_payments-keypair.json diff --git a/shine/doc/READ_ME.txt b/shine/doc/READ_ME.txt deleted file mode 100644 index 89831f0..0000000 --- a/shine/doc/READ_ME.txt +++ /dev/null @@ -1,39 +0,0 @@ -# подключаться надо к -JSON RPC URL: http://127.0.0.1:8899 - -# Запустить саму ноду -solana-test-validator -# Удалить процесс ноды что бы запустить заново -kill -9 $(pgrep -f "solana-test-validator") - -или -ps aux | grep solana-test-validator -а потом -kill -9 1052345 -# Убивает и логи и всю базу локальной ноды -rm -rf test-ledger - - -# Удалить все данные с ноды -solana-test-validator --reset - - -# Что бы запустить просмотр логов ноды -solana logs - -# Запустить контракт -anchor deploy - -# Cкомпилировать и задеплоить новую версию -anchor build # Скомпилировать контракт и сгенерировать IDL -anchor deploy # Задеплоить контракт в сеть (указанную в Anchor.toml) -Если ты хочешь сразу убедиться, куда он деплоится — проверь Anchor.toml. -[provider] -cluster = "https://api.testnet.solana.com" # или "localnet" -wallet = "~/.config/solana/id.json" - - - - -# Создать новый проект -anchor init имя_проекта diff --git a/shine/doc/READ_ME_NEW_DEPLOY.txt b/shine/doc/READ_ME_NEW_DEPLOY.txt deleted file mode 100644 index 708ca0e..0000000 --- a/shine/doc/READ_ME_NEW_DEPLOY.txt +++ /dev/null @@ -1,65 +0,0 @@ -https://api.devnet.solana.com - - -проверить настройки -solana config get - -настроить -solana config set --url https://api.devnet.solana.com -или -solana config set --url http://127.0.0.1:8899 - -потом -solana airdrop 2 --keypair /home/ai/.config/solana/id.json -и -solana balance --keypair /home/ai/.config/solana/id.json - - - -anchor deploy \ - --provider.cluster https://api.devnet.solana.com \ - --provider.wallet /home/ai/.config/solana/id.json - - - - - - - - - - - - - - - -Шаг 1. Создай новый ключ для новой программы - -solana-keygen new --outfile target/deploy/user_registry-testnet-keypair.json - -Шаг 2. Укажи новый ID в declare_id!: - -declare_id!("НОВЫЙ_PUBKEY_ОТСЮДА"); // получен из предыдущей команды - -Чтобы узнать pubkey: - -solana address -k target/deploy/user_registry-testnet-keypair.json - -Шаг 3. Обнови Anchor.toml: - -[programs.testnet] -user_registry = "НОВЫЙ_PUBKEY" - -[provider] -cluster = "https://api.testnet.solana.com" -wallet = "~/.config/solana/id.json" - -Шаг 4. Компиляция и деплой: - -anchor build -anchor deploy --provider.cluster testnet - -Шаг 5. Проверка: - -solana program show НОВЫЙ_PUBKEY --url https://api.testnet.solana.com \ No newline at end of file diff --git a/shine/doc/ReadMe.md b/shine/doc/ReadMe.md deleted file mode 100644 index ae846df..0000000 --- a/shine/doc/ReadMe.md +++ /dev/null @@ -1 +0,0 @@ -Просто разные заметки для себя \ No newline at end of file diff --git a/shine/doc/SHINE_PAYMENTS_TEST_PLAN.md b/shine/doc/SHINE_PAYMENTS_TEST_PLAN.md deleted file mode 100644 index ddf2b87..0000000 --- a/shine/doc/SHINE_PAYMENTS_TEST_PLAN.md +++ /dev/null @@ -1,50 +0,0 @@ -# SHINE Payments v2: тестовый план (Devnet) - -## Цель - -Проверить: - -1. создание PDA и инициализацию; -2. покупку обычных билетов (очередь 1); -3. выдачу DAO-лимитов менеджеру; -4. создание менеджерских билетов в очередь 1 и 2; -5. корректный приоритет выплат (очередь 1 > очередь 2); -6. перевод средств в DAO и награды вызывающему шаг выплат. - -## Сценарий А: один кошелек - -1. Открыть `admin_tools`, выполнить `init`. -2. В `buy_ticket` купить несколько билетов в очередь 1. -3. В `dao_tools` (тем же кошельком, если он DAO) выдать лимиты менеджеру. -4. В `manager_tools` создать билеты в очередь 1 и 2. -5. Пополнить inflow-вольт вручную. -6. В `track_ticket` запускать шаг выплат и смотреть, что: - - сначала платится очередь 1; - - после исчерпания очереди 1 платится очередь 2; - - если в процессе снова появился билет в очереди 1, приоритет возвращается к ней. - -## Сценарий Б: разные кошельки - -1. Кошелек DAO: выдает лимиты менеджерам. -2. Кошелек менеджера: добавляет тикеты через `manager_add_ticket`. -3. Кошельки покупателей: покупают обычные билеты. -4. Любой кошелек: вызывает `step_payout`. - -## Проверка результата - -1. У получателей тикетов растут балансы. -2. В DAO поступает симметричная сумма `X` при каждом шаге выплаты. -3. Вызывающий шаг выплат получает фиксированную награду. -4. Агрегаты очередей (`total/paid/sum_total/sum_paid`) изменяются ожидаемо. - -## Возврат средств после теста - -1. Перевести остатки SOL с тестовых кошельков на исходный кошелек. -2. При необходимости закрыть неиспользуемые program-аккаунты и вернуть ренту. -3. Проверить, что ключевые кошельки и devnet-параметры не содержат лишних средств/прав после завершения теста. - -## Ограничения текущего этапа - -1. DAO пока заменен обычным кошельком. -2. Финальный governance (голосование DAO) не подключен. -3. Расчеты пока в SOL/lamports, переход на USDT по курсу запланирован. diff --git a/shine/doc/SHINE_PAYMENTS_V2.md b/shine/doc/SHINE_PAYMENTS_V2.md deleted file mode 100644 index 5c3b646..0000000 --- a/shine/doc/SHINE_PAYMENTS_V2.md +++ /dev/null @@ -1,121 +0,0 @@ -# SHINE Payments v2 - -## Назначение - -`shine_payments` v2 — контракт очереди выплат с двумя очередями: - -1. обычная покупка билета (очередь 1); -2. менеджерское добавление билетов (очередь 1 и очередь 2 по лимитам от DAO); -3. пошаговые выплаты из inflow-вольта с приоритетом очереди 1. - -Сейчас тестовый этап в Devnet: расчеты и хранение в USD-центах (1 USD = 100), при этом реальные on-chain переводы выполняются в SOL по курсу Pyth SOL/USD. - -## PDA - -1. `config_pda` (`shine_payments_v3_config`) - - `dao_wallet` - - `manager_wallet` (сервисный параметр, для будущих сценариев) - - `inflow_vault` - -2. `coef_limit_pda` (`shine_payments_v3_coef_limit`) - - `coef_ppm` (fixed-point, scale = 1_000_000) - - `limit_usd_cents` (лимит суммарной исторической суммы очереди 1 для обычной покупки) - - `call_reward_lamports` (награда за шаг выплат, максимум 0.01 SOL) - -3. `queues_pda` (`shine_payments_v3_queues`) - - очередь 1: `tickets_total`, `tickets_paid`, `sum_total`, `sum_paid` - - очередь 2: `tickets_total`, `tickets_paid`, `sum_total`, `sum_paid` - -4. `inflow_vault_pda` (`shine_payments_v3_inflow_vault`) - - входящий PDA-вольт программы для выплат. - -5. `ticket_pda` - - очередь 1: `shine_payments_v3_q1_ticket + index_le_u64` - - очередь 2: `shine_payments_v3_q2_ticket + index_le_u64` - - поля тикета: - - `queue_id` - - `index` - - `is_paid` - - `recipient_wallet` - - `payout_usd_cents` - - `debt_before_usd_cents` - -6. `manager_allowance_pda` (`shine_p_v3_manager_allow + manager_pubkey`) - - `manager_wallet` - - `q1_available_usd_cents` - - `q2_available_usd_cents` - -## Методы - -1. `init` - - вызывается один раз (кто угодно); - - создает `config_pda`, `coef_limit_pda`, `queues_pda`, `inflow_vault_pda`. - -2. `update_coef_limit` (только `dao_wallet` из config) - - меняет коэффициент, лимит покупки в очередь 1 и награду шага выплат; - - ограничение награды: не более `0.01 SOL`. - -3. `grant_manager_limits` (только `dao_wallet` из config) - - DAO выдает/добавляет лимиты менеджеру: - - `add_q1_usd_cents` - - `add_q2_usd_cents` - - если PDA менеджера нет — создается; - - если есть — лимиты увеличиваются. - -4. `buy_ticket` - - обратная совместимость: покупка с входом в lamports, но запись тикета в USD-центах; - - использует Pyth SOL/USD и stale-check (не старше 120 секунд). - -5. `buy_ticket_usd` - - покупка билета в очередь 1 с суммой в USD-центах; - - сумма SOL к оплате считается в контракте по Pyth; - - есть `max_pay_lamports` для slippage-защиты. - -6. `buy_ticket_sol` - - покупка билета в очередь 1 с суммой в lamports; - - USD-объём покупки считается в контракте по Pyth; - - есть `min_expected_usd_cents` для slippage-защиты. - -7. `manager_add_ticket` - - менеджер добавляет тикет в очередь 1 или 2; - - без денежного перевода; - - списывает лимит менеджера по выбранной очереди (в USD-центах). - -8. `step_payout` - - выбирает очередь по приоритету: - 1. сначала очередь 1; - 2. если в 1-й нет ожидания — очередь 2. - - перед выплатой получает текущий SOL/USD из Pyth и проверяет stale. - - шаг выплаты: - - очередь 1: `X` получателю тикета + `X` в DAO + `reward` вызывающему; - - очередь 2: `X` получателю тикета + `2X` в DAO + `reward` вызывающему. - - если обе очереди пусты/выплачены: - - переводит весь доступный остаток inflow-вольта в DAO (без reward). - -9. `change_ticket_recipient` - - текущий `recipient_wallet` тикета может сменить получателя на другой кошелек; - - тикет должен быть невыплаченным; - - смена запрещена, если этот тикет является следующим к выплате в текущем шаге - (с учетом приоритета очереди 1 над очередью 2). - -10. Экономика покупки - - сумма покупки идет в DAO; - - тикет получает выплату `purchase_usd_cents * coef_ppm / 1_000_000`; - - проверка лимита выполняется по `q1_sum_total_usd_cents` (исторически накопленная сумма, без вычета уже выплаченного). - -## Стартовые настройки - -См. `programs/shine_payments/src/settings.rs`: - -- `START_COEF_PPM = 5_000_000` (коэффициент 5.0) -- `START_LIMIT_USD_CENTS = 10_000 USD` -- `START_CALL_REWARD_LAMPORTS = 0.008 SOL` -- `ORACLE_MAX_AGE_SECS = 120` -- `PYTH_SOL_USD_FEED_ID` -- `PYTH_SOL_USD_ACCOUNT` -- `DAO_WALLET` -- `MANAGER_WALLET` - -## Тестовый режим - -Пока нет финального production-потока пополнения inflow из регистрации/экосистемы, inflow-вольт пополняется вручную в Devnet, после чего выполняются шаги выплат. diff --git a/shine/doc/SHINE_USER_PDA_V1.md b/shine/doc/SHINE_USER_PDA_V1.md deleted file mode 100644 index 1d4e0cd..0000000 --- a/shine/doc/SHINE_USER_PDA_V1.md +++ /dev/null @@ -1,146 +0,0 @@ -# SHiNE User PDA v1.0 - -## 1. Назначение - -`SHiNE User PDA v1.0` — бинарный формат пользовательской записи в PDA Solana. - -Хранит: -- логин; -- ключи пользователя; -- номер внутренней сети; -- лимит (баланс); -- серверные данные (опционально); -- список серверов подключения; -- данные восстановления; -- связь с предыдущей версией через `prev_hash`; -- подпись владельца `root_key`. - -Размер PDA фиксированный: - -`768 bytes` - -Полезная длина записи указывается в `record_len`. - -## 2. Общие правила кодирования - -- Числа: `little-endian`. -- Строки: `UTF-8` с префиксом длины `u8`. -- Padding до 768 байт: нули `0x00`. -- Padding не входит в `record_len`. - -## 3. Структура записи - -- `magic` (5): `"SHiNE"` -- `format_major` (1): `1` -- `format_minor` (1): `0` -- `record_len` (2): длина от `magic` до `signature` включительно -- `created_at_ms` (8) -- `updated_at_ms` (8) -- `version` (4) -- `prev_hash` (32) -- `login_len` (1) -- `login` (N) -- `root_key` (32) -- `blockchain_key` (32) -- `device_key` (32) -- `chain_number` (2) -- `balance` (8) -- `is_server` (1) -- если `is_server=1`: - - `server_key` (32) - - `server_address_len` (1) - - `server_address` (N) -- `connection_servers_count` (1) -- повтор `count` раз: - - `server_login_len` (1) - - `server_login` (N) -- `trusted_count` (1) -- `reserved` (5) = `0x00 0x00 0x00 0x00 0x00` -- `signature` (64) -- `padding` до 768 - -## 4. Подпись (v1.0) - -В `v1.0` подписывается не сырой блок полей напрямую, а его SHA-256. - -1. Формируется `unsigned_bytes`: - - все поля от `magic` до `reserved` включительно; - - поле `signature` не включается; - - padding не включается. -2. Считается `msg_hash = SHA-256(unsigned_bytes)`. -3. `signature = Ed25519.sign(root_private_key, msg_hash)`. -4. Проверка: - - `Ed25519.verify(root_key, msg_hash, signature)`. - - В текущей реализации проверка выполняется через встроенную Solana-инструкцию `Ed25519Program` - (инструкция должна идти в транзакции перед вызовом `create_user_pda` / `update_user_pda`). - -## 5. Что входит в `prev_hash` - -Для связи версий: - -- `prev_hash = SHA-256(previous_unsigned_bytes)` - -Где `previous_unsigned_bytes` — предыдущая версия записи от `magic` до `reserved` включительно, без `signature` и без padding. - -Для первой версии: - -- `prev_hash = 32` нулевых байта. - -## 6. Правила create/update в текущей реализации - -### Create - -- PDA: seed `["login=", login]`. -- Создаётся запись версии `0`. -- `updated_at_ms = created_at_ms`. -- Стартовый лимит: - - `START_BONUS_LIMIT + additional_limit`. -- Оплата: - - регистрационная комиссия; - - пополнение `additional_limit` по курсу; - - рента PDA (плательщик транзакции). - -### Update - -- Проверка подписи новой записи по `root_key`. -- Проверка: - - `magic`, `format_major`, `format_minor`; - - корректного `record_len`; - - `prev_hash` на соответствие предыдущей версии; - - `version = old_version + 1`; - - неизменяемых полей: `login`, `created_at_ms`, `root_key`. -- `balance` не уменьшается: - - `new_balance = old_balance + additional_limit`. -- При `additional_limit > 0` берётся комиссия пополнения. - -## 7. Параметры экономики/размера (settings) - -См. `programs/shine_users/src/settings.rs`: - -- `USER_PDA_SPACE = 768` -- `REGISTRATION_FEE_RECEIVER` -- `REGISTRATION_FEE_LAMPORTS` -- `LIMIT_STEP` -- `LAMPORTS_PER_LIMIT_STEP` -- `START_BONUS_LIMIT` - -## 8. Root Key Rotation (пока не включено) - -В `v1.0` `root_key` неизменяем. - -Варианты расширения: - -1. **Dual-signature rotate tx**: - - отдельный флаг операции rotate; - - запись подписывается и старым, и новым root key; - - контракт проверяет обе подписи. - -2. **Two-step commit/confirm**: - - шаг 1: proposal смены root (`old root` подпись); - - шаг 2: confirm (`new root` подпись) в отдельной tx. - -3. **Recovery guardians**: - - отдельная PDA для доверенных лиц; - - пороговая схема (например `m-of-n`) для восстановления root. - -Для v1.0 решение отложено, но изменение root_key в update запрещено. diff --git a/SHINY_USER_FORMAT_V1_0_DRAFT.md b/shine/doc/SHINY_USER_FORMAT_V1_0_DRAFT.md similarity index 100% rename from SHINY_USER_FORMAT_V1_0_DRAFT.md rename to shine/doc/SHINY_USER_FORMAT_V1_0_DRAFT.md diff --git a/shine/doc/TODO b/shine/doc/TODO deleted file mode 100644 index 93bbc21..0000000 --- a/shine/doc/TODO +++ /dev/null @@ -1,7 +0,0 @@ -. Сделать новые форматы для пользователей что бы там было больше информации - -. Протестировать работу и может доработать что бы можно было паралельно регистрировать 5 и более юзеров - за счёт передачи при вызове адресов PDA +1 +2 +3 +4 и т.д. - - - -. - пока не надо - Сделать что бы в файле общей информации добавилась запись для будущей миграции пользователей (хотя можно потом и добавить будет :))) \ No newline at end of file diff --git a/КЛЮЧИ_И_ДЕПЛОЙ_ТЕСТОВОЕ.md b/shine/doc/devnet_keys_and_deploy.md similarity index 100% rename from КЛЮЧИ_И_ДЕПЛОЙ_ТЕСТОВОЕ.md rename to shine/doc/devnet_keys_and_deploy.md diff --git a/shine/doc/how to getback money after deploy from buffer.txt b/shine/doc/how to getback money after deploy from buffer.txt deleted file mode 100644 index 3501e79..0000000 --- a/shine/doc/how to getback money after deploy from buffer.txt +++ /dev/null @@ -1,19 +0,0 @@ -Как вернуть деньги - - Узнаём адрес buffer account: - -solana program show <адрес_твоей_программы> - -Там будет строчка Buffer: . - -Закрываем буфер: - - solana program close --recipient <адрес_кошелька> - - 💡 --recipient — это куда вернуть SOL (обычно твой же кошелёк из ~/.config/solana/id.json). - -Сколько вернётся - - Если бинарник весит ~400 KB, с одного буфера вернётся ~0.35 SOL. - - У тебя две программы (shine_users, shine_payments), значит можно вернуть ~0.7 SOL. \ No newline at end of file diff --git a/shine/doc/how todo minimal size.txt b/shine/doc/how todo minimal size.txt deleted file mode 100644 index fe6b7f0..0000000 --- a/shine/doc/how todo minimal size.txt +++ /dev/null @@ -1,6 +0,0 @@ -✅ Чтобы максимально уменьшить .so надо будет включить флаг оптимизации в Cargo.toml: - -[profile.release] -opt-level = "z" # Максимальная компрессия -lto = true # Link Time Optimization -codegen-units = 1 # Уменьшает размер бинаря diff --git a/shine/doc/sh/restart.sh b/shine/doc/sh/restart.sh deleted file mode 100755 index e13ccd0..0000000 --- a/shine/doc/sh/restart.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash - -set -e # Завершаем при ошибке -set -o pipefail - - - - - -PROGRAM_KEYPAIR="target/deploy/shine-keypair.json" # замени на свой путь -WALLET=$(solana address) - -echo "🧹 Удаление старого ledger..." -rm -rf test-ledger - -echo "🚀 Запуск solana-test-validator в фоне..." -solana-test-validator --ledger test-ledger --reset > validator.log 2>&1 & -VALIDATOR_PID=$! - -# Убедимся, что validator запущен -echo "⏳ Ожидание запуска валидатора..." -until solana cluster-version &>/dev/null; do - sleep 1 -done -sleep 2 # На всякий случай немного подождём - -echo "💸 Airdrop 10 SOL на $WALLET..." -solana airdrop 10 HMww7YSVfwVm4i8sugqj7wyH26dqzHykzv3wzWwzEvPA - -solana airdrop 5 $WALLET - -echo "🔨 Сборка контракта..." -anchor build - -echo "📦 Деплой контракта..." -anchor deploy - -echo "✅ Готово!" - -# Не убиваем валидатор, чтобы он оставался запущенным -echo "ℹ️ Валидатор всё ещё работает (PID $VALIDATOR_PID)" - -echo "ℹ️ Запускаем логи" -solana logs diff --git a/shine/doc/sh/stop.sh b/shine/doc/sh/stop.sh deleted file mode 100755 index c54d0a4..0000000 --- a/shine/doc/sh/stop.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -set -e # Завершаем при ошибке -set -o pipefail - - -kill -9 $(pgrep -f "solana-test-validator") - -# 🔍 Ищем запущенный solana-test-validator -EXISTING_PID=$(pgrep -f "solana-test-validator") - -if [ -n "$EXISTING_PID" ]; then - echo "🛑 Найден работающий solana-test-validator (PID $EXISTING_PID), останавливаем..." - bash kill -9 $(pgrep -f "solana-test-validator") - echo "✅ Пытаюсь остановить старый валидатор..." - - # ждём завершения - while kill -0 "$EXISTING_PID" 2>/dev/null; do - sleep 0.5 - done - echo "✅ Старый валидатор остановлен." -fi - diff --git a/КОШЕЛЬКИ_DEVNET_ТЕСТ.md b/shine/doc/КОШЕЛЬКИ_DEVNET_ТЕСТ.md similarity index 100% rename from КОШЕЛЬКИ_DEVNET_ТЕСТ.md rename to shine/doc/КОШЕЛЬКИ_DEVNET_ТЕСТ.md diff --git a/shine/doc/что содержит utils.txt b/shine/doc/что содержит utils.txt deleted file mode 100644 index 2ee02f1..0000000 --- a/shine/doc/что содержит utils.txt +++ /dev/null @@ -1,145 +0,0 @@ -Функции для работы с PDA: - - -🧩 create_pda(...) - - Создаёт новый PDA, если он ещё не существует. - - Проверяет, чтобы не было коллизии. - - Без записи данных. - -🧩 write_to_pda(...) - - Просто записывает байты в существующий PDA. - - Без создания. - -🧩 create_and_write_pda(...) - - Комбинированная функция. - - Сначала проверяет, есть ли PDA — если нет, создаёт. - - Затем записывает данные. - - Очень удобна для инициализации одного PDA в один вызов. - -🧩 safe_read_pda(...) - - Возвращает Vec с содержимым PDA. - - Никогда не паникует: если PDA не существует или пустой — просто отдаёт Vec::new(). - - Защита от двойного borrow'а (через try_borrow_data()). - - Отличный инструмент для безопасного считывания данных. - -💡 Да, с этим ты можешь: -Возможность Функция -📦 Создать PDA create_pda -💾 Записать в PDA write_to_pda -⚡ Создать и записать create_and_write_pda -📖 Безопасно прочитать safe_read_pda - - -🔧 create_pda(...) - -🔹 Назначение: -Создаёт новый PDA-аккаунт, если он ещё не существует. - -📥 Аргументы: - - pda_account: &AccountInfo — аккаунт, который хотим создать - - signer: &AccountInfo — аккаунт плательщика (обычно пользователь) - - system_program: &AccountInfo — системная программа - - program_id: &Pubkey — адрес текущей программы - - seeds: &[&[u8]] — массив сидов, по которым создавался PDA - - space: u64 — сколько байт выделить под данные - -📤 Возвращает: - - Result<()> — Ok если успешно, Err если PDA уже существует или при ошибке создания - -🧠 Особенности: - - Проверяет, что PDA ещё не создан (через pda_account.owner == Pubkey::default()) - - Выбрасывает ErrCode::PdaAlreadyExists, если уже существует - -🔧 write_to_pda(...) - -🔹 Назначение: -Записывает бинарные данные в существующий PDA. - -📥 Аргументы: - - pda_account: &AccountInfo — аккаунт, в который пишем - - data: &[u8] — массив байт, которые нужно записать - -📤 Возвращает: - - Result<()> — Ok при успехе, Err если не удалось получить доступ к данным - -🧠 Особенности: - - ⚠️ Только пишет, не создаёт PDA - - Записывает в начало data-секции аккаунта - -🔧 create_and_write_pda(...) - -🔹 Назначение: -Если PDA ещё не существует — создаёт, затем сразу записывает данные. - -📥 Аргументы: - - pda_account: &AccountInfo — аккаунт для создания/записи - - signer: &AccountInfo — кто оплачивает создание - - system_program: &AccountInfo — системная программа - - program_id: &Pubkey — адрес текущей программы - - seeds: &[&[u8]] — сиды PDA - - data: Vec — данные для записи - - space: u64 — сколько байт выделить (при создании) - -📤 Возвращает: - - Result<()> — Ok при успехе, Err при ошибке создания или записи - -🧠 Особенности: - - Безопасно создаёт и пишет за один вызов - - Не выбрасывает ошибку, если PDA уже существует — просто пишет - -🔧 safe_read_pda(...) - -🔹 Назначение: -Безопасно считывает байты из PDA. Никогда не паникует. - -📥 Аргументы: - - pda_account: &AccountInfo — аккаунт для чтения - -📤 Возвращает: - - Vec — массив байт с содержимым PDA - → Если аккаунт не инициализирован или пустой, возвращает Vec::new() - -🧠 Особенности: - - Не выбрасывает ошибки — только логирует - - Полностью безопасно: подходит для чтения read-only PDA в любой ситуации \ No newline at end of file diff --git a/shine/mu.sh b/shine/mu.sh deleted file mode 100755 index 90132e9..0000000 --- a/shine/mu.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -# Кол-во адресов для генерации -NUM_KEYS=5 - -# RPC endpoint (можешь поменять) -#RPC_URL="https://api.testnet.solana.com" -RPC_URL="http://127.0.0.1:8899" - -echo "👉 Используем RPC: $RPC_URL" - -for i in $(seq 1 $NUM_KEYS); do - KEYPAIR="temp-key-$i.json" - echo "🔐 Генерирую ключ №$i: $KEYPAIR" - solana-keygen new --outfile "$KEYPAIR" --no-bip39-passphrase --silent - - PUBKEY=$(solana-keygen pubkey "$KEYPAIR") - echo "🪙 Публичный ключ: $PUBKEY" - - echo "💸 Запрашиваю airdrop на $PUBKEY..." - solana airdrop 1 "$PUBKEY" --url "$RPC_URL" - - echo "🔍 Проверяю баланс:" - solana balance "$PUBKEY" --url "$RPC_URL" - echo "-----------------------------" -done - -echo "✅ Готово. Удаляю временные ключи..." -rm temp-key-*.json diff --git a/shine/programs/DEPLOY_CONFIG_CHECKLIST.md b/shine/programs/DEPLOY_CONFIG_CHECKLIST.md deleted file mode 100644 index 867c104..0000000 --- a/shine/programs/DEPLOY_CONFIG_CHECKLIST.md +++ /dev/null @@ -1,81 +0,0 @@ -# DEPLOY CONFIG CHECKLIST (Shine Programs) - -Документ для подготовки к реальному деплою (mainnet/prod): какие адреса и где заменить. - -## 1) Program IDs - -1. `shine/programs/shine_payments/src/lib.rs` - - `declare_id!("...")` для `shine_payments`. -2. `shine/programs/shine_users/src/lib.rs` - - `declare_id!("...")` для `shine_users`. -3. `shine/Anchor.toml` - - обновить `programs.devnet` / `programs.localnet` (и при необходимости добавить/обновить секцию под mainnet workflow). - -## 2) Shine Payments on-chain settings - -Файл: `shine/programs/shine_payments/src/settings.rs` - -Обязательные адреса: -1. `DAO_WALLET` -2. `MANAGER_WALLET` -3. `PYTH_SOL_USD_ACCOUNT` -4. `PYTH_SOL_USD_FEED_ID` (идентификатор feed для SOL/USD) - -Параметры экономики (по необходимости): -1. `START_COEF_PPM` -2. `START_LIMIT_USD_CENTS` -3. `START_CALL_REWARD_LAMPORTS` -4. `MAX_CALL_REWARD_LAMPORTS` -5. `ORACLE_MAX_AGE_SECS` - -## 3) Shine Users on-chain settings - -Файл: `shine/programs/shine_users/src/settings.rs` - -Обязательные параметры: -1. `REGISTRATION_FEE_RECEIVER` (куда идет комиссия регистрации) -2. `REGISTRATION_FEE_LAMPORTS` -3. при необходимости скорректировать лимитные/бонусные константы: - - `LIMIT_STEP` - - `LAMPORTS_PER_LIMIT_STEP` - - `START_BONUS_LIMIT` - -## 4) Web UI constants (hardcoded values) - -Проверить и заменить Program ID / Oracle account в HTML: -1. `shine/programs/shine_payments/web/buy_ticket.html` -2. `shine/programs/shine_payments/web/track_ticket.html` -3. `shine/programs/shine_payments/web/admin_tools.html` -4. `shine/programs/shine_payments/web/dao_tools.html` -5. `shine/programs/shine_payments/web/manager_tools.html` - -Проверить RPC endpoint для нужной сети в соответствующих страницах. - -## 5) Скрипты и окружение - -Проверить конфиги и env-файлы, где участвуют адреса: -1. `shine/scripts/**/config.env` -2. `shine/scripts/**/dao.config.env` -3. `shine/scripts/**/governance_token.config.env` - -## 6) Проверка перед деплоем - -1. `cargo check -p shine_payments` -2. `cargo check -p shine_users` -3. сверить, что `declare_id` совпадает с ключами деплоя программ. -4. убедиться, что `PYTH_SOL_USD_ACCOUNT` читается в выбранной сети. -5. прогнать smoke-тесты UI (buy / track / admin / dao / manager). - -## 7) Проверка после деплоя - -1. Выполнить `init` для `shine_payments`. -2. Проверить существование PDA: - - `config_pda` - - `coef_limit_pda` - - `queues_pda` - - `inflow_vault_pda` -3. Проверить покупку тикета и шаг выплаты на малой сумме. -4. Проверить `change_ticket_recipient`: - - разрешено для не-next тикета; - - запрещено для next тикета. - diff --git a/shine/programs/shine_payments/oracle_check/README.md b/shine/programs/shine_payments/oracle_check/README.md deleted file mode 100644 index ea92a44..0000000 --- a/shine/programs/shine_payments/oracle_check/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Oracle Check - -Мини-страница диагностики оракула Pyth для `SOL/USD`. - -Файл: - -- `index.html` - -Что проверяет: - -1. Чтение oracle account через RPC (`devnet`, `mainnet-beta`, `testnet`). -2. Парсинг по текущим оффсетам из UI (`74/90/94`). -3. Альтернативный парсинг (`73/89/93`) для проверки сдвига формата. -4. Сравнение с Hermes API (эталонный источник цены по feed id). - -Запуск: - -Открыть `index.html` в браузере и нажать кнопку «Проверить все сети». - diff --git a/shine/programs/shine_payments/oracle_check/index.html b/shine/programs/shine_payments/oracle_check/index.html deleted file mode 100644 index 06c18e8..0000000 --- a/shine/programs/shine_payments/oracle_check/index.html +++ /dev/null @@ -1,255 +0,0 @@ - - - - - - Проверка оракула Pyth (Devnet/Mainnet/Testnet) - - - -
-

Диагностика оракула Pyth (SOL/USD)

-
-
- Страница нужна, чтобы проверить, что именно возвращает аккаунт оракула в разных сетях и где ломается парсинг. -
-
- Проверяются три сети: devnet, mainnet-beta, testnet. -
-
- -
-

Настройки

-
- -
-
- -
-
- -
-
- -
-
- -
-
- Если для сети аккаунт не существует, это тоже покажется в отчёте. -
-
- -
-

Результаты

-
Нажмите «Проверить все сети».
-
-
- - - - - diff --git a/solana-shine-client-lib/.gitignore b/solana-shine-client-lib/.gitignore deleted file mode 100644 index b63da45..0000000 --- a/solana-shine-client-lib/.gitignore +++ /dev/null @@ -1,42 +0,0 @@ -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ - -### IntelliJ IDEA ### -.idea/modules.xml -.idea/jarRepositories.xml -.idea/compiler.xml -.idea/libraries/ -*.iws -*.iml -*.ipr -out/ -!**/src/main/**/out/ -!**/src/test/**/out/ - -### Eclipse ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache -bin/ -!**/src/main/**/bin/ -!**/src/test/**/bin/ - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ - -### VS Code ### -.vscode/ - -### Mac OS ### -.DS_Store \ No newline at end of file diff --git a/solana-shine-client-lib/.idea/.gitignore b/solana-shine-client-lib/.idea/.gitignore deleted file mode 100644 index 7bc07ec..0000000 --- a/solana-shine-client-lib/.idea/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Environment-dependent path to Maven home directory -/mavenHomeManager.xml -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/solana-shine-client-lib/.idea/gradle.xml b/solana-shine-client-lib/.idea/gradle.xml deleted file mode 100644 index d19c938..0000000 --- a/solana-shine-client-lib/.idea/gradle.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/solana-shine-client-lib/.idea/misc.xml b/solana-shine-client-lib/.idea/misc.xml deleted file mode 100644 index fe0b0da..0000000 --- a/solana-shine-client-lib/.idea/misc.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/solana-shine-client-lib/.idea/vcs.xml b/solana-shine-client-lib/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/solana-shine-client-lib/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/solana-shine-client-lib/build.gradle b/solana-shine-client-lib/build.gradle deleted file mode 100644 index 203cd31..0000000 --- a/solana-shine-client-lib/build.gradle +++ /dev/null @@ -1,46 +0,0 @@ -plugins { - id 'java' -} - -apply plugin: 'java' - -group = 'com.shine' -version = '1.0.0' - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - -tasks.withType(Jar) { - manifest { - attributes( - 'Implementation-Title': 'solana-shine-lib', // или solana-shine-client-lib - 'Implementation-Version': version - ) - } -} - -repositories { - mavenCentral() - flatDir { - dirs 'libs' - } -} - -dependencies { - testImplementation platform('org.junit:junit-bom:5.10.0') - testImplementation 'org.junit.jupiter:junit-jupiter' - - implementation project(':solana-shine-lib') - implementation 'com.google.code.gson:gson:2.10.1' - // implementation "com.mmorrell:solanaj:1.15.1" - -} - -test { - useJUnitPlatform() -} - - - diff --git a/solana-shine-client-lib/gradle/wrapper/gradle-wrapper.jar b/solana-shine-client-lib/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 249e583..0000000 Binary files a/solana-shine-client-lib/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/solana-shine-client-lib/gradle/wrapper/gradle-wrapper.properties b/solana-shine-client-lib/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 2aa7c28..0000000 --- a/solana-shine-client-lib/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Fri Jun 13 15:16:43 MSK 2025 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/solana-shine-client-lib/gradlew b/solana-shine-client-lib/gradlew deleted file mode 100755 index 1b6c787..0000000 --- a/solana-shine-client-lib/gradlew +++ /dev/null @@ -1,234 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" -APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/solana-shine-client-lib/gradlew.bat b/solana-shine-client-lib/gradlew.bat deleted file mode 100644 index 107acd3..0000000 --- a/solana-shine-client-lib/gradlew.bat +++ /dev/null @@ -1,89 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/solana-shine-client-lib/libs/tmp/solanaj-1.20.4.jar b/solana-shine-client-lib/libs/tmp/solanaj-1.20.4.jar deleted file mode 100644 index a385a68..0000000 Binary files a/solana-shine-client-lib/libs/tmp/solanaj-1.20.4.jar and /dev/null differ diff --git a/solana-shine-client-lib/settings.gradle b/solana-shine-client-lib/settings.gradle deleted file mode 100644 index 0168e29..0000000 --- a/solana-shine-client-lib/settings.gradle +++ /dev/null @@ -1,2 +0,0 @@ -rootProject.name = 'solana-shine-client-lib' -include 'solana-shine-lib' diff --git a/solana-shine-client-lib/solana-shine-lib/build.gradle b/solana-shine-client-lib/solana-shine-lib/build.gradle deleted file mode 100644 index 2716a73..0000000 --- a/solana-shine-client-lib/solana-shine-lib/build.gradle +++ /dev/null @@ -1,56 +0,0 @@ -plugins { - id 'java' -} - -group = 'me.shineup' -version = '1.0' - - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} -repositories { - mavenCentral() - flatDir { - dirs 'libs' - } -} - -dependencies { - // были стандартные - testImplementation platform('org.junit:junit-bom:5.10.0') - testImplementation 'org.junit.jupiter:junit-jupiter' - - // шифрование нужна - implementation 'org.bouncycastle:bcprov-jdk15on:1.70' - -// implementation 'com.squareup.okhttp3:okhttp:4.12.0' // запросы по сети - //солана - implementation "com.mmorrell:solanaj:1.15.1" - -// implementation 'org.bitcoinj:bitcoinj-core:0.15.10' -// implementation 'com.squareup.moshi:moshi:1.13.0' - - -// implementation 'com.mmorrell:solanaj:1.20.4' - старые соланы не нужны -// implementation name: 'solanaj-1.20.4' - старые соланы не нужны - - - - // Logging - implementation 'org.slf4j:slf4j-api:1.7.36' - implementation 'ch.qos.logback:logback-classic:1.2.11' - - - implementation 'com.google.code.gson:gson:2.10.1' - -} - -test { - useJUnitPlatform() -} - - - - diff --git a/solana-shine-client-lib/solana-shine-lib/libs/solanaj-1.20.4.jar b/solana-shine-client-lib/solana-shine-lib/libs/solanaj-1.20.4.jar deleted file mode 100644 index a385a68..0000000 Binary files a/solana-shine-client-lib/solana-shine-lib/libs/solanaj-1.20.4.jar and /dev/null differ diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/TODO.txt b/solana-shine-client-lib/solana-shine-lib/src/main/java/TODO.txt deleted file mode 100644 index a07225c..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/TODO.txt +++ /dev/null @@ -1,9 +0,0 @@ -Что ещё надо сделать по библиотеке - -1. Сделать более рандомное создание пар ключей. (тк я три из трёх угадал в девнет!!!) - -2. Доделат обработку ошибок при вызове функции ( наверно уже в UI надо отлавливать ексепшены которые возвращает нода при препроверки вызова функции) - - 2.5 метод проверки что транзакция прям точно добавлена в систему. (хотя нужен ли он??. Тк пользователь добавился значет уже всё хорошо :) ) - -3. Исправить перевод денег (тк он не работает после перехода на старую библиотеку) \ No newline at end of file diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/SolanaSettings.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/SolanaSettings.java deleted file mode 100644 index 97dbcaa..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/SolanaSettings.java +++ /dev/null @@ -1,42 +0,0 @@ -package me.shineup.solana; - -import me.shineup.solana.config.Const; - -/** - * Настройки подключения к Solana. - * Позволяет выбирать RPC-сервер (локальный, тестовая сеть или произвольный). - */ -public class SolanaSettings { - - /** - * Устанавливает локальный RPC-адрес (например, http://127.0.0.1:8899). - */ - public static void setRpcUrlLocal() { - Const.RPC_URL = Const.LOCAL_RPC_URL; - } - - /** - * Устанавливает RPC-адрес тестовой сети Solana (https://api.testnet.solana.com). - */ - public static void setRpcUrlTestNet() { - Const.RPC_URL = "https://api.testnet.solana.com"; // или Const.TESTNET_RPC_URL - } - - /** - * Устанавливает пользовательский RPC-адрес. - * - * @param url Строка с адресом RPC-сервера - */ - public static void setRpcUrl(String url) { - Const.RPC_URL = url; - } - - /** - * Получает текущий установленный RPC-адрес. - * - * @return строка с текущим RPC-URL - */ - public static String getRpcUrl() { - return Const.RPC_URL; - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/SolanaTxWatcher.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/SolanaTxWatcher.java deleted file mode 100644 index 6d1f8c3..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/SolanaTxWatcher.java +++ /dev/null @@ -1,218 +0,0 @@ -package me.shineup.solana; - - -import me.shineup.solana.internal.utils.resultChecker.TransactionStatusHelper; -import me.shineup.solana.model.TxStatus; - -import java.time.Instant; -import java.util.HashMap; -import java.util.Map; - -/** - *

SolanaTxWatcher

- *

- * Класс-наблюдатель за одной транзакцией сети Solana. - * Он: - *

    - *
  • Хранит подпись (signature) и фиксирует момент создания объекта.
  • - *
  • Через {@link #updateStatus()} опрашивает RPC (используя {@link TransactionStatusHelper}) - * и обновляет внутренний статус.
  • - *
  • Через {@link #shouldRetry()} сообщает, надо ли продолжать опрос (с учётом таймаута, - * лимита неудачных попыток и финального статуса).
  • - *
  • Через {@link #isSuccess()} указывает, прошла ли транзакция успешно.
  • - *
- * - *

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

- * - *
{@code
- * SolanaTxWatcher watcher = new SolanaTxWatcher("5QgV...sig");
- *
- * while (watcher.shouldRetry()) {        // ◀ проверяем, нужно ли ещё опрашивать
- *     watcher.updateStatus();            // ◀ запрашиваем статус через RPC
- *     System.out.println(
- *         watcher.getStatus() + " | " +  // ◀ печатаем статус
- *         watcher.getStatusComment());
- *     Thread.sleep(SolanaTxWatcher.getRetryIntervalMs()); // ◀ ждём секунду
- * }
- *
- * if (watcher.isSuccess()) {
- *     System.out.println("✅ Транзакция прошла успешно!");
- * } else {
- *     System.out.println("⛔ Завершили слежение без успеха.");
- * }
- * }
- - */ -public class SolanaTxWatcher { - - /* ---------- НАСТРАИВАЕМЫЕ КОНСТАНТЫ ---------- */ - - /** Максимальное время слежения (мс). */ - private static final long TIMEOUT_MS = 30_000; - - /** Допустимое количество подряд статусов UNKNOWN / NETWORK_ERROR. */ - private static final int MAX_FAILED_ATTEMPTS = 3; - - /** Рекомендуемый интервал (мс) между вызовами {@link #updateStatus()}. */ - private static final long RETRY_INTERVAL_MS = 1_000; - - /* ---------- ПОЛЯ ЭКЗЕМПЛЯРА ---------- */ - - /** Подпись (signature) транзакции. */ - private final String signature; - - /** Время создания объекта (Unix-millis). */ - private final long startTimeMs; - - /** Кол-во подряд «слабых» ошибок (UNKNOWN / NETWORK_ERROR). */ - private int failedAttempts; - - /** Флаг: нужно ли ещё опрашивать RPC. */ - private boolean needRetry; - - /** Флаг: успешна ли транзакция (устанавливается при FINALIZED_SUCCESS). */ - private boolean success; - - /** Текущий статус из {@link TxStatus}. */ - private TxStatus status; - - /* ---------- ЧЕЛОВЕКО-ЧИТАЕМЫЕ ОПИСАНИЯ СТАТУСОВ ---------- */ - - private static final Map COMMENTS = new HashMap<>(); - static { - COMMENTS.put(TxStatus.NOT_FOUND, - "Подпись не дошла до RPC — ждём появления."); - COMMENTS.put(TxStatus.PROCESSED, - "Принята в обработку — ожидаем включения в блок."); - COMMENTS.put(TxStatus.CONFIRMED, - "Уже в блоке — ждём финализации."); - COMMENTS.put(TxStatus.FINALIZED_SUCCESS, - "Финализирована успешно."); - COMMENTS.put(TxStatus.FINALIZED_ERROR, - "Финализирована с ошибкой."); - COMMENTS.put(TxStatus.UNKNOWN, - "Неизвестная ошибка RPC/парсинга."); - COMMENTS.put(TxStatus.NETWORK_ERROR, - "Сбой сети или RPC недоступен."); - } - - /* ---------- КОНСТРУКТОР ---------- */ - - /** - * Создаёт watcher для указанной подписи. - * - * @param signature подпись транзакции (Base58). - */ - public SolanaTxWatcher(String signature) { - this.signature = signature; - this.startTimeMs = System.currentTimeMillis(); - this.failedAttempts = 0; - this.needRetry = true; // по умолчанию пытаемся - this.success = false; // успех пока не достигнут - this.status = TxStatus.NOT_FOUND; // стартовый - } - - /* ---------- ГЕТТЕРЫ ---------- */ - - /** @return подпись транзакции. */ - public String getSignature() { return signature; } - - /** @return время создания watcher’а (Unix-millis). */ - public long getStartTimeMs() { return startTimeMs; } - - /** @return текущий статус. */ - public TxStatus getStatus() { return status; } - - /** @return true, если транзакция финализирована без ошибок. */ - public boolean isSuccess() { return success; } - - /** @return кол-во подряд неудачных (UNKNOWN/NETWORK_ERROR) попыток. */ - public int getFailedAttempts() { return failedAttempts; } - - /** @return человеко-читаемый комментарий к текущему статусу. */ - public String getStatusComment() { - return COMMENTS.getOrDefault(status, ""); - } - - /* ---------- ОСНОВНОЙ МЕТОД ОПРОСА ---------- */ - - /** - * Запрашивает актуальный статус транзакции через {@link TransactionStatusHelper} - * и обновляет внутренние поля. - *

— При промежуточных статусах (NOT_FOUND / PROCESSED / CONFIRMED) - * счётчик ошибок сбрасывается.
- * — При {@code FINALIZED_SUCCESS} или {@code FINALIZED_ERROR} - * флаг {@link #needRetry} переводится в {@code false}.
- * — При {@code UNKNOWN} или {@code NETWORK_ERROR} - * счётчик ошибок увеличивается; если превышен лимит — дальнейший опрос прекращается. - */ - public void updateStatus() { - if (!needRetry) return; // уже решено не опрашивать - - TxStatus newStatus = - TransactionStatusHelper.getTxStatus(signature); - - switch (newStatus) { - - case NOT_FOUND: - case PROCESSED: - case CONFIRMED: - failedAttempts = 0; // успешное промежуточное обновление - break; - - case FINALIZED_SUCCESS: - success = true; - needRetry = false; // финальный успех - break; - - case FINALIZED_ERROR: - success = false; - needRetry = false; // финальный провал - break; - - case UNKNOWN: - case NETWORK_ERROR: - failedAttempts++; - if (failedAttempts > MAX_FAILED_ATTEMPTS) { - needRetry = false; // слишком много ошибок — прекращаем - } - break; - } - - status = newStatus; // сохраняем новый статус - } - - /* ---------- РЕШЕНИЕ: НУЖНО ЛИ ПОВТОРЯТЬ ---------- */ - - /** - * @return {@code true}, если можно и стоит делать ещё один запрос статуса.
- * {@code false} — если достигнут финальный статус, превышен таймаут - * или лимит неудачных попыток. - */ - public boolean shouldRetry() { - if (!needRetry) return false; // наш флаг запрещает - long elapsed = System.currentTimeMillis() - startTimeMs; - if (elapsed > TIMEOUT_MS) { // вышли за таймаут - needRetry = false; - return false; - } - return true; - } - - /* ---------- ВСПОМОГАТЕЛЬНОЕ ---------- */ - - /** @return рекомендуемую задержку (мс) между опросами. */ - public static long getRetryIntervalMs() { return RETRY_INTERVAL_MS; } - - @Override - public String toString() { - return String.format("[%s] sig=%s | status=%s | needRetry=%s | success=%s | attempts=%d | %s", - Instant.ofEpochMilli(startTimeMs), - signature, - status, - needRetry, - success, - failedAttempts, - getStatusComment()); - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/SolanaWrapper.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/SolanaWrapper.java deleted file mode 100644 index 73a14fc..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/SolanaWrapper.java +++ /dev/null @@ -1,88 +0,0 @@ -package me.shineup.solana; - -import me.shineup.solana.internal.callSolanaFunc.RegisterUser.RegisterUserWithOneDev; -import me.shineup.solana.model.TxStatus; -import me.shineup.solana.model.UserById; -import me.shineup.solana.internal.readFromSolana.userById.UserByIdReader; -import me.shineup.solana.model.UserByLogin; -import me.shineup.solana.internal.readFromSolana.userByLogin.UserByLoginReader; -import me.shineup.solana.internal.standartActions.airDrops.SolanaAirdrop; -import me.shineup.solana.internal.standartActions.balanse.SolanaBalanceChecker; -import me.shineup.solana.internal.standartActions.keysGenerator.KeyPairBase58; -import me.shineup.solana.internal.standartActions.keysGenerator.SolanaKeyGeneratorManual; -import me.shineup.solana.internal.standartActions.transfer.SolanaTransfer; -import me.shineup.solana.config.Const; -import me.shineup.solana.internal.utils.resultChecker.TransactionStatusHelper; - -public class SolanaWrapper { - - /** Получает баланс по публичному ключу */ - public static long getBalance(String publicKey) throws Exception{ - return SolanaBalanceChecker.getBalance(publicKey); - } - - /** Запрашивает Airdrop на указанный публичный ключ */ - public static String requestAirdrop(String publicKey, long lamports) throws Exception{ - return SolanaAirdrop.requestAirdrop(publicKey, lamports); - } - - /** Обёртка для перевода lamports между двумя публичными ключами (оба ключа — в base58, приватный — отправителя). */ -// public static String sendLamports(String fromBase58Secret, String toBase58Pubkey, long lamports) throws Exception { -// return SolanaTransfer.sendSol(fromBase58Secret, toBase58Pubkey, lamports); -// } - - /** Генерирует новый Ed25519-кошелёк (ключи Solana) */ - public static KeyPairBase58 generateNewWallet() throws Exception{ // todo возмаожно ключи генерируются недостаточно рандомно - return SolanaKeyGeneratorManual.generateKeyPair(); - } - - /** Генерирует новeую пару ключей X25519 */ - public static KeyPairBase58 generateNewKeyPairX25519() throws Exception{ // todo это пока заглушка - return SolanaKeyGeneratorManual.generateKeyPair(); - } - - /** Проверяет статус транзакции по её подписи (signature) */ - public static TxStatus getTransactionStatus(String signature) throws Exception { - return TransactionStatusHelper.getTxStatus(signature); - } - - /** - * Обёртка для перевода SOL с одного аккаунта на другой. - * - * @param fromBase58Secret приватный ключ отправителя (в base58) - * @param toAddressBase58 публичный ключ получателя (в base58) - * @param lamports сумма в лампортах (1 SOL = 1_000_000_000 лампортов) - */ - public static String sendSol(String fromBase58Secret, String toAddressBase58, long lamports) throws Exception{ - return SolanaTransfer.sendSol(fromBase58Secret, toAddressBase58, lamports); - } - - /** Выполняет регистрацию пользователя и одного устройства */ - public static String registerUserWithOneDev( - String payerPubkeyB58, - String payerPrivkeyB58, - String login, - String deviceSignPubkeyB58, - String deviceX25519PubkeyB58 - ) throws Exception { - return RegisterUserWithOneDev.callRegisterUserWithOneDev( - payerPubkeyB58, - payerPrivkeyB58, - login, - deviceSignPubkeyB58, - deviceX25519PubkeyB58 - ); - } - - /** Получить объект UserByLogin по логину */ - public static UserByLogin getUserByLogin(String login) throws Exception { - return UserByLoginReader.getUserByLogin(login); - } - - /** Получить объект UserById по числовому ID */ - public static UserById getUserById(long id) throws Exception { - return UserByIdReader.getUserById(id); - } - - -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/actions.md b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/actions.md deleted file mode 100644 index 49e4fbc..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/actions.md +++ /dev/null @@ -1,19 +0,0 @@ -InitializeUserCounter - инициализирует счётчик пользователь (вызывается один раз) - -RegisterUserWithOneDev - регистрирует пользователя с одним устройством -- оплачивает деньги -- решистрирует акаунт (PDA) по ЛОГИНУ и (PDA) по id -- регистрирует ключи пользователя и устройствва -- увеличивает количество пользователей в ситеме ++1 - -UserByLoginReader - читает данные пользователя по логину -UserCounterReader - читает количество пользователей в системе - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -SolanaKeyGeneratorManual - генерирует пары ключей - - - - - -SolanaBalanceChecker - показывает баланс акаунта -SolanaAirdrop - запрашивает Airdrop -SolanaTransfer - перевод со счёта на счёт ------------------------------------------ -Const - хранит константы настройки -ResultChecker(sig) - проверяет результат транзакции diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/config/Const.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/config/Const.java deleted file mode 100644 index 26d937b..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/config/Const.java +++ /dev/null @@ -1,76 +0,0 @@ -package me.shineup.solana.config; - -import me.shineup.solana.internal.utils.KeyPair; -import org.p2p.solanaj.core.PublicKey; - -import java.text.DecimalFormat; - - - -public class Const { - - /** Program ID из declare_id! */ - - public static final String PROGRAM_ID_str = "5dFcWDNp42Xn9Vv4oDMJzM4obBJ8hvDuAtPX54fT5L3t"; // shine - - public static final String userSeedsPrefix = "u="; // префикс для Seed адреса пользователя по логину - - public static String USER_COUNTER_SEED = "user_counter"; // Seed Адрес PDA счётчика пользователей - - public static final PublicKey PROGRAM_ID_key = new PublicKey(PROGRAM_ID_str);//"BmCgGmQbSjkE6Zg8WAwhxDMNHiTknMYqTF4ZVMrPdTpz"); // shine - - - - public static PublicKey ADMIN_FEE_ACCOUNT = new PublicKey("6bFc5Gz5qF172GQhK5HpDbWs8F6qcSxdHn5XqAstf1fY"); - - - public static final String LOCAL_RPC_URL = "http://127.0.0.1:8899"; - public static final String LOCAL_ANDROID_TEST_RPC_URL = "http://10.0.2.2:8899"; - public static final String TESTNET_RPC_URL = "https://api.testnet.solana.com"; - public static final String DEVNET_RPC_URL = "https://api.devnet.solana.com"; - - // RPC URL для используемой ноды Solana - public static String RPC_URL = DEVNET_RPC_URL; - // Запись для хранения ключей -// public record KeyPair(String name, String publicKey, String privateKey) {} не надо больше - - // Массив пар ключей - public static final KeyPair[] KEYS = { - new KeyPair("key1", "HMww7YSVfwVm4i8sugqj7wyH26dqzHykzv3wzWwzEvPA", // есть в дев нет!!! --url https://api.devnet.solana.com - "5pbFo9Zq1VsNheHwbEp6AZKa6R62CZHoGkJFZnugpMEtCmkQFjuUP7TgA5hSPqv4NABGmPP62qVnDPHmRqEAwvJc"), - new KeyPair("key2", "E3ZDHbWv1qiFvDTmaRc9wjFCgbQw6UmKJLJYbaTNvjAh", - "5qm1GJGXB1fFJ3YsU5Y3XXgTiQfaimqBWk79oEveFASH9D2of3jqUoT7dumBvS449fW5j5Sw8MgAMH2QBMmFPdry"), - new KeyPair("key3", "6bFc5Gz5qF172GQhK5HpDbWs8F6qcSxdHn5XqAstf1fY", - "3VYfYZZ3ugmgwisiQQAfcimX9T65AE9BmwmYVixAUj4jyneccSE9rzbC3g5twvH7ECZ8xgp7emJo3pR4yQqCwjGn") - }; - - // Метод для определения ключа - public static String identifyKey(String key) { - for (KeyPair kp : KEYS) { - if (kp.getPublicKey().equals(key)) { - return kp.getName() + "(public)"; - } - if (kp.getPrivateKey().equals(key)) { - return kp.getName() + "(private)"; - } - } - return key; // если не найдено - } - - // Метод для получения KeyPair по имени - public static KeyPair getKeyByName(String name) { - for (KeyPair kp : KEYS) { - if (kp.getName().equals(name)) { - return kp; - } - } - return null; // если не найдено - } - - // Метод форматирования лампортов в SOL - public static String lamportsToSol(long lamports) { - double sol = lamports / 1_000_000_000.0; - DecimalFormat df = new DecimalFormat("0.00000000"); - return df.format(sol); - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/ExeptionList.txt b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/ExeptionList.txt deleted file mode 100644 index f23c3a0..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/ExeptionList.txt +++ /dev/null @@ -1,35 +0,0 @@ - -Exeption - если что то полетело в коде - -SolanaRpcConnectionException - Не удалось подключиться к RPC Solana или получить ответ - -SolanaLibLogicException - Базовое исключение всех ошибок, связанных с Solana - Выкидываем во всех не стандартных случаях из библиотеки вместо стандартного Exception - -SolanaProgramException - Исключение, выбрасываемое при кастомной ошибке от Solana-программы (смарт контракта). Например: "custom program error: 0x1771" - -SolanaInsufficientFundsForFeeException - Недостаточно SOL для оплаты комиссии (InsufficientFundsForFee). - это если вызвали регистрацию без средств - (но получается тоже не надо так как - прога не вызовет её без проверки баланса) - - - - -нет такого пользователя - - - - - - - - - - - - - - - SolanaIncorrectProgramIdException Неверный programId — вызывается не та программа (IncorrectProgramId) -возможно надо что бы быстро находить новый програм Ид -(но по факту он не меняется если кто то не потеряет пароль!!! ) ну или потребуется один раз при переходе на DAO \ No newline at end of file diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaErrorHandler.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaErrorHandler.java deleted file mode 100644 index e53c095..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaErrorHandler.java +++ /dev/null @@ -1,43 +0,0 @@ -package me.shineup.solana.exceptions; - - -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; - -public class SolanaErrorHandler { - - public static void handleRpcJsonError(String json) throws SolanaException { - try { - JsonObject obj = JsonParser.parseString(json).getAsJsonObject(); - if (!obj.has("error")) return; - - JsonObject error = obj.getAsJsonObject("error"); - String msg = error.has("message") ? error.get("message").getAsString() : ""; - - handleSolanaError(msg); - - } catch (Exception e) { - // fallback - throw new SolanaException("Ошибка обработки RPC-ошибки", e); - } - } - - public static void handleSolanaError(String errorMessage) throws SolanaException { - if (errorMessage == null || errorMessage.isEmpty()) return; - - if (errorMessage.contains("custom program error: 0x")) { - String hex = errorMessage.substring(errorMessage.indexOf("0x")).split(" ")[0]; - throw new SolanaException_InProgram(hex); - } - - if (errorMessage.contains("InsufficientFundsForFee")) { - throw new SolanaException_InsufficientFundsForFee(); - } - - if (errorMessage.contains("IncorrectProgramId")) { - throw new SolanaException_IncorrectProgramId(); - } - - throw new SolanaException("Неизвестная ошибка Solana: " + errorMessage); - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaException.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaException.java deleted file mode 100644 index b054ba4..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaException.java +++ /dev/null @@ -1,13 +0,0 @@ -package me.shineup.solana.exceptions; - - -/** Базовое исключение всех ошибок, связанных с Solana */ -public class SolanaException extends Exception { - public SolanaException(String message) { - super(message); - } - - public SolanaException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaException_InProgram.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaException_InProgram.java deleted file mode 100644 index 4367d56..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaException_InProgram.java +++ /dev/null @@ -1,41 +0,0 @@ -package me.shineup.solana.exceptions; - -/** - * Исключение, выбрасываемое при кастомной ошибке от Solana-программы. - * Например: "custom program error: 0x1771" или 10001 - */ -public class SolanaException_InProgram extends SolanaException { - private final int errorCode; - - /** - * Создаёт исключение на основе шестнадцатеричного кода (например, "0x1771"). - */ - public SolanaException_InProgram(String errorCodeHex) { - super("Ошибка от смарт-контракта. Код: " + parseHex(errorCodeHex)); - this.errorCode = parseHex(errorCodeHex); - } - - /** - * Создаёт исключение на основе десятичного кода (например, 10001). - */ - public SolanaException_InProgram(int errorCodeDecimal) { - super("Ошибка от смарт-контракта. Код: " + errorCodeDecimal); - this.errorCode = errorCodeDecimal; - } - - public int getErrorCodeDecimal() { - return errorCode; - } - - public String getErrorCodeHex() { - return "0x" + Integer.toHexString(errorCode); - } - - private static int parseHex(String hex) { - try { - return Integer.parseInt(hex.replace("0x", ""), 16); - } catch (NumberFormatException e) { - return -1; - } - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaException_IncorrectProgramId.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaException_IncorrectProgramId.java deleted file mode 100644 index 4f4c6ad..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaException_IncorrectProgramId.java +++ /dev/null @@ -1,12 +0,0 @@ -package me.shineup.solana.exceptions; - -/** - * Неверный programId — вызывается не та программа (IncorrectProgramId). - */ -public class SolanaException_IncorrectProgramId extends SolanaException { - public SolanaException_IncorrectProgramId() { - super("Указан неверный programId — не соответствует контракту"); - } -} - - diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaException_InsufficientFundsForFee.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaException_InsufficientFundsForFee.java deleted file mode 100644 index 64f10ee..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaException_InsufficientFundsForFee.java +++ /dev/null @@ -1,10 +0,0 @@ -package me.shineup.solana.exceptions; - -/** - * Недостаточно SOL для оплаты комиссии (InsufficientFundsForFee). - */ -public class SolanaException_InsufficientFundsForFee extends SolanaException { - public SolanaException_InsufficientFundsForFee() { - super("Недостаточно средств на балансе для оплаты комиссии (InsufficientFundsForFee)"); - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaException_LibLogic.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaException_LibLogic.java deleted file mode 100644 index 2680816..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaException_LibLogic.java +++ /dev/null @@ -1,21 +0,0 @@ -package me.shineup.solana.exceptions; - -/** - * Исключение, выбрасываемое вручную при логических проверках в библиотеке. - * К сообщению автоматически добавляется строка вызова (класс и номер строки). - */ -public class SolanaException_LibLogic extends SolanaException { - - public SolanaException_LibLogic(String userMessage) { - super(userMessage + getSourceSuffix()); - } - - private static String getSourceSuffix() { - StackTraceElement[] stack = Thread.currentThread().getStackTrace(); - if (stack.length > 3) { - StackTraceElement caller = stack[3]; - return " (исходный код: " + caller.getFileName() + ":" + caller.getLineNumber() + ")"; - } - return ""; - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaException_RpcConnection.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaException_RpcConnection.java deleted file mode 100644 index 73e7ff2..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaException_RpcConnection.java +++ /dev/null @@ -1,8 +0,0 @@ -package me.shineup.solana.exceptions; - -/** Не удалось подключиться к RPC или получить ответ */ -public class SolanaException_RpcConnection extends SolanaException { - public SolanaException_RpcConnection(String message) { - super(message); - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaException_UserNotFound.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaException_UserNotFound.java deleted file mode 100644 index 04bf799..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/exceptions/SolanaException_UserNotFound.java +++ /dev/null @@ -1,14 +0,0 @@ -package me.shineup.solana.exceptions; - -/** - * Пользователь с указанным идентификатором не найден в системе. - */ -public class SolanaException_UserNotFound extends SolanaException { - public SolanaException_UserNotFound() { - super("Пользователь не найден в системе."); - } - - public SolanaException_UserNotFound(String msg) { - super("Пользователь не найден в системе: " + msg); - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/callSolanaFunc/InitializeUserCounter/InitializeUserCounter.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/callSolanaFunc/InitializeUserCounter/InitializeUserCounter.java deleted file mode 100644 index 677fa0e..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/callSolanaFunc/InitializeUserCounter/InitializeUserCounter.java +++ /dev/null @@ -1,98 +0,0 @@ -package me.shineup.solana.internal.callSolanaFunc.InitializeUserCounter; - - -import org.p2p.solanaj.core.AccountMeta; -import org.p2p.solanaj.core.PublicKey; -import org.p2p.solanaj.programs.SystemProgram; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import me.shineup.solana.internal.utils.resultChecker.ResultChecker; -import me.shineup.solana.config.Const; -import me.shineup.solana.internal.utils.SolanaProgramCaller; - -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -/** - * Вызывает Anchor-функцию `initialize_user_counter` смарт-контракта на Solana. - * - * Эта функция предназначена для одноразовой инициализации PDA-аккаунта, в котором будет храниться - * счётчик пользователей. Аккаунт создаётся с сидом "user_counter" и содержит 8 байт, представляющих число. - * - * Повторный вызов приведёт к ошибке (если PDA уже существует). - * - */ - -public class InitializeUserCounter { - - private static final Logger LOG = LoggerFactory.getLogger(InitializeUserCounter.class); - -/** - * Вызывает Anchor-функцию `initialize_user_counter` смарт-контракта на Solana. - * - * Эта функция предназначена для одноразовой инициализации PDA-аккаунта, в котором будет храниться - * счётчик пользователей. Аккаунт создаётся с сидом "user_counter" и содержит 8 байт, представляющих число. - * Вызвать её в принципе может кто угодно, кто оплатит создание этого пда - * - * После того как PDA будет создан, любой повторный вызов приведёт к ошибке (если PDA уже существует). - */ - public static String callInitializeUserCounter( - String publicKeyB58, - String privateKeyB58 - ) { - try { - // ───────────────────────────────────────────────────────────── - // 1. Генерируем PDA-адрес для сидов ["user_counter"] - // ───────────────────────────────────────────────────────────── - String seed = "user_counter"; - PublicKey counterPda = PublicKey.findProgramAddress( - Collections.singletonList(seed.getBytes(StandardCharsets.UTF_8)), - Const.PROGRAM_ID_key - ).getAddress(); - - // ───────────────────────────────────────────────────────────── - // 2. Аргументы Anchor-функции: initialize_user_counter не требует входных данных - // ───────────────────────────────────────────────────────────── - byte[] serializedArgs = new byte[0]; // нет параметров - - // ───────────────────────────────────────────────────────────── - // 3. Список аккаунтов - // ───────────────────────────────────────────────────────────── - List accounts = Arrays.asList( - new AccountMeta(new PublicKey(publicKeyB58), true, true), // payer / signer - new AccountMeta(counterPda, false, true), // pda: user_counter - new AccountMeta(SystemProgram.PROGRAM_ID, false, false) // system_program - ); - - // ───────────────────────────────────────────────────────────── - // 4. Вызов Anchor-функции - // ───────────────────────────────────────────────────────────── - return SolanaProgramCaller.callAnchorFunction( - publicKeyB58, - privateKeyB58, - "initialize_user_counter", // имя функции Anchor - Const.PROGRAM_ID_key, - accounts, - serializedArgs - ); - - } catch (Exception e) { - LOG.error("❌ Ошибка вызова initialize_user_counter", e); - return null; - } - } - - - // Для теста - public static void main(String[] args) { -// Const.RPC_URL=Const.DEVNET_RPC_URL; - String sig = InitializeUserCounter.callInitializeUserCounter( - Const.getKeyByName("key1").getPublicKey(), - Const.getKeyByName("key1").getPrivateKey() - ); - ResultChecker.check(sig); - } -} - diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/callSolanaFunc/RegisterUser/RegisterUserStepOne.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/callSolanaFunc/RegisterUser/RegisterUserStepOne.java deleted file mode 100644 index c2bef03..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/callSolanaFunc/RegisterUser/RegisterUserStepOne.java +++ /dev/null @@ -1,104 +0,0 @@ -package me.shineup.solana.internal.callSolanaFunc.RegisterUser; - -import org.p2p.solanaj.core.AccountMeta; -import org.p2p.solanaj.core.PublicKey; -import org.p2p.solanaj.programs.SystemProgram; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import me.shineup.solana.internal.utils.resultChecker.ResultChecker; -import me.shineup.solana.config.Const; -import me.shineup.solana.internal.utils.SolanaProgramCaller; - -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -/** - * Вызывает Anchor-функцию `register_user_step_one`. !!!!!!!!! ЭТО УСТАРЕЛО И БОЛЬШЕ НЕ НАДО - * - * Функция создаёт нового пользователя: - * - проверяет логин, - * - создаёт PDA-аккаунт пользователя, - * - переводит 0.01 SOL на счёт администрации, - * - сохраняет логин, ID, pubkey и статус 0 в PDA, - * - обновляет счётчик пользователей. - */ - - -public class RegisterUserStepOne { - - private static final Logger LOG = LoggerFactory.getLogger(RegisterUserStepOne.class); - - public static String callRegisterUserStepOne( - String payerPubkeyB58, - String payerPrivkeyB58, - String login, - String userPubkeyB58 - ) { - try { - // 1. Адрес получателя комиссии - PublicKey feeReceiver = Const.ADMIN_FEE_ACCOUNT; - - // 2. PDA-адрес по логину: seed = "u=" + login - String seed = Const.userSeedsPrefix + login; - PublicKey userPda = PublicKey.findProgramAddress( - Collections.singletonList(seed.getBytes(StandardCharsets.UTF_8)), - Const.PROGRAM_ID_key - ).getAddress(); - - // 3. Адрес PDA счётчика пользователей - String counterSeed = Const.USER_COUNTER_SEED; - PublicKey counterPda = PublicKey.findProgramAddress( - Collections.singletonList(counterSeed.getBytes(StandardCharsets.UTF_8)), - Const.PROGRAM_ID_key - ).getAddress(); - - // 4. Сериализация аргументов Anchor - byte[] loginBytes = login.getBytes(StandardCharsets.UTF_8); - byte[] userPubkeyBytes = new PublicKey(userPubkeyB58).toByteArray(); - - byte[] serializedArgs = SolanaProgramCaller.encodeAnchorArgs( - Arrays.asList("string", "pubkey"), - Arrays.asList(loginBytes, userPubkeyBytes) - ); - - // 5. Аккаунты, требуемые для вызова - List accounts = Arrays.asList( - new AccountMeta(new PublicKey(payerPubkeyB58), true, true), // signer - new AccountMeta(counterPda, false, true), // user_counter - new AccountMeta(userPda, false, true), // user_by_login_pda - new AccountMeta(SystemProgram.PROGRAM_ID, false, false), // system_program - new AccountMeta(feeReceiver, false, true) // fee_receiver - ); - - // 6. Вызов Anchor-функции - return SolanaProgramCaller.callAnchorFunction( - payerPubkeyB58, - payerPrivkeyB58, - "register_user_step_one", - Const.PROGRAM_ID_key, - accounts, - serializedArgs - ); - - } catch (Exception e) { - LOG.error("❌ Ошибка вызова register_user_step_one", e); - return null; - } - } - - // Для теста - public static void main(String[] args) { - Const.RPC_URL = Const.LOCAL_RPC_URL; - - String sig = RegisterUserStepOne.callRegisterUserStepOne( - Const.getKeyByName("key1").getPublicKey(), // кто платит - Const.getKeyByName("key1").getPrivateKey(), // - "testlogin", // логин пользователя - Const.getKeyByName("key2").getPublicKey() // публичный ключ пользователя - ); - ResultChecker.check(sig); - } - -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/callSolanaFunc/RegisterUser/RegisterUserWithOneDev.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/callSolanaFunc/RegisterUser/RegisterUserWithOneDev.java deleted file mode 100644 index 4d4ec4b..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/callSolanaFunc/RegisterUser/RegisterUserWithOneDev.java +++ /dev/null @@ -1,156 +0,0 @@ -package me.shineup.solana.internal.callSolanaFunc.RegisterUser; - -import me.shineup.solana.SolanaWrapper; -import me.shineup.solana.internal.readFromSolana.userCounter.UserCounterReader; -import me.shineup.solana.config.Const; -import me.shineup.solana.internal.standartActions.keysGenerator.KeyPairBase58; -import me.shineup.solana.internal.utils.SolanaProgramCaller; -import me.shineup.solana.internal.utils.resultChecker.ResultChecker; -import org.p2p.solanaj.core.AccountMeta; -import org.p2p.solanaj.core.PublicKey; -import org.p2p.solanaj.programs.SystemProgram; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import static me.shineup.solana.internal.standartActions.keysGenerator.SolanaKeyGeneratorManual.generateKeyPair; - -/** - * Вызывает Anchor-функцию `register_user_with_one_dev`. - * - * Выполняет регистрацию пользователя и одного устройства. - */ -public class RegisterUserWithOneDev { - - private static final Logger LOG = LoggerFactory.getLogger(RegisterUserWithOneDev.class); - - // Пример вызова - public static void main(String[] args) { -// Const.RPC_URL = Const.LOCAL_RPC_URL; - Const.RPC_URL=Const.DEVNET_RPC_URL; - try { - String publicKey = "2fppzT84GoDqQe2RCxuK2gjZUrMhkzKgVTY6BzfLF9RX"; - String privateKey = "2qTERJQ2EBPsHWNXxhAWW4pBk1beo1BWtifCPsDbrDhw9z5riNLsUUj6BNQ9UbprJq398Zk3Fv21ZGUjRrAU4T73"; - SolanaWrapper.getBalance(Const.getKeyByName("key1").publicKey); -// SolanaWrapper.sendSol(Const.getKeyByName("key1").getPrivateKey(),publicKey, 1000000); - SolanaWrapper.getBalance(publicKey); -// KeyPairBase58 keys = generateKeyPair(); - String sig = RegisterUserWithOneDev.callRegisterUserWithOneDev( - Const.getKeyByName("key1").getPublicKey(), // payer - Const.getKeyByName("key1").getPrivateKey(), // - "testlogin", // логин - Const.getKeyByName("key2").getPublicKey(), // подпись устройства - Const.getKeyByName("key2").getPublicKey() // x25519 - ); - ResultChecker.check(sig); - } catch (Exception e) { - LOG.error(e.getMessage()); - } - - - } - - - public static String callRegisterUserWithOneDev( - String payerPubkeyB58, - String payerPrivkeyB58, - String login, -// String userPubkeyB58, пока userPubkeyB58=payerPubkeyB58 - String deviceSignPubkeyB58, - String deviceX25519PubkeyB58 - ) throws Exception { - - // Адреса - PublicKey payer = new PublicKey(payerPubkeyB58); -/** PublicKey userPub = new PublicKey(userPubkeyB58); - * тут в принципе можно передавать на смарт контракт разные параметры - * и получиться что платит один а создаёт аккаунт на другое имя - * это может актуально при расширении, если дописать додумать и т.д. - * но пока это отключено и кто оплатил тна того и оформляем аккаунт - * пока так работает только за себя и можно отпраавлять транзакции - */ - PublicKey userPub = new PublicKey(payerPubkeyB58); // пока тот кто подписал транзакцию, на него же и аккаунт создаём - - - PublicKey devSignPub = new PublicKey(deviceSignPubkeyB58); - PublicKey devX25519Pub = new PublicKey(deviceX25519PubkeyB58); - - // Читаем текущий ID - long currentId = UserCounterReader.getUserCount(); - long newId = currentId + 1; - - // Счётчик пользователей - - PublicKey counterPda = PublicKey.findProgramAddress( - Arrays.asList("user_counter".getBytes(StandardCharsets.UTF_8)), - Const.PROGRAM_ID_key - ).getAddress(); - - // PDA по логину: ["login=", login] - PublicKey loginPda = PublicKey.findProgramAddress( - Arrays.asList( - "login=".getBytes(StandardCharsets.UTF_8), - login.getBytes(StandardCharsets.UTF_8) - ), - Const.PROGRAM_ID_key - ).getAddress(); - - // 5 возможных PDA по ID: ["userId=", String.valueOf(newId)] - String idSeedStr = String.valueOf(newId); - List idCandidates = new ArrayList<>(); - for (int i = 0; i < 5; i++) { - byte[] prefix = "userId=".getBytes(StandardCharsets.UTF_8); - byte[] id = idSeedStr.getBytes(StandardCharsets.UTF_8); - byte[] seed1 = prefix; - byte[] seed2 = id; - PublicKey pda = PublicKey.findProgramAddress(Arrays.asList(seed1, seed2), Const.PROGRAM_ID_key).getAddress(); - idCandidates.add(pda); - } - - // Комиссионный адрес - PublicKey feeReceiver = Const.ADMIN_FEE_ACCOUNT; - - // Аргументы Anchor - byte[] loginBytes = login.getBytes(StandardCharsets.UTF_8); - byte[] pubkeyBytes = userPub.toByteArray(); - byte[] devSignBytes = devSignPub.toByteArray(); - byte[] devX25519Bytes = devX25519Pub.toByteArray(); - - byte[] serializedArgs = SolanaProgramCaller.encodeAnchorArgs( - Arrays.asList("string", "pubkey", "pubkey", "pubkey"), - Arrays.asList(loginBytes, pubkeyBytes, devSignBytes, devX25519Bytes) - ); - - // Список аккаунтов - List accounts = new ArrayList(); - accounts.add(new AccountMeta(payer, true, true)); - accounts.add(new AccountMeta(counterPda, false, true)); - accounts.add(new AccountMeta(loginPda, false, true)); - - for (PublicKey idPda : idCandidates) { - accounts.add(new AccountMeta(idPda, false, true)); - } - - accounts.add(new AccountMeta(SystemProgram.PROGRAM_ID, false, false)); - accounts.add(new AccountMeta(feeReceiver, false, true)); - - // Вызов Anchor-функции - return SolanaProgramCaller.callAnchorFunction( - payerPubkeyB58, - payerPrivkeyB58, - "register_user_with_one_dev", - Const.PROGRAM_ID_key, - accounts, - serializedArgs - ); - - } - -} - - - diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/readFromSolana/userById/UserByIdParser.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/readFromSolana/userById/UserByIdParser.java deleted file mode 100644 index 6a06e9d..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/readFromSolana/userById/UserByIdParser.java +++ /dev/null @@ -1,103 +0,0 @@ -package me.shineup.solana.internal.readFromSolana.userById; - -import me.shineup.solana.exceptions.SolanaException; -import me.shineup.solana.model.UserById; -import org.bitcoinj.core.Base58; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; - -/** - * Десериализует сырые байты из PDA в объект {@link UserById}. - * - * Сейчас поддерживается только формат 1, но общий метод - * parse(...) готов к расширению версий. - */ -class UserByIdParser { - - /** Точка входа: определяем формат по первым 4 байтам (LE u32). */ - public static UserById parse(byte[] data) throws Exception { - if (data.length < 4) - throw new SolanaException("Недостаточно данных для чтения format_type"); - - int fmt = ByteBuffer.wrap(data, 0, 4) - .order(ByteOrder.LITTLE_ENDIAN) - .getInt(); - - switch (fmt) { - case 1: - return parseFormat1(data); - default: - throw new SolanaException("Неподдерживаемый формат: " + fmt); - } - } - - /** - * Формат 1 (см. Rust-комментарии): - * [0..4] format - * [4..12] id (u64 LE) - * [12] len(login) u8 - * [13..] login - * [...] pubkey 32 байта - * [...] deviceCount u8 - * [...] devices ×65 байт (type + 32 + 32) - */ - private static UserById parseFormat1(byte[] data) throws Exception { - int offset = 4; - - // id - if (data.length < offset + 8) throw new Exception("Мало байт для id"); - long id = ByteBuffer.wrap(data, offset, 8) - .order(ByteOrder.LITTLE_ENDIAN).getLong(); - offset += 8; - - // login - int loginLen = data[offset] & 0xFF; - offset += 1; - if (data.length < offset + loginLen) throw new Exception("Мало байт для login"); - String login = new String(data, offset, loginLen, StandardCharsets.UTF_8); - offset += loginLen; - - // pubkey - if (data.length < offset + 32) throw new Exception("Мало байт для pubkey"); - byte[] pubkeyBytes = new byte[32]; - System.arraycopy(data, offset, pubkeyBytes, 0, 32); - String pubkey58 = Base58.encode(pubkeyBytes); - offset += 32; - - // deviceCount - if (data.length < offset + 1) throw new Exception("Мало байт для deviceCount"); - int devCount = data[offset] & 0xFF; - offset += 1; - - // devices - List devices = new ArrayList<>(); - for (int i = 0; i < devCount; i++) { - if (data.length < offset + 65) - throw new Exception("Мало байт для devices[" + i + "]"); - UserById.DeviceInfo d = new UserById.DeviceInfo(); - d.deviceType = data[offset] & 0xFF; - byte[] devPub = new byte[32]; - byte[] x25519 = new byte[32]; - System.arraycopy(data, offset + 1, devPub, 0, 32); - System.arraycopy(data, offset + 33, x25519, 0, 32); - d.devicePubkey = Base58.encode(devPub); - d.x25519Pubkey = Base58.encode(x25519); - devices.add(d); - offset += 65; - } - - // собираем объект - UserById u = new UserById(); - u.format = 1; - u.id = id; - u.login = login; - u.pubkey = pubkey58; - u.deviceCount = devCount; - u.devices = devices; - return u; - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/readFromSolana/userById/UserByIdReader.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/readFromSolana/userById/UserByIdReader.java deleted file mode 100644 index 67d5a26..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/readFromSolana/userById/UserByIdReader.java +++ /dev/null @@ -1,48 +0,0 @@ -package me.shineup.solana.internal.readFromSolana.userById; - -import me.shineup.solana.exceptions.SolanaException_UserNotFound; -import me.shineup.solana.model.UserById; -import me.shineup.solana.internal.utils.reader.PdaReader; -import me.shineup.solana.config.Const; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.nio.charset.StandardCharsets; - -/** - * Читает из PDA данные пользователя по числовому ID - * и десериализует их в {@link UserById}. - */ -public class UserByIdReader { - - private static final Logger LOG = LoggerFactory.getLogger(UserByIdReader.class); - - /** Получить UserById по ID. */ - public static UserById getUserById(long id) throws Exception{ - byte[] seed1 = "userId=".getBytes(StandardCharsets.UTF_8); - byte[] seed2 = Long.toString(id).getBytes(StandardCharsets.UTF_8); - String programId = Const.PROGRAM_ID_str; - - byte[] data = PdaReader.readTwoSeeds(seed1, seed2, programId); - - if (data == null) { - LOG.warn("⚠️ Нет данных в PDA для id={}", id); - throw new SolanaException_UserNotFound(); - } - - return UserByIdParser.parse(data); // Передаём данные в парсер - } - - /** Быстрый тест чтения. */ - public static void main(String[] args) { - long id = 10; // поменять на существующий ID - try { - UserById u = UserByIdReader.getUserById(id); - System.out.println("✅ Найден: " + u); - } catch (SolanaException_UserNotFound e) { - System.out.println("⚠️ Пользователь с id=" + id + " не найден"); - } catch (Exception e) { - e.printStackTrace(); - } - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/readFromSolana/userByLogin/UserByLoginParser.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/readFromSolana/userByLogin/UserByLoginParser.java deleted file mode 100644 index 9c20a74..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/readFromSolana/userByLogin/UserByLoginParser.java +++ /dev/null @@ -1,73 +0,0 @@ -package me.shineup.solana.internal.readFromSolana.userByLogin; - - -import me.shineup.solana.model.UserByLogin; - -/** - * Парсер для десериализации массива байт в объект UserByLogin. - * Поддерживает несколько форматов, определяемых по первым 4 байтам (LE u32). - */ -class UserByLoginParser { - - /** - * Основной метод: определяет формат по первым 4 байтам, вызывает нужный парсер. - * @param data байты, полученные из PDA - * @return объект UserByLogin - * @throws Exception если формат неизвестен или ошибка разбора - */ - public static UserByLogin parse(byte[] data) throws Exception { - if (data.length < 4) throw new Exception("Недостаточно данных для чтения format_type"); - - int format = java.nio.ByteBuffer.wrap(data, 0, 4) - .order(java.nio.ByteOrder.LITTLE_ENDIAN).getInt(); - - switch (format) { - case 1: - return parseFormat1(data); - default: - throw new Exception("Неподдерживаемый формат данных: " + format); - } - } - - /** - * Парсит формат 1: - * [0..4] — формат - * [4] — длина логина (u8) - * [5..X] — логин - * [X..X+8] — id (u64 LE) - * [X..X+32] — pubkey (32 байта) - * [X+32..X+36] — status (u32 LE) - */ - public static UserByLogin parseFormat1(byte[] data) throws Exception { - int offset = 4; // после format_type - - int loginLen = data[offset] & 0xFF; - offset += 1; - - if (data.length < offset + loginLen + 8 + 32 + 4) - throw new Exception("Недостаточно байт для парсинга format 1"); - - String login = new String(data, offset, loginLen, java.nio.charset.StandardCharsets.UTF_8); - offset += loginLen; - - long id = java.nio.ByteBuffer.wrap(data, offset, 8) - .order(java.nio.ByteOrder.LITTLE_ENDIAN).getLong(); - offset += 8; - - byte[] pubkeyBytes = new byte[32]; - System.arraycopy(data, offset, pubkeyBytes, 0, 32); - String pubkey = org.bitcoinj.core.Base58.encode(pubkeyBytes); - offset += 32; - - int status = java.nio.ByteBuffer.wrap(data, offset, 4) - .order(java.nio.ByteOrder.LITTLE_ENDIAN).getInt(); - - UserByLogin result = new UserByLogin(); - result.format = 1; - result.login = login; - result.id = id; - result.pubkey = pubkey; - result.status = status; - return result; - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/readFromSolana/userByLogin/UserByLoginReader.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/readFromSolana/userByLogin/UserByLoginReader.java deleted file mode 100644 index ff227e4..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/readFromSolana/userByLogin/UserByLoginReader.java +++ /dev/null @@ -1,58 +0,0 @@ -package me.shineup.solana.internal.readFromSolana.userByLogin; - - -import me.shineup.solana.exceptions.SolanaException_UserNotFound; -import me.shineup.solana.model.UserByLogin; -import me.shineup.solana.internal.utils.reader.PdaReader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import me.shineup.solana.config.Const; - -import java.nio.charset.StandardCharsets; - -/** - * Класс для получения объекта UserByLogin по логину из PDA - */ -public class UserByLoginReader { - - private static final Logger LOG = LoggerFactory.getLogger(UserByLoginReader.class); - - /** - * Получает объект UserByLogin по логину - * @param login Логин пользователя (например, "sol_user") - * @return Объект UserByLogin или null, если данных нет - * @throws Exception если ошибка соединения или парсинга - */ - public static UserByLogin getUserByLogin(String login) throws Exception { - byte[] seed1 = "login=".getBytes(StandardCharsets.UTF_8); - byte[] seed2 = login.getBytes(StandardCharsets.UTF_8); - String programId = Const.PROGRAM_ID_str; - - byte[] data = PdaReader.readTwoSeeds(seed1, seed2, programId); - - if (data == null) { - LOG.warn("⚠️ Нет данных в PDA для логина '{}'", login); - throw new SolanaException_UserNotFound(); - } - - // Передаём данные в парсер - return UserByLoginParser.parse(data); - } - - /** - * Тестовый запуск чтения информации о пользователе - */ - public static void main(String[] args) { - String login = "testlogin3"; // замените на нужный логин - try { - UserByLogin user = UserByLoginReader.getUserByLogin(login); - System.out.println("✅ Найден пользователь: " + login); - } catch (SolanaException_UserNotFound e) { - System.out.println("⚠️ Пользователь с id=" + login + " не найден"); - } catch (Exception e) { - System.err.println("❌ Ошибка при получении информации о пользователе: " + e.getMessage()); - e.printStackTrace(); - } - } -} - diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/readFromSolana/userCounter/UserCounterReader.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/readFromSolana/userCounter/UserCounterReader.java deleted file mode 100644 index addb2a6..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/readFromSolana/userCounter/UserCounterReader.java +++ /dev/null @@ -1,60 +0,0 @@ -package me.shineup.solana.internal.readFromSolana.userCounter; - - - -import me.shineup.solana.exceptions.SolanaException; -import me.shineup.solana.internal.utils.reader.PdaReader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import me.shineup.solana.config.Const; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -/** - * Утилита для получения значения счётчика пользователей из PDA. - */ -public class UserCounterReader { - - private static final Logger LOG = LoggerFactory.getLogger(UserCounterReader.class); - - /** - * Считывает текущее количество пользователей из PDA "user_counter". - * - * @return количество пользователей (long), либо -1 если нет данных - * @throws Exception при ошибке подключения или чтения - */ - public static long getUserCount() throws Exception { - String seed = "user_counter"; - String programId = Const.PROGRAM_ID_str; - - byte[] data = PdaReader.readOneSeed(seed, programId); - if (data == null || data.length < 8) { - throw new SolanaException("⚠️ Не удалось прочитать счётчик пользователей — PDA пуст или слишком короткий"); // этого не должно случаться - } - - // Считываем первые 8 байт как u64 (Little Endian) - ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN); - long count = buffer.getLong(); - - LOG.debug("👥 На данный момент в системе зарегистрировано: {} пользователей", count); - return count; - } - - /** - * Тестовый запуск - */ - public static void main(String[] args) { - try { - long count = getUserCount(); - if (count >= 0) { - System.out.println("✅ 👥 Количество пользователей системы на данный момент: " + count); - } else { - System.out.println("⚠️ Счётчик не найден или пуст"); - } - } catch (Exception e) { - System.err.println("❌ Ошибка при чтении счётчика пользователей: " + e.getMessage()); - e.printStackTrace(); - } - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/standartActions/airDrops/SolanaAirdrop.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/standartActions/airDrops/SolanaAirdrop.java deleted file mode 100644 index 22bb9e5..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/standartActions/airDrops/SolanaAirdrop.java +++ /dev/null @@ -1,68 +0,0 @@ -package me.shineup.solana.internal.standartActions.airDrops; - - -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import me.shineup.solana.exceptions.SolanaException_LibLogic; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import me.shineup.solana.config.Const; -import me.shineup.solana.internal.utils.SolanaRpcClient; - - -public class SolanaAirdrop { - private static final Logger log = LoggerFactory.getLogger(SolanaAirdrop.class); - - - // - public static void main(String[] args) { - - // Кол-во лампортов (1 SOL = 1_000_000_000 лампортов) - long lamports = 1_000_000_000L; - try { - - // Вызываем airdrop - String trx = SolanaAirdrop.requestAirdrop(Const.getKeyByName("key1").getPublicKey(), lamports); - - - // Вывод результата - log.info("Баланс должен скоро обновиться. " + trx); - } catch (Exception e) { - log.error("Airdrop не удался." + e.getMessage()); - } - } - - /** запрашивает AirDrop на счёт */ - public static String requestAirdrop(String publicKey, long lamports) throws Exception{ -// String requestJson = """ -// { -// "jsonrpc": "2.0", -// "id": 1, -// "method": "requestAirdrop", -// "params": ["%s", %d] -// } -// """.formatted(publicKey, lamports); - - String requestJson = String.format( - "{\n" + - " \"jsonrpc\": \"2.0\",\n" + - " \"id\": 1,\n" + - " \"method\": \"requestAirdrop\",\n" + - " \"params\": [\"%s\", %d]\n" + - "}", publicKey, lamports); - - - String response = SolanaRpcClient.getInstance().sendRequest(requestJson); - - JsonObject json = JsonParser.parseString(response).getAsJsonObject(); - - if (json.has("result")) { - String txSignature = json.get("result").getAsString(); - log.info("✅ Airdrop успешно запрошен. Tx: " + txSignature); - return txSignature; - } - throw new SolanaException_LibLogic("Неизвестный ответ: " + response); - } - - -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/standartActions/balanse/SolanaBalanceChecker.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/standartActions/balanse/SolanaBalanceChecker.java deleted file mode 100644 index 31d2c0b..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/standartActions/balanse/SolanaBalanceChecker.java +++ /dev/null @@ -1,72 +0,0 @@ -package me.shineup.solana.internal.standartActions.balanse; - -import com.google.gson.Gson; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import me.shineup.solana.config.Const; -import me.shineup.solana.internal.utils.SolanaRpcClient; - -public class SolanaBalanceChecker { - - // для теста - public static void main(String[] args) { - try { - -// Const.RPC_URL = Const.LOCAL_RPC_URL; - Const.RPC_URL = "https://api.devnet.solana.com"; - //Long balance1 = SolanaBalanceChecker.getBalance(Const.getKeyByName("key1").getPublicKey()); - Long balance1 = SolanaBalanceChecker.getBalance("HMww7YSVfwVm4i8sugqj7wyH26dqzHykzv3wzWwzEvPA"); - Long balance2 = SolanaBalanceChecker.getBalance(Const.getKeyByName("key2").getPublicKey()); - Long balance3 = SolanaBalanceChecker.getBalance(Const.getKeyByName("key3").getPublicKey()); - } catch (Exception e) { - log.error( e.getMessage()); - } - } - - private static final Logger log = LoggerFactory.getLogger(SolanaBalanceChecker.class); - private final Gson gson = new Gson(); - - /** показывает баланс счёта */ - public static long getBalance(String publicKey) throws Exception { - - String requestJson = String.format( - "{\n" + - " \"jsonrpc\": \"2.0\",\n" + - " \"id\": 1,\n" + - " \"method\": \"getBalance\",\n" + - " \"params\": [\"%s\"]\n" + - "}", publicKey); -// try { - String responseJson = SolanaRpcClient.getInstance().sendRequest(requestJson); - - // Парсим строку JSON в дерево объектов - JsonObject root = JsonParser.parseString(responseJson).getAsJsonObject(); - -// if (root.has("error")) { -// log.error("❌ Не удалось получить баланс для " + publicKey); -// log.error("Ошибка от RPC: " + root.get("error")); -// new SolanaLibLogicException() ; -// } - - log.debug("📥 Ответ от RPC: " + responseJson); - - long balance = root.getAsJsonObject("result").get("value").getAsLong(); - double sol = balance / 1_000_000_000.0; - - log.info("✅ Баланс кошелька " + Const.identifyKey(publicKey) + ": " + Const.lamportsToSol(balance));// + " SOL или лампортов " + balance ); - - return balance; - -// } catch (Exception e) { -// log.error("❌ Не удалось получить баланс для " + publicKey); -// log.error("Ошибка при получении баланса: " + e.getMessage()); -// log.error("Ошибка при получении баланса: " + e.getStackTrace()[0] ); -// log.error("Ошибка при получении баланса: " + e.getStackTrace()[1] ); -// log.error("Ошибка при получении баланса: " + e.getStackTrace()[2] ); -// return -1; -// } - - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/standartActions/keysGenerator/KeyPairBase58.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/standartActions/keysGenerator/KeyPairBase58.java deleted file mode 100644 index 3eef7b4..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/standartActions/keysGenerator/KeyPairBase58.java +++ /dev/null @@ -1,26 +0,0 @@ -package me.shineup.solana.internal.standartActions.keysGenerator; - - - -/** - * Объект, представляющий пару ключей Solana в формате Base58: - * - публичный ключ (32 байта) - * - приватный ключ (64 байта, включает публичный) - */ -public class KeyPairBase58 { - public final String publicKey; - public final String privateKey; - - public KeyPairBase58(String publicKey, String privateKey) { - this.publicKey = publicKey; - this.privateKey = privateKey; - } - - @Override - public String toString() { - return "KeyPairBase58{\n" + - " publicKey='" + publicKey + "',\n" + - " privateKey='" + privateKey + "'\n" + - '}'; - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/standartActions/keysGenerator/SolanaKeyGeneratorManual.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/standartActions/keysGenerator/SolanaKeyGeneratorManual.java deleted file mode 100644 index f9cdd46..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/standartActions/keysGenerator/SolanaKeyGeneratorManual.java +++ /dev/null @@ -1,101 +0,0 @@ -package me.shineup.solana.internal.standartActions.keysGenerator; - - - -import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters; -import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator; -import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters; -import org.bouncycastle.crypto.KeyGenerationParameters; -import org.bouncycastle.crypto.AsymmetricCipherKeyPair; - -import java.security.SecureRandom; - -public class SolanaKeyGeneratorManual { - - // ✅ Новый метод: возвращает пару ключей (в Base58) - public static KeyPairBase58 generateKeyPair() { - // Генератор ключей Ed25519 - Ed25519KeyPairGenerator keyGen = new Ed25519KeyPairGenerator(); - keyGen.init(new KeyGenerationParameters(new SecureRandom(), 256)); - AsymmetricCipherKeyPair keyPair = keyGen.generateKeyPair(); - - Ed25519PrivateKeyParameters privateKeyParams = (Ed25519PrivateKeyParameters) keyPair.getPrivate(); - Ed25519PublicKeyParameters publicKeyParams = (Ed25519PublicKeyParameters) keyPair.getPublic(); - - byte[] privateKey = privateKeyParams.getEncoded(); // 32 байта - byte[] publicKey = publicKeyParams.getEncoded(); // 32 байта - - byte[] solanaSecretKey = new byte[64]; - System.arraycopy(privateKey, 0, solanaSecretKey, 0, 32); - System.arraycopy(publicKey, 0, solanaSecretKey, 32, 32); - - return new KeyPairBase58( - Base58.encode(publicKey), - Base58.encode(solanaSecretKey) - ); - } - - - - public static void main(String[] args) { - KeyPairBase58 keys = generateKeyPair(); - - System.out.println("✅ Сгенерирован новый кошелёк Solana:"); - System.out.println("Публичный ключ (Base58): " + keys.publicKey); - System.out.println("Приватный ключ (Base58, 64 байта): " + keys.privateKey); - - System.out.println(); - System.out.println("String publicKey = \"" + keys.publicKey + "\";"); - System.out.println("String privateKey = \"" + keys.privateKey + "\";"); - } - -// -// // 👇 Встроенная реализация Base58 (без bitcoinj) - static class Base58 { - private static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); - private static final int BASE_58 = ALPHABET.length; - - public static String encode(byte[] input) { - if (input.length == 0) return ""; - - // Count leading zeros. - int zeros = 0; - while (zeros < input.length && input[zeros] == 0) { - ++zeros; - } - - // Convert base-256 digits to base-58 digits (plus conversion to ASCII characters) - int size = input.length * 2; - int[] encoded = new int[size]; - int length = 0; - - for (byte b : input) { - int carry = b & 0xFF; - int i = 0; - for (int j = size - 1; (carry != 0 || i < length) && j >= 0; j--, i++) { - carry += 256 * encoded[j]; - encoded[j] = carry % BASE_58; - carry /= BASE_58; - } - length = i; - } - - // Skip leading zeros in encoded. - int encodedStart = size - length; - while (encodedStart < size && encoded[encodedStart] == 0) { - encodedStart++; - } - - // Translate the result into a string. - StringBuilder result = new StringBuilder(zeros + size - encodedStart); - for (int i = 0; i < zeros; i++) { - result.append('1'); - } - for (int i = encodedStart; i < size; i++) { - result.append(ALPHABET[encoded[i]]); - } - - return result.toString(); - } - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/standartActions/transfer/SolanaTransfer.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/standartActions/transfer/SolanaTransfer.java deleted file mode 100644 index 0f1d738..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/standartActions/transfer/SolanaTransfer.java +++ /dev/null @@ -1,73 +0,0 @@ -package me.shineup.solana.internal.standartActions.transfer; - -import me.shineup.solana.exceptions.SolanaException_RpcConnection; -import me.shineup.solana.internal.standartActions.balanse.SolanaBalanceChecker; -import org.p2p.solanaj.core.Account; -import org.p2p.solanaj.core.PublicKey; -import org.p2p.solanaj.core.Transaction; -import org.p2p.solanaj.rpc.RpcClient; -import org.p2p.solanaj.rpc.RpcException; -import org.p2p.solanaj.programs.SystemProgram; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import me.shineup.solana.config.Const; -import me.shineup.solana.internal.utils.KeyUtils; - -import static me.shineup.solana.internal.utils.SolanaProgramCaller.getLatestBlockhash; -import static me.shineup.solana.internal.utils.SolanaProgramCaller.sendTransactionWithBlockhash; - -public class SolanaTransfer { - private static final Logger log = LoggerFactory.getLogger(SolanaTransfer.class); - - public static void main(String[] args) { - try { - Const.RPC_URL = Const.LOCAL_RPC_URL; - SolanaBalanceChecker.getBalance(Const.getKeyByName("key1").getPublicKey()); - SolanaTransfer.sendSol(Const.getKeyByName("key1").getPrivateKey(), Const.getKeyByName("key2").getPublicKey(),100_000_000); - SolanaBalanceChecker.getBalance(Const.getKeyByName("key1").getPublicKey()); - SolanaBalanceChecker.getBalance(Const.getKeyByName("key2").getPublicKey()); - } catch (Exception e) { - log.error( e.getMessage()); - } - } - - - - /** - * Переводит lamports (1 SOL = 1_000_000_000 лампортов) - * @param fromBase58Secret - приватный ключ в виде массива из 64 байт - * @param toAddressBase58 - публичный ключ получателя - * @param lamports - сумма в лампортах - */ - public static String sendSol(String fromBase58Secret, String toAddressBase58, long lamports) throws Exception{ - try { - - RpcClient rpc = new RpcClient(Const.RPC_URL); - - byte[] senderSecretKey58 = KeyUtils.base58ToBytes(fromBase58Secret); - - // Загружаем отправителя - Account from = new Account(senderSecretKey58); - PublicKey to = new PublicKey(toAddressBase58); - - // Создаём транзакцию - Transaction transaction = new Transaction(); - transaction.addInstruction( - SystemProgram.transfer(from.getPublicKey(), to, lamports) - ); - - // Получаем blockhash - String recentBlockhash = getLatestBlockhash(rpc); - - // отправляем транзакцию - String signature = sendTransactionWithBlockhash(rpc, transaction, recentBlockhash, from); - - - log.info("✅ Перевод " + Const.lamportsToSol(lamports) + " отправлен с " + Const.identifyKey(fromBase58Secret) + " на " + Const.identifyKey(toAddressBase58) + " . Подпись: " + signature); - return signature; - } catch (RpcException e) { - throw new SolanaException_RpcConnection("❌ Ошибка RPC: " + e.getMessage()); - } - - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/KeyPair.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/KeyPair.java deleted file mode 100644 index f47a04f..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/KeyPair.java +++ /dev/null @@ -1,55 +0,0 @@ -package me.shineup.solana.internal.utils; - -public class KeyPair { - - public final String name; - public final String publicKey; - public final String privateKey; - - public KeyPair(String name, String publicKey, String privateKey) { - this.name = name; - this.publicKey = publicKey; - this.privateKey = privateKey; - } - - public String getName() { - return name; - } - - public String getPublicKey() { - return publicKey; - } - - public String getPrivateKey() { - return privateKey; - } - - @Override - public String toString() { - return "KeyPair{" + - "name='" + name + '\'' + - ", publicKey='" + publicKey + '\'' + - ", privateKey='" + privateKey + '\'' + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - KeyPair keyPair = (KeyPair) o; - - if (!name.equals(keyPair.name)) return false; - if (!publicKey.equals(keyPair.publicKey)) return false; - return privateKey.equals(keyPair.privateKey); - } - - @Override - public int hashCode() { - int result = name.hashCode(); - result = 31 * result + publicKey.hashCode(); - result = 31 * result + privateKey.hashCode(); - return result; - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/KeyUtils.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/KeyUtils.java deleted file mode 100644 index 15eaf54..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/KeyUtils.java +++ /dev/null @@ -1,39 +0,0 @@ -package me.shineup.solana.internal.utils; - - -import java.util.Arrays; - -public class KeyUtils { - - private static final String BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; - - public static byte[] base58ToBytes(String base58) { - // Простейший декодер Base58 - int[] indexes = new int[128]; - Arrays.fill(indexes, -1); - for (int i = 0; i < BASE58_ALPHABET.length(); i++) { - indexes[BASE58_ALPHABET.charAt(i)] = i; - } - - byte[] input = new byte[base58.length()]; - for (int i = 0; i < base58.length(); i++) { - input[i] = (byte) indexes[base58.charAt(i)]; - } - - byte[] result = new byte[64]; // длина ключа - int length = 0; - - for (byte b : input) { - int carry = b & 0xFF; - int i = 0; - for (int j = result.length - 1; (carry != 0 || i < length) && j >= 0; j--, i++) { - carry += 58 * (result[j] & 0xFF); - result[j] = (byte) (carry % 256); - carry /= 256; - } - length = i; - } - - return result; - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/SolanaProgramCaller.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/SolanaProgramCaller.java deleted file mode 100644 index e8ac18f..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/SolanaProgramCaller.java +++ /dev/null @@ -1,378 +0,0 @@ -package me.shineup.solana.internal.utils; - -import me.shineup.solana.config.Const; -import me.shineup.solana.exceptions.SolanaException; -import me.shineup.solana.exceptions.SolanaException_InProgram; -import me.shineup.solana.exceptions.SolanaException_InsufficientFundsForFee; -import me.shineup.solana.exceptions.SolanaException_RpcConnection; -import org.bitcoinj.core.Base58; -import org.p2p.solanaj.core.*; -import org.p2p.solanaj.rpc.RpcClient; -import org.p2p.solanaj.rpc.RpcException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.net.ConnectException; -import java.net.SocketTimeoutException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.util.*; - -import java.util.Map; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - - -public class SolanaProgramCaller { - - // Логгер для вывода отладочной информации - private static final Logger log = LoggerFactory.getLogger(SolanaProgramCaller.class); - - /** - * Универсальный метод вызова Anchor-функции на Solana. - * - * @param publicKeyB58 Публичный ключ вызывающего аккаунта в формате base58 - * @param privateKeyB58 Приватный ключ в формате base58 (только первые 32 байта или полный 64-байтный seed) - * @param functionName Имя функции Anchor (например: "register_user") - * @param programId Адрес Anchor-программы, в которую делается вызов - * @param accounts Список аккаунтов, необходимых для вызова (AccountMeta) - * @param serializedArgs Сериализованные аргументы вызова (без дискриминатора) - * @return base58-подпись транзакции или null при ошибке - */ - public static String callAnchorFunction( - String publicKeyB58, - String privateKeyB58, - String functionName, - PublicKey programId, - List accounts, - byte[] serializedArgs - ) throws Exception { - - // Создаём RPC клиент для взаимодействия с Solana (через URL, заданный в конфиге) - RpcClient rpc = new RpcClient(Const.RPC_URL); - - // Декодируем публичный ключ вызывающего аккаунта из Base58 строки - PublicKey pubKey = new PublicKey(publicKeyB58); - - // Декодируем приватный ключ из Base58 строки (ожидается 32 байта seed или 64 байта полного ключа) - byte[] priv = Base58.decode(privateKeyB58); - - // Если длина приватного ключа всего 32 байта (seed), то дополняем его 32 байтами публичного ключа - if (priv.length == 32) { - byte[] full = new byte[64]; // 64-байтный ключ для Ed25519 (32 priv + 32 pub) - System.arraycopy(priv, 0, full, 0, 32); // копируем 32 байта приватного ключа - System.arraycopy(pubKey.toByteArray(), 0, full, 32, 32); // дописываем 32 байта публичного ключа - priv = full; // теперь priv содержит полный 64-байтный ключ - } - - // Создаём объект аккаунта для подписи транзакции - Account signer = new Account(priv); - - // Логируем вызов функции и аккаунт, от имени которого он будет происходить - log.debug("Вызов Anchor-функции '{}' от аккаунта {}", functionName, Const.identifyKey(pubKey.toBase58())); - - // Генерируем Anchor-дискриминатор — первые 8 байт SHA-256 хеша строки "global:имя_функции" - byte[] discriminator = MessageDigest - .getInstance("SHA-256") // используем SHA-256 - .digest(("global:" + functionName).getBytes(StandardCharsets.UTF_8)); // хешируем - discriminator = Arrays.copyOf(discriminator, 8); // берём только первые 8 байт (Anchor-дискриминатор) - - // Формируем payload: сначала дискриминатор, потом сериализованные параметры - byte[] data = new byte[discriminator.length + serializedArgs.length]; - System.arraycopy(discriminator, 0, data, 0, discriminator.length); // копируем дискриминатор - System.arraycopy(serializedArgs, 0, data, discriminator.length, serializedArgs.length); // копируем параметры - - // Создаём инструкцию Solana-транзакции: - // - указываем ID программы (куда отправляем) - // - список аккаунтов, задействованных в вызове (AccountMeta) - // - подготовленные данные (дискриминатор + параметры) - TransactionInstruction instruction = new TransactionInstruction( - programId, // ID Solana-программы (Anchor) - accounts, // список аккаунтов - data // бинарные данные вызова - ); - - // Создаём Solana транзакцию и добавляем в неё инструкцию - Transaction tx = new Transaction(); - tx.addInstruction(instruction); - - - // Получаем blockhash - String recentBlockhash = getLatestBlockhash(rpc); - - // отправляем транзакцию - String sig = sendTransactionWithBlockhash(rpc, tx, recentBlockhash, signer); - - log.info("✅ Tx отправлена: {}", sig); - - return sig; // возвращаем base58-подпись транзакции - - } - - // Вспомогательная склейка массивов - public static byte[] concat(byte[]... arrays) { - int length = 0; - for (byte[] a : arrays) length += a.length; - byte[] result = new byte[length]; - int pos = 0; - for (byte[] a : arrays) { - System.arraycopy(a, 0, result, pos, a.length); - pos += a.length; - } - return result; - } - - - /** - * Сериализует аргументы Anchor/Borsh. - * Сейчас поддерживает: string, pubkey. - * - string → 4-байтная длина (LE) + UTF-8 - * - pubkey → 32 байта - */ - public static byte[] encodeAnchorArgs(List types, List values) { - if (types.size() != values.size()) - throw new IllegalArgumentException("Количество типов и значений должно совпадать"); - - ByteBuffer buf = ByteBuffer.allocate(1024).order(ByteOrder.LITTLE_ENDIAN); - - for (int i = 0; i < types.size(); i++) { - String t = types.get(i); - Object v = values.get(i); - - - switch (t) { - case "string": { - byte[] s = (byte[]) v; // уже UTF-8 байты - buf.putInt(s.length); // 4-byte length LE - buf.put(s); - break; - } - case "pubkey": { - byte[] pk = (byte[]) v; - if (pk.length != 32) - throw new IllegalArgumentException("Pubkey должен быть 32 байта"); - buf.put(pk); - break; - } - default: - throw new UnsupportedOperationException("Неизвестный тип: " + t); - } - } - byte[] out = new byte[buf.position()]; - buf.flip(); - buf.get(out); - return out; - } - - - /*------------------------------------------------------------ - * 1. Получаем актуальный blockhash - *-----------------------------------------------------------*/ - @SuppressWarnings("unchecked") - public static String getLatestBlockhash(RpcClient rpc) throws Exception { - - Map commitment = new HashMap(); - commitment.put("commitment", "finalized"); - - Map bhJson; - try { - bhJson = rpc.call( - "getLatestBlockhash", - Collections.singletonList(commitment), - Map.class - ); - // остальной код - } catch (Exception e) { - throw new SolanaException_RpcConnection("Неудаётся соедениться при попытке получить blockhash: " + e.getMessage()); - } - - // --- проверка ответа + поддержка обоих форматов --- - if (bhJson == null) { - throw new SolanaException("При получении номера последнего блока RPC вернул null"); - } - if (bhJson.containsKey("error")) { - throw new SolanaException("При получении номера последнего блока: RPC error: " + bhJson.get("error")); - } - - Map value; - if (bhJson.containsKey("result")) { - Map result = (Map) bhJson.get("result"); - value = (Map) result.get("value"); - } else { - value = (Map) bhJson.get("value"); - } - - if (value == null) { - throw new SolanaException("При получении номера последнего блока: Поле \"value\" отсутствует. Ответ: " + bhJson); - } - String recentBlockhash = (String) value.get("blockhash"); - if (recentBlockhash == null) { - throw new SolanaException("При получении номера последнего блока: Поле \"blockhash\" отсутствует. Ответ: " + bhJson); - } - - return recentBlockhash; - } - - /*------------------------------------------------------------ - * 2. Подписываем и отправляем транзакцию, зная blockhash - *-----------------------------------------------------------*/ - public static String sendTransactionWithBlockhash( - RpcClient rpc, - Transaction tx, - String recentBlockhash, - Account... signers - ) throws Exception { - - /* ---- подставляем hash ---- */ - tx.setRecentBlockHash(recentBlockhash); - - /* ---- подписываем ---- */ - if (signers.length == 0) { - throw new IllegalArgumentException("Нужен хотя бы один подписант"); - } else if (signers.length == 1) { - tx.sign(signers[0]); - } else { - tx.sign(Arrays.asList(signers)); - } - - -/** // можно сериализовывать в базу 64 и в базу 58 // пока оставил 58 -// // ---- сериализация в база 64 ---- -// String b64Tx = Base64.getEncoder() -// .encodeToString(tx.serialize()); -// -// // ---- отправка ---- -// Map cfg = new HashMap(); -// cfg.put("skipPreflight", Boolean.FALSE); //Boolean.TRUE); -// -// cfg.put("encoding", "base64"); // ← ключевое добавление! -// -// -// return rpc.call( -// "sendTransaction", -// Arrays.asList(b64Tx, cfg), -// String.class -// ); - */ - - /* ---- сериализация и отправка в база 58 ---- */ - byte[] txBytes = tx.serialize(); - String base58EncodedTx = Base58.encode(txBytes); - - Map cfg = new HashMap<>(); - cfg.put("skipPreflight", false); // если false - то транзакцию сразу проверяют на ошибки - - try { - return rpc.call( - "sendTransaction", - Arrays.asList(base58EncodedTx, cfg), - String.class - ); - } catch (RpcException e) { - /* ---------- низкоуровневые сетевые ошибки (не достучались до RPC) */ - Throwable cause = e.getCause(); - if (cause instanceof ConnectException) { - throw new SolanaException_RpcConnection("Не удалось подключиться к RPC-узлу: " + cause.getMessage()); - } - if (cause instanceof SocketTimeoutException) { - throw new SolanaException_RpcConnection("Время ожидания ответа RPC истекло"); - } - - /* ---------- парсинг сообщения об ошибке от RPC/симуляции -------- */ - String err = Objects.toString(e.getMessage(), ""); - - /* 1) Стандартные InstructionError-ы */ - if (err.contains("\"InstructionError\"")) { - // Чаще всего приходит: "InstructionError": [0,"InsufficientFunds"] - if (err.contains("InsufficientFunds")) { - throw new SolanaException_InsufficientFundsForFee(); - } -// if (err.contains("AccountNotFound")) { -// throw new SolanaException_AccountNotFound(); -// } -// if (err.contains("InvalidAccountData")) { -// throw new SolanaException_InvalidAccountData(); -// } -// if (err.contains("InstructionMissing")) { -// throw new SolanaException_InstructionMissing(); -// } -// if (err.contains("UninitializedAccount")) { -// throw new SolanaException_UninitializedAccount(); -// } -// if (err.contains("IncorrectProgramId")) { -// throw new SolanaException_IncorrectProgramId(); -// } - } - - /* ) Отдельная проверка на ошибку дебета с пустого аккаунта */ - if (err.contains("insufficient funds for rent") // или такое сообщение если акаунт пуст - || err.matches("(?i).*custom program error: 0x0*1\\b.*")) { // или такое если денег не хватает - throw new SolanaException_InsufficientFundsForFee(); - } - - /* 2) Кастомная ошибка из твоей программы: "custom program error: 0xXXXX" */ - Pattern p = Pattern.compile("custom program error: (0x[0-9a-fA-F]+)"); - Matcher m = p.matcher(err); - if (m.find()) { - String hexCode = m.group(1); - int decimalCode = Integer.parseInt(hexCode.substring(2), 16); - throw new SolanaException_InProgram(decimalCode); - } - - /* 3) Прочие симуляционные ошибки (можно расширять по вкусу) */ -// if (err.contains("Transaction simulation failed")) { -// throw new SolanaException( -// "Симуляция транзакции завершилась ошибкой: " + err -// ); -// } - - /* 4) Всё остальное – оборачиваем как «неизвестную» */ - throw new SolanaException("RPC-ошибка: " + err, e); - - } - } - - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/SolanaRpcClient.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/SolanaRpcClient.java deleted file mode 100644 index 6709444..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/SolanaRpcClient.java +++ /dev/null @@ -1,86 +0,0 @@ -package me.shineup.solana.internal.utils; - -import me.shineup.solana.config.Const; -import me.shineup.solana.exceptions.SolanaException_RpcConnection; -import me.shineup.solana.exceptions.SolanaErrorHandler; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.io.IOException; - -public class SolanaRpcClient { - - private static final Logger log = LoggerFactory.getLogger(SolanaRpcClient.class); - private static SolanaRpcClient instance; - - private final OkHttpClient httpClient; - private final String rpcUrl; - - private static final MediaType JSON = MediaType.get("application/json; charset=utf-8"); - - private SolanaRpcClient(String rpcUrl) { - this.rpcUrl = rpcUrl; - this.httpClient = new OkHttpClient(); - } - - public static SolanaRpcClient getInstance() { - if (instance == null) { - instance = new SolanaRpcClient(Const.RPC_URL); - } - return instance; - } - - public static SolanaRpcClient withCustomUrl(String customUrl) { - instance = new SolanaRpcClient(customUrl); - return instance; - } - - /** - * Выполняет JSON-RPC запрос и возвращает ответ как строку. - * В случае ошибки — выбрасывает RpcConnectionException. - */ - public String sendRequest(String jsonRequest) throws SolanaException_RpcConnection { - log.debug("📤 Отправка RPC-запроса: {}", jsonRequest); - - RequestBody body = RequestBody.create(jsonRequest, JSON); - - Request request = new Request.Builder() - .url(rpcUrl) - .post(body) - .addHeader("Content-Type", "application/json") - .build(); - - try (Response response = httpClient.newCall(request).execute()) { - if (!response.isSuccessful()) { - log.error("❌ RPC ответ с ошибкой: {} {}", response.code(), response.message()); - throw new SolanaException_RpcConnection("RPC ответ с ошибкой: " + response.code() + " " + response.message()); - } - - if (response.body() == null) { - log.error("❌ RPC вернул пустое тело"); - throw new SolanaException_RpcConnection("Пустой ответ от RPC"); - } - - String responseText = response.body().string(); - log.debug("📥 Получен ответ от RPC: {}", responseText); - - // ✅ Обработка ошибок, если в ответе есть error - if (responseText.contains("\"error\"")) { - SolanaErrorHandler.handleRpcJsonError(responseText); - } - - return responseText; - - } catch (IOException e) { - log.error("❌ Ошибка подключения к RPC: {}", e.toString()); - throw new SolanaException_RpcConnection("Ошибка подключения к RPC" + e.toString()); - } catch (Exception e) { - log.error("❌ Непредвиденная ошибка при вызове RPC: {}", e.toString()); - throw new SolanaException_RpcConnection("Непредвиденная ошибка RPC" + e.toString()); - } - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/jsonrpc/JsonRpcRequest.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/jsonrpc/JsonRpcRequest.java deleted file mode 100644 index 66310a8..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/jsonrpc/JsonRpcRequest.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.shineup.solana.internal.utils.jsonrpc; - - - -import java.util.List; - - -/** странная фигня котыль который пришлось добавить при переходе на старую библиотеку solanaj для джава 8 */ -public class JsonRpcRequest { - public String jsonrpc = "2.0"; - public String method; - public List params; - public int id = 1; - - public JsonRpcRequest(String method, List params) { - this.method = method; - this.params = params; - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/reader/PdaReader.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/reader/PdaReader.java deleted file mode 100644 index 1351eea..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/reader/PdaReader.java +++ /dev/null @@ -1,157 +0,0 @@ -package me.shineup.solana.internal.utils.reader; - - -import me.shineup.solana.exceptions.SolanaException_RpcConnection; -import me.shineup.solana.exceptions.SolanaException; -import org.p2p.solanaj.core.PublicKey; -import org.p2p.solanaj.rpc.RpcClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import me.shineup.solana.config.Const; - -import java.nio.charset.StandardCharsets; -import java.util.*; - -/** - * Чтение произвольного PDA-аккаунта в актуальных кластерах Solana (v1.18+). - * - * ─────────────────────────────────────────────────────────────────────────────── - * ▸ Solana HTTP RPC: getAccountInfo - * - * { - * "jsonrpc":"2.0", - * "id":1, - * "result":{ - * "context":{"slot":123456}, - * "value":{ - * "data":[ // 0 - base64-строка, 1 - "base64" - * "BASE64_STRING", - * "base64" - * ], - * "executable":false, - * "lamports":2039280, - * "owner":"BPFLoaderUpgradeab1e11111111111111111111111", - * "rentEpoch":"18446744073709551615" // - строка-u64 (v1.18+) - * } - * } - * } - * - * ▸ Мы читаем только value.data[0] (Base64) – остальное не трогаем, - * поэтому не важен тип rentEpoch (строка или число). - * ─────────────────────────────────────────────────────────────────────────────── - */ -public final class PdaReader { - - /* --------------------------------------------------------------------- */ - /* CONFIG & LOG */ - /* --------------------------------------------------------------------- */ - private static final Logger LOG = LoggerFactory.getLogger(PdaReader.class); - private static final RpcClient RPC = new RpcClient(Const.RPC_URL); // один клиент на класс - - private PdaReader() {} // запретить new - - /* --------------------------------------------------------------------- */ - /* PUBLIC API */ - /* --------------------------------------------------------------------- */ - - /** Чтение PDA, вычисленного из одного текстового сида. */ - public static byte[] readOneSeed(String seed, String programId) throws Exception { - PublicKey pda = derivePda(Collections.singletonList(seed.getBytes(StandardCharsets.UTF_8)), - programId); - return fetchAccountData(pda); - } - - /** Чтение PDA, вычисленного из двух произвольных сид-массивов. */ - public static byte[] readTwoSeeds(byte[] seed1, byte[] seed2, String programId) throws Exception { - PublicKey pda = derivePda(Arrays.asList(seed1, seed2), programId); - return fetchAccountData(pda); - } - - /* --------------------------------------------------------------------- */ - /* INTERNALS */ - /* --------------------------------------------------------------------- */ - - /** Высчитываем адрес PDA для списка сидов. */ - private static PublicKey derivePda(List seeds, String programId) throws Exception { - PublicKey program = new PublicKey(programId); - PublicKey pda = PublicKey.findProgramAddress(seeds, program).getAddress(); - LOG.info("📡 PDA адрес: {}", pda.toBase58()); - return pda; - } - - /** - * Достаём бинарные данные аккаунта.
- * Возвращает null, если аккаунт отсутствует или пуст. - */ - - - @SuppressWarnings("unchecked") - private static byte[] fetchAccountData(PublicKey pda) throws Exception { - - // 1) getAccountInfo c base64-энкодингом - Map cfg = new HashMap<>(); - cfg.put("encoding", "base64"); - cfg.put("commitment", "confirmed"); - -// Map resp = RPC.call( -// "getAccountInfo", -// Arrays.asList(pda.toBase58(), cfg), -// Map.class // сырое дерево -// ); - - - - Map resp; - try { - resp = RPC.call( - "getAccountInfo", - Arrays.asList(pda.toBase58(), cfg), - Map.class - ); - } catch (Exception e) { // solanaj бросает RuntimeException/IOException - throw new SolanaException_RpcConnection("Не удалось выполнить getAccountInfo"); - } - - // Если RPC вернул стандартное поле error — разбираем его централизованно - if (resp.get("error") != null) { - throw new SolanaException("RPC вернул поле error"); //тут можно добавить вывод что за конкретная ошибка случилась - } - - // 2) Достаём value → data[0] -// Map result = (Map) resp.get("result"); -// if (result == null) return null; - - - Map value = (Map) resp.get("value");//result.get("value"); - if (value == null) return null; - - List dataArr = (List) value.get("data"); - if (dataArr == null || dataArr.isEmpty()) return null; - - String b64 = (String) dataArr.get(0); // ← вот он payload - if (b64 == null || b64.isEmpty()) return null; - - return Base64.getDecoder().decode(b64); - } - - @SuppressWarnings("unchecked") - private static void debugDumpJson(Object node, int indent) { - String pad = String.join("", Collections.nCopies(indent, " ")); // два пробела × indent - if (node instanceof Map) { - Map map = (Map) node; - for (Map.Entry e : map.entrySet()) { - System.out.println(pad + e.getKey() + ":"); - debugDumpJson(e.getValue(), indent + 1); - } - } else if (node instanceof Iterable) { - Iterable it = (Iterable) node; - for (Object val : it) { - debugDumpJson(val, indent + 1); - } - } else { - System.out.println(pad + String.valueOf(node)); - } - } - - -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/resultChecker/ResultChecker.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/resultChecker/ResultChecker.java deleted file mode 100644 index 5ff55ab..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/resultChecker/ResultChecker.java +++ /dev/null @@ -1,29 +0,0 @@ -package me.shineup.solana.internal.utils.resultChecker; - - -/** - * Утилита для проверки результата выполнения транзакции по подписи. - * - * Данный класс: - * - выводит подпись транзакции в консоль - * - ждёт подтверждения транзакции через Solana RPC - * - выводит статус транзакции после завершения - */ -public class ResultChecker { - - /** - * Проверяет статус транзакции по её подписи. - * - * @param sig Подпись (signature) транзакции в base58-формате. - */ - public static void check(String sig) { - System.out.println("📦 Signature: " + sig); - - if (sig != null) { - TransactionStatusChecker.waitForConfirmation(sig); - SolanaTransactionStatusChecker.getTransactionStatus(sig); - } else { - System.out.println("⚠️ Подпись транзакции пуста или null"); - } - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/resultChecker/SolanaTransactionStatusChecker.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/resultChecker/SolanaTransactionStatusChecker.java deleted file mode 100644 index a0d7cb5..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/resultChecker/SolanaTransactionStatusChecker.java +++ /dev/null @@ -1,210 +0,0 @@ -package me.shineup.solana.internal.utils.resultChecker; - -import me.shineup.solana.config.Const; -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import me.shineup.solana.internal.utils.SolanaRpcClient; - -public class SolanaTransactionStatusChecker { - - private static final Logger log = LoggerFactory.getLogger(SolanaTransactionStatusChecker.class); - private static final Gson gson = new Gson(); - - - public static boolean getTransactionStatus(String signature) { - boolean isOk = false; - String requestJson = String.format( - "{\n" + - " \"jsonrpc\": \"2.0\",\n" + - " \"id\": 1,\n" + - " \"method\": \"getTransaction\",\n" + - " \"params\": [\"%s\", { \"encoding\": \"jsonParsed\", \"commitment\": \"finalized\" }]\n" + - "}", signature); - -// String requestJson = """ -// { -// "jsonrpc": "2.0", -// "id": 1, -// "method": "getTransaction", -// "params": ["%s", { "encoding": "jsonParsed", "commitment": "finalized" }] -// } -// """.formatted(signature); - - - try { - String responseJson = SolanaRpcClient.getInstance().sendRequest(requestJson); - JsonObject root = gson.fromJson(responseJson, JsonObject.class); - - if (root.has("error")) { - log.error("❌ Ошибка при запросе транзакции: {}", root.get("error")); - return false; - } - - JsonElement resultElement = root.get("result"); - - if (resultElement == null || resultElement.isJsonNull()) { - log.warn("⚠️ Транзакция с сигнатурой {} не найдена или ещё не финализирована", signature); - return false; - } - - JsonObject result = root.getAsJsonObject("result"); - - if (result == null) { - log.warn("⚠️ Транзакция с сигнатурой {} не найдена", signature); - return false; - } - - JsonObject meta = result.getAsJsonObject("meta"); - if (meta == null) { - log.warn("⚠️ Нет информации о результате выполнения"); - return false; - } - - JsonElement err = meta.get("err"); - if (err == null || err.isJsonNull()) { - log.info("✅ Транзакция завершилась успешно"); - isOk = true; - } else { - log.warn("⚠️ Транзакция завершилась с ошибкой. Код ошибки: {}", extractCustomErrorCode(err)); - isOk = false; - } - - - // 💸 Вывод комиссии - if (meta.has("fee")) { - long fee = meta.get("fee").getAsLong(); - log.info("💸 Комиссия за транзакцию: {} лампортов", fee); - } - - // Получаем message → accountKeys - JsonObject message = result.getAsJsonObject("transaction").getAsJsonObject("message"); - JsonArray accountKeys = message.getAsJsonArray("accountKeys"); - - // 🔄 Выводим изменения балансов - logBalanceChanges(meta, accountKeys); - - // 💸 Общая сумма списанных лампортов (не только комиссия) - long totalSpent = calculateTotalSpent(meta); - log.info("💸 Общая сумма списанных лампортов (включая переводы и аренду аккаунтов): {}", totalSpent); - - - // Выводим лог исполнения (если есть) - if (meta.has("logMessages")) { - JsonElement logsElement = meta.get("logMessages"); - if (logsElement != null && logsElement.isJsonArray()) { - JsonArray logs = logsElement.getAsJsonArray(); - log.info("📝 Логи исполнения:"); - for (JsonElement logLine : logs) { - log.info(" → " + logLine.getAsString()); - } - } else if (logsElement != null && logsElement.isJsonNull()) { - log.info("📝 Логи исполнения: отсутствуют (null)"); - } else { - log.warn("📝 Логи исполнения: неожиданный формат данных"); - } - } - - } catch (Exception e) { - log.error("❌ Ошибка при запросе статуса транзакции", e); - return false; - } - return isOk; - } - - - - - /** - * Извлекает значение Custom Anchor ошибки из поля "err", если оно есть. - * - * Пример ожидаемого JSON: - * { - * "InstructionError": [0, { "Custom": 10000 }] - * } - * - * @param errJson поле "err" из ответа Solana - * @return числовой код ошибки (например, 10000) или null, если не найден - */ - public static Integer extractCustomErrorCode(JsonElement errJson) { - if (errJson == null || errJson.isJsonNull()) return null; - - try { - JsonObject errObj = errJson.getAsJsonObject(); - if (errObj.has("InstructionError")) { - JsonArray instrError = errObj.getAsJsonArray("InstructionError"); - - if (instrError.size() == 2 && instrError.get(1).isJsonObject()) { - JsonObject customObj = instrError.get(1).getAsJsonObject(); - if (customObj.has("Custom")) { - return customObj.get("Custom").getAsInt(); - } - } - } - } catch (Exception e) { - // безопасно возвращаем null - } - - return null; - } - - - /** - * Логирует изменения балансов всех аккаунтов, участвующих в транзакции. - * - * @param meta JSON-объект "meta" из транзакции (включает preBalances и postBalances) - * @param accountKeys Список accountKeys из message (содержит pubkey для каждого аккаунта) - */ - public static void logBalanceChanges(JsonObject meta, JsonArray accountKeys) { - JsonArray preBalances = meta.getAsJsonArray("preBalances"); - JsonArray postBalances = meta.getAsJsonArray("postBalances"); - - log.info("💰 Изменения балансов по аккаунтам:"); - - for (int i = 0; i < preBalances.size(); i++) { - long pre = preBalances.get(i).getAsLong(); - long post = postBalances.get(i).getAsLong(); - long delta = post - pre; - - String pubkey = accountKeys.get(i) - .getAsJsonObject() - .get("pubkey") - .getAsString(); - - double usdValue = delta * 150.0 / 1_000_000_000; - String usdFormatted = String.format("%.5f", usdValue); - - log.info("🔄 {}: {} → {} (Δ: {} лампортов) ≈ {} $", Const.identifyKey(pubkey), pre, post, delta, usdFormatted); - - - } - } - - /** - * Считает общую сумму списанных лампортов по всем аккаунтам, - * т.е. где postBalance < preBalance. - * - * @param meta JSON-объект "meta" из транзакции - * @return общее количество списанных лампортов - */ - public static long calculateTotalSpent(JsonObject meta) { - JsonArray preBalances = meta.getAsJsonArray("preBalances"); - JsonArray postBalances = meta.getAsJsonArray("postBalances"); - - long totalSpent = 0; - - for (int i = 0; i < preBalances.size(); i++) { - long pre = preBalances.get(i).getAsLong(); - long post = postBalances.get(i).getAsLong(); - - if (post < pre) { - totalSpent += (pre - post); - } - } - - return totalSpent; - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/resultChecker/TransactionStatusChecker.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/resultChecker/TransactionStatusChecker.java deleted file mode 100644 index 44bca8f..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/resultChecker/TransactionStatusChecker.java +++ /dev/null @@ -1,68 +0,0 @@ -package me.shineup.solana.internal.utils.resultChecker; - -import org.p2p.solanaj.rpc.RpcClient; -import org.p2p.solanaj.rpc.types.SignatureStatuses; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import me.shineup.solana.config.Const; - -import java.util.Collections; -import java.util.List; - -public class TransactionStatusChecker { - - private static final Logger LOG = LoggerFactory.getLogger(TransactionStatusChecker.class); - - /** - * Проверяет статус транзакции по подписи, делает до 10 попыток с паузой. - * - * @param signature Подпись транзакции (Base58) - * @return true, если транзакция прошла успешно, иначе false - */ - public static boolean waitForConfirmation(String signature) { - RpcClient rpc = new RpcClient(Const.RPC_URL); - - try { - for (int attempt = 1; attempt <= 10; attempt++) { - LOG.info("🔍 Попытка {} проверки транзакции {}", attempt, signature); - - SignatureStatuses statuses = rpc.getApi().getSignatureStatuses(Collections.singletonList(signature), true); - List infoList = statuses.getValue(); - - if (infoList != null && !infoList.isEmpty()) { - SignatureStatuses.Value info = infoList.get(0); - - if (info != null) { - String status = info.getConfirmationStatus(); // Или getConfirmationStatusString() в других версиях - - LOG.info("⏳ Статус: {}", status); - if ("finalized".equals(status)) { - LOG.info("🎉 Финализирована большинством"); - //todo - return true; - } else if ("processed".equals(status)) { - LOG.info("Транзакция принята в пул"); - } else if ("confirmed".equals(status)) { - LOG.info("Транзакция вошла в блок, но не финализирована"); - } else { - LOG.info("Хер его знает "); - } - - } else { - LOG.info("Пока такой транзакции нету"); - } - } else { - LOG.info("Запрос вообще не удался"); - } - - Thread.sleep(3000); // Ждать 3 секунды перед следующей попыткой - } - - LOG.warn("❌ Транзакция не подтвердилась за 10 попыток: {}", signature); - } catch (Exception e) { - LOG.error("❌ Ошибка при проверке транзакции", e); - } - - return false; - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/resultChecker/TransactionStatusHelper.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/resultChecker/TransactionStatusHelper.java deleted file mode 100644 index cf6bb4b..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/internal/utils/resultChecker/TransactionStatusHelper.java +++ /dev/null @@ -1,126 +0,0 @@ -package me.shineup.solana.internal.utils.resultChecker; - -// Демонстрационный класс: единая точка проверки статуса транзакции Solana -// ---------------------------------------------------------------------- -// Содержит: -// 1. Enum TxStatus – перечень возможных состояний -// 2. Статический метод getTxStatus(...) – собственно проверка -// 3. Метод main(...) – пример использования -// ---------------------------------------------------------------------- - -import java.util.Collections; - -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import me.shineup.solana.config.Const; -import me.shineup.solana.internal.utils.SolanaRpcClient; -import me.shineup.solana.model.TxStatus; -import org.p2p.solanaj.rpc.RpcClient; -import org.p2p.solanaj.rpc.types.SignatureStatuses; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class TransactionStatusHelper { - -// // -------------------------------------------------- -// // 1. Enum – возможные состояния транзакции -// // -------------------------------------------------- -// public enum TxStatus { -// // ✅ Повторять запрос, если лимит времени не исчерпан: -// NOT_FOUND, // RPC‑узел ещё не видел подпись */ -// PROCESSED, // Подпись обработана, но не попала в блок -// CONFIRMED, // Транзакция в блоке, но блок не финализирован -// -// //❌ Не повторять, даже если лимит не исчерпан: -// FINALIZED_SUCCESS, // Блок финализирован и meta.err == null -// FINALIZED_ERROR, // Блок финализирован, но meta.err содержит ошибку -// //⚠️ Не повторять или повторять ограниченно (1–3 раза, или ≤ N секунд): -// UNKNOWN, // Не удалось определить статус (RPC‑ошибка, исключение и т.д.) */ -// NETWORK_ERROR // Не удаётся подключиться по сете -// } - - // -------------------------------------------------- - // 2. Метод проверки статуса - // -------------------------------------------------- - private static final Logger log = LoggerFactory.getLogger(TransactionStatusHelper.class); - - /** - * Универсальная проверка статуса транзакции. - * @param signature подпись (transaction signature / id) - * @return текущее состояние {@link TxStatus} - */ - public static TxStatus getTxStatus(String signature) { - RpcClient rpc = new RpcClient(Const.RPC_URL); - - try { - // --- 1. Быстрый запрос STATUSES - // Пробуем получить статус транзакции - SignatureStatuses.Value info; - try { - info = rpc.getApi() - .getSignatureStatuses(Collections.singletonList(signature), true) - .getValue() - .get(0); - } catch (Exception e) { - log.error("🔌 Ошибка подключения к RPC или сети", e); - return TxStatus.NETWORK_ERROR; // Тут можно вернуть специальный статус NETWORK_ERROR, если нужно точнее - } - - - - if (info == null) { - return TxStatus.NOT_FOUND; // подпись ещё не дошла до RPC - } - - String commit = info.getConfirmationStatus(); // processed / confirmed / finalized - - switch (commit) { - case "processed": - return TxStatus.PROCESSED; - case "confirmed": - return TxStatus.CONFIRMED; - case "finalized": - // --- 2. Дошли до финала – нужен getTransaction, чтобы узнать err - String reqJson = String.format( - "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"getTransaction\"," + - "\"params\":[\"%s\",{\"encoding\":\"json\",\"commitment\":\"finalized\"}]}", - signature); - - // SolanaRpcClient – ваша внутренняя обёртка. Замените на свой http‑клиент. - String raw = SolanaRpcClient.getInstance().sendRequest(reqJson); - - JsonObject meta = JsonParser.parseString(raw) - .getAsJsonObject() - .getAsJsonObject("result") - .getAsJsonObject("meta"); - - return meta.get("err").isJsonNull() ? TxStatus.FINALIZED_SUCCESS : TxStatus.FINALIZED_ERROR; - default: - return TxStatus.UNKNOWN; - } - - } catch (Exception e) { - log.error("RPC error while checking {}", signature, e); - return TxStatus.UNKNOWN; - } - } - - // -------------------------------------------------- - // 3. Пример использования - // -------------------------------------------------- - /** - * Точка входа для быстрого ручного теста. - * Запускайте так: - * java TransactionStatusHelper - * Если аргумент не передан, используется демо‑подпись. - */ - public static void main(String[] args) { - // Подпись можно передать первым аргументом - String sig = (args.length > 0) - ? args[0] - : "4bxeRu4pNk9UzN6QgTPy6Q3DLJ6ZQt3xMkUQnDzohBpxjMVqRyba2Riqm8o7MBYo2YfSfvqbMFxRRWwu1XbbeiKf"; - - TxStatus status = getTxStatus(sig); - System.out.println("Статус транзакции " + sig + " → " + status); - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/model/TxStatus.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/model/TxStatus.java deleted file mode 100644 index 5c86b9a..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/model/TxStatus.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.shineup.solana.model; - -/** - * Статус транзакции в Solana. - * Используется для отслеживания состояния по сигнатуре. - */ -// -------------------------------------------------- -// 1. Enum – возможные состояния транзакции -// -------------------------------------------------- -public enum TxStatus { - // ✅ Повторять запрос, если лимит времени не исчерпан: - NOT_FOUND, // RPC‑узел ещё не видел подпись */ - PROCESSED, // Подпись обработана, но не попала в блок - CONFIRMED, // Транзакция в блоке, но блок не финализирован - - //❌ Не повторять, даже если лимит не исчерпан: - FINALIZED_SUCCESS, // Блок финализирован и meta.err == null - FINALIZED_ERROR, // Блок финализирован, но meta.err содержит ошибку - //⚠️ Не повторять или повторять ограниченно (1–3 раза, или ≤ N секунд): - UNKNOWN, // Не удалось определить статус (RPC‑ошибка, исключение и т.д.) */ - NETWORK_ERROR // Не удаётся подключиться по сете -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/model/UserById.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/model/UserById.java deleted file mode 100644 index 8dea416..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/model/UserById.java +++ /dev/null @@ -1,52 +0,0 @@ -package me.shineup.solana.model; - - -import java.util.List; - -/** - * Java-представление Solana-структуры UserById. - * - * Содержит: - * • format – версия сериализации; - * • id – числовой ID пользователя; - * • login – строковый логин; - * • pubkey – публичная подпись пользователя (Base58); - * • deviceCount – сколько устройств хранится; - * • devices – список устройств (DeviceInfo). - */ -public class UserById { - - /* ---------- поля, идущие в сериализации ---------- */ - public int format; - public long id; - public String login; - public String pubkey; - public int deviceCount; - public List devices; - - /** Вложенный класс описания одного устройства. */ - public static class DeviceInfo { - public int deviceType; // 1 байт в on-chain - public String devicePubkey; // 32 байта (Base58) - public String x25519Pubkey; // 32 байта (Base58) - - @Override - public String toString() { - return "DeviceInfo{type=" + deviceType + - ", devPub=" + devicePubkey + - ", x25519=" + x25519Pubkey + '}'; - } - } - - @Override - public String toString() { - return "UserById{" + - "format=" + format + - ", id=" + id + - ", login='" + login + '\'' + - ", pubkey='" + pubkey + '\'' + - ", deviceCount=" + deviceCount + - ", devices=" + devices + - '}'; - } -} diff --git a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/model/UserByLogin.java b/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/model/UserByLogin.java deleted file mode 100644 index a53e420..0000000 --- a/solana-shine-client-lib/solana-shine-lib/src/main/java/me/shineup/solana/model/UserByLogin.java +++ /dev/null @@ -1,23 +0,0 @@ -package me.shineup.solana.model; - -/** - * Класс описывающий объект UserByLogin, аналогичный структуре на стороне Solana. - */ -public class UserByLogin { - public int format; // формат сериализации (например, 1) - public String login; // логин - public long id; // числовой ID - public String pubkey; // публичный ключ (base58) - public int status; // статус (например, 0) - - @Override - public String toString() { - return "UserByLogin{" + - "format=" + format + - ", login='" + login + '\'' + - ", id=" + id + - ", pubkey='" + pubkey + '\'' + - ", status=" + status + - '}'; - } -} \ No newline at end of file diff --git a/solana-shine-client-lib/src/main/Build.txt b/solana-shine-client-lib/src/main/Build.txt deleted file mode 100644 index ef4c9fd..0000000 --- a/solana-shine-client-lib/src/main/Build.txt +++ /dev/null @@ -1,2 +0,0 @@ -./gradlew :solana-shine-lib:build -- команда что бы сбилдить библиотеку \ No newline at end of file diff --git a/solana-shine-client-lib/src/main/airDrop.txt b/solana-shine-client-lib/src/main/airDrop.txt deleted file mode 100644 index a27d170..0000000 --- a/solana-shine-client-lib/src/main/airDrop.txt +++ /dev/null @@ -1 +0,0 @@ -solana airdrop 1 HMww7YSVfwVm4i8sugqj7wyH26dqzHykzv3wzWwzEvPA --url https://api.devnet.solana.com diff --git a/solana-shine-client-lib/src/main/java/com/shine/solana/test/AddUserExemple.java b/solana-shine-client-lib/src/main/java/com/shine/solana/test/AddUserExemple.java deleted file mode 100644 index cec2458..0000000 --- a/solana-shine-client-lib/src/main/java/com/shine/solana/test/AddUserExemple.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.shine.solana.test; - -import me.shineup.solana.SolanaWrapper; -import me.shineup.solana.internal.standartActions.keysGenerator.KeyPairBase58; -import me.shineup.solana.config.Const; -import me.shineup.solana.SolanaTxWatcher; -import me.shineup.solana.model.TxStatus; - -public class AddUserExemple { - public static void main(String[] args) { -try { -// SolanaWrapper.setRPC_URL_testNet(); -// SolanaWrapper.requestAirdrop(Const.getKeyByName("key3").getPublicKey(), 1); - SolanaWrapper.getBalance(Const.getKeyByName("key3").getPublicKey()); - -// long id =1; -// try { -// UserById u = SolanaWrapper.getUserById(id); -// System.out.println(u); -// UserByLogin u2 =SolanaWrapper.getUserByLogin(u.login); -// System.out.println(u2); -// } catch (Exception e) { -// e.printStackTrace(); -// } - - KeyPairBase58 k = SolanaWrapper.generateNewWallet(); - - String sig; -// sig = SolanaWrapper.sendSol(Const.getKeyByName("key1").getPrivateKey(), k.publicKey, 100000000); - SolanaWrapper.getBalance(Const.getKeyByName("key1").getPublicKey()); -// SolanaWrapper.getBalance(k.publicKey); -// SolanaWrapper.getBalance(k.publicKey); -// SolanaWrapper.getBalance(k.publicKey); - - -// String sig = SolanaWrapper.registerUserWithOneDev(k.publicKey,k.privateKey,"ivan11263456",k.publicKey, k.publicKey); - - sig = SolanaWrapper.registerUserWithOneDev(Const.getKeyByName("key1").getPublicKey(), Const.getKeyByName("key1").getPrivateKey(), - "ivan1261", Const.getKeyByName("key1").getPublicKey(), Const.getKeyByName("key1").getPublicKey()); - - - SolanaTxWatcher watcher = new SolanaTxWatcher(sig); - while (watcher.shouldRetry()) { // ◀ проверяем, нужно ли ещё опрашивать - watcher.updateStatus(); // ◀ запрашиваем статус через RPC - System.out.println( - watcher.getStatus() + " | " + // ◀ печатаем статус - watcher.getStatusComment()); - try { - Thread.sleep(SolanaTxWatcher.getRetryIntervalMs()); // ◀ ждём секунду - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - if (watcher.isSuccess()) { - System.out.println("✅ Транзакция прошла успешно!"); - } else { - System.out.println("❌ Завершили слежение без успеха."); - } - - -// TxStatus st = SolanaWrapper.getTransactionStatus(sig); -// System.out.println("Статус транзакции " + sig + " → " + st); -// TxStatus result = waitTransactionFinalized(sig); -// -// System.out.println("Итоговый статус: " + result); -// if (result == TxStatus.FINALIZED_SUCCESS) { -// System.out.println("✅ Транзакция прошла успешно!"); -// } else if (result == TxStatus.FINALIZED_ERROR) { -// System.out.println("❌ Транзакция завершилась с ошибкой."); -// } - -} catch (Exception e) { - throw new RuntimeException(e); -} - } - - - /** - * Ожидает финализации транзакции, проверяя статус каждую секунду. - * Максимальное время ожидания — 20 секунд. - */ - public static TxStatus waitTransactionFinalized(String signature) { - final int maxSeconds = 20; - TxStatus st; - for (int i = 0; i < maxSeconds; i++) { - try { - st = SolanaWrapper.getTransactionStatus(signature); - } catch (Exception e) { - throw new RuntimeException(e); //и тут дописывать проверки если надо - } - System.out.println("Статус транзакции " + signature + " → " + st); - - // Если достигли финального состояния — возвращаем - if (st == TxStatus.FINALIZED_SUCCESS || st == TxStatus.FINALIZED_ERROR) { - return st; - } - - // Если транзакция не найдена или в неизвестном состоянии — тоже выходим - if (st == TxStatus.UNKNOWN) { - System.out.println("Ошибка или подпись не найдена. Прерываем ожидание."); - return st; - } - - // Подождать 1 секунду - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - System.out.println("Ожидание прервано."); - return TxStatus.UNKNOWN; - } - } - - System.out.println("⏱ Превышен лимит ожидания (20 сек)."); - return TxStatus.UNKNOWN; - } - -} diff --git a/solana-shine-client-lib/src/main/java/com/shine/solana/test/AirDrop.java b/solana-shine-client-lib/src/main/java/com/shine/solana/test/AirDrop.java deleted file mode 100644 index a85cfe8..0000000 --- a/solana-shine-client-lib/src/main/java/com/shine/solana/test/AirDrop.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.shine.solana.test; - -import me.shineup.solana.SolanaWrapper; -import me.shineup.solana.config.Const; - -public class AirDrop { - public static void main(String[] args) { - // Если первый аргумент равен строке "1" → запрашиваем airdrop, - // иначе просто выводим баланс. - try { -// SolanaWrapper.setRPC_URL("https://api.testnet.solana.com"); - // SolanaWrapper.setRPC_URL("https://api.devnet.solana.com"); - boolean needAirdrop = args.length > 0 && "1".equals(args[0]); - // needAirdrop = true; - if (needAirdrop) { - long oneSolLamports = 1_000_000_000L; // 1 SOL в лампортах - SolanaWrapper.requestAirdrop( - Const.getKeyByName("key1").getPublicKey(), - oneSolLamports); - } else { - SolanaWrapper.getBalance(Const.getKeyByName("key1").getPublicKey()); - SolanaWrapper.getBalance(Const.getKeyByName("key2").getPublicKey()); - SolanaWrapper.getBalance(Const.getKeyByName("key3").getPublicKey()); - } - - } catch (Exception e) { - throw new RuntimeException(e); - } - - } - -} diff --git a/solana-shine-client-lib/src/main/java/com/shine/solana/test/Main.java b/solana-shine-client-lib/src/main/java/com/shine/solana/test/Main.java deleted file mode 100644 index 22c4ecf..0000000 --- a/solana-shine-client-lib/src/main/java/com/shine/solana/test/Main.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.shine.solana.test; - - -import me.shineup.solana.SolanaWrapper; -import me.shineup.solana.config.Const; -import me.shineup.solana.internal.callSolanaFunc.InitializeUserCounter.InitializeUserCounter; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -public class Main { - private static final ExecutorService executorService = Executors.newCachedThreadPool(); - - public static void main(String[] args) { -// SolanaWrapper.setRPC_URL("https://api.devnet.solana.com"); -// InitializeUserCounter - - long oneSolLamports = 5_000_000_000L; // 1 SOL в лампортах - - try { - SolanaWrapper.requestAirdrop( - "FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P", - oneSolLamports); - System.out.println(SolanaWrapper.getBalance("FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P"));//H6q58ytZk5sd3KQisC57R6urKUjn5PaWKLCv7sNZtj4i")); - System.out.println("Баланс Shine" + SolanaWrapper.getBalance( "jZnbqzrbKaksVomiqAxsKGiz5ct8rHPgcNDiiKyTZDD")); - } catch (Exception e) { - System.out.println(e.getMessage()); - } - - // 5RpEoxRKSr2norQP3vEnq9XokQGh9EbGN8q8xUUVAdm1M5mTD1vMuyJPYJfViMWFf6c8qT5mj2bt64gLE2zm6VG3 - тестовые в фантом валет - // FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P - - // -// executorService.submit(() -> { -// try { -// System.out.println(SolanaWrapper.getUserById(1).login); -// } catch (Exception e) { -// System.out.println(e.getMessage()); -// } -// }); - - - - - } - -} diff --git a/solana-shine-client-lib/src/main/java/com/shine/utils/CreateToken_story.txt b/solana-shine-client-lib/src/main/java/com/shine/utils/CreateToken_story.txt deleted file mode 100644 index b2c0623..0000000 --- a/solana-shine-client-lib/src/main/java/com/shine/utils/CreateToken_story.txt +++ /dev/null @@ -1,58 +0,0 @@ -ai@ai-home:~$ ~/.cargo/bin/spl-token --version -spl-token-cli 5.3.0 - -//создание -ai@ai-home:~$ ~/.cargo/bin/spl-token create-token -Creating token Dt7t2kKXYT8UqdioTQZeH4SRXqDA2kh2dscqZQmfr1Rr under program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA - -Address: Dt7t2kKXYT8UqdioTQZeH4SRXqDA2kh2dscqZQmfr1Rr -Decimals: 9 - -Signature: 656MfR7x4K5fZJEsf1nJTk5gorMkuZtSCw2YGzvtAEHLyftDsYhoXspGspEhD1Jto8pNNfkyNVb2jRVZPBg5gi9u - ------------------------------------------------------------------- -//Эмиссия -ai@ai-home:~$ ~/.cargo/bin/spl-token mint Dt7t2kKXYT8UqdioTQZeH4SRXqDA2kh2dscqZQmfr1Rr 10 -Minting 10 tokens - Token: Dt7t2kKXYT8UqdioTQZeH4SRXqDA2kh2dscqZQmfr1Rr - Recipient: AcyCkqNEdR78s6wsuhwEeqfSe4BHGuK1EkzGcUbArifq - -Signature: 5SWjE1cHrcWEnArZuUsx4efSVXKWsADxPsuEZL2KbeWfUGy4DqQe4Mdg3AhKtXMLLmRMeQYSSX1mB7VgzLcbEGao - -ai@ai-home:~$ ~/.cargo/bin/spl-token balance Dt7t2kKXYT8UqdioTQZeH4SRXqDA2kh2dscqZQmfr1Rr -10 ------------------------------------------------------------------------------- -✅ 2. Перевод токенов на существующий аккаунт -Шаг 1. Создаём токен-аккаунт получателя (один раз) -Вариант A: вручную - -~/.cargo/bin/spl-token create-account <адрес_токена> --owner <публичный_адрес_получателя> - -Вариант B: автоматически при переводе - -~/.cargo/bin/spl-token transfer <адрес_токена> 1 <публичный_адрес_получателя> --fund-recipient - -Флаг --fund-recipient автоматически создаст токен-аккаунт получателя и оплатит аренду за счёт твоего кошелька. - ------ - -ai@ai-home:~$ ~/.cargo/bin/spl-token transfer Dt7t2kKXYT8UqdioTQZeH4SRXqDA2kh2dscqZQmfr1Rr 1 HMww7YSVfwVm4i8sugqj7wyH26dqzHykzv3wzWwzEvPA --fund-recipient -Transfer 1 tokens - Sender: AcyCkqNEdR78s6wsuhwEeqfSe4BHGuK1EkzGcUbArifq - Recipient: HMww7YSVfwVm4i8sugqj7wyH26dqzHykzv3wzWwzEvPA - Recipient associated token account: GJQPLqNXZNXATm4J1sGgtiKKCk9wFced2NXhwGxc5hfA - Funding recipient: GJQPLqNXZNXATm4J1sGgtiKKCk9wFced2NXhwGxc5hfA - -Signature: 5R3bGiqC49GBHJFMTGMRvBxNt12nkvmKrwRxUQVcvhyRn23rmr7YAaWxzvedkneuSr3YUmAQXk7UFyorhdr5WvhY - -ai@ai-home:~$ ~/.cargo/bin/spl-token accounts -Token Balance ------------------------------------------------------ -Dt7t2kKXYT8UqdioTQZeH4SRXqDA2kh2dscqZQmfr1Rr 9 - -ai@ai-home:~$ ~/.cargo/bin/spl-token accounts --owner ^C -ai@ai-home:~$ ~/.cargo/bin/spl-token accounts --owner HMww7YSVfwVm4i8sugqj7wyH26dqzHykzv3wzWwzEvPA -Token Balance ------------------------------------------------------ -Dt7t2kKXYT8UqdioTQZeH4SRXqDA2kh2dscqZQmfr1Rr 1 - diff --git a/solana-shine-client-lib/src/main/java/com/shine/utils/SolanaKeyBase58Converter.java b/solana-shine-client-lib/src/main/java/com/shine/utils/SolanaKeyBase58Converter.java deleted file mode 100644 index 8450c94..0000000 --- a/solana-shine-client-lib/src/main/java/com/shine/utils/SolanaKeyBase58Converter.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.shine.utils; - -import java.util.ArrayList; -import java.util.List; - -/** - * Класс для конвертации приватного ключа Solana из Base58 в формат JSON-массива чисел, - * который понимает Solana CLI. - * - * Использование: - * 1. Вставьте свой приватный ключ в Base58 в main(). - * 2. Запустите программу — она выведет строку JSON, которую можно сохранить в файл. - * 3. С помощью этого файла можно подключить кошелек в Solana CLI. - */ -public class SolanaKeyBase58Converter { - - // ─────────────────────────────────────────────── - // 🔹 ТЕСТОВЫЙ ЗАПУСК - // ─────────────────────────────────────────────── - public static void main(String[] args) { - // Вставьте сюда свой приватный ключ в Base58 - String base58Key = "5RpEoxRKSr2norQP3vEnq9XokQGh9EbGN8q8xUUVAdm1M5mTD1vMuyJPYJfViMWFf6c8qT5mj2bt64gLE2zm6VG3"; //"ВСТАВЬ_СЮДА_СВОЙ_ПРИВАТНЫЙ_КЛЮЧ"; - - // Конвертация в JSON-массив чисел - String jsonKey = base58ToJson(base58Key); - - // Вывод в консоль - System.out.println("Скопируйте эту строку и сохраните в файл (например, mywallet.json):"); - System.out.println(jsonKey); - } - - // ─────────────────────────────────────────────── - // 🔹 ОСНОВНОЙ МЕТОД КОНВЕРТАЦИИ - // ─────────────────────────────────────────────── - - /** - * Преобразует приватный ключ из Base58 в строку JSON-массива чисел, - * которую можно сохранить в файл для Solana CLI. - * - * @param base58Key приватный ключ в формате Base58 (строка) - * @return строка вида "[25,156,13,48,...,147]" - */ - public static String base58ToJson(String base58Key) { - byte[] bytes = decodeBase58(base58Key); - - // Формируем JSON-массив из байт - StringBuilder json = new StringBuilder("["); - for (int i = 0; i < bytes.length; i++) { - json.append(bytes[i] & 0xFF); // "& 0xFF" нужно, чтобы убрать знак и получить 0–255 - if (i < bytes.length - 1) { - json.append(","); - } - } - json.append("]"); - return json.toString(); - } - - // ─────────────────────────────────────────────── - // 🔹 ДЕКОДЕР BASE58 → БАЙТЫ - // ─────────────────────────────────────────────── - - // Алфавит Base58 (стандарт Solana/Bitcoin) - private static final String BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; - - /** - * Декодирует строку Base58 в байтовый массив. - * - * @param input приватный ключ в формате Base58 - * @return байтовый массив приватного ключа - */ - private static byte[] decodeBase58(String input) { - java.math.BigInteger num = java.math.BigInteger.ZERO; - java.math.BigInteger base = java.math.BigInteger.valueOf(58); - - // Переводим Base58 в большое число - for (char c : input.toCharArray()) { - int digit = BASE58_ALPHABET.indexOf(c); - if (digit < 0) { - throw new IllegalArgumentException("Недопустимый символ в Base58: " + c); - } - num = num.multiply(base).add(java.math.BigInteger.valueOf(digit)); - } - - // Конвертируем большое число в байты - List byteList = new ArrayList<>(); - byte[] numBytes = num.toByteArray(); - - // Убираем возможный ведущий 0x00 (для BigInteger) - int startIndex = (numBytes.length > 1 && numBytes[0] == 0) ? 1 : 0; - for (int i = startIndex; i < numBytes.length; i++) { - byteList.add(numBytes[i]); - } - - // Добавляем ведущие нули для каждого символа '1' в Base58 - for (char c : input.toCharArray()) { - if (c == '1') { - byteList.add(0, (byte) 0); - } else { - break; - } - } - - // Преобразуем в обычный массив - byte[] result = new byte[byteList.size()]; - for (int i = 0; i < byteList.size(); i++) { - result[i] = byteList.get(i); - } - return result; - } -} diff --git a/solana-shine-client-lib/src/main/java/com/shine/utils/Wallets.txt b/solana-shine-client-lib/src/main/java/com/shine/utils/Wallets.txt deleted file mode 100644 index 80105e2..0000000 --- a/solana-shine-client-lib/src/main/java/com/shine/utils/Wallets.txt +++ /dev/null @@ -1,37 +0,0 @@ -solana/phantomWallet.json - // 5RpEoxRKSr2norQP3vEnq9XokQGh9EbGN8q8xUUVAdm1M5mTD1vMuyJPYJfViMWFf6c8qT5mj2bt64gLE2zm6VG3 - тестовые в фантом валет - // FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P - - -Баланс - - - - -✅ 2. Инструкция: как сохранить и подключить ключ в Ubuntu -1. Сохраняем JSON в файл - -После запуска программы скопируй результат, например: - -[25,156,13,48,203,...,147] - -Сохрани в файл mywallet.json: - -echo '[25,156,13,48,203,...,147]' > mywallet.json - -2. Временно подключаем кошелёк в Solana CLI - -Подключаем как текущий кошелёк: - -solana config set --keypair mywallet.json - -Проверяем: - -solana address - -Должен показать публичный ключ, соответствующий этому приватному. -3. Возвращаем обратно свой основной ключ - -После работы верни основной ключ: - -solana config set --keypair ~/.config/solana/id.json