refactor(commons): extract CashuWalletOps to commons for amy reuse

Move the NIP-60/61 wallet orchestration layer (CashuWalletOps + its
result types: TokenEntry, MintQuoteStarted, MintCompleted, MeltCompleted,
SendTokenCompleted, RedeemCompleted, NutzapSent, RestoreOutcome,
MigrationResult, CreatedWallet, describeMintError) out of amethyst into
commons/jvmAndroid/cashu/ops.

The class already had zero Android dependencies — it takes signer,
publish, okHttpClient, secretFactory, and the NUT-13 counter callbacks as
constructor params. It lands in the jvmAndroid source set (not commonMain)
because it composes quartz's jvmAndroid CashuMintOperations/MintHttpClient
and uses ConcurrentHashMap. Both Android (amethyst) and the JVM CLI (amy)
can now drive the exact same wallet code path.

This is Extraction B of cli/plans/2026-05-28-cashu-cli.md. Android
callers (CashuWalletState, the wallet ViewModels, AccountViewModel)
updated to the new package; no behavioral change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011SapGdtAc1j7woifoCZ9fY
This commit is contained in:
Claude
2026-06-21 22:17:04 +00:00
parent 3df9f831b8
commit fc7db088b2
7 changed files with 19 additions and 13 deletions
@@ -20,6 +20,13 @@
*/
package com.vitorpamplona.amethyst.model.nip60Cashu
import com.vitorpamplona.amethyst.commons.cashu.ops.CashuWalletOps
import com.vitorpamplona.amethyst.commons.cashu.ops.MeltCompleted
import com.vitorpamplona.amethyst.commons.cashu.ops.NutzapSent
import com.vitorpamplona.amethyst.commons.cashu.ops.RestoreOutcome
import com.vitorpamplona.amethyst.commons.cashu.ops.SendTokenCompleted
import com.vitorpamplona.amethyst.commons.cashu.ops.TokenEntry
import com.vitorpamplona.amethyst.commons.cashu.ops.describeMintError
import com.vitorpamplona.amethyst.commons.relayClient.assemblers.CashuWalletFilterAssembler
import com.vitorpamplona.amethyst.commons.relayClient.assemblers.CashuWalletQueryState
import com.vitorpamplona.amethyst.model.AccountSettings
@@ -35,7 +35,7 @@ import kotlin.coroutines.cancellation.CancellationException
* via NUT-05 melt (`POST /v1/melt/quote/bolt11` + `POST /v1/melt/bolt11`).
*
* This is the "Redeem" button on a received cashu token preview — distinct
* from the NIP-60 wallet's own send-LN flow ([com.vitorpamplona.amethyst.model.nip60Cashu.CashuWalletOps.meltToLightning]),
* from the NIP-60 wallet's own send-LN flow ([com.vitorpamplona.amethyst.commons.cashu.ops.CashuWalletOps.meltToLightning]),
* which spends the user's stored proofs and rolls change back into the wallet.
* Here there is no wallet: the proofs come straight off the pasted token and
* any leftover stays with the mint.
@@ -40,6 +40,7 @@ import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.LocalPreferences
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.audio.VisualizerStyle
import com.vitorpamplona.amethyst.commons.cashu.ops.describeMintError
import com.vitorpamplona.amethyst.commons.model.LiveHiddenUsers
import com.vitorpamplona.amethyst.commons.model.emphChat.EphemeralChatChannel
import com.vitorpamplona.amethyst.commons.model.nip28PublicChats.PublicChatChannel
@@ -1009,8 +1010,7 @@ class AccountViewModel(
} catch (e: Exception) {
onError(
stringRes(com.vitorpamplona.amethyst.Amethyst.instance.appContext, R.string.nutzap_failed_title),
com.vitorpamplona.amethyst.model.nip60Cashu
.describeMintError(e),
describeMintError(e),
baseNote.author,
)
}
@@ -1042,8 +1042,7 @@ class AccountViewModel(
if (e is CancellationException) throw e
onError(
stringRes(com.vitorpamplona.amethyst.Amethyst.instance.appContext, R.string.nutzap_failed_title),
com.vitorpamplona.amethyst.model.nip60Cashu
.describeMintError(e),
describeMintError(e),
getUserIfExists(recipientPubKey),
)
}
@@ -21,12 +21,12 @@
package com.vitorpamplona.amethyst.ui.screen.loggedIn.wallet
import androidx.lifecycle.ViewModel
import com.vitorpamplona.amethyst.commons.cashu.ops.CashuWalletOps
import com.vitorpamplona.amethyst.commons.cashu.ops.MintQuoteStarted
import com.vitorpamplona.amethyst.commons.cashu.ops.TokenEntry
import com.vitorpamplona.amethyst.commons.cashu.ops.describeMintError
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.nip60Cashu.CashuWalletOps
import com.vitorpamplona.amethyst.model.nip60Cashu.CashuWalletState
import com.vitorpamplona.amethyst.model.nip60Cashu.MintQuoteStarted
import com.vitorpamplona.amethyst.model.nip60Cashu.TokenEntry
import com.vitorpamplona.amethyst.model.nip60Cashu.describeMintError
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.lightning.LnInvoiceUtil
import com.vitorpamplona.quartz.nip60Cashu.mintApi.MeltQuoteBolt11ResponseDto
@@ -23,9 +23,9 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.wallet
import androidx.compose.runtime.Immutable
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.commons.cashu.ops.describeMintError
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.nip60Cashu.CashuWalletState
import com.vitorpamplona.amethyst.model.nip60Cashu.describeMintError
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect
@@ -23,8 +23,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.wallet
import androidx.compose.runtime.Immutable
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.commons.cashu.ops.describeMintError
import com.vitorpamplona.amethyst.model.nip60Cashu.CashuWalletState
import com.vitorpamplona.amethyst.model.nip60Cashu.describeMintError
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.PayInvoiceMethod
@@ -47,7 +47,7 @@ import kotlinx.coroutines.launch
*
* Reuses the same funding primitives as [ReloadMintViewModel] —
* [CashuWalletState.rebalance] for a mint-to-mint move and
* [com.vitorpamplona.amethyst.model.nip60Cashu.CashuWalletOps.startMintFromLightning] /
* [com.vitorpamplona.amethyst.commons.cashu.ops.CashuWalletOps.startMintFromLightning] /
* `completeMintFromLightning` for an LN top-up — but without any of the
* zap-specific machinery (recipient, shared-mint intersection, fixed send
* amount + shortfall, terminal nutzap, fund-then-send atomicity). The user
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.amethyst.model.nip60Cashu
package com.vitorpamplona.amethyst.commons.cashu.ops
import com.vitorpamplona.quartz.nip01Core.core.Address
import com.vitorpamplona.quartz.nip01Core.core.Event