Merge branch 'Release-0.13.2' into cln-peer-swap

This commit is contained in:
ShahanaFarooqui 2022-08-25 02:31:42 -07:00 committed by GitHub
commit 9cff990441
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 110 additions and 62 deletions

File diff suppressed because one or more lines are too long

View File

@ -13,6 +13,6 @@
<style>@font-face{font-family:Roboto;src:url(Roboto-Thin.f7a95c9c5999532c.woff2) format("woff2"),url(Roboto-Thin.c13c157cb81e8ebb.woff) format("woff");font-weight:100;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-ThinItalic.b0e084abf689f393.woff2) format("woff2"),url(Roboto-ThinItalic.1111028df6cea564.woff) format("woff");font-weight:100;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Light.0e01b6cd13b3857f.woff2) format("woff2"),url(Roboto-Light.603ca9a537b88428.woff) format("woff");font-weight:300;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-LightItalic.232ef4b20215f720.woff2) format("woff2"),url(Roboto-LightItalic.1b5e142f787151c8.woff) format("woff");font-weight:300;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Regular.475ba9e4e2d63456.woff2) format("woff2"),url(Roboto-Regular.bcefbfee882bc1cb.woff) format("woff");font-weight:400;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-RegularItalic.e3a9ebdaac06bbc4.woff2) format("woff2"),url(Roboto-RegularItalic.0668fae6af0cf8c2.woff) format("woff");font-weight:400;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Medium.457532032ceb0168.woff2) format("woff2"),url(Roboto-Medium.6e1ae5f0b324a0aa.woff) format("woff");font-weight:500;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-MediumItalic.872f7060602d55d2.woff2) format("woff2"),url(Roboto-MediumItalic.e06fb533801cbb08.woff) format("woff");font-weight:500;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Bold.447291a88c067396.woff2) format("woff2"),url(Roboto-Bold.fc482e6133cf5e26.woff) format("woff");font-weight:700;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-BoldItalic.1b15168ef6fa4e16.woff2) format("woff2"),url(Roboto-BoldItalic.e26ba339b06f09f7.woff) format("woff");font-weight:700;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Black.2eaa390d458c877d.woff2) format("woff2"),url(Roboto-Black.b25f67ad8583da68.woff) format("woff");font-weight:900;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-BlackItalic.7dc03ee444552bc5.woff2) format("woff2"),url(Roboto-BlackItalic.c8dc642467cb3099.woff) format("woff");font-weight:900;font-style:italic}html{width:100%;height:99%;line-height:1.5;overflow-x:hidden;font-family:Roboto,sans-serif!important;font-size:62.5%}body{box-sizing:border-box;height:100%;margin:0;overflow:hidden}*{margin:0;padding:0}</style><link rel="stylesheet" href="styles.43515fc39338348b.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles.43515fc39338348b.css"></noscript></head>
<body>
<rtl-app></rtl-app>
<script src="runtime.340d96b403855bde.js" type="module"></script><script src="polyfills.eddc63f1737a019a.js" type="module"></script><script src="main.39b99f2612129aae.js" type="module"></script>
<script src="runtime.1ca59cd92764ed45.js" type="module"></script><script src="polyfills.eddc63f1737a019a.js" type="module"></script><script src="main.9b30da1402d5f9f2.js" type="module"></script>
</body></html>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
(()=>{"use strict";var e,v={},g={};function r(e){var n=g[e];if(void 0!==n)return n.exports;var t=g[e]={id:e,loaded:!1,exports:{}};return v[e].call(t.exports,t,t.exports,r),t.loaded=!0,t.exports}r.m=v,e=[],r.O=(n,t,f,o)=>{if(!t){var a=1/0;for(i=0;i<e.length;i++){for(var[t,f,o]=e[i],c=!0,u=0;u<t.length;u++)(!1&o||a>=o)&&Object.keys(r.O).every(b=>r.O[b](t[u]))?t.splice(u--,1):(c=!1,o<a&&(a=o));if(c){e.splice(i--,1);var d=f();void 0!==d&&(n=d)}}return n}o=o||0;for(var i=e.length;i>0&&e[i-1][2]>o;i--)e[i]=e[i-1];e[i]=[t,f,o]},r.n=e=>{var n=e&&e.__esModule?()=>e.default:()=>e;return r.d(n,{a:n}),n},r.d=(e,n)=>{for(var t in n)r.o(n,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:n[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((n,t)=>(r.f[t](e,n),n),[])),r.u=e=>e+"."+{564:"3d38ee9330b2ba94",636:"95c8ae357b1ed820",893:"9a615c46b89a5a79",924:"1c1eb885f1f101d2"}[e]+".js",r.miniCssF=e=>{},r.o=(e,n)=>Object.prototype.hasOwnProperty.call(e,n),(()=>{var e={},n="RTLApp:";r.l=(t,f,o,i)=>{if(e[t])e[t].push(f);else{var a,c;if(void 0!==o)for(var u=document.getElementsByTagName("script"),d=0;d<u.length;d++){var l=u[d];if(l.getAttribute("src")==t||l.getAttribute("data-webpack")==n+o){a=l;break}}a||(c=!0,(a=document.createElement("script")).type="module",a.charset="utf-8",a.timeout=120,r.nc&&a.setAttribute("nonce",r.nc),a.setAttribute("data-webpack",n+o),a.src=r.tu(t)),e[t]=[f];var s=(m,b)=>{a.onerror=a.onload=null,clearTimeout(p);var h=e[t];if(delete e[t],a.parentNode&&a.parentNode.removeChild(a),h&&h.forEach(_=>_(b)),m)return m(b)},p=setTimeout(s.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=s.bind(null,a.onerror),a.onload=s.bind(null,a.onload),c&&document.head.appendChild(a)}}})(),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{var e;r.tt=()=>(void 0===e&&(e={createScriptURL:n=>n},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e)})(),r.tu=e=>r.tt().createScriptURL(e),r.p="",(()=>{var e={666:0};r.f.j=(f,o)=>{var i=r.o(e,f)?e[f]:void 0;if(0!==i)if(i)o.push(i[2]);else if(666!=f){var a=new Promise((l,s)=>i=e[f]=[l,s]);o.push(i[2]=a);var c=r.p+r.u(f),u=new Error;r.l(c,l=>{if(r.o(e,f)&&(0!==(i=e[f])&&(e[f]=void 0),i)){var s=l&&("load"===l.type?"missing":l.type),p=l&&l.target&&l.target.src;u.message="Loading chunk "+f+" failed.\n("+s+": "+p+")",u.name="ChunkLoadError",u.type=s,u.request=p,i[1](u)}},"chunk-"+f,f)}else e[f]=0},r.O.j=f=>0===e[f];var n=(f,o)=>{var u,d,[i,a,c]=o,l=0;if(i.some(p=>0!==e[p])){for(u in a)r.o(a,u)&&(r.m[u]=a[u]);if(c)var s=c(r)}for(f&&f(o);l<i.length;l++)r.o(e,d=i[l])&&e[d]&&e[d][0](),e[d]=0;return r.O(s)},t=self.webpackChunkRTLApp=self.webpackChunkRTLApp||[];t.forEach(n.bind(null,0)),t.push=n.bind(null,t.push.bind(t))})()})();

View File

@ -46,7 +46,18 @@
</mat-form-field>
</div>
</form> -->
<div [perfectScrollbar] fxLayout="column" fxLayoutAlign="start center" fxFlex="100" class="table-container mt-2">
<div fxLayout="row" fxLayoutAlign="start center" class="page-sub-title-container padding-gap-x mt-2">
<div fxFlex="30" fxFlex.gt-xs="70">
<fa-icon [icon]="faUsers" class="page-title-img mr-1"></fa-icon>
<span class="page-title">Liquidity Providing Peers</span>
</div>
<mat-form-field fxFlex="30">
<div fxLayout="row" fxLayoutAlign="start start">
<input matInput (keyup)="applyFilter()" [(ngModel)]="selFilter" placeholder="Filter">
</div>
</mat-form-field>
</div>
<div [perfectScrollbar] fxLayout="column" fxLayoutAlign="start center" fxFlex="100" class="table-container">
<mat-progress-bar *ngIf="apiCallStatus.status === apiCallStatusEnum.INITIATED" mode="indeterminate"></mat-progress-bar>
<table mat-table #table [dataSource]="liquidityNodes" matSort [ngClass]="{'overflow-auto error-border': errorMessage !== '','overflow-auto': true}">
<ng-container matColumnDef="alias">

View File

@ -5,12 +5,12 @@ import { Store } from '@ngrx/store';
import { MatTableDataSource } from '@angular/material/table';
import { MatSort } from '@angular/material/sort';
import { MatPaginator, MatPaginatorIntl } from '@angular/material/paginator';
import { faBullhorn, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { faBullhorn, faExclamationTriangle, faUsers } from '@fortawesome/free-solid-svg-icons';
import { DataService } from '../../../shared/services/data.service';
import { LoggerService } from '../../../shared/services/logger.service';
import { CommonService } from '../../../shared/services/common.service';
import { AlertTypeEnum, APICallStatusEnum, DataTypeEnum, getPaginatorLabel, PAGE_SIZE, PAGE_SIZE_OPTIONS, ScreenSizeEnum } from '../../../shared/services/consts-enums-functions';
import { AlertTypeEnum, APICallStatusEnum, DataTypeEnum, getPaginatorLabel, PAGE_SIZE, PAGE_SIZE_OPTIONS, ScreenSizeEnum, NODE_FEATURES_CLN } from '../../../shared/services/consts-enums-functions';
import { GetInfo, LookupNode } from '../../../shared/models/clnModels';
import { ApiCallStatusPayload } from '../../../shared/models/apiCallsPayload';
import { openAlert, openConfirmation } from '../../../store/rtl.actions';
@ -19,6 +19,7 @@ import { RTLState } from '../../../store/rtl.state';
import { RTLEffects } from '../../../store/rtl.effects';
import { CLNOpenLiquidityChannelComponent } from '../open-liquidity-channel-modal/open-liquidity-channel-modal.component';
import { nodeInfoAndNodeSettingsAndBalance } from '../../store/cln.selector';
import { DecimalPipe } from '@angular/common';
@Component({
selector: 'rtl-cln-liquidity-ads-list',
@ -37,6 +38,7 @@ export class CLNLiquidityAdsListComponent implements OnInit, OnDestroy {
public displayedColumns: any[] = [];
public faBullhorn = faBullhorn;
public faExclamationTriangle = faExclamationTriangle;
public faUsers = faUsers;
public totalBalance = 0;
public information: GetInfo;
public channelAmount = 100000;
@ -51,11 +53,12 @@ export class CLNLiquidityAdsListComponent implements OnInit, OnDestroy {
public screenSize = '';
public screenSizeEnum = ScreenSizeEnum;
public errorMessage = '';
public selFilter = '';
public apiCallStatus: ApiCallStatusPayload = { status: APICallStatusEnum.INITIATED };
public apiCallStatusEnum = APICallStatusEnum;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject()];
constructor(private logger: LoggerService, private store: Store<RTLState>, private dataService: DataService, private commonService: CommonService, private rtlEffects: RTLEffects) {
constructor(private logger: LoggerService, private store: Store<RTLState>, private dataService: DataService, private commonService: CommonService, private rtlEffects: RTLEffects, private decimalPipe: DecimalPipe) {
this.askTooltipMsg = 'Specify the liquidity requirements for your node: \n 1. Channel Amount - Amount in Sats you need on the channel opened to your node \n 2. Channel opening fee rate - Rate in Sats/vByte that you are willing to pay to open the channel to you';
this.nodesTooltipMsg = 'These nodes are advertising their liquidity offering on the network.\nYou should pay attention to the following aspects to evaluate each node offer: \n- The total bitcoin deployed on the node, the more the better\n';
this.nodesTooltipMsg = this.nodesTooltipMsg + '- The number of channels open on the node, the more the better\n- The channel open fee which the node will charge from you\n- The routing fee which the node will charge on the payments, the lesser the better\n- The reliability of the node, ideally uptime. Refer to the information being provided by the node explorers';
@ -85,8 +88,7 @@ export class CLNLiquidityAdsListComponent implements OnInit, OnDestroy {
if (nodeListRes && !(<any[]>nodeListRes).length) { nodeListRes = []; }
this.logger.info('Received Liquidity Ads Enabled Nodes: ' + JSON.stringify(nodeListRes));
this.apiCallStatus.status = APICallStatusEnum.COMPLETED;
this.liquidityNodesData = (<LookupNode[]>nodeListRes);
this.liquidityNodesData.forEach((lqNode) => {
(<any[]>nodeListRes).forEach((lqNode) => {
const a: string[] = [];
lqNode.address_types = Array.from(new Set(lqNode.addresses?.reduce((acc, addr) => {
if (addr.type?.includes('ipv') || addr.type?.includes('tor')) {
@ -95,6 +97,7 @@ export class CLNLiquidityAdsListComponent implements OnInit, OnDestroy {
return acc;
}, a)));
});
this.liquidityNodesData = (<LookupNode[]>nodeListRes).filter(node => node.nodeid !== this.information.id);
this.onCalculateOpeningFee();
this.loadLiqNodesTable(this.liquidityNodesData);
}, error: (err) => {
@ -118,14 +121,26 @@ export class CLNLiquidityAdsListComponent implements OnInit, OnDestroy {
// this.liquidityNodes.filter = 'Changed';
}
applyFilter() {
this.liquidityNodes.filter = this.selFilter.trim().toLowerCase();
}
loadLiqNodesTable(liqNodes: LookupNode[]) {
this.liquidityNodes = new MatTableDataSource<LookupNode>([...liqNodes]);
this.liquidityNodes.sortingDataAccessor = (data: any, sortHeaderId: string) => ((data[sortHeaderId] && isNaN(data[sortHeaderId])) ? data[sortHeaderId].toLocaleLowerCase() : data[sortHeaderId] ? +data[sortHeaderId] : null);
this.liquidityNodes.sort = this.sort;
this.liquidityNodes.paginator = this.paginator;
if (this.sort) { this.sort.sort({ id: 'channelOpeningFee', start: 'asc', disableClear: true }); }
this.liquidityNodes.filterPredicate = (node: LookupNode, fltr: string) => (node.channelCount || 0) >= this.channelCount && (node.nodeCapacity || 0) >= this.nodeCapacity;
this.onFilter();
this.liquidityNodes.filterPredicate = (node: LookupNode, fltr: string) => {
const newNode = ((node.alias) ? node.alias.toLocaleLowerCase() : '') + (node.channelOpeningFee ? node.channelOpeningFee + ' Sats' : '')
+ (node.option_will_fund?.lease_fee_base_msat ? (node.option_will_fund?.lease_fee_base_msat/1000) + ' Sats' : '') + (node.option_will_fund?.lease_fee_basis ? (this.decimalPipe.transform(node.option_will_fund?.lease_fee_basis/100, '1.2-2') + '%') : '')
+ (node.option_will_fund?.channel_fee_max_base_msat ? (node.option_will_fund?.channel_fee_max_base_msat/1000) + ' Sats' : '') + (node.option_will_fund?.channel_fee_max_proportional_thousandths ? (node.option_will_fund?.channel_fee_max_proportional_thousandths*1000) + ' ppm' : '')
+ (node.address_types ? node.address_types.reduce((acc, curr) => acc + (curr === 'tor' ? ' tor' : curr === 'ipv' ? ' clearnet' : (' ' + curr.toLowerCase())), '') : '');
return newNode.includes(fltr);
}
this.applyFilter();
// this.liquidityNodes.filterPredicate = (node: LookupNode, fltr: string) => node.channelCount >= this.channelCount && node.nodeCapacity >= this.nodeCapacity;
// this.onFilter();
}
viewLeaseOn(lqNode: LookupNode, link: string) {
@ -158,17 +173,28 @@ export class CLNLiquidityAdsListComponent implements OnInit, OnDestroy {
onViewLeaseInfo(lqNode: LookupNode) {
const addArr = lqNode.addresses?.reduce((acc, curr) => {
if (curr.address && curr.address.length > 40) { curr.address = curr.address.substring(0, 39) + '...'; }
return acc.concat(JSON.stringify(curr)?.replace('{', '')?.replace('}', '')?.replace(/:/g, ': ')?.replace(/,/g, '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;')?.replace(/"/g, ''));
}, <any[]>[]);
return acc.concat(JSON.stringify(curr).replace('{', '').replace('}', '').replace(/:/g, ': ').replace(/,/g, '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;').replace(/"/g, ''));
}, <any>[]);
const featureDescriptions: string[] = [];
if (lqNode.features && lqNode.features.trim() !== '') {
const featureHex = parseInt(lqNode.features, 16);
NODE_FEATURES_CLN.forEach((feature) => {
if (!!(featureHex & ((1 << feature.range.min) | (1 << feature.range.max)))) {
featureDescriptions.push(feature.description);
}
});
}
const reorderedLQNode = [
[{ key: 'alias', value: lqNode.alias, title: 'Node Alias', width: 50, type: DataTypeEnum.STRING },
{ key: 'last_timestamp', value: lqNode.last_timestamp, title: 'Last Timestamp', width: 50, type: DataTypeEnum.DATE_TIME }],
[{ key: 'nodeid', value: lqNode.nodeid, title: 'Node ID', width: 100, type: DataTypeEnum.STRING }],
[{ key: 'base_fee', value: ((lqNode.option_will_fund?.lease_fee_base_msat || 0) / 1000), title: 'Lease Base Fee (Sats)', width: 50, type: DataTypeEnum.NUMBER },
[{ key: 'compact_lease', value: lqNode.option_will_fund?.compact_lease, title: 'Compact Lease', width: 100, type: DataTypeEnum.STRING }],
[{ key: 'base_fee', value: lqNode.option_will_fund?.lease_fee_base_msat ? (lqNode.option_will_fund.lease_fee_base_msat / 1000) : 0, title: 'Lease Base Fee (Sats)', width: 50, type: DataTypeEnum.NUMBER },
{ key: 'fee_basis', value: lqNode.option_will_fund?.lease_fee_basis, title: 'Lease Base Basis (bps)', width: 50, type: DataTypeEnum.NUMBER }],
[{ key: 'channel_max_base', value: (lqNode.option_will_fund?.channel_fee_max_base_msat || 0) / 1000, title: 'Max Channel Routing Base Fee (Sats)', width: 50, type: DataTypeEnum.NUMBER },
[{ key: 'channel_max_base', value: lqNode.option_will_fund?.channel_fee_max_base_msat ? (lqNode.option_will_fund.channel_fee_max_base_msat / 1000) : 0, title: 'Max Channel Routing Base Fee (Sats)', width: 50, type: DataTypeEnum.NUMBER },
{ key: 'channel_max_rate', value: (lqNode.option_will_fund?.channel_fee_max_proportional_thousandths || 0) * 1000, title: 'Max Channel Routing Fee Rate (ppm)', width: 50, type: DataTypeEnum.NUMBER }],
[{ key: 'funding_rate', value: (lqNode.option_will_fund?.funding_weight || 0), title: 'Funding Weight', width: 100, type: DataTypeEnum.NUMBER }],
[{ key: 'funding_rate', value: lqNode.option_will_fund?.funding_weight, title: 'Funding Weight', width: 100, type: DataTypeEnum.NUMBER }],
[{ key: 'features', value: featureDescriptions, title: 'Features', width: 100, type: DataTypeEnum.ARRAY }],
[{ key: 'address', value: addArr, title: 'Address', width: 100, type: DataTypeEnum.ARRAY }]
];
this.store.dispatch(openConfirmation({

View File

@ -75,7 +75,7 @@ export class CLNOpenLiquidityChannelComponent implements OnInit, OnDestroy {
if (!this.node || !this.node.option_will_fund || !this.requestedAmount || !this.feeRate || !this.localAmount || this.localAmount < 20000) {
return true;
}
const newChannel = { peerId: this.node.nodeid || '', satoshis: this.localAmount.toString(), feeRate: this.feeRate + 'perkb', requestAmount: this.requestedAmount.toString(), compactLease: this.node.option_will_fund.compact_lease };
const newChannel = { peerId: this.node.nodeid || '', satoshis: this.localAmount.toString(), feeRate: this.feeRate + 'perkb', requestAmount: this.requestedAmount.toString(), compactLease: this.node.option_will_fund.compact_lease, announce: true };
this.store.dispatch(saveNewChannel({ payload: newChannel }));
}

View File

@ -38,54 +38,62 @@
<mat-slide-toggle autoFocus class="my-1" tabindex="1" color="primary" name="enableOfr" [(ngModel)]="enableOffers" (change)="onUpdateFeature()">Enable Offers {{enableOffers ? '(You can find Offers under Lightning -> Transactions -> Offers)' : ''}}</mat-slide-toggle>
</form>
<form *ngIf="i === 1" fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch" class="page-sub-title-container" #form="ngForm">
<div fxFlex="100" fxLayout="row" class="alert alert-warn">
<fa-icon [icon]="faExclamationTriangle" class="mr-1 alert-icon"></fa-icon>
<span>These config changes should be configured permanently via the config file on your CLN node otherwise the policy would need to be configured again, if your node restarts.</span>
<div *ngIf="!features[1].enabled">
<div fxFlex="100" fxLayout="row" class="alert alert-warn">
<fa-icon [icon]="faExclamationTriangle" class="mr-1 alert-icon"></fa-icon>
<span>Please ensure that <strong>experimental-dual-fund</strong> flag is set to true in the Core Lightning config before enabling it in RTL. Click <strong><a href="https://medium.com/blockstream/setting-up-liquidity-ads-in-c-lightning-54e4c59c091d" target="_blank">here</a></strong> to learn more about Core Lightning Liquidity Ads.</span>
</div>
</div>
<div fxLayout="column" fxLayout.gt-sm="row" fxFlex="100" fxLayoutAlign.gt-sm="space-between center" fxLayoutAlign="start stretch">
<mat-form-field fxFlex="49" fxLayoutAlign="start end">
<mat-select autofocus tabindex="1" [(ngModel)]="selPolicyType" (selectionChange)="policyMod=null" placeholder="Policy" name="policy">
<mat-option *ngFor="let policyType of policyTypes" [value]="policyType">
{{policyType.id | titlecase}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex="49">
<input matInput [(ngModel)]="policyMod" [placeholder]="selPolicyType.placeholder" type="number" [step]="selPolicyType.id === 'fixed' ? 1000 : 10" [min]="selPolicyType.min" [max]="selPolicyType.max" tabindex="2" required name="plcMod" #plcMod="ngModel">
<mat-hint>{{selPolicyType.placeholder}} should be between {{selPolicyType.min}} and {{selPolicyType.max}}</mat-hint>
<mat-error *ngIf="!policyMod">{{selPolicyType.placeholder}} is required.</mat-error>
<mat-error *ngIf="policyMod < selPolicyType.min">{{selPolicyType.placeholder}} must be greater than or equal to {{selPolicyType.min}}.</mat-error>
<mat-error *ngIf="policyMod > selPolicyType.max">{{selPolicyType.placeholder}} must be less than or equal to {{selPolicyType.max}}.</mat-error>
</mat-form-field>
</div>
<div fxLayout="column" fxLayout.gt-sm="row" fxFlex="100" fxLayoutAlign.gt-sm="space-between center" fxLayoutAlign="start stretch">
<mat-form-field fxFlex="49">
<input matInput [(ngModel)]="leaseFeeBaseSat" placeholder="Lease Base Fee (Sats)" type="number" step="100" min="0" tabindex="3" required name="leaseFeeBaseSat">
<mat-error *ngIf="!leaseFeeBaseSat">Lease base fee is required.</mat-error>
</mat-form-field>
<mat-form-field fxFlex="49">
<input matInput [(ngModel)]="leaseFeeBasis" placeholder="Lease Base Basis (bps)" type="number" step="1" min="0" tabindex="4" required name="leaseFeeBasis">
<mat-error *ngIf="!leaseFeeBasis">Lease base basis is required.</mat-error>
</mat-form-field>
</div>
<div fxLayout="column" fxLayout.gt-sm="row" fxFlex="100" fxLayoutAlign.gt-sm="space-between center" fxLayoutAlign="start stretch">
<mat-form-field fxFlex="49">
<input matInput [(ngModel)]="channelFeeMaxBaseSat" placeholder="Max Channel Routing Base Fee (Sats)" type="number" step="100" min="0" tabindex="5" required name="channelFeeMaxBaseSat">
<mat-error *ngIf="!channelFeeMaxBaseSat">Max channel routing base fee is required.</mat-error>
</mat-form-field>
<mat-form-field fxFlex="49">
<input matInput [(ngModel)]="channelFeeMaxProportional" placeholder="Max Channel Routing Fee Rate (ppm)" type="number" step="1000" min="0" tabindex="6" required name="channelFeeMaxProportional">
<mat-error *ngIf="!channelFeeMaxProportional">Max channel routing fee rate is required.</mat-error>
</mat-form-field>
</div>
<h4 *ngIf="flgUpdateCalled" fxLayoutAlign="start stretch" class="font-bold-500 mt-2">
<span fxFlex="100" class="alert" [ngClass]="{'alert-danger': !!updateMsg.error, 'alert-info': !!updateMsg.data}">
{{(updateMsg.error && updateMsg.error !== '') ? (('Error: ' + updateMsg.error) || 'Unknown Error') : (updateMsg.data && updateMsg.data !== '') ? updateMsg.data : 'Successfully Updated the Funding Policy!'}}
</span>
</h4>
<div fxLayout="row" class="my-1">
<button class="mr-1" mat-stroked-button color="primary" (click)="onResetPolicy()" tabindex="7">Reset</button>
<button mat-flat-button color="primary" (click)="onUpdateFundingPolicy()" tabindex="8">Update</button>
<div *ngIf="features[1].enabled" fxLayout="column">
<div fxFlex="100" fxLayout="row" class="alert alert-warn mb-2">
<fa-icon [icon]="faExclamationTriangle" class="mr-1 alert-icon"></fa-icon>
<span>These config changes should be configured permanently via the config file on your CLN node otherwise the policy would need to be configured again, if your node restarts.</span>
</div>
<div fxLayout="column" fxLayout.gt-sm="row" fxFlex="100" fxLayoutAlign.gt-sm="space-between center" fxLayoutAlign="start stretch">
<mat-form-field fxFlex="49" fxLayoutAlign="start end">
<mat-select autofocus tabindex="1" [(ngModel)]="selPolicyType" (selectionChange)="policyMod=null" placeholder="Policy" name="policy">
<mat-option *ngFor="let policyType of policyTypes" [value]="policyType">
{{policyType.id | titlecase}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex="49">
<input matInput [(ngModel)]="policyMod" [placeholder]="selPolicyType.placeholder" type="number" [step]="selPolicyType.id === 'fixed' ? 1000 : 10" [min]="selPolicyType.min" [max]="selPolicyType.max" tabindex="2" required name="plcMod" #plcMod="ngModel">
<mat-hint>{{selPolicyType.placeholder}} should be between {{selPolicyType.min}} and {{selPolicyType.max}}</mat-hint>
<mat-error *ngIf="!policyMod">{{selPolicyType.placeholder}} is required.</mat-error>
<mat-error *ngIf="policyMod < selPolicyType.min">{{selPolicyType.placeholder}} must be greater than or equal to {{selPolicyType.min}}.</mat-error>
<mat-error *ngIf="policyMod > selPolicyType.max">{{selPolicyType.placeholder}} must be less than or equal to {{selPolicyType.max}}.</mat-error>
</mat-form-field>
</div>
<div fxLayout="column" fxLayout.gt-sm="row" fxFlex="100" fxLayoutAlign.gt-sm="space-between center" fxLayoutAlign="start stretch">
<mat-form-field fxFlex="49">
<input matInput [(ngModel)]="leaseFeeBaseSat" placeholder="Lease Base Fee (Sats)" type="number" step="100" min="0" tabindex="3" required name="leaseFeeBaseSat">
<mat-error *ngIf="!leaseFeeBaseSat">Lease base fee is required.</mat-error>
</mat-form-field>
<mat-form-field fxFlex="49">
<input matInput [(ngModel)]="leaseFeeBasis" placeholder="Lease Base Basis (bps)" type="number" step="1" min="0" tabindex="4" required name="leaseFeeBasis">
<mat-error *ngIf="!leaseFeeBasis">Lease base basis is required.</mat-error>
</mat-form-field>
</div>
<div fxLayout="column" fxLayout.gt-sm="row" fxFlex="100" fxLayoutAlign.gt-sm="space-between center" fxLayoutAlign="start stretch">
<mat-form-field fxFlex="49">
<input matInput [(ngModel)]="channelFeeMaxBaseSat" placeholder="Max Channel Routing Base Fee (Sats)" type="number" step="100" min="0" tabindex="5" required name="channelFeeMaxBaseSat">
<mat-error *ngIf="!channelFeeMaxBaseSat">Max channel routing base fee is required.</mat-error>
</mat-form-field>
<mat-form-field fxFlex="49">
<input matInput [(ngModel)]="channelFeeMaxProportional" placeholder="Max Channel Routing Fee Rate (ppm)" type="number" step="1000" min="0" tabindex="6" required name="channelFeeMaxProportional">
<mat-error *ngIf="!channelFeeMaxProportional">Max channel routing fee rate is required.</mat-error>
</mat-form-field>
</div>
<h4 *ngIf="flgUpdateCalled" fxLayoutAlign="start stretch" class="font-bold-500 mt-2">
<span fxFlex="100" class="alert" [ngClass]="{'alert-danger': !!updateMsg.error, 'alert-info': !!updateMsg.data}">
{{(updateMsg.error && updateMsg.error !== '') ? (('Error: ' + updateMsg.error) || 'Unknown Error') : (updateMsg.data && updateMsg.data !== '') ? updateMsg.data : 'Successfully Updated the Funding Policy!'}}
</span>
</h4>
<div fxLayout="row" class="my-1">
<button class="mr-1" mat-stroked-button color="primary" (click)="onResetPolicy()" tabindex="7">Reset</button>
<button mat-flat-button color="primary" (click)="onUpdateFundingPolicy()" tabindex="8">Update</button>
</div>
</div>
</form>
</div>