Type cashu-ts Amount correctly

This commit is contained in:
minibits-cash
2026-09-29 09:36:00 +02:00
parent 718029a6e5
commit a9f4a6e7a4
11 changed files with 28 additions and 16 deletions
+2 -1
View File
@@ -5,6 +5,7 @@ import {
flow, flow,
isAlive, isAlive,
} from 'mobx-state-tree' } from 'mobx-state-tree'
import { Amount } from '@cashu/cashu-ts'
import { withSetPropAction } from './helpers/withSetPropAction' import { withSetPropAction } from './helpers/withSetPropAction'
import { ProofModel, Proof, ProofRecord, ProofState } from './Proof' import { ProofModel, Proof, ProofRecord, ProofState } from './Proof'
import { TransactionData, TransactionStatus } from './Transaction' import { TransactionData, TransactionStatus } from './Transaction'
@@ -588,7 +589,7 @@ import {
} else { } else {
const node = ProofModel.create({ const node = ProofModel.create({
...proof, ...proof,
amount: Number(proof.amount), amount: Amount.from(proof.amount as any).toNumber(),
mintUrl: commitMintUrl, mintUrl: commitMintUrl,
tId: group.tId, tId: group.tId,
unit: reservation.unit, unit: reservation.unit,
+2 -2
View File
@@ -596,7 +596,7 @@ export const NfcPayScreen = observer(function NfcPayScreen({ route }: Props) {
log.trace('[handlePaymentRequest] decoded Cashu payment request', { pr }) log.trace('[handlePaymentRequest] decoded Cashu payment request', { pr })
// Validate basics // Validate basics
if (!pr.amount || Number(pr.amount) <= 0) { if (!pr.amount || pr.amount.toNumber() <= 0) {
throw new AppError(Err.VALIDATION_ERROR, 'Payment request has no valid amount') throw new AppError(Err.VALIDATION_ERROR, 'Payment request has no valid amount')
} }
@@ -616,7 +616,7 @@ export const NfcPayScreen = observer(function NfcPayScreen({ route }: Props) {
// setEncodedCashuPaymentRequest(encoded) // setEncodedCashuPaymentRequest(encoded)
if (pr.description) setMemo(pr.description) if (pr.description) setMemo(pr.description)
const requiredAmount = Number(pr.amount) const requiredAmount = pr.amount.toNumber()
const eligibleBalances = await getEligibleMintBalancesForCashu(pr, requiredAmount, unit) const eligibleBalances = await getEligibleMintBalancesForCashu(pr, requiredAmount, unit)
if (eligibleBalances.length === 0) { if (eligibleBalances.length === 0) {
+1 -1
View File
@@ -625,7 +625,7 @@ export const SendScreen = observer(function SendScreen({ route }: Props) {
setIsCashuPrWithDesc(true) setIsCashuPrWithDesc(true)
} }
const prAmount = pr.amount ? Number(pr.amount) : undefined const prAmount = pr.amount ? pr.amount.toNumber() : undefined
if (prAmount) { if (prAmount) {
setAmountToSend(`${numbro(prAmount / getCurrency(unitRef.current).precision).format({ setAmountToSend(`${numbro(prAmount / getCurrency(unitRef.current).precision).format({
+11 -1
View File
@@ -38,11 +38,20 @@ const isObj = function(v: unknown): v is object {
return typeof v === 'object' return typeof v === 'object'
} }
/**
* An amount as a number. cashu-ts (4.x) types proof, token and payment-request
* amounts as `Amount` objects, while our own models hold plain numbers; `Number()`
* on an `Amount` is deprecated and throws in cashu-ts v5.
*/
const toNumberAmount = function(amount: number | Amount | bigint | string): number {
return typeof amount === 'number' ? amount : Amount.from(amount).toNumber()
}
/** /**
* Sum the amounts of an array of proofs * Sum the amounts of an array of proofs
*/ */
const sumProofs = function(proofs: CashuProof[]): number { const sumProofs = function(proofs: CashuProof[]): number {
return proofs.reduce((acc: number, proof: CashuProof) => acc + Number(proof.amount), 0) return proofs.reduce((acc: number, proof: CashuProof) => acc + toNumberAmount(proof.amount as any), 0)
} }
const CASHU_URI_PREFIXES = [ const CASHU_URI_PREFIXES = [
@@ -839,6 +848,7 @@ export const CashuUtils = {
extractEncodedCashuPaymentRequest, extractEncodedCashuPaymentRequest,
getProofsAmount, getProofsAmount,
getMintsFromToken, getMintsFromToken,
toNumberAmount,
findExactMatch, findExactMatch,
findMinExcess, findMinExcess,
getProofsToSend, getProofsToSend,
+2 -1
View File
@@ -1,4 +1,5 @@
import {isAlive} from 'mobx-state-tree' import {isAlive} from 'mobx-state-tree'
import {Amount} from '@cashu/cashu-ts'
import {Proof, ProofState} from '../../models/Proof' import {Proof, ProofState} from '../../models/Proof'
import {CashuProof} from '../cashu/cashuUtils' import {CashuProof} from '../cashu/cashuUtils'
import {TransactionStatus} from '../../models/Transaction' import {TransactionStatus} from '../../models/Transaction'
@@ -225,7 +226,7 @@ export const commitReservation = function (
// binding can't bind non-primitive objects to an INTEGER column and // binding can't bind non-primitive objects to an INTEGER column and
// would silently drop the row, leaving the proof in MST but absent // would silently drop the row, leaving the proof in MST but absent
// from the database (lost on the next restart). // from the database (lost on the next restart).
const amount = typeof proof.amount === 'number' ? proof.amount : Number(proof.amount) const amount = typeof proof.amount === 'number' ? proof.amount : Amount.from(proof.amount as any).toNumber()
batch.push([ batch.push([
`INSERT OR REPLACE INTO proofs (id, amount, secret, C, dleq_r, dleq_s, dleq_e, unit, tId, mintUrl, state, updatedAt) `INSERT OR REPLACE INTO proofs (id, amount, secret, C, dleq_r, dleq_s, dleq_e, unit, tId, mintUrl, state, updatedAt)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
@@ -381,7 +381,7 @@ async function finalize(
transactionId, transactionId,
) )
const receivedAmount = receivedProofs.reduce((acc, p) => acc + Number(p.amount), 0) const receivedAmount = CashuUtils.getProofsAmount(receivedProofs)
const outputToken = getEncodedToken({ const outputToken = getEncodedToken({
mint: mintUrl, mint: mintUrl,
proofs: normalizeProofAmounts(receivedProofs), proofs: normalizeProofAmounts(receivedProofs),
@@ -140,7 +140,7 @@ const handleClaimTask = async function (params: {
const result: TransactionTaskResult = await receiveTask( const result: TransactionTaskResult = await receiveTask(
decodedToken, decodedToken,
Number(tokenInfo.amount), tokenInfo.amount.toNumber(),
tokenInfo.memo || 'Received to Lightning address', tokenInfo.memo || 'Received to Lightning address',
encodedToken, encodedToken,
) )
@@ -402,7 +402,7 @@ const handleReceivedEventTask = async function (encryptedEvent: NostrEvent): Pro
if (incoming.type === IncomingDataType.CASHU) { if (incoming.type === IncomingDataType.CASHU) {
const tokenInfo = getTokenMetadata(incoming.encoded) const tokenInfo = getTokenMetadata(incoming.encoded)
const amountToReceive = Number(tokenInfo.amount) const amountToReceive = tokenInfo.amount.toNumber()
const memo = tokenInfo.memo || 'Received over Nostr' const memo = tokenInfo.memo || 'Received over Nostr'
const {unit, mint: mintUrl} = tokenInfo const {unit, mint: mintUrl} = tokenInfo
@@ -70,7 +70,7 @@ import {
NUT20_RECOVERY_GAP_LIMIT, NUT20_RECOVERY_GAP_LIMIT,
} from '../../cashu/nut20' } from '../../cashu/nut20'
import {capMintAmount, mintableAmount} from './onchainAmounts' import {capMintAmount, mintableAmount} from './onchainAmounts'
import {CashuProof} from '../../cashu/cashuUtils' import {CashuProof, CashuUtils} from '../../cashu/cashuUtils'
import {MintUnit} from '../currency' import {MintUnit} from '../currency'
import {WalletUtils} from '../utils' import {WalletUtils} from '../utils'
import {sendTopupNotification} from '../notifications' import {sendTopupNotification} from '../notifications'
@@ -548,7 +548,7 @@ async function _mintAvailable(
}) })
} }
const mintedAmount = proofs.reduce((acc, p) => acc + Number(p.amount), 0) const mintedAmount = CashuUtils.getProofsAmount(proofs)
const currentSpendable = proofsStore.getUnitBalance(unit)?.unitBalance ?? 0 const currentSpendable = proofsStore.getUnitBalance(unit)?.unitBalance ?? 0
const balanceAfter = currentSpendable + mintedAmount const balanceAfter = currentSpendable + mintedAmount
@@ -318,7 +318,7 @@ async function execute(prepared: PreparedReceiveData): Promise<CompletedTransact
tx.id, tx.id,
) )
const receivedAmount = proofs.reduce((acc, p) => acc + Number(p.amount), 0) const receivedAmount = CashuUtils.getProofsAmount(proofs)
const outputToken = getEncodedToken({ const outputToken = getEncodedToken({
mint: prepared.mintUrl, mint: prepared.mintUrl,
proofs: normalizeProofAmounts(proofs), proofs: normalizeProofAmounts(proofs),
@@ -56,7 +56,7 @@ import {
isPrepared, isPrepared,
isReverted, isReverted,
} from '../../../models/TransactionStates' } from '../../../models/TransactionStates'
import {CashuProof} from '../../cashu/cashuUtils' import {CashuProof, CashuUtils} from '../../cashu/cashuUtils'
import {LightningUtils} from '../../lightning/lightningUtils' import {LightningUtils} from '../../lightning/lightningUtils'
import {MintUnit, formatCurrency, getCurrency} from '../currency' import {MintUnit, formatCurrency, getCurrency} from '../currency'
import {NostrEvent} from '../../nostrService' import {NostrEvent} from '../../nostrService'
@@ -533,7 +533,7 @@ async function _finalizePaid(tx: Transaction): Promise<CompletedTransaction> {
}) })
const currentSpendable = proofsStore.getUnitBalance(unit)?.unitBalance ?? 0 const currentSpendable = proofsStore.getUnitBalance(unit)?.unitBalance ?? 0
const proofsAmount = proofs.reduce((acc, p) => acc + Number(p.amount), 0) const proofsAmount = CashuUtils.getProofsAmount(proofs)
const balanceAfter = currentSpendable + proofsAmount const balanceAfter = currentSpendable + proofsAmount
const txData = _parseData(tx) const txData = _parseData(tx)
+2 -2
View File
@@ -10,7 +10,7 @@ import { TransactionTaskResult } from '../walletService'
import { ProofReservation } from './proofReservation' import { ProofReservation } from './proofReservation'
import { WalletUtils } from './utils' import { WalletUtils } from './utils'
import { MintUnit } from './currency' import { MintUnit } from './currency'
import { Token, getEncodedToken, normalizeProofAmounts } from '@cashu/cashu-ts' import { Token, getEncodedToken, normalizeProofAmounts, sumProofs } from '@cashu/cashu-ts'
const { const {
mintsStore, mintsStore,
@@ -99,7 +99,7 @@ try {
// simulated: the original pending proofs were PENDING (contribute 0 to // simulated: the original pending proofs were PENDING (contribute 0 to
// UNSPENT pool); moving them to SPENT changes nothing. The new fresh // UNSPENT pool); moving them to SPENT changes nothing. The new fresh
// proofs added as UNSPENT raise the spendable balance. // proofs added as UNSPENT raise the spendable balance.
const receivedAmount = receivedProofs.reduce((sum, p) => sum + Number(p.amount), 0) const receivedAmount = sumProofs(receivedProofs).toNumber()
const currentSpendable = proofsStore.getUnitBalance(unit)?.unitBalance ?? 0 const currentSpendable = proofsStore.getUnitBalance(unit)?.unitBalance ?? 0
const balanceAfter = currentSpendable + receivedAmount const balanceAfter = currentSpendable + receivedAmount