Обновление архитектуры ключей и кошельков

This commit is contained in:
AidarKC
2026-10-01 17:11:38 +03:00
parent f452ce1f1e
commit 8d22602761
51 changed files with 565 additions and 898 deletions
+70 -6
View File
@@ -1,4 +1,4 @@
import { extractClientKey32FromStoredValue } from './client-key-utils.js';
import { extractEd25519Seed32FromStoredValue } from './client-key-utils.js';
import { base64ToBytes } from './crypto-utils.js';
import { loadEncryptedUserSecrets } from './key-vault.js';
import { SOLANA_ENDPOINT_DEFAULT } from '../solana-programs.js';
@@ -104,7 +104,7 @@ function decodeBase58(input) {
async function keypairFromPkcs8(pkcs8B64) {
const solana = await loadSolanaLib();
const seed32 = extractClientKey32FromStoredValue(pkcs8B64);
const seed32 = extractEd25519Seed32FromStoredValue(pkcs8B64);
try {
return solana.Keypair.fromSeed(seed32);
} finally {
@@ -114,7 +114,7 @@ async function keypairFromPkcs8(pkcs8B64) {
async function keypairFromStoredSecret(storedSecret) {
const solana = await loadSolanaLib();
const seed32 = extractClientKey32FromStoredValue(storedSecret);
const seed32 = extractEd25519Seed32FromStoredValue(storedSecret);
try {
return solana.Keypair.fromSeed(seed32);
} finally {
@@ -161,7 +161,7 @@ export async function createSolanaWalletFromPrivateBase58(privateKey32Base58) {
};
}
export async function getWalletFromStoredClientKey({ login, storagePwd }) {
) {
const cleanLogin = String(login || '').trim();
const cleanPwd = String(storagePwd || '').trim();
if (!cleanLogin || !cleanPwd) {
@@ -200,7 +200,7 @@ export async function getWalletFromStoredBlockchainKey({ login, storagePwd }) {
};
}
export async function getWalletFromStoredRootKey({ login, storagePwd }) {
) {
const cleanLogin = String(login || '').trim();
const cleanPwd = String(storagePwd || '').trim();
if (!cleanLogin || !cleanPwd) {
@@ -227,7 +227,7 @@ export async function getStoredSolanaWalletChoices({ login, storagePwd } = {}) {
return [{
id: 'blockchain-key',
keySource: 'blockchain',
label: 'blockchain key',
label: 'Ключ блокчейна',
address: wallet.address,
keypair: wallet.keypair,
}];
@@ -289,6 +289,70 @@ export async function transferSol({ endpoint, fromKeypair, toAddress, amountSol
return { endpoint: rpc, signature, lamports };
}
export async function transferAllSol({ endpoint, fromKeypair, toAddress }) {
const solana = await loadSolanaLib();
const rpc = normalizeEndpoint(endpoint);
const cleanTo = String(toAddress || '').trim();
if (!fromKeypair?.publicKey) throw new Error('Не передан старый Solana-кошелёк');
if (!cleanTo) throw new Error('Не указан новый адрес кошелька');
if (fromKeypair.publicKey.toBase58() === cleanTo) {
throw new Error('Старый и новый кошельки совпадают');
}
const conn = new solana.Connection(rpc, 'confirmed');
const balanceLamports = await conn.getBalance(fromKeypair.publicKey, 'confirmed');
if (balanceLamports <= 0) {
return { endpoint: rpc, signature: '', lamports: 0, feeLamports: 0, balanceLamports, skipped: true };
}
const latest = await conn.getLatestBlockhash('confirmed');
const probeTx = new solana.Transaction({
feePayer: fromKeypair.publicKey,
recentBlockhash: latest.blockhash,
}).add(solana.SystemProgram.transfer({
fromPubkey: fromKeypair.publicKey,
toPubkey: new solana.PublicKey(cleanTo),
lamports: 1,
}));
const feeResult = await conn.getFeeForMessage(probeTx.compileMessage(), 'confirmed');
const feeLamports = Number(feeResult?.value ?? 5000);
const transferLamports = balanceLamports - feeLamports;
if (transferLamports <= 0) {
return {
endpoint: rpc,
signature: '',
lamports: 0,
feeLamports,
balanceLamports,
skipped: true,
};
}
const tx = new solana.Transaction({
feePayer: fromKeypair.publicKey,
recentBlockhash: latest.blockhash,
}).add(solana.SystemProgram.transfer({
fromPubkey: fromKeypair.publicKey,
toPubkey: new solana.PublicKey(cleanTo),
lamports: transferLamports,
}));
tx.sign(fromKeypair);
const signature = await conn.sendRawTransaction(tx.serialize(), { skipPreflight: false });
await conn.confirmTransaction({
signature,
blockhash: latest.blockhash,
lastValidBlockHeight: latest.lastValidBlockHeight,
}, 'confirmed');
return {
endpoint: rpc,
signature,
lamports: transferLamports,
feeLamports,
balanceLamports,
skipped: false,
};
}
export function formatSol(value, digits = 6) {
const n = Number(value);
if (!Number.isFinite(n)) return '0';