diff --git a/backend/src/api/bitcoin/bitcoin.routes.ts b/backend/src/api/bitcoin/bitcoin.routes.ts index 69d42f570..e6075ffa2 100644 --- a/backend/src/api/bitcoin/bitcoin.routes.ts +++ b/backend/src/api/bitcoin/bitcoin.routes.ts @@ -129,17 +129,7 @@ class BitcoinRoutes { res.send('Service Unavailable'); return; } - let minFee = 0; - if (req.query.min) { - try { - minFee = parseFloat(req.query.min as string); - } catch (e) { - res.statusCode = 400; - res.send('Invalid minimum fee'); - return; - } - } - const result = feeApi.getPreciseRecommendedFee(minFee); + const result = feeApi.getPreciseRecommendedFee(); res.json(result); } diff --git a/backend/src/api/fee-api.ts b/backend/src/api/fee-api.ts index 2edb8c0e4..0914a1098 100644 --- a/backend/src/api/fee-api.ts +++ b/backend/src/api/fee-api.ts @@ -18,6 +18,9 @@ class FeeApi { constructor() { } minimumIncrement = isLiquid ? 0.1 : 1; + minFastestFee = isLiquid ? 0.1 : 1; + minHalfHourFee = isLiquid ? 0.1 : 0.5; + priorityFactor = isLiquid ? 0 : 0.5; public getRecommendedFee(): RecommendedFees { const pBlocks = projectedBlocks.getMempoolBlocks(); @@ -26,17 +29,17 @@ class FeeApi { return this.calculateRecommendedFee(pBlocks, mPool); } - public getPreciseRecommendedFee(minimum: number = 0): RecommendedFees { + public getPreciseRecommendedFee(): RecommendedFees { const pBlocks = projectedBlocks.getMempoolBlocks(); const mPool = mempool.getMempoolInfo(); // minimum non-zero minrelaytxfee / incrementalrelayfee is 1 sat/kvB = 0.001 sat/vB - return this.calculateRecommendedFee(pBlocks, mPool, minimum, 0.001); + return this.calculateRecommendedFee(pBlocks, mPool, 0.001); } - public calculateRecommendedFee(pBlocks: MempoolBlock[], mPool: IBitcoinApi.MempoolInfo, minimumRecommendation: number = 0, minIncrement: number = this.minimumIncrement): RecommendedFees { + public calculateRecommendedFee(pBlocks: MempoolBlock[], mPool: IBitcoinApi.MempoolInfo, minIncrement: number = this.minimumIncrement): RecommendedFees { const purgeRate = this.roundUpToNearest(mPool.mempoolminfee * 100000, minIncrement); - const minimumFee = Math.max(purgeRate, minimumRecommendation, minIncrement); + const minimumFee = Math.max(purgeRate, minIncrement); if (!pBlocks.length) { return { @@ -67,8 +70,8 @@ class FeeApi { hourFee = Math.max(hourFee, economyFee); return { - 'fastestFee': this.roundToNearest(fastestFee, minIncrement), - 'halfHourFee': this.roundToNearest(halfHourFee, minIncrement), + 'fastestFee': Math.max(this.roundToNearest(fastestFee + this.priorityFactor, minIncrement), this.minFastestFee), + 'halfHourFee': Math.max(this.roundToNearest(halfHourFee + (this.priorityFactor / 2), minIncrement), this.minHalfHourFee), 'hourFee': this.roundToNearest(hourFee, minIncrement), 'economyFee': this.roundToNearest(economyFee, minIncrement), 'minimumFee': this.roundToNearest(minimumFee, minIncrement), diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts index 57fd46730..532d6d4a1 100644 --- a/backend/src/api/websocket-handler.ts +++ b/backend/src/api/websocket-handler.ts @@ -102,7 +102,7 @@ class WebsocketHandler { 'backendInfo': backendInfo.getBackendInfo(), 'loadingIndicators': loadingIndicators.getLoadingIndicators(), 'da': da?.previousTime ? da : undefined, - 'fees': feeApi.getRecommendedFee(), + 'fees': feeApi.getPreciseRecommendedFee(), }); } @@ -639,7 +639,7 @@ class WebsocketHandler { } memPool.removeFromSpendMap(deletedTransactions); memPool.addToSpendMap(newTransactions); - const recommendedFees = feeApi.getRecommendedFee(); + const recommendedFees = feeApi.getPreciseRecommendedFee(); const latestTransactions = memPool.getLatestTransactions(); @@ -1137,7 +1137,7 @@ class WebsocketHandler { const mBlockDeltas = mempoolBlocks.getMempoolBlockDeltas(); const da = difficultyAdjustment.getDifficultyAdjustment(); - const fees = feeApi.getRecommendedFee(); + const fees = feeApi.getPreciseRecommendedFee(); const mempoolInfo = memPool.getMempoolInfo(); // pre-compute address transactions diff --git a/frontend/src/app/components/clock/clock.component.html b/frontend/src/app/components/clock/clock.component.html index 376d72bfc..3d23f68dc 100644 --- a/frontend/src/app/components/clock/clock.component.html +++ b/frontend/src/app/components/clock/clock.component.html @@ -46,7 +46,7 @@

High Priority

- +

diff --git a/frontend/src/app/components/fees-box/fees-box.component.html b/frontend/src/app/components/fees-box/fees-box.component.html index 580307df5..a3b2a5286 100644 --- a/frontend/src/app/components/fees-box/fees-box.component.html +++ b/frontend/src/app/components/fees-box/fees-box.component.html @@ -10,26 +10,27 @@ High Priority
+
-
+
-
+
-
+
-
+
diff --git a/frontend/src/app/services/websocket.service.ts b/frontend/src/app/services/websocket.service.ts index b82b32dd5..8979ce4cf 100644 --- a/frontend/src/app/services/websocket.service.ts +++ b/frontend/src/app/services/websocket.service.ts @@ -428,7 +428,9 @@ export class WebsocketService { } if (response.fees) { - this.stateService.recommendedFees$.next(response.fees); + this.stateService.recommendedFees$.next({ + ...response.fees, + }); } if (response.backendInfo) { diff --git a/frontend/src/app/shared/components/fee-rate/fee-rate.component.html b/frontend/src/app/shared/components/fee-rate/fee-rate.component.html index f9e86cc2b..0f4491dd1 100644 --- a/frontend/src/app/shared/components/fee-rate/fee-rate.component.html +++ b/frontend/src/app/shared/components/fee-rate/fee-rate.component.html @@ -1,7 +1,7 @@ - {{ fee / (weight / 4) | feeRounding:rounding }} sat/vB - {{ fee / weight | feeRounding:rounding }} sat/WU + {{ fee / (weight / 4) | feeRounding:rounding:dp }} sat/vB + {{ fee / weight | feeRounding:rounding:dp }} sat/WU - sat/vB diff --git a/frontend/src/app/shared/components/fee-rate/fee-rate.component.ts b/frontend/src/app/shared/components/fee-rate/fee-rate.component.ts index 857b653c3..c71f5f5f9 100644 --- a/frontend/src/app/shared/components/fee-rate/fee-rate.component.ts +++ b/frontend/src/app/shared/components/fee-rate/fee-rate.component.ts @@ -1,6 +1,7 @@ import { Component, Input, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { StateService } from '@app/services/state.service'; +import { FeeRoundingPipe } from '@app/shared/pipes/fee-rounding/fee-rounding.pipe'; @Component({ selector: 'app-fee-rate', @@ -12,6 +13,8 @@ export class FeeRateComponent implements OnInit { @Input() fee: number | undefined; @Input() weight: number = 4; @Input() rounding: string = null; + @Input() dp: number = null; + @Input() softDecimals: boolean = false; @Input() showUnit: boolean = true; @Input() unitClass: string = 'symbol'; @Input() unitStyle: any; @@ -20,9 +23,22 @@ export class FeeRateComponent implements OnInit { constructor( private stateService: StateService, + private feeRoundingPipe: FeeRoundingPipe, ) { } ngOnInit() { this.rateUnits$ = this.stateService.rateUnits$; } + + getIntegerPart(rate: number): string { + const formatted = this.feeRoundingPipe.transform(rate, this.rounding, this.dp); + const decimalIndex = formatted.indexOf('.'); + return decimalIndex === -1 ? formatted : formatted.substring(0, decimalIndex); + } + + getDecimalPart(rate: number): string { + const formatted = this.feeRoundingPipe.transform(rate, this.rounding, this.dp); + const decimalIndex = formatted.indexOf('.'); + return decimalIndex === -1 ? ' ' : formatted.substring(decimalIndex) + ' '; + } } diff --git a/frontend/src/app/shared/pipes/fee-rounding/fee-rounding.pipe.ts b/frontend/src/app/shared/pipes/fee-rounding/fee-rounding.pipe.ts index e2a6e9968..1e7a51e9b 100644 --- a/frontend/src/app/shared/pipes/fee-rounding/fee-rounding.pipe.ts +++ b/frontend/src/app/shared/pipes/fee-rounding/fee-rounding.pipe.ts @@ -10,16 +10,16 @@ export class FeeRoundingPipe implements PipeTransform { @Inject(LOCALE_ID) private locale: string, ) {} - transform(fee: number, rounding = null): string { + transform(fee: number, rounding = null, dp = 3): string { if (rounding) { return formatNumber(fee, this.locale, rounding); } - if (fee >= 100) { - return formatNumber(fee, this.locale, '1.0-0') - } else if (fee < 10) { - return formatNumber(fee, this.locale, '1.2-2') + if (fee >= Math.pow(10, (dp || 3) - 1)) { + return formatNumber(fee, this.locale, '1.0-0'); + } else if (fee < Math.pow(10, (dp || 3) - 2)) { + return formatNumber(fee, this.locale, '1.2-2'); } - return formatNumber(fee, this.locale, '1.1-1') + return formatNumber(fee, this.locale, '1.1-1'); } } diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index 6ac11ca90..146c4c9b6 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -279,6 +279,7 @@ import { GithubLogin } from '@components/github-login.component/github-login.com ShortenStringPipe, CapAddressPipe, AmountShortenerPipe, + FeeRoundingPipe, ], exports: [ MenuComponent,