Closing coin selection on open channel #409 (#423)

Closing coin selection on open channel #409
pull/426/head
ShahanaFarooqui 4 years ago committed by GitHub
parent 1409042275
commit da553bc055
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -15,5 +15,5 @@
<link rel="stylesheet" href="styles.7f0a84d9b012559f3600.css"></head>
<body>
<rtl-app></rtl-app>
<script src="runtime.2493904abb92f5d5f622.js" defer></script><script src="polyfills-es5.2ac0d98b22574ae745b1.js" nomodule defer></script><script src="polyfills.5ae721a6ae5ab597a53d.js" defer></script><script src="main.8a5e79e0326ca1db85cb.js" defer></script></body>
<script src="runtime.49fb4993ddaaa7d367b3.js" defer></script><script src="polyfills-es5.2ac0d98b22574ae745b1.js" nomodule defer></script><script src="polyfills.5ae721a6ae5ab597a53d.js" defer></script><script src="main.63add4e662e0f112964c.js" defer></script></body>
</html>

File diff suppressed because one or more lines are too long

@ -1 +1 @@
!function(e){function r(r){for(var n,u,i=r[0],c=r[1],f=r[2],p=0,s=[];p<i.length;p++)u=i[p],Object.prototype.hasOwnProperty.call(o,u)&&o[u]&&s.push(o[u][0]),o[u]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(l&&l(r);s.length;)s.shift()();return a.push.apply(a,f||[]),t()}function t(){for(var e,r=0;r<a.length;r++){for(var t=a[r],n=!0,i=1;i<t.length;i++)0!==o[t[i]]&&(n=!1);n&&(a.splice(r--,1),e=u(u.s=t[0]))}return e}var n={},o={0:0},a=[];function u(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,u),t.l=!0,t.exports}u.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var a,i=document.createElement("script");i.charset="utf-8",i.timeout=120,u.nc&&i.setAttribute("nonce",u.nc),i.src=function(e){return u.p+""+({}[e]||e)+"."+{1:"9bb271dd8dffd2d994a5",6:"34493377e46c62128b7c",7:"4a00e92294df28ac9ca1",8:"5ad5f94d29aeaf5781a7"}[e]+".js"}(e);var c=new Error;a=function(r){i.onerror=i.onload=null,clearTimeout(f);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),a=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+a+")",c.name="ChunkLoadError",c.type=n,c.request=a,t[1](c)}o[e]=void 0}};var f=setTimeout((function(){a({type:"timeout",target:i})}),12e4);i.onerror=i.onload=a,document.head.appendChild(i)}return Promise.all(r)},u.m=e,u.c=n,u.d=function(e,r,t){u.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},u.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},u.t=function(e,r){if(1&r&&(e=u(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(u.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)u.d(t,n,(function(r){return e[r]}).bind(null,n));return t},u.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return u.d(r,"a",r),r},u.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},u.p="",u.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var f=0;f<i.length;f++)r(i[f]);var l=c;t()}([]);
!function(e){function r(r){for(var n,u,i=r[0],c=r[1],f=r[2],p=0,s=[];p<i.length;p++)u=i[p],Object.prototype.hasOwnProperty.call(o,u)&&o[u]&&s.push(o[u][0]),o[u]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(l&&l(r);s.length;)s.shift()();return a.push.apply(a,f||[]),t()}function t(){for(var e,r=0;r<a.length;r++){for(var t=a[r],n=!0,i=1;i<t.length;i++)0!==o[t[i]]&&(n=!1);n&&(a.splice(r--,1),e=u(u.s=t[0]))}return e}var n={},o={0:0},a=[];function u(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,u),t.l=!0,t.exports}u.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var a,i=document.createElement("script");i.charset="utf-8",i.timeout=120,u.nc&&i.setAttribute("nonce",u.nc),i.src=function(e){return u.p+""+({}[e]||e)+"."+{1:"9bb271dd8dffd2d994a5",6:"1221c5e1ffe0cefc11c3",7:"4a00e92294df28ac9ca1",8:"5ad5f94d29aeaf5781a7"}[e]+".js"}(e);var c=new Error;a=function(r){i.onerror=i.onload=null,clearTimeout(f);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),a=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+a+")",c.name="ChunkLoadError",c.type=n,c.request=a,t[1](c)}o[e]=void 0}};var f=setTimeout((function(){a({type:"timeout",target:i})}),12e4);i.onerror=i.onload=a,document.head.appendChild(i)}return Promise.all(r)},u.m=e,u.c=n,u.d=function(e,r,t){u.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},u.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},u.t=function(e,r){if(1&r&&(e=u(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(u.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)u.d(t,n,(function(r){return e[r]}).bind(null,n));return t},u.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return u.d(r,"a",r),r},u.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},u.p="",u.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var f=0;f<i.length;f++)r(i[f]);var l=c;t()}([]);

@ -71,7 +71,6 @@ export class CLOnChainSendComponent implements OnInit, OnDestroy {
filter(action => action.type === CLActions.EFFECT_ERROR_CL || action.type === CLActions.SET_CHANNEL_TRANSACTION_RES_CL))
.subscribe((action: CLActions.EffectError | CLActions.SetChannelTransactionRes) => {
if (action.type === CLActions.SET_CHANNEL_TRANSACTION_RES_CL) {
this.store.dispatch(new CLActions.FetchTransactions());
this.store.dispatch(new RTLActions.OpenSnackBar('Fund Sent Successfully!'));
this.dialogRef.close();
}
@ -121,6 +120,8 @@ export class CLOnChainSendComponent implements OnInit, OnDestroy {
let utxoNew = {value: 0};
if (this.selUTXOs.length && this.selUTXOs.length > 0) {
this.totalSelectedUTXOAmount = this.selUTXOs.reduce((a, b) => {utxoNew.value = a.value + b.value; return utxoNew;}).value;
} else {
this.totalSelectedUTXOAmount = 0;
}
}

@ -57,7 +57,7 @@ export class CLOnChainTransactionHistoryComponent implements OnInit, OnDestroy {
}
ngOnInit() {
this.store.dispatch(new CLActions.FetchTransactions());
// this.store.dispatch(new CLActions.FetchTransactions());
this.store.select('cl')
.pipe(takeUntil(this.unsub[0]))
.subscribe((rtlStore) => {

@ -98,6 +98,14 @@
</mat-form-field>
</div>
</div>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between center">
<mat-form-field fxFlex="48" fxLayoutAlign="start end">
<mat-select tabindex="6" placeholder="Coin Selection" (selectionChange)="onUTXOSelectionChange($event)" [(value)]="selUTXOs" multiple>
<mat-select-trigger>{{totalSelectedUTXOAmount | number}} Sats ({{selUTXOs.length > 1 ? selUTXOs.length + ' UTXOs' : '1 UTXO'}})</mat-select-trigger>
<mat-option *ngFor="let transaction of transactions" [value]="transaction">{{transaction.value | number}} Sats</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
</mat-expansion-panel>
</form>

@ -1,5 +1,6 @@
import { Component, OnInit, Inject, OnDestroy, ViewChild } from '@angular/core';
import { FormControl } from '@angular/forms';
import { DecimalPipe } from '@angular/common';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { Subject, Observable } from 'rxjs';
import { takeUntil, filter, startWith, map } from 'rxjs/operators';
@ -7,7 +8,7 @@ import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { Peer, GetInfo } from '../../../../shared/models/clModels';
import { Peer, GetInfo, Transaction } from '../../../../shared/models/clModels';
import { CLOpenChannelAlert } from '../../../../shared/models/alertData';
import { FEE_RATE_TYPES } from '../../../../shared/services/consts-enums-functions';
@ -29,6 +30,9 @@ export class CLOpenChannelComponent implements OnInit, OnDestroy {
public peers: Peer[];
public sortedPeers: Peer[];
public filteredPeers: Observable<Peer[]>;
public transactions: Transaction[] = [];
public selUTXOs = [];
public totalSelectedUTXOAmount = 0;
public channelConnectionError = '';
public advancedTitle = 'Advanced Options';
public information: GetInfo;
@ -42,11 +46,12 @@ export class CLOpenChannelComponent implements OnInit, OnDestroy {
public minConfValue = null;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
constructor(public dialogRef: MatDialogRef<CLOpenChannelComponent>, @Inject(MAT_DIALOG_DATA) public data: CLOpenChannelAlert, private store: Store<fromRTLReducer.RTLState>, private actions$: Actions) {}
constructor(public dialogRef: MatDialogRef<CLOpenChannelComponent>, @Inject(MAT_DIALOG_DATA) public data: CLOpenChannelAlert, private store: Store<fromRTLReducer.RTLState>, private actions$: Actions, private decimalPipe: DecimalPipe) {}
ngOnInit() {
this.information = this.data.message.information;
this.totalBalance = this.data.message.balance;
this.transactions = this.data.message.transactions;
this.alertTitle = this.data.alertTitle;
this.peer = this.data.message.peer ? this.data.message.peer : null;
this.peers = this.data.message.peers && this.data.message.peers.length ? this.data.message.peers : [];
@ -112,18 +117,30 @@ export class CLOpenChannelComponent implements OnInit, OnDestroy {
onAdvancedPanelToggle(isClosed: boolean) {
if (isClosed) {
this.advancedTitle = (!this.flgMinConf && !this.selFeeRate) ? 'Advanced Options' : 'Advanced Options | ' + (this.flgMinConf ? 'Min Confirmation Blocks: ' : 'Fee Rate: ') + (this.flgMinConf ? this.minConfValue : (this.selFeeRate ? this.feeRateTypes.find(feeRateType => feeRateType.feeRateId === this.selFeeRate).feeRateType : ''));
this.advancedTitle = (!this.flgMinConf && !this.selFeeRate) ? 'Advanced Options' : 'Advanced Options | ' + (this.flgMinConf ? 'Min Confirmation Blocks: ' : 'Fee Rate: ') + (this.flgMinConf ? this.minConfValue : (this.selFeeRate ? this.feeRateTypes.find(feeRateType => feeRateType.feeRateId === this.selFeeRate).feeRateType : '') + ((this.selUTXOs.length && this.selUTXOs.length > 0) ? ' | Selected UTXOs: ' + this.selUTXOs.length + ' | Total Amount: ' + this.decimalPipe.transform(this.totalSelectedUTXOAmount) + ' Sats' : ''));
} else {
this.advancedTitle = 'Advanced Options';
}
}
onUTXOSelectionChange(event: any) {
let utxoNew = {value: 0};
if (this.selUTXOs.length && this.selUTXOs.length > 0) {
this.totalSelectedUTXOAmount = this.selUTXOs.reduce((a, b) => {utxoNew.value = a.value + b.value; return utxoNew;}).value;
} else {
this.totalSelectedUTXOAmount = 0;
}
}
onOpenChannel() {
if ((!this.peer && !this.selectedPubkey) || (!this.fundingAmount || ((this.totalBalance - this.fundingAmount) < 0) || (this.flgMinConf && !this.minConfValue))) { return true; }
let newChannel = { peerId: ((!this.peer || !this.peer.id) ? this.selectedPubkey : this.peer.id), satoshis: this.fundingAmount, announce: !this.isPrivate, feeRate: this.selFeeRate, minconf: this.flgMinConf ? this.minConfValue : null };
if (this.selUTXOs.length && this.selUTXOs.length > 0) {
newChannel['utxos'] = [];
this.selUTXOs.forEach(utxo => newChannel['utxos'].push(utxo.txid + ':' + utxo.output));
}
this.store.dispatch(new RTLActions.OpenSpinner('Opening Channel...'));
this.store.dispatch(new CLActions.SaveNewChannel({
peerId: ((!this.peer || !this.peer.id) ? this.selectedPubkey : this.peer.id), satoshis: this.fundingAmount, announce: !this.isPrivate, feeRate: this.selFeeRate, minconf: this.flgMinConf ? this.minConfValue : null
}));
this.store.dispatch(new CLActions.SaveNewChannel(newChannel));
}
ngOnDestroy() {

@ -2,12 +2,13 @@ import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { faUsers, faChartPie } from '@fortawesome/free-solid-svg-icons';
import { GetInfo, Peer } from '../../shared/models/clModels';
import { GetInfo, Peer, Transaction } from '../../shared/models/clModels';
import { CLOpenChannelComponent } from './channels/open-channel-modal/open-channel.component';
import { SelNodeChild } from '../../shared/models/RTLconfig';
import { LoggerService } from '../../shared/services/logger.service';
import { CommonService } from '../../shared/services/common.service';
import * as RTLActions from '../../store/rtl.actions';
import * as fromRTLReducer from '../../store/rtl.reducers';
@ -21,6 +22,7 @@ export class CLPeersChannelsComponent implements OnInit, OnDestroy {
public selNode: SelNodeChild = {};
public information: GetInfo = {};
public peers: Peer[] = [];
public transactions: Transaction[] = [];
public totalBalance = 0;
public activePeers = 0;
public activeChannels = 0;
@ -29,7 +31,7 @@ export class CLPeersChannelsComponent implements OnInit, OnDestroy {
public balances = [{title: 'Total Balance', dataValue: 0}, {title: 'Confirmed', dataValue: 0}, {title: 'Unconfirmed', dataValue: 0}];
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject()];
constructor(private store: Store<fromRTLReducer.RTLState>, private actions$: Actions) {}
constructor(private store: Store<fromRTLReducer.RTLState>, private logger: LoggerService, private commonService: CommonService) {}
ngOnInit() {
this.store.select('cl')
@ -38,10 +40,12 @@ export class CLPeersChannelsComponent implements OnInit, OnDestroy {
this.selNode = rtlStore.nodeSettings;
this.information = rtlStore.information;
this.peers = rtlStore.peers;
this.transactions = this.commonService.sortAscByKey(rtlStore.transactions.filter(tran => tran.status === 'confirmed'), 'value');
this.activePeers = (rtlStore.peers && rtlStore.peers.length) ? rtlStore.peers.length : 0;
this.activeChannels = rtlStore.information.num_active_channels;
this.totalBalance = rtlStore.balance.totalBalance;
this.balances = [{title: 'Total Balance', dataValue: rtlStore.balance.totalBalance || 0}, {title: 'Confirmed', dataValue: rtlStore.balance.confBalance}, {title: 'Unconfirmed', dataValue: rtlStore.balance.unconfBalance}];
this.logger.info(rtlStore);
});
}
@ -49,7 +53,8 @@ export class CLPeersChannelsComponent implements OnInit, OnDestroy {
const peerToAddChannelMessage = {
peers: this.peers,
information: this.information,
balance: this.totalBalance
balance: this.totalBalance,
transactions: this.transactions
};
this.store.dispatch(new RTLActions.OpenAlert({ data: {
alertTitle: 'Open Channel',

@ -226,7 +226,7 @@ export class UpdateChannels implements Action {
export class SaveNewChannel implements Action {
readonly type = SAVE_NEW_CHANNEL_CL;
constructor(public payload: {peerId: string, satoshis: number, feeRate: string, announce: boolean, minconf?: number}) {}
constructor(public payload: {peerId: string, satoshis: number, feeRate: string, announce: boolean, minconf?: number, utxos?: string[]}) {}
}
export class CloseChannel implements Action {

@ -37,7 +37,7 @@ export class CLEffects implements OnDestroy {
this.store.select('cl')
.pipe(takeUntil(this.unSubs[0]))
.subscribe((rtlStore) => {
if(rtlStore.initialAPIResponseStatus[0] === 'INCOMPLETE' && rtlStore.initialAPIResponseStatus.length > 7) {
if(rtlStore.initialAPIResponseStatus[0] === 'INCOMPLETE' && rtlStore.initialAPIResponseStatus.length > 8) {
rtlStore.initialAPIResponseStatus[0] = 'COMPLETE';
this.store.dispatch(new RTLActions.CloseSpinner());
}
@ -279,24 +279,23 @@ export class CLEffects implements OnDestroy {
ofType(CLActions.SAVE_NEW_CHANNEL_CL),
mergeMap((action: CLActions.SaveNewChannel) => {
this.store.dispatch(new CLActions.ClearEffectError('SaveNewChannel'));
return this.httpClient.post(this.CHILD_API_URL + environment.CHANNELS_API, {
id: action.payload.peerId, satoshis: action.payload.satoshis, feeRate: action.payload.feeRate, announce: action.payload.announce, minconf: (action.payload.minconf) ? action.payload.minconf : null
})
.pipe(
map((postRes: any) => {
this.logger.info(postRes);
this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.OpenSnackBar('Channel Added Successfully!'));
this.store.dispatch(new CLActions.FetchBalance());
return {
type: CLActions.FETCH_CHANNELS_CL
};
}),
catchError((err: any) => {
this.handleErrorWithoutAlert('SaveNewChannel', 'Opening Channel Failed.', err);
return of({type: RTLActions.VOID});
})
);
let newPayload = {id: action.payload.peerId, satoshis: action.payload.satoshis, feeRate: action.payload.feeRate, announce: action.payload.announce, minconf: (action.payload.minconf) ? action.payload.minconf : null};
if (action.payload.utxos) { newPayload['utxos'] = action.payload.utxos; }
return this.httpClient.post(this.CHILD_API_URL + environment.CHANNELS_API, newPayload)
.pipe(map((postRes: any) => {
this.logger.info(postRes);
this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.OpenSnackBar('Channel Added Successfully!'));
this.store.dispatch(new CLActions.FetchBalance());
this.store.dispatch(new CLActions.FetchTransactions());
return {
type: CLActions.FETCH_CHANNELS_CL
};
}),
catchError((err: any) => {
this.handleErrorWithoutAlert('SaveNewChannel', 'Opening Channel Failed.', err);
return of({type: RTLActions.VOID});
}));
}
));
@ -688,6 +687,7 @@ export class CLEffects implements OnDestroy {
this.logger.info(postRes);
this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new CLActions.FetchBalance());
this.store.dispatch(new CLActions.FetchTransactions());
return {
type: CLActions.SET_CHANNEL_TRANSACTION_RES_CL,
payload: postRes
@ -743,6 +743,7 @@ export class CLEffects implements OnDestroy {
this.store.dispatch(new CLActions.FetchFeeRates('perkw'));
this.store.dispatch(new CLActions.FetchFeeRates('perkb'));
this.store.dispatch(new CLActions.FetchPeers());
this.store.dispatch(new CLActions.FetchTransactions());
let newRoute = this.location.path();
if(newRoute.includes('/lnd/')) {
newRoute = newRoute.replace('/lnd/', '/cl/');

@ -175,11 +175,11 @@ export function CLReducer(state = initCLState, action: CLActions.CLActions) {
action.payload.forwarding_events.forEach(event => {
if (storedChannels && storedChannels.length > 0) {
for (let idx = 0; idx < storedChannels.length; idx++) {
if (storedChannels[idx].short_channel_id === event.in_channel) {
if (storedChannels[idx].short_channel_id && storedChannels[idx].short_channel_id === event.in_channel) {
event.in_channel_alias = storedChannels[idx].alias ? storedChannels[idx].alias : event.in_channel;
if (event.out_channel_alias) { return; }
}
if (storedChannels[idx].short_channel_id.toString() === event.out_channel) {
if (storedChannels[idx].short_channel_id && storedChannels[idx].short_channel_id.toString() === event.out_channel) {
event.out_channel_alias = storedChannels[idx].alias ? storedChannels[idx].alias : event.out_channel;
if (event.in_channel_alias) { return; }
}
@ -214,8 +214,10 @@ export function CLReducer(state = initCLState, action: CLActions.CLActions) {
totalInvoices: action.payload
};
case CLActions.SET_TRANSACTIONS_CL:
newAPIStatus = [...state.initialAPIResponseStatus, 'TRANSACTIONS'];
return {
...state,
initialAPIResponseStatus: newAPIStatus,
transactions: action.payload
};
default:

@ -1,7 +1,7 @@
import { DataTypeEnum, SwapTypeEnum } from '../services/consts-enums-functions';
import { GetInfoRoot, RTLConfiguration } from './RTLconfig';
import { GetInfo, Invoice, Channel, Peer } from './lndModels';
import { Invoice as InvoiceCL, GetInfo as GetInfoCL, Peer as PeerCL, Channel as ChannelCL } from './clModels';
import { Invoice as InvoiceCL, GetInfo as GetInfoCL, Peer as PeerCL, Channel as ChannelCL, Transaction as TransactionCL } from './clModels';
import { GetInfo as GetInfoECL, Peer as PeerECL, Channel as ChannelECL, Invoice as InvoiceECL, PaymentSent as PaymentSentECL } from './eclModels';
import { LoopQuote } from './loopModels';
@ -44,7 +44,7 @@ export interface OpenChannelAlert {
export interface CLOpenChannelAlert {
alertTitle?: string;
titleMessage?: string;
message?: { information: GetInfoCL, balance: number, peer?: PeerCL, peers?: PeerCL[] };
message?: { information: GetInfoCL, balance: number, transactions: TransactionCL[], peer?: PeerCL, peers?: PeerCL[] };
newlyAdded?: boolean;
component?: any;
}

Loading…
Cancel
Save