mirror of
https://github.com/minibits-cash/minibits_wallet.git
synced 2026-07-22 07:48:28 +00:00
Upgrade to cashu-ts v4
This commit is contained in:
@@ -22,7 +22,7 @@
|
||||
"machine-translate": "npx @inlang/cli machine translate --project minibits.inlang"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cashu/cashu-ts": "^3.6.1",
|
||||
"@cashu/cashu-ts": "^4.2.1",
|
||||
"@fortawesome/fontawesome-svg-core": "^7.1.0",
|
||||
"@fortawesome/free-brands-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-stack": "^7.3.27",
|
||||
"@react-navigation/stack": "^7.4.9",
|
||||
"@scure/bip32": "^2.0.1",
|
||||
"@scure/bip32": "^2.2.0",
|
||||
"@scure/bip39": "2.0.1",
|
||||
"@sentry/react-native": "^7.7.0",
|
||||
"date-fns": "^4.1.0",
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
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
|
||||
+++ 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 { abytes, concatBytes, createView } from '@noble/hashes/utils.js';
|
||||
import { createBase58check } from '@scure/base';
|
||||
+import quickCrypto from 'react-native-quick-crypto';
|
||||
const Point = secp.Point;
|
||||
const { Fn } = Point;
|
||||
const base58check = createBase58check(sha256);
|
||||
@@ -87,7 +88,8 @@ export class HDKey {
|
||||
throw new Error('HDKey: seed length must be between 128 and 512 bits; 256 bits is advised, got ' +
|
||||
const Point = /* @__PURE__ */ (() => secp.Point)();
|
||||
const Fn = /* @__PURE__ */ (() => Point.Fn)();
|
||||
const base58check = /* @__PURE__ */ createBase58check(sha256);
|
||||
@@ -102,7 +103,8 @@ export class HDKey {
|
||||
throw new RangeError('HDKey: seed length must be between 128 and 512 bits; 256 bits is advised, got ' +
|
||||
seed.length);
|
||||
}
|
||||
- const I = hmac(sha512, MASTER_SECRET, seed);
|
||||
@@ -20,13 +20,13 @@ index 935277b..361161a 100644
|
||||
const privateKey = I.slice(0, 32);
|
||||
const chainCode = I.slice(32);
|
||||
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)
|
||||
data = concatBytes(this._publicKey, data);
|
||||
}
|
||||
- const I = hmac(sha512, this.chainCode, data);
|
||||
+ //const I = hmac(sha512, this.chainCode, data);
|
||||
+ const I = new Uint8Array(quickCrypto.createHmac('sha512', this.chainCode).update(data).digest());
|
||||
const childTweak = I.slice(0, 32);
|
||||
const chainCode = I.slice(32);
|
||||
if (!secp.utils.isValidSecretKey(childTweak)) {
|
||||
- const out = _I || hmac(sha512, this.chainCode, data);
|
||||
+ //const out = _I || hmac(sha512, this.chainCode, data);
|
||||
+ const out = _I || new Uint8Array(quickCrypto.createHmac('sha512', this.chainCode).update(data).digest());
|
||||
abytes(out, 64);
|
||||
const childTweak = out.slice(0, 32);
|
||||
const chainCode = out.slice(32);
|
||||
@@ -15,13 +15,13 @@ import { MintUnit, MintUnits } from '../services/wallet/currency'
|
||||
import { getRootStore } from './helpers/getRootStore'
|
||||
import { generateId } from '../utils/utils'
|
||||
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): any {
|
||||
return JSON.parse(JSON.stringify(meltPreview, (_key, value) =>
|
||||
typeof value === 'bigint' ? value.toString() : value
|
||||
))
|
||||
function serializeMeltPreview(meltPreview: MeltPreview): StoredMeltPreview {
|
||||
return {
|
||||
keysetId: meltPreview.keysetId,
|
||||
outputData: CashuUtils.serializeOutputData(meltPreview.outputData),
|
||||
}
|
||||
}
|
||||
|
||||
export type MintBalance = {
|
||||
@@ -60,7 +60,7 @@ const InFlightRequestModel = types.model('InFlightRequest', {
|
||||
const MeltCounterValueModel = types.model('MeltCounterValue', {
|
||||
transactionId: types.number,
|
||||
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
|
||||
})
|
||||
|
||||
@@ -155,13 +155,10 @@ export const MintProofsCounterModel = types
|
||||
return self.meltCounterValues.get(key)!.counterAtMelt
|
||||
}
|
||||
|
||||
// Serialize MeltPreview to handle BigInt values
|
||||
const serializableMeltPreview = meltPreview ? serializeMeltPreview(meltPreview) : undefined
|
||||
|
||||
self.meltCounterValues.set(key, {
|
||||
transactionId,
|
||||
counterAtMelt: self.counter,
|
||||
meltPreview: serializableMeltPreview as any,
|
||||
meltPreview: meltPreview ? serializeMeltPreview(meltPreview) : undefined,
|
||||
createdAt: new Date(),
|
||||
})
|
||||
|
||||
|
||||
@@ -205,6 +205,7 @@ import {
|
||||
} else {
|
||||
proofNode = ProofModel.create({
|
||||
...proof,
|
||||
amount: Number(proof.amount),
|
||||
mintUrl,
|
||||
tId,
|
||||
unit,
|
||||
|
||||
@@ -2,6 +2,7 @@ import {Instance, SnapshotOut, types, flow, getRoot, getSnapshot} from 'mobx-sta
|
||||
import {
|
||||
Mint as CashuMint,
|
||||
Wallet as CashuWallet,
|
||||
KeyChain as CashuKeyChain,
|
||||
MeltQuoteBolt11Response,
|
||||
setGlobalRequestOptions,
|
||||
type MintKeys,
|
||||
@@ -337,13 +338,17 @@ export const WalletStoreModel = types
|
||||
|
||||
const newSeedWallet = new CashuWallet(cashuMint, {
|
||||
unit,
|
||||
keys: mintInstance.keys,
|
||||
keysets: mintInstance.keysets,
|
||||
mintInfo: mintInstance.mintInfo,
|
||||
keysetId: walletKeys.id,
|
||||
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)
|
||||
|
||||
log.trace('[WalletStore.getWallet]', 'Returning NEW cashuWallet instance with seed', {mintUrl})
|
||||
@@ -363,13 +368,16 @@ export const WalletStoreModel = types
|
||||
|
||||
const newWallet = new CashuWallet(cashuMint, {
|
||||
unit,
|
||||
keys: mintInstance.keys,
|
||||
keysets: mintInstance.keysets,
|
||||
mintInfo: mintInstance.mintInfo,
|
||||
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)
|
||||
|
||||
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(
|
||||
mintUrl: string,
|
||||
seed: Uint8Array,
|
||||
|
||||
@@ -36,7 +36,7 @@ import { ContactsStoreSnapshot } from '../models/ContactsStore'
|
||||
import { MintsStoreSnapshot } from '../models/MintsStore'
|
||||
import { ResultModalInfo } from './Wallet/ResultModalInfo'
|
||||
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'
|
||||
|
||||
const OPTIMIZE_FROM_PROOFS_COUNT = 10
|
||||
@@ -262,7 +262,7 @@ export const ExportBackupScreen = function ExportBackup({ route }: Props) {
|
||||
|
||||
const tokenByKeysetId: Token = {
|
||||
mint,
|
||||
proofs: proofsToExport,
|
||||
proofs: normalizeProofAmounts(proofsToExport),
|
||||
unit: proofsByKeysetId[0].unit
|
||||
}
|
||||
|
||||
|
||||
@@ -429,7 +429,7 @@ function NutsCard(props: {info: GetInfoResponse}) {
|
||||
continue;
|
||||
}
|
||||
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
|
||||
nutsSimple.push([nut, !!info.supported])
|
||||
} else {
|
||||
@@ -567,14 +567,20 @@ function getMintLimits(info: GetInfoResponse) {
|
||||
console.log('runs')
|
||||
for (const method of info.nuts['4'].methods) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
for (const method of info.nuts['5'].methods) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
View,
|
||||
TextStyle,
|
||||
} 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 { colors, spacing, typography, useThemeColor } from '../theme'
|
||||
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 })
|
||||
|
||||
// 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')
|
||||
}
|
||||
|
||||
@@ -420,7 +420,7 @@ export const NfcPayScreen = observer(function NfcPayScreen({ route }: Props) {
|
||||
// setEncodedCashuPaymentRequest(encoded)
|
||||
if (pr.description) setMemo(pr.description)
|
||||
|
||||
const requiredAmount = pr.amount
|
||||
const requiredAmount = Number(pr.amount)
|
||||
const eligibleBalances = await getEligibleMintBalancesForCashu(pr, requiredAmount, unit)
|
||||
|
||||
if (eligibleBalances.length === 0) {
|
||||
@@ -678,14 +678,14 @@ export const NfcPayScreen = observer(function NfcPayScreen({ route }: Props) {
|
||||
setInvoice(decoded)
|
||||
setInvoiceExpiry(expiresAt)
|
||||
setMeltQuote(quote)
|
||||
setAmountToPay(formatDisplayAmount(quote.amount, unitRef.current))
|
||||
setAmountToPay(formatDisplayAmount(quote.amount.toNumber(), unitRef.current))
|
||||
if (description) setMemo(description)
|
||||
|
||||
|
||||
setNfcInfo('Paying Lightning invoice...')
|
||||
|
||||
const result = await WalletTask.transferQueueAwaitable(
|
||||
balanceToUse,
|
||||
quote.amount,
|
||||
quote.amount.toNumber(),
|
||||
unitRef.current,
|
||||
quote,
|
||||
description ?? '',
|
||||
|
||||
@@ -136,7 +136,8 @@ export const ReceiveScreen = observer(function ReceiveScreen({ route }: Props) {
|
||||
|
||||
// keysetsV2 support
|
||||
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) {
|
||||
throw new AppError(Err.VALIDATION_ERROR, translate("decodedMissingCurrencyUnit", { unit: CurrencyCode.SAT }))
|
||||
|
||||
@@ -618,8 +618,10 @@ export const SendScreen = observer(function SendScreen({ route }: Props) {
|
||||
setIsCashuPrWithDesc(true)
|
||||
}
|
||||
|
||||
if (pr.amount) {
|
||||
setAmountToSend(`${numbro(pr.amount / getCurrency(unitRef.current).precision).format({
|
||||
const prAmount = pr.amount ? Number(pr.amount) : undefined
|
||||
|
||||
if (prAmount) {
|
||||
setAmountToSend(`${numbro(prAmount / getCurrency(unitRef.current).precision).format({
|
||||
thousandSeparated: true,
|
||||
mantissa: getCurrency(unitRef.current).mantissa,
|
||||
})}`)
|
||||
@@ -646,22 +648,22 @@ export const SendScreen = observer(function SendScreen({ route }: Props) {
|
||||
|
||||
withEnoughBalance = availableBalances.filter(balance => {
|
||||
const unitBalance = balance.balances[unitRef.current]
|
||||
if(!pr.amount) return balance
|
||||
if(pr.amount > 0 && unitBalance && unitBalance >= pr.amount) return balance
|
||||
if(!prAmount) return balance
|
||||
if(prAmount > 0 && unitBalance && unitBalance >= prAmount) return balance
|
||||
return null
|
||||
})
|
||||
|
||||
if (pr.amount && withEnoughBalance.length === 0) {
|
||||
dispatch({ type: 'SET_INFO', message: `Not enough balance to pay this payment request. Required: ${pr.amount} ${unitRef.current}.`})
|
||||
//infoMessage(`Not enough balance to pay this payment request. Required: ${pr.amount} ${unitRef.current}.`)
|
||||
if (prAmount && withEnoughBalance.length === 0) {
|
||||
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: ${prAmount} ${unitRef.current}.`)
|
||||
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})
|
||||
} else {
|
||||
withEnoughBalance = (pr.amount && pr.amount > 0)
|
||||
? proofsStore.getMintBalancesWithEnoughBalance(pr.amount, unitRef.current)
|
||||
withEnoughBalance = (prAmount && prAmount > 0)
|
||||
? proofsStore.getMintBalancesWithEnoughBalance(prAmount, unitRef.current)
|
||||
: proofsStore.getMintBalancesWithUnit(unitRef.current)
|
||||
}
|
||||
|
||||
|
||||
@@ -514,14 +514,14 @@ export const TransferScreen = observer(function TransferScreen({ route }: Props)
|
||||
meltQuoteRef.current = quote
|
||||
|
||||
// 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,
|
||||
mantissa: getCurrency(unitRef.current).mantissa,
|
||||
})
|
||||
setAmountToTransfer(displayAmount)
|
||||
|
||||
// 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)
|
||||
|
||||
if (availableBalances.length === 0) {
|
||||
@@ -934,7 +934,7 @@ export const TransferScreen = observer(function TransferScreen({ route }: Props)
|
||||
{encodedInvoice && (meltQuote?.fee_reserve || finalFee) ? (
|
||||
<FeeBadge
|
||||
currencyCode={getCurrency(unitRef.current).code}
|
||||
estimatedFee={meltQuote?.fee_reserve || 0}
|
||||
estimatedFee={meltQuote?.fee_reserve.toNumber() ?? 0}
|
||||
finalFee={finalFee}
|
||||
/>
|
||||
) : (
|
||||
|
||||
@@ -121,7 +121,7 @@ export const QRCodeBlock = function (props: {
|
||||
setKeysetFormat('hex')
|
||||
} else {
|
||||
try {
|
||||
const encodedV3 = getEncodedToken(decoded, {version: 3})
|
||||
const encodedV3 = getEncodedToken(decoded)
|
||||
setEncodedV3Token(encodedV3)
|
||||
} catch (e: any) {
|
||||
handleQrError(e)
|
||||
@@ -195,7 +195,7 @@ export const QRCodeBlock = function (props: {
|
||||
} else if(type === 'EncodedV4Token') {
|
||||
if(!decodedToken) return
|
||||
|
||||
const encodedV3 = getEncodedToken(decodedToken, {version: 3})
|
||||
const encodedV3 = getEncodedToken(decodedToken)
|
||||
setEncodedV3Token(encodedV3)
|
||||
}
|
||||
} catch (e: any) {
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import {Mint} from '../../models/Mint'
|
||||
import {
|
||||
Amount,
|
||||
OutputData,
|
||||
} from '@cashu/cashu-ts'
|
||||
import type {
|
||||
Token,
|
||||
Proof as CashuProof,
|
||||
Token,
|
||||
ProofLike as CashuProof,
|
||||
PaymentRequest as CashuPaymentRequest,
|
||||
PaymentRequestPayload,
|
||||
TokenMetadata,
|
||||
OutputDataLike,
|
||||
} from '@cashu/cashu-ts'
|
||||
import { bytesToHex } from '@noble/hashes/utils'
|
||||
import { bytesToHex, hexToBytes } from '@noble/hashes/utils'
|
||||
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 { log } from '../logService'
|
||||
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
|
||||
*/
|
||||
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 = [
|
||||
@@ -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 = {
|
||||
findEncodedCashuToken,
|
||||
findEncodedCashuPaymentRequest,
|
||||
@@ -427,7 +464,9 @@ export const CashuUtils = {
|
||||
isTokenP2PKLocked,
|
||||
isCollidingKeysetId,
|
||||
isObj,
|
||||
sumProofs
|
||||
sumProofs,
|
||||
serializeOutputData,
|
||||
deserializeOutputData,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -11,8 +11,7 @@ import AppError, {Err} from '../../utils/AppError'
|
||||
import { TransactionTaskResult } from '../walletService'
|
||||
import { WalletUtils } from './utils'
|
||||
import { MintUnit, formatCurrency, getCurrency } from './currency'
|
||||
import { PaymentRequestPayload, Token, getDecodedToken } from '@cashu/cashu-ts'
|
||||
import { getEncodedToken } from '@cashu/cashu-ts'
|
||||
import { PaymentRequestPayload, Token, getDecodedToken, getEncodedToken, normalizeProofAmounts } from '@cashu/cashu-ts'
|
||||
|
||||
const {
|
||||
mintsStore,
|
||||
@@ -605,7 +604,7 @@ export const receiveSync = async function (
|
||||
// store swapped proofs as encoded token in tx data
|
||||
const outputToken = getEncodedToken({
|
||||
mint: mintToReceive,
|
||||
proofs: receivedProofs,
|
||||
proofs: normalizeProofAmounts(receivedProofs),
|
||||
unit,
|
||||
memo,
|
||||
})
|
||||
|
||||
@@ -9,8 +9,7 @@ import AppError, {Err} from '../../utils/AppError'
|
||||
import { TransactionTaskResult } from '../walletService'
|
||||
import { WalletUtils } from './utils'
|
||||
import { MintUnit } from './currency'
|
||||
import { Token } from '@cashu/cashu-ts'
|
||||
import { getEncodedToken, getKeepAmounts } from '@cashu/cashu-ts'
|
||||
import { Token, getEncodedToken, normalizeProofAmounts } from '@cashu/cashu-ts'
|
||||
|
||||
const {
|
||||
mintsStore,
|
||||
@@ -52,7 +51,7 @@ try {
|
||||
// This will invalidate originally sent proofs effectively reverting the transaction.
|
||||
const encodedToken: Token = {
|
||||
mint: mintInstance.mintUrl,
|
||||
proofs: pendingProofs,
|
||||
proofs: normalizeProofAmounts(pendingProofs),
|
||||
unit
|
||||
}
|
||||
|
||||
@@ -69,8 +68,8 @@ try {
|
||||
// store freshed proofs as encoded token in tx data
|
||||
const outputToken = getEncodedToken({
|
||||
mint: mintInstance.mintUrl,
|
||||
proofs: receivedProofs,
|
||||
unit
|
||||
proofs: normalizeProofAmounts(receivedProofs),
|
||||
unit
|
||||
})
|
||||
|
||||
// Remove original pending proofs
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
MintKeyset,
|
||||
ProofState,
|
||||
getEncodedToken,
|
||||
normalizeProofAmounts,
|
||||
} from '@cashu/cashu-ts'
|
||||
import { MAX_SWAP_INPUT_SIZE, TransactionTaskResult, WalletTask } from '../walletService'
|
||||
import { Mint, MintBalance } from '../../models/Mint'
|
||||
@@ -44,7 +45,7 @@ const _monitorSentProofs = async (params: {
|
||||
try {
|
||||
log.trace('[send] Subscribing to proofStateUpdates for sent proof', {secret: proofsToSend[0]})
|
||||
const unsub = await wsWallet.on.proofStateUpdates(
|
||||
[proofsToSend[0]],
|
||||
normalizeProofAmounts([proofsToSend[0]]),
|
||||
async (proofState: ProofState) => {
|
||||
log.trace(`Websocket: proof state updated: ${proofState.state} with secret: ${proofsToSend[0].secret}`)
|
||||
if (proofState.state == CheckStateEnum.SPENT) {
|
||||
@@ -146,7 +147,7 @@ export const sendTask = async function (
|
||||
|
||||
const outputToken = getEncodedToken({
|
||||
mint: mintUrl,
|
||||
proofs: proofsToSend,
|
||||
proofs: normalizeProofAmounts(proofsToSend),
|
||||
unit,
|
||||
memo,
|
||||
})
|
||||
@@ -340,7 +341,7 @@ export const sendFromMintSync = async function (
|
||||
if(isSwapNeeded) {
|
||||
// Calculate feeReserve from mint fee rate
|
||||
const walletInstance = await walletStore.getWallet(mintUrl, unit, {withSeed: true}) as CashuWallet
|
||||
swapFeeReserve = walletInstance.getFeesForProofs(proofsToSendFrom)
|
||||
swapFeeReserve = walletInstance.getFeesForProofs(proofsToSendFrom).toNumber()
|
||||
const amountWithFees = amountToSend + swapFeeReserve
|
||||
|
||||
if (totalAmountFromMint < amountWithFees) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {CashuUtils} from '../cashu/cashuUtils'
|
||||
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 { TransactionTaskResult, WalletTask } from '../walletService'
|
||||
import { MintBalance } from '../../models/Mint'
|
||||
@@ -116,7 +116,7 @@ export const transferTask = async function (
|
||||
const newTransaction = {
|
||||
type: TransactionType.TRANSFER,
|
||||
amount: amountToTransfer,
|
||||
fee: meltQuote.fee_reserve,
|
||||
fee: meltQuote.fee_reserve.toNumber(),
|
||||
unit,
|
||||
data: JSON.stringify(transactionData),
|
||||
memo,
|
||||
@@ -137,7 +137,7 @@ export const transferTask = async function (
|
||||
// Replace individual setters with a single update
|
||||
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(
|
||||
Err.VALIDATION_ERROR,
|
||||
'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)
|
||||
|
||||
proofsToMeltFrom = CashuUtils.getProofsToSend(
|
||||
amountToTransfer + meltQuote.fee_reserve,
|
||||
amountToTransfer + meltQuote.fee_reserve.toNumber(),
|
||||
proofsFromMint
|
||||
)
|
||||
|
||||
proofsToMeltFromAmount = CashuUtils.getProofsAmount(proofsToMeltFrom)
|
||||
|
||||
const walletInstance = await walletStore.getWallet(mintUrl, unit, {withSeed: true})
|
||||
meltFeeReserve = walletInstance.getFeesForProofs(proofsToMeltFrom)
|
||||
const amountWithFees = amountToTransfer + meltQuote.fee_reserve + meltFeeReserve
|
||||
meltFeeReserve = walletInstance.getFeesForProofs(proofsToMeltFrom).toNumber()
|
||||
const amountWithFees = amountToTransfer + meltQuote.fee_reserve.toNumber() + meltFeeReserve
|
||||
|
||||
if (totalAmountFromMint < amountWithFees) {
|
||||
throw new AppError(
|
||||
@@ -217,15 +217,15 @@ export const transferTask = async function (
|
||||
transactionData.push({
|
||||
status: TransactionStatus.PREPARED,
|
||||
proofsToMeltFromAmount,
|
||||
lightningFeeReserve: meltQuote.fee_reserve,
|
||||
lightningFeeReserve: meltQuote.fee_reserve.toNumber(),
|
||||
meltFeeReserve,
|
||||
createdAt: new Date(),
|
||||
})
|
||||
|
||||
const inputToken = getEncodedToken({
|
||||
mint: mintUrl,
|
||||
proofs: proofsToMeltFrom,
|
||||
unit
|
||||
proofs: normalizeProofAmounts(proofsToMeltFrom),
|
||||
unit
|
||||
})
|
||||
|
||||
transaction.update({
|
||||
@@ -383,7 +383,7 @@ export const transferTask = async function (
|
||||
taskFunction: TRANSFER_TASK,
|
||||
mintUrl,
|
||||
transaction,
|
||||
message: 'Lightning payment is in progress. Check your transaction history for the result.',
|
||||
message: 'Lightning payment is in progress...',
|
||||
meltQuote: meltResponse.quote,
|
||||
nwcEvent,
|
||||
} as TransactionTaskResult
|
||||
|
||||
@@ -15,7 +15,7 @@ import {CashuProof, CashuUtils} from './cashu/cashuUtils'
|
||||
import {LightningUtils} from './lightning/lightningUtils'
|
||||
import AppError, {Err} from '../utils/AppError'
|
||||
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 {pollerExists, stopPolling} from '../utils/poller'
|
||||
import { NostrClient, NostrEvent, NostrProfile } from './nostrService'
|
||||
@@ -95,7 +95,7 @@ type WalletTaskService = {
|
||||
mintBalanceToTransferFrom: MintBalance,
|
||||
amountToTransfer: number,
|
||||
unit: MintUnit,
|
||||
meltQuote: MeltQuoteResponse,
|
||||
meltQuote: MeltQuoteBolt11Response,
|
||||
memo: string,
|
||||
invoiceExpiry: Date,
|
||||
encodedInvoice: string,
|
||||
@@ -149,7 +149,7 @@ type WalletTaskService = {
|
||||
}) => Promise<{recoveredAmount: number}>
|
||||
recoverMeltQuoteChange: (params: {
|
||||
mintUrl: string,
|
||||
meltQuote: string | MeltQuoteResponse
|
||||
meltQuote: string | MeltQuoteBolt11Response
|
||||
}) => Promise<{recoveredAmount: number}>
|
||||
handlePendingMeltTask: (params: {
|
||||
mintUrl: string
|
||||
@@ -301,7 +301,8 @@ const receiveQueueAwaitable = function (
|
||||
): Promise<TransactionTaskResult> {
|
||||
|
||||
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 taskId = proofsCount > MAX_SWAP_INPUT_SIZE
|
||||
@@ -338,7 +339,7 @@ const receiveQueueAwaitable = function (
|
||||
taskId,
|
||||
async () => {
|
||||
try {
|
||||
const token = getDecodedToken(encodedToken, mint.keysetIds)
|
||||
const token = getDecodedToken(encodedToken, mint.keysetIds ?? [])
|
||||
return proofsCount > MAX_SWAP_INPUT_SIZE
|
||||
? await receiveBatchTask(token, amount, memo || '', encodedToken)
|
||||
: await receiveTask(token, amount, memo || '', encodedToken)
|
||||
@@ -443,7 +444,8 @@ const receiveOfflinePrepareQueueAwaitable = function (
|
||||
): Promise<TransactionTaskResult> {
|
||||
const now = Date.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) => {
|
||||
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
|
||||
|
||||
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(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
|
||||
|
||||
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 keyset = cashuWallet.getKeyset(meltPreview.keysetId)
|
||||
|
||||
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 reconstructedOutputData = CashuUtils.deserializeOutputData(meltPreview.outputData)
|
||||
const recoveredChange = change.map((sig, i) => reconstructedOutputData[i].toProof(sig, keyset))
|
||||
currentCounter.removeMeltCounterValue(tx.id)
|
||||
|
||||
@@ -2383,19 +2377,11 @@ const handlePendingMeltTask = async (params: {
|
||||
{ withSeed: true, keysetId: meltPreview.keysetId })
|
||||
const keyset = cashuWallet.getKeyset(meltPreview.keysetId)
|
||||
|
||||
// Reconstruct OutputData instances: serializeMeltPreview converts bigint→string
|
||||
// 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 reconstructedOutputData = CashuUtils.deserializeOutputData(meltPreview.outputData)
|
||||
const change = quote.change.map((sig, i) => reconstructedOutputData[i].toProof(sig, keyset))
|
||||
|
||||
log.trace('[handlePendingMelt] Change unblinded', { transactionId, quoteId, change })
|
||||
|
||||
currentCounter.removeMeltCounterValue(transactionId)
|
||||
|
||||
if (change.length > 0) {
|
||||
@@ -2410,7 +2396,7 @@ const handlePendingMeltTask = async (params: {
|
||||
}
|
||||
}
|
||||
} 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) : []
|
||||
@@ -2540,7 +2526,7 @@ const handleClaimTask = async function (params: {
|
||||
|
||||
const result: TransactionTaskResult = await receiveTask(
|
||||
decoded,
|
||||
tokenInfo.amount,
|
||||
Number(tokenInfo.amount),
|
||||
tokenInfo.memo || 'Received to Lightning address',
|
||||
encodedToken,
|
||||
)
|
||||
@@ -2838,7 +2824,7 @@ const handleReceivedEventTask = async function (encryptedEvent: NostrEvent): Pro
|
||||
|
||||
// keysetsV2 support
|
||||
const tokenInfo = getTokenMetadata(incoming.encoded)
|
||||
const amountToReceive = tokenInfo.amount
|
||||
const amountToReceive = Number(tokenInfo.amount)
|
||||
const memo = tokenInfo.memo || 'Received over Nostr'
|
||||
const {unit, mint: mintUrl} = tokenInfo
|
||||
|
||||
@@ -3041,7 +3027,8 @@ const handleReceivedEventTask = async function (encryptedEvent: NostrEvent): Pro
|
||||
}
|
||||
|
||||
// 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[] = []
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"exclude": ["**/node_modules", "**/Pods"],
|
||||
"compilerOptions": {
|
||||
"jsx": "react",
|
||||
"typeRoots": ["./src/utils/envtypes"],
|
||||
"typeRoots": ["./src/utils/envtypes", "./node_modules/@types"],
|
||||
"lib": ["ES2022"],
|
||||
"paths": {
|
||||
"nostr-tools/*": ["./node_modules/nostr-tools/lib/types/*"]
|
||||
|
||||
62
yarn.lock
62
yarn.lock
@@ -2869,15 +2869,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@cashu/cashu-ts@npm:^3.6.1":
|
||||
version: 3.6.1
|
||||
resolution: "@cashu/cashu-ts@npm:3.6.1"
|
||||
"@cashu/cashu-ts@npm:^4.2.1":
|
||||
version: 4.2.1
|
||||
resolution: "@cashu/cashu-ts@npm:4.2.1"
|
||||
dependencies:
|
||||
"@noble/curves": ^2.0.1
|
||||
"@noble/hashes": ^2.0.1
|
||||
"@scure/base": ^2.0.0
|
||||
"@scure/bip32": ^2.0.1
|
||||
checksum: 07018c68fa9b5bd8affde081a6f5c6fb0ba67e1e6e1a3872527547461aea57a3aeb6bb6dace5dad430464b17f53503ad11d6a56b985a43cea3a8a18e8e86838b
|
||||
"@noble/curves": ^2.2.0
|
||||
"@noble/hashes": ^2.2.0
|
||||
"@scure/base": ^2.2.0
|
||||
"@scure/bip32": ^2.2.0
|
||||
checksum: a950f38686fcab220a61ea7b83c08f14f8af0ed5b54a18b38f8a370fc3e63897a6c1f266d8e7e9e269f05413d8b5f9d58d319bd1b194b2e2830cb7983169c931
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -5433,12 +5433,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@noble/curves@npm:2.0.1, @noble/curves@npm:^2.0.1":
|
||||
version: 2.0.1
|
||||
resolution: "@noble/curves@npm:2.0.1"
|
||||
"@noble/curves@npm:2.2.0, @noble/curves@npm:^2.2.0":
|
||||
version: 2.2.0
|
||||
resolution: "@noble/curves@npm:2.2.0"
|
||||
dependencies:
|
||||
"@noble/hashes": 2.0.1
|
||||
checksum: b6844f350d629bb6de55d3a123bc148403901c8b8eed77d2132a389d080d553242bcf1e06913bf51f51e395849ec70b9a8e8902c19e562ea3c6538bb0240d92f
|
||||
"@noble/hashes": 2.2.0
|
||||
checksum: 2b6f02c18918578f528791644886f54333c67323f5ccefe10bf250f08760f63786b807390d8d5f8562c661e70dd3559ce8e51e1fe360118c8fd47071c2bd6991
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -5465,13 +5465,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@noble/hashes@npm:2.0.1, @noble/hashes@npm:^2.0.1":
|
||||
"@noble/hashes@npm:2.0.1":
|
||||
version: 2.0.1
|
||||
resolution: "@noble/hashes@npm:2.0.1"
|
||||
checksum: aea67a671ca464027cd512d07666a804bd99abc343ba98c93ee8463a1c870c5594f8de468279ef3c60f3b23b6330db9f1a92bd23723b8a911ec73bddc9420248
|
||||
languageName: node
|
||||
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":
|
||||
version: 1.8.0
|
||||
resolution: "@noble/hashes@npm:1.8.0"
|
||||
@@ -6405,13 +6412,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@scure/base@npm:2.0.0, @scure/base@npm:^2.0.0":
|
||||
"@scure/base@npm:2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "@scure/base@npm:2.0.0"
|
||||
checksum: 15d10f154256f52bb3e02b63cdf93b6df6db3814fa4c61a8bb2d339831aec7859609fb715b93c7fc852755f0fe67fb6411664c06159745711bf38af3b10ab93e
|
||||
languageName: node
|
||||
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":
|
||||
version: 1.1.9
|
||||
resolution: "@scure/base@npm:1.1.9"
|
||||
@@ -6430,14 +6444,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@scure/bip32@npm:^2.0.1":
|
||||
version: 2.0.1
|
||||
resolution: "@scure/bip32@npm:2.0.1"
|
||||
"@scure/bip32@npm:^2.2.0":
|
||||
version: 2.2.0
|
||||
resolution: "@scure/bip32@npm:2.2.0"
|
||||
dependencies:
|
||||
"@noble/curves": 2.0.1
|
||||
"@noble/hashes": 2.0.1
|
||||
"@scure/base": 2.0.0
|
||||
checksum: 5e6c7b455c4a5599673d5fa1b7edc1b3655fe2f7b8388dc19ebe4a89adb9c80c511215d4af624eb774c63c216fd8b7bb8fecf1eb8c3d5e979e279c7542657a42
|
||||
"@noble/curves": 2.2.0
|
||||
"@noble/hashes": 2.2.0
|
||||
"@scure/base": 2.2.0
|
||||
checksum: 6cd0ddf603b739d3c24a13618fc246d409bbf0dffec22bc2c3c8e3e64271bb58c7078bea0b6ab9601fcfe881a969ee31e3074d478affe13a439d7527a0b68ecf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -14606,7 +14620,7 @@ __metadata:
|
||||
"@babel/plugin-proposal-export-namespace-from": ^7.18.9
|
||||
"@babel/preset-env": ^7.25.3
|
||||
"@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/free-brands-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-stack": ^7.3.27
|
||||
"@react-navigation/stack": ^7.4.9
|
||||
"@scure/bip32": ^2.0.1
|
||||
"@scure/bip32": ^2.2.0
|
||||
"@scure/bip39": 2.0.1
|
||||
"@sentry/cli": ^2.56.0
|
||||
"@sentry/react-native": ^7.7.0
|
||||
|
||||
Reference in New Issue
Block a user