add sweepAll for Monero send-all; add copy buttons for accounts/subaddresses
This commit is contained in:
@@ -16,6 +16,7 @@ export {
|
||||
getFeeEstimate,
|
||||
getDaemonHeight,
|
||||
transfer,
|
||||
sweepAll,
|
||||
isWalletOpen,
|
||||
isConnected,
|
||||
listWallets,
|
||||
|
||||
@@ -423,6 +423,33 @@ export async function getDaemonHeight(
|
||||
}
|
||||
}
|
||||
|
||||
export async function sweepAll(
|
||||
state: MoneroWalletState,
|
||||
address: string,
|
||||
priority: number = 0,
|
||||
accountIndex: number = 0,
|
||||
): Promise<{ txHash: string; fee: string; amount: string }> {
|
||||
if (!state.wallet) throw new Error('Wallet RPC not started')
|
||||
console.log(
|
||||
`[monero] sweepAll: ${address} priority=${priority} account=${accountIndex}`,
|
||||
)
|
||||
const result = await rawRpc(state, 'sweep_all', {
|
||||
address,
|
||||
priority,
|
||||
account_index: accountIndex,
|
||||
get_tx_keys: true,
|
||||
get_tx_hex: false,
|
||||
get_tx_metadata: false,
|
||||
})
|
||||
const txHash = result.tx_hash_list?.[0] ?? ''
|
||||
const fee = result.fee_list?.[0]?.toString() ?? '0'
|
||||
const amount = result.amount_list?.[0]?.toString() ?? '0'
|
||||
console.log(
|
||||
`[monero] sweepAll result: tx_hash=${txHash} fee=${fee} amount=${amount}`,
|
||||
)
|
||||
return { txHash, fee, amount }
|
||||
}
|
||||
|
||||
export async function transfer(
|
||||
state: MoneroWalletState,
|
||||
address: string,
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
getDaemonHeight as walletGetDaemonHeight,
|
||||
getFeeEstimate as walletGetFeeEstimate,
|
||||
transfer as walletTransfer,
|
||||
sweepAll as walletSweepAll,
|
||||
isWalletOpen,
|
||||
isConnected,
|
||||
} from '../../lib/monero'
|
||||
@@ -231,6 +232,30 @@ export function createMoneroHandlers(state: {
|
||||
console.log('[rpc] moneroTransfer complete:', result.txHash)
|
||||
return result
|
||||
},
|
||||
moneroSweepAll: async ({
|
||||
address,
|
||||
priority,
|
||||
accountIndex,
|
||||
}: {
|
||||
address: string
|
||||
priority?: number
|
||||
accountIndex?: number
|
||||
}) => {
|
||||
console.log('[rpc] moneroSweepAll:', {
|
||||
address,
|
||||
priority,
|
||||
})
|
||||
if (!state.manager)
|
||||
throw new Error('Monero wallet RPC not started')
|
||||
const result = await walletSweepAll(
|
||||
state.manager,
|
||||
address,
|
||||
priority ?? 0,
|
||||
accountIndex ?? 0,
|
||||
)
|
||||
console.log('[rpc] moneroSweepAll complete:', result.txHash)
|
||||
return result
|
||||
},
|
||||
moneroGetTransferDetails: async ({ txid }: { txid: string }) => {
|
||||
console.log('[rpc] moneroGetTransferDetails:', txid)
|
||||
if (!state.manager)
|
||||
|
||||
@@ -179,6 +179,7 @@ export type MoneroRpcRequests = {
|
||||
moneroGetAccounts: { params: {}; response: MoneroAccountEntry[] }
|
||||
moneroListWallets: { params: {}; response: string[] }
|
||||
moneroTransfer: { params: { address: string; amount: string; priority?: number; accountIndex?: number }; response: MoneroSendResult }
|
||||
moneroSweepAll: { params: { address: string; priority?: number; accountIndex?: number }; response: MoneroSendResult }
|
||||
moneroGetTransferDetails: { params: { txid: string }; response: MoneroTransferDetails | null }
|
||||
moneroGetFeeEstimate: { params: {}; response: MoneroFeeEstimate | null }
|
||||
}
|
||||
|
||||
@@ -293,6 +293,21 @@ export const MoneroWallet = () => {
|
||||
return result
|
||||
}
|
||||
|
||||
const sendAll = async (
|
||||
address: string,
|
||||
priority?: number,
|
||||
accountIndex?: number,
|
||||
): Promise<MoneroSendResult> => {
|
||||
if (!electrobun.rpc) throw new Error('RPC not available')
|
||||
const result = await electrobun.rpc.request.moneroSweepAll({
|
||||
address,
|
||||
priority,
|
||||
accountIndex,
|
||||
})
|
||||
await refresh()
|
||||
return result
|
||||
}
|
||||
|
||||
return {
|
||||
get accountType() {
|
||||
return accountType
|
||||
@@ -396,6 +411,7 @@ export const MoneroWallet = () => {
|
||||
openWallet,
|
||||
refresh,
|
||||
send,
|
||||
sendAll,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,12 +18,19 @@
|
||||
import ArrowUp from '@lucide/svelte/icons/arrow-up'
|
||||
import Fingerprint from '@lucide/svelte/icons/fingerprint'
|
||||
import SettingsIcon from '@lucide/svelte/icons/settings'
|
||||
import Copy from '@lucide/svelte/icons/copy'
|
||||
import Loader from '$lib/components/loader.svelte'
|
||||
import QrCode from '$lib/components/qr.svelte'
|
||||
import { navigate } from 'sv-router/generated'
|
||||
|
||||
const w = moneroWallet
|
||||
|
||||
const copyToClipboard = async (text: string) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text)
|
||||
} catch {}
|
||||
}
|
||||
|
||||
let moneroWalletName = $state('')
|
||||
let moneroWalletPass = $state('')
|
||||
let moneroMnemonic = $state('')
|
||||
@@ -464,6 +471,11 @@
|
||||
class="mb-2 font-mono text-xs text-muted-foreground break-all">
|
||||
{account.primaryAddress}
|
||||
</p>
|
||||
<button
|
||||
onclick={() => copyToClipboard(account.primaryAddress)}
|
||||
class="text-xs text-muted-foreground hover:text-foreground flex items-center gap-1 transition-colors">
|
||||
<Copy size={12} /> Copy address
|
||||
</button>
|
||||
{#if account.subaddresses.length > 1}
|
||||
<div class="space-y-1 border-t border-border pt-2">
|
||||
<p class="text-xs text-muted-foreground mb-1">
|
||||
@@ -490,6 +502,11 @@
|
||||
class="font-mono text-muted-foreground/50 truncate mt-0.5">
|
||||
{sub.address}
|
||||
</p>
|
||||
<button
|
||||
onclick={() => copyToClipboard(sub.address)}
|
||||
class="mt-1 text-xs text-muted-foreground hover:text-foreground flex items-center gap-1 transition-colors">
|
||||
<Copy size={12} /> Copy
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
let recipient = $state('')
|
||||
let amountXmr = $state('')
|
||||
let sendAll = $state(false)
|
||||
let priority = $state('0')
|
||||
let accountIndex = $state(
|
||||
w.accounts.length > 0 ? String(w.accounts[0].index) : '0',
|
||||
@@ -57,8 +58,8 @@
|
||||
const canSend = $derived(
|
||||
recipient.startsWith('4') &&
|
||||
recipient.length >= 95 &&
|
||||
amountAtomic > 0n &&
|
||||
!exceedsBalance &&
|
||||
(sendAll || amountAtomic > 0n) &&
|
||||
(sendAll || !exceedsBalance) &&
|
||||
!sending,
|
||||
)
|
||||
|
||||
@@ -67,7 +68,13 @@
|
||||
sendError = ''
|
||||
sentResult = null
|
||||
try {
|
||||
const result = await w.send(
|
||||
const result = sendAll
|
||||
? await w.sendAll(
|
||||
recipient,
|
||||
parseInt(priority),
|
||||
parseInt(accountIndex),
|
||||
)
|
||||
: await w.send(
|
||||
recipient,
|
||||
amountAtomic.toString(),
|
||||
parseInt(priority),
|
||||
@@ -76,6 +83,7 @@
|
||||
sentResult = { txHash: result.txHash, fee: result.fee }
|
||||
recipient = ''
|
||||
amountXmr = ''
|
||||
sendAll = false
|
||||
} catch (e) {
|
||||
sendError = e instanceof Error ? e.message : 'Send failed'
|
||||
} finally {
|
||||
@@ -148,13 +156,21 @@
|
||||
step="0.000000000001"
|
||||
min="0"
|
||||
placeholder="0.0"
|
||||
bind:value={amountXmr} />
|
||||
bind:value={amountXmr}
|
||||
disabled={sendAll} />
|
||||
<span
|
||||
class="absolute right-3 top-1/2 -translate-y-1/2 text-xs text-muted-foreground">
|
||||
XMR
|
||||
</span>
|
||||
</div>
|
||||
{#if exceedsBalance}
|
||||
<label class="flex items-center gap-2 text-xs">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={sendAll}
|
||||
class="h-3.5 w-3.5" />
|
||||
Send all unlocked XMR (fee deducted automatically)
|
||||
</label>
|
||||
{#if exceedsBalance && !sendAll}
|
||||
<p class="text-xs text-red-500">
|
||||
Amount exceeds unlocked balance
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user