diff --git a/apps/desktop/src/bun/index.ts b/apps/desktop/src/bun/index.ts index 8254859..f813789 100644 --- a/apps/desktop/src/bun/index.ts +++ b/apps/desktop/src/bun/index.ts @@ -623,6 +623,13 @@ const rpc = BrowserView.defineRPC({ console.log(`[rpc] moneroGetAccounts: ${accounts.length} accounts`) return accounts }, + moneroListWallets: async () => { + console.log('[rpc] moneroListWallets') + if (!moneroManager) throw new Error('Monero wallet RPC not started') + const wallets = moneroManager.listWallets() + console.log(`[rpc] moneroListWallets: ${wallets.join(', ') || 'none'}`) + return wallets + }, }, messages: {}, }, diff --git a/apps/desktop/src/bun/lib/monero/wallet.ts b/apps/desktop/src/bun/lib/monero/wallet.ts index 598b63f..4764425 100644 --- a/apps/desktop/src/bun/lib/monero/wallet.ts +++ b/apps/desktop/src/bun/lib/monero/wallet.ts @@ -1,6 +1,7 @@ import { MoneroWalletRpc, MoneroRpcConnection, connectToWalletRpc, connectToDaemonRpc } from 'monero-ts' import { getBinaryPath, getWalletDir } from './binary' -import { existsSync } from 'fs' +import { existsSync, readdirSync } from 'fs' +import { join } from 'path' const DEFAULT_DAEMON = 'xmr-node.cakewallet.com:18081' @@ -18,6 +19,17 @@ export class MoneroWalletManager { this.rpcPassword = crypto.randomUUID() } + listWallets(): string[] { + const dir = getWalletDir() + if (!existsSync(dir)) return [] + const files = readdirSync(dir) + const wallets = files + .filter(f => f.endsWith('.keys')) + .map(f => f.replace(/\.keys$/, '')) + console.log(`[monero] wallets on disk: ${wallets.join(', ') || 'none'}`) + return wallets + } + private get rpcUrl() { return `http://127.0.0.1:${this.rpcPort}` } diff --git a/apps/desktop/src/lib/electrobun.ts b/apps/desktop/src/lib/electrobun.ts index b207dec..89dcc06 100644 --- a/apps/desktop/src/lib/electrobun.ts +++ b/apps/desktop/src/lib/electrobun.ts @@ -196,6 +196,10 @@ type RPC = { params: {} response: MoneroAccountEntry[] } + moneroListWallets: { + params: {} + response: string[] + } } messages: {} } diff --git a/apps/desktop/src/lib/states/wallet.svelte.ts b/apps/desktop/src/lib/states/wallet.svelte.ts index adc06a2..fa1b46c 100644 --- a/apps/desktop/src/lib/states/wallet.svelte.ts +++ b/apps/desktop/src/lib/states/wallet.svelte.ts @@ -79,6 +79,7 @@ export const Wallet = () => { let moneroDownloading = $state(false) let moneroWalletName = $state('') let moneroAccounts = $state([]) + let moneroWallets = $state([]) const net = () => networks.find((n) => n.id === network)! @@ -132,6 +133,7 @@ export const Wallet = () => { await moneroStart() await checkMoneroStatus() } + await moneroListWallets() } else if (seed) { await refresh() } @@ -215,6 +217,22 @@ export const Wallet = () => { return true } + const moneroListWallets = async () => { + if (!electrobun.rpc) return + const [wallets] = await tryCatch( + electrobun.rpc.request.moneroListWallets({}), + ) + moneroWallets = wallets ?? [] + } + + const moneroOpenExistingWallet = async (name: string, password: string) => { + if (!electrobun.rpc) throw new Error('RPC not available') + await electrobun.rpc.request.moneroOpenWallet({ name, password }) + moneroWalletName = name + moneroWalletOpen = true + await moneroRefresh() + } + const saveApiKey = async (key: string) => { await electrobun.rpc?.request.setSecret({ service: 'koins', @@ -285,6 +303,7 @@ export const Wallet = () => { moneroUnlockedAtomic = '0' moneroTxs = [] moneroAccounts = [] + moneroWallets = [] } const moneroCreateWallet = async (name: string, password: string) => { @@ -379,6 +398,7 @@ export const Wallet = () => { get moneroDownloading() { return moneroDownloading }, get moneroWalletName() { return moneroWalletName }, get moneroAccounts() { return moneroAccounts }, + get moneroWallets() { return moneroWallets }, refresh, init, lock, @@ -395,5 +415,7 @@ export const Wallet = () => { moneroOpenWallet, moneroRefresh, moneroFetchAccounts, + moneroListWallets, + moneroOpenExistingWallet, } } diff --git a/apps/desktop/src/routes/index.svelte b/apps/desktop/src/routes/index.svelte index 9140167..fc45a9d 100644 --- a/apps/desktop/src/routes/index.svelte +++ b/apps/desktop/src/routes/index.svelte @@ -50,6 +50,8 @@ let moneroWalletPass = $state('') let moneroMnemonic = $state('') let moneroRestoreHeight = $state(undefined) + let moneroSelectedWallet = $state('') + let moneroSelectedWalletPass = $state('') $effect(() => { w.init() @@ -137,10 +139,44 @@ Monero Wallet - Create a new wallet or restore an existing one + + {w.moneroWallets.length > 0 + ? `${w.moneroWallets.length} wallet${w.moneroWallets.length > 1 ? 's' : ''} found on disk` + : 'No existing wallets found'} +
+ {#if w.moneroWallets.length > 0} +

Select a wallet to open:

+
+ {#each w.moneroWallets as name} + + {/each} +
+ {#if moneroSelectedWallet} +
+ + +
+ {/if} +
+ {/if} +

Or create a new wallet:

@@ -152,12 +188,6 @@ disabled={w.loading || !moneroWalletName || !moneroWalletPass}> Create Wallet -

Restore from seed