import { renderHeader } from '../components/header.js';
import { state } from '../state.js';
import {
formatSol,
getBalanceSol,
getTopupSiteUrl,
requestAirdropSol,
} from '../services/solana-wallet-service.js';
export const pageMeta = { id: 'topup-view', title: 'Пополнение счета', showAppChrome: false };
// Канонический Solana-адрес пополнения = публичный device-ключ из сгенерированного набора ключей.
// Тот же путь, что в registration-payment-view (deriveUserWalletAddress); не выводим адрес
// напрямую из пароля, иначе он расходится с device-ключом регистрации.
async function deviceWalletAddressFromBundle() {
const keyBundle = state.registrationDraft.preGeneratedKeyBundle;
if (!keyBundle || !keyBundle.devicePair) {
throw new Error('Ключи ещё не сгенерированы. Вернитесь на экран регистрации.');
}
const raw = atob(keyBundle.devicePair.publicKeyB64);
const bytes = new Uint8Array(raw.length);
for (let i = 0; i < raw.length; i += 1) bytes[i] = raw.charCodeAt(i);
const { PublicKey } = await import('https://esm.sh/@solana/web3.js@1.98.4');
return new PublicKey(bytes).toBase58();
}
export function render({ navigate }) {
const screen = document.createElement('section');
screen.className = 'stack';
const walletValue = document.createElement('input');
walletValue.className = 'input';
walletValue.type = 'text';
walletValue.value = state.registrationPayment.walletAddress || '';
walletValue.readOnly = true;
walletValue.style.fontSize = '13px';
const status = document.createElement('p');
status.className = 'meta-muted';
status.textContent = 'Проверяем кошелек...';
const copyButton = document.createElement('button');
copyButton.className = 'ghost-btn';
copyButton.type = 'button';
copyButton.textContent = 'Скопировать';
copyButton.addEventListener('click', async () => {
try {
await navigator.clipboard.writeText(walletValue.value);
copyButton.textContent = 'Скопировано';
window.setTimeout(() => {
copyButton.textContent = 'Скопировать';
}, 1500);
} catch {
window.alert('Не удалось скопировать номер кошелька.');
}
});
const walletRow = document.createElement('div');
walletRow.className = 'inline-input-row';
walletRow.append(walletValue, copyButton);
const card = document.createElement('div');
card.className = 'card stack';
card.innerHTML = `
Кнопка «Пополнить» в кошельке будет переводить на отдельный сайт. Пока доступно тестовое пополнение.
1. Вы можете открыть промо-сайт пополнения с подставленным кошельком.
2. Либо нажать «Тестовое пополнение» и получить 1 SOL через DevNet airdrop.
Открыть сайт пополнения
Кошелёк для пополнения (device key = Solana wallet)
`;
card.children[3].append(walletRow);
const testButton = document.createElement('button');
testButton.className = 'ghost-btn';
testButton.type = 'button';
testButton.textContent = 'Тестовое пополнение (1 SOL)';
testButton.addEventListener('click', async () => {
const address = String(walletValue.value || '').trim();
if (!address) {
window.alert('Адрес кошелька не найден.');
return;
}
testButton.disabled = true;
try {
const drop = await requestAirdropSol({
endpoint: state.entrySettings.solanaServer,
address,
amountSol: 1,
});
const bal = await getBalanceSol({
endpoint: state.entrySettings.solanaServer,
address,
});
state.registrationPayment.balanceSOL = String(bal.sol);
status.textContent = `Тестовое пополнение выполнено. Новый баланс: ${formatSol(bal.sol, 6)} SOL. Signature: ${drop.signature}`;
} catch (error) {
status.textContent = `Ошибка тестового пополнения: ${error?.message || 'unknown'}`;
} finally {
testButton.disabled = false;
}
});
const backButton = document.createElement('button');
backButton.className = 'primary-btn';
backButton.type = 'button';
backButton.textContent = 'Назад';
backButton.addEventListener('click', () => navigate('registration-payment-view'));
const actions = document.createElement('div');
actions.className = 'auth-footer-actions';
actions.append(testButton, backButton);
(async () => {
try {
if (!walletValue.value) {
const address = await deviceWalletAddressFromBundle();
state.registrationPayment.walletAddress = address;
walletValue.value = 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,
});
state.registrationPayment.balanceSOL = String(balance.sol);
status.textContent = `Текущий баланс: ${formatSol(balance.sol, 6)} SOL`;
} catch (error) {
status.textContent = `Не удалось получить баланс: ${error?.message || 'unknown'}`;
}
})();
screen.append(
renderHeader({
title: 'Пополнение счета',
leftAction: { label: '←', onClick: () => navigate('registration-payment-view') },
}),
card,
status,
actions,
);
return screen;
}