Skip to content

Commit

Permalink
cln: Boltz auto-send (#1366)
Browse files Browse the repository at this point in the history
* Bug-fix (CLN Boltz): Hide claim tx id and routing fee for non-zero conf reverse swap

* cln: Boltz auto-send

- Added auto send option for Swap In
- Checking compatiblity with v2.0.0 and above
  • Loading branch information
ShahanaFarooqui authored Apr 4, 2024
1 parent 86cef68 commit ba36aa8
Show file tree
Hide file tree
Showing 18 changed files with 147 additions and 56 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.93a63d10cec22d2d.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion frontend/main.bb1d6589ffe4efe7.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 Expand Up @@ -87,7 +93,7 @@ <h4 *ngIf="swapStatus" fxLayoutAlign="start" class="font-bold-500 mt-2">{{(swapS
</div>
</div>
<ng-template #swapStatusBlock>
<rtl-boltz-swap-status fxLayout="column" [swapStatus]="swapStatus" [direction]="direction" />
<rtl-boltz-swap-status fxLayout="column" [swapStatus]="swapStatus" [direction]="direction" [acceptZeroConf]="inputFormGroup?.controls?.acceptZeroConf.value" [sendFromInternal]="inputFormGroup?.controls?.sendFromInternal.value" />
</ng-template>
<div *ngIf="flgShowInfo" fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch" class="info-graphics-container" [@opacityAnimation]>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch">
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 @@ -7,52 +7,71 @@
<ng-template #swapOutBlock>
<div fxLayout="column">
<div fxLayout="row">
<div fxFlex="50">
<div fxFlex="33">
<h4 fxLayoutAlign="start" class="font-bold-500">ID</h4>
<span class="foreground-secondary-text">{{swapStatus?.id}}</span>
</div>
<div fxFlex="50">
<div *ngIf="acceptZeroConf" fxFlex="33">
<h4 fxLayoutAlign="start" class="font-bold-500">Routing Fee (mSats)</h4>
<span class="foreground-secondary-text">{{swapStatus?.routingFeeMilliSat | number}}</span>
</div>
</div>
<mat-divider class="w-100 my-1" />
<div fxLayout="row">
<div fxFlex="50">
<div *ngIf="acceptZeroConf" fxFlex="33">
<h4 fxLayoutAlign="start" class="font-bold-500">Claim Transaction ID</h4>
<span class="foreground-secondary-text">{{swapStatus?.claimTransactionId}}</span>
</div>
<div fxFlex="50">
</div>
<mat-divider class="w-100 my-1" />
<div fxLayout="row">
<div fxFlex="100">
<h4 fxLayoutAlign="start" class="font-bold-500">Lockup Address</h4>
<span class="foreground-secondary-text">{{swapStatus?.lockupAddress}}</span>
</div>
</div>
</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?.txId || swapStatus?.address) !== '' ? 'center start' : 'center center'" [ngClass]="{'display-none': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM}">
<qr-code [value]="swapStatus?.txId || swapStatus?.address" [size]="qrWidth" [errorCorrectionLevel]="'L'" />
<span *ngIf="(swapStatus?.txId || 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?.txId || swapStatus?.address) !== '' ? 'center start' : 'center center'" [ngClass]="{'display-none': screenSize !== screenSizeEnum.XS && screenSize !== screenSizeEnum.SM}">
<qr-code [value]="swapStatus?.txId || swapStatus?.address" [size]="qrWidth" [errorCorrectionLevel]="'L'" />
<span *ngIf="(swapStatus?.txId || 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 *ngIf="sendFromInternal" fxLayout="row">
<div fxFlex="100">
<h4 fxLayoutAlign="start" class="font-bold-500">Transaction ID</h4>
<span class="foreground-secondary-text">{{swapStatus?.txId}}</span>
</div>
</div>
<div *ngIf="!sendFromInternal" 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 *ngIf="!sendFromInternal" class="w-100 my-1" />
<div *ngIf="!sendFromInternal" 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 *ngIf="!sendFromInternal" class="w-100 my-1" />
<div *ngIf="!sendFromInternal" 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,18 +1,31 @@
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;
@Input() sendFromInternal = true;
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
10 changes: 10 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 Expand Up @@ -62,6 +71,7 @@ export interface CreateSwapResponse {
address?: string;
expectedAmount?: string;
bip21?: string;
txId?: string;
}

export interface CreateReverseSwapRequest {
Expand Down
Loading

0 comments on commit ba36aa8

Please sign in to comment.