parent
							
								
									87a9be55cc
								
							
						
					
					
						commit
						d7ba9b52eb
					
				@ -13,6 +13,7 @@ export interface AbstractBitcoinApi {
 | 
				
			|||||||
  $getAddressPrefix(prefix: string): string[];
 | 
					  $getAddressPrefix(prefix: string): string[];
 | 
				
			||||||
  $sendRawTransaction(rawTransaction: string): Promise<string>;
 | 
					  $sendRawTransaction(rawTransaction: string): Promise<string>;
 | 
				
			||||||
  $getOutspends(txId: string): Promise<IEsploraApi.Outspend[]>;
 | 
					  $getOutspends(txId: string): Promise<IEsploraApi.Outspend[]>;
 | 
				
			||||||
 | 
					  $getBatchedOutspends(txId: string[]): Promise<IEsploraApi.Outspend[][]>;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
export interface BitcoinRpcCredentials {
 | 
					export interface BitcoinRpcCredentials {
 | 
				
			||||||
  host: string;
 | 
					  host: string;
 | 
				
			||||||
 | 
				
			|||||||
@ -141,6 +141,15 @@ class BitcoinApi implements AbstractBitcoinApi {
 | 
				
			|||||||
    return outSpends;
 | 
					    return outSpends;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async $getBatchedOutspends(txId: string[]): Promise<IEsploraApi.Outspend[][]> {
 | 
				
			||||||
 | 
					    const outspends: IEsploraApi.Outspend[][] = [];
 | 
				
			||||||
 | 
					    for (const tx of txId) {
 | 
				
			||||||
 | 
					      const outspend = await this.$getOutspends(tx);
 | 
				
			||||||
 | 
					      outspends.push(outspend);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return outspends;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  $getEstimatedHashrate(blockHeight: number): Promise<number> {
 | 
					  $getEstimatedHashrate(blockHeight: number): Promise<number> {
 | 
				
			||||||
    // 120 is the default block span in Core
 | 
					    // 120 is the default block span in Core
 | 
				
			||||||
    return this.bitcoindClient.getNetworkHashPs(120, blockHeight);
 | 
					    return this.bitcoindClient.getNetworkHashPs(120, blockHeight);
 | 
				
			||||||
 | 
				
			|||||||
@ -61,8 +61,18 @@ class ElectrsApi implements AbstractBitcoinApi {
 | 
				
			|||||||
    throw new Error('Method not implemented.');
 | 
					    throw new Error('Method not implemented.');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  $getOutspends(): Promise<IEsploraApi.Outspend[]> {
 | 
					  $getOutspends(txId: string): Promise<IEsploraApi.Outspend[]> {
 | 
				
			||||||
    throw new Error('Method not implemented.');
 | 
					    return axios.get<IEsploraApi.Outspend[]>(config.ESPLORA.REST_API_URL + '/tx/' + txId, this.axiosConfig)
 | 
				
			||||||
 | 
					      .then((response) => response.data);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async $getBatchedOutspends(txId: string[]): Promise<IEsploraApi.Outspend[][]> {
 | 
				
			||||||
 | 
					    const outspends: IEsploraApi.Outspend[][] = [];
 | 
				
			||||||
 | 
					    for (const tx of txId) {
 | 
				
			||||||
 | 
					      const outspend = await this.$getOutspends(tx);
 | 
				
			||||||
 | 
					      outspends.push(outspend);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return outspends;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -195,6 +195,7 @@ class Server {
 | 
				
			|||||||
  setUpHttpApiRoutes() {
 | 
					  setUpHttpApiRoutes() {
 | 
				
			||||||
    this.app
 | 
					    this.app
 | 
				
			||||||
      .get(config.MEMPOOL.API_URL_PREFIX + 'transaction-times', routes.getTransactionTimes)
 | 
					      .get(config.MEMPOOL.API_URL_PREFIX + 'transaction-times', routes.getTransactionTimes)
 | 
				
			||||||
 | 
					      .get(config.MEMPOOL.API_URL_PREFIX + 'outspends', routes.$getBatchedOutspends)
 | 
				
			||||||
      .get(config.MEMPOOL.API_URL_PREFIX + 'cpfp/:txId', routes.getCpfpInfo)
 | 
					      .get(config.MEMPOOL.API_URL_PREFIX + 'cpfp/:txId', routes.getCpfpInfo)
 | 
				
			||||||
      .get(config.MEMPOOL.API_URL_PREFIX + 'difficulty-adjustment', routes.getDifficultyChange)
 | 
					      .get(config.MEMPOOL.API_URL_PREFIX + 'difficulty-adjustment', routes.getDifficultyChange)
 | 
				
			||||||
      .get(config.MEMPOOL.API_URL_PREFIX + 'fees/recommended', routes.getRecommendedFees)
 | 
					      .get(config.MEMPOOL.API_URL_PREFIX + 'fees/recommended', routes.getRecommendedFees)
 | 
				
			||||||
 | 
				
			|||||||
@ -120,6 +120,30 @@ class Routes {
 | 
				
			|||||||
    res.json(times);
 | 
					    res.json(times);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public async $getBatchedOutspends(req: Request, res: Response) {
 | 
				
			||||||
 | 
					    if (!Array.isArray(req.query.txId)) {
 | 
				
			||||||
 | 
					      res.status(500).send('Not an array');
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (req.query.txId.length > 50) {
 | 
				
			||||||
 | 
					      res.status(400).send('Too many txids requested');
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const txIds: string[] = [];
 | 
				
			||||||
 | 
					    for (const _txId in req.query.txId) {
 | 
				
			||||||
 | 
					      if (typeof req.query.txId[_txId] === 'string') {
 | 
				
			||||||
 | 
					        txIds.push(req.query.txId[_txId].toString());
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const batchedOutspends = await bitcoinApi.$getBatchedOutspends(txIds);
 | 
				
			||||||
 | 
					      res.json(batchedOutspends);
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      res.status(500).send(e instanceof Error ? e.message : e);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public getCpfpInfo(req: Request, res: Response) {
 | 
					  public getCpfpInfo(req: Request, res: Response) {
 | 
				
			||||||
    if (!/^[a-fA-F0-9]{64}$/.test(req.params.txId)) {
 | 
					    if (!/^[a-fA-F0-9]{64}$/.test(req.params.txId)) {
 | 
				
			||||||
      res.status(501).send(`Invalid transaction ID.`);
 | 
					      res.status(501).send(`Invalid transaction ID.`);
 | 
				
			||||||
 | 
				
			|||||||
@ -5,8 +5,9 @@ import { Outspend, Transaction, Vin, Vout } from '../../interfaces/electrs.inter
 | 
				
			|||||||
import { ElectrsApiService } from '../../services/electrs-api.service';
 | 
					import { ElectrsApiService } from '../../services/electrs-api.service';
 | 
				
			||||||
import { environment } from 'src/environments/environment';
 | 
					import { environment } from 'src/environments/environment';
 | 
				
			||||||
import { AssetsService } from 'src/app/services/assets.service';
 | 
					import { AssetsService } from 'src/app/services/assets.service';
 | 
				
			||||||
import { map, switchMap } from 'rxjs/operators';
 | 
					import { map, tap, switchMap } from 'rxjs/operators';
 | 
				
			||||||
import { BlockExtended } from 'src/app/interfaces/node-api.interface';
 | 
					import { BlockExtended } from 'src/app/interfaces/node-api.interface';
 | 
				
			||||||
 | 
					import { ApiService } from 'src/app/services/api.service';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-transactions-list',
 | 
					  selector: 'app-transactions-list',
 | 
				
			||||||
@ -30,7 +31,7 @@ export class TransactionsListComponent implements OnInit, OnChanges {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  latestBlock$: Observable<BlockExtended>;
 | 
					  latestBlock$: Observable<BlockExtended>;
 | 
				
			||||||
  outspendsSubscription: Subscription;
 | 
					  outspendsSubscription: Subscription;
 | 
				
			||||||
  refreshOutspends$: ReplaySubject<{ [str: string]: Observable<Outspend[]>}> = new ReplaySubject();
 | 
					  refreshOutspends$: ReplaySubject<string[]> = new ReplaySubject();
 | 
				
			||||||
  showDetails$ = new BehaviorSubject<boolean>(false);
 | 
					  showDetails$ = new BehaviorSubject<boolean>(false);
 | 
				
			||||||
  outspends: Outspend[][] = [];
 | 
					  outspends: Outspend[][] = [];
 | 
				
			||||||
  assetsMinimal: any;
 | 
					  assetsMinimal: any;
 | 
				
			||||||
@ -38,6 +39,7 @@ export class TransactionsListComponent implements OnInit, OnChanges {
 | 
				
			|||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    public stateService: StateService,
 | 
					    public stateService: StateService,
 | 
				
			||||||
    private electrsApiService: ElectrsApiService,
 | 
					    private electrsApiService: ElectrsApiService,
 | 
				
			||||||
 | 
					    private apiService: ApiService,
 | 
				
			||||||
    private assetsService: AssetsService,
 | 
					    private assetsService: AssetsService,
 | 
				
			||||||
    private ref: ChangeDetectorRef,
 | 
					    private ref: ChangeDetectorRef,
 | 
				
			||||||
  ) { }
 | 
					  ) { }
 | 
				
			||||||
@ -55,20 +57,14 @@ export class TransactionsListComponent implements OnInit, OnChanges {
 | 
				
			|||||||
    this.outspendsSubscription = merge(
 | 
					    this.outspendsSubscription = merge(
 | 
				
			||||||
      this.refreshOutspends$
 | 
					      this.refreshOutspends$
 | 
				
			||||||
        .pipe(
 | 
					        .pipe(
 | 
				
			||||||
          switchMap((observableObject) => forkJoin(observableObject)),
 | 
					          switchMap((txIds) => this.apiService.getOutspendsBatched$(txIds)),
 | 
				
			||||||
          map((outspends: any) => {
 | 
					          tap((outspends: Outspend[][]) => {
 | 
				
			||||||
            const newOutspends: Outspend[] = [];
 | 
					            this.outspends = this.outspends.concat(outspends);
 | 
				
			||||||
            for (const i in outspends) {
 | 
					 | 
				
			||||||
              if (outspends.hasOwnProperty(i)) {
 | 
					 | 
				
			||||||
                newOutspends.push(outspends[i]);
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            this.outspends = this.outspends.concat(newOutspends);
 | 
					 | 
				
			||||||
          }),
 | 
					          }),
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
      this.stateService.utxoSpent$
 | 
					      this.stateService.utxoSpent$
 | 
				
			||||||
        .pipe(
 | 
					        .pipe(
 | 
				
			||||||
          map((utxoSpent) => {
 | 
					          tap((utxoSpent) => {
 | 
				
			||||||
            for (const i in utxoSpent) {
 | 
					            for (const i in utxoSpent) {
 | 
				
			||||||
              this.outspends[0][i] = {
 | 
					              this.outspends[0][i] = {
 | 
				
			||||||
                spent: true,
 | 
					                spent: true,
 | 
				
			||||||
@ -96,7 +92,7 @@ export class TransactionsListComponent implements OnInit, OnChanges {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }, 10);
 | 
					      }, 10);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const observableObject = {};
 | 
					
 | 
				
			||||||
    this.transactions.forEach((tx, i) => {
 | 
					    this.transactions.forEach((tx, i) => {
 | 
				
			||||||
      tx['@voutLimit'] = true;
 | 
					      tx['@voutLimit'] = true;
 | 
				
			||||||
      tx['@vinLimit'] = true;
 | 
					      tx['@vinLimit'] = true;
 | 
				
			||||||
@ -117,10 +113,9 @@ export class TransactionsListComponent implements OnInit, OnChanges {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        tx['addressValue'] = addressIn - addressOut;
 | 
					        tx['addressValue'] = addressIn - addressOut;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					 | 
				
			||||||
      observableObject[i] = this.electrsApiService.getOutspends$(tx.txid);
 | 
					 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    this.refreshOutspends$.next(observableObject);
 | 
					
 | 
				
			||||||
 | 
					    this.refreshOutspends$.next(this.transactions.map((tx) => tx.txid));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onScroll() {
 | 
					  onScroll() {
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@ import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITrans
 | 
				
			|||||||
import { Observable } from 'rxjs';
 | 
					import { Observable } from 'rxjs';
 | 
				
			||||||
import { StateService } from './state.service';
 | 
					import { StateService } from './state.service';
 | 
				
			||||||
import { WebsocketResponse } from '../interfaces/websocket.interface';
 | 
					import { WebsocketResponse } from '../interfaces/websocket.interface';
 | 
				
			||||||
 | 
					import { Outspend } from '../interfaces/electrs.interface';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable({
 | 
					@Injectable({
 | 
				
			||||||
  providedIn: 'root'
 | 
					  providedIn: 'root'
 | 
				
			||||||
@ -74,6 +75,14 @@ export class ApiService {
 | 
				
			|||||||
    return this.httpClient.get<number[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/transaction-times', { params });
 | 
					    return this.httpClient.get<number[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/transaction-times', { params });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getOutspendsBatched$(txIds: string[]): Observable<Outspend[][]> {
 | 
				
			||||||
 | 
					    let params = new HttpParams();
 | 
				
			||||||
 | 
					    txIds.forEach((txId: string) => {
 | 
				
			||||||
 | 
					      params = params.append('txId[]', txId);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    return this.httpClient.get<Outspend[][]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/outspends', { params });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  requestDonation$(amount: number, orderId: string): Observable<any> {
 | 
					  requestDonation$(amount: number, orderId: string): Observable<any> {
 | 
				
			||||||
    const params = {
 | 
					    const params = {
 | 
				
			||||||
      amount: amount,
 | 
					      amount: amount,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user