From 689f35fea28752eb7693446639b3c7be9a6e023d9243a638133ed4de3ac6822e Mon Sep 17 00:00:00 2001 From: AidarKC Date: Sun, 7 Jun 2026 11:19:58 +0400 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=B0=D1=82=D1=8C=20=D0=BA=D0=BB=D0=B8=D0=B5=D0=BD=D1=82=D1=81?= =?UTF-8?q?=D0=BA=D0=B8=D0=B9=20UI=20Solana-=D1=80=D0=B5=D0=B3=D0=B8=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- VERSION.properties | 4 +- shine-UI/js/pages/devnet-topup-view.js | 54 +++++++++++++++---- shine-UI/js/pages/login-password-view.js | 2 +- .../js/pages/registration-payment-view.js | 8 ++- .../js/services/solana-register-service.js | 12 ++++- 5 files changed, 64 insertions(+), 16 deletions(-) diff --git a/VERSION.properties b/VERSION.properties index ebdb0ec..94a38d8 100644 --- a/VERSION.properties +++ b/VERSION.properties @@ -1,2 +1,2 @@ -client.version=1.2.133 -server.version=1.2.125 +client.version=1.2.134 +server.version=1.2.126 diff --git a/shine-UI/js/pages/devnet-topup-view.js b/shine-UI/js/pages/devnet-topup-view.js index 47a855a..4f4bc72 100644 --- a/shine-UI/js/pages/devnet-topup-view.js +++ b/shine-UI/js/pages/devnet-topup-view.js @@ -5,7 +5,7 @@ import { state } from '../state.js'; export const pageMeta = { id: 'devnet-topup-view', title: 'Пополнение DEVNET', showAppChrome: false }; const SENDER_PRIVATE_32_BASE58 = '6xqAuKYvA8qrCdAkcw7Y8aMgvBnYk8JLxWLma5BzbAvu'; -const TRANSFER_AMOUNT_SOL = 0.01; +const TRANSFER_AMOUNT_SOL = 0.02; function readWalletFromUrl() { try { @@ -40,6 +40,7 @@ export function render({ navigate }) { Кошелёк получателя

${targetWallet || 'Не передан параметр wallet'}

Сумма перевода: ${TRANSFER_AMOUNT_SOL} SOL

+

Баланс: ...

`; const status = document.createElement('p'); @@ -50,22 +51,32 @@ export function render({ navigate }) { status.style.whiteSpace = 'pre-wrap'; status.textContent = 'Готово к пополнению.'; + const successHint = document.createElement('p'); + successHint.style.width = 'min(100%, 320px)'; + successHint.style.display = 'none'; + successHint.style.margin = '0'; + successHint.style.padding = '14px 16px'; + successHint.style.borderRadius = '16px'; + successHint.style.background = 'rgba(15, 159, 99, 0.14)'; + successHint.style.border = '2px solid rgba(15, 159, 99, 0.32)'; + successHint.style.fontSize = '24px'; + successHint.style.fontWeight = '800'; + successHint.style.lineHeight = '1.25'; + successHint.style.color = '#0f9f63'; + successHint.style.textAlign = 'center'; + successHint.style.whiteSpace = 'pre-wrap'; + successHint.textContent = ''; + const fillBtn = document.createElement('button'); fillBtn.className = 'primary-btn'; fillBtn.type = 'button'; fillBtn.textContent = `Пополнить на ${TRANSFER_AMOUNT_SOL} SOL`; - const backBtn = document.createElement('button'); - backBtn.className = 'secondary-btn'; - backBtn.type = 'button'; - backBtn.textContent = 'Назад'; - backBtn.addEventListener('click', () => navigate('registration-payment-view')); - const actions = document.createElement('div'); actions.className = 'auth-footer-actions'; actions.style.width = 'min(100%, 320px)'; actions.style.justifySelf = 'center'; - actions.append(fillBtn, backBtn); + actions.append(fillBtn); let senderAddress = ''; let senderKeypair = null; @@ -78,6 +89,15 @@ export function render({ navigate }) { if (senderBalanceEl) senderBalanceEl.textContent = `Баланс: ${formatSol(balance.sol, 6)} SOL`; }; + const updateTargetBalance = async () => { + if (!targetWallet) return null; + const endpoint = state.entrySettings.solanaServer; + const balance = await getBalanceSol({ endpoint, address: targetWallet }); + const targetBalanceEl = targetBox.querySelector('#devnet-topup-target-balance'); + if (targetBalanceEl) targetBalanceEl.textContent = `Баланс: ${formatSol(balance.sol, 6)} SOL`; + return balance.sol; + }; + fillBtn.addEventListener('click', async () => { if (!targetWallet) { status.textContent = 'Ошибка: в URL не передан параметр wallet.'; @@ -90,6 +110,7 @@ export function render({ navigate }) { fillBtn.disabled = true; status.textContent = 'Отправляем перевод...'; + successHint.style.display = 'none'; try { const endpoint = state.entrySettings.solanaServer; @@ -100,9 +121,21 @@ export function render({ navigate }) { amountSol: TRANSFER_AMOUNT_SOL, }); await updateSenderBalance(); - status.textContent = `Готово.\nSignature: ${tx.signature}`; + const targetBalanceSol = await updateTargetBalance(); + status.textContent = `Транзакция прошла.\nSignature: ${tx.signature}`; + status.style.fontSize = '18px'; + status.style.fontWeight = '700'; + status.style.color = '#0f9f63'; + fillBtn.style.display = 'none'; + actions.style.display = 'none'; + successHint.style.display = ''; + successHint.textContent = `Кошелёк пополнен на ${TRANSFER_AMOUNT_SOL} SOL.\nНовый баланс: ${formatSol(targetBalanceSol || 0, 6)} SOL.\nМожете закрыть эту страницу и продолжить регистрацию.`; } catch (error) { status.textContent = `Ошибка перевода: ${error?.message || 'unknown'}`; + status.style.fontSize = ''; + status.style.fontWeight = ''; + status.style.color = ''; + successHint.style.display = 'none'; } finally { fillBtn.disabled = false; } @@ -116,6 +149,7 @@ export function render({ navigate }) { const senderAddressEl = senderBox.querySelector('#devnet-topup-sender-address'); if (senderAddressEl) senderAddressEl.textContent = `Адрес: ${senderAddress}`; await updateSenderBalance(); + await updateTargetBalance(); if (!targetWallet) { fillBtn.disabled = true; status.textContent = 'Передайте адрес получателя в параметре wallet.'; @@ -129,11 +163,11 @@ export function render({ navigate }) { screen.append( renderHeader({ title: 'DEVNET пополнение', - leftAction: { label: '←', onClick: () => navigate('registration-payment-view') }, }), senderBox, targetBox, status, + successHint, actions, ); diff --git a/shine-UI/js/pages/login-password-view.js b/shine-UI/js/pages/login-password-view.js index e96a85d..2c1541f 100644 --- a/shine-UI/js/pages/login-password-view.js +++ b/shine-UI/js/pages/login-password-view.js @@ -46,7 +46,7 @@ export function render({ navigate }) { advanced.className = 'card stack'; advanced.innerHTML = ` Расширенные -

Схема derivation ключей: логин нормализуется как `trim().toLowerCase()`. При непустом пароле используется Argon2id, затем из результата строится секрет для root/bch/dev ключей.

+

Схема derivation ключей: логин нормализуется как trim().toLowerCase(). При непустом пароле используется Argon2id, затем из результата строится секрет для root/bch/dev ключей.

Если пароль пустой — используется прежний детерминированный режим совместимости.

Для тестов можно оставить пустой пароль.

Профиль Argon2id сейчас фиксированный: t=2, m=65536 KiB (64 MiB), p=1, dkLen=32. Выбор уровня будет добавлен позже.

diff --git a/shine-UI/js/pages/registration-payment-view.js b/shine-UI/js/pages/registration-payment-view.js index 00c8b78..25c3ee6 100644 --- a/shine-UI/js/pages/registration-payment-view.js +++ b/shine-UI/js/pages/registration-payment-view.js @@ -11,7 +11,11 @@ import { getBalanceSol, getTopupSiteUrl, } from '../services/solana-wallet-service.js'; -import { formatSolanaErrorDetails, registerUserOnSolana } from '../services/solana-register-service.js'; +import { + formatSolanaErrorDetails, + isUserAlreadyExistsSolanaError, + registerUserOnSolana, +} from '../services/solana-register-service.js'; export const pageMeta = { id: 'registration-payment-view', title: 'Оплата регистрации', showAppChrome: false }; const MIN_REQUIRED_SOL = 0.01; @@ -194,7 +198,7 @@ export function render({ navigate }) { } catch (solanaError) { const solanaMsg = formatSolanaErrorDetails(solanaError); // Пользователь уже зарегистрирован в Solana — продолжаем - if (!solanaMsg.includes('already') && !solanaMsg.includes('UserAlreadyExists')) { + if (!solanaMsg.includes('already') && !isUserAlreadyExistsSolanaError(solanaError)) { throw new Error(`Ошибка регистрации в Solana: ${solanaMsg}`); } } diff --git a/shine-UI/js/services/solana-register-service.js b/shine-UI/js/services/solana-register-service.js index 23d1416..79dbeb0 100644 --- a/shine-UI/js/services/solana-register-service.js +++ b/shine-UI/js/services/solana-register-service.js @@ -6,6 +6,7 @@ import { const CLASSIFY_LOGIN_INSTRUCTION_TAG = 1; const PRECHECK_SIM_PAYER = 'FUc28vNixp7F3nnkpGVt6nuJbgvJ4429v4B5wS52Df6P'; +const SHINE_USERS_USER_PDA_SEED_PREFIX = 'user_login='; let solanaLibPromise = null; function loadSolanaLib() { @@ -71,6 +72,15 @@ export function formatSolanaErrorDetails(error) { return parts.join(' :: '); } +export function isUserAlreadyExistsSolanaError(error) { + const details = formatSolanaErrorDetails(error); + return details.includes('UserAlreadyExists') + || details.includes('custom program error: 0x4') + || details.includes('"Custom":4') + || details.includes('"InstructionError":[2,{"Custom":4}]') + || details.includes('Instruction 2: custom program error: 0x4'); +} + export async function precheckLoginClassOnSolana({ login, solanaEndpoint }) { const solana = await loadSolanaLib(); const connection = new solana.Connection(String(solanaEndpoint || ''), 'confirmed'); @@ -121,7 +131,7 @@ export async function checkLoginExistsOnSolana({ login, solanaEndpoint }) { throw new Error('EMPTY_LOGIN'); } const [userPda] = solana.PublicKey.findProgramAddressSync( - [enc.encode('login='), enc.encode(loginNorm)], + [enc.encode(SHINE_USERS_USER_PDA_SEED_PREFIX), enc.encode(loginNorm)], usersProgram, ); const ai = await connection.getAccountInfo(userPda, 'confirmed');