Merge pull request #6442 from mempool/mononaut/shill-accelerator
Some checks failed
Backend Integration Tests with MariaDB / Backend Integration Tests - node 24.13.0 (push) Has been cancelled
CI Pipeline for the Backend and Frontend / Backend (dev) - node 24.13.0 (push) Has been cancelled
CI Pipeline for the Backend and Frontend / Backend (prod) - node 24.13.0 (push) Has been cancelled
CI Pipeline for the Backend and Frontend / Cache assets for builds (push) Has been cancelled
CI Pipeline for the Backend and Frontend / Frontend (dev) - node 24.13.0 (push) Has been cancelled
CI Pipeline for the Backend and Frontend / Frontend (prod) - node 24.13.0 (push) Has been cancelled
CI Pipeline for the Backend and Frontend / E2E tests for liquid (push) Has been cancelled
CI Pipeline for the Backend and Frontend / E2E tests for mempool (push) Has been cancelled
CI Pipeline for the Backend and Frontend / E2E tests for testnet4 (push) Has been cancelled
CI Pipeline for the Backend and Frontend / Validate generated backend Docker JSON (push) Has been cancelled
Supply Chain Audit / Backend install-script audit (push) Has been cancelled
Supply Chain Audit / Frontend install-script audit (push) Has been cancelled

This commit is contained in:
wiz 2026-05-30 10:41:13 +09:00 committed by GitHub
commit 7d1bfca75b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 65 additions and 6 deletions

View file

@ -46,6 +46,11 @@
<ng-template #detailsRight>
<ng-container *ngTemplateOutlet="feeRow"></ng-container>
<ng-container *ngTemplateOutlet="feeRateRow"></ng-container>
@if (showAcceleratorSavingsMsg) {
<tr>
<td colspan="2" style="--bs-table-bg-type: var(--tertiary); white-space: normal; text-align: center;">Could have saved <app-fiat [blockConversion]="tx.price" [value]="acceleratorSavingsSats"></app-fiat> using <a href="https://mempool.space/enterprise#accelerator-pro" target="_blank" rel="noopener">Mempool Accelerator&reg; Pro</a></td>
</tr>
}
@if (!isLoadingTx && !tx?.status?.confirmed && isAcceleration) {
<ng-container *ngTemplateOutlet="acceleratingRow"></ng-container>
} @else {

View file

@ -1,11 +1,14 @@
import { Component, OnInit, Input, ChangeDetectionStrategy, Output, EventEmitter } from '@angular/core';
import { Component, OnChanges, SimpleChanges, Input, ChangeDetectionStrategy, ChangeDetectorRef, Output, EventEmitter } from '@angular/core';
import { Transaction } from '@interfaces/electrs.interface';
import { Acceleration, CpfpInfo } from '@interfaces/node-api.interface';
import { Acceleration, BlockExtended, CpfpInfo } from '@interfaces/node-api.interface';
import { Pool, TxAuditStatus } from '@components/transaction/transaction.component';
import { Observable } from 'rxjs';
import { first, timeout } from 'rxjs/operators';
import { ETA } from '@app/services/eta.service';
import { MiningStats } from '@app/services/mining.service';
import { Filter } from '@app/shared/filters.utils';
import { Filter, TransactionFlags } from '@app/shared/filters.utils';
import { StateService } from '@app/services/state.service';
import { CacheService } from '@app/services/cache.service';
@Component({
selector: 'app-transaction-details',
@ -14,7 +17,7 @@ import { Filter } from '@app/shared/filters.utils';
standalone: false,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TransactionDetailsComponent implements OnInit {
export class TransactionDetailsComponent implements OnChanges {
@Input() network: string;
@Input() tx: Transaction;
@Input() isLoadingTx: boolean;
@ -45,9 +48,60 @@ export class TransactionDetailsComponent implements OnInit {
@Output() accelerateClicked = new EventEmitter<boolean>();
@Output() toggleCpfp$ = new EventEmitter<void>();
constructor() {}
acceleratorSavingsSats = 0;
officialMempoolSpace: boolean;
ngOnInit(): void {}
constructor(
private stateService: StateService,
private cacheService: CacheService,
private cd: ChangeDetectorRef,
) {
this.officialMempoolSpace = this.stateService.env.OFFICIAL_MEMPOOL_SPACE;
}
ngOnChanges(changes: SimpleChanges): void {
if (!changes.tx) {
return;
}
this.acceleratorSavingsSats = 0;
const hasIneligibleFlags = ((this.tx?.flags ?? 0n) & (TransactionFlags.inscription | TransactionFlags.sighash_none | TransactionFlags.sighash_single | TransactionFlags.sighash_acp)) > 0n;
if (this.officialMempoolSpace
&& this.tx?.status?.confirmed
&& !this.tx.acceleration && !this.accelerationInfo
&& this.tx.weight <= 4000
&& !hasIneligibleFlags
&& Math.min(...this.tx.vout.map(o => o.value)) <= 1000000
) {
const block = this.cacheService.getCachedBlock(this.tx.status.block_height);
if (block) {
this.calculateAcceleratorSavings(block);
} else {
const txid = this.tx.txid;
this.cacheService.loadBlock(this.tx.status.block_height);
this.cacheService.loadedBlocks$.pipe(
first(b => b.height === this.tx.status.block_height),
timeout({ each: 30000, with: () => [] }),
).subscribe((block) => {
if (this.tx?.txid === txid) {
this.calculateAcceleratorSavings(block);
this.cd.markForCheck();
}
});
}
}
}
calculateAcceleratorSavings(block: BlockExtended): void {
const minBlockRate = block?.extras?.feeRange?.[0];
if (minBlockRate !== undefined) {
const vsize = this.tx.weight / 4;
this.acceleratorSavingsSats = Math.max(0, this.tx.fee - Math.ceil(minBlockRate * vsize) - 75000);
}
}
get showAcceleratorSavingsMsg(): boolean {
return this.acceleratorSavingsSats > 0 && this.cpfpInfo !== null && this.cpfpInfo !== undefined && !this.hasCpfp;
}
onAccelerateClicked(): void {
this.accelerateClicked.emit(true);