feat: add CDK multimint qoute planning and payment progress

plan multimint melt qoutes before payments
show live status updates for each mint while payment in progress
save payment results and refresh balances after send
This commit is contained in:
Khushal
2026-06-01 06:06:31 +05:30
parent 83ac9c0db3
commit 278e5443fd
3 changed files with 537 additions and 192 deletions
+486 -154
View File
@@ -56,7 +56,9 @@ import Base64Utils from '../utils/Base64Utils';
import { BIP39_WORD_LIST } from '../utils/Bip39Utils';
import CashuUtils, {
MintPaymentStatus,
MintProgressInfo
MintProgressInfo,
MultimintProgressCallback,
MultinutPaymentStep
} from '../utils/CashuUtils';
import { errorToUserFriendly } from '../utils/ErrorUtils';
import { localeString } from '../utils/LocaleUtils';
@@ -206,6 +208,10 @@ export default class CashuStore {
@observable public paymentError?: boolean;
@observable public paymentErrorMsg?: string;
@observable public feeEstimate?: number;
@observable public meltQuotes: {
mintUrl: string;
meltQuote: CDKMeltQuote;
}[] = [];
@observable public meltQuote?: {
quote: string;
amount: number;
@@ -994,6 +1000,15 @@ export default class CashuStore {
return totalReceived;
};
private getMintName = (mintUrl: string): string => {
const normalizedMintUrl = this.normalizeMintUrl(mintUrl);
return (
this.mintInfos[normalizedMintUrl]?.name ||
this.mintInfos[mintUrl]?.name ||
normalizedMintUrl
);
};
private mintSupportsMpp = (mintUrl: string): boolean => {
const normalizedMintUrl = this.normalizeMintUrl(mintUrl);
const mintInfo =
@@ -1003,20 +1018,172 @@ export default class CashuStore {
return !!(nuts && (nuts[15] || nuts['15']));
};
private prepareMultiMintMeltQuotesCDK = async ({
invoice,
amountToPay,
mintUrls
}: {
invoice: string;
amountToPay: number;
mintUrls?: string[];
}): Promise<
{
mintUrl: string;
meltQuote: CDKMeltQuote;
}[]
> => {
const selectedMints =
mintUrls && mintUrls.length > 0
? mintUrls
: this.selectedMintUrls.length > 0
? this.selectedMintUrls
: this.selectedMintUrl
? [this.selectedMintUrl]
: [];
const uniqueSelectedMints = Array.from(
new Set(selectedMints.map((mint) => this.normalizeMintUrl(mint)))
);
const supportedMints = uniqueSelectedMints.filter((mintUrl) =>
this.mintSupportsMpp(mintUrl)
);
const balances = await CashuDevKit.getBalances();
const normalizedBalances = Object.entries(balances).reduce(
(acc, [mintUrl, balance]) => {
acc[this.normalizeMintUrl(mintUrl)] = balance;
return acc;
},
{} as Record<string, number>
);
const mintsBySmallestBalance = supportedMints
.map((mintUrl) => ({
mintUrl,
balance: normalizedBalances[mintUrl] || 0
}))
.filter(({ balance }) => balance > 0)
.sort((a, b) => a.balance - b.balance);
if (mintsBySmallestBalance.length === 0 || amountToPay <= 0) {
this.meltQuotes = [];
return [];
}
const nextMeltQuotes: { mintUrl: string; meltQuote: CDKMeltQuote }[] =
[];
let remainingAmount = amountToPay;
for (const { mintUrl } of mintsBySmallestBalance) {
if (remainingAmount <= 0) {
break;
}
const mintBalance = normalizedBalances[mintUrl] || 0;
if (mintBalance <= 0) {
continue;
}
const initialAllocation = Math.min(remainingAmount, mintBalance);
if (initialAllocation <= 0) {
continue;
}
await this.ensureMintAdded(mintUrl);
let quote = await this.createMeltQuoteCDK(mintUrl, invoice, {
mpp: { amount: initialAllocation }
});
let requiredFromMint = quote.amount + quote.fee_reserve;
if (requiredFromMint > mintBalance) {
const affordableAllocation = Math.max(
0,
Math.min(remainingAmount, mintBalance - quote.fee_reserve)
);
if (affordableAllocation <= 0) {
continue;
}
quote = await this.createMeltQuoteCDK(mintUrl, invoice, {
mpp: { amount: affordableAllocation }
});
requiredFromMint = quote.amount + quote.fee_reserve;
if (requiredFromMint > mintBalance) {
continue;
}
}
nextMeltQuotes.push({ mintUrl, meltQuote: quote });
remainingAmount = Math.max(0, remainingAmount - quote.amount);
}
this.meltQuotes = nextMeltQuotes;
return nextMeltQuotes;
};
@action
public getMultimintInfo = (): MintProgressInfo[] => {
const mintUrls =
this.meltQuotes.length > 0
? this.meltQuotes.map(({ mintUrl }) => mintUrl)
: this.selectedMintUrls.length > 0
? this.selectedMintUrls
: this.selectedMintUrl
? [this.selectedMintUrl]
: [];
const uniqueMintUrls = Array.from(
new Set(mintUrls.map((mintUrl) => this.normalizeMintUrl(mintUrl)))
);
const selectedMintSet = new Set(
(this.selectedMintUrls || []).map((mintUrl) =>
this.normalizeMintUrl(mintUrl)
)
);
return uniqueMintUrls.map((mintUrl) => ({
mintUrl,
mintName: this.getMintName(mintUrl),
balance:
this.mintBalances[mintUrl] ||
this.mintBalances[this.normalizeMintUrl(mintUrl)] ||
0,
selected: selectedMintSet.has(mintUrl),
status: MintPaymentStatus.IDLE,
allocatedAmount: 0
}));
};
private payLnInvoiceFromEcashMultiMint = async ({
paymentAmt,
isDonationPayment
isDonationPayment,
onProgress
}: {
paymentAmt: number;
isDonationPayment: boolean;
onProgress?: MultimintProgressCallback;
}): Promise<CashuPayment | undefined> => {
const paymentRequest = this.paymentRequest;
const emptyMints: MintProgressInfo[] = [];
if (!paymentRequest) {
runInAction(() => {
this.paymentError = true;
this.paymentErrorMsg = 'No payment request available';
this.loading = false;
});
onProgress?.({
step: MultinutPaymentStep.FAILED,
mints: emptyMints,
totalSelectedBalance: 0,
isProcessing: false
});
return;
}
@@ -1043,27 +1210,63 @@ export default class CashuStore {
);
this.loading = false;
});
onProgress?.({
step: MultinutPaymentStep.FAILED,
mints: emptyMints,
totalSelectedBalance: 0,
isProcessing: false
});
return;
}
const balances = await CashuDevKit.getBalances();
const normalizedBalances = Object.entries(balances).reduce(
(acc, [mintUrl, balance]) => {
acc[this.normalizeMintUrl(mintUrl)] = balance;
return acc;
},
{} as Record<string, number>
let plannedMeltQuotes: { mintUrl: string; meltQuote: CDKMeltQuote }[];
try {
plannedMeltQuotes = await this.prepareMultiMintMeltQuotesCDK({
invoice: paymentRequest,
amountToPay: paymentAmt,
mintUrls: supportedMints
});
} catch (error: any) {
runInAction(() => {
this.paymentError = true;
this.paymentErrorMsg =
error?.message ||
localeString('stores.CashuStore.errorPayingInvoice');
this.loading = false;
});
onProgress?.({
step: MultinutPaymentStep.FAILED,
mints: emptyMints,
totalSelectedBalance: 0,
isProcessing: false
});
return;
}
if (!plannedMeltQuotes.length) {
runInAction(() => {
this.paymentError = true;
this.paymentErrorMsg = localeString(
'stores.CashuStore.notEnoughFunds'
);
this.loading = false;
});
onProgress?.({
step: MultinutPaymentStep.FAILED,
mints: emptyMints,
totalSelectedBalance: 0,
isProcessing: false
});
return;
}
const totalPlannedAmount = plannedMeltQuotes.reduce(
(sum, { meltQuote }) => sum + (meltQuote.amount || 0),
0
);
const mintsBySmallestBalance = supportedMints
.map((mintUrl) => ({
mintUrl,
balance: normalizedBalances[mintUrl] || 0
}))
.filter(({ balance }) => balance > 0)
.sort((a, b) => a.balance - b.balance);
if (mintsBySmallestBalance.length === 0) {
if (totalPlannedAmount < paymentAmt) {
runInAction(() => {
this.paymentError = true;
this.paymentErrorMsg = localeString(
@@ -1071,106 +1274,163 @@ export default class CashuStore {
);
this.loading = false;
});
onProgress?.({
step: MultinutPaymentStep.FAILED,
mints: emptyMints,
totalSelectedBalance: 0,
isProcessing: false
});
return;
}
let remainingAmount = paymentAmt;
let totalFeePaid = 0;
const mintProgressInfo: MintProgressInfo[] = plannedMeltQuotes.map(
({ mintUrl, meltQuote }) => ({
mintUrl,
mintName: this.getMintName(mintUrl),
balance: this.mintBalances[mintUrl] || 0,
selected: true,
status: MintPaymentStatus.IDLE,
allocatedAmount: meltQuote.amount,
error: undefined
})
);
const totalSelectedBalance = mintProgressInfo.reduce(
(sum, mint) => sum + mint.balance,
0
);
onProgress?.({
step: MultinutPaymentStep.PROCESSING,
mints: [...mintProgressInfo],
totalSelectedBalance,
isProcessing: true
});
const segmentPayments: CashuPayment[] = [];
const failedMints: { mintUrl: string; reason: string }[] = [];
for (const { mintUrl } of mintsBySmallestBalance) {
if (remainingAmount <= 0) {
break;
}
for (let index = 0; index < plannedMeltQuotes.length; index++) {
const { mintUrl, meltQuote } = plannedMeltQuotes[index];
let mintBalance = normalizedBalances[mintUrl] || 0;
if (mintBalance <= 0) {
continue;
}
try {
mintProgressInfo[index].status = MintPaymentStatus.REQUESTING;
mintProgressInfo[index].error = undefined;
const initialAllocation = Math.min(remainingAmount, mintBalance);
if (initialAllocation <= 0) {
continue;
}
await this.ensureMintAdded(mintUrl);
let quote = await this.createMeltQuoteCDK(mintUrl, paymentRequest, {
mpp: { amount: initialAllocation }
});
let requiredFromMint = quote.amount + quote.fee_reserve;
if (requiredFromMint > mintBalance) {
const affordableAllocation = Math.max(
0,
Math.min(remainingAmount, mintBalance - quote.fee_reserve)
);
if (affordableAllocation <= 0) {
continue;
}
quote = await this.createMeltQuoteCDK(mintUrl, paymentRequest, {
mpp: { amount: affordableAllocation }
onProgress?.({
step: MultinutPaymentStep.PROCESSING,
mints: [...mintProgressInfo],
totalSelectedBalance,
isProcessing: true
});
requiredFromMint = quote.amount + quote.fee_reserve;
if (requiredFromMint > mintBalance) {
continue;
await this.ensureMintAdded(mintUrl);
mintProgressInfo[index].status = MintPaymentStatus.PAYING;
onProgress?.({
step: MultinutPaymentStep.PROCESSING,
mints: [...mintProgressInfo],
totalSelectedBalance,
isProcessing: true
});
const meltResult = await CashuDevKit.melt(
mintUrl,
meltQuote.id
);
const meltState = (
meltResult.state ||
meltQuote.state ||
''
).toString();
const normalizedMeltState =
meltState.length > 0 ? meltState.toUpperCase() : 'PAID';
if (normalizedMeltState !== 'PAID') {
throw new Error(
localeString('stores.CashuStore.errorPayingInvoice')
);
}
}
const meltResult = await CashuDevKit.melt(mintUrl, quote.id);
const paidAmount = meltResult.amount || quote.amount;
const meltState = (
meltResult.state ||
quote.state ||
''
).toString();
const normalizedMeltState =
meltState.length > 0 ? meltState.toUpperCase() : 'PAID';
const paymentPreimage =
meltResult.preimage || meltQuote.payment_preimage || '';
totalFeePaid += meltResult.fee_paid || 0;
remainingAmount = Math.max(0, remainingAmount - paidAmount);
mintBalance = Math.max(0, mintBalance - requiredFromMint);
normalizedBalances[mintUrl] = mintBalance;
const payment = new CashuPayment({
...this.payReq,
bolt11: this.paymentRequest,
meltResponse: {
quote: {
quote: quote.id,
amount: quote.amount,
fee_reserve: quote.fee_reserve,
const payment = new CashuPayment({
...this.payReq,
bolt11: this.paymentRequest,
meltResponse: {
quote: {
quote: meltQuote.id,
amount: meltQuote.amount,
fee_reserve: meltQuote.fee_reserve,
state: normalizedMeltState,
expiry: meltQuote.expiry,
payment_preimage: paymentPreimage || null
},
change: meltResult.change,
state: normalizedMeltState,
expiry: quote.expiry,
payment_preimage: meltResult.preimage || null
amount: meltResult.amount,
fee_paid: meltResult.fee_paid,
preimage: paymentPreimage
},
change: meltResult.change,
state: normalizedMeltState,
amount: meltResult.amount,
fee_paid: meltResult.fee_paid,
preimage: meltResult.preimage
},
amount: meltResult.amount,
fee: meltResult.fee_paid,
payment_preimage: meltResult.preimage || '',
mintUrl
});
amount: meltResult.amount || meltQuote.amount,
fee: meltResult.fee_paid,
payment_preimage: paymentPreimage,
mintUrl
});
segmentPayments.push(payment);
segmentPayments.push(payment);
mintProgressInfo[index].status = MintPaymentStatus.SUCCESS;
mintProgressInfo[index].error = undefined;
onProgress?.({
step: MultinutPaymentStep.PROCESSING,
mints: [...mintProgressInfo],
totalSelectedBalance,
isProcessing: true
});
} catch (error: any) {
const errorMessage =
error?.message ||
localeString('stores.CashuStore.errorPayingInvoice');
failedMints.push({
mintUrl,
reason: errorMessage
});
mintProgressInfo[index].status = MintPaymentStatus.FAILED;
mintProgressInfo[index].error = errorMessage;
onProgress?.({
step: MultinutPaymentStep.PROCESSING,
mints: [...mintProgressInfo],
totalSelectedBalance,
isProcessing: true
});
}
}
if (remainingAmount > 0) {
if (failedMints.length > 0 || segmentPayments.length === 0) {
const firstFailure = failedMints[0]?.reason;
runInAction(() => {
this.paymentError = true;
this.paymentErrorMsg = localeString(
'stores.CashuStore.notEnoughFunds'
);
this.paymentErrorMsg =
firstFailure ||
localeString('stores.CashuStore.errorPayingInvoice');
this.loading = false;
});
onProgress?.({
step: MultinutPaymentStep.FAILED,
mints: [...mintProgressInfo],
totalSelectedBalance,
isProcessing: false
});
return;
}
@@ -1182,17 +1442,24 @@ export default class CashuStore {
await this.syncCDKBalances(true);
const totalFeePaid = segmentPayments.reduce(
(sum, payment) => sum + (Number(payment.fee) || 0),
0
);
const firstPreimage = segmentPayments
.map((payment) => payment?.payment_preimage)
.find((value) => !!value);
const usedMints = segmentPayments
.map((payment) => this.normalizeMintUrl(payment.mintUrl))
.filter((mintUrl, index, list) => list.indexOf(mintUrl) === index);
runInAction(() => {
this.lastMultiMintUsedMints = segmentPayments.map(
(payment) => payment.mintUrl
);
this.lastMultiMintUsedMints = usedMints;
if (!isDonationPayment) {
const preimage = segmentPayments
.map((payment) => payment?.payment_preimage)
.find((value) => !!value);
this.paymentPreimage = preimage || '';
this.paymentPreimage = firstPreimage || '';
this.paymentSuccess = true;
this.paymentFee = totalFeePaid;
this.noteKey = segmentPayments[0]?.getNoteKey;
@@ -1205,6 +1472,13 @@ export default class CashuStore {
}
});
onProgress?.({
step: MultinutPaymentStep.COMPLETE,
mints: [...mintProgressInfo],
totalSelectedBalance,
isProcessing: false
});
return segmentPayments[0];
};
@@ -1556,6 +1830,7 @@ export default class CashuStore {
this.paymentPreimage = undefined;
this.getPayReqError = undefined;
this.feeEstimate = undefined;
this.meltQuotes = [];
this.meltQuote = undefined;
this.noteKey = undefined;
this.error = false;
@@ -2382,21 +2657,6 @@ export default class CashuStore {
}
};
@action
public getMultimintInfo = (): MintProgressInfo[] => {
const availableMints = this.mintUrls.map((mintUrl) => {
const mintName = this.mintInfos[mintUrl]?.name || mintUrl;
return {
mintUrl,
mintName,
balance: this.mintBalances[mintUrl] || 0,
status: MintPaymentStatus.IDLE,
error: undefined
};
});
return availableMints;
};
@action
public removeMint = async (mintUrl: string) => {
this.loading = true;
@@ -3742,36 +4002,104 @@ export default class CashuStore {
console.log('getPayReq: bolt11 decoded');
}
// Create melt quote via CDK
if (__DEV__) {
console.log('getPayReq: About to call createMeltQuoteCDK');
}
const cdkQuote = await this.createMeltQuoteCDK(
this.selectedMintUrl,
bolt11Invoice
);
if (__DEV__) {
console.log(
'getPayReq: createMeltQuoteCDK completed',
cdkQuote
const payReq = new Invoice(data);
const paymentAmt = payReq.getRequestAmount
? payReq.getRequestAmount
: 0;
const isMultiMint =
!!this.settingsStore.settings?.ecash?.enableMultiMint &&
Array.isArray(this.selectedMintUrls) &&
this.selectedMintUrls.length > 1;
let singleMeltQuote:
| {
quote: string;
amount: number;
fee_reserve: number;
state: string;
expiry: number;
payment_preimage?: string | null;
}
| undefined;
let totalFeeEstimate = 0;
if (isMultiMint) {
if (paymentAmt > 0) {
if (__DEV__) {
console.log(
'getPayReq: preparing multi-mint melt quotes'
);
}
const preparedQuotes =
await this.prepareMultiMintMeltQuotesCDK({
invoice: bolt11Invoice,
amountToPay: paymentAmt
});
if (!preparedQuotes.length) {
throw new Error(
localeString('stores.CashuStore.notEnoughFunds')
);
}
const totalAllocated = preparedQuotes.reduce(
(sum, { meltQuote }) => sum + (meltQuote.amount || 0),
0
);
if (totalAllocated < paymentAmt) {
throw new Error(
localeString('stores.CashuStore.notEnoughFunds')
);
}
totalFeeEstimate = preparedQuotes.reduce(
(sum, { meltQuote }) =>
sum + (meltQuote.fee_reserve || 0),
0
);
} else {
this.meltQuotes = [];
totalFeeEstimate = 0;
}
} else {
if (__DEV__) {
console.log('getPayReq: About to call createMeltQuoteCDK');
}
const cdkQuote = await this.createMeltQuoteCDK(
this.selectedMintUrl,
bolt11Invoice
);
if (__DEV__) {
console.log(
'getPayReq: createMeltQuoteCDK completed',
cdkQuote
);
}
singleMeltQuote = {
quote: cdkQuote.id,
amount: cdkQuote.amount,
fee_reserve: cdkQuote.fee_reserve,
state: cdkQuote.state as any,
expiry: cdkQuote.expiry,
payment_preimage: cdkQuote.payment_preimage
};
totalFeeEstimate = singleMeltQuote.fee_reserve || 0;
this.meltQuotes = [];
}
const meltQuote = {
quote: cdkQuote.id,
amount: cdkQuote.amount,
fee_reserve: cdkQuote.fee_reserve,
state: cdkQuote.state as any,
expiry: cdkQuote.expiry,
payment_preimage: cdkQuote.payment_preimage
};
runInAction(() => {
if (!isDonationPayment) {
this.payReq = new Invoice(data);
this.getPayReqError = undefined;
this.feeEstimate = meltQuote.fee_reserve || 0;
}
this.meltQuote = meltQuote;
this.payReq = payReq;
this.getPayReqError = undefined;
this.meltQuote = singleMeltQuote;
this.feeEstimate = totalFeeEstimate;
});
if (__DEV__) {
console.log('getPayReq: Success, setting loading = false');
@@ -3781,12 +4109,12 @@ export default class CashuStore {
console.log('getPayReq: Error caught', e?.message || e);
}
const errorMsg = errorToUserFriendly(e);
if (!isDonationPayment) {
runInAction(() => {
this.payReq = undefined;
this.getPayReqError = errorMsg;
});
}
runInAction(() => {
this.payReq = undefined;
this.meltQuotes = [];
this.meltQuote = undefined;
this.getPayReqError = errorMsg;
});
} finally {
// ALWAYS set loading = false when done
if (__DEV__) {
@@ -3805,10 +4133,12 @@ export default class CashuStore {
@action
public payLnInvoiceFromEcash = async ({
amount,
isDonationPayment = false
isDonationPayment = false,
onProgress
}: {
amount?: string;
isDonationPayment?: boolean;
onProgress?: MultimintProgressCallback;
}) => {
// Reset payment state (don't set loading=true here as it triggers LoadingIndicator on CashuPaymentRequest)
if (isDonationPayment) {
@@ -3818,6 +4148,7 @@ export default class CashuStore {
this.paymentSuccess = false;
this.paymentError = false;
this.paymentErrorMsg = '';
this.lastMultiMintUsedMints = [];
this.paymentStartTime = Date.now();
}
@@ -3836,7 +4167,8 @@ export default class CashuStore {
if (shouldUseMultiMint) {
return await this.payLnInvoiceFromEcashMultiMint({
paymentAmt,
isDonationPayment
isDonationPayment,
onProgress
});
}
+32 -23
View File
@@ -1,5 +1,37 @@
import CashuDevKit, { CDKToken } from '../cashu-cdk';
export enum MintPaymentStatus {
IDLE = 'idle',
REQUESTING = 'requesting',
PAYING = 'paying',
SUCCESS = 'success',
FAILED = 'failed'
}
export interface MintProgressInfo {
mintUrl: string;
mintName?: string;
balance: number;
selected: boolean;
status: MintPaymentStatus;
allocatedAmount?: number;
error?: string;
}
export enum MultinutPaymentStep {
SELECT = 'select',
PROCESSING = 'processing',
COMPLETE = 'complete',
FAILED = 'failed'
}
export type MultimintProgressCallback = (progress: {
step: MultinutPaymentStep;
mints: MintProgressInfo[];
totalSelectedBalance: number;
isProcessing: boolean;
}) => void;
export const cashuTokenPrefixes = [
'https://wallet.nutstash.app/#',
'https://wallet.cashu.me/?token=',
@@ -191,26 +223,3 @@ class CashuUtils {
const cashuUtils = new CashuUtils();
export default cashuUtils;
export enum MintPaymentStatus {
IDLE = 'idle',
REQUESTING = 'requesting',
PAYING = 'paying',
SUCCESS = 'success',
FAILED = 'failed'
}
export enum MultinutPaymentStep {
IDLE = 'idle',
PROCESSING = 'processing',
COMPLETE = 'complete',
FAILED = 'failed'
}
export interface MintProgressInfo {
mintUrl: string;
mintName: string;
balance: number;
status: MintPaymentStatus;
error?: string;
}
+19 -15
View File
@@ -71,14 +71,14 @@ export default class MultimintPayment extends React.Component<
selectedMints.length > 0 ? selectedMints : availableMints;
const totalSelectedBalance = effectiveMints.reduce(
(sum, mint) => sum + Number(mint.balance || 0),
(sum, mint) => sum + mint.balance,
0
);
const requestAmount =
CashuStore?.payReq?.getRequestAmount ||
Number(route.params?.paymentAmount || 0);
const feeEstimate = Number(CashuStore?.feeEstimate) || 0;
const feeEstimate = CashuStore?.feeEstimate || 0;
const totalNeeded = requestAmount + feeEstimate;
const hasNoMintsSelected = effectiveMints.length === 0;
@@ -134,6 +134,15 @@ export default class MultimintPayment extends React.Component<
executePayment = async () => {
const { CashuStore, route } = this.props;
if (!CashuStore) {
this.setState({
step: MultinutPaymentStep.FAILED,
isProcessing: false,
error: 'CashuStore not available'
});
return;
}
try {
this.setState((prev) => ({
step: MultinutPaymentStep.PROCESSING,
@@ -146,16 +155,17 @@ export default class MultimintPayment extends React.Component<
}))
}));
const payment = await CashuStore!!.payLnInvoiceFromEcash({
amount: route.params?.paymentAmount
const payment = await CashuStore.payLnInvoiceFromEcash({
amount: route.params?.paymentAmount,
onProgress: this.onProgressUpdate
});
if (!payment || CashuStore!!.paymentError) {
if (!payment || CashuStore.paymentError) {
this.setState({
step: MultinutPaymentStep.FAILED,
isProcessing: false,
error:
CashuStore!!.paymentErrorMsg ||
CashuStore.paymentErrorMsg ||
localeString('stores.CashuStore.errorPayingInvoice')
});
return;
@@ -199,14 +209,10 @@ export default class MultimintPayment extends React.Component<
}`;
}
if (item.status === MintPaymentStatus.PAYING && isProcessing) {
return `${localeString(
'views.Cashu.MultimintPayment.paying'
)} | ${item.mintUrl}`;
return `Paying... | ${item.mintUrl}`;
}
if (item.status === MintPaymentStatus.REQUESTING && isProcessing) {
return `${localeString(
'views.Cashu.MultimintPayment.requesting'
)} | ${item.mintUrl}`;
return `Requesting... | ${item.mintUrl}`;
}
return item.mintUrl;
};
@@ -391,9 +397,7 @@ export default class MultimintPayment extends React.Component<
fontFamily: 'PPNeueMontreal-Book'
}}
>
{localeString(
'views.Cashu.MultimintPayment.totalAvailable'
)}
Total Available:
</Text>
<Amount
sats={totalSelectedBalance}