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 3acfb2b commit f0293c0
Show file tree
Hide file tree
Showing 18 changed files with 130 additions and 48 deletions.
5 changes: 4 additions & 1 deletion backend/controllers/shared/boltz.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export const getSwapInfo = (req, res, next) => {
});
};
export const createSwap = (req, res, next) => {
const { amount, address } = req.body;
const { amount, sendFromInternal, address } = req.body;
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Creating Swap..' });
options = common.getBoltzServerOptions(req);
if (options.url === '') {
Expand All @@ -89,6 +89,9 @@ export const createSwap = (req, res, next) => {
}
options.url = options.url + '/v1/createswap';
options.body = { amount: amount };
if (sendFromInternal) {
options.body.send_from_internal = sendFromInternal;
}
if (address && address !== '') {
options.body.address = address;
}
Expand Down
4 changes: 2 additions & 2 deletions frontend/index.html

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions frontend/main.4fdfc6814d69f389.js

Large diffs are not rendered by default.

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

This file was deleted.

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 @@ -80,7 +80,7 @@ export const getSwapInfo = (req, res, next) => {
};

export const createSwap = (req, res, next) => {
const { amount, address } = req.body;
const { amount, sendFromInternal, address } = req.body;
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Boltz', msg: 'Creating Swap..' });
options = common.getBoltzServerOptions(req);
if (options.url === '') {
Expand All @@ -90,6 +90,7 @@ export const createSwap = (req, res, next) => {
}
options.url = options.url + '/v1/createswap';
options.body = { amount: amount };
if (sendFromInternal) { options.body.send_from_internal = sendFromInternal; }
if (address && address !== '') { options.body.address = address; }
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Boltz', msg: 'Create Swap Options Body', data: options.body });
request.post(options).then((createSwapRes) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export class BoltzRootComponent implements OnInit, OnDestroy {
constructor(private router: Router, private store: Store<RTLState>, private boltzService: BoltzService) { }

ngOnInit() {
this.boltzService.getBoltzInfo();
this.boltzService.listSwaps();
const linkFound = this.links.find((link) => this.router.url.includes(link.link));
this.activeTab = linkFound ? linkFound : this.links[0];
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 && isSendFromInternalCompatible" 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="sendFromInternal" name="sendFromInternal">Send from Internal Wallet</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 isSendFromInternalCompatible = true;
inputFormGroup: UntypedFormGroup;
addressFormGroup: UntypedFormGroup;
statusFormGroup: UntypedFormGroup;
Expand All @@ -53,14 +55,26 @@ export class SwapModalComponent implements OnInit, AfterViewInit, OnDestroy {
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],
sendFromInternal: [true]
});
this.addressFormGroup = this.formBuilder.group({
addressType: ['local', [Validators.required]],
address: [{ value: '', disabled: true }]
});
this.statusFormGroup = this.formBuilder.group({});
this.onFormValueChanges();
this.boltzService.boltzInfoChanged.
pipe(takeUntil(this.unSubs[0])).
subscribe({
next: (boltzInfo: BoltzInfo) => {
this.boltzInfo = boltzInfo;
this.isSendFromInternalCompatible = this.commonService.isVersionCompatible(this.boltzInfo.version, '2.0.0');
}, error: (err) => {
this.boltzInfo = { version: '2.0.0' };
this.logger.error(err);
}
});
}

ngAfterViewInit() {
Expand All @@ -71,7 +85,7 @@ export class SwapModalComponent implements OnInit, AfterViewInit, OnDestroy {

onFormValueChanges() {
if (this.direction === SwapTypeEnum.SWAP_OUT) {
this.addressFormGroup.valueChanges.pipe(takeUntil(this.unSubs[2])).subscribe((changedValues) => {
this.addressFormGroup.valueChanges.pipe(takeUntil(this.unSubs[1])).subscribe((changedValues) => {
this.addressFormGroup.setErrors({ Invalid: true });
});
}
Expand Down Expand Up @@ -101,7 +115,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.isSendFromInternalCompatible ? this.inputFormGroup.controls.sendFromInternal.value : null).pipe(takeUntil(this.unSubs[2])).
subscribe({
next: (swapStatus: CreateSwapResponse) => {
this.swapStatus = swapStatus;
Expand All @@ -116,7 +130,7 @@ export class SwapModalComponent implements OnInit, AfterViewInit, OnDestroy {
});
} else {
const destAddress = this.addressFormGroup.controls.addressType.value === 'external' ? this.addressFormGroup.controls.address.value : '';
this.boltzService.swapOut(this.inputFormGroup.controls.amount.value, destAddress, this.inputFormGroup.controls.acceptZeroConf.value).pipe(takeUntil(this.unSubs[4])).
this.boltzService.swapOut(this.inputFormGroup.controls.amount.value, destAddress, this.inputFormGroup.controls.acceptZeroConf.value).pipe(takeUntil(this.unSubs[3])).
subscribe({
next: (swapStatus: CreateReverseSwapResponse) => {
this.swapStatus = swapStatus;
Expand All @@ -142,7 +156,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 | Send from Internal Wallet: ' + (this.inputFormGroup.controls.sendFromInternal.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 +201,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, sendFromInternal: 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
2 changes: 1 addition & 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
9 changes: 9 additions & 0 deletions src/app/shared/models/boltzModels.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
export interface BoltzInfo {
version: string;
node?: string;
network?: string;
nodePubkey?: string;
autoSwapStatus?: string;
blockHeights?: any;
}

export interface ServiceInfo {
fees?: {percentage?: number | null, miner: {normal?: number | null, reverse?: number | null}};
limits?: {minimal?: number | null, maximal?: number | null};
Expand Down
30 changes: 25 additions & 5 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,11 +48,29 @@ 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() {
this.store.dispatch(openSpinner({ payload: UI_MESSAGES.GET_BOLTZ_INFO }));
this.swapUrl = API_URL + API_END_POINTS.BOLTZ_API + '/info';
this.httpClient.get(this.swapUrl).
pipe(takeUntil(this.unSubs[1])).
subscribe({
next: (res: BoltzInfo) => {
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.GET_BOLTZ_INFO }));
this.boltzInfo = res;
this.boltzInfoChanged.next(this.boltzInfo);
}, error: (err) => {
this.boltzInfo = { version: '2.0.0' };
this.boltzInfoChanged.next(this.boltzInfo);
return of(this.handleErrorWithoutAlert(UI_MESSAGES.GET_BOLTZ_INFO, this.swapUrl, err));
}
});
}

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]),
takeUntil(this.unSubs[2]),
map((res) => {
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.GET_SERVICE_INFO }));
return res;
Expand All @@ -65,8 +85,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, sendFromInternal: boolean) {
const requestBody = { amount: amount, sendFromInternal: sendFromInternal };
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
Loading

0 comments on commit f0293c0

Please sign in to comment.