diff --git a/neode-ui/mock-backend.js b/neode-ui/mock-backend.js index 75b4ae66..20dbc648 100755 --- a/neode-ui/mock-backend.js +++ b/neode-ui/mock-backend.js @@ -132,6 +132,11 @@ const SEED_WALLET = { channel_sats: 8_250_000, ecash_sats: 250_000, ecash_tokens: 12, + // Ark (barkd sidecar) demo balances — signet defaults + ark_sats: 75_000, + ark_pending_sats: 0, + ark_onchain_sats: 20_000, + ark_config: { network: 'signet', ark_server: 'https://ark.signet.2nd.dev', esplora: 'https://esplora.signet.2nd.dev' }, block_height: 892451, transactions: [ { tx_hash: 'ab12cd34ef5678901234567890abcdef12345678', amount_sats: 2_000_000, direction: 'incoming', num_confirmations: 142, block_height: 892310, time_stamp: Math.floor(Date.now()/1000) - 86400, label: 'Channel funding', total_fees: 0, dest_addresses: [] }, @@ -2209,6 +2214,63 @@ app.post('/rpc/v1', (req, res) => { const total = (mockState.federations || []).reduce((s, f) => s + (f.balance_sats || 0), 0) return res.json({ result: { balance_sats: total } }) } + case 'wallet.ark-status': { + return res.json({ + result: { + available: true, + wallet_ready: true, + wallet: { network: walletState.ark_config.network }, + ark_info: { server: walletState.ark_config.ark_server }, + config: walletState.ark_config, + }, + }) + } + case 'wallet.ark-balance': { + return res.json({ + result: { + balance_sats: walletState.ark_sats, + spendable_sats: walletState.ark_sats, + pending_sats: walletState.ark_pending_sats, + onchain_sats: walletState.ark_onchain_sats, + }, + }) + } + case 'wallet.ark-address': { + const onchain = params?.onchain === true + const address = onchain ? 'tb1q' + randomHex(16) : 'tark1' + randomHex(28) + return res.json({ result: { address, onchain } }) + } + case 'wallet.ark-board': { + const amt = params?.amount_sats ?? walletState.ark_onchain_sats + walletState.ark_onchain_sats = Math.max(0, walletState.ark_onchain_sats - amt) + walletState.ark_sats += amt + return res.json({ result: { boarded_sats: amt } }) + } + case 'wallet.ark-offboard': { + const amt = walletState.ark_sats + walletState.ark_sats = 0 + walletState.ark_onchain_sats += amt + return res.json({ result: { offboarded_sats: amt } }) + } + case 'wallet.ark-send': { + const amt = params?.amount_sats || 1000 + walletState.ark_sats = Math.max(0, walletState.ark_sats - amt) + return res.json({ result: { sent: true, movement: { id: 1, effective_balance_sat: -amt } } }) + } + case 'wallet.ark-invoice': { + return res.json({ result: { invoice: 'lntbs' + randomHex(64) } }) + } + case 'wallet.ark-history': { + return res.json({ result: { transactions: [] } }) + } + case 'wallet.ark-configure': { + const cfg = { ...walletState.ark_config } + for (const key of ['network', 'ark_server', 'esplora']) { + if (typeof params?.[key] === 'string' && params[key].trim()) cfg[key] = params[key].trim() + } + walletState.ark_config = cfg + return res.json({ result: { config: cfg } }) + } case 'content.request-onchain': { const price = params?.price_sats || 500 return res.json({ @@ -3225,6 +3287,8 @@ app.post('/rpc/v1', (req, res) => { { type: 'receive', amount_sats: 50000, timestamp: new Date(Date.now() - 86400000).toISOString(), note: 'Minted from Lightning' }, { type: 'send', amount_sats: 5000, timestamp: new Date(Date.now() - 43200000).toISOString(), note: 'Sent ecash token' }, { type: 'receive', amount_sats: 10000, timestamp: new Date(Date.now() - 3600000).toISOString(), note: 'Redeemed token' }, + // Ark movement in the unified history shape (see wallet.ark-history / load_ark_txs) + { id: 'ark-1', tx_type: 'receive', type: 'receive', amount_sats: 75000, timestamp: new Date(Date.now() - 7200000).toISOString(), description: 'Received via Ark', note: 'Received via Ark', mint_url: '', peer: '', kind: 'ark' }, ], }, }) diff --git a/neode-ui/public/assets/img/app-icons/bark.png b/neode-ui/public/assets/img/app-icons/bark.png new file mode 100644 index 00000000..e9be9a0b Binary files /dev/null and b/neode-ui/public/assets/img/app-icons/bark.png differ diff --git a/neode-ui/public/catalog.json b/neode-ui/public/catalog.json index a3c3aaa3..ca1b6a22 100644 --- a/neode-ui/public/catalog.json +++ b/neode-ui/public/catalog.json @@ -298,6 +298,25 @@ ] } }, + { + "id": "barkd", + "title": "Ark Wallet", + "version": "0.3.0", + "description": "Ark protocol wallet daemon (barkd). Lets the node hold self-custodial off-chain bitcoin via an Ark server; the wallet talks to it over a local REST API. Signet by default while Ark matures.", + "icon": "/assets/img/app-icons/bark.png", + "author": "Second", + "category": "money", + "dockerImage": "146.59.87.168:3000/lfg2025/barkd:0.3.0", + "repoUrl": "https://gitlab.com/ark-bitcoin/bark", + "containerConfig": { + "ports": [ + "3535:3535" + ], + "volumes": [ + "/var/lib/archipelago/barkd:/data" + ] + } + }, { "id": "jellyfin", "title": "Jellyfin", diff --git a/neode-ui/scripts/download-app-icons.js b/neode-ui/scripts/download-app-icons.js index 6fa5ca2f..7cbb9d12 100755 --- a/neode-ui/scripts/download-app-icons.js +++ b/neode-ui/scripts/download-app-icons.js @@ -45,6 +45,10 @@ const customIconUrls = { 'fedimint': [ 'https://raw.githubusercontent.com/fedibtc/fedimint-ui/master/apps/router/public/favicon.svg', ], + // Official bark project avatar (Ark protocol wallet daemon) + 'bark': [ + 'https://gitlab.com/uploads/-/system/project/avatar/75519706/bark-smiling-square-white-2.jpg', + ], } const iconDir = path.join(__dirname, '../public/assets/img/app-icons') diff --git a/neode-ui/src/components/TransactionsModal.vue b/neode-ui/src/components/TransactionsModal.vue index 8d174364..a2a831af 100644 --- a/neode-ui/src/components/TransactionsModal.vue +++ b/neode-ui/src/components/TransactionsModal.vue @@ -52,7 +52,7 @@ {{ kindLabel(tx) }} @@ -90,7 +90,7 @@ interface WalletTransaction { label: string block_height: number // Which rail the transaction happened on; absent = onchain (older backends) - kind?: 'onchain' | 'lightning' | 'cashu' | 'fedimint' + kind?: 'onchain' | 'lightning' | 'cashu' | 'fedimint' | 'ark' } defineProps<{ @@ -114,6 +114,7 @@ function kindLabel(tx: WalletTransaction): string { if (tx.kind === 'lightning') return '⚡ Lightning' if (tx.kind === 'cashu') return 'Cashu' if (tx.kind === 'fedimint') return 'Fedimint' + if (tx.kind === 'ark') return 'Ark' return '' } diff --git a/neode-ui/src/components/WalletSettingsModal.vue b/neode-ui/src/components/WalletSettingsModal.vue index 38f5a666..57d403fe 100644 --- a/neode-ui/src/components/WalletSettingsModal.vue +++ b/neode-ui/src/components/WalletSettingsModal.vue @@ -148,11 +148,107 @@ Joining federations lands with the Fedimint client backend.
+ + ++ Ark holds self-custodial off-chain bitcoin via an Ark server. Funds stay recoverable on-chain even if the server disappears. +
+ Not installed + {{ arkStatus.config.network }} ++ Install the Ark Wallet app from the app store to enable Ark payments. +
+ + +Spendable
+{{ (arkBalance?.spendable_sats ?? 0).toLocaleString() }} sats
+Pending
+{{ (arkBalance?.pending_sats ?? 0).toLocaleString() }} sats
+On-chain
+{{ (arkBalance?.onchain_sats ?? 0).toLocaleString() }} sats
++ Applied when the Ark wallet is created — an existing wallet stays bound to its server. +
+