feat: BalancePane: surface pending close balance alongside pending open
This commit is contained in:
+26
-6
@@ -347,9 +347,6 @@ export default class LdkNode {
|
||||
pendingCloseBalance = pendingCloseBalance.plus(sb.amountSatoshis);
|
||||
}
|
||||
|
||||
const totalPendingBalance =
|
||||
pendingOpenBalance.plus(pendingCloseBalance);
|
||||
|
||||
return {
|
||||
balance: localBalance.toString(),
|
||||
local_balance: {
|
||||
@@ -360,10 +357,10 @@ export default class LdkNode {
|
||||
sat: remoteBalance.toString(),
|
||||
msat: remoteBalance.times(1000).toString()
|
||||
},
|
||||
pending_open_balance: totalPendingBalance.toString(),
|
||||
pending_open_balance: pendingOpenBalance.toString(),
|
||||
pending_open_local_balance: {
|
||||
sat: totalPendingBalance.toString(),
|
||||
msat: totalPendingBalance.times(1000).toString()
|
||||
sat: pendingOpenBalance.toString(),
|
||||
msat: pendingOpenBalance.times(1000).toString()
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -686,7 +683,30 @@ export default class LdkNode {
|
||||
}
|
||||
}
|
||||
|
||||
// total_limbo_balance: sats in close-side limbo (cooperative closes
|
||||
// awaiting confirmation + force-close timelocks + waiting closes).
|
||||
// Matches lnrpc.PendingChannelsResponse.total_limbo_balance semantics
|
||||
// so ChannelsStore can read it uniformly across backends.
|
||||
let totalLimboBalance = new BigNumber(0);
|
||||
for (const lb of balances.lightningBalances) {
|
||||
if (lb.type === 'claimableOnChannelClose') continue;
|
||||
if (activeChannelIds.has(lb.channelId)) continue;
|
||||
totalLimboBalance = totalLimboBalance.plus(lb.amountSatoshis);
|
||||
}
|
||||
for (const sb of balances.pendingBalancesFromChannelClosures) {
|
||||
if (sb.channelId && activeChannelIds.has(sb.channelId)) continue;
|
||||
if (
|
||||
sb.type === 'awaitingThresholdConfirmations' &&
|
||||
sb.confirmationHeight != null &&
|
||||
sb.confirmationHeight <= currentBlockHeight
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
totalLimboBalance = totalLimboBalance.plus(sb.amountSatoshis);
|
||||
}
|
||||
|
||||
return {
|
||||
total_limbo_balance: totalLimboBalance.toString(),
|
||||
pending_open_channels: pendingOpen.map((channel) => ({
|
||||
channel: this.formatChannel(channel)
|
||||
})),
|
||||
|
||||
@@ -14,6 +14,11 @@ export default class BalanceStore {
|
||||
@observable public loadingLightningBalance = false;
|
||||
@observable public error = false;
|
||||
@observable public pendingOpenBalance: number | string | any;
|
||||
// Sats locked up in pending close, force close, and waiting close
|
||||
// channels — i.e. funds in close-side limbo. Mirrors pendingOpenBalance
|
||||
// semantics but for the closing side. Populated by ChannelsStore from
|
||||
// PendingChannelsResponse.total_limbo_balance (or the LDK-node analogue).
|
||||
@observable public pendingCloseBalance: number | string | any;
|
||||
@observable public lightningBalance: number | string;
|
||||
@observable public otherAccounts: any = {};
|
||||
settingsStore: SettingsStore;
|
||||
@@ -50,10 +55,16 @@ export default class BalanceStore {
|
||||
|
||||
private resetLightningBalance = () => {
|
||||
this.pendingOpenBalance = 0;
|
||||
this.pendingCloseBalance = 0;
|
||||
this.lightningBalance = 0;
|
||||
this.loadingLightningBalance = false;
|
||||
};
|
||||
|
||||
@action
|
||||
public setPendingCloseBalance = (value: number | string) => {
|
||||
this.pendingCloseBalance = Number(value || 0);
|
||||
};
|
||||
|
||||
@action
|
||||
private balanceError = () => {
|
||||
this.error = true;
|
||||
|
||||
+10
-1
@@ -13,6 +13,7 @@ import Peer from '../models/Peer';
|
||||
import OpenChannelRequest from '../models/OpenChannelRequest';
|
||||
import CloseChannelRequest from '../models/CloseChannelRequest';
|
||||
|
||||
import BalanceStore from './BalanceStore';
|
||||
import SettingsStore from './SettingsStore';
|
||||
|
||||
import BackendUtils from '../utils/BackendUtils';
|
||||
@@ -115,9 +116,11 @@ export default class ChannelsStore {
|
||||
@observable public haveAnnouncedChannels = false;
|
||||
|
||||
settingsStore: SettingsStore;
|
||||
balanceStore: BalanceStore;
|
||||
|
||||
constructor(settingsStore: SettingsStore) {
|
||||
constructor(settingsStore: SettingsStore, balanceStore: BalanceStore) {
|
||||
this.settingsStore = settingsStore;
|
||||
this.balanceStore = balanceStore;
|
||||
|
||||
reaction(
|
||||
() => this.channelRequest,
|
||||
@@ -598,6 +601,12 @@ export default class ChannelsStore {
|
||||
.concat(pendingCloseChannels)
|
||||
.concat(forceCloseChannels)
|
||||
.concat(waitCloseChannels);
|
||||
// Push close-side limbo (cooperative close + force-close
|
||||
// timelocks + waiting-close confirmations) to BalanceStore
|
||||
// so BalancePane can render it next to pendingOpenBalance.
|
||||
this.balanceStore.setPendingCloseBalance(
|
||||
data.total_limbo_balance || 0
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
+2
-2
@@ -34,7 +34,8 @@ export const connectivityStore = new ConnectivityStore(settingsStore);
|
||||
export const modalStore = new ModalStore();
|
||||
export const offersStore = new OffersStore();
|
||||
export const fiatStore = new FiatStore(settingsStore);
|
||||
export const channelsStore = new ChannelsStore(settingsStore);
|
||||
export const balanceStore = new BalanceStore(settingsStore);
|
||||
export const channelsStore = new ChannelsStore(settingsStore, balanceStore);
|
||||
export const nodeInfoStore = new NodeInfoStore(channelsStore, settingsStore);
|
||||
export const alertStore = new AlertStore(settingsStore, nodeInfoStore);
|
||||
export const lspStore = new LSPStore(
|
||||
@@ -52,7 +53,6 @@ export const invoicesStore = new InvoicesStore(
|
||||
channelsStore,
|
||||
nodeInfoStore
|
||||
);
|
||||
export const balanceStore = new BalanceStore(settingsStore);
|
||||
export const transactionsStore = new TransactionsStore(
|
||||
settingsStore,
|
||||
nodeInfoStore,
|
||||
|
||||
@@ -182,13 +182,19 @@ export default class BalancePane extends React.PureComponent<
|
||||
totalBlockchainBalance,
|
||||
unconfirmedBlockchainBalance,
|
||||
lightningBalance,
|
||||
pendingOpenBalance
|
||||
pendingOpenBalance,
|
||||
pendingCloseBalance
|
||||
} = BalanceStore;
|
||||
const cashuBalance = CashuStore.totalBalanceSats;
|
||||
const cashuOfflinePendingBalance = CashuStore.offlinePendingBalance;
|
||||
const { implementation, settings, lndFolderMissing } = SettingsStore;
|
||||
|
||||
const pendingUnconfirmedBalance = new BigNumber(pendingOpenBalance)
|
||||
const lightningPendingTotal = new BigNumber(pendingOpenBalance || 0)
|
||||
.plus(pendingCloseBalance || 0)
|
||||
.toNumber();
|
||||
const hasLightningPending = lightningPendingTotal > 0;
|
||||
|
||||
const pendingUnconfirmedBalance = new BigNumber(lightningPendingTotal)
|
||||
.plus(unconfirmedBlockchainBalance)
|
||||
.toNumber()
|
||||
.toFixed(3);
|
||||
@@ -206,40 +212,55 @@ export default class BalancePane extends React.PureComponent<
|
||||
jumboText
|
||||
toggleable
|
||||
/>
|
||||
{!(pendingOpenBalance > 0) && (
|
||||
{!hasLightningPending && (
|
||||
<View style={styles.conversion}>
|
||||
<Conversion sats={lightningBalance} sensitive />
|
||||
</View>
|
||||
)}
|
||||
{pendingOpenBalance > 0 ? (
|
||||
<>
|
||||
<View style={styles.pendingAmountSpacing}>
|
||||
<Amount
|
||||
sats={pendingOpenBalance}
|
||||
sensitive
|
||||
jumboText
|
||||
toggleable
|
||||
pending
|
||||
onPendingPress={() =>
|
||||
this.handlePendingPress('lightning')
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.conversion}>
|
||||
<Conversion
|
||||
sats={lightningBalance}
|
||||
satsPending={pendingOpenBalance}
|
||||
sensitive
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
) : null}
|
||||
{pendingOpenBalance > 0 && (
|
||||
<View style={styles.pendingAmountSpacing}>
|
||||
<Amount
|
||||
sats={pendingOpenBalance}
|
||||
sensitive
|
||||
jumboText
|
||||
toggleable
|
||||
pending
|
||||
onPendingPress={() =>
|
||||
this.handlePendingPress('lightning')
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
{pendingCloseBalance > 0 && (
|
||||
<View style={styles.pendingAmountSpacing}>
|
||||
<Amount
|
||||
sats={pendingCloseBalance}
|
||||
sensitive
|
||||
jumboText
|
||||
toggleable
|
||||
pending
|
||||
onPendingPress={() =>
|
||||
this.handlePendingPress('lightning')
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
{hasLightningPending && (
|
||||
<View style={styles.conversion}>
|
||||
<Conversion
|
||||
sats={lightningBalance}
|
||||
satsPending={lightningPendingTotal}
|
||||
sensitive
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
const BalanceViewCombined = () => {
|
||||
const hasOnchainPending = Boolean(
|
||||
Number(unconfirmedBlockchainBalance ?? 0) ||
|
||||
Number(pendingOpenBalance ?? 0)
|
||||
Number(pendingOpenBalance ?? 0) ||
|
||||
Number(pendingCloseBalance ?? 0)
|
||||
);
|
||||
const hasAnyPending = hasOnchainPending;
|
||||
const renderPendingBalance = (sats: string | number) => (
|
||||
|
||||
Reference in New Issue
Block a user