mirror of
https://github.com/Ride-The-Lightning/RTL.git
synced 2024-11-19 09:50:36 +01:00
Funder Policy Update
Funder Policy Update
This commit is contained in:
parent
ee096d7e1b
commit
519c18bafc
@ -120,3 +120,22 @@ export const listForwards = (req, res, next) => {
|
||||
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||
});
|
||||
};
|
||||
export const funderUpdatePolicy = (req, res, next) => {
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting or Updating Funder Policy..' });
|
||||
options = common.getOptions(req);
|
||||
if (options.error) {
|
||||
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||
}
|
||||
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/funderUpdate';
|
||||
if (req.body && req.body.policy) {
|
||||
options.body = req.body;
|
||||
}
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Funder Update Body', data: options.body });
|
||||
request.post(options).then((body) => {
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Funder Policy Received', data: body });
|
||||
res.status(200).json(body);
|
||||
}).catch((errRes) => {
|
||||
const err = common.handleError(errRes, 'Channels', 'Funder Policy Error', req.session.selectedNode);
|
||||
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||
});
|
||||
};
|
||||
|
@ -1,12 +1,13 @@
|
||||
import exprs from 'express';
|
||||
const { Router } = exprs;
|
||||
import { isAuthenticated } from '../../utils/authCheck.js';
|
||||
import { listChannels, openChannel, setChannelFee, closeChannel, getLocalRemoteBalance, listForwards } from '../../controllers/cln/channels.js';
|
||||
import { listChannels, openChannel, setChannelFee, closeChannel, getLocalRemoteBalance, listForwards, funderUpdatePolicy } from '../../controllers/cln/channels.js';
|
||||
const router = Router();
|
||||
router.get('/listChannels', isAuthenticated, listChannels);
|
||||
router.post('/', isAuthenticated, openChannel);
|
||||
router.post('/setChannelFee', isAuthenticated, setChannelFee);
|
||||
router.delete('/:channelId', isAuthenticated, closeChannel);
|
||||
router.get('/localremotebalance', isAuthenticated, getLocalRemoteBalance);
|
||||
router.get('/localRemoteBalance', isAuthenticated, getLocalRemoteBalance);
|
||||
router.get('/listForwards', isAuthenticated, listForwards);
|
||||
router.post('/funderUpdate', isAuthenticated, funderUpdatePolicy);
|
||||
export default router;
|
||||
|
@ -106,3 +106,21 @@ export const listForwards = (req, res, next) => {
|
||||
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||
});
|
||||
};
|
||||
|
||||
export const funderUpdatePolicy = (req, res, next) => {
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Getting or Updating Funder Policy..' });
|
||||
options = common.getOptions(req);
|
||||
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); }
|
||||
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/funderUpdate';
|
||||
if (req.body && req.body.policy) {
|
||||
options.body = req.body;
|
||||
}
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Funder Update Body', data: options.body });
|
||||
request.post(options).then((body) => {
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Funder Policy Received', data: body });
|
||||
res.status(200).json(body);
|
||||
}).catch((errRes) => {
|
||||
const err = common.handleError(errRes, 'Channels', 'Funder Policy Error', req.session.selectedNode);
|
||||
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||
});
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import exprs from 'express';
|
||||
const { Router } = exprs;
|
||||
import { isAuthenticated } from '../../utils/authCheck.js';
|
||||
import { listChannels, openChannel, setChannelFee, closeChannel, getLocalRemoteBalance, listForwards } from '../../controllers/cln/channels.js';
|
||||
import { listChannels, openChannel, setChannelFee, closeChannel, getLocalRemoteBalance, listForwards, funderUpdatePolicy } from '../../controllers/cln/channels.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
@ -10,7 +10,9 @@ router.post('/', isAuthenticated, openChannel);
|
||||
router.post('/setChannelFee', isAuthenticated, setChannelFee);
|
||||
router.delete('/:channelId', isAuthenticated, closeChannel);
|
||||
|
||||
router.get('/localremotebalance', isAuthenticated, getLocalRemoteBalance);
|
||||
router.get('/localRemoteBalance', isAuthenticated, getLocalRemoteBalance);
|
||||
router.get('/listForwards', isAuthenticated, listForwards);
|
||||
|
||||
router.post('/funderUpdate', isAuthenticated, funderUpdatePolicy);
|
||||
|
||||
export default router;
|
||||
|
@ -198,7 +198,7 @@ export class CLNEffects implements OnDestroy {
|
||||
ofType(CLNActions.FETCH_LOCAL_REMOTE_BALANCE_CLN),
|
||||
mergeMap(() => {
|
||||
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchLocalRemoteBalance', status: APICallStatusEnum.INITIATED } }));
|
||||
return this.httpClient.get<LocalRemoteBalance>(this.CHILD_API_URL + environment.CHANNELS_API + '/localremotebalance');
|
||||
return this.httpClient.get<LocalRemoteBalance>(this.CHILD_API_URL + environment.CHANNELS_API + '/localRemoteBalance');
|
||||
}),
|
||||
map((lrBalance) => {
|
||||
this.logger.info(lrBalance);
|
||||
|
@ -9,7 +9,7 @@
|
||||
<span class="page-title">Features</span>
|
||||
</div>
|
||||
<mat-accordion>
|
||||
<mat-expansion-panel [expanded]="false" class="flat-expansion-panel my-1" *ngFor="let feature of features; index as i">
|
||||
<mat-expansion-panel [expanded]="false" class="flat-expansion-panel my-1" *ngFor="let feature of features; index as i" (opened)="onPanelOpen(i)">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title fxFlex="100" fxLayoutAlign="space-between center">
|
||||
<h4 class="font-bold-500">{{feature.name}}</h4>
|
||||
@ -37,6 +37,53 @@
|
||||
</div>
|
||||
<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>
|
||||
<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="1000" 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="10" 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="1" 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="10" 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" class="font-bold-500 mt-2">{{(updateMsg.error && updateMsg.error !== '') ? (('Error: ' + updateMsg.error) || 'Unknown Error') : ((updateMsg) || 'Successfully Updated the Funding Policy!')}}</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>
|
||||
</form>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
||||
|
@ -12,7 +12,11 @@ import { updateServiceSettings } from '../../../../store/rtl.actions';
|
||||
import { setChildNodeSettingsLND } from '../../../../lnd/store/lnd.actions';
|
||||
import { setChildNodeSettingsCL } from '../../../../cln/store/cln.actions';
|
||||
import { setChildNodeSettingsECL } from '../../../../eclair/store/ecl.actions';
|
||||
import { ServicesEnum, UI_MESSAGES } from '../../../services/consts-enums-functions';
|
||||
import { DataService } from '../../../services/data.service';
|
||||
import { ServicesEnum, UI_MESSAGES, LADS_POLICY } from '../../../services/consts-enums-functions';
|
||||
import { balance } from '../../../../cln/store/cln.selector';
|
||||
import { Balance, FunderPolicy } from '../../../models/clModels';
|
||||
import { ApiCallStatusPayload } from '../../../models/apiCallsPayload';
|
||||
|
||||
@Component({
|
||||
selector: 'rtl-experimental-settings',
|
||||
@ -24,21 +28,36 @@ export class ExperimentalSettingsComponent implements OnInit, OnDestroy {
|
||||
public faInfoCircle = faInfoCircle;
|
||||
public faExclamationTriangle = faExclamationTriangle;
|
||||
public faCode = faCode;
|
||||
public features = [{ name: 'Offers', enabled: false }];
|
||||
public features = [{ name: 'Offers', enabled: false }, { name: 'Channel Funding Policy', enabled: false }];
|
||||
public enableOffers = false;
|
||||
public selNode: ConfigSettingsNode;
|
||||
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
|
||||
public fundingPolicy: FunderPolicy = null;
|
||||
public policyTypes = LADS_POLICY;
|
||||
public selPolicyType = LADS_POLICY[0];
|
||||
public policyMod: number;
|
||||
public leaseFeeBaseSat: number;
|
||||
public leaseFeeBasis: number;
|
||||
public channelFeeMaxBaseSat: number;
|
||||
public channelFeeMaxProportional: number;
|
||||
public flgUpdateCalled = false;
|
||||
public updateMsg = '';
|
||||
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject(), new Subject()];
|
||||
|
||||
constructor(private logger: LoggerService, private store: Store<RTLState>) { }
|
||||
|
||||
constructor(private logger: LoggerService, private store: Store<RTLState>, private dataService: DataService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.store.select(rootSelectedNode).pipe(takeUntil(this.unSubs[0])).
|
||||
this.store.select(rootSelectedNode).pipe(takeUntil(this.unSubs[1])).
|
||||
subscribe((selNode) => {
|
||||
this.selNode = selNode;
|
||||
this.enableOffers = this.selNode.settings.enableOffers;
|
||||
this.features[0].enabled = this.enableOffers;
|
||||
this.logger.info(this.selNode);
|
||||
});
|
||||
this.store.select(balance).pipe(takeUntil(this.unSubs[2])).
|
||||
subscribe((balanceSeletor: { balance: Balance, apiCallStatus: ApiCallStatusPayload }) => {
|
||||
this.policyTypes[2].max = balanceSeletor.balance.totalBalance || 1000;
|
||||
});
|
||||
}
|
||||
|
||||
onUpdateFeature(): boolean | void {
|
||||
@ -66,6 +85,52 @@ export class ExperimentalSettingsComponent implements OnInit, OnDestroy {
|
||||
}));
|
||||
}
|
||||
|
||||
onPanelOpen(i) {
|
||||
if (i === 1 && !this.fundingPolicy) {
|
||||
this.dataService.getOrUpdateFunderPolicy().pipe(takeUntil(this.unSubs[0])).subscribe((res: any) => {
|
||||
this.logger.info('Received Funder Update Policy: ' + JSON.stringify(res));
|
||||
this.fundingPolicy = res;
|
||||
if (this.fundingPolicy.policy) {
|
||||
this.selPolicyType = LADS_POLICY.find((policy) => policy.id === this.fundingPolicy.policy);
|
||||
}
|
||||
this.policyMod = this.fundingPolicy.policy_mod || this.fundingPolicy.policy_mod === 0 ? this.fundingPolicy.policy_mod : null;
|
||||
this.leaseFeeBaseSat = this.fundingPolicy.lease_fee_base_msat ? this.fundingPolicy.lease_fee_base_msat / 1000 : this.fundingPolicy.lease_fee_base_msat === 0 ? 0 : null;
|
||||
this.leaseFeeBasis = this.fundingPolicy.lease_fee_basis || this.fundingPolicy.lease_fee_basis === 0 ? this.fundingPolicy.lease_fee_basis : null;
|
||||
this.channelFeeMaxBaseSat = this.fundingPolicy.channel_fee_max_base_msat ? this.fundingPolicy.channel_fee_max_base_msat / 1000 : this.fundingPolicy.channel_fee_max_base_msat === 0 ? 0 : null;
|
||||
this.channelFeeMaxProportional = this.fundingPolicy.channel_fee_max_proportional_thousandths || this.fundingPolicy.channel_fee_max_proportional_thousandths === 0 ? this.fundingPolicy.channel_fee_max_proportional_thousandths : null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onUpdateFundingPolicy() {
|
||||
this.flgUpdateCalled = false;
|
||||
this.updateMsg = '';
|
||||
this.dataService.getOrUpdateFunderPolicy(this.selPolicyType.id, this.policyMod, this.leaseFeeBaseSat, this.leaseFeeBasis, this.channelFeeMaxBaseSat * 1000, this.channelFeeMaxProportional).
|
||||
pipe(takeUntil(this.unSubs[3])).
|
||||
subscribe({
|
||||
next: (updatePolicyRes: any) => {
|
||||
this.logger.info(updatePolicyRes);
|
||||
this.fundingPolicy = updatePolicyRes;
|
||||
this.onResetPolicy();
|
||||
this.updateMsg = 'Compact Lease: ' + updatePolicyRes.compact_lease;
|
||||
}, error: (err) => {
|
||||
this.logger.error(err);
|
||||
this.updateMsg = JSON.stringify(err.error.error.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onResetPolicy() {
|
||||
this.flgUpdateCalled = false;
|
||||
this.updateMsg = '';
|
||||
this.selPolicyType = LADS_POLICY[0];
|
||||
this.policyMod = this.fundingPolicy.policy_mod !== 0 && !this.fundingPolicy.policy_mod ? null : this.fundingPolicy.policy_mod;
|
||||
this.leaseFeeBaseSat = this.fundingPolicy.lease_fee_base_msat !== 0 && !this.fundingPolicy.lease_fee_base_msat ? null : this.fundingPolicy.lease_fee_base_msat / 1000;
|
||||
this.leaseFeeBasis = this.fundingPolicy.lease_fee_basis !== 0 && !this.fundingPolicy.lease_fee_basis ? null : this.fundingPolicy.lease_fee_basis;
|
||||
this.channelFeeMaxBaseSat = this.fundingPolicy.channel_fee_max_base_msat !== 0 && !this.fundingPolicy.channel_fee_max_base_msat ? null : this.fundingPolicy.channel_fee_max_base_msat / 1000;
|
||||
this.channelFeeMaxProportional = this.fundingPolicy.channel_fee_max_proportional_thousandths !== 0 && !this.fundingPolicy.channel_fee_max_proportional_thousandths ? null : this.fundingPolicy.channel_fee_max_proportional_thousandths;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.unSubs.forEach((completeSub) => {
|
||||
completeSub.next(null);
|
||||
|
@ -454,3 +454,22 @@ export interface FetchInvoices {
|
||||
index_offset?: number;
|
||||
reversed?: boolean;
|
||||
}
|
||||
|
||||
export interface FunderPolicy {
|
||||
summary?: string;
|
||||
policy?: string;
|
||||
policy_mod?: number;
|
||||
leases_only?: boolean;
|
||||
min_their_funding_msat?: string;
|
||||
max_their_funding_msat?: string;
|
||||
per_channel_min_msat?: string;
|
||||
per_channel_max_msat?: string;
|
||||
reserve_tank_msat?: string;
|
||||
fuzz_percent?: number;
|
||||
fund_probability?: number;
|
||||
lease_fee_base_msat?: number;
|
||||
lease_fee_basis?: number;
|
||||
funding_weight?: number;
|
||||
channel_fee_max_base_msat?: number;
|
||||
channel_fee_max_proportional_thousandths?: number;
|
||||
}
|
||||
|
@ -317,6 +317,7 @@ export const UI_MESSAGES = {
|
||||
DISABLE_OFFER: 'Disabling Offer...',
|
||||
CREATE_OFFER: 'Creating Offer...',
|
||||
DELETE_OFFER_BOOKMARK: 'Deleting Bookmark...',
|
||||
GET_FUNDER_POLICY: 'Getting Or Updating Funder Policy...',
|
||||
LOG_OUT: 'Logging Out...'
|
||||
};
|
||||
|
||||
@ -615,3 +616,9 @@ export enum NodeFeaturesLND {
|
||||
'anchors-zero-fee-htlc-tx' = 'Anchor commitment type with zero fee HTLC transactions',
|
||||
'amp' = 'AMP'
|
||||
};
|
||||
|
||||
export const LADS_POLICY = [
|
||||
{ id: 'match', placeholder: 'Policy Match (%age)', min: 0, max: 200 },
|
||||
{ id: 'available', placeholder: 'Policy Available (%age)', min: 0, max: 100 },
|
||||
{ id: 'fixed', placeholder: 'Fixed Policy (Sats)', min: 0, max: 100 }
|
||||
];
|
||||
|
@ -247,6 +247,21 @@ export class DataService implements OnDestroy {
|
||||
}));
|
||||
}
|
||||
|
||||
getOrUpdateFunderPolicy(policy?: any, policyMod?: any, leaseFeeBaseMsat?: any, leaseFeeBasis?: any, channelFeeMaxBaseMsat?: any, channelFeeMaxProportional?: any) {
|
||||
const postParams = policy ? { policy: policy, policyMod: policyMod, leaseFeeBaseMsat: leaseFeeBaseMsat, leaseFeeBasis: leaseFeeBasis, channelFeeMaxBaseMsat: channelFeeMaxBaseMsat, channelFeeMaxProportional: channelFeeMaxProportional } : null;
|
||||
this.store.dispatch(openSpinner({ payload: UI_MESSAGES.GET_FUNDER_POLICY }));
|
||||
return this.httpClient.post(this.childAPIUrl + environment.CHANNELS_API + '/funderUpdate', postParams).pipe(
|
||||
takeUntil(this.unSubs[8]),
|
||||
map((res) => {
|
||||
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.GET_FUNDER_POLICY }));
|
||||
return res;
|
||||
}), catchError((err) => {
|
||||
this.handleErrorWithoutAlert('Funder Policy', UI_MESSAGES.GET_FUNDER_POLICY, err);
|
||||
return throwError(() => new Error(this.extractErrorMessage(err)));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
extractErrorMessage(err: any, genericErrorMessage: string = 'Unknown Error.') {
|
||||
return this.titleCasePipe.transform((err.error && err.error.error && err.error.error.error && err.error.error.error.error && err.error.error.error.error.error && typeof err.error.error.error.error.error === 'string') ? err.error.error.error.error.error :
|
||||
(err.error && err.error.error && err.error.error.error && err.error.error.error.error && typeof err.error.error.error.error === 'string') ? err.error.error.error.error :
|
||||
|
Loading…
Reference in New Issue
Block a user