Files
brk/website_next/wallets/storage/wallets.js
T
2026-06-16 23:37:03 +02:00

156 lines
3.3 KiB
JavaScript

import { decryptSecret, encryptSecret } from "./encryption.js";
const STORAGE_KEY = "bitview.wallets.v2";
/**
* @typedef {import("./encryption.js").EncryptedSecret} EncryptedSecret
* @typedef {import("../xpub/address.js").AddressScript} AddressScript
*/
/**
* @typedef {Object} StoredWallet
* @property {string} id
* @property {string} name
* @property {AddressScript} script
* @property {string} xpub
* @property {number} createdAt
* @property {number} updatedAt
*/
/**
* @typedef {Object} AddWalletInput
* @property {string} name
* @property {AddressScript} script
* @property {string} xpub
*/
/**
* @typedef {Object} UpdateWalletScriptInput
* @property {string} walletId
* @property {AddressScript} script
*/
/**
* @typedef {Object} WalletVault
* @property {StoredWallet[]} wallets
*/
/**
* @param {unknown} value
* @returns {EncryptedSecret | undefined}
*/
function readEncryptedVault(value) {
return value && typeof value === "object"
? /** @type {EncryptedSecret} */ (value)
: undefined;
}
/**
* @param {unknown} value
* @returns {WalletVault}
*/
function readVault(value) {
if (!value || typeof value !== "object" || !("wallets" in value)) {
return { wallets: [] };
}
return Array.isArray(value.wallets)
? /** @type {WalletVault} */ (value)
: { wallets: [] };
}
function createWalletId() {
return crypto.randomUUID();
}
function now() {
return Date.now();
}
export function hasStoredWallets() {
return Boolean(localStorage.getItem(STORAGE_KEY));
}
export function resetWalletVault() {
localStorage.removeItem(STORAGE_KEY);
}
/**
* @param {string} pagePassword
*/
export async function createWalletVault(pagePassword) {
await writeWallets([], pagePassword);
}
/**
* @param {string} pagePassword
*/
export async function loadWallets(pagePassword) {
const value = localStorage.getItem(STORAGE_KEY);
const encrypted = value ? readEncryptedVault(JSON.parse(value)) : undefined;
if (!encrypted) return [];
return readVault(JSON.parse(await decryptSecret(encrypted, pagePassword))).wallets;
}
/**
* @param {StoredWallet[]} wallets
* @param {string} pagePassword
*/
async function writeWallets(wallets, pagePassword) {
localStorage.setItem(
STORAGE_KEY,
JSON.stringify(
await encryptSecret(JSON.stringify({ wallets }), pagePassword),
),
);
}
/**
* @param {StoredWallet[]} wallets
* @param {AddWalletInput} input
* @param {string} pagePassword
*/
export async function addWallet(wallets, input, pagePassword) {
const time = now();
const wallet = {
id: createWalletId(),
name: input.name.trim(),
script: input.script,
xpub: input.xpub.trim(),
createdAt: time,
updatedAt: time,
};
const nextWallets = [...wallets, wallet];
await writeWallets(nextWallets, pagePassword);
return {
wallet,
wallets: nextWallets,
};
}
/**
* @param {StoredWallet[]} wallets
* @param {UpdateWalletScriptInput} input
* @param {string} pagePassword
*/
export async function updateWalletScript(wallets, input, pagePassword) {
const time = now();
const nextWallets = wallets.map((wallet) => {
return wallet.id === input.walletId
? {
...wallet,
script: input.script,
updatedAt: time,
}
: wallet;
});
await writeWallets(nextWallets, pagePassword);
return nextWallets;
}