|
|
|
import { createReducer, on } from '@ngrx/store';
|
|
|
|
|
|
|
|
import { initECLState } from './ecl.state';
|
|
|
|
import { addInvoice, removeChannel, removePeer, resetECLStore, setActiveChannels, setChannelsStatus, setChildNodeSettingsECL,
|
|
|
|
setFees, setInactiveChannels, setInfo, setInvoices, setLightningBalance, setOnchainBalance, setPayments, setPeers, setPendingChannels,
|
|
|
|
setTransactions, updateECLAPICallStatus, updateChannelState, updateInvoice, updateRelayedPayment, setPageSettings } from './ecl.actions';
|
|
|
|
import { Channel, PaymentReceived, PaymentRelayed } from '../../shared/models/eclModels';
|
|
|
|
import { PageSettings } from '../../shared/models/pageSettings';
|
|
|
|
import { ECL_DEFAULT_PAGE_SETTINGS } from '../../shared/services/consts-enums-functions';
|
|
|
|
|
|
|
|
export const ECLReducer = createReducer(initECLState,
|
|
|
|
on(updateECLAPICallStatus, (state, { payload }) => {
|
|
|
|
const updatedApisCallStatus = JSON.parse(JSON.stringify(state.apisCallStatus));
|
|
|
|
if (payload.action) {
|
|
|
|
updatedApisCallStatus[payload.action] = {
|
|
|
|
status: payload.status,
|
|
|
|
statusCode: payload.statusCode,
|
|
|
|
message: payload.message,
|
|
|
|
URL: payload.URL,
|
|
|
|
filePath: payload.filePath
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
...state,
|
|
|
|
apisCallStatus: updatedApisCallStatus
|
|
|
|
};
|
|
|
|
}),
|
|
|
|
on(setChildNodeSettingsECL, (state, { payload }) => ({
|
|
|
|
...state,
|
|
|
|
nodeSettings: payload
|
|
|
|
})),
|
|
|
|
on(resetECLStore, (state, { payload }) => ({
|
|
|
|
...initECLState,
|
|
|
|
nodeSettings: payload
|
|
|
|
})),
|
|
|
|
on(setInfo, (state, { payload }) => ({
|
|
|
|
...state,
|
|
|
|
information: payload
|
|
|
|
})),
|
|
|
|
on(setFees, (state, { payload }) => ({
|
|
|
|
...state,
|
|
|
|
fees: payload
|
|
|
|
})),
|
|
|
|
on(setActiveChannels, (state, { payload }) => ({
|
|
|
|
...state,
|
|
|
|
activeChannels: payload
|
|
|
|
})),
|
|
|
|
on(setPendingChannels, (state, { payload }) => ({
|
|
|
|
...state,
|
|
|
|
pendingChannels: payload
|
|
|
|
})),
|
|
|
|
on(setInactiveChannels, (state, { payload }) => ({
|
|
|
|
...state,
|
|
|
|
inactiveChannels: payload
|
|
|
|
})),
|
|
|
|
on(setChannelsStatus, (state, { payload }) => ({
|
|
|
|
...state,
|
|
|
|
channelsStatus: payload
|
|
|
|
})),
|
|
|
|
on(setOnchainBalance, (state, { payload }) => ({
|
|
|
|
...state,
|
|
|
|
onchainBalance: payload
|
|
|
|
})),
|
|
|
|
on(setLightningBalance, (state, { payload }) => ({
|
|
|
|
...state,
|
|
|
|
lightningBalance: payload
|
|
|
|
})),
|
|
|
|
on(setPeers, (state, { payload }) => ({
|
|
|
|
...state,
|
|
|
|
peers: payload
|
|
|
|
})),
|
|
|
|
on(removePeer, (state, { payload }) => {
|
|
|
|
const modifiedPeers = [...state.peers];
|
|
|
|
const removePeerIdx = state.peers.findIndex((peer) => peer.nodeId === payload.nodeId);
|
|
|
|
if (removePeerIdx > -1) {
|
|
|
|
modifiedPeers.splice(removePeerIdx, 1);
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
...state,
|
|
|
|
peers: modifiedPeers
|
|
|
|
};
|
|
|
|
}),
|
|
|
|
on(removeChannel, (state, { payload }) => {
|
|
|
|
const modifiedChannels = [...state.activeChannels];
|
|
|
|
const removeChannelIdx = state.activeChannels.findIndex((channel) => channel.channelId === payload.channelId);
|
|
|
|
if (removeChannelIdx > -1) {
|
|
|
|
modifiedChannels.splice(removeChannelIdx, 1);
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
...state,
|
|
|
|
activeChannels: modifiedChannels
|
|
|
|
};
|
|
|
|
}),
|
|
|
|
on(setPayments, (state, { payload }) => {
|
|
|
|
if (payload && payload.sent) {
|
|
|
|
const storedChannels = [...state.activeChannels, ...state.pendingChannels, ...state.inactiveChannels];
|
|
|
|
payload.sent?.map((sentPayment) => {
|
|
|
|
const peerFound = state.peers.find((peer) => peer.nodeId === sentPayment.recipientNodeId);
|
|
|
|
sentPayment.recipientNodeAlias = peerFound ? peerFound.alias : sentPayment.recipientNodeId;
|
|
|
|
if (sentPayment.parts) {
|
|
|
|
sentPayment.parts?.map((part) => {
|
|
|
|
const channelFound = storedChannels.find((channel) => channel.channelId === part.toChannelId);
|
|
|
|
part.toChannelAlias = channelFound ? channelFound.alias : part.toChannelId;
|
|
|
|
return sentPayment.parts;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return payload.sent;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if (payload && payload.relayed) {
|
|
|
|
const storedChannels = [...state.activeChannels, ...state.pendingChannels, ...state.inactiveChannels];
|
|
|
|
payload.relayed.forEach((rlEvent) => {
|
|
|
|
rlEvent = mapAliases(rlEvent, storedChannels);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
...state,
|
|
|
|
payments: payload
|
|
|
|
};
|
|
|
|
}),
|
|
|
|
on(setTransactions, (state, { payload }) => ({
|
|
|
|
...state,
|
|
|
|
transactions: payload
|
|
|
|
})),
|
|
|
|
on(addInvoice, (state, { payload }) => {
|
|
|
|
const newInvoices = state.invoices;
|
|
|
|
newInvoices?.unshift(payload);
|
|
|
|
return {
|
|
|
|
...state,
|
|
|
|
invoices: newInvoices
|
|
|
|
};
|
|
|
|
}),
|
|
|
|
on(setInvoices, (state, { payload }) => ({
|
|
|
|
...state,
|
|
|
|
invoices: payload
|
|
|
|
})),
|
|
|
|
on(updateInvoice, (state, { payload }) => {
|
|
|
|
let modifiedInvoices = state.invoices;
|
|
|
|
modifiedInvoices = modifiedInvoices?.map((invoice) => {
|
|
|
|
if (invoice.paymentHash === payload.paymentHash) {
|
|
|
|
if (payload.hasOwnProperty('type')) {
|
|
|
|
const updatedInvoice = JSON.parse(JSON.stringify(invoice));
|
|
|
|
updatedInvoice.amountSettled = ((<PaymentReceived>payload).parts && (<PaymentReceived>payload).parts.length && (<PaymentReceived>payload).parts.length > 0 && (<PaymentReceived>payload).parts[0].amount) ? ((<PaymentReceived>payload).parts[0].amount || 0) / 1000 : 0;
|
|
|
|
updatedInvoice.receivedAt = ((<PaymentReceived>payload).parts && (<PaymentReceived>payload).parts.length && (<PaymentReceived>payload).parts.length > 0 && (<PaymentReceived>payload).parts[0].timestamp) ? Math.round(((<PaymentReceived>payload).parts[0].timestamp || 0) / 1000) : 0;
|
|
|
|
updatedInvoice.status = 'received';
|
|
|
|
return updatedInvoice;
|
|
|
|
} else {
|
|
|
|
return payload;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return invoice;
|
|
|
|
});
|
|
|
|
return {
|
|
|
|
...state,
|
|
|
|
invoices: modifiedInvoices
|
|
|
|
};
|
|
|
|
}),
|
|
|
|
on(updateChannelState, (state, { payload }) => {
|
|
|
|
let modifiedPendingChannels = state.pendingChannels;
|
|
|
|
modifiedPendingChannels = modifiedPendingChannels?.map((pendingChannel) => {
|
|
|
|
if (pendingChannel.channelId === payload.channelId && pendingChannel.nodeId === payload.remoteNodeId) {
|
|
|
|
payload.currentState = payload.currentState?.replace(/_/g, ' ');
|
|
|
|
pendingChannel.state = payload.currentState;
|
|
|
|
}
|
|
|
|
return pendingChannel;
|
|
|
|
});
|
|
|
|
return {
|
|
|
|
...state,
|
|
|
|
pendingChannels: modifiedPendingChannels
|
|
|
|
};
|
|
|
|
}),
|
|
|
|
on(updateRelayedPayment, (state, { payload }) => {
|
|
|
|
const modifiedPayments = state.payments;
|
|
|
|
const updatedPayload = mapAliases(payload, [...state.activeChannels, ...state.pendingChannels, ...state.inactiveChannels]);
|
|
|
|
modifiedPayments.relayed?.unshift(updatedPayload);
|
|
|
|
const feeSats = (payload.amountIn || 0) - (payload.amountOut || 0);
|
|
|
|
const modifiedLightningBalance = { localBalance: (state.lightningBalance.localBalance + feeSats), remoteBalance: (state.lightningBalance.remoteBalance - feeSats) };
|
|
|
|
const modifiedChannelStatus = state.channelsStatus;
|
|
|
|
if (modifiedChannelStatus.active) {
|
|
|
|
modifiedChannelStatus.active.capacity = (state.channelsStatus?.active?.capacity || 0) + feeSats;
|
|
|
|
}
|
|
|
|
const modifiedFees = {
|
|
|
|
daily_fee: ((state.fees.daily_fee || 0) + feeSats), daily_txs: ((state.fees.daily_txs || 0) + 1),
|
|
|
|
weekly_fee: ((state.fees.weekly_fee || 0) + feeSats), weekly_txs: ((state.fees.weekly_txs || 0) + 1),
|
|
|
|
monthly_fee: ((state.fees.monthly_fee || 0) + feeSats), monthly_txs: ((state.fees.monthly_txs || 0) + 1)
|
|
|
|
};
|
|
|
|
const modifiedActiveChannels = state.activeChannels;
|
|
|
|
let foundFrom = false;
|
|
|
|
let foundTo = false;
|
|
|
|
for (const channel of modifiedActiveChannels) {
|
|
|
|
if (channel.channelId === payload.fromChannelId) {
|
|
|
|
foundFrom = true;
|
|
|
|
const channelTotal = (channel.toLocal || 0) + (channel.toRemote || 0);
|
|
|
|
channel.toLocal = (channel.toLocal || 0) + updatedPayload.amountIn;
|
|
|
|
channel.toRemote = (channel.toRemote || 0) - updatedPayload.amountIn;
|
|
|
|
channel.balancedness = (channelTotal === 0) ? 1 : +(1 - Math.abs((channel.toLocal - channel.toRemote) / channelTotal)).toFixed(3);
|
|
|
|
}
|
|
|
|
if (channel.channelId === payload.toChannelId) {
|
|
|
|
foundTo = true;
|
|
|
|
const channelTotal = (channel.toLocal || 0) + (channel.toRemote || 0);
|
|
|
|
channel.toLocal = (channel.toLocal || 0) - updatedPayload.amountOut;
|
|
|
|
channel.toRemote = (channel.toRemote || 0) + updatedPayload.amountOut;
|
|
|
|
channel.balancedness = (channelTotal === 0) ? 1 : +(1 - Math.abs((channel.toLocal - channel.toRemote) / channelTotal)).toFixed(3);
|
|
|
|
}
|
|
|
|
if (foundTo && foundFrom) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return {
|
|
|
|
...state,
|
|
|
|
payments: modifiedPayments,
|
|
|
|
lightningBalance: modifiedLightningBalance,
|
|
|
|
channelStatus: modifiedChannelStatus,
|
|
|
|
fees: modifiedFees,
|
|
|
|
activeChannels: modifiedActiveChannels
|
|
|
|
};
|
|
|
|
}),
|
|
|
|
on(setPageSettings, (state, { payload }) => {
|
|
|
|
const newPageSettings: PageSettings[] = [];
|
|
|
|
ECL_DEFAULT_PAGE_SETTINGS.forEach((defaultPage) => {
|
|
|
|
const pageSetting = payload && payload.length && payload.length > 0 ? payload.find((p) => p.pageId === defaultPage.pageId) : null;
|
|
|
|
if (pageSetting) {
|
|
|
|
const tablesSettings = JSON.parse(JSON.stringify(pageSetting.tables));
|
|
|
|
pageSetting.tables = []; // To maintain settings order
|
|
|
|
defaultPage.tables.forEach((defaultTable) => {
|
|
|
|
const tableSetting = tablesSettings.find((t) => t.tableId === defaultTable.tableId) || null;
|
|
|
|
if (tableSetting) {
|
|
|
|
pageSetting.tables.push(tableSetting);
|
|
|
|
} else {
|
|
|
|
pageSetting.tables.push(JSON.parse(JSON.stringify(defaultTable)));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
newPageSettings.push(pageSetting);
|
|
|
|
} else {
|
|
|
|
newPageSettings.push(JSON.parse(JSON.stringify(defaultPage)));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return {
|
|
|
|
...state,
|
|
|
|
pageSettings: newPageSettings
|
|
|
|
};
|
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
const mapAliases = (rlEvent: PaymentRelayed, storedChannels: Channel[]) => {
|
|
|
|
if (rlEvent.type === 'payment-relayed') {
|
|
|
|
if (storedChannels && storedChannels.length > 0) {
|
|
|
|
for (let idx = 0; idx < storedChannels.length; idx++) {
|
|
|
|
if (storedChannels[idx].channelId?.toString() === rlEvent.fromChannelId) {
|
|
|
|
rlEvent.fromChannelAlias = storedChannels[idx].alias ? storedChannels[idx].alias : rlEvent.fromChannelId;
|
|
|
|
rlEvent.fromShortChannelId = storedChannels[idx].shortChannelId ? storedChannels[idx].shortChannelId : '';
|
|
|
|
if (rlEvent.toChannelAlias) {
|
|
|
|
return rlEvent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (storedChannels[idx].channelId?.toString() === rlEvent.toChannelId) {
|
|
|
|
rlEvent.toChannelAlias = storedChannels[idx].alias ? storedChannels[idx].alias : rlEvent.toChannelId;
|
|
|
|
rlEvent.toShortChannelId = storedChannels[idx].shortChannelId ? storedChannels[idx].shortChannelId : '';
|
|
|
|
if (rlEvent.fromChannelAlias) {
|
|
|
|
return rlEvent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (idx === storedChannels.length - 1) {
|
|
|
|
if (!rlEvent.fromChannelAlias) {
|
|
|
|
rlEvent.fromChannelAlias = rlEvent.fromChannelId?.substring(0, 17) + '...';
|
|
|
|
rlEvent.fromShortChannelId = '';
|
|
|
|
}
|
|
|
|
if (!rlEvent.toChannelAlias) {
|
|
|
|
rlEvent.toChannelAlias = rlEvent.toChannelId?.substring(0, 17) + '...';
|
|
|
|
rlEvent.toShortChannelId = '';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rlEvent.fromChannelAlias = rlEvent.fromChannelId?.substring(0, 17) + '...';
|
|
|
|
rlEvent.fromShortChannelId = '';
|
|
|
|
rlEvent.toChannelAlias = rlEvent.toChannelId?.substring(0, 17) + '...';
|
|
|
|
rlEvent.toShortChannelId = '';
|
|
|
|
}
|
|
|
|
} else if (rlEvent.type = 'trampoline-payment-relayed') {
|
|
|
|
if (storedChannels && storedChannels.length > 0) {
|
|
|
|
for (let idx = 0; idx < storedChannels.length; idx++) {
|
|
|
|
rlEvent.incoming?.forEach((incomingEvent) => {
|
|
|
|
if (storedChannels[idx].channelId?.toString() === incomingEvent.channelId) {
|
|
|
|
incomingEvent.channelAlias = storedChannels[idx].alias ? storedChannels[idx].alias : incomingEvent.channelId;
|
|
|
|
incomingEvent.shortChannelId = storedChannels[idx].shortChannelId ? storedChannels[idx].shortChannelId : '';
|
|
|
|
}
|
|
|
|
});
|
|
|
|
rlEvent.outgoing?.forEach((outgoingEvent) => {
|
|
|
|
if (storedChannels[idx].channelId?.toString() === outgoingEvent.channelId) {
|
|
|
|
outgoingEvent.channelAlias = storedChannels[idx].alias ? storedChannels[idx].alias : outgoingEvent.channelId;
|
|
|
|
outgoingEvent.shortChannelId = storedChannels[idx].shortChannelId ? storedChannels[idx].shortChannelId : '';
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (idx === storedChannels.length - 1) {
|
|
|
|
if (rlEvent.incoming && rlEvent.incoming.length && rlEvent.incoming.length > 0 && !rlEvent.incoming[0].channelAlias) {
|
|
|
|
rlEvent.incoming?.forEach((incomingEvent) => {
|
|
|
|
incomingEvent.channelAlias = incomingEvent.channelId?.substring(0, 17) + '...';
|
|
|
|
incomingEvent.shortChannelId = '';
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if (rlEvent.outgoing && rlEvent.outgoing.length && rlEvent.outgoing.length > 0 && !rlEvent.outgoing[0].channelAlias) {
|
|
|
|
rlEvent.outgoing?.forEach((outgoingEvent) => {
|
|
|
|
outgoingEvent.channelAlias = outgoingEvent.channelId?.substring(0, 17) + '...';
|
|
|
|
outgoingEvent.shortChannelId = '';
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rlEvent.incoming?.forEach((incomingEvent) => {
|
|
|
|
incomingEvent.channelAlias = incomingEvent.channelId?.substring(0, 17) + '...';
|
|
|
|
incomingEvent.shortChannelId = '';
|
|
|
|
});
|
|
|
|
rlEvent.outgoing?.forEach((outgoingEvent) => {
|
|
|
|
outgoingEvent.channelAlias = outgoingEvent.channelId?.substring(0, 17) + '...';
|
|
|
|
outgoingEvent.shortChannelId = '';
|
|
|
|
});
|
|
|
|
}
|
|
|
|
const amountInTotalmSats = rlEvent.incoming?.reduce((acc, curr) => acc + curr.amount, 0) || 0;
|
|
|
|
rlEvent.amountIn = Math.round(amountInTotalmSats / 1000);
|
|
|
|
rlEvent.fromChannelId = rlEvent.incoming && rlEvent.incoming.length ? rlEvent.incoming[0].channelId : '';
|
|
|
|
rlEvent.fromChannelAlias = rlEvent.incoming && rlEvent.incoming.length ? rlEvent.incoming[0].channelAlias : '';
|
|
|
|
rlEvent.fromShortChannelId = rlEvent.incoming && rlEvent.incoming.length ? rlEvent.incoming[0].shortChannelId : '';
|
|
|
|
|
|
|
|
const amountOutTotalmSats = rlEvent.outgoing?.reduce((acc, curr) => acc + curr.amount, 0) || 0;
|
|
|
|
rlEvent.amountOut = Math.round(amountOutTotalmSats / 1000);
|
|
|
|
rlEvent.toChannelId = rlEvent.outgoing && rlEvent.outgoing.length ? rlEvent.outgoing[0].channelId : '';
|
|
|
|
rlEvent.toChannelAlias = rlEvent.outgoing && rlEvent.outgoing.length ? rlEvent.outgoing[0].channelAlias : '';
|
|
|
|
rlEvent.toShortChannelId = rlEvent.outgoing && rlEvent.outgoing.length ? rlEvent.outgoing[0].shortChannelId : '';
|
|
|
|
}
|
|
|
|
return rlEvent;
|
|
|
|
};
|