diff --git a/apps/machine/src/stores/atm.ts b/apps/machine/src/stores/atm.ts index fd00df2..bb5bc8b 100644 --- a/apps/machine/src/stores/atm.ts +++ b/apps/machine/src/stores/atm.ts @@ -306,6 +306,22 @@ export const useAtmStore = defineStore('atm', () => { // screen (raw lnurlw, spent by this call) or the session opened at entry // (hit-keyed steps, no p/c). type BoltCardSource = { lnurlw: string } | { session: CardSession } + // Electron IPC structured-clones its arguments and rejects Vue reactive + // proxies with "An object could not be cloned". `loadedBoltCard` is a ref, + // so anything reached through it is a proxy — copy the steps field by field + // into plain objects before they cross the bridge. + const plainWithdrawStep = (w: NonNullable) => ({ + callback: w.callback, + k1: w.k1, + minWithdrawable: w.minWithdrawable, + maxWithdrawable: w.maxWithdrawable, + }) + const plainPayStep = (p: CardSession['pay']) => ({ + callback: p.callback, + minSendable: p.minSendable, + maxSendable: p.maxSendable, + metadata: p.metadata, + }) // Access-control gate config (ADR-003). Defaults disabled → the machine's // `locked` state bypasses straight to `idle` (behaviour identical to no gate). // Populated from RuntimeConfig.accessControl in initializeForProduction. @@ -673,7 +689,7 @@ export const useAtmStore = defineStore('atm', () => { 'session' in source ? source.session.withdraw ? await api.withdrawWithSession({ - withdraw: source.session.withdraw, + withdraw: plainWithdrawStep(source.session.withdraw), bolt11: invoice, amountMsat, }) @@ -713,7 +729,7 @@ export const useAtmStore = defineStore('atm', () => { const api = window.electronAPI! const res = 'session' in source - ? await api.resolveSessionInvoice({ pay: source.session.pay, amountMsat }) + ? await api.resolveSessionInvoice({ pay: plainPayStep(source.session.pay), amountMsat }) : await api.resolveCardInvoice({ lnurlw: source.lnurlw, amountMsat }) if (!res.ok || !res.bolt11) { boltCardProcessing.value = false