Merge branch 'master' into nymkappa/apple-pay

This commit is contained in:
nymkappa
2024-07-24 22:04:44 +02:00
32 changed files with 1111 additions and 925 deletions

View File

@@ -1,7 +1,7 @@
import { Component, OnInit, OnDestroy, Output, EventEmitter, Input, ChangeDetectorRef, SimpleChanges, HostListener } from '@angular/core';
import { Subscription, tap, of, catchError, Observable, switchMap } from 'rxjs';
import { ServicesApiServices } from '../../services/services-api.service';
import { md5, nextRoundNumber } from '../../shared/common.utils';
import { md5, nextRoundNumber, insecureRandomUUID } from '../../shared/common.utils';
import { StateService } from '../../services/state.service';
import { AudioService } from '../../services/audio.service';
import { ETA, EtaService } from '../../services/eta.service';
@@ -9,6 +9,7 @@ import { Transaction } from '../../interfaces/electrs.interface';
import { MiningStats } from '../../services/mining.service';
import { IAuth, AuthServiceMempool } from '../../services/auth.service';
import { EnterpriseService } from '../../services/enterprise.service';
import { ApiService } from '../../services/api.service';
export type PaymentMethod = 'balance' | 'bitcoin' | 'cashapp';
@@ -125,6 +126,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
constructor(
public stateService: StateService,
private apiService: ApiService,
private servicesApiService: ServicesApiServices,
private etaService: EtaService,
private audioService: AudioService,
@@ -132,7 +134,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
private authService: AuthServiceMempool,
private enterpriseService: EnterpriseService,
) {
this.accelerationUUID = window.crypto.randomUUID();
this.accelerationUUID = insecureRandomUUID();
// Check if Apple Pay available
// @ts-ignore https://developer.apple.com/documentation/apple_pay_on_the_web/apple_pay_js_api/checking_for_apple_pay_availability#overview
@@ -384,10 +386,11 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
this.accelerationUUID
).subscribe({
next: () => {
this.apiService.logAccelerationRequest$(this.tx.txid).subscribe();
this.audioService.playSound('ascend-chime-cartoon');
this.showSuccess = true;
this.estimateSubscription.unsubscribe();
this.moveToStep('paid')
this.moveToStep('paid');
},
error: (response) => {
this.accelerateError = response.error;
@@ -590,6 +593,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
this.accelerationUUID
).subscribe({
next: () => {
this.apiService.logAccelerationRequest$(this.tx.txid).subscribe();
this.audioService.playSound('ascend-chime-cartoon');
if (this.cashAppPay) {
this.cashAppPay.destroy();
@@ -639,9 +643,10 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
}
bitcoinPaymentCompleted(): void {
this.apiService.logAccelerationRequest$(this.tx.txid).subscribe();
this.audioService.playSound('ascend-chime-cartoon');
this.estimateSubscription.unsubscribe();
this.moveToStep('paid')
this.moveToStep('paid');
}
isLoggedIn(): boolean {

View File

@@ -201,7 +201,7 @@
<span i18n="address.error.loading-address-data">Error loading address data.</span>
<br>
<ng-container i18n="Electrum server limit exceeded error">
<i>There many transactions on this address, more than your backend can handle. See more on <a href="/docs/faq#address-lookup-issues">setting up a stronger backend</a>.</i>
<i>There are too many transactions on this address, more than your backend can handle. See more on <a href="/docs/faq#address-lookup-issues">setting up a stronger backend</a>.</i>
<br><br>
Consider viewing this address on the official Mempool website instead:
</ng-container>

View File

@@ -3,7 +3,7 @@ import { FormBuilder, FormGroup } from '@angular/forms';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
import { ActivatedRoute } from '@angular/router';
import { Subscription, of, timer } from 'rxjs';
import { retry, switchMap, tap } from 'rxjs/operators';
import { filter, repeat, retry, switchMap, take, tap } from 'rxjs/operators';
import { ServicesApiServices } from '../../services/services-api.service';
@Component({
@@ -73,11 +73,11 @@ export class BitcoinInvoiceComponent implements OnInit, OnChanges, OnDestroy {
this.paymentStatus = 4;
}
this.paymentStatusSubscription = this.apiService.getPaymentStatus$(this.invoice.btcpayInvoiceId).pipe(
retry({ delay: () => timer(2000)})
).subscribe((response) => {
if (response.status === 204 || response.status === 404) {
return;
}
retry({ delay: () => timer(2000)}),
repeat({delay: 2000}),
filter((response) => response.status !== 204 && response.status !== 404),
take(1),
).subscribe(() => {
this.paymentStatus = 3;
this.completed.emit();
});

View File

@@ -68,7 +68,7 @@ export class BlockOverviewTooltipComponent implements OnChanges {
this.effectiveRate = this.tx.rate;
const txFlags = BigInt(this.tx.flags) || 0n;
this.acceleration = this.tx.acc || (txFlags & TransactionFlags.acceleration);
this.hasEffectiveRate = this.tx.acc || Math.abs((this.fee / this.vsize) - this.effectiveRate) > 0.05
this.hasEffectiveRate = this.tx.acc || !(Math.abs((this.fee / this.vsize) - this.effectiveRate) <= 0.1 && Math.abs((this.fee / Math.ceil(this.vsize)) - this.effectiveRate) <= 0.1)
|| (txFlags && (txFlags & (TransactionFlags.cpfp_child | TransactionFlags.cpfp_parent)) > 0n);
this.filters = this.tx.flags ? toFilters(txFlags).filter(f => f.tooltip) : [];
this.activeFilters = {}

View File

@@ -234,7 +234,7 @@ export class StartComponent implements OnInit, AfterViewChecked, OnDestroy {
this.minScrollWidth = 40 + (8 * this.blockWidth) + (this.pageWidth * 2);
if (firstVisibleBlock != null) {
this.scrollToBlock(firstVisibleBlock, offset);
this.scrollToBlock(firstVisibleBlock, offset + (this.isMobile ? this.blockWidth : 0));
} else {
this.updatePages();
}

View File

@@ -88,6 +88,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
blocksSubscription: Subscription;
miningSubscription: Subscription;
auditSubscription: Subscription;
txConfirmedSubscription: Subscription;
currencyChangeSubscription: Subscription;
fragmentParams: URLSearchParams;
rbfTransaction: undefined | Transaction;
@@ -141,7 +142,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
taprootEnabled: boolean;
hasEffectiveFeeRate: boolean;
accelerateCtaType: 'alert' | 'button' = 'button';
acceleratorAvailable: boolean = this.stateService.env.ACCELERATOR && this.stateService.network === '';
acceleratorAvailable: boolean = this.stateService.env.ACCELERATOR_BUTTON && this.stateService.network === '';
eligibleForAcceleration: boolean = false;
forceAccelerationSummary = false;
hideAccelerationSummary = false;
@@ -195,7 +196,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
this.stateService.networkChanged$.subscribe(
(network) => {
this.network = network;
this.acceleratorAvailable = this.stateService.env.ACCELERATOR && this.stateService.network === '';
this.acceleratorAvailable = this.stateService.env.ACCELERATOR_BUTTON && this.stateService.network === '';
}
);
@@ -599,7 +600,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
bestDescendant: tx.bestDescendant,
});
const hasRelatives = !!(tx.ancestors?.length || tx.bestDescendant);
this.hasEffectiveFeeRate = hasRelatives || (tx.effectiveFeePerVsize && (Math.abs(tx.effectiveFeePerVsize - tx.feePerVsize) > 0.01));
this.hasEffectiveFeeRate = hasRelatives || (tx.effectiveFeePerVsize && (Math.abs(tx.effectiveFeePerVsize - tx.feePerVsize) >= 0.1));
} else {
this.fetchCpfp$.next(this.tx.txid);
}
@@ -625,7 +626,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
}
);
this.stateService.txConfirmed$.subscribe(([txConfirmed, block]) => {
this.txConfirmedSubscription = this.stateService.txConfirmed$.subscribe(([txConfirmed, block]) => {
if (txConfirmed && this.tx && !this.tx.status.confirmed && txConfirmed === this.tx.txid) {
if (this.tx.acceleration) {
this.waitingForAccelerationInfo = true;
@@ -1070,6 +1071,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
this.blocksSubscription.unsubscribe();
this.miningSubscription?.unsubscribe();
this.auditSubscription?.unsubscribe();
this.txConfirmedSubscription?.unsubscribe();
this.currencyChangeSubscription?.unsubscribe();
this.leaveTransaction();
}

View File

@@ -8993,7 +8993,7 @@ export const restApiDocsData = [
fragment: "accelerator-estimate",
title: "POST Calculate Estimated Costs",
description: {
default: "<p>Returns estimated costs to accelerate a transaction. Optionally set the <code>api_key</code> header to get customized estimation.</p>"
default: "<p>Returns estimated costs to accelerate a transaction. Optionally set the <code>X-Mempool-Auth</code> header to get customized estimation.</p>"
},
urlString: "/v1/services/accelerator/estimate",
showConditions: [""],
@@ -9009,7 +9009,7 @@ export const restApiDocsData = [
esModule: [],
commonJS: [],
curl: ["txInput=ee13ebb99632377c15c94980357f674d285ac413452050031ea6dcd3e9b2dc29"],
headers: "api_key: stacksats",
headers: "X-Mempool-Auth: stacksats",
response: `{
"txSummary": {
"txid": "ee13ebb99632377c15c94980357f674d285ac413452050031ea6dcd3e9b2dc29",
@@ -9240,7 +9240,7 @@ export const restApiDocsData = [
esModule: [],
commonJS: [],
curl: [],
headers: "api_key: stacksats",
headers: "X-Mempool-Auth: stacksats",
response: `[
{
"type": "Bitcoin",
@@ -9288,7 +9288,7 @@ export const restApiDocsData = [
esModule: [],
commonJS: [],
curl: [],
headers: "api_key: stacksats",
headers: "X-Mempool-Auth: stacksats",
response: `{
"balance": 99900000,
"hold": 101829,
@@ -9322,7 +9322,7 @@ export const restApiDocsData = [
esModule: [],
commonJS: [],
curl: ["txInput=ee13ebb99632377c15c94980357f674d285ac413452050031ea6dcd3e9b2dc29&userBid=21000000"],
headers: "api_key: stacksats",
headers: "X-Mempool-Auth: stacksats",
response: `HTTP/1.1 200 OK`,
},
}
@@ -9352,7 +9352,7 @@ export const restApiDocsData = [
esModule: [],
commonJS: [],
curl: [],
headers: "api_key: stacksats",
headers: "X-Mempool-Auth: stacksats",
response: `[
{
"id": 89,

View File

@@ -536,6 +536,10 @@ export class ApiService {
);
}
logAccelerationRequest$(txid: string): Observable<any> {
return this.httpClient.post(this.apiBaseUrl + this.apiBasePath + '/api/v1/acceleration/request/' + txid, '');
}
// Cache methods
async setBlockAuditLoaded(hash: string) {
this.blockAuditLoaded[hash] = true;

View File

@@ -30,6 +30,7 @@ export class EnterpriseService {
this.fetchSubdomainInfo();
this.disableSubnetworks();
this.stateService.env.ACCELERATOR = false;
this.stateService.env.ACCELERATOR_BUTTON = false;
} else {
this.insertMatomo();
}

View File

@@ -71,6 +71,7 @@ export interface Env {
SIGNET_BLOCK_AUDIT_START_HEIGHT: number;
HISTORICAL_PRICE: boolean;
ACCELERATOR: boolean;
ACCELERATOR_BUTTON: boolean;
PUBLIC_ACCELERATIONS: boolean;
ADDITIONAL_CURRENCIES: boolean;
GIT_COMMIT_HASH_MEMPOOL_SPACE?: string;
@@ -108,6 +109,7 @@ const defaultEnv: Env = {
'SIGNET_BLOCK_AUDIT_START_HEIGHT': 0,
'HISTORICAL_PRICE': true,
'ACCELERATOR': false,
'ACCELERATOR_BUTTON': true,
'PUBLIC_ACCELERATIONS': false,
'ADDITIONAL_CURRENCIES': false,
'SERVICES_API': 'https://mempool.space/api/v1/services',

View File

@@ -183,6 +183,19 @@ export function uncompressDeltaChange(delta: MempoolBlockDeltaCompressed): Mempo
};
}
export function insecureRandomUUID(): string {
const hexDigits = '0123456789abcdef';
const uuidLengths = [8, 4, 4, 4, 12];
let uuid = '';
for (const length of uuidLengths) {
for (let i = 0; i < length; i++) {
uuid += hexDigits[Math.floor(Math.random() * 16)];
}
uuid += '-';
}
return uuid.slice(0, -1);
}
// https://stackoverflow.com/a/60467595
export function md5(inputString): string {
var hc="0123456789abcdef";
@@ -225,4 +238,4 @@ export function md5(inputString): string {
b=ii(b,c,d,a,x[i+ 9],21, -343485551);a=ad(a,olda);b=ad(b,oldb);c=ad(c,oldc);d=ad(d,oldd);
}
return rh(a)+rh(b)+rh(c)+rh(d);
}
}