parent
							
								
									87a9be55cc
								
							
						
					
					
						commit
						d7ba9b52eb
					
				@ -13,6 +13,7 @@ export interface AbstractBitcoinApi {
 | 
			
		||||
  $getAddressPrefix(prefix: string): string[];
 | 
			
		||||
  $sendRawTransaction(rawTransaction: string): Promise<string>;
 | 
			
		||||
  $getOutspends(txId: string): Promise<IEsploraApi.Outspend[]>;
 | 
			
		||||
  $getBatchedOutspends(txId: string[]): Promise<IEsploraApi.Outspend[][]>;
 | 
			
		||||
}
 | 
			
		||||
export interface BitcoinRpcCredentials {
 | 
			
		||||
  host: string;
 | 
			
		||||
 | 
			
		||||
@ -141,6 +141,15 @@ class BitcoinApi implements AbstractBitcoinApi {
 | 
			
		||||
    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> {
 | 
			
		||||
    // 120 is the default block span in Core
 | 
			
		||||
    return this.bitcoindClient.getNetworkHashPs(120, blockHeight);
 | 
			
		||||
 | 
			
		||||
@ -61,8 +61,18 @@ class ElectrsApi implements AbstractBitcoinApi {
 | 
			
		||||
    throw new Error('Method not implemented.');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $getOutspends(): Promise<IEsploraApi.Outspend[]> {
 | 
			
		||||
    throw new Error('Method not implemented.');
 | 
			
		||||
  $getOutspends(txId: string): Promise<IEsploraApi.Outspend[]> {
 | 
			
		||||
    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() {
 | 
			
		||||
    this.app
 | 
			
		||||
      .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 + 'difficulty-adjustment', routes.getDifficultyChange)
 | 
			
		||||
      .get(config.MEMPOOL.API_URL_PREFIX + 'fees/recommended', routes.getRecommendedFees)
 | 
			
		||||
 | 
			
		||||
@ -120,6 +120,30 @@ class Routes {
 | 
			
		||||
    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) {
 | 
			
		||||
    if (!/^[a-fA-F0-9]{64}$/.test(req.params.txId)) {
 | 
			
		||||
      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 { environment } from 'src/environments/environment';
 | 
			
		||||
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 { ApiService } from 'src/app/services/api.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-transactions-list',
 | 
			
		||||
@ -30,7 +31,7 @@ export class TransactionsListComponent implements OnInit, OnChanges {
 | 
			
		||||
 | 
			
		||||
  latestBlock$: Observable<BlockExtended>;
 | 
			
		||||
  outspendsSubscription: Subscription;
 | 
			
		||||
  refreshOutspends$: ReplaySubject<{ [str: string]: Observable<Outspend[]>}> = new ReplaySubject();
 | 
			
		||||
  refreshOutspends$: ReplaySubject<string[]> = new ReplaySubject();
 | 
			
		||||
  showDetails$ = new BehaviorSubject<boolean>(false);
 | 
			
		||||
  outspends: Outspend[][] = [];
 | 
			
		||||
  assetsMinimal: any;
 | 
			
		||||
@ -38,6 +39,7 @@ export class TransactionsListComponent implements OnInit, OnChanges {
 | 
			
		||||
  constructor(
 | 
			
		||||
    public stateService: StateService,
 | 
			
		||||
    private electrsApiService: ElectrsApiService,
 | 
			
		||||
    private apiService: ApiService,
 | 
			
		||||
    private assetsService: AssetsService,
 | 
			
		||||
    private ref: ChangeDetectorRef,
 | 
			
		||||
  ) { }
 | 
			
		||||
@ -55,20 +57,14 @@ export class TransactionsListComponent implements OnInit, OnChanges {
 | 
			
		||||
    this.outspendsSubscription = merge(
 | 
			
		||||
      this.refreshOutspends$
 | 
			
		||||
        .pipe(
 | 
			
		||||
          switchMap((observableObject) => forkJoin(observableObject)),
 | 
			
		||||
          map((outspends: any) => {
 | 
			
		||||
            const newOutspends: Outspend[] = [];
 | 
			
		||||
            for (const i in outspends) {
 | 
			
		||||
              if (outspends.hasOwnProperty(i)) {
 | 
			
		||||
                newOutspends.push(outspends[i]);
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            this.outspends = this.outspends.concat(newOutspends);
 | 
			
		||||
          switchMap((txIds) => this.apiService.getOutspendsBatched$(txIds)),
 | 
			
		||||
          tap((outspends: Outspend[][]) => {
 | 
			
		||||
            this.outspends = this.outspends.concat(outspends);
 | 
			
		||||
          }),
 | 
			
		||||
        ),
 | 
			
		||||
      this.stateService.utxoSpent$
 | 
			
		||||
        .pipe(
 | 
			
		||||
          map((utxoSpent) => {
 | 
			
		||||
          tap((utxoSpent) => {
 | 
			
		||||
            for (const i in utxoSpent) {
 | 
			
		||||
              this.outspends[0][i] = {
 | 
			
		||||
                spent: true,
 | 
			
		||||
@ -96,7 +92,7 @@ export class TransactionsListComponent implements OnInit, OnChanges {
 | 
			
		||||
        }
 | 
			
		||||
      }, 10);
 | 
			
		||||
    }
 | 
			
		||||
    const observableObject = {};
 | 
			
		||||
 | 
			
		||||
    this.transactions.forEach((tx, i) => {
 | 
			
		||||
      tx['@voutLimit'] = true;
 | 
			
		||||
      tx['@vinLimit'] = true;
 | 
			
		||||
@ -117,10 +113,9 @@ export class TransactionsListComponent implements OnInit, OnChanges {
 | 
			
		||||
 | 
			
		||||
        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() {
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@ import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITrans
 | 
			
		||||
import { Observable } from 'rxjs';
 | 
			
		||||
import { StateService } from './state.service';
 | 
			
		||||
import { WebsocketResponse } from '../interfaces/websocket.interface';
 | 
			
		||||
import { Outspend } from '../interfaces/electrs.interface';
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
  providedIn: 'root'
 | 
			
		||||
@ -74,6 +75,14 @@ export class ApiService {
 | 
			
		||||
    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> {
 | 
			
		||||
    const params = {
 | 
			
		||||
      amount: amount,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user