Block view.
This commit is contained in:
		
							parent
							
								
									bd2bd478ef
								
							
						
					
					
						commit
						309e851ead
					
				@ -5,9 +5,12 @@ export interface AbstractBitcoinApi {
 | 
			
		||||
  getRawMempool(): Promise<ITransaction['txid'][]>;
 | 
			
		||||
  getRawTransaction(txId: string): Promise<ITransaction>;
 | 
			
		||||
  getBlockCount(): Promise<number>;
 | 
			
		||||
  getBlock(hash: string): Promise<IBlock>;
 | 
			
		||||
  getBlockAndTransactions(hash: string): Promise<IBlock>;
 | 
			
		||||
  getBlockHash(height: number): Promise<string>;
 | 
			
		||||
 | 
			
		||||
  getBlock(hash: string): Promise<IBlock>;
 | 
			
		||||
  getBlockTransactions(hash: string): Promise<IBlock>;
 | 
			
		||||
  getBlockTransactionsFromIndex(hash: string, index: number): Promise<IBlock>;
 | 
			
		||||
  getBlocks(): Promise<string>;
 | 
			
		||||
  getBlocksFromHeight(height: number): Promise<string>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -59,7 +59,7 @@ class BitcoindApi implements AbstractBitcoinApi {
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getBlock(hash: string, verbosity:  1 | 2 = 1): Promise<IBlock> {
 | 
			
		||||
  getBlockAndTransactions(hash: string, verbosity:  1 | 2 = 1): Promise<IBlock> {
 | 
			
		||||
    return new Promise((resolve, reject) => {
 | 
			
		||||
      this.client.getBlock(hash, verbosity, (err: Error, block: IBlock) => {
 | 
			
		||||
        if (err) {
 | 
			
		||||
@ -81,6 +81,10 @@ class BitcoindApi implements AbstractBitcoinApi {
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getBlock(hash: string): Promise<IBlock> {
 | 
			
		||||
    throw new Error('Method not implemented.');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getBlocks(): Promise<string> {
 | 
			
		||||
    throw new Error('Method not implemented.');
 | 
			
		||||
  }
 | 
			
		||||
@ -88,6 +92,14 @@ class BitcoindApi implements AbstractBitcoinApi {
 | 
			
		||||
  getBlocksFromHeight(height: number): Promise<string> {
 | 
			
		||||
    throw new Error('Method not implemented.');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getBlockTransactions(hash: string): Promise<IBlock> {
 | 
			
		||||
    throw new Error('Method not implemented.');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getBlockTransactionsFromIndex(hash: string, index: number): Promise<IBlock> {
 | 
			
		||||
    throw new Error('Method not implemented.');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default BitcoindApi;
 | 
			
		||||
 | 
			
		||||
@ -65,7 +65,7 @@ class EsploraApi implements AbstractBitcoinApi {
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getBlock(hash: string): Promise<IBlock> {
 | 
			
		||||
  getBlockAndTransactions(hash: string): Promise<IBlock> {
 | 
			
		||||
    return new Promise(async (resolve, reject) => {
 | 
			
		||||
      try {
 | 
			
		||||
        const blockInfo: AxiosResponse = await this.client.get('/block/' + hash);
 | 
			
		||||
@ -116,6 +116,39 @@ class EsploraApi implements AbstractBitcoinApi {
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getBlock(hash: string): Promise<IBlock> {
 | 
			
		||||
    return new Promise(async (resolve, reject) => {
 | 
			
		||||
      try {
 | 
			
		||||
        const blockInfo: AxiosResponse = await this.client.get('/block/' + hash);
 | 
			
		||||
        resolve(blockInfo.data);
 | 
			
		||||
      } catch (error) {
 | 
			
		||||
        reject(error);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getBlockTransactions(hash: string): Promise<IBlock> {
 | 
			
		||||
    return new Promise(async (resolve, reject) => {
 | 
			
		||||
      try {
 | 
			
		||||
        const blockInfo: AxiosResponse = await this.client.get('/block/' + hash + '/txs');
 | 
			
		||||
        resolve(blockInfo.data);
 | 
			
		||||
      } catch (error) {
 | 
			
		||||
        reject(error);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getBlockTransactionsFromIndex(hash: string, index: number): Promise<IBlock> {
 | 
			
		||||
    return new Promise(async (resolve, reject) => {
 | 
			
		||||
      try {
 | 
			
		||||
        const blockInfo: AxiosResponse = await this.client.get('/block/' + hash + '/txs/' + index);
 | 
			
		||||
        resolve(blockInfo.data);
 | 
			
		||||
      } catch (error) {
 | 
			
		||||
        reject(error);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default EsploraApi;
 | 
			
		||||
 | 
			
		||||
@ -56,7 +56,7 @@ class Blocks {
 | 
			
		||||
          block = storedBlock;
 | 
			
		||||
        } else {
 | 
			
		||||
          const blockHash = await bitcoinApi.getBlockHash(this.currentBlockHeight);
 | 
			
		||||
          block = await bitcoinApi.getBlock(blockHash);
 | 
			
		||||
          block = await bitcoinApi.getBlockAndTransactions(blockHash);
 | 
			
		||||
 | 
			
		||||
          const coinbase = await memPool.getRawTransaction(block.tx[0], true);
 | 
			
		||||
          if (coinbase && coinbase.totalOut) {
 | 
			
		||||
 | 
			
		||||
@ -120,7 +120,7 @@ class MempoolSpace {
 | 
			
		||||
                  console.log('Found block by looking in local cache');
 | 
			
		||||
                  client['blockHeight'] = foundBlock.height;
 | 
			
		||||
                } else {
 | 
			
		||||
                  const theBlock = await bitcoinApi.getBlock(tx.blockhash);
 | 
			
		||||
                  const theBlock = await bitcoinApi.getBlockAndTransactions(tx.blockhash);
 | 
			
		||||
                  if (theBlock) {
 | 
			
		||||
                    client['blockHeight'] = theBlock.height;
 | 
			
		||||
                  }
 | 
			
		||||
@ -269,6 +269,9 @@ class MempoolSpace {
 | 
			
		||||
        .get(config.API_ENDPOINT + 'explorer/blocks', routes.getBlocks)
 | 
			
		||||
        .get(config.API_ENDPOINT + 'explorer/blocks/:height', routes.getBlocks)
 | 
			
		||||
        .get(config.API_ENDPOINT + 'explorer/tx/:id', routes.getRawTransaction)
 | 
			
		||||
        .get(config.API_ENDPOINT + 'explorer/block/:hash', routes.getBlock)
 | 
			
		||||
        .get(config.API_ENDPOINT + 'explorer/block/:hash/tx', routes.getBlockTransactions)
 | 
			
		||||
        .get(config.API_ENDPOINT + 'explorer/block/:hash/tx/:index', routes.getBlockTransactionsFromIndex)
 | 
			
		||||
        ;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -99,6 +99,33 @@ class Routes {
 | 
			
		||||
      res.status(500).send(e.message);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async getBlock(req, res) {
 | 
			
		||||
    try {
 | 
			
		||||
      const result = await bitcoinApi.getBlock(req.params.hash);
 | 
			
		||||
      res.send(result);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      res.status(500).send(e.message);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async getBlockTransactions(req, res) {
 | 
			
		||||
    try {
 | 
			
		||||
      const result = await bitcoinApi.getBlockTransactions(req.params.hash);
 | 
			
		||||
      res.send(result);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      res.status(500).send(e.message);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async getBlockTransactionsFromIndex(req, res) {
 | 
			
		||||
    try {
 | 
			
		||||
      const result = await bitcoinApi.getBlockTransactionsFromIndex(req.params.hash, req.params.index);
 | 
			
		||||
      res.send(result);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      res.status(500).send(e.message);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default new Routes();
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,8 @@
 | 
			
		||||
<div class="modal-header">
 | 
			
		||||
  <h4 class="modal-title">Fee distribution for block <a href="https://www.blockstream.info/block-height/{{ block.height }}" target="_blank">#{{ block.height }}</a></h4>
 | 
			
		||||
  <h4 class="modal-title">Fee distribution for block 
 | 
			
		||||
    <a *ngIf="!isEsploraEnabled" href="https://www.blockstream.info/block-height/{{ block.height }}" target="_blank">#{{ block.height }}</a>
 | 
			
		||||
    <a *ngIf="isEsploraEnabled" (click)="activeModal.dismiss()" [routerLink]="['/explorer/block/', block.hash]">#{{ block.height }}</a>
 | 
			
		||||
  </h4>
 | 
			
		||||
  <button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross click')">
 | 
			
		||||
    <span aria-hidden="true">×</span>
 | 
			
		||||
  </button>
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ import { Component, OnInit, Input } from '@angular/core';
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
 | 
			
		||||
import { IBlock } from '../../blockchain/interfaces';
 | 
			
		||||
import { MemPoolService } from '../../services/mem-pool.service';
 | 
			
		||||
import { environment } from '../../../environments/environment';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-block-modal',
 | 
			
		||||
@ -11,7 +12,7 @@ import { MemPoolService } from '../../services/mem-pool.service';
 | 
			
		||||
export class BlockModalComponent implements OnInit {
 | 
			
		||||
  @Input() block: IBlock;
 | 
			
		||||
  blockSubsidy = 50;
 | 
			
		||||
 | 
			
		||||
  isEsploraEnabled = !!environment.esplora;
 | 
			
		||||
  conversions: any;
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
 | 
			
		||||
@ -1 +1,77 @@
 | 
			
		||||
<p>block works!</p>
 | 
			
		||||
<div class="container">
 | 
			
		||||
    <h1>Block <ng-template [ngIf]="!isLoadingBlock"><a [routerLink]="['/explorer/block/', block.id]">#{{ block.height }}</a></ng-template></h1>
 | 
			
		||||
  
 | 
			
		||||
    <ng-template [ngIf]="!isLoadingBlock" [ngIfElse]="loadingBlock">
 | 
			
		||||
 | 
			
		||||
      <br>
 | 
			
		||||
  
 | 
			
		||||
      <div class="row">
 | 
			
		||||
        <div class="col">
 | 
			
		||||
          <table class="table table-borderless table-striped">
 | 
			
		||||
            <tbody>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td>Timestamp</td>
 | 
			
		||||
                <td>{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }} ({{ block.timestamp | timeSince }} ago)</td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td>Number of transactions</td>
 | 
			
		||||
                <td>{{ block.tx_count }}</td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td>Size</td>
 | 
			
		||||
                <td>{{ block.size | bytes: 2 }}</td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td>Weight</td>
 | 
			
		||||
                <td>{{ block.weight | wuBytes: 2 }}</td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td>Status</td>
 | 
			
		||||
                <td>{{ latestBlockHeight - block.height + 1 }} confirmations</td>
 | 
			
		||||
              </tr>
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="col">
 | 
			
		||||
          <table class="table table-borderless table-striped">
 | 
			
		||||
            <tbody>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td>Hash</td>
 | 
			
		||||
                <td><a [routerLink]="['/explorer/block/', block.id]">{{ block.id | shortenString : 36 }}</a></td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td>Previous Block</td>
 | 
			
		||||
                <td><a [routerLink]="['/explorer/block/', block.previousblockhash]">{{ block.previousblockhash | shortenString : 36 }}</a></td>
 | 
			
		||||
              </tr>
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <br>
 | 
			
		||||
 | 
			
		||||
      <h2>{{ transactions?.length || '?' }} of {{ block.tx_count }} transactions</h2>
 | 
			
		||||
 | 
			
		||||
      <br>
 | 
			
		||||
 | 
			
		||||
      <app-transactions-list [transactions]="transactions"></app-transactions-list>
 | 
			
		||||
 | 
			
		||||
      <div class="text-center">
 | 
			
		||||
        <ng-template [ngIf]="isLoadingTransactions">
 | 
			
		||||
          <div class="spinner-border text-light"></div>
 | 
			
		||||
          <br><br>
 | 
			
		||||
        </ng-template>
 | 
			
		||||
        <button  *ngIf="transactions?.length" type="button" class="btn btn-primary" (click)="loadMore()">Load more</button>
 | 
			
		||||
      </div>
 | 
			
		||||
  
 | 
			
		||||
    </ng-template>
 | 
			
		||||
    <ng-template #loadingBlock>
 | 
			
		||||
      <div class="text-center">
 | 
			
		||||
        <div class="spinner-border text-light"></div>
 | 
			
		||||
        <br><br>
 | 
			
		||||
      </div>
 | 
			
		||||
    </ng-template>
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
  
 | 
			
		||||
  <br>
 | 
			
		||||
@ -1,4 +1,9 @@
 | 
			
		||||
import { Component, OnInit } from '@angular/core';
 | 
			
		||||
import { ActivatedRoute, ParamMap } from '@angular/router';
 | 
			
		||||
import { ApiService } from 'src/app/services/api.service';
 | 
			
		||||
import { MemPoolService } from 'src/app/services/mem-pool.service';
 | 
			
		||||
import { switchMap } from 'rxjs/operators';
 | 
			
		||||
import { ChangeDetectorRef } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-block',
 | 
			
		||||
@ -6,10 +11,55 @@ import { Component, OnInit } from '@angular/core';
 | 
			
		||||
  styleUrls: ['./block.component.scss']
 | 
			
		||||
})
 | 
			
		||||
export class BlockComponent implements OnInit {
 | 
			
		||||
  block: any;
 | 
			
		||||
  isLoadingBlock = true;
 | 
			
		||||
  latestBlockHeight: number;
 | 
			
		||||
  transactions: any[];
 | 
			
		||||
  isLoadingTransactions = true;
 | 
			
		||||
 | 
			
		||||
  constructor() { }
 | 
			
		||||
  constructor(
 | 
			
		||||
    private route: ActivatedRoute,
 | 
			
		||||
    private apiService: ApiService,
 | 
			
		||||
    private memPoolService: MemPoolService,
 | 
			
		||||
    private ref: ChangeDetectorRef,
 | 
			
		||||
  ) { }
 | 
			
		||||
 | 
			
		||||
  ngOnInit() {
 | 
			
		||||
    this.route.paramMap.pipe(
 | 
			
		||||
      switchMap((params: ParamMap) => {
 | 
			
		||||
        this.isLoadingBlock = true;
 | 
			
		||||
        const blockHash: string = params.get('id') || '';
 | 
			
		||||
        this.getBlockTransactions(blockHash);
 | 
			
		||||
        return this.apiService.getBlock$(blockHash);
 | 
			
		||||
      })
 | 
			
		||||
    )
 | 
			
		||||
    .subscribe((block) => {
 | 
			
		||||
      this.block = block;
 | 
			
		||||
      this.isLoadingBlock = false;
 | 
			
		||||
      this.ref.markForCheck();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    this.memPoolService.blocks$
 | 
			
		||||
      .subscribe((block) => {
 | 
			
		||||
        this.latestBlockHeight = block.height;
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getBlockTransactions(hash: string) {
 | 
			
		||||
    this.apiService.getBlockTransactions$(hash)
 | 
			
		||||
      .subscribe((transactions: any) => {
 | 
			
		||||
        this.transactions = transactions;
 | 
			
		||||
        this.isLoadingTransactions = false;
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  loadMore() {
 | 
			
		||||
    this.isLoadingTransactions = true;
 | 
			
		||||
    this.apiService.getBlockTransactions$(this.block.id, this.transactions.length)
 | 
			
		||||
      .subscribe((transactions) => {
 | 
			
		||||
        this.transactions = this.transactions.concat(transactions);
 | 
			
		||||
        this.isLoadingTransactions = false;
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,7 @@ import { RouterModule, Routes } from '@angular/router';
 | 
			
		||||
import { SharedModule } from '../shared/shared.module';
 | 
			
		||||
import { BlockComponent } from './block/block.component';
 | 
			
		||||
import { AddressComponent } from './address/address.component';
 | 
			
		||||
import { TransactionsListComponent } from './transactions-list/transactions-list.component';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
  {
 | 
			
		||||
@ -27,7 +28,7 @@ const routes: Routes = [
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
  declarations: [ExplorerComponent, TransactionComponent, BlockComponent, AddressComponent],
 | 
			
		||||
  declarations: [ExplorerComponent, TransactionComponent, BlockComponent, AddressComponent, TransactionsListComponent],
 | 
			
		||||
  imports: [
 | 
			
		||||
    SharedModule,
 | 
			
		||||
    CommonModule,
 | 
			
		||||
 | 
			
		||||
@ -7,8 +7,8 @@
 | 
			
		||||
      <th>Timestamp</th>
 | 
			
		||||
      <th>Mined</th>
 | 
			
		||||
      <th>Transactions</th>
 | 
			
		||||
      <th>Size (kB)</th>
 | 
			
		||||
      <th>Weight (kWU)</th>
 | 
			
		||||
      <th>Size</th>
 | 
			
		||||
      <th>Weight</th>
 | 
			
		||||
    </thead>
 | 
			
		||||
    <tbody>
 | 
			
		||||
      <tr *ngFor="let block of blocks; let i= index;">
 | 
			
		||||
@ -17,7 +17,7 @@
 | 
			
		||||
        <td>{{ block.timestamp | timeSince }} ago </td>
 | 
			
		||||
        <td>{{ block.tx_count }}</td>
 | 
			
		||||
        <td>{{ block.size | bytes: 2 }}</td>
 | 
			
		||||
        <td>{{ block.weight | bytes: 2 }}</td>
 | 
			
		||||
        <td>{{ block.weight | wuBytes: 2 }}</td>
 | 
			
		||||
      </tr>
 | 
			
		||||
    </tbody>
 | 
			
		||||
  </table>
 | 
			
		||||
 | 
			
		||||
@ -1,96 +1,12 @@
 | 
			
		||||
<div class="container">
 | 
			
		||||
  <h1>Transaction</h1>
 | 
			
		||||
 | 
			
		||||
  <br>
 | 
			
		||||
 | 
			
		||||
  <ng-template [ngIf]="!isLoadingTx" [ngIfElse]="loadingTx">
 | 
			
		||||
 | 
			
		||||
    <table class="table table-borderless">
 | 
			
		||||
      <thead>
 | 
			
		||||
        <tr class="header-bg">
 | 
			
		||||
          <th>
 | 
			
		||||
            {{ tx.txid }}
 | 
			
		||||
          </th>
 | 
			
		||||
        </tr>
 | 
			
		||||
      </thead>
 | 
			
		||||
    </table>
 | 
			
		||||
    <app-transactions-list [transactions]="[tx]" [showConfirmations]="true"></app-transactions-list>
 | 
			
		||||
 | 
			
		||||
    <div class="row">
 | 
			
		||||
        <div class="col">
 | 
			
		||||
 | 
			
		||||
          <table class="table table-borderless smaller-text">
 | 
			
		||||
            <tbody>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td>
 | 
			
		||||
                  <div *ngFor="let vin of tx.vin">
 | 
			
		||||
                    <ng-template [ngIf]="vin.is_coinbase" [ngIfElse]="regularVin">
 | 
			
		||||
                      Coinbase
 | 
			
		||||
                    </ng-template>
 | 
			
		||||
                    <ng-template #regularVin>
 | 
			
		||||
                      <a [routerLink]="['/explorer/address/', vin.prevout.scriptpubkey_address]">{{ vin.prevout.scriptpubkey_address }}</a>
 | 
			
		||||
                      (<a [routerLink]="['/explorer/tx/', vin.txid]">tx</a>)
 | 
			
		||||
                    </ng-template>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </td>
 | 
			
		||||
                <td class="text-right">
 | 
			
		||||
                  <div *ngFor="let vin of tx.vin">
 | 
			
		||||
                    <ng-template [ngIf]="vin.prevout">
 | 
			
		||||
                      <ng-template [ngIf]="viewFiat" [ngIfElse]="viewFiatVin">
 | 
			
		||||
                        <span class="green-color">{{ conversions.USD * (vin.prevout.value / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span>
 | 
			
		||||
                      </ng-template>
 | 
			
		||||
                      <ng-template #viewFiatVin>
 | 
			
		||||
                        {{ vin.prevout.value / 100000000 }} BTC
 | 
			
		||||
                      </ng-template>
 | 
			
		||||
                    </ng-template>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </td>
 | 
			
		||||
              </tr>
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="col">
 | 
			
		||||
          <table class="table table-borderless smaller-text">
 | 
			
		||||
            <tbody>
 | 
			
		||||
              <tr>
 | 
			
		||||
              <td>
 | 
			
		||||
                <div *ngFor="let vout of tx.vout">
 | 
			
		||||
                  <a *ngIf="vout.scriptpubkey_address; else scriptpubkey_type" [routerLink]="['/explorer/address/', vout.scriptpubkey_address]">{{ vout.scriptpubkey_address }}</a>
 | 
			
		||||
                  <ng-template #scriptpubkey_type>
 | 
			
		||||
                    {{ vout.scriptpubkey_type | uppercase }}
 | 
			
		||||
                  </ng-template>
 | 
			
		||||
                </div>
 | 
			
		||||
              </td>
 | 
			
		||||
              <td class="text-right">
 | 
			
		||||
                <div *ngFor="let vout of tx.vout">
 | 
			
		||||
                  <ng-template [ngIf]="viewFiat" [ngIfElse]="viewFiatVout">
 | 
			
		||||
                    <span class="green-color">{{ conversions.USD * (vout.value / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span>
 | 
			
		||||
                  </ng-template>
 | 
			
		||||
                  <ng-template #viewFiatVout>
 | 
			
		||||
                    {{ vout.value / 100000000 }} BTC
 | 
			
		||||
                  </ng-template>
 | 
			
		||||
                </div>
 | 
			
		||||
              </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
            <tr>
 | 
			
		||||
              <td class="text-right" colspan="4">
 | 
			
		||||
                <button *ngIf="tx.status.confirmed" type="button" class="btn btn-success">{{ latestBlockHeight - tx.status.block_height + 1 }} confirmations</button>
 | 
			
		||||
                <ng-template #unconfirmedButton>
 | 
			
		||||
                  <button type="button" class="btn btn-danger">Unconfirmed</button>
 | 
			
		||||
                </ng-template>
 | 
			
		||||
                 
 | 
			
		||||
                <button type="button" class="btn btn-primary" (click)="viewFiat = !viewFiat">
 | 
			
		||||
                  <ng-template [ngIf]="viewFiat" [ngIfElse]="viewFiatButton">
 | 
			
		||||
                    <span *ngIf="conversions">{{ conversions.USD * (totalOutput / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span>
 | 
			
		||||
                  </ng-template>
 | 
			
		||||
                  <ng-template #viewFiatButton>
 | 
			
		||||
                    {{ totalOutput / 100000000 }} BTC
 | 
			
		||||
                  </ng-template>
 | 
			
		||||
                </button>
 | 
			
		||||
              </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <br>
 | 
			
		||||
 | 
			
		||||
    <h2>Details</h2>
 | 
			
		||||
@ -103,11 +19,11 @@
 | 
			
		||||
          <tbody>
 | 
			
		||||
            <tr>
 | 
			
		||||
              <td>Size</td>
 | 
			
		||||
              <td>{{ tx.size | bytes }}</td>
 | 
			
		||||
              <td>{{ tx.size | bytes: 2 }}</td>
 | 
			
		||||
            </tr>
 | 
			
		||||
            <tr>
 | 
			
		||||
              <td>Weight</td>
 | 
			
		||||
              <td>{{ tx.weight }} WU</td>
 | 
			
		||||
              <td>{{ tx.weight | wuBytes: 2 }}</td>
 | 
			
		||||
            </tr>
 | 
			
		||||
            <tr *ngIf="tx.status.confirmed">
 | 
			
		||||
              <td>Included in block</td>
 | 
			
		||||
@ -117,7 +33,7 @@
 | 
			
		||||
        </table>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="col">
 | 
			
		||||
        <table class="table table-borderless table-striped"  *ngIf="tx.fee">
 | 
			
		||||
        <table class="table table-borderless table-striped" *ngIf="tx.fee">
 | 
			
		||||
          <tbody>
 | 
			
		||||
            <tr>
 | 
			
		||||
              <td>Fees</td>
 | 
			
		||||
 | 
			
		||||
@ -13,10 +13,6 @@ export class TransactionComponent implements OnInit {
 | 
			
		||||
  tx: any;
 | 
			
		||||
  isLoadingTx = true;
 | 
			
		||||
  conversions: any;
 | 
			
		||||
  totalOutput: number;
 | 
			
		||||
 | 
			
		||||
  viewFiat = false;
 | 
			
		||||
  latestBlockHeight: number;
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private route: ActivatedRoute,
 | 
			
		||||
@ -33,7 +29,6 @@ export class TransactionComponent implements OnInit {
 | 
			
		||||
    )
 | 
			
		||||
    .subscribe((tx) => {
 | 
			
		||||
      this.tx = tx;
 | 
			
		||||
      this.totalOutput = this.tx.vout.map((v: any) => v.value || 0).reduce((a: number, b: number) => a + b);
 | 
			
		||||
      this.isLoadingTx = false;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -41,10 +36,5 @@ export class TransactionComponent implements OnInit {
 | 
			
		||||
      .subscribe((conversions) => {
 | 
			
		||||
        this.conversions = conversions;
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    this.memPoolService.blocks$
 | 
			
		||||
      .subscribe((block) => {
 | 
			
		||||
        this.latestBlockHeight = block.height;
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,94 @@
 | 
			
		||||
<ng-template ngFor let-tx [ngForOf]="transactions">
 | 
			
		||||
    
 | 
			
		||||
  <table class="table table-borderless">
 | 
			
		||||
      <thead>
 | 
			
		||||
        <tr class="header-bg">
 | 
			
		||||
          <th>
 | 
			
		||||
            <a [routerLink]="['/explorer/tx/', tx.txid]">{{ tx.txid }}</a>
 | 
			
		||||
          </th>
 | 
			
		||||
        </tr>
 | 
			
		||||
      </thead>
 | 
			
		||||
    </table>
 | 
			
		||||
 | 
			
		||||
    <div class="row">
 | 
			
		||||
        <div class="col">
 | 
			
		||||
          <table class="table table-borderless smaller-text">
 | 
			
		||||
            <tbody>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td>
 | 
			
		||||
                  <div *ngFor="let vin of tx.vin">
 | 
			
		||||
                    <ng-template [ngIf]="vin.is_coinbase" [ngIfElse]="regularVin">
 | 
			
		||||
                      Coinbase
 | 
			
		||||
                    </ng-template>
 | 
			
		||||
                    <ng-template #regularVin>
 | 
			
		||||
                      <a [routerLink]="['/explorer/address/', vin.prevout.scriptpubkey_address]">{{ vin.prevout.scriptpubkey_address }}</a>
 | 
			
		||||
                      (<a [routerLink]="['/explorer/tx/', vin.txid]">tx</a>)
 | 
			
		||||
                    </ng-template>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </td>
 | 
			
		||||
                <td class="text-right">
 | 
			
		||||
                  <div *ngFor="let vin of tx.vin">
 | 
			
		||||
                    <ng-template [ngIf]="vin.prevout">
 | 
			
		||||
                      <ng-template [ngIf]="viewFiat" [ngIfElse]="viewFiatVin">
 | 
			
		||||
                        <span class="green-color">{{ conversions.USD * (vin.prevout.value / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span>
 | 
			
		||||
                      </ng-template>
 | 
			
		||||
                      <ng-template #viewFiatVin>
 | 
			
		||||
                        {{ vin.prevout.value / 100000000 }} BTC
 | 
			
		||||
                      </ng-template>
 | 
			
		||||
                    </ng-template>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </td>
 | 
			
		||||
              </tr>
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="col">
 | 
			
		||||
          <table class="table table-borderless smaller-text">
 | 
			
		||||
            <tbody>
 | 
			
		||||
              <tr>
 | 
			
		||||
              <td>
 | 
			
		||||
                <div *ngFor="let vout of tx.vout">
 | 
			
		||||
                  <a *ngIf="vout.scriptpubkey_address; else scriptpubkey_type" [routerLink]="['/explorer/address/', vout.scriptpubkey_address]">{{ vout.scriptpubkey_address }}</a>
 | 
			
		||||
                  <ng-template #scriptpubkey_type>
 | 
			
		||||
                    {{ vout.scriptpubkey_type | uppercase }}
 | 
			
		||||
                  </ng-template>
 | 
			
		||||
                </div>
 | 
			
		||||
              </td>
 | 
			
		||||
              <td class="text-right">
 | 
			
		||||
                <div *ngFor="let vout of tx.vout">
 | 
			
		||||
                  <ng-template [ngIf]="viewFiat" [ngIfElse]="viewFiatVout">
 | 
			
		||||
                    <span class="green-color">{{ conversions.USD * (vout.value / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span>
 | 
			
		||||
                  </ng-template>
 | 
			
		||||
                  <ng-template #viewFiatVout>
 | 
			
		||||
                    {{ vout.value / 100000000 }} BTC
 | 
			
		||||
                  </ng-template>
 | 
			
		||||
                </div>
 | 
			
		||||
              </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
            <tr>
 | 
			
		||||
              <td class="text-right" colspan="4">
 | 
			
		||||
                <ng-template [ngIf]="showConfirmations">
 | 
			
		||||
                  <button *ngIf="tx.status.confirmed; else unconfirmedButton" type="button" class="btn btn-success">{{ latestBlockHeight - tx.status.block_height + 1 }} confirmations</button>
 | 
			
		||||
                  <ng-template #unconfirmedButton>
 | 
			
		||||
                    <button type="button" class="btn btn-danger">Unconfirmed</button>
 | 
			
		||||
                  </ng-template>
 | 
			
		||||
                   
 | 
			
		||||
                </ng-template>
 | 
			
		||||
                <button type="button" class="btn btn-primary" (click)="viewFiat = !viewFiat">
 | 
			
		||||
                  <ng-template [ngIf]="viewFiat" [ngIfElse]="viewFiatButton">
 | 
			
		||||
                    <span *ngIf="conversions">{{ conversions.USD * (getTotalTxOutput(tx) / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span>
 | 
			
		||||
                  </ng-template>
 | 
			
		||||
                  <ng-template #viewFiatButton>
 | 
			
		||||
                    {{ getTotalTxOutput(tx) / 100000000 }} BTC
 | 
			
		||||
                  </ng-template>
 | 
			
		||||
                </button>
 | 
			
		||||
              </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
  </ng-template>
 | 
			
		||||
  
 | 
			
		||||
@ -0,0 +1,3 @@
 | 
			
		||||
.header-bg {
 | 
			
		||||
  background-color: #181b2d !important;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,36 @@
 | 
			
		||||
import { Component, OnInit, Input } from '@angular/core';
 | 
			
		||||
import { MemPoolService } from 'src/app/services/mem-pool.service';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-transactions-list',
 | 
			
		||||
  templateUrl: './transactions-list.component.html',
 | 
			
		||||
  styleUrls: ['./transactions-list.component.scss']
 | 
			
		||||
})
 | 
			
		||||
export class TransactionsListComponent implements OnInit {
 | 
			
		||||
  @Input() transactions: any[];
 | 
			
		||||
  @Input() showConfirmations = false;
 | 
			
		||||
  latestBlockHeight: number;
 | 
			
		||||
 | 
			
		||||
  viewFiat = false;
 | 
			
		||||
  conversions: any;
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private memPoolService: MemPoolService,
 | 
			
		||||
  ) { }
 | 
			
		||||
 | 
			
		||||
  ngOnInit() {
 | 
			
		||||
    this.memPoolService.conversions$
 | 
			
		||||
      .subscribe((conversions) => {
 | 
			
		||||
        this.conversions = conversions;
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    this.memPoolService.blocks$
 | 
			
		||||
      .subscribe((block) => {
 | 
			
		||||
        this.latestBlockHeight = block.height;
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getTotalTxOutput(tx: any) {
 | 
			
		||||
    return tx.vout.map((v: any) => v.value || 0).reduce((a: number, b: number) => a + b);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -162,11 +162,19 @@ export class ApiService {
 | 
			
		||||
    return this.httpClient.get<IMempoolStats[]>(API_BASE_URL + '/statistics/6m');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  listBlocks$(height?: number): Observable<IBlockTransaction[]> {
 | 
			
		||||
    return this.httpClient.get<IBlockTransaction[]>(API_BASE_URL + '/explorer/blocks/' + (height || ''));
 | 
			
		||||
  listBlocks$(height?: number): Observable<any[]> {
 | 
			
		||||
    return this.httpClient.get<any[]>(API_BASE_URL + '/explorer/blocks/' + (height || ''));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getTransaction$(txId: string): Observable<IBlockTransaction[]> {
 | 
			
		||||
    return this.httpClient.get<IBlockTransaction[]>(API_BASE_URL + '/explorer/tx/' + txId);
 | 
			
		||||
  getTransaction$(txId: string): Observable<any[]> {
 | 
			
		||||
    return this.httpClient.get<any[]>(API_BASE_URL + '/explorer/tx/' + txId);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getBlock$(hash: string): Observable<any[]> {
 | 
			
		||||
    return this.httpClient.get<any[]>(API_BASE_URL + '/explorer/block/' + hash);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getBlockTransactions$(hash: string, index?: number): Observable<any[]> {
 | 
			
		||||
    return this.httpClient.get<any[]>(API_BASE_URL + '/explorer/block/' + hash + '/tx/' + (index || ''));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										63
									
								
								frontend/src/app/shared/pipes/bytes-pipe/wubytes.pipe.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								frontend/src/app/shared/pipes/bytes-pipe/wubytes.pipe.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
import { Pipe, PipeTransform } from '@angular/core';
 | 
			
		||||
import { isNumberFinite, isPositive, isInteger, toDecimal } from './utils';
 | 
			
		||||
 | 
			
		||||
export type ByteUnit = 'WU' | 'kWU' | 'MWU' | 'GWU' | 'TWU';
 | 
			
		||||
 | 
			
		||||
@Pipe({
 | 
			
		||||
    name: 'wuBytes'
 | 
			
		||||
})
 | 
			
		||||
export class WuBytesPipe implements PipeTransform {
 | 
			
		||||
 | 
			
		||||
    static formats: { [key: string]: { max: number, prev?: ByteUnit } } = {
 | 
			
		||||
        'WU': {max: 1000},
 | 
			
		||||
        'kWU': {max: Math.pow(1000, 2), prev: 'WU'},
 | 
			
		||||
        'MWU': {max: Math.pow(1000, 3), prev: 'kWU'},
 | 
			
		||||
        'GWU': {max: Math.pow(1000, 4), prev: 'MWU'},
 | 
			
		||||
        'TWU': {max: Number.MAX_SAFE_INTEGER, prev: 'GWU'}
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    transform(input: any, decimal: number = 0, from: ByteUnit = 'WU', to?: ByteUnit): any {
 | 
			
		||||
 | 
			
		||||
        if (!(isNumberFinite(input) &&
 | 
			
		||||
                isNumberFinite(decimal) &&
 | 
			
		||||
                isInteger(decimal) &&
 | 
			
		||||
                isPositive(decimal))) {
 | 
			
		||||
            return input;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let bytes = input;
 | 
			
		||||
        let unit = from;
 | 
			
		||||
        while (unit !== 'WU') {
 | 
			
		||||
            bytes *= 1024;
 | 
			
		||||
            unit = WuBytesPipe.formats[unit].prev!;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (to) {
 | 
			
		||||
            const format = WuBytesPipe.formats[to];
 | 
			
		||||
 | 
			
		||||
            const result = toDecimal(WuBytesPipe.calculateResult(format, bytes), decimal);
 | 
			
		||||
 | 
			
		||||
            return WuBytesPipe.formatResult(result, to);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (const key in WuBytesPipe.formats) {
 | 
			
		||||
            const format = WuBytesPipe.formats[key];
 | 
			
		||||
            if (bytes < format.max) {
 | 
			
		||||
 | 
			
		||||
                const result = toDecimal(WuBytesPipe.calculateResult(format, bytes), decimal);
 | 
			
		||||
 | 
			
		||||
                return WuBytesPipe.formatResult(result, key);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static formatResult(result: number, unit: string): string {
 | 
			
		||||
        return `${result} ${unit}`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static calculateResult(format: { max: number, prev?: ByteUnit }, bytes: number) {
 | 
			
		||||
        const prev = format.prev ? WuBytesPipe.formats[format.prev] : undefined;
 | 
			
		||||
        return prev ? bytes / prev.max : bytes;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
import { Pipe, PipeTransform } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
@Pipe({ name: 'shortenString' })
 | 
			
		||||
export class ShortenStringPipe implements PipeTransform {
 | 
			
		||||
  transform(str: string, length: number = 12) {
 | 
			
		||||
    const half = length / 2;
 | 
			
		||||
    return str.substring(0, half) + '...' + str.substring(str.length - half);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -8,6 +8,8 @@ import { RoundPipe } from './pipes/math-round-pipe/math-round.pipe';
 | 
			
		||||
import { CeilPipe } from './pipes/math-ceil/math-ceil.pipe';
 | 
			
		||||
import { ChartistComponent } from '../statistics/chartist.component';
 | 
			
		||||
import { TimeSincePipe } from './pipes/time-since/time-since.pipe';
 | 
			
		||||
import { WuBytesPipe } from './pipes/bytes-pipe/wubytes.pipe';
 | 
			
		||||
import { ShortenStringPipe } from './pipes/shorten-string-pipe/shorten-string.pipe';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
  imports: [
 | 
			
		||||
@ -21,6 +23,8 @@ import { TimeSincePipe } from './pipes/time-since/time-since.pipe';
 | 
			
		||||
    CeilPipe,
 | 
			
		||||
    BytesPipe,
 | 
			
		||||
    VbytesPipe,
 | 
			
		||||
    WuBytesPipe,
 | 
			
		||||
    ShortenStringPipe,
 | 
			
		||||
    TimeSincePipe,
 | 
			
		||||
  ],
 | 
			
		||||
  exports: [
 | 
			
		||||
@ -28,7 +32,9 @@ import { TimeSincePipe } from './pipes/time-since/time-since.pipe';
 | 
			
		||||
    CeilPipe,
 | 
			
		||||
    BytesPipe,
 | 
			
		||||
    VbytesPipe,
 | 
			
		||||
    WuBytesPipe,
 | 
			
		||||
    TimeSincePipe,
 | 
			
		||||
    ShortenStringPipe,
 | 
			
		||||
    NgbButtonsModule,
 | 
			
		||||
    NgbModalModule,
 | 
			
		||||
    ChartistComponent,
 | 
			
		||||
@ -36,6 +42,8 @@ import { TimeSincePipe } from './pipes/time-since/time-since.pipe';
 | 
			
		||||
  providers: [
 | 
			
		||||
    BytesPipe,
 | 
			
		||||
    VbytesPipe,
 | 
			
		||||
    WuBytesPipe,
 | 
			
		||||
    ShortenStringPipe,
 | 
			
		||||
  ]
 | 
			
		||||
})
 | 
			
		||||
export class SharedModule { }
 | 
			
		||||
 | 
			
		||||
@ -4,8 +4,8 @@
 | 
			
		||||
        <tr>
 | 
			
		||||
            <td class="text-left"><b>Transaction hash</b></td>
 | 
			
		||||
            <td class="text-right">
 | 
			
		||||
              <a *ngIf="!isEsploraEnabled" href="https://www.blockstream.info/tx/{{ tx?.txid }}" target="_blank">{{ txIdShort }}</a>
 | 
			
		||||
              <a *ngIf="isEsploraEnabled" [routerLink]="['/explorer/tx/', tx?.txid]">{{ txIdShort }}</a>
 | 
			
		||||
              <a *ngIf="!isEsploraEnabled" href="https://www.blockstream.info/tx/{{ tx?.txid }}" target="_blank">{{ tx?.txid | shortenString }}</a>
 | 
			
		||||
              <a *ngIf="isEsploraEnabled" [routerLink]="['/explorer/tx/', tx?.txid]">{{ tx?.txid | shortenString }}</a>
 | 
			
		||||
            </td>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
 | 
			
		||||
@ -61,9 +61,6 @@ export class TxBubbleComponent implements OnInit, OnDestroy {
 | 
			
		||||
        if (this.txShowTxNotFound) {
 | 
			
		||||
          setTimeout(() => { this.txShowTxNotFound = false; }, 2000);
 | 
			
		||||
        }
 | 
			
		||||
        if (this.tx) {
 | 
			
		||||
          this.txIdShort = this.tx.txid.substring(0, 6) + '...' + this.tx.txid.substring(this.tx.txid.length - 6);
 | 
			
		||||
        }
 | 
			
		||||
        if (this.latestBlockHeight) {
 | 
			
		||||
          this.confirmations = (this.latestBlockHeight - this.txTrackingBlockHeight) + 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -146,10 +146,6 @@ hr {
 | 
			
		||||
  background-color: #181b2d !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.header-bg {
 | 
			
		||||
  background-color: #653b9c;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.bordertop {
 | 
			
		||||
  border-top: 1px solid #4c4c4c;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user