Compare commits

...

44 Commits

Author SHA1 Message Date
Mononaut
126b4ccb09 Refactor pool logo component & fallback logic 2024-07-10 15:50:11 +00:00
softsimon
fed3012449 prevent goggles from becoming small or move with many filters activated 2024-07-10 23:26:34 +09:00
softsimon
10de603ee7 use default link color for top up link 2024-07-10 23:16:28 +09:00
softsimon
685c1c9fb2 Merge pull request #5308 from mempool/revert-5306-mononaut/selected-block-pool
Revert "align block arrows & reposition selected block pool tag"
2024-07-10 21:51:17 +09:00
softsimon
d02a67766d Revert "align block arrows & reposition selected block pool tag" 2024-07-10 21:51:04 +09:00
wiz
7721fde7b6 Merge pull request #5306 from mempool/mononaut/selected-block-pool
align block arrows & reposition selected block pool tag
2024-07-10 21:33:02 +09:00
wiz
aa10d1233c Merge pull request #5304 from mempool/natsoni/fix-miner-tag-loading
Possibly fix miner tag loading on tracked transactions
2024-07-10 21:31:56 +09:00
orangesurf
ba79821aac 20240710 Update ToS and PP (#5307) 2024-07-10 21:29:02 +09:00
Mononaut
e054e1d5a3 align block arrows & reposition selected block pool tag 2024-07-10 08:15:29 +00:00
softsimon
565910f9f9 Merge pull request #5303 from mempool/mononaut/oob-8dp
always show out-of-band block fees to 8 decimal places
2024-07-10 13:24:53 +09:00
natsoni
2915be8fd6 Possibly fix miner tag loading on tracked transactions 2024-07-10 12:55:34 +09:00
Mononaut
ff25b8ff1e always show out-of-band block fees to 8 decimal places 2024-07-10 03:51:51 +00:00
softsimon
2d03ab6346 make arrow position more consistent
fixes #5180
2024-07-10 12:49:16 +09:00
wiz
a530b70f9f Merge pull request #5302 from mempool/simon/smaller-block-arrow
Smaller block arrow
2024-07-10 01:46:42 +09:00
softsimon
986d71d47f Smaller block arrow 2024-07-10 01:44:15 +09:00
wiz
79f4720516 Merge pull request #5299 from mempool/mononaut/services-api-config
services api endpoint config
2024-07-09 23:51:53 +09:00
wiz
6135b1db10 Merge pull request #5300 from mempool/simon/pool-search-icons
Icons to pool search
2024-07-09 23:51:05 +09:00
wiz
4269077d4b Merge pull request #5301 from mempool/natsoni/hide-standard-eta-timeline
Remove standard ETA from timeline
2024-07-09 23:50:47 +09:00
natsoni
da0df70ad2 Acc timeline: More similar color logic with RBF 2024-07-09 22:14:40 +09:00
softsimon
6253d3716d Icons to pool search 2024-07-09 21:52:19 +09:00
Mononaut
614432426a call services api directly, make endpoint configurable 2024-07-09 12:23:52 +00:00
softsimon
e51951c3ff Merge pull request #5298 from svrgnty/master
add seconds to address and transaction views
2024-07-09 21:02:34 +09:00
natsoni
b38bf0f7b6 Hide standard ETA data until proper ETA calculation gets implemented 2024-07-09 20:50:47 +09:00
svrgnty
503de93094 add seconds to address and transaction views 2024-07-09 13:10:48 +02:00
natsoni
58f3169712 Faster, synced chevron animation 2024-07-09 18:48:58 +09:00
natsoni
53da6549e2 Remove unused CSS 2024-07-09 18:48:33 +09:00
wiz
65046c4cb8 Change blockstream/electrs to mempool/electrs in README 2024-07-09 18:03:41 +09:00
Mononaut
9416fd25f4 [accelerator] tidy up chevron animation 2024-07-09 08:58:03 +00:00
Mononaut
adde1a86e4 [accelerator] fast track chevrons animation 2024-07-09 08:57:09 +00:00
wiz
8a96669260 Merge pull request #5296 from mempool/mononaut/disable-services-proxy
[ops] disable node services api proxy on production
2024-07-09 15:38:38 +09:00
Mononaut
92434d41a4 [ops] disable services api proxy on production 2024-07-09 06:27:26 +00:00
softsimon
2c81ebb637 Merge pull request #5294 from mempool/mononaut/acc-fee-graph-fixes
[accelerator] improve rendering of acceleration fee rate graph
2024-07-09 01:01:27 +09:00
wiz
7735da96f2 Merge pull request #5293 from mempool/simon/block-mining-pool-logos
Block pool logos [Test]
2024-07-09 00:10:06 +09:00
softsimon
d914df20ba updating miner tag on tx page 2024-07-09 00:01:20 +09:00
softsimon
852e2b2fa0 miner tag as texts instead of badges 2024-07-08 23:44:22 +09:00
Mononaut
9396a4bbae [accelerator] improve rendering of acceleration fee rate graph 2024-07-08 14:36:38 +00:00
wiz
bf95938be8 Merge branch 'master' into simon/block-mining-pool-logos 2024-07-08 23:06:28 +09:00
wiz
8d2e7bef7a Merge pull request #5287 from mempool/natsoni/acc-timeline-polish
Acceleration timeline polishing
2024-07-08 23:06:09 +09:00
wiz
6f31fb2a08 Merge branch 'master' into natsoni/acc-timeline-polish 2024-07-08 22:54:31 +09:00
wiz
34b5678199 Merge pull request #5292 from mempool/mononaut/high-fee-accelerations
[accelerator] hide modal for transactions near the top of the mempool
2024-07-08 22:53:53 +09:00
softsimon
432496d2a0 move logo into the badge 2024-07-08 22:53:03 +09:00
softsimon
c391a532de fix overflow 2024-07-08 22:29:49 +09:00
softsimon
eec6efcc22 Block pool logos 2024-07-08 21:45:57 +09:00
Mononaut
487d82eccf [accelerator] hide modal for transactions near the top of the mempool 2024-07-08 09:45:49 +00:00
35 changed files with 392 additions and 181 deletions

View File

@@ -333,7 +333,9 @@ class Server {
if (config.MEMPOOL_SERVICES.ACCELERATIONS) {
accelerationRoutes.initRoutes(this.app);
}
aboutRoutes.initRoutes(this.app);
if (!config.MEMPOOL.OFFICIAL) {
aboutRoutes.initRoutes(this.app);
}
}
healthCheck(): void {

3
contributors/svrgnty.txt Normal file
View File

@@ -0,0 +1,3 @@
I hereby accept the terms of the Contributor License Agreement in the CONTRIBUTING.md file of the mempool/mempool git repository as of July 9, 2024.
Signed: svrgnty

View File

@@ -40,6 +40,7 @@ __MAINNET_BLOCK_AUDIT_START_HEIGHT__=${MAINNET_BLOCK_AUDIT_START_HEIGHT:=0}
__TESTNET_BLOCK_AUDIT_START_HEIGHT__=${TESTNET_BLOCK_AUDIT_START_HEIGHT:=0}
__SIGNET_BLOCK_AUDIT_START_HEIGHT__=${SIGNET_BLOCK_AUDIT_START_HEIGHT:=0}
__ACCELERATOR__=${ACCELERATOR:=false}
__SERVICES_API__=${SERVICES_API:=false}
__PUBLIC_ACCELERATIONS__=${PUBLIC_ACCELERATIONS:=false}
__HISTORICAL_PRICE__=${HISTORICAL_PRICE:=true}
__ADDITIONAL_CURRENCIES__=${ADDITIONAL_CURRENCIES:=false}
@@ -69,6 +70,7 @@ export __MAINNET_BLOCK_AUDIT_START_HEIGHT__
export __TESTNET_BLOCK_AUDIT_START_HEIGHT__
export __SIGNET_BLOCK_AUDIT_START_HEIGHT__
export __ACCELERATOR__
export __SERVICES_API__
export __PUBLIC_ACCELERATIONS__
export __HISTORICAL_PRICE__
export __ADDITIONAL_CURRENCIES__

View File

@@ -25,5 +25,6 @@
"HISTORICAL_PRICE": true,
"ADDITIONAL_CURRENCIES": false,
"ACCELERATOR": false,
"PUBLIC_ACCELERATIONS": false
"PUBLIC_ACCELERATIONS": false,
"SERVICES_API": "https://mempool.space/api/v1/services"
}

View File

@@ -1,4 +1,4 @@
<div class="fee-graph" *ngIf="tx && estimate">
<div class="fee-graph" *ngIf="tx && estimate" #feeGraph>
<div class="column">
<ng-container *ngFor="let bar of bars">
<div class="bar {{ bar.class }}" [class.active]="bar.active" [style]="bar.style" (click)="onClick($event, bar);">

View File

@@ -1,20 +1,16 @@
import { Component, OnInit, Input, Output, OnChanges, EventEmitter, HostListener, Inject, LOCALE_ID } from '@angular/core';
import { StateService } from '../../services/state.service';
import { Outspend, Transaction, Vin, Vout } from '../../interfaces/electrs.interface';
import { Router } from '@angular/router';
import { ReplaySubject, merge, Subscription, of } from 'rxjs';
import { tap, switchMap } from 'rxjs/operators';
import { ApiService } from '../../services/api.service';
import { Component, Input, Output, OnChanges, EventEmitter, HostListener, OnInit, ViewChild, ElementRef, AfterViewInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
import { Transaction } from '../../interfaces/electrs.interface';
import { AccelerationEstimate, RateOption } from './accelerate-checkout.component';
interface GraphBar {
rate: number;
style: any;
style?: Record<string,string>;
class: 'tx' | 'target' | 'max';
label: string;
active?: boolean;
rateIndex?: number;
fee?: number;
height?: number;
}
@Component({
@@ -22,7 +18,7 @@ interface GraphBar {
templateUrl: './accelerate-fee-graph.component.html',
styleUrls: ['./accelerate-fee-graph.component.scss'],
})
export class AccelerateFeeGraphComponent implements OnInit, OnChanges {
export class AccelerateFeeGraphComponent implements OnInit, AfterViewInit, OnChanges, OnDestroy {
@Input() tx: Transaction;
@Input() estimate: AccelerationEstimate;
@Input() showEstimate = false;
@@ -30,13 +26,37 @@ export class AccelerateFeeGraphComponent implements OnInit, OnChanges {
@Input() maxRateIndex: number = 0;
@Output() setUserBid = new EventEmitter<{ fee: number, index: number }>();
@ViewChild('feeGraph')
container: ElementRef<HTMLDivElement>;
height: number;
observer: ResizeObserver;
stopResizeLoop = false;
bars: GraphBar[] = [];
tooltipPosition = { x: 0, y: 0 };
constructor(
private cd: ChangeDetectorRef,
) {}
ngOnInit(): void {
this.initGraph();
}
ngAfterViewInit(): void {
if (ResizeObserver) {
this.observer = new ResizeObserver(entries => {
for (const entry of entries) {
this.height = entry.contentRect.height;
this.initGraph();
}
});
this.observer.observe(this.container.nativeElement);
} else {
this.startResizeFallbackLoop();
}
}
ngOnChanges(): void {
this.initGraph();
}
@@ -45,44 +65,61 @@ export class AccelerateFeeGraphComponent implements OnInit, OnChanges {
if (!this.tx || !this.estimate) {
return;
}
const hasNextBlockRate = (this.estimate.nextBlockFee > this.estimate.txSummary.effectiveFee);
const numBars = hasNextBlockRate ? 4 : 3;
const maxRate = Math.max(...this.maxRateOptions.map(option => option.rate));
const baseRate = this.estimate.txSummary.effectiveFee / this.estimate.txSummary.effectiveVsize;
const baseHeight = baseRate / maxRate;
const bars: GraphBar[] = this.maxRateOptions.slice().reverse().map(option => {
return {
rate: option.rate,
style: this.getStyle(option.rate, maxRate, baseHeight),
class: 'max',
label: this.showEstimate ? $localize`maximum` : $localize`:@@25fbf6e80a945703c906a5a7d8c92e8729c7ab21:accelerated`,
active: option.index === this.maxRateIndex,
rateIndex: option.index,
fee: option.fee,
}
});
if (this.estimate.nextBlockFee > this.estimate.txSummary.effectiveFee) {
let baseHeight = Math.max(this.height - (numBars * 30), this.height * (baseRate / maxRate));
const bars: GraphBar[] = [];
let lastHeight = 0;
if (hasNextBlockRate) {
lastHeight = Math.max(lastHeight + 30, (this.height * ((this.estimate.targetFeeRate - baseRate) / maxRate)));
bars.push({
rate: this.estimate.targetFeeRate,
style: this.getStyle(this.estimate.targetFeeRate, maxRate, baseHeight),
height: lastHeight,
class: 'target',
label: $localize`:@@bdf0e930eb22431140a2eaeacd809cc5f8ebd38c:Next Block`.toLowerCase(),
fee: this.estimate.nextBlockFee - this.estimate.txSummary.effectiveFee
});
}
this.maxRateOptions.forEach((option, index) => {
lastHeight = Math.max(lastHeight + 30, (this.height * ((option.rate - baseRate) / maxRate)));
bars.push({
rate: option.rate,
height: lastHeight,
class: 'max',
label: this.showEstimate ? $localize`maximum` : $localize`:@@25fbf6e80a945703c906a5a7d8c92e8729c7ab21:accelerated`,
active: option.index === this.maxRateIndex,
rateIndex: option.index,
fee: option.fee,
})
})
bars.reverse();
baseHeight = this.height - lastHeight;
for (const bar of bars) {
bar.style = this.getStyle(bar.height, baseHeight);
}
bars.push({
rate: baseRate,
style: this.getStyle(baseRate, maxRate, 0),
style: this.getStyle(baseHeight, 0),
height: baseHeight,
class: 'tx',
label: '',
fee: this.estimate.txSummary.effectiveFee,
});
this.bars = bars;
this.cd.detectChanges();
}
getStyle(rate, maxRate, base) {
const top = (rate / maxRate);
getStyle(height: number, base: number): Record<string,string> {
return {
height: `${(top - base) * 100}%`,
bottom: base ? `${base * 100}%` : '0',
height: `${height}px`,
bottom: base ? `${base}px` : '0',
}
}
@@ -96,4 +133,20 @@ export class AccelerateFeeGraphComponent implements OnInit, OnChanges {
onPointerMove(event) {
this.tooltipPosition = { x: event.offsetX, y: event.offsetY };
}
startResizeFallbackLoop(): void {
if (this.stopResizeLoop) {
return;
}
requestAnimationFrame(() => {
this.height = this.container?.nativeElement?.clientHeight || 0;
this.initGraph();
this.startResizeFallbackLoop();
});
}
ngOnDestroy(): void {
this.stopResizeLoop = true;
this.observer.disconnect();
}
}

View File

@@ -45,9 +45,9 @@
<div class="interval-spacer">
<div class="acc-to-confirmed"></div>
</div>
<div class="node mined" [id]="'confirmed'" >
<div class="node selected" [id]="'confirmed'">
<div class="acc-to-confirmed left" ></div>
<div class="shape-border mined-selected">
<div class="shape-border">
<div class="shape"></div>
</div>
<div class="status"><span class="badge badge-success" i18n="transaction.rbf.mined">Mined</span></div>
@@ -61,7 +61,7 @@
</div>
} @else if (acceleratedETA) { <!-- Not yet accelerated; to be shown only in acceleration checkout -->
} @else if (standardETA) { <!-- Accelerated -->
<div class="acceleration-timeline box">
<div class="acceleration-timeline box lower-padding">
<div class="timeline-wrapper">
<div class="timeline">
<div class="intervals">
@@ -71,7 +71,7 @@
<div class="interval">
<div class="interval-time">
@if (eta) {
~<app-time [time]="eta?.wait / 1000"></app-time> <span *ngIf="accelerateRatio > 1" class="compare"> ({{ accelerateRatio }}x faster)</span>
~<app-time [time]="eta?.wait / 1000"></app-time> <!-- <span *ngIf="accelerateRatio > 1" class="compare"> ({{ accelerateRatio }}x faster)</span> -->
}
</div>
</div>
@@ -81,13 +81,12 @@
<div class="node-spacer"></div>
<div class="interval-spacer"></div>
<div class="node">
<div class="acc-to-confirmed loading right"></div>
<div class="acc-to-confirmed right go-faster"></div>
</div>
<div class="interval-spacer">
<div class="acc-to-confirmed loading"></div>
</div>
<div class="node" [id]="'confirmed'">
<div class="acc-to-confirmed loading left"></div>
<div class="acc-to-confirmed left go-faster"></div>
<div class="shape-border waiting">
<div class="shape animate"></div>
</div>
@@ -106,7 +105,8 @@
<div class="node-spacer"></div>
<div class="interval">
<div class="interval-time">
~<app-time [time]="standardETA / 1000 - now"></app-time>
<!-- ~<app-time [time]="standardETA / 1000 - now"></app-time> -->
-
</div>
</div>
<div class="node-spacer"></div>
@@ -125,11 +125,11 @@
<div class="interval-spacer">
<div class="seen-to-acc"></div>
</div>
<div class="node" [id]="'accelerated'">
<div class="node accelerated" [id]="'accelerated'">
<div class="seen-to-acc left"></div>
<div class="seen-to-acc right"></div>
<div class="shape-border accelerated-selected">
<div class="shape accelerating"></div>
<div class="shape-border">
<div class="shape"></div>
<div class="connector down loading"></div>
</div>
<div class="time" style="margin-top: 3px;">

View File

@@ -1,7 +1,10 @@
.acceleration-timeline {
position: relative;
width: 100%;
padding: 0.5em 0 1em;
padding: 1em 0;
&.lower-padding {
padding: 0.5em 0 1em;
}
&::after, &::before {
content: '';
@@ -52,7 +55,7 @@
.interval, .interval-spacer {
width: 8em;
min-width: 5em;
min-width: 8em;
max-width: 8em;
height: 32px;
display: flex;
@@ -112,8 +115,20 @@
background: var(--tertiary);
border-radius: 5px;
&.loading {
animation: acceleratePulse 1s infinite;
&.go-faster {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='10'%3E%3Cpath style='fill:%239339f4;' d='M 0,0 5,5 0,10 Z'/%3E%3Cpath style='fill:%23653b9c;' d='M 0,0 10,0 15,5 10,10 0,10 5,5 Z'/%3E%3Cpath style='fill:%239339f4;' d='M 10,0 20,0 20,10 10,10 15,5 Z'/%3E%3C/svg%3E%0A"); background-size: 20px 10px;
border-radius: 0;
&.right {
left: calc(50% + 5px);
margin-right: calc(-4em + 5px);
animation: goFasterRight 0.8s infinite linear;
}
&.left {
right: calc(50% + 5px);
margin-left: calc(-4em + 5px);
animation: goFasterLeft 0.8s infinite linear;
}
}
&.left {
@@ -123,28 +138,6 @@
left: 50%;
}
}
.connector {
position: absolute;
height: 88px;
width: 10px;
left: -5px;
top: -73px;
transform: translateX(120%);
background: var(--tertiary);
&.down {
border-top-left-radius: 10px;
}
&.up {
border-top-right-radius: 10px;
}
&.loading {
animation: acceleratePulse 1s infinite;
}
}
}
.nodes {
@@ -159,16 +152,17 @@
margin-bottom: -8px;
transform: translateY(-50%);
border-radius: 50%;
padding: 2px;
cursor: pointer;
padding: 4px;
background: transparent;
.shape {
position: relative;
width: 100%;
height: 100%;
border-radius: 50%;
background: white;
&.accelerating {
animation: acceleratePulse 1s infinite;
}
z-index: 1;
}
&.waiting {
@@ -176,17 +170,41 @@
background: var(--grey);
}
}
&.accelerated-selected {
.shape {
background: var(--tertiary);
.connector {
position: absolute;
z-index: 0;
height: 88px;
width: 10px;
left: -5px;
top: -73px;
transform: translateX(120%);
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='20'%3E%3Cpath style='fill:%239339f4;' d='M 0,20 5,15 10,20 Z'/%3E%3Cpath style='fill:%23653b9c;' d='M 0,20 5,15 10,20 10,10 5,5 0,10 Z'/%3E%3Cpath style='fill:%239339f4;' d='M 0,10 5,5 10,10 10,0 0,0 Z'/%3E%3C/svg%3E%0A"); // linear-gradient(135deg, var(--tertiary) 34%, transparent 34%),
background-size: 10px 20px;
&.down {
border-top-left-radius: 10px;
}
&.up {
border-top-right-radius: 10px;
}
&.loading {
animation: goFasterUp 0.8s infinite linear;
}
}
}
&.accelerated {
.shape-border {
animation: acceleratePulse 0.4s infinite;
}
}
&.mined-selected {
.shape {
background: var(--success);
}
&.selected {
.shape-border {
background: var(--mainnet-alt);
}
}
@@ -220,3 +238,18 @@
50% { background-color: var(--mainnet-alt) }
100% { background-color: var(--tertiary) }
}
@keyframes goFasterUp {
0% { background-position-y: 0; }
100% { background-position-y: -40px; }
}
@keyframes goFasterLeft {
0% { background-position: left 0px bottom 0px }
100% { background-position: left 40px bottom 0px; }
}
@keyframes goFasterRight {
0% { background-position: right 0 bottom 0px; }
100% { background-position: right -40px bottom 0px; }
}

View File

@@ -28,14 +28,16 @@ export class AccelerationTimelineComponent implements OnInit, OnChanges {
ngOnChanges(changes): void {
this.now = Math.floor(new Date().getTime() / 1000);
if (changes?.eta?.currentValue || changes?.standardETA?.currentValue || changes?.acceleratedETA?.currentValue) {
if (changes?.eta?.currentValue) {
if (changes?.acceleratedETA?.currentValue) {
this.accelerateRatio = Math.floor((Math.floor(changes.eta.currentValue.time / 1000) - this.now) / (Math.floor(changes.acceleratedETA.currentValue / 1000) - this.now));
} else if (changes?.standardETA?.currentValue) {
this.accelerateRatio = Math.floor((Math.floor(changes.standardETA.currentValue / 1000) - this.now) / (Math.floor(changes.eta.currentValue.time / 1000) - this.now));
}
}
}
// Hide standard ETA while we don't have a proper standard ETA calculation, see https://github.com/mempool/mempool/issues/65
// if (changes?.eta?.currentValue || changes?.standardETA?.currentValue || changes?.acceleratedETA?.currentValue) {
// if (changes?.eta?.currentValue) {
// if (changes?.acceleratedETA?.currentValue) {
// this.accelerateRatio = Math.floor((Math.floor(changes.eta.currentValue.time / 1000) - this.now) / (Math.floor(changes.acceleratedETA.currentValue / 1000) - this.now));
// } else if (changes?.standardETA?.currentValue) {
// this.accelerateRatio = Math.floor((Math.floor(changes.standardETA.currentValue / 1000) - this.now) / (Math.floor(changes.eta.currentValue.time / 1000) - this.now));
// }
// }
// }
}
}

View File

@@ -33,6 +33,7 @@
.menu-toggle {
width: 3em;
min-width: 3em;
height: 1.8em;
padding: 0px 1px;
opacity: 0;
@@ -42,6 +43,7 @@
border: none;
border-radius: 0.35em;
pointer-events: all;
align-self: normal;
}
.filter-menu {

View File

@@ -181,8 +181,8 @@
<tr *ngIf="network !== 'liquid' && network !== 'liquidtestnet'">
<td i18n="block.miner">Miner</td>
<td *ngIf="stateService.env.MINING_DASHBOARD">
<a placement="bottom" [routerLink]="['/mining/pool' | relativeUrl, block.extras.pool.slug]" class="badge"
[class]="block.extras.pool.slug === 'unknown' ? 'badge-secondary' : 'badge-primary'">
<a placement="bottom" [routerLink]="['/mining/pool' | relativeUrl, block.extras.pool.slug]" class="badge" style="color: #FFF;padding:0;">
<app-pool-logo [pool]="block.extras.pool" style="position: relative; top: -1px; margin-right: 2px;"></app-pool-logo>
{{ block.extras.pool.name }}
</a>
</td>
@@ -411,7 +411,7 @@
<td class="text-wrap">
<app-amount [satoshis]="block.extras.totalFees" digitsInfo="1.2-3" [noFiat]="true"></app-amount>
<span *ngIf="oobFees" class="oobFees" i18n-ngbTooltip="Acceleration Fees" ngbTooltip="Acceleration fees paid out-of-band">
<app-amount [satoshis]="oobFees" digitsInfo="1.2-8" [noFiat]="true" [addPlus]="true"></app-amount>
<app-amount [satoshis]="oobFees" digitsInfo="1.8-8" [noFiat]="true" [addPlus]="true"></app-amount>
</span>
<span *ngIf="blockAudit.feeDelta" class="difference" [class.positive]="blockAudit.feeDelta <= 0" [class.negative]="blockAudit.feeDelta > 0">
{{ blockAudit.feeDelta < 0 ? '+' : '' }}{{ (-blockAudit.feeDelta * 100) | amountShortener: 2 }}%

View File

@@ -272,3 +272,11 @@ h1 {
}
}
}
.pool-logo {
width: 15px;
height: 15px;
position: relative;
top: -1px;
margin-right: 2px;
}

View File

@@ -59,10 +59,11 @@
<app-time kind="since" [time]="block.timestamp" [fastRender]="true" [precision]="1" minUnit="minute"></app-time></div>
</ng-container>
</div>
<div class="animated" [class]="markHeight === block.height ? 'hide' : 'show'" *ngIf="block.extras?.pool != undefined && showPools">
<a [attr.data-cy]="'bitcoin-block-' + offset + '-index-' + i + '-pool'" class="badge badge-primary"
[routerLink]="[('/mining/pool/' + block.extras.pool.slug) | relativeUrl]">
{{ block.extras.pool.name}}</a>
<div class="animated" *ngIf="block.extras?.pool != undefined && showPools">
<a [attr.data-cy]="'bitcoin-block-' + offset + '-index-' + i + '-pool'" class="badge" [routerLink]="[('/mining/pool/' + block.extras.pool.slug) | relativeUrl]">
<app-pool-logo [pool]="block.extras.pool" style="position: relative; top: -1px; margin-right: 2px;"></app-pool-logo>
{{ block.extras.pool.name}}
</a>
</div>
</div>
</ng-container>
@@ -85,7 +86,7 @@
</ng-template>
</div>
<div [hidden]="!arrowVisible" id="arrow-up" [style.transition]="arrowTransition"
[ngStyle]="{'left': arrowLeftPx + 'px' }"></div>
[ngStyle]="{'left': arrowLeftPx + 8 + 'px' }"></div>
</div>
<ng-template #loadingBlocksTemplate>

View File

@@ -125,12 +125,12 @@
#arrow-up {
position: relative;
left: calc(var(--block-size) * 0.6);
top: calc(var(--block-size) * 1.12);
top: calc(var(--block-size) * 1.28);
width: 0;
height: 0;
border-left: calc(var(--block-size) * 0.3) solid transparent;
border-right: calc(var(--block-size) * 0.3) solid transparent;
border-bottom: calc(var(--block-size) * 0.3) solid var(--fg);
border-left: calc(var(--block-size) * 0.2) solid transparent;
border-right: calc(var(--block-size) * 0.2) solid transparent;
border-bottom: calc(var(--block-size) * 0.2) solid var(--fg);
}
.flashing {
@@ -157,17 +157,20 @@
position: relative;
top: 15px;
z-index: 101;
color: #FFF;
}
.pool-logo {
width: 15px;
height: 15px;
position: relative;
top: -1px;
margin-right: 2px;
}
.animated {
transition: all 0.15s ease-in-out;
}
.show {
opacity: 1;
}
.hide {
opacity: 0.4;
pointer-events : none;
white-space: nowrap;
}
.time-ltr {

View File

@@ -14,8 +14,7 @@
}
.blockchain-wrapper {
height: 250px;
height: 260px;
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+/Edge */
@@ -57,7 +56,7 @@
color: var(--fg);
font-size: 0.8rem;
position: absolute;
bottom: -1.8em;
bottom: -2.2em;
left: 1px;
transform: translateX(-50%);
background: none;

View File

@@ -32,15 +32,13 @@
<td *ngIf="isMempoolModule" class="pool text-left" [ngClass]="{'widget': widget, 'legacy': !isMempoolModule}">
<div *ngIf="indexingAvailable" class="tooltip-custom">
<a class="clear-link" [routerLink]="['/mining/pool' | relativeUrl, block.extras.pool.slug]">
<img width="22" height="22" src="{{ block.extras.pool['logo'] }}"
onError="this.onerror=null; this.src = '/resources/mining-pools/default.svg'" [alt]="'Logo of ' + block.extras.pool.name + ' mining pool'">
<app-pool-logo [pool]="block.extras.pool" [width]="22" [height]="22"></app-pool-logo>
<span class="pool-name">{{ block.extras.pool.name }}</span>
</a>
<span *ngIf="!widget" class="tooltiptext badge badge-secondary scriptmessage">{{ block.extras.coinbaseRaw | hex2ascii }}</span>
</div>
<div *ngIf="!indexingAvailable" class="tooltip-custom">
<img width="22" height="22" src="{{ block.extras.pool['logo'] }}"
onError="this.src = '/resources/mining-pools/default.svg'" [alt]="'Logo of ' + block.extras.pool.name + ' mining pool'">
<app-pool-logo [pool]="block.extras.pool" [width]="22" [height]="22"></app-pool-logo>
<span class="pool-name">{{ block.extras.pool.name }}</span>
<span *ngIf="!widget" class="tooltiptext badge badge-secondary scriptmessage">{{ block.extras.coinbaseRaw | hex2ascii }}</span>
</div>

View File

@@ -126,12 +126,6 @@ export class BlocksList implements OnInit {
this.lastBlockHeight = Math.max(...blocks.map(o => o.height));
}),
map(blocks => {
if (this.stateService.env.BASE_MODULE === 'mempool') {
for (const block of blocks) {
// @ts-ignore: Need to add an extra field for the template
block.extras.pool.logo = `/resources/mining-pools/` + block.extras.pool.slug + '.svg';
}
}
if (this.widget) {
return blocks.slice(0, 6);
}

View File

@@ -114,7 +114,7 @@
#arrow-up {
position: relative;
right: calc(var(--block-size) * 0.6);
top: calc(var(--block-size) * 1.12);
top: calc(var(--block-size) * 1.20);
width: 0;
height: 0;
border-left: calc(var(--block-size) * 0.3) solid transparent;

View File

@@ -102,7 +102,7 @@
<tr *ngFor="let pool of miningStats.pools">
<td class="d-none d-md-table-cell">{{ pool.rank }}</td>
<td class="text-right">
<img width="25" height="25" src="{{ pool.logo }}" [alt]="pool.name + ' mining pool logo'" onError="this.onerror=null; this.src = '/resources/mining-pools/default.svg'">
<app-pool-logo [pool]="pool" [width]="25" [height]="25"></app-pool-logo>
</td>
<td class="pool-name"><a [routerLink]="[('/mining/pool/' + pool.slug) | relativeUrl]">{{ pool.name }}</a></td>
<td class="" *ngIf="this.miningWindowPreference === '24h'">{{ pool.lastEstimatedHashrate | number: '1.2-2' }} {{

View File

@@ -5,8 +5,7 @@
<!-- Pool overview -->
<div *ngIf="poolStats$ | async as poolStats; else loadingMain">
<div style="display:flex" class="mb-3">
<img width="50" height="50" src="{{ poolStats['logo'] }}" [alt]="poolStats.pool.name + ' mining pool logo'"
onError="this.onerror=null; this.src = '/resources/mining-pools/default.svg'" class="mr-3">
<app-pool-logo [pool]="poolStats.pool" [width]="50" [height]="50" class="mr-3"></app-pool-logo>
<h1 class="m-0 pt-1 pt-md-0">{{ poolStats.pool.name }}</h1>
</div>

View File

@@ -5,29 +5,33 @@
<br><br>
<h2>Privacy Policy</h2>
<h6>Updated: November 23, 2023</h6>
<h6>Updated: July 10, 2024</h6>
<br><br>
<div class="text-left">
<p *ngIf="officialMempoolSpace">The <a href="https://mempool.space/">mempool.space</a> website, the <a href="https://liquid.network/">liquid.network</a> website, their associated API services, and related network and server infrastructure (collectively, the "Website") are operated by Mempool Space K.K. in Japan ("Mempool", "We", or "Us") and self-hosted from <a href="https://bgp.tools/as/142052#connectivity">AS142052</a>.</p>
<h4>USE YOUR OWN SELF-HOSTED MEMPOOL EXPLORER</h4>
<p>For maximum privacy, we recommend that you use your own self-hosted instance of The Mempool Open Source Project&reg;; on your own hardware. You can easily install your own self-hosted instance of this website on a Raspberry Pi using a one-click installation method maintained by various Bitcoin fullnode distributions such as Umbrel, RaspiBlitz, MyNode, and RoninDojo. See our project's GitHub page for more details about self-hosting this website. By using your own self-hosted instance you will have maximum security, privacy and freedom.</p>
<br>
<p *ngIf="officialMempoolSpace">The <a href="https://mempool.space/">mempool.space</a> website, the <a href="https://liquid.network/">liquid.network</a> website, the <a href="https://bitcoin.gob.sv/">bitcoin.gob.sv</a> website, their associated API services, and related network and server infrastructure (collectively, the "Website") are operated by Mempool Space K.K. in Japan ("Mempool", "We", or "Us") and self-hosted from <a href="https://bgp.tools/as/142052#connectivity">AS142052</a>.</p>
<p *ngIf="!officialMempoolSpace">This website and its API service (collectively, the "Website") are operated by a member of the Bitcoin community ("We" or "Us"). Mempool Space K.K. in Japan ("Mempool") has no affiliation with the operator of this Website, and does not sponsor or endorse the information provided herein.</p>
<br>
<h5>By accessing this Website, you agree to the following Privacy Policy:</h5>
<br>
<h4>TRUSTED THIRD PARTIES ARE SECURITY HOLES</h4>
<h4>General</h4>
<p>Out of respect for the Bitcoin community, this website does not use any third-party analytics, third-party trackers, or third-party cookies, and we do not share any private user data with third-parties. Additionally, to mitigate the risk of surveillance by malicious third-parties, we self-host this website on our own hardware and network infrastructure, so there are no "hosting companies" or "cloud providers" involved with the operation of this website.</p>
<p *ngIf="officialMempoolSpace">Out of respect for the Bitcoin community, this Website does not use any third-party analytics, third-party trackers, or third-party cookies, and we do not share any private user data with third-parties. Additionally, to mitigate the risk of surveillance by malicious third-parties, we self-host this Website on our own hardware and network infrastructure, so there are no "hosting companies" or "cloud providers" involved with the operation of this Website.</p>
<br>
<h4>TRUSTED FIRST PARTIES ARE ALSO SECURITY HOLES</h4>
<p>Out of respect for the Bitcoin community, this website does not use any first-party cookies, except to store your preferred language setting (if any). However, we do use minimal first-party analytics and logging as needed for the operation of this website, as follows:</p>
<p>Out of respect for the Bitcoin community, this Website does not use any first-party cookies, except to store your preferred language setting (if any). However, we do use minimal first-party analytics and logging as needed for the operation of this Website, as follows:</p>
<ul>
@@ -41,35 +45,49 @@
<br>
<h4>TRUST YOUR OWN SELF-HOSTED MEMPOOL EXPLORER</h4>
<h4>USING MEMPOOL ACCELERATOR&trade;</h4>
<p>For maximum privacy, we recommend that you use your own self-hosted instance of The Mempool Open Source Project&reg; on your own hardware. You can easily install your own self-hosted instance of this website on a Raspberry Pi using a one-click installation method maintained by various Bitcoin fullnode distributions such as Umbrel, RaspiBlitz, MyNode, and RoninDojo. See our project's GitHub page for more details about self-hosting this website.</p>
<p *ngIf="officialMempoolSpace">If you use Mempool Accelerator&trade; your acceleration request will be sent to us and relayed to Mempool's mining pool partners. We will store the TXID of the transactions you accelerate with us. We share this information with our mining pool partners, and publicly display accelerated transaction details on our website and APIs. No personal information or account identifiers will be shared with any third party including mining pool partners.</p>
<p *ngIf="!officialMempoolSpace">If you click the accelerate button on a transaction you will load acceleration pricing information from Mempool. If you make an acceleration request, the TXID and your maximum bid will be sent to Mempool who will store and share this information with their mining pool partners, and publicly display accelerated transaction details on mempool.space and via Mempool's APIs. No personal information or account identifiers will be shared with any third party including mining pool partners.</p>
<br>
<ng-container *ngIf="officialMempoolSpace">
<h4>DONATING TO MEMPOOL.SPACE</h4>
<h4>SIGNING UP FOR AN ACCOUNT ON MEMPOOL.SPACE</h4>
<p>If you donate to mempool.space, your payment information and your Twitter identity (if provided) will be collected in a database, which may be used to publicly display the sponsor profiles on <a href="https://mempool.space/about">mempool.space/about</a>. Thank you for supporting The Mempool Open Source Project.</p>
<p>If you sign up for an account on mempool.space, we may collect the following:</p>
<br>
<ul>
<h4>SIGNING UP FOR AN ACCOUNT ON MEMPOOL.SPACE</h4>
<li>Your e-mail address and/or country; we may use this information to manage your user account, for billing purposes, or to update you about our services. We will not share this with any third-party, except as necessary for our fiat payment processor (see "Payments" below).</li>
<p>If you sign up for an account on mempool.space, we may collect the following:</p>
<li>If you connect your X (fka Twitter) account, we may store your X identity, e-mail address, and profile photo. We may publicly display your profile photo or link to your profile on our website, if you sponsor The Mempool Open Source Project&reg;, claim your Lightning node, or other such use cases.</li>
<ol>
<li>If you sign up for a subscription to Mempool Enterprise&trade; we also collect your company name which is not shared with any third-party.</li>
<li>If you provide your name, country, and/or e-mail address, we may use this information to manage your user account, for billing purposes, or to update you about our services. We will not share this with any third-party, except as detailed below if you sponsor The Mempool Open Source Project®, purchase a subscription to Mempool Enterprise®, or accelerate transactions using Mempool Accelerator™.</li>
<li>If you sign up for an account on mempool.space and use Mempool Accelerator&trade; Pro your accelerated transactions will be associated with your account for the purposes of accounting.</li>
<li>If you connect your Twitter account, we may store your Twitter identity, e-mail address, and profile photo. We may publicly display your profile photo or link to your profile on our website, if you sponsor The Mempool Open Source Project, claim your Lightning node, or other such use cases.</li>
</ul>
<li>If you make a credit card payment, we will process your payment using Square (Block, Inc.), and we will store details about the transaction in our database. Please see "Information we collect about customers" on Square's website at https://squareup.com/us/en/legal/general/privacy</li>
<br>
<li>If you make a Bitcoin or Liquid payment, we will process your payment using our self-hosted BTCPay Server instance and not share these details with any third-party.</li>
</ng-container>
<li>If you accelerate transactions using Mempool Accelerator™, we will store the TXID of your transactions you accelerate with us. We share this information with our mining pool partners, as well as publicly display accelerated transaction details on our website and APIs.</li>
<h4>PAYMENTS AND DONATIONS</h4>
</ol>
<p>If you make any payment to Mempool or donation to The Mempool Open Source Project&reg;, we may collect the following:</p>
<ul>
<li>Your e-mail address and/or country; we may use this information to manage your user account, for billing purposes, or to update you about our services. We will not share this with any third-party, except as necessary for our fiat payment processor.</li>
<li>If you make a payment using Bitcoin, we will process your payment using our self-hosted BTCPay Server instance. We will not share your payment details with any third-party. For payments made over the Lightning network, we may utilize third party LSPs / lightning liquidity providers.</li>
<li>If you make a payment using Fiat we will collect your payment details. We will share your payment details with our fiat payment processor Square (Block, Inc.),. - Please see "Information we collect about customers" on Square's website at https://squareup.com/us/en/legal/general/privacy.</li>
</ul>
<br>

View File

@@ -47,6 +47,7 @@
<div class="card-title" i18n="search.mining-pools">Mining Pools</div>
<ng-template ngFor [ngForOf]="results.pools" let-pool let-i="index">
<button (click)="clickItem(results.hashQuickMatch + results.addresses.length + i)" [class.active]="results.hashQuickMatch + results.addresses.length + i === activeIdx" [class.inactive]="!pool.active" type="button" role="option" class="dropdown-item">
<app-pool-logo [pool]="pool" style="position: relative; top: -1px; margin-right: 10px;"></app-pool-logo>
<ngb-highlight [result]="pool.name" [term]="results.searchText"></ngb-highlight>
</button>
</ng-template>

View File

@@ -5,7 +5,7 @@
<br /><br />
<h2>Terms of Service</h2>
<h6>Updated: August 02, 2021</h6>
<h6>Updated: July 10, 2024</h6>
<br><br>
@@ -67,6 +67,38 @@
</ng-container>
<h4>MEMPOOL ACCELERATOR&trade;</h4>
<p><a href="https://mempool.space/accelerator">Mempool Accelerator&trade;</a> enables members of the Bitcoin community to submit requests for transaction prioritization. </p>
<ul>
<li>Mempool will use reasonable commercial efforts to relay user acceleration requests to Mempool's mining pool partners, but it is at the discretion of Mempool and Mempool's mining pool partners to accept acceleration requests. </li>
<br>
<li>Acceleration requests cannot be canceled by the user once submitted. </li>
<br>
<li>Mempool reserves the right to cancel acceleration requests for any reason, including but not limited to the ejection of an accelerated transaction from Mempool's mempool. Canceled accelerations will not be refunded.</li>
<br>
<li>All acceleration payments and Mempool Accelerator&trade; account credit top-ups are non-refundable. </li>
<br>
<li>Mempool Accelerator&trade; account credit top-ups are prepayment for future accelerations and cannot be withdrawn or transferred.</li>
<br>
<li>Mempool does not provide acceleration services to persons in Cuba, Iran, North Korea, Russia, Syria, Crimea, Donetsk or Luhansk Regions of Ukraine.</li>
</ul>
<br>
<p>EOF</p>
</div>

View File

@@ -449,7 +449,7 @@
<tr>
<td i18n="block.timestamp">Timestamp</td>
<td>
&lrm;{{ tx.status.block_time * 1000 | date:'yyyy-MM-dd HH:mm' }}
&lrm;{{ tx.status.block_time * 1000 | date:'yyyy-MM-dd HH:mm:ss' }}
<div class="lg-inline">
<i class="symbol">(<app-time kind="since" [time]="tx.status.block_time" [fastRender]="true"></app-time>)</i>
</div>
@@ -676,9 +676,9 @@
<td class="td-width" i18n="block.miner">Miner</td>
@if (pool) {
<td class="wrap-cell">
<a placement="bottom" [routerLink]="['/mining/pool' | relativeUrl, pool.slug]" class="badge mr-1"
[class]="pool.slug === 'unknown' ? 'badge-secondary' : 'badge-primary'">
{{ pool.name }}
<a placement="bottom" [routerLink]="['/mining/pool' | relativeUrl, pool.slug]" class="badge" style="color: #FFF;padding:0;">
<app-pool-logo [pool]="pool" style="position: relative; top: -1px; margin-right: 2px;"></app-pool-logo>
{{ pool.name }}
</a>
</td>
} @else {

View File

@@ -324,4 +324,12 @@
.goggles-icon {
display: block;
width: 2.7em;
}
}
.pool-logo {
width: 15px;
height: 15px;
position: relative;
top: -1px;
margin-right: 2px;
}

View File

@@ -65,6 +65,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
txId: string;
txInBlockIndex: number;
mempoolPosition: MempoolPosition;
gotInitialPosition = false;
accelerationPositions: AccelerationPosition[];
isLoadingTx = true;
error: any = undefined;
@@ -335,7 +336,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
});
this.miningSubscription = this.fetchMiningInfo$.pipe(
filter((target) => target.txid === this.txId),
filter((target) => target.txid === this.txId && !this.pool),
tap(() => {
this.pool = null;
}),
@@ -432,9 +433,13 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
if (txPosition.position?.block > 0 && this.tx.weight < 4000) {
this.cashappEligible = true;
}
if (!this.gotInitialPosition && txPosition.position?.block === 0 && txPosition.position?.vsize < 750_000) {
this.accelerationFlowCompleted = true;
}
}
}
}
this.gotInitialPosition = true;
} else {
this.mempoolPosition = null;
this.accelerationPositions = null;
@@ -609,6 +614,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
block_hash: block.id,
block_time: block.timestamp,
};
this.pool = block.extras.pool;
this.txChanged$.next(true);
this.stateService.markBlock$.next({ blockHeight: block.height });
if (this.tx.acceleration || (this.accelerationInfo && ['accelerating', 'completed_provisional', 'completed'].includes(this.accelerationInfo.status))) {
@@ -880,6 +886,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
resetTransaction() {
this.firstLoad = false;
this.gotInitialPosition = false;
this.error = undefined;
this.tx = null;
this.txChanged$.next(true);

View File

@@ -6,7 +6,7 @@
<app-truncate [text]="tx.txid"></app-truncate>
</a>
<div>
<ng-template [ngIf]="tx.status.confirmed">&lrm;{{ tx.status.block_time * 1000 | date:'yyyy-MM-dd HH:mm' }}</ng-template>
<ng-template [ngIf]="tx.status.confirmed">&lrm;{{ tx.status.block_time * 1000 | date:'yyyy-MM-dd HH:mm:ss' }}</ng-template>
<ng-template [ngIf]="!tx.status.confirmed && tx.firstSeen">
<i><app-time kind="since" [time]="tx.firstSeen" [fastRender]="true" [showTooltip]="true"></app-time></i>
</ng-template>

View File

@@ -25,9 +25,6 @@ export interface IUser {
ogRank: number | null;
}
// Todo - move to config.json
const SERVICES_API_PREFIX = `/api/v1/services`;
@Injectable({
providedIn: 'root'
})
@@ -98,7 +95,7 @@ export class ServicesApiServices {
return of(null);
}
return this.httpClient.get<any>(`${SERVICES_API_PREFIX}/account`);
return this.httpClient.get<any>(`${this.stateService.env.SERVICES_API}/account`);
}
getUserMenuGroups$(): Observable<MenuGroup[]> {
@@ -107,7 +104,7 @@ export class ServicesApiServices {
return of(null);
}
return this.httpClient.get<MenuGroup[]>(`${SERVICES_API_PREFIX}/account/menu`);
return this.httpClient.get<MenuGroup[]>(`${this.stateService.env.SERVICES_API}/account/menu`);
}
logout$(): Observable<any> {
@@ -117,59 +114,59 @@ export class ServicesApiServices {
}
localStorage.removeItem('auth');
return this.httpClient.post(`${SERVICES_API_PREFIX}/auth/logout`, {});
return this.httpClient.post(`${this.stateService.env.SERVICES_API}/auth/logout`, {});
}
getJWT$() {
return this.httpClient.get<any>(`${SERVICES_API_PREFIX}/auth/getJWT`);
return this.httpClient.get<any>(`${this.stateService.env.SERVICES_API}/auth/getJWT`);
}
getServicesBackendInfo$(): Observable<IBackendInfo> {
return this.httpClient.get<IBackendInfo>(`${SERVICES_API_PREFIX}/version`);
return this.httpClient.get<IBackendInfo>(`${this.stateService.env.SERVICES_API}/version`);
}
estimate$(txInput: string) {
return this.httpClient.post<any>(`${SERVICES_API_PREFIX}/accelerator/estimate`, { txInput: txInput }, { observe: 'response' });
return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/accelerator/estimate`, { txInput: txInput }, { observe: 'response' });
}
accelerate$(txInput: string, userBid: number, accelerationUUID: string) {
return this.httpClient.post<any>(`${SERVICES_API_PREFIX}/accelerator/accelerate`, { txInput: txInput, userBid: userBid, accelerationUUID: accelerationUUID });
return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/accelerator/accelerate`, { txInput: txInput, userBid: userBid, accelerationUUID: accelerationUUID });
}
accelerateWithCashApp$(txInput: string, token: string, cashtag: string, referenceId: string, accelerationUUID: string) {
return this.httpClient.post<any>(`${SERVICES_API_PREFIX}/accelerator/accelerate/cashapp`, { txInput: txInput, token: token, cashtag: cashtag, referenceId: referenceId, accelerationUUID: accelerationUUID });
return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/cashapp`, { txInput: txInput, token: token, cashtag: cashtag, referenceId: referenceId, accelerationUUID: accelerationUUID });
}
getAccelerations$(): Observable<Acceleration[]> {
return this.httpClient.get<Acceleration[]>(`${SERVICES_API_PREFIX}/accelerator/accelerations`);
return this.httpClient.get<Acceleration[]>(`${this.stateService.env.SERVICES_API}/accelerator/accelerations`);
}
getAggregatedAccelerationHistory$(params: AccelerationHistoryParams): Observable<any> {
return this.httpClient.get<any>(`${SERVICES_API_PREFIX}/accelerator/accelerations/history/aggregated`, { params: { ...params }, observe: 'response' });
return this.httpClient.get<any>(`${this.stateService.env.SERVICES_API}/accelerator/accelerations/history/aggregated`, { params: { ...params }, observe: 'response' });
}
getAccelerationHistory$(params: AccelerationHistoryParams): Observable<Acceleration[]> {
return this.httpClient.get<Acceleration[]>(`${SERVICES_API_PREFIX}/accelerator/accelerations/history`, { params: { ...params } });
return this.httpClient.get<Acceleration[]>(`${this.stateService.env.SERVICES_API}/accelerator/accelerations/history`, { params: { ...params } });
}
getAccelerationHistoryObserveResponse$(params: AccelerationHistoryParams): Observable<any> {
return this.httpClient.get<any>(`${SERVICES_API_PREFIX}/accelerator/accelerations/history`, { params: { ...params }, observe: 'response'});
return this.httpClient.get<any>(`${this.stateService.env.SERVICES_API}/accelerator/accelerations/history`, { params: { ...params }, observe: 'response'});
}
getAccelerationStats$(params: AccelerationHistoryParams): Observable<AccelerationStats> {
return this.httpClient.get<AccelerationStats>(`${SERVICES_API_PREFIX}/accelerator/accelerations/stats`, { params: { ...params } });
return this.httpClient.get<AccelerationStats>(`${this.stateService.env.SERVICES_API}/accelerator/accelerations/stats`, { params: { ...params } });
}
setupSquare$(): Observable<{squareAppId: string, squareLocationId: string}> {
return this.httpClient.get<{squareAppId: string, squareLocationId: string}>(`${SERVICES_API_PREFIX}/square/setup`);
return this.httpClient.get<{squareAppId: string, squareLocationId: string}>(`${this.stateService.env.SERVICES_API}/square/setup`);
}
getFaucetStatus$() {
return this.httpClient.get<{ address?: string, min: number, max: number, code: 'ok' | 'faucet_not_available' | 'faucet_maximum_reached' | 'faucet_too_soon'}>(`${SERVICES_API_PREFIX}/testnet4/faucet/status`, { responseType: 'json' });
return this.httpClient.get<{ address?: string, min: number, max: number, code: 'ok' | 'faucet_not_available' | 'faucet_maximum_reached' | 'faucet_too_soon'}>(`${this.stateService.env.SERVICES_API}/testnet4/faucet/status`, { responseType: 'json' });
}
requestTestnet4Coins$(address: string, sats: number) {
return this.httpClient.get<{txid: string}>(`${SERVICES_API_PREFIX}/testnet4/faucet/request?address=${address}&sats=${sats}`, { responseType: 'json' });
return this.httpClient.get<{txid: string}>(`${this.stateService.env.SERVICES_API}/testnet4/faucet/request?address=${address}&sats=${sats}`, { responseType: 'json' });
}
generateBTCPayAcceleratorInvoice$(txid: string, sats: number): Observable<any> {
@@ -177,14 +174,14 @@ export class ServicesApiServices {
product: txid,
amount: sats,
};
return this.httpClient.post<any>(`${SERVICES_API_PREFIX}/payments/bitcoin`, params);
return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/payments/bitcoin`, params);
}
retreiveInvoice$(invoiceId: string): Observable<any[]> {
return this.httpClient.get<any[]>(`${SERVICES_API_PREFIX}/payments/bitcoin/invoice?id=${invoiceId}`);
return this.httpClient.get<any[]>(`${this.stateService.env.SERVICES_API}/payments/bitcoin/invoice?id=${invoiceId}`);
}
getPaymentStatus$(orderId: string): Observable<any[]> {
return this.httpClient.get<any[]>(`${SERVICES_API_PREFIX}/payments/bitcoin/check?order_id=${orderId}`);
return this.httpClient.get<any[]>(`${this.stateService.env.SERVICES_API}/payments/bitcoin/check?order_id=${orderId}`);
}
}

View File

@@ -75,6 +75,7 @@ export interface Env {
ADDITIONAL_CURRENCIES: boolean;
GIT_COMMIT_HASH_MEMPOOL_SPACE?: string;
PACKAGE_JSON_VERSION_MEMPOOL_SPACE?: string;
SERVICES_API?: string;
customize?: Customization;
}
@@ -109,6 +110,7 @@ const defaultEnv: Env = {
'ACCELERATOR': false,
'PUBLIC_ACCELERATIONS': false,
'ADDITIONAL_CURRENCIES': false,
'SERVICES_API': 'https://mempool.space/api/v1/services',
};
@Injectable({

View File

@@ -6,4 +6,4 @@
</span>
}
<ng-template #lowBalance i18n="accelerator.low-balance">Your balance is too low.<br/>Please <a style="color:#105fb0" href="/services/accelerator/overview">top up your account</a>.</ng-template>
<ng-template #lowBalance i18n="accelerator.low-balance">Your balance is too low.<br/>Please <a class="top-up-link" href="/services/accelerator/overview">top up your account</a>.</ng-template>

View File

@@ -0,0 +1,11 @@
<img
#poolImg
class="pool-logo"
[width]="width"
[height]="height"
[style]="{ width: width + 'px', maxWidth: width + 'px', height: height + 'px' }"
[src]="'/resources/mining-pools/' + pool.slug + '.svg'"
onLoad="this.style.opacity = 1;"
onError="this.src = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjUuMTIgMi44IDE1Ljk4IDE4LjI3Ij4gPHBhdGggZD0iTTEyLjEwMSAyLjkwMDU1QzEyLjIxNDYgMi44MzIyNSAxMi4zNTA4IDIuODExOSAxMi40Nzk0IDIuODQzOTlMMTUuODc1NSAzLjY5MDcxQzE2LjEzMjQgMy43NTQ3NyAxNi4yOTI5IDQuMDA2ODggMTYuMjQ2NyA0LjI2MzcyQzE4LjYxMDggNS4zNzA4OCAyMC4zNzEzIDcuMTk0NjcgMjEuMDYwOSA5LjI0NjYyQzIxLjEzNDMgOS40NjQ3OSAyMS4wNDkyIDkuNzA0ODMgMjAuODU0OCA5LjgyODEyQzIwLjY2MDUgOS45NTE0MSAyMC40MDcxIDkuOTI2MSAyMC4yNDA5IDkuNzY2OEMxOS4wMzA1IDguNjA2MTcgMTcuNDQ2OSA3LjU5NTYxIDE1LjU5OTIgNi44NjU0MkwxNS41NzQ0IDYuOTY1MTNDMTUuNTQyMyA3LjA5MzggMTUuNDYwNCA3LjIwNDQ2IDE1LjM0NjcgNy4yNzI3NkMxNS4yMzMxIDcuMzQxMDYgMTUuMDk2OSA3LjM2MTQgMTQuOTY4MyA3LjMyOTMyTDE0LjQ4MzEgNy4yMDgzNkwxMy41NzU5IDEwLjg0N0MxMy44NDM5IDEwLjkxMzggMTQuMDA2OSAxMS4xODUxIDEzLjk0MDEgMTEuNDUzMUwxMS42NDE4IDIwLjY3MDlDMTEuNjA5OCAyMC43OTk2IDExLjUyNzkgMjAuOTEwMiAxMS40MTQyIDIwLjk3ODVDMTEuMzAwNSAyMS4wNDY4IDExLjE2NDQgMjEuMDY3MiAxMS4wMzU3IDIxLjAzNTFMOC42MDk5OSAyMC40MzAzQzguMzQyMDUgMjAuMzYzNSA4LjE3OSAyMC4wOTIxIDguMjQ1ODEgMTkuODI0MkwxMC41NDQxIDEwLjYwNjRDMTAuNTc2MSAxMC40Nzc3IDEwLjY1OCAxMC4zNjcgMTAuNzcxNyAxMC4yOTg3QzEwLjg4NTQgMTAuMjMwNCAxMS4wMjE1IDEwLjIxMDEgMTEuMTUwMiAxMC4yNDIyTDEyLjA1NzQgNi42MDM1NkwxMS41NzIyIDYuNDgyNTlDMTEuMzA0MyA2LjQxNTc5IDExLjE0MTIgNi4xNDQ0MyAxMS4yMDggNS44NzY0OUwxMS4yMzI5IDUuNzc2NzhDOS4yNTg2NiA1LjU1NDA2IDcuMzg1OTggNS43MDI4OCA1Ljc3MjM3IDYuMTU5NDNDNS41NTA5IDYuMjIyMDkgNS4zMTUyNyA2LjEyNTQ3IDUuMjAxNTUgNS45MjUzN0M1LjA4NzgyIDUuNzI1MjcgNS4xMjUzOSA1LjQ3MzM5IDUuMjkyNTUgNS4zMTUxOEM2Ljg2NDc4IDMuODI3MTMgOS4yNzU0OCAzLjA0MzMyIDExLjg4MjYgMy4xNzU2MkMxMS45MTk2IDMuMDYwOTEgMTEuOTk3IDIuOTYzMDIgMTIuMTAxIDIuOTAwNTVaIiBmaWxsPSIjYjRiNGI0Ij48L3BhdGg+IDwvc3ZnPg=='"
[alt]="'Logo of ' + pool.name + ' mining pool'"
>

View File

@@ -0,0 +1,6 @@
.pool-logo {
position: relative;
overflow: hidden;
opacity: 0;
color: transparent;
}

View File

@@ -0,0 +1,26 @@
import { Component, ElementRef, Input, OnChanges, SimpleChanges, ViewChild } from '@angular/core';
import { nextTick } from 'process';
@Component({
selector: 'app-pool-logo',
templateUrl: './pool-logo.component.html',
styleUrls: ['./pool-logo.component.scss']
})
export class PoolLogoComponent implements OnChanges{
@Input() pool: { slug: string, name: string };
@Input() width: number = 15;
@Input() height: number = 15;
@ViewChild('poolImg') img: ElementRef<HTMLImageElement>;
ngOnChanges(changes: SimpleChanges): void {
if (changes.pool) {
if (this.img?.nativeElement) {
this.img.nativeElement.style.opacity = '0';
setTimeout(() => {
this.img.nativeElement.style.opacity = '1';
}, 50);
}
}
}
}

View File

@@ -88,6 +88,7 @@ import { BtcComponent } from './components/btc/btc.component';
import { FeeRateComponent } from './components/fee-rate/fee-rate.component';
import { AddressTypeComponent } from './components/address-type/address-type.component';
import { TruncateComponent } from './components/truncate/truncate.component';
import { PoolLogoComponent } from './components/pool-logo/pool-logo.component';
import { SearchResultsComponent } from '../components/search-form/search-results/search-results.component';
import { TimestampComponent } from './components/timestamp/timestamp.component';
import { ConfirmationsComponent } from './components/confirmations/confirmations.component';
@@ -201,6 +202,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
FeeRateComponent,
AddressTypeComponent,
TruncateComponent,
PoolLogoComponent,
SearchResultsComponent,
TimestampComponent,
ConfirmationsComponent,
@@ -340,6 +342,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
FeeRateComponent,
AddressTypeComponent,
TruncateComponent,
PoolLogoComponent,
SearchResultsComponent,
TimestampComponent,
ConfirmationsComponent,

View File

@@ -8,7 +8,7 @@ You can also have the mempool.space team run a highly-performant and highly-avai
### Server Hardware
Mempool v2 is powered by [blockstream/electrs](https://github.com/Blockstream/electrs), which is a beast.
Mempool v3 is powered by [mempool/electrs](https://github.com/mempool/electrs), which is a beast.
I recommend a beefy server: