Skip to content

Commit

Permalink
cln: Boltz auto-send
Browse files Browse the repository at this point in the history
- Added auto send option for Swap In
- Checking compatiblity with v2.0.0 and above
  • Loading branch information
ShahanaFarooqui committed Mar 14, 2024
1 parent f0e35c5 commit 15fb0a5
Show file tree
Hide file tree
Showing 18 changed files with 134 additions and 70 deletions.
5 changes: 4 additions & 1 deletion backend/controllers/shared/boltz.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ export const createSwap = (req, res, next) => {
}
options.url = options.url + '/v1/createswap';
options.body = { amount: req.body.amount };
if (req.body.address !== '') {
if (req.body.autoSend) {
options.body.auto_send = req.body.autoSend;
}
if (req.body.address && req.body.address !== '') {
options.body.address = req.body.address;
}
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Boltz', msg: 'Create Swap Options Body', data: options.body });
Expand Down
4 changes: 2 additions & 2 deletions frontend/index.html

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion frontend/main.6539a03bc601bec5.js

This file was deleted.

1 change: 1 addition & 0 deletions frontend/main.7c40e9847a4566a5.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion frontend/styles.092da2fa10498671.css

This file was deleted.

1 change: 1 addition & 0 deletions frontend/styles.5c4af8b44c9c9805.css

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion server/controllers/shared/boltz.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ export const createSwap = (req, res, next) => {
}
options.url = options.url + '/v1/createswap';
options.body = { amount: req.body.amount };
if (req.body.address !== '') { options.body.address = req.body.address; }
if (req.body.autoSend) { options.body.auto_send = req.body.autoSend; }
if (req.body.address && req.body.address !== '') { options.body.address = req.body.address; }
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Boltz', msg: 'Create Swap Options Body', data: options.body });
request.post(options).then((createSwapRes) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Swap Created', data: createSwapRes });
Expand Down
34 changes: 19 additions & 15 deletions src/app/shared/components/ln-services/boltz/boltz-root.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Router, ResolveEnd, Event } from '@angular/router';
import { Subject } from 'rxjs';
import { Subject, forkJoin } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';

Expand Down Expand Up @@ -69,21 +69,25 @@ export class BoltzRootComponent implements OnInit, OnDestroy {
}

onSwap(direction: SwapTypeEnum) {
this.boltzService.serviceInfo().
pipe(takeUntil(this.unSubs[2])).
subscribe({
next: (response: any) => {
this.store.dispatch(openAlert({
payload: {
data: {
serviceInfo: response,
direction: direction,
component: SwapModalComponent
}
forkJoin([
this.boltzService.getBoltzInfo(),
this.boltzService.serviceInfo()
]).subscribe({
next: (responses) => {
this.store.dispatch(openAlert({
payload: {
data: {
boltzInfo: responses[0],
serviceInfo: responses[1],
direction: direction,
component: SwapModalComponent
}
}));
}
});
}
}));
}, error: (err) => {
this.store.dispatch(openAlert({ payload: { data: { type: 'ERROR', alertTitle: 'Boltz Information Error', message: 'Fetching Boltz Information Failed!' } } }));
}
});
}

ngOnDestroy() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@
<mat-icon matTooltip="Only recommended for smaller payments, involves trust in Boltz" matTooltipPosition="above" class="info-icon mt-2">info_outline</mat-icon>
</div>
</div>
<div *ngIf="direction === swapTypeEnum.SWAP_IN && isAutoSendCompatible" fxLayout="column" fxFlex="48" fxLayoutAlign="start stretch">
<div fxLayout="row" fxFlex="100" fxLayoutAlign="start center">
<mat-slide-toggle fxLayoutAlign="start center" tabindex="2" color="primary" formControlName="autoSend" name="autoSend">Auto Send</mat-slide-toggle>
<mat-icon matTooltip="Pay from the node's onchain wallet" matTooltipPosition="above" class="info-icon mt-2">info_outline</mat-icon>
</div>
</div>
</div>
<div class="mt-2" fxLayout="row" fxLayoutAlign="start center" fxFlex="100">
<button *ngIf="direction === swapTypeEnum.SWAP_OUT" mat-button color="primary" tabindex="2" type="button" matStepperNext>Next</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';

import { opacityAnimation } from '../../../../animation/opacity-animation';
import { ScreenSizeEnum, SwapTypeEnum } from '../../../../services/consts-enums-functions';
import { ServiceInfo, CreateSwapResponse, CreateReverseSwapResponse } from '../../../../models/boltzModels';
import { ServiceInfo, CreateSwapResponse, CreateReverseSwapResponse, BoltzInfo } from '../../../../models/boltzModels';
import { SwapAlert } from '../../../../models/alertData';
import { BoltzService } from '../../../../services/boltz.service';
import { LoggerService } from '../../../../services/logger.service';
Expand All @@ -25,6 +25,7 @@ export class SwapModalComponent implements OnInit, AfterViewInit, OnDestroy {

@ViewChild('stepper', { static: false }) stepper: MatStepper;
public faInfoCircle = faInfoCircle;
public boltzInfo: BoltzInfo = null;
public serviceInfo: ServiceInfo = { fees: { percentage: null, miner: { normal: null, reverse: null } }, limits: { minimal: 10000, maximal: 50000000 } };
public swapTypeEnum = SwapTypeEnum;
public direction = SwapTypeEnum.SWAP_OUT;
Expand All @@ -38,6 +39,7 @@ export class SwapModalComponent implements OnInit, AfterViewInit, OnDestroy {
public screenSizeEnum = ScreenSizeEnum;
public animationDirection = 'forward';
public flgEditable = true;
public isAutoSendCompatible = false;
inputFormGroup: UntypedFormGroup;
addressFormGroup: UntypedFormGroup;
statusFormGroup: UntypedFormGroup;
Expand All @@ -47,13 +49,16 @@ export class SwapModalComponent implements OnInit, AfterViewInit, OnDestroy {

ngOnInit() {
this.screenSize = this.commonService.getScreenSize();
this.boltzInfo = this.data.boltzInfo;
this.serviceInfo = this.data.serviceInfo;
this.isAutoSendCompatible = this.commonService.isVersionCompatible(this.boltzInfo.version, '2.0.0');
this.direction = this.data.direction || SwapTypeEnum.SWAP_OUT;
this.swapDirectionCaption = this.direction === SwapTypeEnum.SWAP_OUT ? 'Swap Out' : 'Swap in';
this.inputFormLabel = 'Amount to ' + this.swapDirectionCaption;
this.inputFormGroup = this.formBuilder.group({
amount: [this.serviceInfo.limits?.minimal, [Validators.required, Validators.min(this.serviceInfo.limits?.minimal || 0), Validators.max(this.serviceInfo.limits?.maximal || 0)]],
acceptZeroConf: [false]
acceptZeroConf: [false],
autoSend: [true]
});
this.addressFormGroup = this.formBuilder.group({
addressType: ['local', [Validators.required]],
Expand Down Expand Up @@ -101,7 +106,7 @@ export class SwapModalComponent implements OnInit, AfterViewInit, OnDestroy {
this.stepper.selected?.stepControl.setErrors(null);
this.stepper.next();
if (this.direction === SwapTypeEnum.SWAP_IN) {
this.boltzService.swapIn(this.inputFormGroup.controls.amount.value).pipe(takeUntil(this.unSubs[3])).
this.boltzService.swapIn(this.inputFormGroup.controls.amount.value, this.isAutoSendCompatible ? this.inputFormGroup.controls.autoSend.value : null).pipe(takeUntil(this.unSubs[3])).
subscribe({
next: (swapStatus: CreateSwapResponse) => {
this.swapStatus = swapStatus;
Expand Down Expand Up @@ -142,7 +147,7 @@ export class SwapModalComponent implements OnInit, AfterViewInit, OnDestroy {
case 1:
if (this.inputFormGroup.controls.amount.value) {
if (this.direction === SwapTypeEnum.SWAP_IN) {
this.inputFormLabel = this.swapDirectionCaption + ' Amount: ' + (this.decimalPipe.transform(this.inputFormGroup.controls.amount.value ? this.inputFormGroup.controls.amount.value : 0)) + ' Sats';
this.inputFormLabel = this.swapDirectionCaption + ' Amount: ' + (this.decimalPipe.transform(this.inputFormGroup.controls.amount.value ? this.inputFormGroup.controls.amount.value : 0)) + ' Sats | Auto Send: ' + (this.inputFormGroup.controls.autoSend.value ? 'Yes' : 'No');
} else {
this.inputFormLabel = this.swapDirectionCaption + ' Amount: ' + (this.decimalPipe.transform(this.inputFormGroup.controls.amount.value ? this.inputFormGroup.controls.amount.value : 0)) + ' Sats | Zero Conf: ' + (this.inputFormGroup.controls.acceptZeroConf.value ? 'Yes' : 'No');
}
Expand Down Expand Up @@ -187,7 +192,7 @@ export class SwapModalComponent implements OnInit, AfterViewInit, OnDestroy {
onRestart() {
this.stepper.reset();
this.flgEditable = true;
this.inputFormGroup.reset({ amount: this.serviceInfo.limits?.minimal, acceptZeroConf: false });
this.inputFormGroup.reset({ amount: this.serviceInfo.limits?.minimal, acceptZeroConf: false, autoSend: true });
this.statusFormGroup.reset();
this.addressFormGroup.reset({ addressType: 'local', address: '' });
this.addressFormGroup.controls.address.disable();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,42 @@ <h4 fxLayoutAlign="start" class="font-bold-500">Lockup Address</h4>
</div>
</ng-template>
<ng-template #swapInBlock>
<div fxLayout="column">
<div fxLayout="row">
<div fxFlex="50">
<h4 fxLayoutAlign="start" class="font-bold-500">ID</h4>
<span class="foreground-secondary-text">{{swapStatus?.id}}</span>
</div>
<div fxFlex="50">
<h4 fxLayoutAlign="start" class="font-bold-500">Expected Amount (Sats)</h4>
<span class="foreground-secondary-text">{{swapStatus?.expectedAmount | number}}</span>
</div>
</div>
<mat-divider class="w-100 my-1" />
<div fxLayout="row">
<div fxFlex="100">
<h4 fxLayoutAlign="start" class="font-bold-500">Address</h4>
<span class="foreground-secondary-text">{{swapStatus?.address}}</span>
</div>
<div fxLayout="column" fxLayout.gt-sm="row" fxLayoutAlign="space-between stretch">
<div fxFlex="35" class="modal-qr-code-container padding-gap-large" [fxLayoutAlign]="swapStatus?.address && swapStatus?.address !== '' ? 'center start' : 'center center'" [ngClass]="{'display-none': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM}">
<qr-code *ngIf="swapStatus?.address && swapStatus?.address !== ''" [value]="swapStatus?.address" [size]="qrWidth" [errorCorrectionLevel]="'L'" />
<span *ngIf="!swapStatus?.address || swapStatus?.address === ''" class="font-size-300">N/A</span>
</div>
<mat-divider class="w-100 my-1" />
<div fxLayout="row">
<div fxFlex="100">
<h4 fxLayoutAlign="start" class="font-bold-500">BIP 21</h4>
<span class="foreground-secondary-text">{{swapStatus?.bip21}}</span>
<div fxFlex="65">
<div fxLayout="column">
<div fxFlex="30" class="modal-qr-code-container padding-gap" [fxLayoutAlign]="swapStatus?.address && swapStatus?.address !== '' ? 'center start' : 'center center'" [ngClass]="{'display-none': screenSize !== screenSizeEnum.XS && screenSize !== screenSizeEnum.SM}">
<qr-code *ngIf="swapStatus?.address && swapStatus?.address !== ''" [value]="swapStatus?.address" [size]="qrWidth" [errorCorrectionLevel]="'L'" />
<span *ngIf="!swapStatus?.address || swapStatus?.address === ''" class="font-size-120">QR Code Not Applicable</span>
</div>
<mat-divider *ngIf="screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM" class="my-1" [inset]="true" />
<div fxLayout="row">
<div fxFlex="50">
<h4 fxLayoutAlign="start" class="font-bold-500">ID</h4>
<span class="foreground-secondary-text">{{swapStatus?.id}}</span>
</div>
<div fxFlex="50">
<h4 fxLayoutAlign="start" class="font-bold-500">Expected Amount (Sats)</h4>
<span class="foreground-secondary-text">{{swapStatus?.expectedAmount | number}}</span>
</div>
</div>
<mat-divider class="w-100 my-1" />
<div fxLayout="row">
<div fxFlex="100">
<h4 fxLayoutAlign="start" class="font-bold-500">Address</h4>
<span class="foreground-secondary-text">{{swapStatus?.address}}</span>
</div>
</div>
<mat-divider class="w-100 my-1" />
<div fxLayout="row">
<div fxFlex="100">
<h4 fxLayoutAlign="start" class="font-bold-500">BIP 21</h4>
<span class="foreground-secondary-text">{{swapStatus?.bip21}}</span>
</div>
</div>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
import { Component, Input } from '@angular/core';
import { Component, Input, OnInit } from '@angular/core';

import { SwapTypeEnum } from '../../../../services/consts-enums-functions';
import { SwapTypeEnum, ScreenSizeEnum } from '../../../../services/consts-enums-functions';
import { CommonService } from '../../../../services/common.service';

@Component({
selector: 'rtl-boltz-swap-status',
templateUrl: './swap-status.component.html',
styleUrls: ['./swap-status.component.scss']
})
export class SwapStatusComponent {
export class SwapStatusComponent implements OnInit {

@Input() swapStatus: any = null;
@Input() direction = SwapTypeEnum.SWAP_OUT;
@Input() acceptZeroConf = false;
public qrWidth = 240;
public screenSize = '';
public screenSizeEnum = ScreenSizeEnum;
public swapTypeEnum = SwapTypeEnum;

constructor() {}
constructor(private commonService: CommonService) {}

ngOnInit() {
this.screenSize = this.commonService.getScreenSize();
if (this.screenSize === ScreenSizeEnum.XS) {
this.qrWidth = 180;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
<mat-divider class="w-100" />
<mat-tree *ngIf="settings?.lnServerUrl" #tree [dataSource]="navMenus" [treeControl]="treeControlNested">
<mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle routerLinkActive="active-link" routerLink="{{node.link}}">
<div tabindex="2" (click)="onChildNavClicked(node)">
<div tabindex="2" (click)="onChildNavClicked(node)">
<div fxLayout="row" fxFlex="100" fxLayoutAlign="start center">
<span *ngIf="node.iconType === 'SVG'" class="fa-icon-small mr-2"><ng-container [ngTemplateOutlet]="node.icon === 'boltzIconBlock' ? boltzIconBlock : null" /></span>
<span *ngIf="node.iconType === 'SVG'" class="fa-icon-small mr-2" fxLayout="row" fxFlex="100" fxLayoutAlign="start center"><ng-container [ngTemplateOutlet]="node.icon === 'boltzIconBlock' ? boltzIconBlock : null" /></span>
<fa-icon *ngIf="node.iconType === 'FA'" class="fa-icon-small mr-2" [icon]="node.icon" />
<mat-icon *ngIf="!node.iconType" class="mat-icon-36">{{node.icon}}</mat-icon>
<span>{{node.name}}</span>
Expand Down
3 changes: 2 additions & 1 deletion src/app/shared/models/alertData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { GetInfo, Invoice, Channel, Peer, PendingOpenChannel, UTXO } from './lnd
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 { GetInfo as GetInfoECL, Peer as PeerECL, Channel as ChannelECL, Invoice as InvoiceECL, PaymentSent as PaymentSentECL } from './eclModels';
import { LoopQuote } from './loopModels';
import { ServiceInfo } from './boltzModels';
import { BoltzInfo, ServiceInfo } from './boltzModels';

export interface MessageErrorField {
code: number;
Expand Down Expand Up @@ -182,6 +182,7 @@ export interface LoopAlert {

export interface SwapAlert {
channel: Channel;
boltzInfo: BoltzInfo;
serviceInfo: ServiceInfo;
direction?: SwapTypeEnum;
component?: any;
Expand Down
19 changes: 19 additions & 0 deletions src/app/shared/models/boltzModels.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
export interface BlockHeight {
key: string;
value: number;
}

export interface BoltzInfo {
version: string;
node: string;
network: string;
nodePubkey: string;
autoSwapStatus: string;
blockHeights: BlockHeight[];
symbol: string;
lndPubkey: string;
blockHeight: number;
pendingSwaps: string[];
pendingReverseSwaps: string[];
}

export interface ServiceInfo {
fees?: {percentage?: number | null, miner: {normal?: number | null, reverse?: number | null}};
limits?: {minimal?: number | null, maximal?: number | null};
Expand Down
25 changes: 11 additions & 14 deletions src/app/shared/services/boltz.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ErrorMessageComponent } from '../components/data-modal/error-message/er
import { CommonService } from './common.service';
import { LoggerService } from './logger.service';
import { API_URL, API_END_POINTS, AlertTypeEnum, UI_MESSAGES } from './consts-enums-functions';
import { ListSwaps } from '../models/boltzModels';
import { ListSwaps, BoltzInfo } from '../models/boltzModels';
import { closeSpinner, logout, openAlert, openSpinner } from '../../store/rtl.actions';

import { RTLState } from '../../store/rtl.state';
Expand All @@ -18,8 +18,10 @@ export class BoltzService implements OnDestroy {

private swapUrl = '';
private swaps: ListSwaps = {};
private boltzInfo: BoltzInfo = null;
public boltzInfoChanged = new BehaviorSubject<BoltzInfo>(null);
public swapsChanged = new BehaviorSubject<ListSwaps>({});
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject(), new Subject()];

constructor(private httpClient: HttpClient, private logger: LoggerService, private store: Store<RTLState>, private commonService: CommonService) { }

Expand All @@ -46,17 +48,12 @@ export class BoltzService implements OnDestroy {
return this.httpClient.get(this.swapUrl).pipe(catchError((err) => of(this.handleErrorWithAlert(UI_MESSAGES.NO_SPINNER, this.swapUrl, err))));
}

getBoltzInfo() {
return this.httpClient.get<BoltzInfo>(API_URL + API_END_POINTS.BOLTZ_API + '/info');
}

serviceInfo() {
this.store.dispatch(openSpinner({ payload: UI_MESSAGES.GET_SERVICE_INFO }));
this.swapUrl = API_URL + API_END_POINTS.BOLTZ_API + '/serviceInfo';
return this.httpClient.get(this.swapUrl).pipe(
takeUntil(this.unSubs[1]),
map((res) => {
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.GET_SERVICE_INFO }));
return res;
}),
catchError((err) => of(this.handleErrorWithAlert(UI_MESSAGES.GET_SERVICE_INFO, this.swapUrl, err)))
);
return this.httpClient.get(API_URL + API_END_POINTS.BOLTZ_API + '/serviceInfo');
}

swapOut(amount: number, address: string, acceptZeroConf: boolean) {
Expand All @@ -65,8 +62,8 @@ export class BoltzService implements OnDestroy {
return this.httpClient.post(this.swapUrl, requestBody).pipe(catchError((err) => this.handleErrorWithoutAlert('Swap Out for Address: ' + address, UI_MESSAGES.NO_SPINNER, err)));
}

swapIn(amount: number) {
const requestBody = { amount: amount };
swapIn(amount: number, autoSend: boolean) {
const requestBody = { amount: amount, autoSend: autoSend };
this.swapUrl = API_URL + API_END_POINTS.BOLTZ_API + '/createswap';
return this.httpClient.post(this.swapUrl, requestBody).pipe(catchError((err) => this.handleErrorWithoutAlert('Swap In for Amount: ' + amount, UI_MESSAGES.NO_SPINNER, err)));
}
Expand Down
1 change: 1 addition & 0 deletions src/app/shared/services/consts-enums-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ export const UI_MESSAGES = {
UPDATE_NODE_SETTINGS: 'Updating Node Settings...',
UPDATE_SELECTED_NODE: 'Updating Selected Node...',
OPEN_CONFIG_FILE: 'Opening Config File...',
GET_BOLTZ_INFO: 'Getting Boltz Info...',
GET_SERVICE_INFO: 'Getting Service Info...',
GET_QUOTE: 'Getting Quotes...',
UPDATE_DEFAULT_NODE_SETTING: 'Updating Defaule Node Settings...',
Expand Down
5 changes: 4 additions & 1 deletion src/app/shared/theme/styles/theme-mode-dark.scss
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,12 @@
& .ng-fa-icon, & .mat-icon {
color: $primary-darker;
}
& .sidenav-img svg {
& .sidenav-img svg, & .boltz-icon-fill {
fill: $primary-darker;
}
& .boltz-icon {
stroke: $primary-darker;
}
}
.mat-tree-node .sidenav-img, .mat-nested-tree-node .sidenav-img, .mat-nested-tree-node-parent .sidenav-img,
.page-title-container .page-title-img, svg.top-icon-small {
Expand Down

0 comments on commit 15fb0a5

Please sign in to comment.