Basic Liquid Asset support.
This commit is contained in:
		
							parent
							
								
									7e7b536acb
								
							
						
					
					
						commit
						b2d2fd225c
					
				@ -10,6 +10,7 @@ import { TelevisionComponent } from './components/television/television.componen
 | 
			
		||||
import { StatisticsComponent } from './components/statistics/statistics.component';
 | 
			
		||||
import { MempoolBlockComponent } from './components/mempool-block/mempool-block.component';
 | 
			
		||||
import { LatestBlocksComponent } from './components/latest-blocks/latest-blocks.component';
 | 
			
		||||
import { AssetComponent } from './components/asset/asset.component';
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
  {
 | 
			
		||||
@ -36,6 +37,10 @@ const routes: Routes = [
 | 
			
		||||
            path: 'mempool-block/:id',
 | 
			
		||||
            component: MempoolBlockComponent
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            path: 'asset/:id',
 | 
			
		||||
            component: AssetComponent
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
 | 
			
		||||
@ -45,6 +45,8 @@ import { FeeDistributionGraphComponent } from './components/fee-distribution-gra
 | 
			
		||||
import { TimespanComponent } from './components/timespan/timespan.component';
 | 
			
		||||
import { SeoService } from './services/seo.service';
 | 
			
		||||
import { MempoolGraphComponent } from './components/mempool-graph/mempool-graph.component';
 | 
			
		||||
import { AssetComponent } from './components/asset/asset.component';
 | 
			
		||||
import { ScriptpubkeyTypePipe } from './pipes/scriptpubkey-type-pipe/scriptpubkey-type.pipe';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
  declarations: [
 | 
			
		||||
@ -80,6 +82,8 @@ import { MempoolGraphComponent } from './components/mempool-graph/mempool-graph.
 | 
			
		||||
    MempoolBlockComponent,
 | 
			
		||||
    FeeDistributionGraphComponent,
 | 
			
		||||
    MempoolGraphComponent,
 | 
			
		||||
    AssetComponent,
 | 
			
		||||
    ScriptpubkeyTypePipe,
 | 
			
		||||
  ],
 | 
			
		||||
  imports: [
 | 
			
		||||
    BrowserModule,
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										124
									
								
								frontend/src/app/components/asset/asset.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								frontend/src/app/components/asset/asset.component.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,124 @@
 | 
			
		||||
<div class="container-xl">
 | 
			
		||||
  <h1 style="float: left;">Asset</h1>
 | 
			
		||||
  <a [routerLink]="['/asset/', assetString]" style="line-height: 56px; margin-left: 10px;">
 | 
			
		||||
    <span class="d-inline d-lg-none">{{ assetString | shortenString : 24 }}</span>
 | 
			
		||||
    <span class="d-none d-lg-inline">{{ assetString }}</span>
 | 
			
		||||
  </a>
 | 
			
		||||
  <app-clipboard [text]="assetString"></app-clipboard>
 | 
			
		||||
  <br>
 | 
			
		||||
 | 
			
		||||
  <div class="clearfix"></div>
 | 
			
		||||
 | 
			
		||||
  <ng-template [ngIf]="!isLoadingAsset && !error">
 | 
			
		||||
    <div class="box">
 | 
			
		||||
 | 
			
		||||
      <div class="row">
 | 
			
		||||
        <div class="col">
 | 
			
		||||
          <table class="table table-borderless table-striped">
 | 
			
		||||
            <tbody>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td>Name</td>
 | 
			
		||||
                <td>{{ asset.name }} ({{ asset.ticker }})</td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td>Precision</td>
 | 
			
		||||
                <td>{{ asset.precision }}</td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td>Issuer</td>
 | 
			
		||||
                <td><a target="_blank" href="{{ 'http://' + asset.contract.entity.domain }}">{{ asset.contract.entity.domain }}</a></td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td>Issuance tx</td>
 | 
			
		||||
                <td><a [routerLink]="['/tx/', asset.issuance_txin.txid]">{{ asset.issuance_txin.txid | shortenString : 13 }}</a> <app-clipboard class="d-none d-sm-inline-block" [text]="asset.issuance_txin.txid"></app-clipboard></td>
 | 
			
		||||
              </tr>
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="col">
 | 
			
		||||
          <table class="table table-borderless table-striped">
 | 
			
		||||
            <tbody>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td>Circulating amount</td>
 | 
			
		||||
                <td>{{ (asset.chain_stats.issued_amount - asset.chain_stats.burned_amount) / 100000000 | number: '1.0-' + asset.precision }}</td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td>Issued amount</td>
 | 
			
		||||
                <td>{{ asset.chain_stats.issued_amount / 100000000 | number: '1.0-' + asset.precision }}</td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td>Burned amount</td>
 | 
			
		||||
                <td>{{ asset.chain_stats.burned_amount / 100000000 | number: '1.0-' + asset.precision }}</td>
 | 
			
		||||
              </tr>
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <br>
 | 
			
		||||
 | 
			
		||||
    <h2><ng-template [ngIf]="transactions?.length">{{ (transactions?.length | number) || '?' }} of </ng-template>{{ txCount | number }} transactions</h2>
 | 
			
		||||
 | 
			
		||||
    <app-transactions-list [transactions]="transactions" [showConfirmations]="true" (loadMore)="loadMore()"></app-transactions-list>
 | 
			
		||||
 | 
			
		||||
    <div class="text-center">
 | 
			
		||||
      <ng-template [ngIf]="isLoadingTransactions">
 | 
			
		||||
        <div class="header-bg box" style="padding: 10px; margin-bottom: 10px;">
 | 
			
		||||
          <span class="skeleton-loader"></span>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="header-bg box">
 | 
			
		||||
          <div class="row" style="height: 107px;">
 | 
			
		||||
            <div class="col-sm">
 | 
			
		||||
              <span class="skeleton-loader"></span>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="col-sm">
 | 
			
		||||
              <span class="skeleton-loader"></span>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </ng-template>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
  </ng-template>
 | 
			
		||||
 | 
			
		||||
  <ng-template [ngIf]="isLoadingAsset && !error">
 | 
			
		||||
 | 
			
		||||
    <div class="box">
 | 
			
		||||
      <div class="row">
 | 
			
		||||
        <div class="col">
 | 
			
		||||
          <table class="table table-borderless table-striped">
 | 
			
		||||
            <tbody>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td colspan="2"><span class="skeleton-loader"></span></td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td colspan="2"><span class="skeleton-loader"></span></td>
 | 
			
		||||
              </tr>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td colspan="2"><span class="skeleton-loader"></span></td>
 | 
			
		||||
              </tr>
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="col">
 | 
			
		||||
          
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
  </ng-template>
 | 
			
		||||
 | 
			
		||||
  <ng-template [ngIf]="error">
 | 
			
		||||
    <div class="text-center">
 | 
			
		||||
      Error loading asset data.
 | 
			
		||||
      <br>
 | 
			
		||||
      <i>{{ error.error }}</i>
 | 
			
		||||
    </div>
 | 
			
		||||
  </ng-template>
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<br>
 | 
			
		||||
							
								
								
									
										23
									
								
								frontend/src/app/components/asset/asset.component.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								frontend/src/app/components/asset/asset.component.scss
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
			
		||||
.qr-wrapper {
 | 
			
		||||
  background-color: #FFF;
 | 
			
		||||
  padding: 10px;
 | 
			
		||||
  padding-bottom: 5px;
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  margin-right: 25px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (min-width: 576px) {
 | 
			
		||||
  .qrcode-col {
 | 
			
		||||
    text-align: right;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@media (max-width: 575.98px) {
 | 
			
		||||
  .qrcode-col {
 | 
			
		||||
    text-align: center;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .qrcode-col > div {
 | 
			
		||||
    margin-top: 20px;
 | 
			
		||||
    margin-right: 0px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										198
									
								
								frontend/src/app/components/asset/asset.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								frontend/src/app/components/asset/asset.component.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,198 @@
 | 
			
		||||
import { Component, OnInit, OnDestroy } from '@angular/core';
 | 
			
		||||
import { ActivatedRoute, ParamMap } from '@angular/router';
 | 
			
		||||
import { ElectrsApiService } from '../../services/electrs-api.service';
 | 
			
		||||
import { switchMap, filter, catchError } from 'rxjs/operators';
 | 
			
		||||
import { Asset, Transaction } from '../../interfaces/electrs.interface';
 | 
			
		||||
import { WebsocketService } from 'src/app/services/websocket.service';
 | 
			
		||||
import { StateService } from 'src/app/services/state.service';
 | 
			
		||||
import { AudioService } from 'src/app/services/audio.service';
 | 
			
		||||
import { ApiService } from 'src/app/services/api.service';
 | 
			
		||||
import { of, merge, Subscription } from 'rxjs';
 | 
			
		||||
import { SeoService } from 'src/app/services/seo.service';
 | 
			
		||||
import { environment } from 'src/environments/environment';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-asset',
 | 
			
		||||
  templateUrl: './asset.component.html',
 | 
			
		||||
  styleUrls: ['./asset.component.scss']
 | 
			
		||||
})
 | 
			
		||||
export class AssetComponent implements OnInit, OnDestroy {
 | 
			
		||||
  network = environment.network;
 | 
			
		||||
 | 
			
		||||
  asset: Asset;
 | 
			
		||||
  assetString: string;
 | 
			
		||||
  isLoadingAsset = true;
 | 
			
		||||
  transactions: Transaction[];
 | 
			
		||||
  isLoadingTransactions = true;
 | 
			
		||||
  error: any;
 | 
			
		||||
  mainSubscription: Subscription;
 | 
			
		||||
 | 
			
		||||
  totalConfirmedTxCount = 0;
 | 
			
		||||
  loadedConfirmedTxCount = 0;
 | 
			
		||||
  txCount = 0;
 | 
			
		||||
  receieved = 0;
 | 
			
		||||
  sent = 0;
 | 
			
		||||
 | 
			
		||||
  private tempTransactions: Transaction[];
 | 
			
		||||
  private timeTxIndexes: number[];
 | 
			
		||||
  private lastTransactionTxId: string;
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private route: ActivatedRoute,
 | 
			
		||||
    private electrsApiService: ElectrsApiService,
 | 
			
		||||
    private websocketService: WebsocketService,
 | 
			
		||||
    private stateService: StateService,
 | 
			
		||||
    private audioService: AudioService,
 | 
			
		||||
    private apiService: ApiService,
 | 
			
		||||
    private seoService: SeoService,
 | 
			
		||||
  ) { }
 | 
			
		||||
 | 
			
		||||
  ngOnInit() {
 | 
			
		||||
    this.websocketService.want(['blocks', 'stats', 'mempool-blocks']);
 | 
			
		||||
 | 
			
		||||
    this.mainSubscription = this.route.paramMap
 | 
			
		||||
      .pipe(
 | 
			
		||||
        switchMap((params: ParamMap) => {
 | 
			
		||||
          this.error = undefined;
 | 
			
		||||
          this.isLoadingAsset = true;
 | 
			
		||||
          this.loadedConfirmedTxCount = 0;
 | 
			
		||||
          this.asset = null;
 | 
			
		||||
          this.isLoadingTransactions = true;
 | 
			
		||||
          this.transactions = null;
 | 
			
		||||
          document.body.scrollTo(0, 0);
 | 
			
		||||
          this.assetString = params.get('id') || '';
 | 
			
		||||
          this.seoService.setTitle('Asset: ' + this.assetString, true);
 | 
			
		||||
 | 
			
		||||
          return merge(
 | 
			
		||||
            of(true),
 | 
			
		||||
            this.stateService.connectionState$
 | 
			
		||||
              .pipe(filter((state) => state === 2 && this.transactions && this.transactions.length > 0))
 | 
			
		||||
          )
 | 
			
		||||
          .pipe(
 | 
			
		||||
            switchMap(() => this.electrsApiService.getAsset$(this.assetString)
 | 
			
		||||
              .pipe(
 | 
			
		||||
                catchError((err) => {
 | 
			
		||||
                  this.isLoadingAsset = false;
 | 
			
		||||
                  this.error = err;
 | 
			
		||||
                  console.log(err);
 | 
			
		||||
                  return of(null);
 | 
			
		||||
                })
 | 
			
		||||
              )
 | 
			
		||||
            )
 | 
			
		||||
          );
 | 
			
		||||
        })
 | 
			
		||||
      )
 | 
			
		||||
      .pipe(
 | 
			
		||||
        switchMap((asset: Asset) => {
 | 
			
		||||
          this.asset = asset;
 | 
			
		||||
          this.updateChainStats();
 | 
			
		||||
          this.websocketService.startTrackAsset(asset.asset_id);
 | 
			
		||||
          this.isLoadingAsset = false;
 | 
			
		||||
          this.isLoadingTransactions = true;
 | 
			
		||||
          return this.electrsApiService.getAssetTransactions$(asset.asset_id);
 | 
			
		||||
        }),
 | 
			
		||||
        switchMap((transactions) => {
 | 
			
		||||
          this.tempTransactions = transactions;
 | 
			
		||||
          if (transactions.length) {
 | 
			
		||||
            this.lastTransactionTxId = transactions[transactions.length - 1].txid;
 | 
			
		||||
            this.loadedConfirmedTxCount += transactions.filter((tx) => tx.status.confirmed).length;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          const fetchTxs: string[] = [];
 | 
			
		||||
          this.timeTxIndexes = [];
 | 
			
		||||
          transactions.forEach((tx, index) => {
 | 
			
		||||
            if (!tx.status.confirmed) {
 | 
			
		||||
              fetchTxs.push(tx.txid);
 | 
			
		||||
              this.timeTxIndexes.push(index);
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
          if (!fetchTxs.length) {
 | 
			
		||||
            return of([]);
 | 
			
		||||
          }
 | 
			
		||||
          return this.apiService.getTransactionTimes$(fetchTxs);
 | 
			
		||||
        })
 | 
			
		||||
      )
 | 
			
		||||
      .subscribe((times: number[]) => {
 | 
			
		||||
        times.forEach((time, index) => {
 | 
			
		||||
          this.tempTransactions[this.timeTxIndexes[index]].firstSeen = time;
 | 
			
		||||
        });
 | 
			
		||||
        this.tempTransactions.sort((a, b) => {
 | 
			
		||||
          return b.status.block_time - a.status.block_time || b.firstSeen - a.firstSeen;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.transactions = this.tempTransactions;
 | 
			
		||||
        this.isLoadingTransactions = false;
 | 
			
		||||
      },
 | 
			
		||||
      (error) => {
 | 
			
		||||
        console.log(error);
 | 
			
		||||
        this.error = error;
 | 
			
		||||
        this.isLoadingAsset = false;
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    this.stateService.mempoolTransactions$
 | 
			
		||||
      .subscribe((transaction) => {
 | 
			
		||||
        if (this.transactions.some((t) => t.txid === transaction.txid)) {
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.transactions.unshift(transaction);
 | 
			
		||||
        this.transactions = this.transactions.slice();
 | 
			
		||||
        this.txCount++;
 | 
			
		||||
 | 
			
		||||
        // if (transaction.vout.some((vout) => vout.scriptpubkey_asset === this.asset.asset)) {
 | 
			
		||||
        //   this.audioService.playSound('cha-ching');
 | 
			
		||||
        // } else {
 | 
			
		||||
        //   this.audioService.playSound('chime');
 | 
			
		||||
        // }
 | 
			
		||||
 | 
			
		||||
        // transaction.vin.forEach((vin) => {
 | 
			
		||||
        //   if (vin.prevout.scriptpubkey_asset === this.asset.asset) {
 | 
			
		||||
        //     this.sent += vin.prevout.value;
 | 
			
		||||
        //   }
 | 
			
		||||
        // });
 | 
			
		||||
        // transaction.vout.forEach((vout) => {
 | 
			
		||||
        //   if (vout.scriptpubkey_asset === this.asset.asset) {
 | 
			
		||||
        //     this.receieved += vout.value;
 | 
			
		||||
        //   }
 | 
			
		||||
        // });
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    this.stateService.blockTransactions$
 | 
			
		||||
      .subscribe((transaction) => {
 | 
			
		||||
        const tx = this.transactions.find((t) => t.txid === transaction.txid);
 | 
			
		||||
        if (tx) {
 | 
			
		||||
          tx.status = transaction.status;
 | 
			
		||||
          this.transactions = this.transactions.slice();
 | 
			
		||||
          this.audioService.playSound('magic');
 | 
			
		||||
        }
 | 
			
		||||
        this.totalConfirmedTxCount++;
 | 
			
		||||
        this.loadedConfirmedTxCount++;
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  loadMore() {
 | 
			
		||||
    if (this.isLoadingTransactions || !this.totalConfirmedTxCount || this.loadedConfirmedTxCount >= this.totalConfirmedTxCount) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    this.isLoadingTransactions = true;
 | 
			
		||||
    this.electrsApiService.getAddressTransactionsFromHash$(this.asset.asset_id, this.lastTransactionTxId)
 | 
			
		||||
      .subscribe((transactions: Transaction[]) => {
 | 
			
		||||
        this.lastTransactionTxId = transactions[transactions.length - 1].txid;
 | 
			
		||||
        this.loadedConfirmedTxCount += transactions.length;
 | 
			
		||||
        this.transactions = this.transactions.concat(transactions);
 | 
			
		||||
        this.isLoadingTransactions = false;
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  updateChainStats() {
 | 
			
		||||
    // this.receieved = this.asset.chain_stats.funded_txo_sum + this.asset.mempool_stats.funded_txo_sum;
 | 
			
		||||
    // this.sent = this.asset.chain_stats.spent_txo_sum + this.asset.mempool_stats.spent_txo_sum;
 | 
			
		||||
    this.txCount = this.asset.chain_stats.tx_count + this.asset.mempool_stats.tx_count;
 | 
			
		||||
    // this.totalConfirmedTxCount = this.asset.chain_stats.tx_count;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnDestroy() {
 | 
			
		||||
    this.mainSubscription.unsubscribe();
 | 
			
		||||
    this.websocketService.stopTrackingAsset();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -66,7 +66,7 @@
 | 
			
		||||
                  <span class="d-none d-lg-block">{{ vout.scriptpubkey_address | shortenString : 42 }}</span>
 | 
			
		||||
                </a>
 | 
			
		||||
                <ng-template #scriptpubkey_type>
 | 
			
		||||
                  OP_RETURN
 | 
			
		||||
                  {{ vout.scriptpubkey_type | scriptpubkeyType }}
 | 
			
		||||
                </ng-template>
 | 
			
		||||
              </td>
 | 
			
		||||
              <td class="text-right nowrap">
 | 
			
		||||
 | 
			
		||||
@ -98,3 +98,74 @@ export interface Outspend {
 | 
			
		||||
  vin: number;
 | 
			
		||||
  status: Status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface Asset {
 | 
			
		||||
  asset_id: string;
 | 
			
		||||
  issuance_txin: IssuanceTxin;
 | 
			
		||||
  issuance_prevout: IssuancePrevout;
 | 
			
		||||
  reissuance_token: string;
 | 
			
		||||
  contract_hash: string;
 | 
			
		||||
  status: Status;
 | 
			
		||||
  chain_stats: AssetChainStats;
 | 
			
		||||
  mempool_stats: AssetMempoolStats;
 | 
			
		||||
  contract: Contract;
 | 
			
		||||
  entity: Entity;
 | 
			
		||||
  precision: number;
 | 
			
		||||
  name: string;
 | 
			
		||||
  ticker: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IssuanceTxin {
 | 
			
		||||
  txid: string;
 | 
			
		||||
  vin: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IssuancePrevout {
 | 
			
		||||
  txid: string;
 | 
			
		||||
  vout: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface AssetChainStats {
 | 
			
		||||
  tx_count: number;
 | 
			
		||||
  issuance_count: number;
 | 
			
		||||
  issued_amount: number;
 | 
			
		||||
  burned_amount: number;
 | 
			
		||||
  has_blinded_issuances: boolean;
 | 
			
		||||
  reissuance_tokens: number;
 | 
			
		||||
  burned_reissuance_tokens: number;
 | 
			
		||||
 | 
			
		||||
  peg_in_count: number;
 | 
			
		||||
  peg_in_amount: number;
 | 
			
		||||
  peg_out_count: number;
 | 
			
		||||
  peg_out_amount: number;
 | 
			
		||||
  burn_count: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface AssetMempoolStats {
 | 
			
		||||
  tx_count: number;
 | 
			
		||||
  issuance_count: number;
 | 
			
		||||
  issued_amount: number;
 | 
			
		||||
  burned_amount: number;
 | 
			
		||||
  has_blinded_issuances: boolean;
 | 
			
		||||
  reissuance_tokens: any;
 | 
			
		||||
  burned_reissuance_tokens: number;
 | 
			
		||||
 | 
			
		||||
  peg_in_count: number;
 | 
			
		||||
  peg_in_amount: number;
 | 
			
		||||
  peg_out_count: number;
 | 
			
		||||
  peg_out_amount: number;
 | 
			
		||||
  burn_count: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface Contract {
 | 
			
		||||
  entity: Entity;
 | 
			
		||||
  issuer_pubkey: string;
 | 
			
		||||
  name: string;
 | 
			
		||||
  precision: number;
 | 
			
		||||
  ticker: string;
 | 
			
		||||
  version: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface Entity {
 | 
			
		||||
  domain: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,7 @@ export interface WebsocketResponse {
 | 
			
		||||
  tx?: Transaction;
 | 
			
		||||
  'track-tx'?: string;
 | 
			
		||||
  'track-address'?: string;
 | 
			
		||||
  'track-asset'?: string;
 | 
			
		||||
  'watch-mempool'?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,18 @@
 | 
			
		||||
import { Pipe, PipeTransform } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
@Pipe({
 | 
			
		||||
  name: 'scriptpubkeyType'
 | 
			
		||||
})
 | 
			
		||||
export class ScriptpubkeyTypePipe implements PipeTransform {
 | 
			
		||||
 | 
			
		||||
  transform(value: string): string {
 | 
			
		||||
    switch (value) {
 | 
			
		||||
      case 'fee':
 | 
			
		||||
        return 'Transaction fee';
 | 
			
		||||
      case 'op_return':
 | 
			
		||||
      default:
 | 
			
		||||
          return 'Script';
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { HttpClient } from '@angular/common/http';
 | 
			
		||||
import { Observable } from 'rxjs';
 | 
			
		||||
import { Block, Transaction, Address, Outspend, Recent } from '../interfaces/electrs.interface';
 | 
			
		||||
import { Block, Transaction, Address, Outspend, Recent, Asset } from '../interfaces/electrs.interface';
 | 
			
		||||
 | 
			
		||||
const API_BASE_URL = document.location.protocol + '//' + document.location.hostname + ':' + document.location.port + '/electrs';
 | 
			
		||||
 | 
			
		||||
@ -54,4 +54,16 @@ export class ElectrsApiService {
 | 
			
		||||
    return this.httpClient.get<Transaction[]>(API_BASE_URL + '/address/' + address + '/txs/chain/' + txid);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getAsset$(assetId: string): Observable<Asset> {
 | 
			
		||||
    return this.httpClient.get<Asset>(API_BASE_URL + '/asset/' + assetId);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getAssetTransactions$(assetId: string): Observable<Transaction[]> {
 | 
			
		||||
    return this.httpClient.get<Transaction[]>(API_BASE_URL + '/asset/' + assetId + '/txs');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getAssetTransactionsFromHash$(assetId: string, txid: string): Observable<Transaction[]> {
 | 
			
		||||
    return this.httpClient.get<Transaction[]>(API_BASE_URL + '/asset/' + assetId + '/txs/chain/' + txid);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -157,6 +157,14 @@ export class WebsocketService {
 | 
			
		||||
    this.websocketSubject.next({ 'track-address': 'stop' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  startTrackAsset(asset: string) {
 | 
			
		||||
    this.websocketSubject.next({ 'track-asset': asset });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  stopTrackingAsset() {
 | 
			
		||||
    this.websocketSubject.next({ 'track-asset': 'stop' });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fetchStatistics(historicalDate: string) {
 | 
			
		||||
    this.websocketSubject.next({ historicalDate });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user