SHA256
feat(ui): кошелек на device.key и проверки серверов (тоже пока не проверено)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { renderHeader } from '../components/header.js';
|
||||
import { checkServerAvailability, saveEntrySettings, state } from '../state.js';
|
||||
import { saveEntrySettings, state } from '../state.js';
|
||||
import { checkServerAvailabilityByKey } from '../services/server-health-service.js';
|
||||
|
||||
export const pageMeta = { id: 'entry-settings-view', title: 'Настройки входа', showAppChrome: false };
|
||||
|
||||
@@ -86,9 +87,17 @@ export function render({ navigate }) {
|
||||
}
|
||||
};
|
||||
|
||||
const runCheck = () => {
|
||||
const runCheck = async () => {
|
||||
draft[field.key] = input.value.trim();
|
||||
applyStatus(checkServerAvailability(input.value));
|
||||
checkButton.disabled = true;
|
||||
checkButton.textContent = 'Проверка...';
|
||||
try {
|
||||
const next = await checkServerAvailabilityByKey(field.key, input.value);
|
||||
applyStatus(next);
|
||||
} finally {
|
||||
checkButton.disabled = false;
|
||||
checkButton.textContent = 'Проверить';
|
||||
}
|
||||
};
|
||||
|
||||
applyStatus(draft.statuses[field.key]);
|
||||
@@ -98,13 +107,17 @@ export function render({ navigate }) {
|
||||
draft[field.key] = input.value;
|
||||
applyStatus('idle');
|
||||
window.clearTimeout(timers.get(field.key));
|
||||
timers.set(field.key, window.setTimeout(runCheck, 3000));
|
||||
timers.set(field.key, window.setTimeout(() => {
|
||||
void runCheck();
|
||||
}, 3000));
|
||||
});
|
||||
input.addEventListener('blur', () => {
|
||||
void runCheck();
|
||||
});
|
||||
input.addEventListener('blur', runCheck);
|
||||
input.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
runCheck();
|
||||
void runCheck();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { renderHeader } from '../components/header.js';
|
||||
import { renderHeader } from '../components/header.js';
|
||||
import {
|
||||
authService,
|
||||
refreshRegistrationBalance,
|
||||
setAuthError,
|
||||
setAuthInfo,
|
||||
state,
|
||||
} from '../state.js';
|
||||
import { toUserMessage } from '../services/ui-error-texts.js';
|
||||
import { deriveWalletFromPassword, formatSol, getBalanceSol } from '../services/solana-wallet-service.js';
|
||||
|
||||
export const pageMeta = { id: 'registration-payment-view', title: 'Оплата регистрации', showAppChrome: false };
|
||||
|
||||
@@ -39,7 +39,7 @@ export function render({ navigate }) {
|
||||
const walletValue = document.createElement('input');
|
||||
walletValue.className = 'input';
|
||||
walletValue.type = 'text';
|
||||
walletValue.value = state.registrationPayment.walletAddress;
|
||||
walletValue.value = state.registrationPayment.walletAddress || '';
|
||||
walletValue.addEventListener('input', () => {
|
||||
state.registrationPayment.walletAddress = walletValue.value;
|
||||
});
|
||||
@@ -71,15 +71,36 @@ export function render({ navigate }) {
|
||||
balanceRow.className = 'row wrap-row';
|
||||
|
||||
const balanceValue = document.createElement('strong');
|
||||
balanceValue.textContent = `${state.registrationPayment.balanceSOL} SOL`;
|
||||
balanceValue.textContent = `${formatSol(parseBalanceSol(state.registrationPayment.balanceSOL), 6)} SOL`;
|
||||
|
||||
const refreshButton = document.createElement('button');
|
||||
refreshButton.className = 'square-btn';
|
||||
refreshButton.type = 'button';
|
||||
refreshButton.textContent = '↻';
|
||||
refreshButton.title = 'Обновить';
|
||||
|
||||
const refreshBalance = async () => {
|
||||
const address = String(walletValue.value || '').trim();
|
||||
if (!address) return;
|
||||
refreshButton.disabled = true;
|
||||
try {
|
||||
const balance = await getBalanceSol({
|
||||
endpoint: state.entrySettings.solanaServer,
|
||||
address,
|
||||
});
|
||||
state.registrationPayment.balanceSOL = String(balance.sol);
|
||||
balanceValue.textContent = `${formatSol(balance.sol, 6)} SOL`;
|
||||
} catch (error) {
|
||||
status.className = 'status-line is-unavailable';
|
||||
status.textContent = `Не удалось обновить баланс: ${error?.message || 'unknown'}`;
|
||||
status.style.display = '';
|
||||
} finally {
|
||||
refreshButton.disabled = false;
|
||||
}
|
||||
};
|
||||
|
||||
refreshButton.addEventListener('click', () => {
|
||||
balanceValue.textContent = `${refreshRegistrationBalance()} SOL`;
|
||||
void refreshBalance();
|
||||
});
|
||||
|
||||
balanceRow.append(balanceValue, refreshButton);
|
||||
@@ -100,7 +121,7 @@ export function render({ navigate }) {
|
||||
const balanceSol = parseBalanceSol(state.registrationPayment.balanceSOL);
|
||||
if (balanceSol < MIN_REGISTER_BALANCE_SOL) {
|
||||
status.className = 'status-line is-unavailable';
|
||||
status.textContent = `Недостаточный баланс для регистрации: ${state.registrationPayment.balanceSOL} SOL. Нужно минимум ${MIN_REGISTER_BALANCE_SOL.toFixed(2)} SOL.`;
|
||||
status.textContent = `Недостаточный баланс для регистрации: ${formatSol(balanceSol, 6)} SOL. Нужно минимум ${MIN_REGISTER_BALANCE_SOL.toFixed(2)} SOL.`;
|
||||
status.style.display = '';
|
||||
return;
|
||||
}
|
||||
@@ -141,9 +162,9 @@ export function render({ navigate }) {
|
||||
|
||||
card.innerHTML = `
|
||||
<p class="auth-copy">Для регистрации в Solana нужно заплатить 0,01 SOL (примерно 1 доллар).</p>
|
||||
<label class="stack"><span class="field-label">Номер кошелька</span></label>
|
||||
<label class="stack"><span class="field-label">Номер кошелька (wallet.key = device.key)</span></label>
|
||||
<div class="stack">
|
||||
<span class="field-label">Баланс</span>
|
||||
<span class="field-label">Баланс (Solana)</span>
|
||||
</div>
|
||||
`;
|
||||
card.children[1].append(walletRow);
|
||||
@@ -158,5 +179,20 @@ export function render({ navigate }) {
|
||||
card,
|
||||
);
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const draftPassword = String(state.registrationDraft.password ?? '');
|
||||
const wallet = await deriveWalletFromPassword(draftPassword);
|
||||
state.registrationPayment.walletAddress = wallet.address;
|
||||
walletValue.value = wallet.address;
|
||||
await refreshBalance();
|
||||
} catch (error) {
|
||||
status.className = 'status-line is-unavailable';
|
||||
status.textContent = `Не удалось подготовить wallet.key: ${error?.message || 'unknown'}`;
|
||||
status.style.display = '';
|
||||
}
|
||||
})();
|
||||
|
||||
return screen;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { renderHeader } from '../components/header.js';
|
||||
import { checkServerAvailability, saveEntrySettings, state } from '../state.js';
|
||||
import { saveEntrySettings, state } from '../state.js';
|
||||
import { checkServerAvailabilityByKey } from '../services/server-health-service.js';
|
||||
|
||||
export const pageMeta = { id: 'server-settings-view', title: 'Настройки серверов' };
|
||||
|
||||
@@ -68,9 +69,17 @@ export function render({ navigate }) {
|
||||
}
|
||||
};
|
||||
|
||||
const runCheck = () => {
|
||||
const runCheck = async () => {
|
||||
draft[field.key] = input.value.trim();
|
||||
applyStatus(checkServerAvailability(input.value));
|
||||
checkButton.disabled = true;
|
||||
checkButton.textContent = 'Проверка...';
|
||||
try {
|
||||
const next = await checkServerAvailabilityByKey(field.key, input.value);
|
||||
applyStatus(next);
|
||||
} finally {
|
||||
checkButton.disabled = false;
|
||||
checkButton.textContent = 'Проверить';
|
||||
}
|
||||
};
|
||||
|
||||
applyStatus(draft.statuses[field.key]);
|
||||
@@ -80,13 +89,17 @@ export function render({ navigate }) {
|
||||
draft[field.key] = input.value;
|
||||
applyStatus('idle');
|
||||
window.clearTimeout(timers.get(field.key));
|
||||
timers.set(field.key, window.setTimeout(runCheck, 3000));
|
||||
timers.set(field.key, window.setTimeout(() => {
|
||||
void runCheck();
|
||||
}, 3000));
|
||||
});
|
||||
input.addEventListener('blur', () => {
|
||||
void runCheck();
|
||||
});
|
||||
input.addEventListener('blur', runCheck);
|
||||
input.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
runCheck();
|
||||
void runCheck();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import { renderHeader } from '../components/header.js';
|
||||
import { state } from '../state.js';
|
||||
import {
|
||||
deriveWalletFromPassword,
|
||||
formatSol,
|
||||
getBalanceSol,
|
||||
getTopupSiteUrl,
|
||||
requestAirdropSol,
|
||||
} from '../services/solana-wallet-service.js';
|
||||
|
||||
export const pageMeta = { id: 'topup-view', title: 'Пополнение счета', showAppChrome: false };
|
||||
|
||||
const BUY_LINK = 'https://www.moonpay.com/buy/sol';
|
||||
|
||||
export function render({ navigate }) {
|
||||
const screen = document.createElement('section');
|
||||
screen.className = 'stack';
|
||||
@@ -12,10 +17,14 @@ export function render({ navigate }) {
|
||||
const walletValue = document.createElement('input');
|
||||
walletValue.className = 'input';
|
||||
walletValue.type = 'text';
|
||||
walletValue.value = state.registrationPayment.walletAddress;
|
||||
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';
|
||||
@@ -39,15 +48,14 @@ export function render({ navigate }) {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'card stack';
|
||||
card.innerHTML = `
|
||||
<p class="auth-copy">Для пополнения счета скопируйте номер кошелька.</p>
|
||||
<p class="auth-copy">Кнопка «Пополнить» в кошельке будет переводить на отдельный сайт. Пока доступно тестовое пополнение.</p>
|
||||
<div class="stack" style="gap:6px;">
|
||||
<p class="meta-muted">1. Пополните через любое свое приложение, используя этот кошелек в сети Solana.</p>
|
||||
<p class="meta-muted">2. Либо откройте страницу для покупки SOL.</p>
|
||||
<p class="meta-muted">3. Либо используйте кнопку «Тестовое пополнение» (работает в тестовой Solana).</p>
|
||||
<p class="meta-muted">1. Вы можете открыть сайт для покупки SOL.</p>
|
||||
<p class="meta-muted">2. Либо нажать «Тестовое пополнение» и получить 1 SOL через DevNet airdrop.</p>
|
||||
</div>
|
||||
<a class="link-card" href="${BUY_LINK}" target="_blank" rel="noreferrer">Открыть страницу покупки SOL</a>
|
||||
<a class="link-card" href="${getTopupSiteUrl()}" target="_blank" rel="noreferrer">Открыть сайт пополнения</a>
|
||||
<div class="card stack" style="padding:12px; max-width:320px;">
|
||||
<div class="field-label" style="margin-bottom:6px;">Кошелёк для пополнения</div>
|
||||
<div class="field-label" style="margin-bottom:6px;">Кошелёк для пополнения (wallet.key)</div>
|
||||
</div>
|
||||
`;
|
||||
card.children[3].append(walletRow);
|
||||
@@ -55,10 +63,31 @@ export function render({ navigate }) {
|
||||
const testButton = document.createElement('button');
|
||||
testButton.className = 'ghost-btn';
|
||||
testButton.type = 'button';
|
||||
testButton.textContent = 'Тестовое пополнение';
|
||||
testButton.addEventListener('click', () => {
|
||||
state.registrationPayment.balanceSOL = '0.0250';
|
||||
window.alert('Тестовое пополнение выполнено. Баланс обновлён.');
|
||||
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');
|
||||
@@ -71,14 +100,34 @@ export function render({ navigate }) {
|
||||
actions.className = 'auth-footer-actions';
|
||||
actions.append(testButton, backButton);
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
if (!walletValue.value) {
|
||||
const wallet = await deriveWalletFromPassword(String(state.registrationDraft.password ?? ''));
|
||||
state.registrationPayment.walletAddress = wallet.address;
|
||||
walletValue.value = wallet.address;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,42 +1,67 @@
|
||||
import { renderHeader } from '../components/header.js';
|
||||
import { wallet } from '../mock-data.js';
|
||||
import { state } from '../state.js';
|
||||
import {
|
||||
formatSol,
|
||||
getBalanceSol,
|
||||
getTopupSiteUrl,
|
||||
getWalletFromStoredDeviceKey,
|
||||
requestAirdropSol,
|
||||
transferSol,
|
||||
} from '../services/solana-wallet-service.js';
|
||||
|
||||
export const pageMeta = { id: 'wallet-view', title: 'Кошелёк' };
|
||||
|
||||
function nowRu() {
|
||||
return new Date().toLocaleString('ru-RU');
|
||||
}
|
||||
|
||||
export function render({ navigate }) {
|
||||
const screen = document.createElement('section');
|
||||
screen.className = 'stack';
|
||||
let statusText = 'Данные демонстрационные';
|
||||
|
||||
let walletCtx = null;
|
||||
let walletAddress = '';
|
||||
|
||||
const status = document.createElement('p');
|
||||
status.className = 'meta-muted';
|
||||
status.textContent = 'Инициализация wallet.key...';
|
||||
|
||||
const updateStatus = (text) => {
|
||||
statusText = text;
|
||||
status.textContent = statusText;
|
||||
};
|
||||
|
||||
const screenTitle = 'Кошелёк';
|
||||
screen.append(
|
||||
renderHeader({
|
||||
title: 'Кошелёк',
|
||||
title: screenTitle,
|
||||
leftAction: { label: '←', onClick: () => navigate('profile-view') },
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
const card = document.createElement('div');
|
||||
card.className = 'card stack';
|
||||
card.innerHTML = `
|
||||
<div>
|
||||
<p class="meta-muted">Баланс</p>
|
||||
<h2 style="font-size:30px;">${wallet.balanceSOL} SOL</h2>
|
||||
<p class="meta-muted">Обновлено: ${wallet.updatedAt}</p>
|
||||
</div>
|
||||
<div class="card" style="padding:10px;">
|
||||
<p class="meta-muted" style="margin-bottom:6px;">Публичный адрес</p>
|
||||
<p style="font-size:13px; line-height:1.4; word-break:break-all;">${wallet.publicAddress}</p>
|
||||
</div>
|
||||
|
||||
const balanceWrap = document.createElement('div');
|
||||
const balanceLabel = document.createElement('p');
|
||||
balanceLabel.className = 'meta-muted';
|
||||
balanceLabel.textContent = 'Баланс (Solana)';
|
||||
const balanceValue = document.createElement('h2');
|
||||
balanceValue.style.fontSize = '30px';
|
||||
balanceValue.textContent = '— SOL';
|
||||
const updatedLabel = document.createElement('p');
|
||||
updatedLabel.className = 'meta-muted';
|
||||
updatedLabel.textContent = 'Обновлено: —';
|
||||
const endpointLabel = document.createElement('p');
|
||||
endpointLabel.className = 'meta-muted';
|
||||
endpointLabel.textContent = `RPC: ${state.entrySettings.solanaServer}`;
|
||||
balanceWrap.append(balanceLabel, balanceValue, updatedLabel, endpointLabel);
|
||||
|
||||
const addressCard = document.createElement('div');
|
||||
addressCard.className = 'card';
|
||||
addressCard.style.padding = '10px';
|
||||
addressCard.innerHTML = `
|
||||
<p class="meta-muted" style="margin-bottom:6px;">Публичный адрес (wallet.key = device.key)</p>
|
||||
<p style="font-size:13px; line-height:1.4; word-break:break-all;" id="wallet-address-value">—</p>
|
||||
`;
|
||||
|
||||
card.append(balanceWrap, addressCard);
|
||||
|
||||
const actions = document.createElement('div');
|
||||
actions.className = 'stack';
|
||||
actions.innerHTML = `
|
||||
@@ -50,29 +75,126 @@ export function render({ navigate }) {
|
||||
</div>
|
||||
`;
|
||||
|
||||
actions.querySelector('#copy-address').addEventListener('click', async () => {
|
||||
const copyBtn = actions.querySelector('#copy-address');
|
||||
const refreshBtn = actions.querySelector('#refresh-balance');
|
||||
const sendBtn = actions.querySelector('#send-sol');
|
||||
const topupBtn = actions.querySelector('#topup-sol');
|
||||
const addressEl = addressCard.querySelector('#wallet-address-value');
|
||||
|
||||
const setStatus = (text) => {
|
||||
status.textContent = String(text || '');
|
||||
};
|
||||
|
||||
const refreshBalance = async () => {
|
||||
if (!walletAddress) {
|
||||
setStatus('Кошелёк не инициализирован.');
|
||||
return;
|
||||
}
|
||||
refreshBtn.disabled = true;
|
||||
try {
|
||||
await navigator.clipboard.writeText(wallet.publicAddress);
|
||||
updateStatus('Адрес скопирован в буфер обмена');
|
||||
const balance = await getBalanceSol({
|
||||
endpoint: state.entrySettings.solanaServer,
|
||||
address: walletAddress,
|
||||
});
|
||||
balanceValue.textContent = `${formatSol(balance.sol, 6)} SOL`;
|
||||
updatedLabel.textContent = `Обновлено: ${nowRu()}`;
|
||||
endpointLabel.textContent = `RPC: ${balance.endpoint}`;
|
||||
setStatus('Баланс обновлён.');
|
||||
} catch (error) {
|
||||
setStatus(`Не удалось получить баланс: ${error?.message || 'unknown'}`);
|
||||
} finally {
|
||||
refreshBtn.disabled = false;
|
||||
}
|
||||
};
|
||||
|
||||
copyBtn.addEventListener('click', async () => {
|
||||
if (!walletAddress) return;
|
||||
try {
|
||||
await navigator.clipboard.writeText(walletAddress);
|
||||
setStatus('Адрес скопирован в буфер обмена');
|
||||
} catch {
|
||||
updateStatus('Не удалось скопировать в этом браузере');
|
||||
setStatus('Не удалось скопировать адрес в этом браузере');
|
||||
}
|
||||
});
|
||||
|
||||
actions.querySelector('#refresh-balance').addEventListener('click', () => {
|
||||
updateStatus(`Баланс обновлен: ${wallet.balanceSOL} SOL`);
|
||||
refreshBtn.addEventListener('click', () => {
|
||||
void refreshBalance();
|
||||
});
|
||||
|
||||
actions.querySelector('#send-sol').addEventListener('click', () => {
|
||||
updateStatus('Демо-функция: перевод будет добавлен позже');
|
||||
sendBtn.addEventListener('click', async () => {
|
||||
if (!walletCtx?.keypair) {
|
||||
setStatus('Перевод недоступен: wallet.key не загружен.');
|
||||
return;
|
||||
}
|
||||
const toAddress = window.prompt('Введите адрес получателя (Solana):', '');
|
||||
if (!toAddress) return;
|
||||
const amountRaw = window.prompt('Введите сумму SOL для перевода:', '0.01');
|
||||
if (!amountRaw) return;
|
||||
|
||||
sendBtn.disabled = true;
|
||||
try {
|
||||
const tx = await transferSol({
|
||||
endpoint: state.entrySettings.solanaServer,
|
||||
fromKeypair: walletCtx.keypair,
|
||||
toAddress,
|
||||
amountSol: Number(String(amountRaw || '').replace(',', '.')),
|
||||
});
|
||||
setStatus(`Перевод отправлен. Signature: ${tx.signature}`);
|
||||
await refreshBalance();
|
||||
} catch (error) {
|
||||
setStatus(`Ошибка перевода: ${error?.message || 'unknown'}`);
|
||||
} finally {
|
||||
sendBtn.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
actions.querySelector('#topup-sol').addEventListener('click', () => {
|
||||
updateStatus('Демо-функция: пополнение будет добавлено позже');
|
||||
topupBtn.addEventListener('click', async () => {
|
||||
if (!walletAddress) {
|
||||
setStatus('Кошелёк не инициализирован.');
|
||||
return;
|
||||
}
|
||||
|
||||
const openSite = window.confirm(
|
||||
'Кнопка будет переводить на отдельный сайт пополнения.\n\nНажмите OK, чтобы открыть сайт.\nНажмите Отмена, чтобы выполнить тестовое пополнение (airdrop 1 SOL на DevNet).'
|
||||
);
|
||||
if (openSite) {
|
||||
window.open(getTopupSiteUrl(), '_blank', 'noopener,noreferrer');
|
||||
setStatus('Открыт сайт пополнения. Пока также доступно тестовое пополнение.');
|
||||
return;
|
||||
}
|
||||
|
||||
topupBtn.disabled = true;
|
||||
try {
|
||||
const drop = await requestAirdropSol({
|
||||
endpoint: state.entrySettings.solanaServer,
|
||||
address: walletAddress,
|
||||
amountSol: 1,
|
||||
});
|
||||
setStatus(`Тестовое пополнение выполнено (airdrop 1 SOL). Signature: ${drop.signature}`);
|
||||
await refreshBalance();
|
||||
} catch (error) {
|
||||
setStatus(`Ошибка тестового пополнения: ${error?.message || 'unknown'}`);
|
||||
} finally {
|
||||
topupBtn.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
updateStatus(statusText);
|
||||
(async () => {
|
||||
try {
|
||||
walletCtx = await getWalletFromStoredDeviceKey({
|
||||
login: state.session.login,
|
||||
storagePwd: state.session.storagePwdInMemory,
|
||||
});
|
||||
walletAddress = walletCtx.address;
|
||||
addressEl.textContent = walletAddress;
|
||||
await refreshBalance();
|
||||
} catch (error) {
|
||||
addressEl.textContent = 'wallet.key недоступен';
|
||||
setStatus(`Не удалось инициализировать кошелёк: ${error?.message || 'unknown'}`);
|
||||
}
|
||||
})();
|
||||
|
||||
screen.append(card, actions, status);
|
||||
return screen;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user