Upgrade to cashu-ts v4

This commit is contained in:
minibits-cash
2026-05-11 22:33:22 +02:00
parent 86f4525bf4
commit 2e4d9328bb
20 changed files with 200 additions and 203 deletions

View File

@@ -22,7 +22,7 @@
"machine-translate": "npx @inlang/cli machine translate --project minibits.inlang" "machine-translate": "npx @inlang/cli machine translate --project minibits.inlang"
}, },
"dependencies": { "dependencies": {
"@cashu/cashu-ts": "^3.6.1", "@cashu/cashu-ts": "^4.2.1",
"@fortawesome/fontawesome-svg-core": "^7.1.0", "@fortawesome/fontawesome-svg-core": "^7.1.0",
"@fortawesome/free-brands-svg-icons": "^7.1.0", "@fortawesome/free-brands-svg-icons": "^7.1.0",
"@fortawesome/free-regular-svg-icons": "^7.1.0", "@fortawesome/free-regular-svg-icons": "^7.1.0",
@@ -41,7 +41,7 @@
"@react-navigation/native": "^7.1.18", "@react-navigation/native": "^7.1.18",
"@react-navigation/native-stack": "^7.3.27", "@react-navigation/native-stack": "^7.3.27",
"@react-navigation/stack": "^7.4.9", "@react-navigation/stack": "^7.4.9",
"@scure/bip32": "^2.0.1", "@scure/bip32": "^2.2.0",
"@scure/bip39": "2.0.1", "@scure/bip39": "2.0.1",
"@sentry/react-native": "^7.7.0", "@sentry/react-native": "^7.7.0",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",

View File

@@ -1,17 +1,17 @@
diff --git a/node_modules/@scure/bip32/index.js b/node_modules/@scure/bip32/index.js diff --git a/node_modules/@scure/bip32/index.js b/node_modules/@scure/bip32/index.js
index 935277b..361161a 100644 index 8e49612..d54da85 100644
--- a/node_modules/@scure/bip32/index.js --- a/node_modules/@scure/bip32/index.js
+++ b/node_modules/@scure/bip32/index.js +++ b/node_modules/@scure/bip32/index.js
@@ -23,6 +23,7 @@ import { ripemd160 } from '@noble/hashes/legacy.js'; @@ -28,6 +28,7 @@ import { ripemd160 } from '@noble/hashes/legacy.js';
import { sha256, sha512 } from '@noble/hashes/sha2.js'; import { sha256, sha512 } from '@noble/hashes/sha2.js';
import { abytes, concatBytes, createView } from '@noble/hashes/utils.js'; import { abytes, concatBytes, createView } from '@noble/hashes/utils.js';
import { createBase58check } from '@scure/base'; import { createBase58check } from '@scure/base';
+import quickCrypto from 'react-native-quick-crypto'; +import quickCrypto from 'react-native-quick-crypto';
const Point = secp.Point; const Point = /* @__PURE__ */ (() => secp.Point)();
const { Fn } = Point; const Fn = /* @__PURE__ */ (() => Point.Fn)();
const base58check = createBase58check(sha256); const base58check = /* @__PURE__ */ createBase58check(sha256);
@@ -87,7 +88,8 @@ export class HDKey { @@ -102,7 +103,8 @@ export class HDKey {
throw new Error('HDKey: seed length must be between 128 and 512 bits; 256 bits is advised, got ' + throw new RangeError('HDKey: seed length must be between 128 and 512 bits; 256 bits is advised, got ' +
seed.length); seed.length);
} }
- const I = hmac(sha512, MASTER_SECRET, seed); - const I = hmac(sha512, MASTER_SECRET, seed);
@@ -20,13 +20,13 @@ index 935277b..361161a 100644
const privateKey = I.slice(0, 32); const privateKey = I.slice(0, 32);
const chainCode = I.slice(32); const chainCode = I.slice(32);
return new HDKey({ versions, chainCode, privateKey }); return new HDKey({ versions, chainCode, privateKey });
@@ -206,7 +208,8 @@ export class HDKey { @@ -225,7 +227,8 @@ export class HDKey {
// Normal child: serP(point(kpar)) || ser32(index) // Normal child: serP(point(kpar)) || ser32(index)
data = concatBytes(this._publicKey, data); data = concatBytes(this._publicKey, data);
} }
- const I = hmac(sha512, this.chainCode, data); - const out = _I || hmac(sha512, this.chainCode, data);
+ //const I = hmac(sha512, this.chainCode, data); + //const out = _I || hmac(sha512, this.chainCode, data);
+ const I = new Uint8Array(quickCrypto.createHmac('sha512', this.chainCode).update(data).digest()); + const out = _I || new Uint8Array(quickCrypto.createHmac('sha512', this.chainCode).update(data).digest());
const childTweak = I.slice(0, 32); abytes(out, 64);
const chainCode = I.slice(32); const childTweak = out.slice(0, 32);
if (!secp.utils.isValidSecretKey(childTweak)) { const chainCode = out.slice(32);

View File

@@ -15,13 +15,13 @@ import { MintUnit, MintUnits } from '../services/wallet/currency'
import { getRootStore } from './helpers/getRootStore' import { getRootStore } from './helpers/getRootStore'
import { generateId } from '../utils/utils' import { generateId } from '../utils/utils'
import { Proof } from './Proof' import { Proof } from './Proof'
import { CashuProof, CashuUtils } from '../services/cashu/cashuUtils' import { CashuProof, CashuUtils, StoredMeltPreview } from '../services/cashu/cashuUtils'
// Helper function to serialize MeltPreview and convert BigInt to string function serializeMeltPreview(meltPreview: MeltPreview): StoredMeltPreview {
function serializeMeltPreview(meltPreview: MeltPreview): any { return {
return JSON.parse(JSON.stringify(meltPreview, (_key, value) => keysetId: meltPreview.keysetId,
typeof value === 'bigint' ? value.toString() : value outputData: CashuUtils.serializeOutputData(meltPreview.outputData),
)) }
} }
export type MintBalance = { export type MintBalance = {
@@ -60,7 +60,7 @@ const InFlightRequestModel = types.model('InFlightRequest', {
const MeltCounterValueModel = types.model('MeltCounterValue', { const MeltCounterValueModel = types.model('MeltCounterValue', {
transactionId: types.number, transactionId: types.number,
counterAtMelt: types.number, // the counter value when melt started (kept for backward compatibility) counterAtMelt: types.number, // the counter value when melt started (kept for backward compatibility)
meltPreview: types.maybe(types.frozen<MeltPreview>()), // v3.x MeltPreview object for recovery meltPreview: types.maybe(types.frozen<StoredMeltPreview>()),
createdAt: types.optional(types.Date, () => new Date()), // optional: when it was added createdAt: types.optional(types.Date, () => new Date()), // optional: when it was added
}) })
@@ -155,13 +155,10 @@ export const MintProofsCounterModel = types
return self.meltCounterValues.get(key)!.counterAtMelt return self.meltCounterValues.get(key)!.counterAtMelt
} }
// Serialize MeltPreview to handle BigInt values
const serializableMeltPreview = meltPreview ? serializeMeltPreview(meltPreview) : undefined
self.meltCounterValues.set(key, { self.meltCounterValues.set(key, {
transactionId, transactionId,
counterAtMelt: self.counter, counterAtMelt: self.counter,
meltPreview: serializableMeltPreview as any, meltPreview: meltPreview ? serializeMeltPreview(meltPreview) : undefined,
createdAt: new Date(), createdAt: new Date(),
}) })

View File

@@ -205,6 +205,7 @@ import {
} else { } else {
proofNode = ProofModel.create({ proofNode = ProofModel.create({
...proof, ...proof,
amount: Number(proof.amount),
mintUrl, mintUrl,
tId, tId,
unit, unit,

View File

@@ -2,6 +2,7 @@ import {Instance, SnapshotOut, types, flow, getRoot, getSnapshot} from 'mobx-sta
import { import {
Mint as CashuMint, Mint as CashuMint,
Wallet as CashuWallet, Wallet as CashuWallet,
KeyChain as CashuKeyChain,
MeltQuoteBolt11Response, MeltQuoteBolt11Response,
setGlobalRequestOptions, setGlobalRequestOptions,
type MintKeys, type MintKeys,
@@ -337,13 +338,17 @@ export const WalletStoreModel = types
const newSeedWallet = new CashuWallet(cashuMint, { const newSeedWallet = new CashuWallet(cashuMint, {
unit, unit,
keys: mintInstance.keys,
keysets: mintInstance.keysets,
mintInfo: mintInstance.mintInfo,
keysetId: walletKeys.id, keysetId: walletKeys.id,
bip39seed: seed bip39seed: seed
}) })
if (mintInstance.mintInfo && mintInstance.keysets?.length && mintInstance.keys?.length) {
const keychainCache = CashuKeyChain.mintToCacheDTO(mintUrl, [...mintInstance.keysets], [...mintInstance.keys])
newSeedWallet.loadMintFromCache(mintInstance.mintInfo, keychainCache)
} else {
yield newSeedWallet.loadMint()
}
self.seedWallets.push(newSeedWallet) self.seedWallets.push(newSeedWallet)
log.trace('[WalletStore.getWallet]', 'Returning NEW cashuWallet instance with seed', {mintUrl}) log.trace('[WalletStore.getWallet]', 'Returning NEW cashuWallet instance with seed', {mintUrl})
@@ -363,13 +368,16 @@ export const WalletStoreModel = types
const newWallet = new CashuWallet(cashuMint, { const newWallet = new CashuWallet(cashuMint, {
unit, unit,
keys: mintInstance.keys,
keysets: mintInstance.keysets,
mintInfo: mintInstance.mintInfo,
keysetId: walletKeys.id, keysetId: walletKeys.id,
bip39seed: undefined
}) })
if (mintInstance.mintInfo && mintInstance.keysets?.length && mintInstance.keys?.length) {
const keychainCache = CashuKeyChain.mintToCacheDTO(mintUrl, [...mintInstance.keysets], [...mintInstance.keys])
newWallet.loadMintFromCache(mintInstance.mintInfo, keychainCache)
} else {
yield newWallet.loadMint()
}
self.wallets.push(newWallet) self.wallets.push(newWallet)
log.trace('[WalletStore.getWallet]', 'Returning NEW cashuWallet instance', {mintUrl}) log.trace('[WalletStore.getWallet]', 'Returning NEW cashuWallet instance', {mintUrl})
@@ -1032,63 +1040,6 @@ export const WalletStoreModel = types
) )
} }
}), }),
recoverMeltQuoteChange: flow(function* recoverMeltQuoteChange(
mintUrl: string,
transaction: Transaction
) {
try {
const mintInstance = self.getMintModelInstance(mintUrl)
const transactionId = transaction.id
if(!mintInstance || !transaction.keysetId) {
throw new AppError(Err.VALIDATION_ERROR, 'Missing mint instance or keysetId', {mintUrl, transactionId})
}
const currentCounter = mintInstance.getProofsCounterByKeysetId!(transaction.keysetId)
const meltCounterValue = currentCounter.getMeltCounterValue(transaction.id)
if(!meltCounterValue || !meltCounterValue.meltPreview) {
throw new AppError(Err.VALIDATION_ERROR, 'Change already claimed - melt data not available for this transaction', {mintUrl, transactionId})
}
const meltPreview: MeltPreview = meltCounterValue.meltPreview
if(!meltPreview) {
throw new AppError(Err.VALIDATION_ERROR, 'MeltPreview not found - this transaction may be from an older version', {mintUrl, transactionId})
}
const cashuWallet: CashuWallet = yield self.getWallet(
mintUrl,
transaction.unit,
{
withSeed: true,
keysetId: transaction.keysetId
}
)
// Use completeMelt with the stored MeltPreview to recover change
const {change}: MeltProofsResponse = yield cashuWallet.completeMelt(meltPreview)
currentCounter.removeMeltCounterValue(transactionId)
log.info('[recoverMeltQuoteChange]', {change})
return change
} catch (e: any) {
let message = 'The mint could not return change from a melt quote.'
if (isOnionMint(mintUrl)) message += TorVPNSetupInstructions;
throw new AppError(
Err.MINT_ERROR,
message,
{
message: e.message,
caller: 'recoverMeltQuoteChange',
mintUrl,
}
)
}
}),
restore: flow(function* restore( restore: flow(function* restore(
mintUrl: string, mintUrl: string,
seed: Uint8Array, seed: Uint8Array,

View File

@@ -36,7 +36,7 @@ import { ContactsStoreSnapshot } from '../models/ContactsStore'
import { MintsStoreSnapshot } from '../models/MintsStore' import { MintsStoreSnapshot } from '../models/MintsStore'
import { ResultModalInfo } from './Wallet/ResultModalInfo' import { ResultModalInfo } from './Wallet/ResultModalInfo'
import { verticalScale } from '@gocodingnow/rn-size-matters' import { verticalScale } from '@gocodingnow/rn-size-matters'
import { Token, getEncodedToken } from '@cashu/cashu-ts' import { Token, getEncodedToken, normalizeProofAmounts } from '@cashu/cashu-ts'
import { StaticScreenProps, useNavigation } from '@react-navigation/native' import { StaticScreenProps, useNavigation } from '@react-navigation/native'
const OPTIMIZE_FROM_PROOFS_COUNT = 10 const OPTIMIZE_FROM_PROOFS_COUNT = 10
@@ -262,7 +262,7 @@ export const ExportBackupScreen = function ExportBackup({ route }: Props) {
const tokenByKeysetId: Token = { const tokenByKeysetId: Token = {
mint, mint,
proofs: proofsToExport, proofs: normalizeProofAmounts(proofsToExport),
unit: proofsByKeysetId[0].unit unit: proofsByKeysetId[0].unit
} }

View File

@@ -429,7 +429,7 @@ function NutsCard(props: {info: GetInfoResponse}) {
continue; continue;
} }
if ('disabled' in info && info.disabled === false) { // detailed if ('disabled' in info && info.disabled === false) { // detailed
supportedNutsDetailed.push([nut, info]) supportedNutsDetailed.push([nut, info as unknown as DetailedNutInfo])
} else if ('supported' in info && typeof info.supported !== 'undefined') { // simple } else if ('supported' in info && typeof info.supported !== 'undefined') { // simple
nutsSimple.push([nut, !!info.supported]) nutsSimple.push([nut, !!info.supported])
} else { } else {
@@ -567,14 +567,20 @@ function getMintLimits(info: GetInfoResponse) {
console.log('runs') console.log('runs')
for (const method of info.nuts['4'].methods) { for (const method of info.nuts['4'].methods) {
if ((typeof method.min_amount !== 'undefined' || typeof method.max_amount !== 'undefined') && method.unit === 'sat') { if ((typeof method.min_amount !== 'undefined' || typeof method.max_amount !== 'undefined') && method.unit === 'sat') {
mintSats = { min: method.min_amount, max: method.max_amount } mintSats = {
min: method.min_amount !== undefined ? Number(method.min_amount) : undefined,
max: method.max_amount !== undefined ? Number(method.max_amount) : undefined,
}
break; break;
} }
} }
for (const method of info.nuts['5'].methods) { for (const method of info.nuts['5'].methods) {
if ((typeof method.min_amount !== 'undefined' || typeof method.max_amount !== 'undefined') && method.unit === 'sat') { if ((typeof method.min_amount !== 'undefined' || typeof method.max_amount !== 'undefined') && method.unit === 'sat') {
meltSats = { min: method.min_amount, max: method.max_amount } meltSats = {
min: method.min_amount !== undefined ? Number(method.min_amount) : undefined,
max: method.max_amount !== undefined ? Number(method.max_amount) : undefined,
}
break; break;
} }
} }

View File

@@ -4,7 +4,7 @@ import {
View, View,
TextStyle, TextStyle,
} from 'react-native' } from 'react-native'
import { PaymentRequest as CashuPaymentRequest, MeltQuoteBolt11Response, MeltQuoteResponse, PaymentRequestTransportType, decodePaymentRequest, getDecodedToken, getTokenMetadata } from '@cashu/cashu-ts' import { PaymentRequest as CashuPaymentRequest, MeltQuoteBolt11Response, PaymentRequestTransportType, decodePaymentRequest, getDecodedToken, getTokenMetadata } from '@cashu/cashu-ts'
import NfcManager, { Ndef, NfcEvents } from 'react-native-nfc-manager' import NfcManager, { Ndef, NfcEvents } from 'react-native-nfc-manager'
import { colors, spacing, typography, useThemeColor } from '../theme' import { colors, spacing, typography, useThemeColor } from '../theme'
import EventEmitter from '../utils/eventEmitter' import EventEmitter from '../utils/eventEmitter'
@@ -400,7 +400,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 || pr.amount <= 0) { if (!pr.amount || Number(pr.amount) <= 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')
} }
@@ -420,7 +420,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 = pr.amount const requiredAmount = Number(pr.amount)
const eligibleBalances = await getEligibleMintBalancesForCashu(pr, requiredAmount, unit) const eligibleBalances = await getEligibleMintBalancesForCashu(pr, requiredAmount, unit)
if (eligibleBalances.length === 0) { if (eligibleBalances.length === 0) {
@@ -678,14 +678,14 @@ export const NfcPayScreen = observer(function NfcPayScreen({ route }: Props) {
setInvoice(decoded) setInvoice(decoded)
setInvoiceExpiry(expiresAt) setInvoiceExpiry(expiresAt)
setMeltQuote(quote) setMeltQuote(quote)
setAmountToPay(formatDisplayAmount(quote.amount, unitRef.current)) setAmountToPay(formatDisplayAmount(quote.amount.toNumber(), unitRef.current))
if (description) setMemo(description) if (description) setMemo(description)
setNfcInfo('Paying Lightning invoice...') setNfcInfo('Paying Lightning invoice...')
const result = await WalletTask.transferQueueAwaitable( const result = await WalletTask.transferQueueAwaitable(
balanceToUse, balanceToUse,
quote.amount, quote.amount.toNumber(),
unitRef.current, unitRef.current,
quote, quote,
description ?? '', description ?? '',

View File

@@ -136,7 +136,8 @@ export const ReceiveScreen = observer(function ReceiveScreen({ route }: Props) {
// keysetsV2 support // keysetsV2 support
const tokenInfo = getTokenMetadata(encoded) const tokenInfo = getTokenMetadata(encoded)
const {amount, unit, memo, mint: mintUrl} = tokenInfo const {amount: rawAmount, unit, memo, mint: mintUrl} = tokenInfo
const amount = Number(rawAmount)
if(!unit) { if(!unit) {
throw new AppError(Err.VALIDATION_ERROR, translate("decodedMissingCurrencyUnit", { unit: CurrencyCode.SAT })) throw new AppError(Err.VALIDATION_ERROR, translate("decodedMissingCurrencyUnit", { unit: CurrencyCode.SAT }))

View File

@@ -618,8 +618,10 @@ export const SendScreen = observer(function SendScreen({ route }: Props) {
setIsCashuPrWithDesc(true) setIsCashuPrWithDesc(true)
} }
if (pr.amount) { const prAmount = pr.amount ? Number(pr.amount) : undefined
setAmountToSend(`${numbro(pr.amount / getCurrency(unitRef.current).precision).format({
if (prAmount) {
setAmountToSend(`${numbro(prAmount / getCurrency(unitRef.current).precision).format({
thousandSeparated: true, thousandSeparated: true,
mantissa: getCurrency(unitRef.current).mantissa, mantissa: getCurrency(unitRef.current).mantissa,
})}`) })}`)
@@ -646,22 +648,22 @@ export const SendScreen = observer(function SendScreen({ route }: Props) {
withEnoughBalance = availableBalances.filter(balance => { withEnoughBalance = availableBalances.filter(balance => {
const unitBalance = balance.balances[unitRef.current] const unitBalance = balance.balances[unitRef.current]
if(!pr.amount) return balance if(!prAmount) return balance
if(pr.amount > 0 && unitBalance && unitBalance >= pr.amount) return balance if(prAmount > 0 && unitBalance && unitBalance >= prAmount) return balance
return null return null
}) })
if (pr.amount && withEnoughBalance.length === 0) { if (prAmount && withEnoughBalance.length === 0) {
dispatch({ type: 'SET_INFO', message: `Not enough balance to pay this payment request. Required: ${pr.amount} ${unitRef.current}.`}) dispatch({ type: 'SET_INFO', message: `Not enough balance to pay this payment request. Required: ${prAmount} ${unitRef.current}.`})
//infoMessage(`Not enough balance to pay this payment request. Required: ${pr.amount} ${unitRef.current}.`) //infoMessage(`Not enough balance to pay this payment request. Required: ${prAmount} ${unitRef.current}.`)
return return
//throw new AppError(Err.VALIDATION_ERROR, `Not enough balance to pay this payment request. Required: ${pr.amount} ${unitRef.current}.`) //throw new AppError(Err.VALIDATION_ERROR, `Not enough balance to pay this payment request. Required: ${prAmount} ${unitRef.current}.`)
} }
log.trace('[handlePaymentRequest] available mint balances for this payment request', {availableBalances, withEnoughBalance}) log.trace('[handlePaymentRequest] available mint balances for this payment request', {availableBalances, withEnoughBalance})
} else { } else {
withEnoughBalance = (pr.amount && pr.amount > 0) withEnoughBalance = (prAmount && prAmount > 0)
? proofsStore.getMintBalancesWithEnoughBalance(pr.amount, unitRef.current) ? proofsStore.getMintBalancesWithEnoughBalance(prAmount, unitRef.current)
: proofsStore.getMintBalancesWithUnit(unitRef.current) : proofsStore.getMintBalancesWithUnit(unitRef.current)
} }

View File

@@ -514,14 +514,14 @@ export const TransferScreen = observer(function TransferScreen({ route }: Props)
meltQuoteRef.current = quote meltQuoteRef.current = quote
// Format amount for display // Format amount for display
const displayAmount = numbro(quote.amount / getCurrency(unitRef.current).precision).format({ const displayAmount = numbro(quote.amount.toNumber() / getCurrency(unitRef.current).precision).format({
thousandSeparated: true, thousandSeparated: true,
mantissa: getCurrency(unitRef.current).mantissa, mantissa: getCurrency(unitRef.current).mantissa,
}) })
setAmountToTransfer(displayAmount) setAmountToTransfer(displayAmount)
// Check total required balance (amount + fee reserve) // Check total required balance (amount + fee reserve)
const totalRequired = quote.amount + quote.fee_reserve const totalRequired = quote.amount.toNumber() + quote.fee_reserve.toNumber()
const availableBalances = proofsStore.getMintBalancesWithEnoughBalance(totalRequired, unitRef.current) const availableBalances = proofsStore.getMintBalancesWithEnoughBalance(totalRequired, unitRef.current)
if (availableBalances.length === 0) { if (availableBalances.length === 0) {
@@ -934,7 +934,7 @@ export const TransferScreen = observer(function TransferScreen({ route }: Props)
{encodedInvoice && (meltQuote?.fee_reserve || finalFee) ? ( {encodedInvoice && (meltQuote?.fee_reserve || finalFee) ? (
<FeeBadge <FeeBadge
currencyCode={getCurrency(unitRef.current).code} currencyCode={getCurrency(unitRef.current).code}
estimatedFee={meltQuote?.fee_reserve || 0} estimatedFee={meltQuote?.fee_reserve.toNumber() ?? 0}
finalFee={finalFee} finalFee={finalFee}
/> />
) : ( ) : (

View File

@@ -121,7 +121,7 @@ export const QRCodeBlock = function (props: {
setKeysetFormat('hex') setKeysetFormat('hex')
} else { } else {
try { try {
const encodedV3 = getEncodedToken(decoded, {version: 3}) const encodedV3 = getEncodedToken(decoded)
setEncodedV3Token(encodedV3) setEncodedV3Token(encodedV3)
} catch (e: any) { } catch (e: any) {
handleQrError(e) handleQrError(e)
@@ -195,7 +195,7 @@ export const QRCodeBlock = function (props: {
} else if(type === 'EncodedV4Token') { } else if(type === 'EncodedV4Token') {
if(!decodedToken) return if(!decodedToken) return
const encodedV3 = getEncodedToken(decodedToken, {version: 3}) const encodedV3 = getEncodedToken(decodedToken)
setEncodedV3Token(encodedV3) setEncodedV3Token(encodedV3)
} }
} catch (e: any) { } catch (e: any) {

View File

@@ -1,14 +1,19 @@
import {Mint} from '../../models/Mint' import {Mint} from '../../models/Mint'
import {
Amount,
OutputData,
} from '@cashu/cashu-ts'
import type { import type {
Token, Token,
Proof as CashuProof, ProofLike as CashuProof,
PaymentRequest as CashuPaymentRequest, PaymentRequest as CashuPaymentRequest,
PaymentRequestPayload, PaymentRequestPayload,
TokenMetadata, TokenMetadata,
OutputDataLike,
} from '@cashu/cashu-ts' } from '@cashu/cashu-ts'
import { bytesToHex } from '@noble/hashes/utils' import { bytesToHex, hexToBytes } from '@noble/hashes/utils'
import AppError, {Err} from '../../utils/AppError' import AppError, {Err} from '../../utils/AppError'
import { getDecodedToken, getTokenMetadata } from '@cashu/cashu-ts' import { getTokenMetadata } from '@cashu/cashu-ts'
import {Proof} from '../../models/Proof' import {Proof} from '../../models/Proof'
import { log } from '../logService' import { log } from '../logService'
import { decodePaymentRequest } from '@cashu/cashu-ts' import { decodePaymentRequest } from '@cashu/cashu-ts'
@@ -29,7 +34,7 @@ const isObj = function(v: unknown): v is object {
* 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 + proof.amount, 0) return proofs.reduce((acc: number, proof: CashuProof) => acc + Number(proof.amount), 0)
} }
const CASHU_URI_PREFIXES = [ const CASHU_URI_PREFIXES = [
@@ -407,6 +412,38 @@ const isTokenP2PKLocked = function (token: Token | TokenMetadata): boolean {
export interface StoredMeltPreview {
keysetId: string
outputData: SerializedOutputData[]
}
export interface SerializedOutputData {
blindedMessage: { amount: string | number; id: string; B_: string }
blindingFactor: string // hex
secret: string // hex
ephemeralE?: string
}
const serializeOutputData = (outputData: OutputDataLike[]): SerializedOutputData[] =>
outputData.map(od => ({
blindedMessage: {
amount: od.blindedMessage.amount.toString(),
id: od.blindedMessage.id,
B_: od.blindedMessage.B_,
},
blindingFactor: od.blindingFactor.toString(16),
secret: bytesToHex(od.secret),
ephemeralE: od.ephemeralE,
}))
const deserializeOutputData = (serialized: SerializedOutputData[]): OutputData[] =>
serialized.map(od => new OutputData(
{ amount: Amount.from(od.blindedMessage.amount), id: od.blindedMessage.id, B_: od.blindedMessage.B_ },
BigInt('0x' + od.blindingFactor),
hexToBytes(od.secret),
od.ephemeralE,
))
export const CashuUtils = { export const CashuUtils = {
findEncodedCashuToken, findEncodedCashuToken,
findEncodedCashuPaymentRequest, findEncodedCashuPaymentRequest,
@@ -427,7 +464,9 @@ export const CashuUtils = {
isTokenP2PKLocked, isTokenP2PKLocked,
isCollidingKeysetId, isCollidingKeysetId,
isObj, isObj,
sumProofs sumProofs,
serializeOutputData,
deserializeOutputData,
} }

View File

@@ -11,8 +11,7 @@ import AppError, {Err} from '../../utils/AppError'
import { TransactionTaskResult } from '../walletService' import { TransactionTaskResult } from '../walletService'
import { WalletUtils } from './utils' import { WalletUtils } from './utils'
import { MintUnit, formatCurrency, getCurrency } from './currency' import { MintUnit, formatCurrency, getCurrency } from './currency'
import { PaymentRequestPayload, Token, getDecodedToken } from '@cashu/cashu-ts' import { PaymentRequestPayload, Token, getDecodedToken, getEncodedToken, normalizeProofAmounts } from '@cashu/cashu-ts'
import { getEncodedToken } from '@cashu/cashu-ts'
const { const {
mintsStore, mintsStore,
@@ -605,7 +604,7 @@ export const receiveSync = async function (
// store swapped proofs as encoded token in tx data // store swapped proofs as encoded token in tx data
const outputToken = getEncodedToken({ const outputToken = getEncodedToken({
mint: mintToReceive, mint: mintToReceive,
proofs: receivedProofs, proofs: normalizeProofAmounts(receivedProofs),
unit, unit,
memo, memo,
}) })

View File

@@ -9,8 +9,7 @@ import AppError, {Err} from '../../utils/AppError'
import { TransactionTaskResult } from '../walletService' import { TransactionTaskResult } from '../walletService'
import { WalletUtils } from './utils' import { WalletUtils } from './utils'
import { MintUnit } from './currency' import { MintUnit } from './currency'
import { Token } from '@cashu/cashu-ts' import { Token, getEncodedToken, normalizeProofAmounts } from '@cashu/cashu-ts'
import { getEncodedToken, getKeepAmounts } from '@cashu/cashu-ts'
const { const {
mintsStore, mintsStore,
@@ -52,7 +51,7 @@ try {
// This will invalidate originally sent proofs effectively reverting the transaction. // This will invalidate originally sent proofs effectively reverting the transaction.
const encodedToken: Token = { const encodedToken: Token = {
mint: mintInstance.mintUrl, mint: mintInstance.mintUrl,
proofs: pendingProofs, proofs: normalizeProofAmounts(pendingProofs),
unit unit
} }
@@ -69,8 +68,8 @@ try {
// store freshed proofs as encoded token in tx data // store freshed proofs as encoded token in tx data
const outputToken = getEncodedToken({ const outputToken = getEncodedToken({
mint: mintInstance.mintUrl, mint: mintInstance.mintUrl,
proofs: receivedProofs, proofs: normalizeProofAmounts(receivedProofs),
unit unit
}) })
// Remove original pending proofs // Remove original pending proofs

View File

@@ -15,6 +15,7 @@ import {
MintKeyset, MintKeyset,
ProofState, ProofState,
getEncodedToken, getEncodedToken,
normalizeProofAmounts,
} from '@cashu/cashu-ts' } from '@cashu/cashu-ts'
import { MAX_SWAP_INPUT_SIZE, TransactionTaskResult, WalletTask } from '../walletService' import { MAX_SWAP_INPUT_SIZE, TransactionTaskResult, WalletTask } from '../walletService'
import { Mint, MintBalance } from '../../models/Mint' import { Mint, MintBalance } from '../../models/Mint'
@@ -44,7 +45,7 @@ const _monitorSentProofs = async (params: {
try { try {
log.trace('[send] Subscribing to proofStateUpdates for sent proof', {secret: proofsToSend[0]}) log.trace('[send] Subscribing to proofStateUpdates for sent proof', {secret: proofsToSend[0]})
const unsub = await wsWallet.on.proofStateUpdates( const unsub = await wsWallet.on.proofStateUpdates(
[proofsToSend[0]], normalizeProofAmounts([proofsToSend[0]]),
async (proofState: ProofState) => { async (proofState: ProofState) => {
log.trace(`Websocket: proof state updated: ${proofState.state} with secret: ${proofsToSend[0].secret}`) log.trace(`Websocket: proof state updated: ${proofState.state} with secret: ${proofsToSend[0].secret}`)
if (proofState.state == CheckStateEnum.SPENT) { if (proofState.state == CheckStateEnum.SPENT) {
@@ -146,7 +147,7 @@ export const sendTask = async function (
const outputToken = getEncodedToken({ const outputToken = getEncodedToken({
mint: mintUrl, mint: mintUrl,
proofs: proofsToSend, proofs: normalizeProofAmounts(proofsToSend),
unit, unit,
memo, memo,
}) })
@@ -340,7 +341,7 @@ export const sendFromMintSync = async function (
if(isSwapNeeded) { if(isSwapNeeded) {
// Calculate feeReserve from mint fee rate // Calculate feeReserve from mint fee rate
const walletInstance = await walletStore.getWallet(mintUrl, unit, {withSeed: true}) as CashuWallet const walletInstance = await walletStore.getWallet(mintUrl, unit, {withSeed: true}) as CashuWallet
swapFeeReserve = walletInstance.getFeesForProofs(proofsToSendFrom) swapFeeReserve = walletInstance.getFeesForProofs(proofsToSendFrom).toNumber()
const amountWithFees = amountToSend + swapFeeReserve const amountWithFees = amountToSend + swapFeeReserve
if (totalAmountFromMint < amountWithFees) { if (totalAmountFromMint < amountWithFees) {

View File

@@ -1,6 +1,6 @@
import {CashuUtils} from '../cashu/cashuUtils' import {CashuUtils} from '../cashu/cashuUtils'
import AppError, {Err} from '../../utils/AppError' import AppError, {Err} from '../../utils/AppError'
import {Mint as CashuMint, Wallet as CashuWallet, MeltProofsResponse, MeltQuoteBolt11Response, MeltQuoteResponse, MeltQuoteState, getEncodedToken} from '@cashu/cashu-ts' import {Mint as CashuMint, Wallet as CashuWallet, MeltProofsResponse, MeltQuoteBolt11Response, MeltQuoteState, getEncodedToken, normalizeProofAmounts} from '@cashu/cashu-ts'
import {rootStoreInstance} from '../../models' import {rootStoreInstance} from '../../models'
import { TransactionTaskResult, WalletTask } from '../walletService' import { TransactionTaskResult, WalletTask } from '../walletService'
import { MintBalance } from '../../models/Mint' import { MintBalance } from '../../models/Mint'
@@ -116,7 +116,7 @@ export const transferTask = async function (
const newTransaction = { const newTransaction = {
type: TransactionType.TRANSFER, type: TransactionType.TRANSFER,
amount: amountToTransfer, amount: amountToTransfer,
fee: meltQuote.fee_reserve, fee: meltQuote.fee_reserve.toNumber(),
unit, unit,
data: JSON.stringify(transactionData), data: JSON.stringify(transactionData),
memo, memo,
@@ -137,7 +137,7 @@ export const transferTask = async function (
// Replace individual setters with a single update // Replace individual setters with a single update
transaction.update({ paymentId: paymentHash, quote: meltQuote.quote }) transaction.update({ paymentId: paymentHash, quote: meltQuote.quote })
if (amountToTransfer + meltQuote.fee_reserve > mintBalanceToTransferFrom.balances[unit]!) { if (amountToTransfer + meltQuote.fee_reserve.toNumber() > mintBalanceToTransferFrom.balances[unit]!) {
throw new AppError( throw new AppError(
Err.VALIDATION_ERROR, Err.VALIDATION_ERROR,
'Mint balance is insufficient to cover the amount to transfer with the expected Lightning fees.', 'Mint balance is insufficient to cover the amount to transfer with the expected Lightning fees.',
@@ -165,15 +165,15 @@ export const transferTask = async function (
const totalAmountFromMint = CashuUtils.getProofsAmount(proofsFromMint) const totalAmountFromMint = CashuUtils.getProofsAmount(proofsFromMint)
proofsToMeltFrom = CashuUtils.getProofsToSend( proofsToMeltFrom = CashuUtils.getProofsToSend(
amountToTransfer + meltQuote.fee_reserve, amountToTransfer + meltQuote.fee_reserve.toNumber(),
proofsFromMint proofsFromMint
) )
proofsToMeltFromAmount = CashuUtils.getProofsAmount(proofsToMeltFrom) proofsToMeltFromAmount = CashuUtils.getProofsAmount(proofsToMeltFrom)
const walletInstance = await walletStore.getWallet(mintUrl, unit, {withSeed: true}) const walletInstance = await walletStore.getWallet(mintUrl, unit, {withSeed: true})
meltFeeReserve = walletInstance.getFeesForProofs(proofsToMeltFrom) meltFeeReserve = walletInstance.getFeesForProofs(proofsToMeltFrom).toNumber()
const amountWithFees = amountToTransfer + meltQuote.fee_reserve + meltFeeReserve const amountWithFees = amountToTransfer + meltQuote.fee_reserve.toNumber() + meltFeeReserve
if (totalAmountFromMint < amountWithFees) { if (totalAmountFromMint < amountWithFees) {
throw new AppError( throw new AppError(
@@ -217,15 +217,15 @@ export const transferTask = async function (
transactionData.push({ transactionData.push({
status: TransactionStatus.PREPARED, status: TransactionStatus.PREPARED,
proofsToMeltFromAmount, proofsToMeltFromAmount,
lightningFeeReserve: meltQuote.fee_reserve, lightningFeeReserve: meltQuote.fee_reserve.toNumber(),
meltFeeReserve, meltFeeReserve,
createdAt: new Date(), createdAt: new Date(),
}) })
const inputToken = getEncodedToken({ const inputToken = getEncodedToken({
mint: mintUrl, mint: mintUrl,
proofs: proofsToMeltFrom, proofs: normalizeProofAmounts(proofsToMeltFrom),
unit unit
}) })
transaction.update({ transaction.update({
@@ -383,7 +383,7 @@ export const transferTask = async function (
taskFunction: TRANSFER_TASK, taskFunction: TRANSFER_TASK,
mintUrl, mintUrl,
transaction, transaction,
message: 'Lightning payment is in progress. Check your transaction history for the result.', message: 'Lightning payment is in progress...',
meltQuote: meltResponse.quote, meltQuote: meltResponse.quote,
nwcEvent, nwcEvent,
} as TransactionTaskResult } as TransactionTaskResult

View File

@@ -15,7 +15,7 @@ import {CashuProof, CashuUtils} from './cashu/cashuUtils'
import {LightningUtils} from './lightning/lightningUtils' import {LightningUtils} from './lightning/lightningUtils'
import AppError, {Err} from '../utils/AppError' import AppError, {Err} from '../utils/AppError'
import {MintBalance, MintStatus} from '../models/Mint' import {MintBalance, MintStatus} from '../models/Mint'
import {MeltQuoteBaseResponse, MeltQuoteBolt11Response, MeltQuoteResponse, MeltQuoteState, MintQuoteState, OutputData, PaymentRequestPayload, Token, TokenMetadata, getDecodedToken, getEncodedToken, getTokenMetadata} from '@cashu/cashu-ts' import {MeltQuoteBaseResponse, MeltQuoteBolt11Response, MeltQuoteState, MintQuoteState, PaymentRequestPayload, Token, TokenMetadata, getDecodedToken, getEncodedToken, getTokenMetadata, normalizeProofAmounts} from '@cashu/cashu-ts'
import {Mint} from '../models/Mint' import {Mint} from '../models/Mint'
import {pollerExists, stopPolling} from '../utils/poller' import {pollerExists, stopPolling} from '../utils/poller'
import { NostrClient, NostrEvent, NostrProfile } from './nostrService' import { NostrClient, NostrEvent, NostrProfile } from './nostrService'
@@ -95,7 +95,7 @@ type WalletTaskService = {
mintBalanceToTransferFrom: MintBalance, mintBalanceToTransferFrom: MintBalance,
amountToTransfer: number, amountToTransfer: number,
unit: MintUnit, unit: MintUnit,
meltQuote: MeltQuoteResponse, meltQuote: MeltQuoteBolt11Response,
memo: string, memo: string,
invoiceExpiry: Date, invoiceExpiry: Date,
encodedInvoice: string, encodedInvoice: string,
@@ -149,7 +149,7 @@ type WalletTaskService = {
}) => Promise<{recoveredAmount: number}> }) => Promise<{recoveredAmount: number}>
recoverMeltQuoteChange: (params: { recoverMeltQuoteChange: (params: {
mintUrl: string, mintUrl: string,
meltQuote: string | MeltQuoteResponse meltQuote: string | MeltQuoteBolt11Response
}) => Promise<{recoveredAmount: number}> }) => Promise<{recoveredAmount: number}>
handlePendingMeltTask: (params: { handlePendingMeltTask: (params: {
mintUrl: string mintUrl: string
@@ -301,7 +301,8 @@ const receiveQueueAwaitable = function (
): Promise<TransactionTaskResult> { ): Promise<TransactionTaskResult> {
const now = Date.now() const now = Date.now()
const {amount, memo, unit} = tokenMetadata const {amount: rawAmount, memo, unit} = tokenMetadata
const amount = Number(rawAmount)
const proofsCount = tokenMetadata.incompleteProofs.length const proofsCount = tokenMetadata.incompleteProofs.length
const taskId = proofsCount > MAX_SWAP_INPUT_SIZE const taskId = proofsCount > MAX_SWAP_INPUT_SIZE
@@ -338,7 +339,7 @@ const receiveQueueAwaitable = function (
taskId, taskId,
async () => { async () => {
try { try {
const token = getDecodedToken(encodedToken, mint.keysetIds) const token = getDecodedToken(encodedToken, mint.keysetIds ?? [])
return proofsCount > MAX_SWAP_INPUT_SIZE return proofsCount > MAX_SWAP_INPUT_SIZE
? await receiveBatchTask(token, amount, memo || '', encodedToken) ? await receiveBatchTask(token, amount, memo || '', encodedToken)
: await receiveTask(token, amount, memo || '', encodedToken) : await receiveTask(token, amount, memo || '', encodedToken)
@@ -443,7 +444,8 @@ const receiveOfflinePrepareQueueAwaitable = function (
): Promise<TransactionTaskResult> { ): Promise<TransactionTaskResult> {
const now = Date.now() const now = Date.now()
const taskId = `receiveOfflinePrepareTask-${now}` const taskId = `receiveOfflinePrepareTask-${now}`
const { mint: mintUrl, amount, memo, unit } = tokenMetadata const { mint: mintUrl, amount: rawAmount, memo, unit } = tokenMetadata
const amount = Number(rawAmount)
return new Promise<TransactionTaskResult>((resolve, reject) => { return new Promise<TransactionTaskResult>((resolve, reject) => {
let resolved = false let resolved = false
@@ -1677,7 +1679,7 @@ const handleInFlightByMintTask = async (mint: Mint): Promise<WalletTaskResult> =
}) })
const outputToken = getEncodedToken({ mint: mintUrl, proofs, unit }) const outputToken = getEncodedToken({ mint: mintUrl, proofs: normalizeProofAmounts(proofs), unit })
const balanceAfter = proofsStore.getUnitBalance(unit)?.unitBalance const balanceAfter = proofsStore.getUnitBalance(unit)?.unitBalance
txData.push({ status: TransactionStatus.COMPLETED, receivedAmount, swapFeePaid, createdAt: new Date() }) txData.push({ status: TransactionStatus.COMPLETED, receivedAmount, swapFeePaid, createdAt: new Date() })
@@ -1718,7 +1720,7 @@ const handleInFlightByMintTask = async (mint: Mint): Promise<WalletTaskResult> =
proofsStore.addOrUpdate(returnedProofs, { mintUrl, tId: tx.id, unit, isPending: false, isSpent: false }) proofsStore.addOrUpdate(returnedProofs, { mintUrl, tId: tx.id, unit, isPending: false, isSpent: false })
proofsStore.addOrUpdate(proofsToSend, { mintUrl, tId: tx.id, unit, isPending: true, isSpent: false }) proofsStore.addOrUpdate(proofsToSend, { mintUrl, tId: tx.id, unit, isPending: true, isSpent: false })
const outputToken = getEncodedToken({ mint: mintUrl, proofs: proofsToSend, unit }) const outputToken = getEncodedToken({ mint: mintUrl, proofs: normalizeProofAmounts(proofsToSend), unit })
const balanceAfter = proofsStore.getUnitBalance(unit)?.unitBalance const balanceAfter = proofsStore.getUnitBalance(unit)?.unitBalance
txData.push({ status: TransactionStatus.PENDING, createdAt: new Date() }) txData.push({ status: TransactionStatus.PENDING, createdAt: new Date() })
@@ -2269,15 +2271,7 @@ const recoverMintQuote = async (
const cashuWallet = await walletStore.getWallet(mintUrl, unit, { withSeed: true, keysetId: meltPreview.keysetId }) const cashuWallet = await walletStore.getWallet(mintUrl, unit, { withSeed: true, keysetId: meltPreview.keysetId })
const keyset = cashuWallet.getKeyset(meltPreview.keysetId) const keyset = cashuWallet.getKeyset(meltPreview.keysetId)
const reconstructedOutputData = meltPreview.outputData.map((od: any) => { const reconstructedOutputData = CashuUtils.deserializeOutputData(meltPreview.outputData)
const blindingFactor = BigInt(od.blindingFactor)
const secretValues: number[] = typeof od.secret === 'object' && !ArrayBuffer.isView(od.secret)
? Object.values(od.secret as Record<string, number>)
: Array.from(od.secret as Uint8Array)
const secret = new Uint8Array(secretValues)
return new OutputData(od.blindedMessage, blindingFactor, secret)
})
const recoveredChange = change.map((sig, i) => reconstructedOutputData[i].toProof(sig, keyset)) const recoveredChange = change.map((sig, i) => reconstructedOutputData[i].toProof(sig, keyset))
currentCounter.removeMeltCounterValue(tx.id) currentCounter.removeMeltCounterValue(tx.id)
@@ -2383,19 +2377,11 @@ const handlePendingMeltTask = async (params: {
{ withSeed: true, keysetId: meltPreview.keysetId }) { withSeed: true, keysetId: meltPreview.keysetId })
const keyset = cashuWallet.getKeyset(meltPreview.keysetId) const keyset = cashuWallet.getKeyset(meltPreview.keysetId)
// Reconstruct OutputData instances: serializeMeltPreview converts bigint→string const reconstructedOutputData = CashuUtils.deserializeOutputData(meltPreview.outputData)
// and Uint8Array→plain-object, so we must restore the types before calling toProof.
const reconstructedOutputData = meltPreview.outputData.map((od: any) => {
const blindingFactor = BigInt(od.blindingFactor)
const secretValues: number[] = typeof od.secret === 'object' && !ArrayBuffer.isView(od.secret)
? Object.values(od.secret as Record<string, number>)
: Array.from(od.secret as Uint8Array)
const secret = new Uint8Array(secretValues)
return new OutputData(od.blindedMessage, blindingFactor, secret)
})
const change = quote.change.map((sig, i) => reconstructedOutputData[i].toProof(sig, keyset)) const change = quote.change.map((sig, i) => reconstructedOutputData[i].toProof(sig, keyset))
log.trace('[handlePendingMelt] Change unblinded', { transactionId, quoteId, change })
currentCounter.removeMeltCounterValue(transactionId) currentCounter.removeMeltCounterValue(transactionId)
if (change.length > 0) { if (change.length > 0) {
@@ -2410,7 +2396,7 @@ const handlePendingMeltTask = async (params: {
} }
} }
} catch (e: any) { } catch (e: any) {
log.error('[handlePendingMelt] change recovery failed, completing without change', e.message) log.error('[handlePendingMelt] change recovery failed, completing without change', {message: e.message})
} }
const txData: TransactionData[] = transaction.data ? JSON.parse(transaction.data) : [] const txData: TransactionData[] = transaction.data ? JSON.parse(transaction.data) : []
@@ -2540,7 +2526,7 @@ const handleClaimTask = async function (params: {
const result: TransactionTaskResult = await receiveTask( const result: TransactionTaskResult = await receiveTask(
decoded, decoded,
tokenInfo.amount, Number(tokenInfo.amount),
tokenInfo.memo || 'Received to Lightning address', tokenInfo.memo || 'Received to Lightning address',
encodedToken, encodedToken,
) )
@@ -2838,7 +2824,7 @@ const handleReceivedEventTask = async function (encryptedEvent: NostrEvent): Pro
// keysetsV2 support // keysetsV2 support
const tokenInfo = getTokenMetadata(incoming.encoded) const tokenInfo = getTokenMetadata(incoming.encoded)
const amountToReceive = tokenInfo.amount const amountToReceive = Number(tokenInfo.amount)
const memo = tokenInfo.memo || 'Received over Nostr' const memo = tokenInfo.memo || 'Received over Nostr'
const {unit, mint: mintUrl} = tokenInfo const {unit, mint: mintUrl} = tokenInfo
@@ -3041,7 +3027,8 @@ const handleReceivedEventTask = async function (encryptedEvent: NostrEvent): Pro
} }
// create draft transaction // create draft transaction
const {amount, unit, description, id, mints} = decoded const {amount: rawAmount, unit, description, id, mints} = decoded
const amount = Number(rawAmount)
const availableBalances: MintBalance[] = [] const availableBalances: MintBalance[] = []

View File

@@ -4,7 +4,7 @@
"exclude": ["**/node_modules", "**/Pods"], "exclude": ["**/node_modules", "**/Pods"],
"compilerOptions": { "compilerOptions": {
"jsx": "react", "jsx": "react",
"typeRoots": ["./src/utils/envtypes"], "typeRoots": ["./src/utils/envtypes", "./node_modules/@types"],
"lib": ["ES2022"], "lib": ["ES2022"],
"paths": { "paths": {
"nostr-tools/*": ["./node_modules/nostr-tools/lib/types/*"] "nostr-tools/*": ["./node_modules/nostr-tools/lib/types/*"]

View File

@@ -2869,15 +2869,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@cashu/cashu-ts@npm:^3.6.1": "@cashu/cashu-ts@npm:^4.2.1":
version: 3.6.1 version: 4.2.1
resolution: "@cashu/cashu-ts@npm:3.6.1" resolution: "@cashu/cashu-ts@npm:4.2.1"
dependencies: dependencies:
"@noble/curves": ^2.0.1 "@noble/curves": ^2.2.0
"@noble/hashes": ^2.0.1 "@noble/hashes": ^2.2.0
"@scure/base": ^2.0.0 "@scure/base": ^2.2.0
"@scure/bip32": ^2.0.1 "@scure/bip32": ^2.2.0
checksum: 07018c68fa9b5bd8affde081a6f5c6fb0ba67e1e6e1a3872527547461aea57a3aeb6bb6dace5dad430464b17f53503ad11d6a56b985a43cea3a8a18e8e86838b checksum: a950f38686fcab220a61ea7b83c08f14f8af0ed5b54a18b38f8a370fc3e63897a6c1f266d8e7e9e269f05413d8b5f9d58d319bd1b194b2e2830cb7983169c931
languageName: node languageName: node
linkType: hard linkType: hard
@@ -5433,12 +5433,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@noble/curves@npm:2.0.1, @noble/curves@npm:^2.0.1": "@noble/curves@npm:2.2.0, @noble/curves@npm:^2.2.0":
version: 2.0.1 version: 2.2.0
resolution: "@noble/curves@npm:2.0.1" resolution: "@noble/curves@npm:2.2.0"
dependencies: dependencies:
"@noble/hashes": 2.0.1 "@noble/hashes": 2.2.0
checksum: b6844f350d629bb6de55d3a123bc148403901c8b8eed77d2132a389d080d553242bcf1e06913bf51f51e395849ec70b9a8e8902c19e562ea3c6538bb0240d92f checksum: 2b6f02c18918578f528791644886f54333c67323f5ccefe10bf250f08760f63786b807390d8d5f8562c661e70dd3559ce8e51e1fe360118c8fd47071c2bd6991
languageName: node languageName: node
linkType: hard linkType: hard
@@ -5465,13 +5465,20 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@noble/hashes@npm:2.0.1, @noble/hashes@npm:^2.0.1": "@noble/hashes@npm:2.0.1":
version: 2.0.1 version: 2.0.1
resolution: "@noble/hashes@npm:2.0.1" resolution: "@noble/hashes@npm:2.0.1"
checksum: aea67a671ca464027cd512d07666a804bd99abc343ba98c93ee8463a1c870c5594f8de468279ef3c60f3b23b6330db9f1a92bd23723b8a911ec73bddc9420248 checksum: aea67a671ca464027cd512d07666a804bd99abc343ba98c93ee8463a1c870c5594f8de468279ef3c60f3b23b6330db9f1a92bd23723b8a911ec73bddc9420248
languageName: node languageName: node
linkType: hard linkType: hard
"@noble/hashes@npm:2.2.0, @noble/hashes@npm:^2.2.0":
version: 2.2.0
resolution: "@noble/hashes@npm:2.2.0"
checksum: a8745fb5da57a73ddd3dd6d5ad45d156ea664be51ead5344ca1528f8e56ad0ba0720ec8adff3bda37c50c72a86a33a57332ec02c3fb8d2a2612f9c3e5e7079ab
languageName: node
linkType: hard
"@noble/hashes@npm:^1.1.5": "@noble/hashes@npm:^1.1.5":
version: 1.8.0 version: 1.8.0
resolution: "@noble/hashes@npm:1.8.0" resolution: "@noble/hashes@npm:1.8.0"
@@ -6405,13 +6412,20 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@scure/base@npm:2.0.0, @scure/base@npm:^2.0.0": "@scure/base@npm:2.0.0":
version: 2.0.0 version: 2.0.0
resolution: "@scure/base@npm:2.0.0" resolution: "@scure/base@npm:2.0.0"
checksum: 15d10f154256f52bb3e02b63cdf93b6df6db3814fa4c61a8bb2d339831aec7859609fb715b93c7fc852755f0fe67fb6411664c06159745711bf38af3b10ab93e checksum: 15d10f154256f52bb3e02b63cdf93b6df6db3814fa4c61a8bb2d339831aec7859609fb715b93c7fc852755f0fe67fb6411664c06159745711bf38af3b10ab93e
languageName: node languageName: node
linkType: hard linkType: hard
"@scure/base@npm:2.2.0, @scure/base@npm:^2.2.0":
version: 2.2.0
resolution: "@scure/base@npm:2.2.0"
checksum: 2dd91f310765366e2dcb5e37709eb35ccd57d3f2f112d36232ca03fb8819c8764708f333ac40935fa719ff2e943bd50e1912ab43d212e44e807100d21f835f13
languageName: node
linkType: hard
"@scure/base@npm:~1.1.0": "@scure/base@npm:~1.1.0":
version: 1.1.9 version: 1.1.9
resolution: "@scure/base@npm:1.1.9" resolution: "@scure/base@npm:1.1.9"
@@ -6430,14 +6444,14 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@scure/bip32@npm:^2.0.1": "@scure/bip32@npm:^2.2.0":
version: 2.0.1 version: 2.2.0
resolution: "@scure/bip32@npm:2.0.1" resolution: "@scure/bip32@npm:2.2.0"
dependencies: dependencies:
"@noble/curves": 2.0.1 "@noble/curves": 2.2.0
"@noble/hashes": 2.0.1 "@noble/hashes": 2.2.0
"@scure/base": 2.0.0 "@scure/base": 2.2.0
checksum: 5e6c7b455c4a5599673d5fa1b7edc1b3655fe2f7b8388dc19ebe4a89adb9c80c511215d4af624eb774c63c216fd8b7bb8fecf1eb8c3d5e979e279c7542657a42 checksum: 6cd0ddf603b739d3c24a13618fc246d409bbf0dffec22bc2c3c8e3e64271bb58c7078bea0b6ab9601fcfe881a969ee31e3074d478affe13a439d7527a0b68ecf
languageName: node languageName: node
linkType: hard linkType: hard
@@ -14606,7 +14620,7 @@ __metadata:
"@babel/plugin-proposal-export-namespace-from": ^7.18.9 "@babel/plugin-proposal-export-namespace-from": ^7.18.9
"@babel/preset-env": ^7.25.3 "@babel/preset-env": ^7.25.3
"@babel/runtime": ^7.25.0 "@babel/runtime": ^7.25.0
"@cashu/cashu-ts": ^3.6.1 "@cashu/cashu-ts": ^4.2.1
"@fortawesome/fontawesome-svg-core": ^7.1.0 "@fortawesome/fontawesome-svg-core": ^7.1.0
"@fortawesome/free-brands-svg-icons": ^7.1.0 "@fortawesome/free-brands-svg-icons": ^7.1.0
"@fortawesome/free-regular-svg-icons": ^7.1.0 "@fortawesome/free-regular-svg-icons": ^7.1.0
@@ -14635,7 +14649,7 @@ __metadata:
"@react-navigation/native": ^7.1.18 "@react-navigation/native": ^7.1.18
"@react-navigation/native-stack": ^7.3.27 "@react-navigation/native-stack": ^7.3.27
"@react-navigation/stack": ^7.4.9 "@react-navigation/stack": ^7.4.9
"@scure/bip32": ^2.0.1 "@scure/bip32": ^2.2.0
"@scure/bip39": 2.0.1 "@scure/bip39": 2.0.1
"@sentry/cli": ^2.56.0 "@sentry/cli": ^2.56.0
"@sentry/react-native": ^7.7.0 "@sentry/react-native": ^7.7.0