mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-17 10:19:44 -07:00
156 lines
3.3 KiB
JavaScript
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;
|
|
}
|