Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/features/accounts/cashu-account.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Proof } from '@cashu/cashu-ts';
import { Amount, type Proof } from '@cashu/cashu-ts';
import { z } from 'zod/mini';
import { ProofSchema } from '~/lib/cashu/types';

Expand All @@ -25,7 +25,7 @@ export type CashuProof = z.infer<typeof CashuProofSchema>;
export const toProof = (proof: CashuProof): Proof => {
return {
id: proof.keysetId,
amount: proof.amount,
amount: Amount.from(proof.amount),
secret: proof.secret,
C: proof.unblindedSignature,
dleq: proof.dleq,
Expand Down
16 changes: 11 additions & 5 deletions app/features/receive/cashu-receive-quote-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,17 @@ export async function getLightningQuote(
description,
);

const expiresAt = new Date(mintQuoteResponse.expiry * 1000).toISOString();
const {
decoded: { paymentHash, expiryUnixMs },
} = decodeBolt11(mintQuoteResponse.request);

// NUT-23 defines mint quote expiry as the bolt11 invoice expiry; when the
// mint omits it, use the decoded invoice directly.
const expiresAt = new Date(
mintQuoteResponse.expiry !== null
? mintQuoteResponse.expiry * 1000
: expiryUnixMs,
).toISOString();

const mintingFee = mintQuoteResponse.fee
? new Money({
Expand All @@ -281,10 +291,6 @@ export async function getLightningQuote(
})
: undefined;

const {
decoded: { paymentHash },
} = decodeBolt11(mintQuoteResponse.request);

return {
mintQuote: mintQuoteResponse,
lockingPublicKey,
Expand Down
10 changes: 9 additions & 1 deletion app/features/receive/cashu-receive-quote-repository.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,15 @@ export class CashuReceiveQuoteRepositoryServer {
description,
mintingFee,
cashuTokenMeltData:
receiveType === 'CASHU_TOKEN' ? params.meltData : undefined,
receiveType === 'CASHU_TOKEN'
? {
...params.meltData,
tokenProofs: params.meltData.tokenProofs.map((p) => ({
...p,
amount: p.amount.toNumber(),
Comment thread
gudnuf marked this conversation as resolved.
})),
}
: undefined,
totalFee,
} satisfies z.input<typeof CashuLightningReceiveDbDataSchema>);

Expand Down
15 changes: 13 additions & 2 deletions app/features/receive/cashu-receive-quote-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,15 @@ export class CashuReceiveQuoteRepository {
description,
mintingFee,
cashuTokenMeltData:
receiveType === 'CASHU_TOKEN' ? params.meltData : undefined,
receiveType === 'CASHU_TOKEN'
? {
...params.meltData,
tokenProofs: params.meltData.tokenProofs.map((p) => ({
...p,
amount: p.amount.toNumber(),
})),
Comment on lines +70 to +73

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: this is duplicated on a lot of places, maybe we should extract it in a helper

export const toStoredProofs = (proofs: Proof[]) =>
  proofs.map((p) => ({ ...p, amount: p.amount.toNumber() }));

or maybe even the whole toStoredMeltData.

Up to you, not sure if it's worth it

}
: undefined,
totalFee,
} satisfies z.input<typeof CashuLightningReceiveDbDataSchema>);

Expand Down Expand Up @@ -316,7 +324,10 @@ export class CashuReceiveQuoteRepository {
*/
addedProofs: string[];
}> {
const dataToEncrypt = proofs.flatMap((x) => [x.amount, x.secret]);
const dataToEncrypt = proofs.flatMap((x) => [
x.amount.toNumber(),
x.secret,
]);
const encryptedData = await this.encryption.encryptBatch(dataToEncrypt);
const encryptedProofs = proofs.map((x, index) => {
const encryptedDataIndex = index * 2;
Expand Down
7 changes: 5 additions & 2 deletions app/features/receive/cashu-receive-quote-service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
Amount,
MintOperationError,
MintQuoteState,
OutputData,
Expand Down Expand Up @@ -246,7 +247,9 @@ export class CashuReceiveQuoteService {
const keyset = wallet.getKeyset(keysetId);
const cashuUnit = getCashuUnit(quote.amount.currency);
const amountInCashuUnit = quote.amount.toNumber(cashuUnit);
const outputAmounts = splitAmount(amountInCashuUnit, keyset.keys);
const outputAmounts = splitAmount(amountInCashuUnit, keyset.keys).map((a) =>
a.toNumber(),
Comment thread
gudnuf marked this conversation as resolved.
);

const result = await this.cashuReceiveQuoteRepository.processPayment({
quote,
Expand Down Expand Up @@ -322,7 +325,7 @@ export class CashuReceiveQuoteService {
state: MintQuoteState.PAID,
expiry: Math.floor(new Date(quote.expiresAt).getTime() / 1000),
pubkey: lockingPublicKey,
amount,
amount: Amount.from(amount),
unit: wallet.unit,
})
.keyset(quote.keysetId)
Expand Down
10 changes: 8 additions & 2 deletions app/features/receive/cashu-receive-swap-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,10 @@ export class CashuReceiveSwapRepository {
const receiveData = CashuSwapReceiveDbDataSchema.parse({
tokenMintUrl: token.mint,
tokenAmount: inputAmount,
tokenProofs: token.proofs,
tokenProofs: token.proofs.map((p) => ({
...p,
amount: p.amount.toNumber(),
})),
tokenDescription: token.memo,
amountReceived: receiveAmount,
outputAmounts,
Expand Down Expand Up @@ -174,7 +177,10 @@ export class CashuReceiveSwapRepository {
account: CashuAccount;
addedProofs: string[];
}> {
const dataToEncrypt = proofs.flatMap((x) => [x.amount, x.secret]);
const dataToEncrypt = proofs.flatMap((x) => [
x.amount.toNumber(),
x.secret,
]);
const encryptedData = await this.encryption.encryptBatch(dataToEncrypt);
const encryptedProofs = proofs.map((x, index) => {
const encryptedDataIndex = index * 2;
Expand Down
12 changes: 5 additions & 7 deletions app/features/receive/cashu-receive-swap-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export class CashuReceiveSwapService {
const wallet = account.wallet;

const keyset = wallet.getKeyset();
const fee = wallet.getFeesForProofs(token.proofs);
const fee = wallet.getFeesForProofs(token.proofs).toNumber();
const amountToReceive = sumProofs(token.proofs) - fee;

if (amountToReceive <= 0) {
Expand All @@ -89,7 +89,9 @@ export class CashuReceiveSwapService {
unit: cashuUnit,
});

const outputAmounts = splitAmount(amountToReceive, keyset.keys);
const outputAmounts = splitAmount(amountToReceive, keyset.keys).map((a) =>
a.toNumber(),
);

return await this.receiveSwapRepository.create({
token,
Expand Down Expand Up @@ -205,11 +207,7 @@ export class CashuReceiveSwapService {
) {
try {
return await wallet.ops
.receive({
mint: wallet.mint.mintUrl,
proofs: receiveSwap.tokenProofs,
unit: wallet.unit,
})
.receive(receiveSwap.tokenProofs)
.asCustom(outputData)
.run();
} catch (error) {
Expand Down
8 changes: 5 additions & 3 deletions app/features/receive/receive-cashu-token-quote-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ export class ReceiveCashuTokenQuoteService {
throw new Error('Must melt token to a different account than source');
}

const feesForProofs = sourceAccount.wallet.getFeesForProofs(token.proofs);
const feesForProofs = sourceAccount.wallet
.getFeesForProofs(token.proofs)
.toNumber();
const cashuReceiveFee = new Money({
amount: feesForProofs,
currency: tokenAmount.currency,
Expand All @@ -133,7 +135,7 @@ export class ReceiveCashuTokenQuoteService {
).toISOString();

const lightningFeeReserve = new Money({
amount: quotes.meltQuote.fee_reserve,
amount: quotes.meltQuote.fee_reserve.toNumber(),
currency: tokenAmount.currency,
unit: sourceCashuUnit,
});
Expand Down Expand Up @@ -251,7 +253,7 @@ export class ReceiveCashuTokenQuoteService {
await sourceAccount.wallet.createMeltQuoteBolt11(paymentRequest);

const amountRequired = new Money({
amount: meltQuote.amount + meltQuote.fee_reserve,
amount: meltQuote.amount.toNumber() + meltQuote.fee_reserve.toNumber(),
currency: sourceCurrency,
unit: getCashuUnit(sourceCurrency),
});
Expand Down
7 changes: 3 additions & 4 deletions app/features/receive/receive-cashu-token.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Token } from '@cashu/cashu-ts';
import { type Token, getEncodedToken } from '@cashu/cashu-ts';
import { useMutation } from '@tanstack/react-query';
import { AlertCircle } from 'lucide-react';
import { useState } from 'react';
Expand All @@ -21,7 +21,6 @@ import { Button } from '~/components/ui/button';
import { useFeatureFlag } from '~/features/shared/feature-flags';
import { useBuildLinkWithSearchParams } from '~/hooks/use-search-params-link';
import { useToast } from '~/hooks/use-toast';
import { encodeToken } from '~/lib/cashu/token';
import type { Currency } from '~/lib/money';
import {
LinkWithViewTransition,
Expand Down Expand Up @@ -88,7 +87,7 @@ function TokenAmountDisplay({
className="z-10 transition-transform active:scale-95"
onClick={() => {
copyToClipboard(
encodeToken(claimableToken ?? token, { removeDleq: true }),
getEncodedToken(claimableToken ?? token, { removeDleq: true }),
);
toast({
title: 'Token copied to clipboard',
Expand Down Expand Up @@ -387,7 +386,7 @@ export function PublicReceiveCashuToken({ token }: { token: Token }) {
const mintRequiresTerms =
accountRequiresGiftCardTermsAcceptance(sourceAccount);

const encodedToken = encodeToken(claimableToken ?? token, {
const encodedToken = getEncodedToken(claimableToken ?? token, {
removeDleq: true,
});

Expand Down
10 changes: 9 additions & 1 deletion app/features/receive/spark-receive-quote-repository.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,15 @@ export class SparkReceiveQuoteRepositoryServer {
amountReceived: amount,
description,
cashuTokenMeltData:
receiveType === 'CASHU_TOKEN' ? params.meltData : undefined,
receiveType === 'CASHU_TOKEN'
? {
...params.meltData,
tokenProofs: params.meltData.tokenProofs.map((p) => ({
...p,
amount: p.amount.toNumber(),
})),
}
: undefined,
totalFee,
} satisfies z.input<typeof SparkLightningReceiveDbDataSchema>);

Expand Down
10 changes: 9 additions & 1 deletion app/features/receive/spark-receive-quote-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,15 @@ export class SparkReceiveQuoteRepository {
amountReceived: amount,
description,
cashuTokenMeltData:
receiveType === 'CASHU_TOKEN' ? params.meltData : undefined,
receiveType === 'CASHU_TOKEN'
? {
...params.meltData,
tokenProofs: params.meltData.tokenProofs.map((p) => ({
...p,
amount: p.amount.toNumber(),
})),
}
: undefined,
totalFee,
} satisfies z.input<typeof SparkLightningReceiveDbDataSchema>);

Expand Down
8 changes: 4 additions & 4 deletions app/features/scan/classify-input.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, test } from 'bun:test';
import { type Token, getEncodedToken } from '@cashu/cashu-ts';
import { Amount, type Token, getEncodedToken } from '@cashu/cashu-ts';
import { classifyInput } from './classify-input';

// -- Test fixtures --
Expand All @@ -9,16 +9,16 @@ const CASHU_TOKEN: Token = {
proofs: [
{
id: '009a1f293253e41e',
amount: 1,
amount: Amount.from(1),
secret: 'test-secret-1',
C: '02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904',
},
],
unit: 'sat',
};

const CASHU_A_TOKEN = getEncodedToken(CASHU_TOKEN, { version: 3 });
const CASHU_B_TOKEN = getEncodedToken(CASHU_TOKEN, { version: 4 });
const CASHU_A_TOKEN = getEncodedToken(CASHU_TOKEN);
const CASHU_B_TOKEN = getEncodedToken(CASHU_TOKEN);

// Real BOLT11 test vector from bolt11.test.ts (250,000 sats, "1 cup coffee")
const BOLT11_INVOICE =
Expand Down
2 changes: 1 addition & 1 deletion app/features/send/cashu-send-quote-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ export class CashuSendQuoteRepository {
} satisfies z.input<typeof CashuLightningSendDbDataSchema>);

const proofDataToEncrypt = changeProofs.flatMap((x) => [
x.amount,
x.amount.toNumber(),
x.secret,
]);

Expand Down
21 changes: 12 additions & 9 deletions app/features/send/cashu-send-quote-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,21 +148,23 @@ export class CashuSendQuoteService {
const wallet = account.wallet;

const meltQuote = await wallet.createMeltQuoteBolt11(paymentRequest);
const meltAmount = meltQuote.amount.toNumber();
const feeReserve = meltQuote.fee_reserve.toNumber();

const amountWithLightningFee = meltQuote.amount + meltQuote.fee_reserve;
const amountWithLightningFee = meltAmount + feeReserve;

const { proofs, fee: proofsFee } = this.selectProofs(
account,
amountWithLightningFee,
);

const amountToReceive = new Money({
amount: meltQuote.amount,
amount: meltAmount,
currency: account.currency,
unit: cashuUnit,
});
const lightningFeeReserve = new Money({
amount: meltQuote.fee_reserve,
amount: feeReserve,
currency: account.currency,
unit: cashuUnit,
});
Expand Down Expand Up @@ -249,7 +251,9 @@ export class CashuSendQuoteService {
const keyset = wallet.getKeyset();
const keysetId = keyset.id;

const amountWithLightningFee = meltQuote.amount + meltQuote.fee_reserve;
const meltAmount = meltQuote.amount.toNumber();
const feeReserve = meltQuote.fee_reserve.toNumber();
const amountWithLightningFee = meltAmount + feeReserve;

const { proofs, fee: proofsFee } = this.selectProofs(
account,
Expand All @@ -260,12 +264,12 @@ export class CashuSendQuoteService {
const totalAmountToSend = amountWithLightningFee + proofsFee;

const amountToReceive = new Money({
amount: meltQuote.amount,
amount: meltAmount,
currency: account.currency,
unit: cashuUnit,
});
const lightningFeeReserve = new Money({
amount: meltQuote.fee_reserve,
amount: feeReserve,
currency: account.currency,
unit: cashuUnit,
});
Expand All @@ -283,8 +287,7 @@ export class CashuSendQuoteService {
);
}

const maxPotentialChangeAmount =
proofsToSendSum - meltQuote.amount - proofsFee;
const maxPotentialChangeAmount = proofsToSendSum - meltAmount - proofsFee;
const numberOfChangeOutputs =
maxPotentialChangeAmount === 0
? 0
Expand Down Expand Up @@ -562,7 +565,7 @@ export class CashuSendQuoteService {

return {
proofs: selectedProofs,
fee: account.wallet.getFeesForProofs(send),
fee: account.wallet.getFeesForProofs(send).toNumber(),
};
}
}
Expand Down
5 changes: 4 additions & 1 deletion app/features/send/cashu-send-swap-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,10 @@ export class CashuSendSwapRepository {
changeProofs: Proof[];
}) {
const allProofs = proofsToSend.concat(changeProofs);
const proofDataToEncrypt = allProofs.flatMap((x) => [x.amount, x.secret]);
const proofDataToEncrypt = allProofs.flatMap((x) => [
x.amount.toNumber(),
x.secret,
]);

const encryptedProofData =
await this.encryption.encryptBatch(proofDataToEncrypt);
Expand Down
Loading
Loading