Регистрация Solana: промо-topup URL с wallet(base58) и порог 0.01 SOL

This commit is contained in:
AidarKC 2026-04-27 01:44:07 +03:00
parent 2c68dedea2
commit 50da3e868d
5 changed files with 65 additions and 20 deletions

View File

@ -1,2 +1,2 @@
client.version=1.2.17
server.version=1.2.17
client.version=1.2.18
server.version=1.2.18

View File

@ -6,9 +6,15 @@ import {
state,
} from '../state.js';
import { toUserMessage } from '../services/ui-error-texts.js';
import { deriveWalletFromPassword, formatSol, getBalanceSol } from '../services/solana-wallet-service.js';
import {
deriveWalletFromPassword,
formatSol,
getBalanceSol,
getTopupSiteUrl,
} from '../services/solana-wallet-service.js';
export const pageMeta = { id: 'registration-payment-view', title: 'Оплата регистрации', showAppChrome: false };
const MIN_REQUIRED_SOL = 0.01;
function parseBalanceSol(value) {
const parsed = Number.parseFloat(String(value || '').replace(',', '.'));
@ -77,9 +83,9 @@ export function render({ navigate }) {
refreshButton.textContent = '↻';
refreshButton.title = 'Обновить';
const refreshBalance = async () => {
const refreshBalance = async ({ showError = true } = {}) => {
const address = String(walletValue.value || '').trim();
if (!address) return;
if (!address) return null;
refreshButton.disabled = true;
try {
const balance = await getBalanceSol({
@ -88,10 +94,14 @@ export function render({ navigate }) {
});
state.registrationPayment.balanceSOL = String(balance.sol);
balanceValue.textContent = `${formatSol(balance.sol, 6)} SOL`;
return Number(balance.sol) || 0;
} catch (error) {
if (showError) {
status.className = 'status-line is-unavailable';
status.textContent = `Не удалось обновить баланс: ${error?.message || 'unknown'}`;
status.style.display = '';
}
return null;
} finally {
refreshButton.disabled = false;
}
@ -106,8 +116,11 @@ export function render({ navigate }) {
const topupButton = document.createElement('button');
topupButton.className = 'ghost-btn';
topupButton.type = 'button';
topupButton.textContent = 'Пополнить счет';
topupButton.addEventListener('click', () => navigate('topup-view'));
topupButton.textContent = 'Пополнить кошелёк';
topupButton.addEventListener('click', () => {
const walletAddress = String(walletValue.value || '').trim();
window.open(getTopupSiteUrl(walletAddress), '_blank', 'noopener,noreferrer');
});
const submitButton = document.createElement('button');
submitButton.className = 'primary-btn';
@ -128,6 +141,27 @@ export function render({ navigate }) {
submitButton.disabled = true;
submitButton.textContent = 'Регистрация...';
const walletAddress = String(walletValue.value || '').trim();
if (!walletAddress) {
status.className = 'status-line is-unavailable';
status.textContent = 'Кошелёк не подготовлен. Нажмите «Обновить» и попробуйте снова.';
status.style.display = '';
return;
}
const currentBalance = await refreshBalance({ showError: true });
if (currentBalance == null) return;
if (currentBalance < MIN_REQUIRED_SOL) {
status.className = 'status-line is-unavailable';
status.textContent = `Для регистрации нужно минимум ${MIN_REQUIRED_SOL} SOL. Сейчас на кошельке ${formatSol(currentBalance, 6)} SOL. Пополните на промо-странице или попросите перевод у знакомого с тестовыми SOL.`;
status.style.display = '';
const openTopup = window.confirm('Открыть страницу пополнения с вашим кошельком?');
if (openTopup) {
window.open(getTopupSiteUrl(walletAddress), '_blank', 'noopener,noreferrer');
}
return;
}
await authService.reconnect(state.entrySettings.shineServer);
const result = await authService.registerUser(state.registrationDraft.login, state.registrationDraft.password);
state.registrationDraft.flowType = 'registration';
@ -151,7 +185,7 @@ export function render({ navigate }) {
});
card.innerHTML = `
<p class="auth-copy">Для регистрации в Solana нужно заплатить 0,01 SOL (примерно 1 доллар).</p>
<p class="auth-copy">Для регистрации в тестовой Solana нужно минимум 0,01 SOL на вашем кошельке.</p>
<label class="stack"><span class="field-label">Номер кошелька (wallet.key = device.key)</span></label>
<div class="stack">
<span class="field-label">Баланс (Solana)</span>

View File

@ -50,10 +50,10 @@ export function render({ navigate }) {
card.innerHTML = `
<p class="auth-copy">Кнопка «Пополнить» в кошельке будет переводить на отдельный сайт. Пока доступно тестовое пополнение.</p>
<div class="stack" style="gap:6px;">
<p class="meta-muted">1. Вы можете открыть сайт для покупки SOL.</p>
<p class="meta-muted">1. Вы можете открыть промо-сайт пополнения с подставленным кошельком.</p>
<p class="meta-muted">2. Либо нажать «Тестовое пополнение» и получить 1 SOL через DevNet airdrop.</p>
</div>
<a class="link-card" href="${getTopupSiteUrl()}" target="_blank" rel="noreferrer">Открыть сайт пополнения</a>
<a class="link-card" id="topup-site-link" href="${getTopupSiteUrl(state.registrationPayment.walletAddress || '')}" target="_blank" rel="noreferrer">Открыть сайт пополнения</a>
<div class="card stack" style="padding:12px; max-width:320px;">
<div class="field-label" style="margin-bottom:6px;">Кошелёк для пополнения (wallet.key)</div>
</div>
@ -107,6 +107,10 @@ export function render({ navigate }) {
state.registrationPayment.walletAddress = wallet.address;
walletValue.value = wallet.address;
}
const topupSiteLink = card.querySelector('#topup-site-link');
if (topupSiteLink instanceof HTMLAnchorElement) {
topupSiteLink.href = getTopupSiteUrl(walletValue.value);
}
const balance = await getBalanceSol({
endpoint: state.entrySettings.solanaServer,
address: walletValue.value,
@ -130,4 +134,3 @@ export function render({ navigate }) {
return screen;
}

View File

@ -242,7 +242,7 @@ export function render({ navigate }) {
'Кнопка будет переводить на отдельный сайт пополнения.\n\nНажмите OK, чтобы открыть сайт.\nНажмите Отмена, чтобы выполнить тестовое пополнение (airdrop 1 SOL на DevNet).',
);
if (openSite) {
window.open(getTopupSiteUrl(), '_blank', 'noopener,noreferrer');
window.open(getTopupSiteUrl(walletAddress), '_blank', 'noopener,noreferrer');
setStatus('Открыт сайт пополнения. Пока также доступно тестовое пополнение.');
return;
}

View File

@ -3,7 +3,7 @@ import { extractDeviceKey32FromStoredValue } from './device-key-utils.js';
import { loadEncryptedUserSecrets } from './key-vault.js';
const DEFAULT_SOLANA_ENDPOINT = 'https://api.devnet.solana.com';
const TOPUP_SITE_URL = 'https://www.moonpay.com/buy/sol';
const TOPUP_SITE_URL = 'https://shine-promo-solana-devnet.shineup.me/';
let solanaLibPromise = null;
@ -115,6 +115,14 @@ export function formatSol(value, digits = 6) {
});
}
export function getTopupSiteUrl() {
return TOPUP_SITE_URL;
export function getTopupSiteUrl(walletAddress = '') {
const cleanWallet = String(walletAddress || '').trim();
if (!cleanWallet) return TOPUP_SITE_URL;
try {
const url = new URL(TOPUP_SITE_URL);
url.searchParams.set('wallet', cleanWallet);
return url.toString();
} catch {
return `${TOPUP_SITE_URL}?wallet=${encodeURIComponent(cleanWallet)}`;
}
}