Swap In & Swap Out

Swap In & Swap Out
pull/1088/head
ShahanaFarooqui 2 years ago
parent 42425ebe26
commit 04bb6eaade

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -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> <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> <body>
<rtl-app></rtl-app> <rtl-app></rtl-app>
<script src="runtime.84a51fcf32f6aa74.js" type="module"></script><script src="polyfills.eddc63f1737a019a.js" type="module"></script><script src="main.08b18bef419c8b7e.js" type="module"></script> <script src="runtime.6449ae8affb0dea2.js" type="module"></script><script src="polyfills.eddc63f1737a019a.js" type="module"></script><script src="main.b852398d8716a19c.js" type="module"></script>
</body></html> </body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +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],s=!0,d=0;d<t.length;d++)(!1&o||a>=o)&&Object.keys(r.O).every(b=>r.O[b](t[d]))?t.splice(d--,1):(s=!1,o<a&&(a=o));if(s){e.splice(i--,1);var l=f();void 0!==l&&(n=l)}}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+"."+{275:"517caefe8eeb635c",508:"06f7dec065381b97",515:"cb8cf5fc374ab82d",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,s;if(void 0!==o)for(var d=document.getElementsByTagName("script"),l=0;l<d.length;l++){var u=d[l];if(u.getAttribute("src")==t||u.getAttribute("data-webpack")==n+o){a=u;break}}a||(s=!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 c=(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(c.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=c.bind(null,a.onerror),a.onload=c.bind(null,a.onload),s&&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((u,c)=>i=e[f]=[u,c]);o.push(i[2]=a);var s=r.p+r.u(f),d=new Error;r.l(s,u=>{if(r.o(e,f)&&(0!==(i=e[f])&&(e[f]=void 0),i)){var c=u&&("load"===u.type?"missing":u.type),p=u&&u.target&&u.target.src;d.message="Loading chunk "+f+" failed.\n("+c+": "+p+")",d.name="ChunkLoadError",d.type=c,d.request=p,i[1](d)}},"chunk-"+f,f)}else e[f]=0},r.O.j=f=>0===e[f];var n=(f,o)=>{var d,l,[i,a,s]=o,u=0;if(i.some(p=>0!==e[p])){for(d in a)r.o(a,d)&&(r.m[d]=a[d]);if(s)var c=s(r)}for(f&&f(o);u<i.length;u++)r.o(e,l=i[u])&&e[l]&&e[l][0](),e[l]=0;return r.O(c)},t=self.webpackChunkRTLApp=self.webpackChunkRTLApp||[];t.forEach(n.bind(null,0)),t.push=n.bind(null,t.push.bind(t))})()})(); (()=>{"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],s=!0,d=0;d<t.length;d++)(!1&o||a>=o)&&Object.keys(r.O).every(b=>r.O[b](t[d]))?t.splice(d--,1):(s=!1,o<a&&(a=o));if(s){e.splice(i--,1);var l=f();void 0!==l&&(n=l)}}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+"."+{253:"256a01ccdc95a5d7",508:"06f7dec065381b97",515:"da134be35cc26574",924:"e98936d5bf0dd5da"}[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,s;if(void 0!==o)for(var d=document.getElementsByTagName("script"),l=0;l<d.length;l++){var u=d[l];if(u.getAttribute("src")==t||u.getAttribute("data-webpack")==n+o){a=u;break}}a||(s=!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 c=(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(c.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=c.bind(null,a.onerror),a.onload=c.bind(null,a.onload),s&&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((u,c)=>i=e[f]=[u,c]);o.push(i[2]=a);var s=r.p+r.u(f),d=new Error;r.l(s,u=>{if(r.o(e,f)&&(0!==(i=e[f])&&(e[f]=void 0),i)){var c=u&&("load"===u.type?"missing":u.type),p=u&&u.target&&u.target.src;d.message="Loading chunk "+f+" failed.\n("+c+": "+p+")",d.name="ChunkLoadError",d.type=c,d.request=p,i[1](d)}},"chunk-"+f,f)}else e[f]=0},r.O.j=f=>0===e[f];var n=(f,o)=>{var d,l,[i,a,s]=o,u=0;if(i.some(p=>0!==e[p])){for(d in a)r.o(a,d)&&(r.m[d]=a[d]);if(s)var c=s(r)}for(f&&f(o);u<i.length;u++)r.o(e,l=i[u])&&e[l]&&e[l][0](),e[l]=0;return r.O(c)},t=self.webpackChunkRTLApp=self.webpackChunkRTLApp||[];t.forEach(n.bind(null,0)),t.push=n.bind(null,t.push.bind(t))})()})();

@ -64,6 +64,8 @@ import { SwapPeersComponent } from './ln-services/peerswap/swap-peers/swap-peers
import { PeerswapsCancelledComponent } from './ln-services/peerswap/swaps-cancelled/swaps-cancelled.component'; import { PeerswapsCancelledComponent } from './ln-services/peerswap/swaps-cancelled/swaps-cancelled.component';
import { PeerswapsInComponent } from './ln-services/peerswap/swaps-in/swaps-in.component'; import { PeerswapsInComponent } from './ln-services/peerswap/swaps-in/swaps-in.component';
import { PeerswapsOutComponent } from './ln-services/peerswap/swaps-out/swaps-out.component'; import { PeerswapsOutComponent } from './ln-services/peerswap/swaps-out/swaps-out.component';
import { CLNSwapOutModalComponent } from './ln-services/peerswap/swap-out-modal/swap-out-modal.component';
import { CLNSwapInModalComponent } from './ln-services/peerswap/swap-in-modal/swap-in-modal.component';
import { CLNUnlockedGuard } from '../shared/services/auth.guard'; import { CLNUnlockedGuard } from '../shared/services/auth.guard';
@ -133,7 +135,9 @@ import { CLNUnlockedGuard } from '../shared/services/auth.guard';
SwapPeersComponent, SwapPeersComponent,
PeerswapsCancelledComponent, PeerswapsCancelledComponent,
PeerswapsInComponent, PeerswapsInComponent,
PeerswapsOutComponent PeerswapsOutComponent,
CLNSwapOutModalComponent,
CLNSwapInModalComponent
], ],
providers: [ providers: [
CLNUnlockedGuard CLNUnlockedGuard

@ -12,7 +12,7 @@
<div fxLayout="column" fxLayoutAlign="space-between stretch" fxLayoutAlign.gt-sm="space-between center" fxLayout.gt-sm="row wrap"> <div fxLayout="column" fxLayoutAlign="space-between stretch" fxLayoutAlign.gt-sm="space-between center" fxLayout.gt-sm="row wrap">
<mat-form-field fxFlex="30" fxLayoutAlign="start end"> <mat-form-field fxFlex="30" fxLayoutAlign="start end">
<input autoFocus matInput [(ngModel)]="requestedAmount" (keyup)="calculateFee()" placeholder="Requested Amount" type="number" [step]="10000" [min]="0" tabindex="1" required name="ramount" #ramount="ngModel"> <input autoFocus matInput [(ngModel)]="requestedAmount" (keyup)="calculateFee()" placeholder="Requested Amount" type="number" [step]="10000" [min]="0" tabindex="1" required name="ramount" #ramount="ngModel">
<span matSuffix> Sats </span> <span matSuffix class="ml-1"> Sats </span>
<mat-error *ngIf="ramount.errors?.required">Requested amount is required.</mat-error> <mat-error *ngIf="ramount.errors?.required">Requested amount is required.</mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field fxFlex="30" fxLayoutAlign="start end"> <mat-form-field fxFlex="30" fxLayoutAlign="start end">
@ -23,7 +23,7 @@
<mat-form-field fxFlex="30" fxLayoutAlign="start end"> <mat-form-field fxFlex="30" fxLayoutAlign="start end">
<input matInput [(ngModel)]="localAmount" placeholder="Local Amount" type="number" [step]="10000" [min]="20000" [max]="totalBalance" tabindex="3" required name="lamount" #lamount="ngModel"> <input matInput [(ngModel)]="localAmount" placeholder="Local Amount" type="number" [step]="10000" [min]="20000" [max]="totalBalance" tabindex="3" required name="lamount" #lamount="ngModel">
<mat-hint>Remaining Bal: {{totalBalance - ((localAmount) ? localAmount : 0) | number}}</mat-hint> <mat-hint>Remaining Bal: {{totalBalance - ((localAmount) ? localAmount : 0) | number}}</mat-hint>
<span matSuffix> Sats </span> <span matSuffix class="ml-1"> Sats </span>
<mat-error *ngIf="lamount.errors?.required">Local amount is required.</mat-error> <mat-error *ngIf="lamount.errors?.required">Local amount is required.</mat-error>
<mat-error *ngIf="lamount.errors?.min">Local amount must be greater than or equal to 20,000 Sats. It's required to cover the channel force close fee, if needed.</mat-error> <mat-error *ngIf="lamount.errors?.min">Local amount must be greater than or equal to 20,000 Sats. It's required to cover the channel force close fee, if needed.</mat-error>
<mat-error *ngIf="lamount.errors?.max">Local amount must be less than or equal to {{totalBalance}}.</mat-error> <mat-error *ngIf="lamount.errors?.max">Local amount must be less than or equal to {{totalBalance}}.</mat-error>

@ -0,0 +1,36 @@
<div fxLayout="row">
<div fxFlex="100">
<mat-card-header fxLayout="row" fxLayoutAlign="space-between center" class="modal-info-header">
<div fxFlex="95" fxLayoutAlign="start start">
<span class="page-title">Initiate a Swapin</span>
</div>
<button tabindex="5" fxFlex="5" fxLayoutAlign="center" class="btn-close-x p-0" [mat-dialog-close]="false" default
mat-button>X</button>
</mat-card-header>
<mat-card-content class="padding-gap-x-large">
<form fxLayout="row wrap" fxLayoutAlign="start space-between" fxFlex="100" #swapInForm="ngForm">
<p fxLayoutAlign="start center" class="pb-2 word-break">Swapin with {{sPeer?.alias}}</p>
<mat-form-field fxFlex="100" fxLayoutAlign="start end">
<input matInput [value]="sPeer.short_channel_id" placeholder="Short Channel ID" tabindex="1" name="shortChanId" disabled>
</mat-form-field>
<mat-form-field fxFlex="100">
<input matInput autoFocus [(ngModel)]="swapAmount" (keyup)="onAmountChange()" placeholder="Amount"
type="number" [step]="100" [min]="1" [max]="sPeer.remote_balance" tabindex="2" name="swapAmt" #swapAmt="ngModel" required>
<span matSuffix class="ml-1"> Sats </span>
<mat-error *ngIf="swapAmt.errors?.required">Amount is required.</mat-error>
<mat-error *ngIf="swapAmt.errors?.max">Amount must be less than or equal to {{sPeer.remote_balance}}.</mat-error>
<mat-hint>Remaining Local: {{sPeer.remote_balance - ((swapAmount) ? swapAmount : 0) | number}}<br>{{swapAmountHint}}</mat-hint>
</mat-form-field>
<div fxFlex="100" class="alert alert-danger mt-2" *ngIf="swapInError !== ''">
<fa-icon [icon]="faExclamationTriangle" class="mr-1 alert-icon"></fa-icon>
<span *ngIf="swapInError !== ''">{{swapInError}}</span>
</div>
<div fxLayout="row" fxFlex="100" class="mt-1" fxLayoutAlign="end center">
<button class="mr-1" mat-button color="primary" tabindex="3" type="button" (click)="resetData()">Clear
Field</button>
<button mat-button color="primary" (click)="onExecuteSwapin()" tabindex="4">Execute</button>
</div>
</form>
</mat-card-content>
</div>
</div>

@ -0,0 +1,95 @@
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { CommonService } from '../../../../shared/services/common.service';
import { DataService } from '../../../../shared/services/data.service';
import { RootReducer } from '../../../../store/rtl.reducers';
import { LNDReducer } from '../../../../lnd/store/lnd.reducers';
import { CLNReducer } from '../../../store/cln.reducers';
import { ECLReducer } from '../../../../eclair/store/ecl.reducers';
import { mockCLEffects, mockDataService, mockECLEffects, mockLNDEffects, mockMatDialogRef, mockRTLEffects } from '../../../../shared/test-helpers/mock-services';
import { SharedModule } from '../../../../shared/shared.module';
import { CLNSwapInModalComponent } from './swap-in-modal.component';
describe('CLNSwapInModalComponent', () => {
let component: CLNSwapInModalComponent;
let fixture: ComponentFixture<CLNSwapInModalComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [CLNSwapInModalComponent],
imports: [
BrowserAnimationsModule,
SharedModule,
StoreModule.forRoot({ root: RootReducer, lnd: LNDReducer, cln: CLNReducer, ecl: ECLReducer }),
EffectsModule.forRoot([mockRTLEffects, mockLNDEffects, mockCLEffects, mockECLEffects])
],
providers: [
CommonService,
{ provide: DataService, useClass: mockDataService },
{ provide: MatDialogRef, useClass: mockMatDialogRef },
{
provide: MAT_DIALOG_DATA, useValue: {
swapPeer: {
nodeid: '02c9fc0cc737abcff2b502076c43f123451e542e96a79ae177059f3bb796add784',
swaps_allowed: true,
supported_assets: [
'btc'
],
channels: [
{
short_channel_id: '104239x1x1',
local_balance: 9589761,
remote_balance: 410239,
local_percentage: 0.9589761,
state: 'CHANNELD_NORMAL'
},
{
short_channel_id: '104770x6x1',
local_balance: 200000,
remote_balance: 0,
local_percentage: 1,
state: 'CHANNELD_NORMAL'
}
],
sent: {
total_swaps_out: 2,
total_swaps_in: 2,
total_sats_swapped_out: 1110154,
total_sats_swapped_in: 600154
},
received: {
total_swaps_out: 1,
total_swaps_in: 0,
total_sats_swapped_out: 100000,
total_sats_swapped_in: 0
},
total_fee_paid: 478,
alias: 'nodeSignet',
short_channel_id: '104239x1x1',
local_balance: 9589761,
remote_balance: 410239,
local_percentage: 0.9589761,
state: 'CHANNELD_NORMAL'
}
}
}
]
}).
compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CLNSwapInModalComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,91 @@
import { Component, OnInit, OnDestroy, Inject } from '@angular/core';
import { DecimalPipe } from '@angular/common';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { CLNSwapInformation } from '../../../../shared/models/alertData';
import { CurrencyUnitEnum, CURRENCY_UNIT_FORMATS, APICallStatusEnum, CLNActions } from '../../../../shared/services/consts-enums-functions';
import { SwapPeerChannelsFlattened } from '../../../../shared/models/clnModels';
import { CommonService } from '../../../../shared/services/common.service';
import { RTLState } from '../../../../store/rtl.state';
import { SelNodeChild } from '../../../../shared/models/RTLconfig';
import { clnNodeSettings } from '../../../store/cln.selector';
import { swapIn } from '../../../store/cln.actions';
@Component({
selector: 'rtl-swap-in-modal',
templateUrl: './swap-in-modal.component.html',
styleUrls: ['./swap-in-modal.component.scss']
})
export class CLNSwapInModalComponent implements OnInit, OnDestroy {
public faExclamationTriangle = faExclamationTriangle;
public selNode: SelNodeChild | null = {};
public sPeer: SwapPeerChannelsFlattened | null = null;
public swapAmount: number | null;
public swapAmountHint = '';
public swapInError = '';
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject(), new Subject()];
constructor(public dialogRef: MatDialogRef<CLNSwapInModalComponent>, @Inject(MAT_DIALOG_DATA) public data: CLNSwapInformation, private store: Store<RTLState>, private decimalPipe: DecimalPipe, private commonService: CommonService, private actions: Actions) { }
ngOnInit() {
this.sPeer = this.data.swapPeer;
this.store.select(clnNodeSettings).pipe(takeUntil(this.unSubs[0])).subscribe((nodeSettings: SelNodeChild | null) => {
this.selNode = nodeSettings;
});
this.actions.pipe(
takeUntil(this.unSubs[1]),
filter((action) => action.type === CLNActions.UPDATE_API_CALL_STATUS_CLN)).
subscribe((action: any) => {
if (action.type === CLNActions.UPDATE_API_CALL_STATUS_CLN && action.payload.action === 'PeerswapSwapin') {
if (action.payload.status === APICallStatusEnum.ERROR) {
this.swapInError = action.payload.message;
}
if (action.payload.status === APICallStatusEnum.COMPLETED) {
this.dialogRef.close();
}
}
});
}
onExecuteSwapin(): boolean | void {
this.swapInError = '';
if (!this.swapAmount || !this.sPeer || !this.sPeer.short_channel_id) { return true; }
this.store.dispatch(swapIn({ payload: { amountSats: this.swapAmount, shortChannelId: this.sPeer?.short_channel_id, asset: 'btc' } }));
}
resetData() {
this.swapAmount = null;
this.swapAmountHint = '';
this.swapInError = '';
}
onAmountChange() {
if (this.selNode && this.selNode.fiatConversion && this.swapAmount && this.swapAmount > 99) {
this.swapAmountHint = '';
this.commonService.convertCurrency(this.swapAmount, CurrencyUnitEnum.SATS, CurrencyUnitEnum.OTHER, (this.selNode.currencyUnits && this.selNode.currencyUnits.length > 2 ? this.selNode.currencyUnits[2] : ''), this.selNode.fiatConversion).
pipe(takeUntil(this.unSubs[3])).
subscribe({
next: (data) => {
this.swapAmountHint = '= ' + this.decimalPipe.transform(data.OTHER, CURRENCY_UNIT_FORMATS.OTHER) + ' ' + data.symbol;
}, error: (err) => {
this.swapAmountHint = 'Conversion Error: ' + err;
}
});
}
}
ngOnDestroy() {
this.unSubs.forEach((completeSub) => {
completeSub.next(<any>null);
completeSub.complete();
});
}
}

@ -0,0 +1,36 @@
<div fxLayout="row">
<div fxFlex="100">
<mat-card-header fxLayout="row" fxLayoutAlign="space-between center" class="modal-info-header">
<div fxFlex="95" fxLayoutAlign="start start">
<span class="page-title">Initiate a Swapout</span>
</div>
<button tabindex="5" fxFlex="5" fxLayoutAlign="center" class="btn-close-x p-0" [mat-dialog-close]="false" default
mat-button>X</button>
</mat-card-header>
<mat-card-content class="padding-gap-x-large">
<form fxLayout="row wrap" fxLayoutAlign="start space-between" fxFlex="100" #swapOutForm="ngForm">
<p fxLayoutAlign="start center" class="pb-2 word-break">Swapout with {{sPeer?.alias}}</p>
<mat-form-field fxFlex="100" fxLayoutAlign="start end">
<input matInput [value]="sPeer.short_channel_id" placeholder="Short Channel ID" tabindex="1" name="shortChanId" disabled>
</mat-form-field>
<mat-form-field fxFlex="100">
<input matInput autoFocus [(ngModel)]="swapAmount" (keyup)="onAmountChange()" placeholder="Amount"
type="number" [step]="100" [min]="1" [max]="sPeer.local_balance" tabindex="2" name="swapAmt" #swapAmt="ngModel" required>
<span matSuffix class="ml-1"> Sats </span>
<mat-error *ngIf="swapAmt.errors?.required">Amount is required.</mat-error>
<mat-error *ngIf="swapAmt.errors?.max">Amount must be less than or equal to {{sPeer.local_balance}}.</mat-error>
<mat-hint>Remaining Local: {{sPeer.local_balance - ((swapAmount) ? swapAmount : 0) | number}}<br>{{swapAmountHint}}</mat-hint>
</mat-form-field>
<div fxFlex="100" class="alert alert-danger mt-2" *ngIf="swapOutError !== ''">
<fa-icon [icon]="faExclamationTriangle" class="mr-1 alert-icon"></fa-icon>
<span *ngIf="swapOutError !== ''">{{swapOutError}}</span>
</div>
<div fxLayout="row" fxFlex="100" class="mt-1" fxLayoutAlign="end center">
<button class="mr-1" mat-button color="primary" tabindex="3" type="button" (click)="resetData()">Clear
Field</button>
<button mat-button color="primary" (click)="onExecuteSwapout()" tabindex="4">Execute</button>
</div>
</form>
</mat-card-content>
</div>
</div>

@ -0,0 +1,95 @@
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { CommonService } from '../../../../shared/services/common.service';
import { DataService } from '../../../../shared/services/data.service';
import { RootReducer } from '../../../../store/rtl.reducers';
import { LNDReducer } from '../../../../lnd/store/lnd.reducers';
import { CLNReducer } from '../../../../cln/store/cln.reducers';
import { ECLReducer } from '../../../../eclair/store/ecl.reducers';
import { mockCLEffects, mockDataService, mockECLEffects, mockLNDEffects, mockMatDialogRef, mockRTLEffects } from '../../../../shared/test-helpers/mock-services';
import { SharedModule } from '../../../../shared/shared.module';
import { CLNSwapOutModalComponent } from './swap-out-modal.component';
describe('CLNSwapOutModalComponent', () => {
let component: CLNSwapOutModalComponent;
let fixture: ComponentFixture<CLNSwapOutModalComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [CLNSwapOutModalComponent],
imports: [
BrowserAnimationsModule,
SharedModule,
StoreModule.forRoot({ root: RootReducer, lnd: LNDReducer, cln: CLNReducer, ecl: ECLReducer }),
EffectsModule.forRoot([mockRTLEffects, mockLNDEffects, mockCLEffects, mockECLEffects])
],
providers: [
CommonService,
{ provide: DataService, useClass: mockDataService },
{ provide: MatDialogRef, useClass: mockMatDialogRef },
{
provide: MAT_DIALOG_DATA, useValue: {
swapPeer: {
nodeid: '02c9fc0cc737abcff2b502076c43f123451e542e96a79ae177059f3bb796add784',
swaps_allowed: true,
supported_assets: [
'btc'
],
channels: [
{
short_channel_id: '104239x1x1',
local_balance: 9589761,
remote_balance: 410239,
local_percentage: 0.9589761,
state: 'CHANNELD_NORMAL'
},
{
short_channel_id: '104770x6x1',
local_balance: 200000,
remote_balance: 0,
local_percentage: 1,
state: 'CHANNELD_NORMAL'
}
],
sent: {
total_swaps_out: 2,
total_swaps_in: 2,
total_sats_swapped_out: 1110154,
total_sats_swapped_in: 600154
},
received: {
total_swaps_out: 1,
total_swaps_in: 0,
total_sats_swapped_out: 100000,
total_sats_swapped_in: 0
},
total_fee_paid: 478,
alias: 'nodeSignet',
short_channel_id: '104239x1x1',
local_balance: 9589761,
remote_balance: 410239,
local_percentage: 0.9589761,
state: 'CHANNELD_NORMAL'
}
}
}
]
}).
compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CLNSwapOutModalComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,91 @@
import { Component, OnInit, OnDestroy, Inject } from '@angular/core';
import { DecimalPipe } from '@angular/common';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { CLNSwapInformation } from '../../../../shared/models/alertData';
import { CurrencyUnitEnum, CURRENCY_UNIT_FORMATS, APICallStatusEnum, CLNActions } from '../../../../shared/services/consts-enums-functions';
import { SwapPeerChannelsFlattened } from '../../../../shared/models/clnModels';
import { CommonService } from '../../../../shared/services/common.service';
import { RTLState } from '../../../../store/rtl.state';
import { SelNodeChild } from '../../../../shared/models/RTLconfig';
import { clnNodeSettings } from '../../../store/cln.selector';
import { swapOut } from '../../../store/cln.actions';
@Component({
selector: 'rtl-swap-out-modal',
templateUrl: './swap-out-modal.component.html',
styleUrls: ['./swap-out-modal.component.scss']
})
export class CLNSwapOutModalComponent implements OnInit, OnDestroy {
public faExclamationTriangle = faExclamationTriangle;
public selNode: SelNodeChild | null = {};
public sPeer: SwapPeerChannelsFlattened | null = null;
public swapAmount: number | null;
public swapAmountHint = '';
public swapOutError = '';
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject(), new Subject()];
constructor(public dialogRef: MatDialogRef<CLNSwapOutModalComponent>, @Inject(MAT_DIALOG_DATA) public data: CLNSwapInformation, private store: Store<RTLState>, private decimalPipe: DecimalPipe, private commonService: CommonService, private actions: Actions) { }
ngOnInit() {
this.sPeer = this.data.swapPeer;
this.store.select(clnNodeSettings).pipe(takeUntil(this.unSubs[0])).subscribe((nodeSettings: SelNodeChild | null) => {
this.selNode = nodeSettings;
});
this.actions.pipe(
takeUntil(this.unSubs[1]),
filter((action) => action.type === CLNActions.UPDATE_API_CALL_STATUS_CLN)).
subscribe((action: any) => {
if (action.type === CLNActions.UPDATE_API_CALL_STATUS_CLN && action.payload.action === 'PeerswapSwapout') {
if (action.payload.status === APICallStatusEnum.ERROR) {
this.swapOutError = action.payload.message;
}
if (action.payload.status === APICallStatusEnum.COMPLETED) {
this.dialogRef.close();
}
}
});
}
onExecuteSwapout(): boolean | void {
this.swapOutError = '';
if (!this.swapAmount || !this.sPeer || !this.sPeer.short_channel_id) { return true; }
this.store.dispatch(swapOut({ payload: { amountSats: this.swapAmount, shortChannelId: this.sPeer?.short_channel_id, asset: 'btc' } }));
}
resetData() {
this.swapAmount = null;
this.swapAmountHint = '';
this.swapOutError = '';
}
onAmountChange() {
if (this.selNode && this.selNode.fiatConversion && this.swapAmount && this.swapAmount > 99) {
this.swapAmountHint = '';
this.commonService.convertCurrency(this.swapAmount, CurrencyUnitEnum.SATS, CurrencyUnitEnum.OTHER, (this.selNode.currencyUnits && this.selNode.currencyUnits.length > 2 ? this.selNode.currencyUnits[2] : ''), this.selNode.fiatConversion).
pipe(takeUntil(this.unSubs[3])).
subscribe({
next: (data) => {
this.swapAmountHint = '= ' + this.decimalPipe.transform(data.OTHER, CURRENCY_UNIT_FORMATS.OTHER) + ' ' + data.symbol;
}, error: (err) => {
this.swapAmountHint = 'Conversion Error: ' + err;
}
});
}
}
ngOnDestroy() {
this.unSubs.forEach((completeSub) => {
completeSub.next(<any>null);
completeSub.complete();
});
}
}

@ -1,16 +1,19 @@
<div fxLayout="column" fxLayoutAlign="start stretch" class="padding-gap-x"> <div fxLayout="column" fxLayoutAlign="start stretch" class="padding-gap-x">
<div class="p-2 error-border my-2" *ngIf="errorMessage !== ''">{{errorMessage}}</div> <div fxLayout="row wrap" fxLayoutAlign="start start" fxLayout.gt-sm="column" fxFlex="100" fxLayoutAlign.gt-sm="start stretch">
<div *ngIf="errorMessage === ''" fxLayout.gt-xs="column" fxLayout="row" fxLayoutAlign="start center" fxLayoutAlign.gt-xs="start stretch" class="page-sub-title-container"> <div fxFlex="100" class="page-sub-title-container padding-gap-large pb-0">
<div fxFlex="100"> <fa-icon [icon]="faPeopleGroup" class="page-title-img mr-1"></fa-icon>
<span class="page-title">Number of peers enabled with Peerswap: {{totalSwapPeers}}</span>
</div>
<div fxLayout="column" fxLayout.gt-xs="row wrap" fxLayoutAlign.gt-xs="start center" fxLayoutAlign="start stretch" class="page-sub-title-container">
<div fxFlex="70"></div> <div fxFlex="70"></div>
<mat-form-field fxFlex="30"> <mat-form-field fxFlex="30">
<input matInput (keyup)="applyFilter()" [(ngModel)]="selFilter" placeholder="Filter"> <input matInput (keyup)="applyFilter()" [(ngModel)]="selFilter" placeholder="Filter">
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
<div *ngIf="errorMessage === ''" [perfectScrollbar] fxLayout="column" fxLayoutAlign="start center" fxFlex="100" class="table-container"> <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> <mat-progress-bar *ngIf="apiCallStatus?.status === apiCallStatusEnum.INITIATED" mode="indeterminate"></mat-progress-bar>
<table mat-table #table [dataSource]="swapPeers" fxFlex="100" matSort class="overflow-auto"> <table mat-table #table [dataSource]="swapPeers" fxFlex="100" matSort [ngClass]="{'error-border': errorMessage !== ''}" class="overflow-auto">
<ng-container matColumnDef="short_channel_id"> <ng-container matColumnDef="short_channel_id">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Short Channel ID</th> <th mat-header-cell *matHeaderCellDef mat-sort-header>Short Channel ID</th>
<td mat-cell *matCellDef="let sPeer">{{sPeer?.short_channel_id}}</td> <td mat-cell *matCellDef="let sPeer">{{sPeer?.short_channel_id}}</td>

@ -7,9 +7,10 @@ import { Store } from '@ngrx/store';
import { MatPaginator, MatPaginatorIntl } from '@angular/material/paginator'; import { MatPaginator, MatPaginatorIntl } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort'; import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table';
import { faPeopleGroup } from '@fortawesome/free-solid-svg-icons';
import { SwapPeerChannelsFlattened } from '../../../../shared/models/clnModels'; import { SwapPeerChannelsFlattened } from '../../../../shared/models/clnModels';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS, getPaginatorLabel, ScreenSizeEnum, APICallStatusEnum } from '../../../../shared/services/consts-enums-functions'; import { PAGE_SIZE, PAGE_SIZE_OPTIONS, getPaginatorLabel, ScreenSizeEnum, APICallStatusEnum, DataTypeEnum, AlertTypeEnum } from '../../../../shared/services/consts-enums-functions';
import { ApiCallStatusPayload } from '../../../../shared/models/apiCallsPayload'; import { ApiCallStatusPayload } from '../../../../shared/models/apiCallsPayload';
import { LoggerService } from '../../../../shared/services/logger.service'; import { LoggerService } from '../../../../shared/services/logger.service';
import { CommonService } from '../../../../shared/services/common.service'; import { CommonService } from '../../../../shared/services/common.service';
@ -18,6 +19,8 @@ import { RTLState } from '../../../../store/rtl.state';
import { openAlert } from '../../../../store/rtl.actions'; import { openAlert } from '../../../../store/rtl.actions';
import { fetchSwapPeers } from '../../../store/cln.actions'; import { fetchSwapPeers } from '../../../store/cln.actions';
import { swapPeers } from '../../../store/cln.selector'; import { swapPeers } from '../../../store/cln.selector';
import { CLNSwapOutModalComponent } from '../swap-out-modal/swap-out-modal.component';
import { CLNSwapInModalComponent } from '../swap-in-modal/swap-in-modal.component';
@Component({ @Component({
selector: 'rtl-peerswap-peers', selector: 'rtl-peerswap-peers',
@ -31,7 +34,9 @@ export class SwapPeersComponent implements OnInit, OnDestroy {
@ViewChild(MatSort, { static: false }) sort: MatSort | undefined; @ViewChild(MatSort, { static: false }) sort: MatSort | undefined;
@ViewChild(MatPaginator, { static: false }) paginator: MatPaginator | undefined; @ViewChild(MatPaginator, { static: false }) paginator: MatPaginator | undefined;
public faPeopleGroup = faPeopleGroup;
public displayedColumns: any[] = []; public displayedColumns: any[] = [];
public totalSwapPeers = 0;
public peersData: SwapPeerChannelsFlattened[] = []; public peersData: SwapPeerChannelsFlattened[] = [];
public swapPeers: any; public swapPeers: any;
public flgSticky = false; public flgSticky = false;
@ -67,12 +72,13 @@ export class SwapPeersComponent implements OnInit, OnDestroy {
this.router.onSameUrlNavigation = 'reload'; this.router.onSameUrlNavigation = 'reload';
this.store.dispatch(fetchSwapPeers()); this.store.dispatch(fetchSwapPeers());
this.store.select(swapPeers).pipe(takeUntil(this.unSubs[0])). this.store.select(swapPeers).pipe(takeUntil(this.unSubs[0])).
subscribe((spSeletor: { swapPeers: SwapPeerChannelsFlattened[], apiCallStatus: ApiCallStatusPayload }) => { subscribe((spSeletor: { totalSwapPeers: number, swapPeers: SwapPeerChannelsFlattened[], apiCallStatus: ApiCallStatusPayload }) => {
this.errorMessage = ''; this.errorMessage = '';
this.apiCallStatus = spSeletor.apiCallStatus; this.apiCallStatus = spSeletor.apiCallStatus;
if (this.apiCallStatus.status === APICallStatusEnum.ERROR) { if (this.apiCallStatus.status === APICallStatusEnum.ERROR) {
this.errorMessage = !this.apiCallStatus.message ? '' : (typeof (this.apiCallStatus.message) === 'object') ? JSON.stringify(this.apiCallStatus.message) : this.apiCallStatus.message; this.errorMessage = !this.apiCallStatus.message ? '' : (typeof (this.apiCallStatus.message) === 'object') ? JSON.stringify(this.apiCallStatus.message) : this.apiCallStatus.message;
} }
this.totalSwapPeers = spSeletor.totalSwapPeers;
this.peersData = spSeletor.swapPeers || []; this.peersData = spSeletor.swapPeers || [];
if (this.peersData.length > 0 && this.sort && this.paginator) { if (this.peersData.length > 0 && this.sort && this.paginator) {
this.loadSwapPeersTable(this.peersData); this.loadSwapPeersTable(this.peersData);
@ -83,27 +89,55 @@ export class SwapPeersComponent implements OnInit, OnDestroy {
onSwapPeerClick(selSPeer: SwapPeerChannelsFlattened) { onSwapPeerClick(selSPeer: SwapPeerChannelsFlattened) {
this.logger.warn(selSPeer); this.logger.warn(selSPeer);
// nodeid, alias, swaps_allowed, supported_assets[], total_fee_paid const reorderedSPeer = [
// channels: {short_channel_id, local_balance, remote_balance, local_percentage, state}[] [{ key: 'nodeid', value: selSPeer.nodeid, title: 'Node Id', width: 100, type: DataTypeEnum.STRING }],
// sent: {total_swaps_out, total_swaps_in, total_sats_swapped_out, total_sats_swapped_in} [{ key: 'alias', value: selSPeer.alias, title: 'Alias', width: 50, type: DataTypeEnum.STRING },
// received: {total_swaps_out, total_swaps_in, total_sats_swapped_out, total_sats_swapped_in} { key: 'short_channel_id', value: selSPeer.short_channel_id, title: 'Short Channel ID', width: 50, type: DataTypeEnum.STRING }],
[{ key: 'local_balance', value: selSPeer.local_balance, title: 'Local Balance (Sats)', width: 50, type: DataTypeEnum.NUMBER },
{ key: 'remote_balance', value: selSPeer.remote_balance, title: 'Remote Balance (Sats)', width: 50, type: DataTypeEnum.NUMBER }],
[{ key: 'total_fee_paid', value: selSPeer.total_fee_paid, title: 'Total Fee Paid (Sats)', width: 40, type: DataTypeEnum.NUMBER },
{ key: 'swaps_allowed', value: selSPeer.swaps_allowed ? 'Allowed' : 'Denied', title: 'Swaps Allowed', width: 30, type: DataTypeEnum.STRING },
{ key: 'total_channels', value: selSPeer.channels?.length, title: 'Channels Opened', width: 30, type: DataTypeEnum.NUMBER }],
[{ key: 'sent_total_swaps_out', value: selSPeer.sent?.total_swaps_out, title: 'Swap Out Sent', width: 25, type: DataTypeEnum.NUMBER },
{ key: 'sent_total_swaps_in', value: selSPeer.sent?.total_swaps_in, title: 'Swap In Sent', width: 25, type: DataTypeEnum.NUMBER },
{ key: 'sent_total_sats_swapped_out', value: selSPeer.sent?.total_sats_swapped_out, title: 'Swapped Out Sent (Sats)', width: 25, type: DataTypeEnum.NUMBER },
{ key: 'sent_total_sats_swapped_in', value: selSPeer.sent?.total_sats_swapped_in, title: 'Swapped In Sent (Sats)', width: 25, type: DataTypeEnum.NUMBER }],
[{ key: 'received_total_swaps_out', value: selSPeer.received?.total_swaps_out, title: 'Swap Out Received', width: 25, type: DataTypeEnum.NUMBER },
{ key: 'received_total_swaps_in', value: selSPeer.received?.total_swaps_in, title: 'Swap In Received', width: 25, type: DataTypeEnum.NUMBER },
{ key: 'received_total_sats_swapped_out', value: selSPeer.received?.total_sats_swapped_out, title: 'Swapped Out Received(Sats)', width: 25, type: DataTypeEnum.NUMBER },
{ key: 'received_total_sats_swapped_in', value: selSPeer.received?.total_sats_swapped_in, title: 'Swapped In Received (Sats)', width: 25, type: DataTypeEnum.NUMBER }]
];
this.store.dispatch(openAlert({ this.store.dispatch(openAlert({
payload: { payload: {
data: { data: {
// invoice: reCreatedInvoice, type: AlertTypeEnum.INFORMATION,
// newlyAdded: false, alertTitle: 'Swap Peer Information',
// component: CLNInvoiceInformationComponent message: reorderedSPeer
} }
} }
})); }));
} }
onSwapOut(sPeer) { onSwapOut(sPeer: SwapPeerChannelsFlattened) {
this.logger.warn('Swap Out'); this.store.dispatch(openAlert({
payload: {
data: {
swapPeer: sPeer,
component: CLNSwapOutModalComponent
}
}
}));
} }
onSwapIn(sPeer) { onSwapIn(sPeer: SwapPeerChannelsFlattened) {
this.logger.warn('Swap In'); this.store.dispatch(openAlert({
payload: {
data: {
swapPeer: sPeer,
component: CLNSwapInModalComponent
}
}
}));
} }
loadSwapPeersTable(swapPeers: SwapPeerChannelsFlattened[]) { loadSwapPeersTable(swapPeers: SwapPeerChannelsFlattened[]) {

@ -24,7 +24,7 @@
<mat-form-field fxFlex="70" fxLayoutAlign="start end"> <mat-form-field fxFlex="70" fxLayoutAlign="start end">
<input matInput [(ngModel)]="fundingAmount" placeholder="Amount" type="number" [step]="1000" [min]="1" [max]="totalBalance" tabindex="1" required name="amount" #amount="ngModel" [disabled]="flgUseAllBalance"> <input matInput [(ngModel)]="fundingAmount" placeholder="Amount" type="number" [step]="1000" [min]="1" [max]="totalBalance" tabindex="1" required name="amount" #amount="ngModel" [disabled]="flgUseAllBalance">
<mat-hint>Remaining Bal: {{totalBalance - ((fundingAmount) ? fundingAmount : 0) | number}}{{flgUseAllBalance ? '. Amount replaced by UTXO balance' : ''}}</mat-hint> <mat-hint>Remaining Bal: {{totalBalance - ((fundingAmount) ? fundingAmount : 0) | number}}{{flgUseAllBalance ? '. Amount replaced by UTXO balance' : ''}}</mat-hint>
<span matSuffix> Sats </span> <span matSuffix class="ml-1"> Sats </span>
<mat-error *ngIf="amount.errors?.required || !fundingAmount">Amount is required.</mat-error> <mat-error *ngIf="amount.errors?.required || !fundingAmount">Amount is required.</mat-error>
<mat-error *ngIf="amount.errors?.max">Amount must be less than or equal to {{totalBalance}}.</mat-error> <mat-error *ngIf="amount.errors?.max">Amount must be less than or equal to {{totalBalance}}.</mat-error>
</mat-form-field> </mat-form-field>

@ -33,7 +33,7 @@
<mat-form-field fxFlex="60" fxLayoutAlign="start end"> <mat-form-field fxFlex="60" fxLayoutAlign="start end">
<input matInput autoFocus formControlName="fundingAmount" placeholder="Amount" type="number" [step]="1000" tabindex="1" required> <input matInput autoFocus formControlName="fundingAmount" placeholder="Amount" type="number" [step]="1000" tabindex="1" required>
<mat-hint>Remaining Bal: {{totalBalance - ((channelFormGroup.controls.fundingAmount.value) ? channelFormGroup.controls.fundingAmount.value : 0)}}</mat-hint> <mat-hint>Remaining Bal: {{totalBalance - ((channelFormGroup.controls.fundingAmount.value) ? channelFormGroup.controls.fundingAmount.value : 0)}}</mat-hint>
<span matSuffix> Sats </span> <span matSuffix class="ml-1"> Sats </span>
<mat-error *ngIf="channelFormGroup.controls.fundingAmount.errors?.required">Amount is required.</mat-error> <mat-error *ngIf="channelFormGroup.controls.fundingAmount.errors?.required">Amount is required.</mat-error>
<mat-error *ngIf="channelFormGroup.controls.fundingAmount.errors?.min">Amount must be a positive number.</mat-error> <mat-error *ngIf="channelFormGroup.controls.fundingAmount.errors?.min">Amount must be a positive number.</mat-error>
<mat-error *ngIf="channelFormGroup.controls.fundingAmount.errors?.max">Amount must be less than or equal to {{totalBalance}}.</mat-error> <mat-error *ngIf="channelFormGroup.controls.fundingAmount.errors?.max">Amount must be less than or equal to {{totalBalance}}.</mat-error>

@ -142,3 +142,11 @@ export const setSwapPeers = createAction(CLNActions.SET_SWAP_PEERS_CLN, props<{
export const fetchSwapRequests = createAction(CLNActions.FETCH_SWAP_REQUESTS_CLN); export const fetchSwapRequests = createAction(CLNActions.FETCH_SWAP_REQUESTS_CLN);
export const setSwapRequests = createAction(CLNActions.SET_SWAP_REQUESTS_CLN, props<{ payload: SwapRequest[] }>()); export const setSwapRequests = createAction(CLNActions.SET_SWAP_REQUESTS_CLN, props<{ payload: SwapRequest[] }>());
export const swapOut = createAction(CLNActions.SWAPOUT_CLN, props<{ payload: { amountSats: number, shortChannelId: string, asset: string } }>());
export const addSwapout = createAction(CLNActions.ADD_SWAPOUT_CLN, props<{ payload: Swap }>());
export const swapIn = createAction(CLNActions.SWAPIN_CLN, props<{ payload: { amountSats: number, shortChannelId: string, asset: string } }>());
export const addSwapin = createAction(CLNActions.ADD_SWAPIN_CLN, props<{ payload: Swap }>());

@ -14,8 +14,8 @@ import { SessionService } from '../../shared/services/session.service';
import { WebSocketClientService } from '../../shared/services/web-socket.service'; import { WebSocketClientService } from '../../shared/services/web-socket.service';
import { ErrorMessageComponent } from '../../shared/components/data-modal/error-message/error-message.component'; import { ErrorMessageComponent } from '../../shared/components/data-modal/error-message/error-message.component';
import { CLNInvoiceInformationComponent } from '../transactions/invoices/invoice-information-modal/invoice-information.component'; import { CLNInvoiceInformationComponent } from '../transactions/invoices/invoice-information-modal/invoice-information.component';
import { GetInfo, Fees, Balance, LocalRemoteBalance, Payment, FeeRates, ListInvoices, Invoice, Peer, OnChain, QueryRoutes, SaveChannel, GetNewAddress, DetachPeer, UpdateChannel, CloseChannel, SendPayment, GetQueryRoutes, ChannelLookup, FetchInvoices, Channel, OfferInvoice, Offer, ListForwards, FetchListForwards, ForwardingEvent, LocalFailedEvent } from '../../shared/models/clnModels'; import { GetInfo, Fees, Balance, LocalRemoteBalance, Payment, FeeRates, ListInvoices, Invoice, Peer, OnChain, QueryRoutes, SaveChannel, GetNewAddress, DetachPeer, UpdateChannel, CloseChannel, SendPayment, GetQueryRoutes, ChannelLookup, FetchInvoices, Channel, OfferInvoice, Offer, ListForwards, FetchListForwards, ForwardingEvent, LocalFailedEvent, Swap } from '../../shared/models/clnModels';
import { AlertTypeEnum, APICallStatusEnum, UI_MESSAGES, CLNWSEventTypeEnum, CLNActions, RTLActions, CLNForwardingEventsStatusEnum } from '../../shared/services/consts-enums-functions'; import { AlertTypeEnum, APICallStatusEnum, UI_MESSAGES, CLNWSEventTypeEnum, CLNActions, RTLActions, CLNForwardingEventsStatusEnum, DataTypeEnum } from '../../shared/services/consts-enums-functions';
import { closeAllDialogs, closeSpinner, logout, openAlert, openSnackBar, openSpinner, setApiUrl, setNodeData } from '../../store/rtl.actions'; import { closeAllDialogs, closeSpinner, logout, openAlert, openSnackBar, openSpinner, setApiUrl, setNodeData } from '../../store/rtl.actions';
import { RTLState } from '../../store/rtl.state'; import { RTLState } from '../../store/rtl.state';
@ -966,7 +966,7 @@ export class CLNEffects implements OnDestroy {
payload: res || [] payload: res || []
}; };
}), catchError((err: any) => { }), catchError((err: any) => {
this.handleErrorWithoutAlert('FetchSwaps', UI_MESSAGES.NO_SPINNER, 'Fetching Swap Peers Failed.', err); this.handleErrorWithoutAlert('FetchSwapPeers', UI_MESSAGES.NO_SPINNER, 'Fetching Swap Peers Failed.', err);
return of({ type: RTLActions.VOID }); return of({ type: RTLActions.VOID });
})); }));
}) })
@ -991,6 +991,74 @@ export class CLNEffects implements OnDestroy {
}) })
)); ));
peerswapOutCL = createEffect(() => this.actions.pipe(
ofType(CLNActions.SWAPOUT_CLN),
mergeMap((action: { type: string, payload: { amountSats: number, shortChannelId: string, asset: string } }) => {
this.store.dispatch(openSpinner({ payload: UI_MESSAGES.PEERSWAP_SWAPOUT }));
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'PeerswapSwapout', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.post(this.CHILD_API_URL + environment.PEERSWAP_API + '/swapOut', {
amountSats: action.payload.amountSats, shortChannelId: action.payload.shortChannelId, asset: action.payload.asset
}).pipe(map((postRes: Swap) => {
this.logger.info(postRes);
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.PEERSWAP_SWAPOUT }));
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'PeerswapSwapout', status: APICallStatusEnum.COMPLETED } }));
setTimeout(() => {
this.store.dispatch(openAlert({
payload: {
data: {
type: AlertTypeEnum.INFORMATION,
alertTitle: 'Swapout Initiated',
message: postRes.id
}
}
}));
}, 100);
return {
type: CLNActions.ADD_SWAPOUT_CLN,
message: [{ key: 'id', value: postRes.id, title: 'Swap Id', width: 100, type: DataTypeEnum.STRING }]
};
}), catchError((err: any) => {
this.handleErrorWithoutAlert('PeerswapSwapout', UI_MESSAGES.PEERSWAP_SWAPOUT, 'Swapout Failed.', err);
return of({ type: RTLActions.VOID });
})
);
})
));
peerswapInCL = createEffect(() => this.actions.pipe(
ofType(CLNActions.SWAPIN_CLN),
mergeMap((action: { type: string, payload: { amountSats: number, shortChannelId: string, asset: string } }) => {
this.store.dispatch(openSpinner({ payload: UI_MESSAGES.PEERSWAP_SWAPIN }));
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'PeerswapSwapin', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.post(this.CHILD_API_URL + environment.PEERSWAP_API + '/swapIn', {
amountSats: action.payload.amountSats, shortChannelId: action.payload.shortChannelId, asset: action.payload.asset
}).pipe(map((postRes: Swap) => {
this.logger.info(postRes);
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.PEERSWAP_SWAPIN }));
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'PeerswapSwapin', status: APICallStatusEnum.COMPLETED } }));
setTimeout(() => {
this.store.dispatch(openAlert({
payload: {
data: {
type: AlertTypeEnum.INFORMATION,
alertTitle: 'Swapin Initiated',
message: [{ key: 'id', value: postRes.id, title: 'Swap Id', width: 100, type: DataTypeEnum.STRING }]
}
}
}));
}, 100);
return {
type: CLNActions.ADD_SWAPIN_CLN,
payload: postRes
};
}), catchError((err: any) => {
this.handleErrorWithoutAlert('PeerswapSwapin', UI_MESSAGES.PEERSWAP_SWAPIN, 'Swapin Failed.', err);
return of({ type: RTLActions.VOID });
})
);
})
));
initializeRemainingData(info: any, landingPage: string) { initializeRemainingData(info: any, landingPage: string) {
this.sessionService.setItem('clUnlocked', 'true'); this.sessionService.setItem('clUnlocked', 'true');
const node_data = { const node_data = {

@ -5,10 +5,10 @@ import {
setChildNodeSettingsCL, setFeeRates, setFees, setForwardingHistory, setChildNodeSettingsCL, setFeeRates, setFees, setForwardingHistory,
setInfo, setInvoices, setLocalRemoteBalance, setOffers, addOffer, setPayments, setPeers, setUTXOs, setInfo, setInvoices, setLocalRemoteBalance, setOffers, addOffer, setPayments, setPeers, setUTXOs,
updateCLAPICallStatus, updateInvoice, updateOffer, setOfferBookmarks, addUpdateOfferBookmark, removeOfferBookmark, updateCLAPICallStatus, updateInvoice, updateOffer, setOfferBookmarks, addUpdateOfferBookmark, removeOfferBookmark,
setSwaps, setSwapPeers, setSwapRequests setSwaps, setSwapPeers, setSwapRequests, addSwapout, addSwapin
} from './cln.actions'; } from './cln.actions';
import { Channel, OfferBookmark, SwapPeerChannelsFlattened } from '../../shared/models/clnModels'; import { Channel, OfferBookmark, Swap } from '../../shared/models/clnModels';
import { CLNForwardingEventsStatusEnum } from '../../shared/services/consts-enums-functions'; import { CLNForwardingEventsStatusEnum, PeerswapTypes } from '../../shared/services/consts-enums-functions';
export const CLNReducer = createReducer(initCLNState, export const CLNReducer = createReducer(initCLNState,
on(updateCLAPICallStatus, (state, { payload }) => { on(updateCLAPICallStatus, (state, { payload }) => {
@ -218,10 +218,22 @@ export const CLNReducer = createReducer(initCLNState,
offersBookmarks: modifiedOfferBookmarks offersBookmarks: modifiedOfferBookmarks
}; };
}), }),
on(setSwaps, (state, { payload }) => ({ on(setSwaps, (state, { payload }) => {
...state, const swapOutArr: Swap[] = [];
swaps: payload const swapInArr: Swap[] = [];
})), payload.forEach((swap) => {
if (swap.type === PeerswapTypes.SWAP_OUT) {
swapOutArr.push(swap);
} else {
swapInArr.push(swap);
}
});
return {
...state,
swapOuts: swapOutArr,
swapIns: swapInArr
};
}),
on(setSwapRequests, (state, { payload }) => ({ on(setSwapRequests, (state, { payload }) => ({
...state, ...state,
swapRequests: payload swapRequests: payload
@ -235,8 +247,25 @@ export const CLNReducer = createReducer(initCLNState,
}, <any[]>[]); }, <any[]>[]);
return { return {
...state, ...state,
totalSwapPeers: payload.length || 0,
swapPeers: flattenedSwapPeers swapPeers: flattenedSwapPeers
}; };
}),
on(addSwapout, (state, { payload }) => {
const newSwapOuts = state.swapOuts;
newSwapOuts?.unshift(payload);
return {
...state,
swapOuts: newSwapOuts
};
}),
on(addSwapin, (state, { payload }) => {
const newSwapIns = state.swapIns;
newSwapIns?.unshift(payload);
return {
...state,
swapIns: newSwapIns
};
}) })
); );

@ -27,6 +27,7 @@ export const nodeInfoAndNodeSettingsAndAPIsStatus = createSelector(clnState, (st
export const offers = createSelector(clnState, (state: CLNState) => ({ offers: state.offers, apiCallStatus: state.apisCallStatus.FetchOffers })); export const offers = createSelector(clnState, (state: CLNState) => ({ offers: state.offers, apiCallStatus: state.apisCallStatus.FetchOffers }));
export const offerBookmarks = createSelector(clnState, (state: CLNState) => ({ offersBookmarks: state.offersBookmarks, apiCallStatus: state.apisCallStatus.FetchOfferBookmarks })); export const offerBookmarks = createSelector(clnState, (state: CLNState) => ({ offersBookmarks: state.offersBookmarks, apiCallStatus: state.apisCallStatus.FetchOfferBookmarks }));
export const getoffer = (bolt12Str) => createSelector(clnState, (state: CLNState) => (state.offers.find((offer: Offer) => offer.bolt12 === bolt12Str))); export const getoffer = (bolt12Str) => createSelector(clnState, (state: CLNState) => (state.offers.find((offer: Offer) => offer.bolt12 === bolt12Str)));
export const swaps = createSelector(clnState, (state: CLNState) => ({ swaps: state.swaps, apiCallStatus: state.apisCallStatus.FetchSwaps })); export const swapOuts = createSelector(clnState, (state: CLNState) => ({ swapOuts: state.swapOuts, apiCallStatus: state.apisCallStatus.FetchSwaps }));
export const swapPeers = createSelector(clnState, (state: CLNState) => ({ swapPeers: state.swapPeers, apiCallStatus: state.apisCallStatus.FetchSwapPeers })); export const swapIns = createSelector(clnState, (state: CLNState) => ({ swapIns: state.swapIns, apiCallStatus: state.apisCallStatus.FetchSwaps }));
export const swapPeers = createSelector(clnState, (state: CLNState) => ({ totalSwapPeers: state.totalSwapPeers, swapPeers: state.swapPeers, apiCallStatus: state.apisCallStatus.FetchSwapPeers }));
export const swapRequests = createSelector(clnState, (state: CLNState) => ({ swapRequests: state.swapRequests, apiCallStatus: state.apisCallStatus.FetchSwapRequests })); export const swapRequests = createSelector(clnState, (state: CLNState) => ({ swapRequests: state.swapRequests, apiCallStatus: state.apisCallStatus.FetchSwapRequests }));

@ -24,8 +24,10 @@ export interface CLNState {
utxos: UTXO[]; utxos: UTXO[];
offers: Offer[]; offers: Offer[];
offersBookmarks: OfferBookmark[]; offersBookmarks: OfferBookmark[];
totalSwapPeers: number;
swapPeers: SwapPeerChannelsFlattened[]; swapPeers: SwapPeerChannelsFlattened[];
swaps: Swap[]; swapOuts: Swap[];
swapIns: Swap[];
swapRequests: SwapRequest[]; swapRequests: SwapRequest[];
} }
@ -70,7 +72,9 @@ export const initCLNState: CLNState = {
utxos: [], utxos: [],
offers: [], offers: [],
offersBookmarks: [], offersBookmarks: [],
totalSwapPeers: 0,
swapPeers: [], swapPeers: [],
swaps: [], swapOuts: [],
swapIns: [],
swapRequests: [] swapRequests: []
}; };

@ -100,7 +100,7 @@ export class CLNCreateInvoiceComponent implements OnInit, OnDestroy {
pipe(takeUntil(this.unSubs[3])). pipe(takeUntil(this.unSubs[3])).
subscribe({ subscribe({
next: (data) => { next: (data) => {
this.invoiceValueHint = '= ' + data.symbol + this.decimalPipe.transform(data.OTHER, CURRENCY_UNIT_FORMATS.OTHER) + ' ' + data.unit; this.invoiceValueHint = '= ' + this.decimalPipe.transform(data.OTHER, CURRENCY_UNIT_FORMATS.OTHER) + ' ' + data.symbol;
}, error: (err) => { }, error: (err) => {
this.invoiceValueHint = 'Conversion Error: ' + err; this.invoiceValueHint = 'Conversion Error: ' + err;
} }

@ -9,7 +9,7 @@ import { MatPaginator, MatPaginatorIntl } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort'; import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table';
import { CurrencyUnitEnum, CURRENCY_UNIT_FORMATS, PAGE_SIZE, PAGE_SIZE_OPTIONS, getPaginatorLabel, ScreenSizeEnum, APICallStatusEnum, UI_MESSAGES, CLNActions } from '../../../../shared/services/consts-enums-functions'; import { CurrencyUnitEnum, CURRENCY_UNIT_FORMATS, PAGE_SIZE, PAGE_SIZE_OPTIONS, getPaginatorLabel, ScreenSizeEnum, APICallStatusEnum, CLNActions } from '../../../../shared/services/consts-enums-functions';
import { ApiCallStatusPayload } from '../../../../shared/models/apiCallsPayload'; import { ApiCallStatusPayload } from '../../../../shared/models/apiCallsPayload';
import { SelNodeChild } from '../../../../shared/models/RTLconfig'; import { SelNodeChild } from '../../../../shared/models/RTLconfig';
import { GetInfo, Invoice, ListInvoices } from '../../../../shared/models/clnModels'; import { GetInfo, Invoice, ListInvoices } from '../../../../shared/models/clnModels';
@ -201,7 +201,7 @@ export class CLNLightningInvoicesTableComponent implements OnInit, AfterViewInit
pipe(takeUntil(this.unSubs[5])). pipe(takeUntil(this.unSubs[5])).
subscribe({ subscribe({
next: (data) => { next: (data) => {
this.invoiceValueHint = '= ' + data.symbol + this.decimalPipe.transform(data.OTHER, CURRENCY_UNIT_FORMATS.OTHER) + ' ' + data.unit; this.invoiceValueHint = '= ' + this.decimalPipe.transform(data.OTHER, CURRENCY_UNIT_FORMATS.OTHER) + ' ' + data.symbol;
}, error: (err) => { }, error: (err) => {
this.invoiceValueHint = 'Conversion Error: ' + err; this.invoiceValueHint = 'Conversion Error: ' + err;
} }

@ -83,7 +83,7 @@ export class CLNCreateOfferComponent implements OnInit, OnDestroy {
pipe(takeUntil(this.unSubs[3])). pipe(takeUntil(this.unSubs[3])).
subscribe({ subscribe({
next: (data) => { next: (data) => {
this.offerValueHint = '= ' + data.symbol + this.decimalPipe.transform(data.OTHER, CURRENCY_UNIT_FORMATS.OTHER) + ' ' + data.unit; this.offerValueHint = '= ' + this.decimalPipe.transform(data.OTHER, CURRENCY_UNIT_FORMATS.OTHER) + ' ' + data.symbol;
}, error: (err) => { }, error: (err) => {
this.offerValueHint = 'Conversion Error: ' + err; this.offerValueHint = 'Conversion Error: ' + err;
} }

@ -33,7 +33,7 @@
<mat-form-field fxFlex="60" fxLayoutAlign="start end"> <mat-form-field fxFlex="60" fxLayoutAlign="start end">
<input matInput autoFocus formControlName="fundingAmount" placeholder="Amount" type="number" [step]="1000" tabindex="1" required> <input matInput autoFocus formControlName="fundingAmount" placeholder="Amount" type="number" [step]="1000" tabindex="1" required>
<mat-hint>Remaining Bal: {{totalBalance - ((channelFormGroup.controls.fundingAmount.value) ? channelFormGroup.controls.fundingAmount.value : 0)}}</mat-hint> <mat-hint>Remaining Bal: {{totalBalance - ((channelFormGroup.controls.fundingAmount.value) ? channelFormGroup.controls.fundingAmount.value : 0)}}</mat-hint>
<span matSuffix> Sats </span> <span matSuffix class="ml-1"> Sats </span>
<mat-error *ngIf="channelFormGroup.controls.fundingAmount.errors?.required">Amount is required.</mat-error> <mat-error *ngIf="channelFormGroup.controls.fundingAmount.errors?.required">Amount is required.</mat-error>
<mat-error *ngIf="channelFormGroup.controls.fundingAmount.errors?.min">Amount must be a positive number.</mat-error> <mat-error *ngIf="channelFormGroup.controls.fundingAmount.errors?.min">Amount must be a positive number.</mat-error>
<mat-error *ngIf="channelFormGroup.controls.fundingAmount.errors?.max">Amount must be less than or equal to {{totalBalance}}.</mat-error> <mat-error *ngIf="channelFormGroup.controls.fundingAmount.errors?.max">Amount must be less than or equal to {{totalBalance}}.</mat-error>

@ -104,7 +104,7 @@ export class ECLCreateInvoiceComponent implements OnInit, OnDestroy {
pipe(takeUntil(this.unSubs[3])). pipe(takeUntil(this.unSubs[3])).
subscribe({ subscribe({
next: (data) => { next: (data) => {
this.invoiceValueHint = '= ' + data.symbol + this.decimalPipe.transform(data.OTHER, CURRENCY_UNIT_FORMATS.OTHER) + ' ' + data.unit; this.invoiceValueHint = '= ' + this.decimalPipe.transform(data.OTHER, CURRENCY_UNIT_FORMATS.OTHER) + ' ' + data.symbol;
}, error: (err) => { }, error: (err) => {
this.invoiceValueHint = 'Conversion Error: ' + err; this.invoiceValueHint = 'Conversion Error: ' + err;
} }

@ -195,7 +195,7 @@ export class ECLLightningInvoicesComponent implements OnInit, AfterViewInit, OnD
pipe(takeUntil(this.unSubs[4])). pipe(takeUntil(this.unSubs[4])).
subscribe({ subscribe({
next: (data) => { next: (data) => {
this.invoiceValueHint = '= ' + data.symbol + this.decimalPipe.transform(data.OTHER, CURRENCY_UNIT_FORMATS.OTHER) + ' ' + data.unit; this.invoiceValueHint = '= ' + this.decimalPipe.transform(data.OTHER, CURRENCY_UNIT_FORMATS.OTHER) + ' ' + data.symbol;
}, error: (err) => { }, error: (err) => {
this.invoiceValueHint = 'Conversion Error: ' + err; this.invoiceValueHint = 'Conversion Error: ' + err;
} }

@ -24,7 +24,7 @@
<mat-form-field fxFlex="60" fxLayoutAlign="start end"> <mat-form-field fxFlex="60" fxLayoutAlign="start end">
<input matInput [(ngModel)]="fundingAmount" placeholder="Amount" type="number" [step]="1000" [min]="1" [max]="totalBalance" tabindex="1" required name="amnt" #amt="ngModel"> <input matInput [(ngModel)]="fundingAmount" placeholder="Amount" type="number" [step]="1000" [min]="1" [max]="totalBalance" tabindex="1" required name="amnt" #amt="ngModel">
<mat-hint>(Remaining Bal: {{totalBalance - ((fundingAmount) ? fundingAmount : 0) | number}})</mat-hint> <mat-hint>(Remaining Bal: {{totalBalance - ((fundingAmount) ? fundingAmount : 0) | number}})</mat-hint>
<span matSuffix> Sats </span> <span matSuffix class="ml-1"> Sats </span>
<mat-error *ngIf="amount.errors?.required">Amount is required.</mat-error> <mat-error *ngIf="amount.errors?.required">Amount is required.</mat-error>
<mat-error *ngIf="amount.errors?.max">Amount must be less than or equal to {{totalBalance}}.</mat-error> <mat-error *ngIf="amount.errors?.max">Amount must be less than or equal to {{totalBalance}}.</mat-error>
</mat-form-field> </mat-form-field>

@ -33,7 +33,7 @@
<mat-form-field fxFlex="60" fxLayoutAlign="start end"> <mat-form-field fxFlex="60" fxLayoutAlign="start end">
<input matInput formControlName="fundingAmount" placeholder="Amount" type="number" [step]="1000" tabindex="1" required> <input matInput formControlName="fundingAmount" placeholder="Amount" type="number" [step]="1000" tabindex="1" required>
<mat-hint>Remaining Bal: {{totalBalance - ((channelFormGroup.controls.fundingAmount.value) ? channelFormGroup.controls.fundingAmount.value : 0)}}</mat-hint> <mat-hint>Remaining Bal: {{totalBalance - ((channelFormGroup.controls.fundingAmount.value) ? channelFormGroup.controls.fundingAmount.value : 0)}}</mat-hint>
<span matSuffix> Sats </span> <span matSuffix class="ml-1"> Sats </span>
<mat-error *ngIf="channelFormGroup.controls.fundingAmount.errors?.required">Amount is required.</mat-error> <mat-error *ngIf="channelFormGroup.controls.fundingAmount.errors?.required">Amount is required.</mat-error>
<mat-error *ngIf="channelFormGroup.controls.fundingAmount.errors?.min">Amount must be a positive number.</mat-error> <mat-error *ngIf="channelFormGroup.controls.fundingAmount.errors?.min">Amount must be a positive number.</mat-error>
<mat-error *ngIf="channelFormGroup.controls.fundingAmount.errors?.max">Amount must be less than or equal to {{totalBalance}}.</mat-error> <mat-error *ngIf="channelFormGroup.controls.fundingAmount.errors?.max">Amount must be less than or equal to {{totalBalance}}.</mat-error>

@ -99,7 +99,7 @@ export class CreateInvoiceComponent implements OnInit, OnDestroy {
pipe(takeUntil(this.unSubs[3])). pipe(takeUntil(this.unSubs[3])).
subscribe({ subscribe({
next: (data) => { next: (data) => {
this.invoiceValueHint = '= ' + data.symbol + this.decimalPipe.transform(data.OTHER, CURRENCY_UNIT_FORMATS.OTHER) + ' ' + data.unit; this.invoiceValueHint = '= ' + this.decimalPipe.transform(data.OTHER, CURRENCY_UNIT_FORMATS.OTHER) + ' ' + data.symbol;
}, error: (err) => { }, error: (err) => {
this.invoiceValueHint = 'Conversion Error: ' + err; this.invoiceValueHint = 'Conversion Error: ' + err;
} }

@ -205,7 +205,7 @@ export class LightningInvoicesComponent implements OnInit, AfterViewInit, OnDest
pipe(takeUntil(this.unSubs[4])). pipe(takeUntil(this.unSubs[4])).
subscribe({ subscribe({
next: (data) => { next: (data) => {
this.invoiceValueHint = '= ' + data.symbol + this.decimalPipe.transform(data.OTHER, CURRENCY_UNIT_FORMATS.OTHER) + ' ' + data.unit; this.invoiceValueHint = '= ' + this.decimalPipe.transform(data.OTHER, CURRENCY_UNIT_FORMATS.OTHER) + ' ' + data.symbol;
}, error: (err) => { }, error: (err) => {
this.invoiceValueHint = 'Conversion Error: ' + err; this.invoiceValueHint = 'Conversion Error: ' + err;
} }

@ -1,7 +1,7 @@
import { DataTypeEnum, LoopTypeEnum, PaymentTypes, SwapTypeEnum } from '../services/consts-enums-functions'; import { DataTypeEnum, LoopTypeEnum, PaymentTypes, SwapTypeEnum } from '../services/consts-enums-functions';
import { GetInfoRoot, RTLConfiguration } from './RTLconfig'; import { GetInfoRoot, RTLConfiguration } from './RTLconfig';
import { GetInfo, Invoice, Channel, Peer, PendingOpenChannel, UTXO } from './lndModels'; import { GetInfo, Invoice, Channel, Peer, PendingOpenChannel, UTXO } from './lndModels';
import { Invoice as InvoiceCLN, GetInfo as GetInfoCLN, Peer as PeerCLN, Channel as ChannelCLN, UTXO as UTXOCLN, Offer as OfferCLN, LookupNode as LookupNodeCLN } from './clnModels'; import { Invoice as InvoiceCLN, GetInfo as GetInfoCLN, Peer as PeerCLN, Channel as ChannelCLN, UTXO as UTXOCLN, Offer as OfferCLN, LookupNode as LookupNodeCLN, SwapPeerChannelsFlattened as SwapPeerChannelsFlattenedCLN } from './clnModels';
import { GetInfo as GetInfoECL, Peer as PeerECL, Channel as ChannelECL, Invoice as InvoiceECL, PaymentSent as PaymentSentECL } from './eclModels'; import { GetInfo as GetInfoECL, Peer as PeerECL, Channel as ChannelECL, Invoice as InvoiceECL, PaymentSent as PaymentSentECL } from './eclModels';
import { LoopQuote } from './loopModels'; import { LoopQuote } from './loopModels';
import { ServiceInfo } from './boltzModels'; import { ServiceInfo } from './boltzModels';
@ -117,6 +117,11 @@ export interface CLNOfferInformation {
component?: any; component?: any;
} }
export interface CLNSwapInformation {
swapPeer: SwapPeerChannelsFlattenedCLN;
component?: any;
}
export interface ECLInvoiceInformation { export interface ECLInvoiceInformation {
invoice: InvoiceECL; invoice: InvoiceECL;
newlyAdded?: boolean; newlyAdded?: boolean;
@ -229,7 +234,7 @@ export interface DialogConfig {
maxWidth?: string; maxWidth?: string;
minHeight?: string; minHeight?: string;
data: AlertData | ConfirmationData | ErrorData | ChannelRebalanceAlert | OpenChannelAlert | CLNOpenChannelAlert | InvoiceInformation | data: AlertData | ConfirmationData | ErrorData | ChannelRebalanceAlert | OpenChannelAlert | CLNOpenChannelAlert | InvoiceInformation |
CLNPaymentInformation | CLNInvoiceInformation | CLNOfferInformation | ECLInvoiceInformation | ECLPaymentInformation | ChannelInformation | CLNChannelInformation | CLNPaymentInformation | CLNInvoiceInformation | CLNOfferInformation | CLNSwapInformation | ECLInvoiceInformation | ECLPaymentInformation |
PendingOpenChannelInformation | OnChainAddressInformation | ShowPubkeyData | LoopAlert | SwapAlert | AuthConfig | ChannelInformation | CLNChannelInformation | PendingOpenChannelInformation | OnChainAddressInformation | ShowPubkeyData | LoopAlert |
OnChainLabelUTXO | OnChainSendFunds | CLNOnChainSendFunds | ECLChannelInformation | ECLOpenChannelAlert; SwapAlert | AuthConfig | OnChainLabelUTXO | OnChainSendFunds | CLNOnChainSendFunds | ECLChannelInformation | ECLOpenChannelAlert;
} }

@ -590,6 +590,7 @@ export interface Swap {
short_channel_id?: string; short_channel_id?: string;
opening_tx_id?: string; opening_tx_id?: string;
claim_tx_id?: string; claim_tx_id?: string;
cancel_message?: string;
} }
export interface SwapChannel { export interface SwapChannel {
@ -626,6 +627,7 @@ export interface SwapPeerChannelsFlattened {
sent?: SwapPeerSentReceived; sent?: SwapPeerSentReceived;
received?: SwapPeerSentReceived; received?: SwapPeerSentReceived;
total_fee_paid?: number; total_fee_paid?: number;
channels?: SwapChannel[];
// Channels array flattened // Channels array flattened
short_channel_id?: string; short_channel_id?: string;
local_balance?: number; local_balance?: number;

@ -322,6 +322,8 @@ export const UI_MESSAGES = {
GET_FUNDER_POLICY: 'Getting Or Updating Funder Policy...', GET_FUNDER_POLICY: 'Getting Or Updating Funder Policy...',
GET_LIST_CONFIGS: 'Getting Configurations List...', GET_LIST_CONFIGS: 'Getting Configurations List...',
LIST_NETWORK_NODES: 'Getting Network Nodes List...', LIST_NETWORK_NODES: 'Getting Network Nodes List...',
PEERSWAP_SWAPOUT: 'Initializing Swapout...',
PEERSWAP_SWAPIN: 'Initializing Swapin...',
LOG_OUT: 'Logging Out...' LOG_OUT: 'Logging Out...'
}; };
@ -331,6 +333,11 @@ export enum PaymentTypes {
KEYSEND = 'KEYSEND' KEYSEND = 'KEYSEND'
} }
export enum PeerswapTypes {
SWAP_OUT = 'swap-out',
SWAP_IN = 'swap-in'
}
export enum ReportBy { export enum ReportBy {
FEES = 'FEES', FEES = 'FEES',
EVENTS = 'EVENTS' EVENTS = 'EVENTS'
@ -525,6 +532,10 @@ export enum CLNActions {
SET_SWAP_PEERS_CLN = 'SET_SWAP_PEERS_CLN', SET_SWAP_PEERS_CLN = 'SET_SWAP_PEERS_CLN',
FETCH_SWAP_REQUESTS_CLN = 'FETCH_SWAP_REQUESTS_CLN', FETCH_SWAP_REQUESTS_CLN = 'FETCH_SWAP_REQUESTS_CLN',
SET_SWAP_REQUESTS_CLN = 'SET_SWAP_REQUESTS_CLN', SET_SWAP_REQUESTS_CLN = 'SET_SWAP_REQUESTS_CLN',
SWAPOUT_CLN = 'SWAPOUT_CLN',
ADD_SWAPOUT_CLN = 'ADD_SWAPOUT_CLN',
SWAPIN_CLN = 'SWAPIN_CLN',
ADD_SWAPIN_CLN = 'ADD_SWAPIN_CLN',
}; };
export enum ECLActions { export enum ECLActions {

Loading…
Cancel
Save