import { ref } from 'vue'; import { csrfFetch } from '@/utils/csrfFetch'; export interface VaultBalances { balance: string; vault_balance: string; vault_balances: Record; currency: string | null; } export function useVault() { const loading = ref(false); const error = ref(null); const balances = ref(null); const transfers = ref(null); const pinRequired = ref(false); const lockedUntil = ref(null); const sessionPin = ref(null); async function load(perPage = 10) { loading.value = true; error.value = null; try { const resp = await fetch(`/api/wallet/vault?per_page=${perPage}`, { headers: { 'Accept': 'application/json' } }); if (!resp.ok) throw new Error(`HTTP ${resp.status}`); const json = await resp.json(); balances.value = { balance: json.balance, vault_balance: json.vault_balance, vault_balances: json.vault_balances ?? { BTX: json.vault_balance }, currency: json.currency, }; transfers.value = json.transfers; } catch (e: any) { error.value = e?.message || 'Failed to load vault'; } finally { loading.value = false; } } function uuidv4(): string { if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) { return (crypto as any).randomUUID(); } return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { const r = (Math.random() * 16) | 0; const v = c === 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); }); } async function deposit(amount: string, currency = 'BTX', pin?: string) { return doAction('/api/wallet/vault/deposit', amount, currency, pin); } async function withdraw(amount: string, currency = 'BTX', pin?: string) { return doAction('/api/wallet/vault/withdraw', amount, currency, pin); } async function doAction(url: string, amount: string, currency: string, pin?: string) { loading.value = true; error.value = null; pinRequired.value = false; lockedUntil.value = null; try { const usePin = pin ?? sessionPin.value; if (!usePin) { pinRequired.value = true; throw new Error('PIN required'); } const body = { amount, currency, pin: usePin, idempotency_key: uuidv4() }; const resp = await csrfFetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify(body), }); if (resp.status === 423) { const j = await resp.json().catch(() => ({})); pinRequired.value = true; lockedUntil.value = j?.locked_until || null; throw new Error(j?.message || 'PIN required'); } if (!resp.ok) { const j = await resp.json().catch(() => ({})); throw new Error(j?.message || `HTTP ${resp.status}`); } const json = await resp.json(); if (json?.balances) { balances.value = { balance: json.balances.balance, vault_balance: json.balances.vault_balance, vault_balances: json.balances.vault_balances ?? { BTX: json.balances.vault_balance }, currency: balances.value?.currency || 'BTX', }; } else { await load(); } return json; } catch (e: any) { error.value = e?.message || 'Vault action failed'; throw e; } finally { loading.value = false; } } async function verifyPin(pin: string) { error.value = null; try { const resp = await csrfFetch('/api/wallet/vault/pin/verify', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ pin }), }); if (!resp.ok) { const j = await resp.json().catch(() => ({})); if (resp.status === 423) lockedUntil.value = j?.locked_until || null; throw new Error(j?.message || `HTTP ${resp.status}`); } sessionPin.value = pin; pinRequired.value = false; lockedUntil.value = null; return await resp.json(); } catch (e: any) { error.value = e?.message || 'PIN verification failed'; throw e; } } function clearSessionPin() { sessionPin.value = null; } async function setPin(pin: string, current_pin?: string) { error.value = null; const payload: any = { pin }; if (current_pin) payload.current_pin = current_pin; const resp = await csrfFetch('/api/wallet/vault/pin/set', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify(payload), }); if (!resp.ok) { const j = await resp.json().catch(() => ({})); throw new Error(j?.message || `HTTP ${resp.status}`); } sessionPin.value = pin; return await resp.json(); } return { loading, error, balances, transfers, pinRequired, lockedUntil, load, deposit, withdraw, verifyPin, setPin, clearSessionPin, }; }