mirror of
https://github.com/mempool/mempool.git
synced 2025-03-15 12:20:28 +01:00
Merge branch 'master' into update_liquid_tests
This commit is contained in:
commit
af38ef8ee7
13 changed files with 424 additions and 295 deletions
|
@ -21,11 +21,6 @@ class BitcoinApi implements AbstractBitcoinApi {
|
||||||
return this.$addPrevouts(txInMempool);
|
return this.$addPrevouts(txInMempool);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special case to fetch the Coinbase transaction
|
|
||||||
if (txId === '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b') {
|
|
||||||
return this.$returnCoinbaseTransaction();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.bitcoindClient.getRawTransaction(txId, true)
|
return this.bitcoindClient.getRawTransaction(txId, true)
|
||||||
.then((transaction: IBitcoinApi.Transaction) => {
|
.then((transaction: IBitcoinApi.Transaction) => {
|
||||||
if (skipConversion) {
|
if (skipConversion) {
|
||||||
|
@ -35,6 +30,12 @@ class BitcoinApi implements AbstractBitcoinApi {
|
||||||
return transaction;
|
return transaction;
|
||||||
}
|
}
|
||||||
return this.$convertTransaction(transaction, addPrevout);
|
return this.$convertTransaction(transaction, addPrevout);
|
||||||
|
})
|
||||||
|
.catch((e: Error) => {
|
||||||
|
if (e.message.startsWith('The genesis block coinbase')) {
|
||||||
|
return this.$returnCoinbaseTransaction();
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,12 +239,14 @@ class BitcoinApi implements AbstractBitcoinApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected $returnCoinbaseTransaction(): Promise<IEsploraApi.Transaction> {
|
protected $returnCoinbaseTransaction(): Promise<IEsploraApi.Transaction> {
|
||||||
return this.bitcoindClient.getBlock('000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f', 2)
|
return this.bitcoindClient.getBlockHash(0).then((hash: string) =>
|
||||||
|
this.bitcoindClient.getBlock(hash, 2)
|
||||||
.then((block: IBitcoinApi.Block) => {
|
.then((block: IBitcoinApi.Block) => {
|
||||||
return this.$convertTransaction(Object.assign(block.tx[0], {
|
return this.$convertTransaction(Object.assign(block.tx[0], {
|
||||||
confirmations: blocks.getCurrentBlockHeight() + 1,
|
confirmations: blocks.getCurrentBlockHeight() + 1,
|
||||||
blocktime: 1231006505 }), false);
|
blocktime: block.time }), false);
|
||||||
});
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private $getMempoolEntry(txid: string): Promise<IBitcoinApi.MempoolEntry> {
|
private $getMempoolEntry(txid: string): Promise<IBitcoinApi.MempoolEntry> {
|
||||||
|
|
|
@ -61,10 +61,7 @@ PROXY_CONFIG = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
context: ['/api/liquidtestnet**', '/liquidtestnet/api/**'],
|
context: ['/api/liquidtestnet**', '/liquidtestnet/api/**'],
|
||||||
target: "https://liquid.network/testnet",
|
target: "https://liquid.network",
|
||||||
pathRewrite: {
|
|
||||||
"^/api/liquidtestnet/": "/liquidtestnet/api"
|
|
||||||
},
|
|
||||||
ws: true,
|
ws: true,
|
||||||
secure: false,
|
secure: false,
|
||||||
changeOrigin: true
|
changeOrigin: true
|
||||||
|
@ -73,7 +70,9 @@ PROXY_CONFIG = [
|
||||||
|
|
||||||
if (configContent && configContent.BASE_MODULE == "liquid") {
|
if (configContent && configContent.BASE_MODULE == "liquid") {
|
||||||
PROXY_CONFIG.push({
|
PROXY_CONFIG.push({
|
||||||
context: ['/resources/pools.json', '/resources/assets.json', '/resources/assets.minimal.json'],
|
context: ['/resources/pools.json',
|
||||||
|
'/resources/assets.json', '/resources/assets.minimal.json',
|
||||||
|
'/resources/assets-testnet.json', '/resources/assets-testnet.minimal.json'],
|
||||||
target: "https://liquid.network",
|
target: "https://liquid.network",
|
||||||
secure: false,
|
secure: false,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
|
|
|
@ -46,6 +46,7 @@ import { SharedModule } from './shared/shared.module';
|
||||||
import { NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { FeesBoxComponent } from './components/fees-box/fees-box.component';
|
import { FeesBoxComponent } from './components/fees-box/fees-box.component';
|
||||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||||
|
import { DifficultyComponent } from './components/difficulty/difficulty.component';
|
||||||
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
|
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
|
||||||
import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, faChartArea, faCogs, faCubes, faDatabase, faExchangeAlt, faInfoCircle,
|
import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, faChartArea, faCogs, faCubes, faDatabase, faExchangeAlt, faInfoCircle,
|
||||||
faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown, faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl } from '@fortawesome/free-solid-svg-icons';
|
faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown, faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
@ -97,6 +98,7 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
StatusViewComponent,
|
StatusViewComponent,
|
||||||
FeesBoxComponent,
|
FeesBoxComponent,
|
||||||
DashboardComponent,
|
DashboardComponent,
|
||||||
|
DifficultyComponent,
|
||||||
ApiDocsComponent,
|
ApiDocsComponent,
|
||||||
CodeTemplateComponent,
|
CodeTemplateComponent,
|
||||||
TermsOfServiceComponent,
|
TermsOfServiceComponent,
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
<thead>
|
<thead>
|
||||||
<th i18n="Asset name header">Name</th>
|
<th i18n="Asset name header">Name</th>
|
||||||
<th i18n="Asset ticker header">Ticker</th>
|
<th i18n="Asset ticker header">Ticker</th>
|
||||||
<th i18n="Asset Issuer Domain header">Issuer domain</th>
|
<th class="d-none d-md-block" i18n="Asset Issuer Domain header">Issuer domain</th>
|
||||||
<th i18n="Asset ID header">Asset ID</th>
|
<th i18n="Asset ID header">Asset ID</th>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
|
@ -35,13 +35,6 @@
|
||||||
<td i18n="asset.issuance-tx|Liquid Asset issuance TX">Issuance TX</td>
|
<td i18n="asset.issuance-tx|Liquid Asset issuance TX">Issuance TX</td>
|
||||||
<td><a [routerLink]="['/tx/' | relativeUrl, 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>
|
<td><a [routerLink]="['/tx/' | relativeUrl, 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>
|
</tr>
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div class="w-100 d-block d-md-none"></div>
|
|
||||||
<div class="col">
|
|
||||||
<table class="table table-borderless table-striped">
|
|
||||||
<tbody>
|
|
||||||
<tr *ngIf="isNativeAsset && asset.chain_stats.peg_in_amount">
|
<tr *ngIf="isNativeAsset && asset.chain_stats.peg_in_amount">
|
||||||
<td i18n="asset.pegged-in|Liquid Asset pegged-in amount">Pegged in</td>
|
<td i18n="asset.pegged-in|Liquid Asset pegged-in amount">Pegged in</td>
|
||||||
<td>{{ formatAmount(asset.chain_stats.peg_in_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
|
<td>{{ formatAmount(asset.chain_stats.peg_in_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
|
||||||
|
@ -69,6 +62,13 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="w-100 d-block d-md-none"></div>
|
||||||
|
<div class="col icon-holder">
|
||||||
|
<img *ngIf="!imageError; else defaultIcon" class="assetIcon" [src]="'https://liquid.network/api/v1/asset/' + asset.asset_id + '/icon'" (error)="imageError = true">
|
||||||
|
<ng-template #defaultIcon>
|
||||||
|
<fa-icon class="defaultIcon" [icon]="['fas', 'database']" [fixedWidth]="true" size="8x"></fa-icon>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -109,7 +109,8 @@
|
||||||
|
|
||||||
<ng-template [ngIf]="isLoadingAsset && !error">
|
<ng-template [ngIf]="isLoadingAsset && !error">
|
||||||
|
|
||||||
<ng-template #loadingTmpl>
|
<div class="box">
|
||||||
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<table class="table table-borderless table-striped">
|
<table class="table table-borderless table-striped">
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -122,15 +123,25 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="2"><span class="skeleton-loader"></span></td>
|
<td colspan="2"><span class="skeleton-loader"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<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>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2"><span class="skeleton-loader"></span></td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
<div class="w-100 d-block d-md-none"></div>
|
||||||
|
<div class="col icon-holder">
|
||||||
<div class="box">
|
<fa-icon class="defaultIcon skeleton" [icon]="['fas', 'database']" [fixedWidth]="true" size="8x"></fa-icon>
|
||||||
<div class="row">
|
</div>
|
||||||
<ng-container *ngTemplateOutlet="loadingTmpl"></ng-container>
|
|
||||||
<ng-container *ngTemplateOutlet="loadingTmpl"></ng-container>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -50,3 +50,26 @@ h1 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.assetIcon {
|
||||||
|
height: 150px;
|
||||||
|
margin: 25px;
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
height: 250px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-holder {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.defaultIcon {
|
||||||
|
margin: 25px;
|
||||||
|
height: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.defaultIcon.skeleton {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ export class AssetComponent implements OnInit, OnDestroy {
|
||||||
isNativeAsset = false;
|
isNativeAsset = false;
|
||||||
error: any;
|
error: any;
|
||||||
mainSubscription: Subscription;
|
mainSubscription: Subscription;
|
||||||
|
imageError = false;
|
||||||
|
|
||||||
totalConfirmedTxCount = 0;
|
totalConfirmedTxCount = 0;
|
||||||
loadedConfirmedTxCount = 0;
|
loadedConfirmedTxCount = 0;
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
<div class="main-title" i18n="dashboard.difficulty-adjustment">Difficulty Adjustment</div>
|
||||||
|
<div class="card-wrapper">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body more-padding">
|
||||||
|
<div class="difficulty-adjustment-container" *ngIf="(isLoadingWebSocket$ | async) === false && (difficultyEpoch$ | async) as epochData; else loadingDifficulty">
|
||||||
|
<div class="item">
|
||||||
|
<h5 class="card-title" i18n="difficulty-box.remaining">Remaining</h5>
|
||||||
|
<div class="card-text">
|
||||||
|
<ng-container *ngTemplateOutlet="epochData.remainingBlocks === 1 ? blocksSingular : blocksPlural; context: {$implicit: epochData.remainingBlocks }"></ng-container>
|
||||||
|
<ng-template #blocksPlural let-i i18n="shared.blocks">{{ i }} <span class="shared-block">blocks</span></ng-template>
|
||||||
|
<ng-template #blocksSingular let-i i18n="shared.block">{{ i }} <span class="shared-block">block</span></ng-template>
|
||||||
|
</div>
|
||||||
|
<div class="symbol"><app-time-until [time]="epochData.remainingTime" [fastRender]="true"></app-time-until></div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<h5 class="card-title" i18n="difficulty-box.estimate">Estimate</h5>
|
||||||
|
<div *ngIf="epochData.remainingBlocks < 1870; else recentlyAdjusted" class="card-text" [ngStyle]="{'color': epochData.colorAdjustments}">
|
||||||
|
<span *ngIf="epochData.change > 0; else arrowDownDifficulty" >
|
||||||
|
<fa-icon class="retarget-sign" [icon]="['fas', 'caret-up']" [fixedWidth]="true"></fa-icon>
|
||||||
|
</span>
|
||||||
|
<ng-template #arrowDownDifficulty >
|
||||||
|
<fa-icon class="retarget-sign" [icon]="['fas', 'caret-down']" [fixedWidth]="true"></fa-icon>
|
||||||
|
</ng-template>
|
||||||
|
{{ epochData.change | absolute | number: '1.2-2' }}
|
||||||
|
<span class="symbol">%</span>
|
||||||
|
</div>
|
||||||
|
<ng-template #recentlyAdjusted>
|
||||||
|
<div class="card-text">—</div>
|
||||||
|
</ng-template>
|
||||||
|
<div class="symbol">
|
||||||
|
<span i18n="difficulty-box.previous">Previous</span>:
|
||||||
|
<span [ngStyle]="{'color': epochData.colorPreviousAdjustments}">
|
||||||
|
<span *ngIf="epochData.previousRetarget > 0; else arrowDownPreviousDifficulty" >
|
||||||
|
<fa-icon class="previous-retarget-sign" [icon]="['fas', 'caret-up']" [fixedWidth]="true"></fa-icon>
|
||||||
|
</span>
|
||||||
|
<ng-template #arrowDownPreviousDifficulty >
|
||||||
|
<fa-icon class="previous-retarget-sign" [icon]="['fas', 'caret-down']" [fixedWidth]="true"></fa-icon>
|
||||||
|
</ng-template>
|
||||||
|
{{ epochData.previousRetarget | absolute | number: '1.2-2' }} </span> %
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<h5 class="card-title" i18n="difficulty-box.current-period">Current Period</h5>
|
||||||
|
<div class="card-text">{{ epochData.progress | number: '1.2-2' }} <span class="symbol">%</span></div>
|
||||||
|
<div class="progress small-bar">
|
||||||
|
<div class="progress-bar" role="progressbar" style="width: 15%; background-color: #105fb0" [ngStyle]="{'width': epochData.base}"> </div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-template #loadingDifficulty>
|
||||||
|
<div class="difficulty-skeleton loading-container">
|
||||||
|
<div class="item">
|
||||||
|
<h5 class="card-title" i18n="difficulty-box.remaining">Remaining</h5>
|
||||||
|
<div class="card-text">
|
||||||
|
<div class="skeleton-loader"></div>
|
||||||
|
<div class="skeleton-loader"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<h5 class="card-title" i18n="difficulty-box.estimate">Estimate</h5>
|
||||||
|
<div class="card-text">
|
||||||
|
<div class="skeleton-loader"></div>
|
||||||
|
<div class="skeleton-loader"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<h5 class="card-title" i18n="difficulty-box.current-period">Current Period</h5>
|
||||||
|
<div class="card-text">
|
||||||
|
<div class="skeleton-loader"></div>
|
||||||
|
<div class="skeleton-loader"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
150
frontend/src/app/components/difficulty/difficulty.component.scss
Normal file
150
frontend/src/app/components/difficulty/difficulty.component.scss
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
.difficulty-adjustment-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-around;
|
||||||
|
height: 76px;
|
||||||
|
.shared-block {
|
||||||
|
color: #ffffff66;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.item {
|
||||||
|
padding: 0 5px;
|
||||||
|
width: 100%;
|
||||||
|
&:nth-child(1) {
|
||||||
|
display: none;
|
||||||
|
@media (min-width: 485px) {
|
||||||
|
display: table-cell;
|
||||||
|
}
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
display: table-cell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.card-text {
|
||||||
|
font-size: 22px;
|
||||||
|
margin-top: -9px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.difficulty-skeleton {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
@media (min-width: 376px) {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
.item {
|
||||||
|
max-width: 150px;
|
||||||
|
margin: 0;
|
||||||
|
width: -webkit-fill-available;
|
||||||
|
@media (min-width: 376px) {
|
||||||
|
margin: 0 auto 0px;
|
||||||
|
}
|
||||||
|
&:first-child{
|
||||||
|
display: none;
|
||||||
|
@media (min-width: 485px) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.card-text {
|
||||||
|
.skeleton-loader {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
&:first-child {
|
||||||
|
margin: 14px auto 0;
|
||||||
|
max-width: 80px;
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
margin: 10px auto 0;
|
||||||
|
max-width: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background-color: #1d1f31;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
color: #4a68b9;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
display: inline-flex;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #2d3348;
|
||||||
|
height: 1.1rem;
|
||||||
|
max-width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-loader {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-padding {
|
||||||
|
padding: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-bar {
|
||||||
|
height: 8px;
|
||||||
|
top: -4px;
|
||||||
|
max-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-container {
|
||||||
|
min-height: 76px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-title {
|
||||||
|
position: relative;
|
||||||
|
color: #ffffff91;
|
||||||
|
margin-top: -13px;
|
||||||
|
font-size: 10px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: center;
|
||||||
|
padding-bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-wrapper {
|
||||||
|
.card {
|
||||||
|
height: auto !important;
|
||||||
|
}
|
||||||
|
.card-body {
|
||||||
|
display: flex;
|
||||||
|
flex: inherit;
|
||||||
|
text-align: center;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
padding: 22px 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.retarget-sign {
|
||||||
|
margin-right: -3px;
|
||||||
|
font-size: 14px;
|
||||||
|
top: -2px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.previous-retarget-sign {
|
||||||
|
margin-right: -2px;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
111
frontend/src/app/components/difficulty/difficulty.component.ts
Normal file
111
frontend/src/app/components/difficulty/difficulty.component.ts
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||||
|
import { combineLatest, Observable, timer } from 'rxjs';
|
||||||
|
import { map, switchMap } from 'rxjs/operators';
|
||||||
|
import { StateService } from '../..//services/state.service';
|
||||||
|
|
||||||
|
interface EpochProgress {
|
||||||
|
base: string;
|
||||||
|
change: number;
|
||||||
|
progress: string;
|
||||||
|
remainingBlocks: number;
|
||||||
|
newDifficultyHeight: number;
|
||||||
|
colorAdjustments: string;
|
||||||
|
colorPreviousAdjustments: string;
|
||||||
|
timeAvg: string;
|
||||||
|
remainingTime: number;
|
||||||
|
previousRetarget: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-difficulty',
|
||||||
|
templateUrl: './difficulty.component.html',
|
||||||
|
styleUrls: ['./difficulty.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class DifficultyComponent implements OnInit {
|
||||||
|
isLoadingWebSocket$: Observable<boolean>;
|
||||||
|
difficultyEpoch$: Observable<EpochProgress>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public stateService: StateService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$;
|
||||||
|
this.difficultyEpoch$ = timer(0, 1000)
|
||||||
|
.pipe(
|
||||||
|
switchMap(() => combineLatest([
|
||||||
|
this.stateService.blocks$.pipe(map(([block]) => block)),
|
||||||
|
this.stateService.lastDifficultyAdjustment$,
|
||||||
|
this.stateService.previousRetarget$
|
||||||
|
])),
|
||||||
|
map(([block, DATime, previousRetarget]) => {
|
||||||
|
const now = new Date().getTime() / 1000;
|
||||||
|
const diff = now - DATime;
|
||||||
|
const blocksInEpoch = block.height % 2016;
|
||||||
|
const progress = (blocksInEpoch >= 0) ? (blocksInEpoch / 2016 * 100).toFixed(2) : `100`;
|
||||||
|
const remainingBlocks = 2016 - blocksInEpoch;
|
||||||
|
const newDifficultyHeight = block.height + remainingBlocks;
|
||||||
|
|
||||||
|
let change = 0;
|
||||||
|
if (remainingBlocks < 1870) {
|
||||||
|
if (blocksInEpoch > 0) {
|
||||||
|
change = (600 / (diff / blocksInEpoch ) - 1) * 100;
|
||||||
|
}
|
||||||
|
if (change > 300) {
|
||||||
|
change = 300;
|
||||||
|
}
|
||||||
|
if (change < -75) {
|
||||||
|
change = -75;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeAvgDiff = change * 0.1;
|
||||||
|
|
||||||
|
let timeAvgMins = 10;
|
||||||
|
if (timeAvgDiff > 0) {
|
||||||
|
timeAvgMins -= Math.abs(timeAvgDiff);
|
||||||
|
} else {
|
||||||
|
timeAvgMins += Math.abs(timeAvgDiff);
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeAvg = timeAvgMins.toFixed(0);
|
||||||
|
const remainingTime = (remainingBlocks * timeAvgMins * 60 * 1000) + (now * 1000);
|
||||||
|
|
||||||
|
let colorAdjustments = '#ffffff66';
|
||||||
|
if (change > 0) {
|
||||||
|
colorAdjustments = '#3bcc49';
|
||||||
|
}
|
||||||
|
if (change < 0) {
|
||||||
|
colorAdjustments = '#dc3545';
|
||||||
|
}
|
||||||
|
|
||||||
|
let colorPreviousAdjustments = '#dc3545';
|
||||||
|
if (previousRetarget) {
|
||||||
|
if (previousRetarget >= 0) {
|
||||||
|
colorPreviousAdjustments = '#3bcc49';
|
||||||
|
}
|
||||||
|
if (previousRetarget === 0) {
|
||||||
|
colorPreviousAdjustments = '#ffffff66';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
colorPreviousAdjustments = '#ffffff66';
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
base: `${progress}%`,
|
||||||
|
change,
|
||||||
|
progress,
|
||||||
|
remainingBlocks,
|
||||||
|
timeAvg,
|
||||||
|
colorAdjustments,
|
||||||
|
colorPreviousAdjustments,
|
||||||
|
blocksInEpoch,
|
||||||
|
newDifficultyHeight,
|
||||||
|
remainingTime,
|
||||||
|
previousRetarget,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col" *ngIf="(network$ | async) !== 'liquid' && (network$ | async) !== 'liquidtestnet'">
|
<div class="col" *ngIf="(network$ | async) !== 'liquid' && (network$ | async) !== 'liquidtestnet'">
|
||||||
<ng-container *ngTemplateOutlet="difficultyEpoch"></ng-container>
|
<app-difficulty></app-difficulty>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col" *ngIf="(network$ | async) !== 'liquid' && (network$ | async) !== 'liquidtestnet'">
|
<div class="col" *ngIf="(network$ | async) !== 'liquid' && (network$ | async) !== 'liquidtestnet'">
|
||||||
<ng-container *ngTemplateOutlet="difficultyEpoch"></ng-container>
|
<app-difficulty></app-difficulty>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card graph-card">
|
<div class="card graph-card">
|
||||||
|
@ -228,84 +228,3 @@
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template #difficultyEpoch>
|
|
||||||
<div class="main-title" i18n="dashboard.difficulty-adjustment">Difficulty Adjustment</div>
|
|
||||||
<div class="card-wrapper">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body more-padding">
|
|
||||||
<div class="difficulty-adjustment-container" *ngIf="(isLoadingWebSocket$ | async) === false && (difficultyEpoch$ | async) as epochData; else loadingDifficulty">
|
|
||||||
<div class="item">
|
|
||||||
<h5 class="card-title" i18n="difficulty-box.remaining">Remaining</h5>
|
|
||||||
<div class="card-text">
|
|
||||||
<ng-container *ngTemplateOutlet="epochData.remainingBlocks === 1 ? blocksSingular : blocksPlural; context: {$implicit: epochData.remainingBlocks }"></ng-container>
|
|
||||||
<ng-template #blocksPlural let-i i18n="shared.blocks">{{ i }} <span class="shared-block">blocks</span></ng-template>
|
|
||||||
<ng-template #blocksSingular let-i i18n="shared.block">{{ i }} <span class="shared-block">block</span></ng-template>
|
|
||||||
</div>
|
|
||||||
<div class="symbol"><app-time-until [time]="epochData.remainingTime" [fastRender]="true"></app-time-until></div>
|
|
||||||
</div>
|
|
||||||
<div class="item">
|
|
||||||
<h5 class="card-title" i18n="difficulty-box.estimate">Estimate</h5>
|
|
||||||
<div *ngIf="epochData.remainingBlocks < 1870; else recentlyAdjusted" class="card-text" [ngStyle]="{'color': epochData.colorAdjustments}">
|
|
||||||
<span *ngIf="epochData.change > 0; else arrowDownDifficulty" >
|
|
||||||
<fa-icon class="retarget-sign" [icon]="['fas', 'caret-up']" [fixedWidth]="true"></fa-icon>
|
|
||||||
</span>
|
|
||||||
<ng-template #arrowDownDifficulty >
|
|
||||||
<fa-icon class="retarget-sign" [icon]="['fas', 'caret-down']" [fixedWidth]="true"></fa-icon>
|
|
||||||
</ng-template>
|
|
||||||
{{ epochData.change | absolute | number: '1.2-2' }}
|
|
||||||
<span class="symbol">%</span>
|
|
||||||
</div>
|
|
||||||
<ng-template #recentlyAdjusted>
|
|
||||||
<div class="card-text">—</div>
|
|
||||||
</ng-template>
|
|
||||||
<div class="symbol">
|
|
||||||
<span i18n="difficulty-box.previous">Previous</span>:
|
|
||||||
<span [ngStyle]="{'color': epochData.colorPreviousAdjustments}">
|
|
||||||
<span *ngIf="epochData.previousRetarget > 0; else arrowDownPreviousDifficulty" >
|
|
||||||
<fa-icon class="previous-retarget-sign" [icon]="['fas', 'caret-up']" [fixedWidth]="true"></fa-icon>
|
|
||||||
</span>
|
|
||||||
<ng-template #arrowDownPreviousDifficulty >
|
|
||||||
<fa-icon class="previous-retarget-sign" [icon]="['fas', 'caret-down']" [fixedWidth]="true"></fa-icon>
|
|
||||||
</ng-template>
|
|
||||||
{{ epochData.previousRetarget | absolute | number: '1.2-2' }} </span> %
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="item">
|
|
||||||
<h5 class="card-title" i18n="difficulty-box.current-period">Current Period</h5>
|
|
||||||
<div class="card-text">{{ epochData.progress | number: '1.2-2' }} <span class="symbol">%</span></div>
|
|
||||||
<div class="progress small-bar">
|
|
||||||
<div class="progress-bar" role="progressbar" style="width: 15%; background-color: #105fb0" [ngStyle]="{'width': epochData.base}"> </div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
|
|
||||||
<ng-template #loadingDifficulty>
|
|
||||||
<div class="difficulty-skeleton loading-container">
|
|
||||||
<div class="item">
|
|
||||||
<h5 class="card-title" i18n="difficulty-box.remaining">Remaining</h5>
|
|
||||||
<div class="card-text">
|
|
||||||
<div class="skeleton-loader"></div>
|
|
||||||
<div class="skeleton-loader"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="item">
|
|
||||||
<h5 class="card-title" i18n="difficulty-box.estimate">Estimate</h5>
|
|
||||||
<div class="card-text">
|
|
||||||
<div class="skeleton-loader"></div>
|
|
||||||
<div class="skeleton-loader"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="item">
|
|
||||||
<h5 class="card-title" i18n="difficulty-box.current-period">Current Period</h5>
|
|
||||||
<div class="card-text">
|
|
||||||
<div class="skeleton-loader"></div>
|
|
||||||
<div class="skeleton-loader"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
|
|
|
@ -243,84 +243,6 @@
|
||||||
max-width: 120px;
|
max-width: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.difficulty-adjustment-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-around;
|
|
||||||
height: 76px;
|
|
||||||
.shared-block {
|
|
||||||
color: #ffffff66;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
.item {
|
|
||||||
padding: 0 5px;
|
|
||||||
width: 100%;
|
|
||||||
&:nth-child(1) {
|
|
||||||
display: none;
|
|
||||||
@media (min-width: 485px) {
|
|
||||||
display: table-cell;
|
|
||||||
}
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
@media (min-width: 992px) {
|
|
||||||
display: table-cell;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.card-text {
|
|
||||||
font-size: 22px;
|
|
||||||
margin-top: -9px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.difficulty-skeleton {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
@media (min-width: 376px) {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
.item {
|
|
||||||
max-width: 150px;
|
|
||||||
margin: 0;
|
|
||||||
width: -webkit-fill-available;
|
|
||||||
@media (min-width: 376px) {
|
|
||||||
margin: 0 auto 0px;
|
|
||||||
}
|
|
||||||
&:first-child{
|
|
||||||
display: none;
|
|
||||||
@media (min-width: 485px) {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
@media (min-width: 992px) {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.card-text {
|
|
||||||
.skeleton-loader {
|
|
||||||
width: 100%;
|
|
||||||
display: block;
|
|
||||||
&:first-child {
|
|
||||||
margin: 14px auto 0;
|
|
||||||
max-width: 80px;
|
|
||||||
}
|
|
||||||
&:last-child {
|
|
||||||
margin: 10px auto 0;
|
|
||||||
max-width: 120px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-container {
|
.loading-container {
|
||||||
min-height: 76px;
|
min-height: 76px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,19 +15,6 @@ interface MempoolBlocksData {
|
||||||
size: number;
|
size: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EpochProgress {
|
|
||||||
base: string;
|
|
||||||
change: number;
|
|
||||||
progress: string;
|
|
||||||
remainingBlocks: number;
|
|
||||||
newDifficultyHeight: number;
|
|
||||||
colorAdjustments: string;
|
|
||||||
colorPreviousAdjustments: string;
|
|
||||||
timeAvg: string;
|
|
||||||
remainingTime: number;
|
|
||||||
previousRetarget: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MempoolInfoData {
|
interface MempoolInfoData {
|
||||||
memPoolInfo: MempoolInfo;
|
memPoolInfo: MempoolInfo;
|
||||||
vBytesPerSecond: number;
|
vBytesPerSecond: number;
|
||||||
|
@ -51,7 +38,6 @@ export class DashboardComponent implements OnInit {
|
||||||
network$: Observable<string>;
|
network$: Observable<string>;
|
||||||
mempoolBlocksData$: Observable<MempoolBlocksData>;
|
mempoolBlocksData$: Observable<MempoolBlocksData>;
|
||||||
mempoolInfoData$: Observable<MempoolInfoData>;
|
mempoolInfoData$: Observable<MempoolInfoData>;
|
||||||
difficultyEpoch$: Observable<EpochProgress>;
|
|
||||||
mempoolLoadingStatus$: Observable<number>;
|
mempoolLoadingStatus$: Observable<number>;
|
||||||
vBytesPerSecondLimit = 1667;
|
vBytesPerSecondLimit = 1667;
|
||||||
blocks$: Observable<Block[]>;
|
blocks$: Observable<Block[]>;
|
||||||
|
@ -126,82 +112,6 @@ export class DashboardComponent implements OnInit {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this.difficultyEpoch$ = timer(0, 1000)
|
|
||||||
.pipe(
|
|
||||||
switchMap(() => combineLatest([
|
|
||||||
this.stateService.blocks$.pipe(map(([block]) => block)),
|
|
||||||
this.stateService.lastDifficultyAdjustment$,
|
|
||||||
this.stateService.previousRetarget$
|
|
||||||
])),
|
|
||||||
map(([block, DATime, previousRetarget]) => {
|
|
||||||
const now = new Date().getTime() / 1000;
|
|
||||||
const diff = now - DATime;
|
|
||||||
const blocksInEpoch = block.height % 2016;
|
|
||||||
const progress = (blocksInEpoch >= 0) ? (blocksInEpoch / 2016 * 100).toFixed(2) : `100`;
|
|
||||||
const remainingBlocks = 2016 - blocksInEpoch;
|
|
||||||
const newDifficultyHeight = block.height + remainingBlocks;
|
|
||||||
|
|
||||||
let change = 0;
|
|
||||||
if (remainingBlocks < 1870) {
|
|
||||||
if (blocksInEpoch > 0) {
|
|
||||||
change = (600 / (diff / blocksInEpoch ) - 1) * 100;
|
|
||||||
}
|
|
||||||
if (change > 300) {
|
|
||||||
change = 300;
|
|
||||||
}
|
|
||||||
if (change < -75) {
|
|
||||||
change = -75;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeAvgDiff = change * 0.1;
|
|
||||||
|
|
||||||
let timeAvgMins = 10;
|
|
||||||
if (timeAvgDiff > 0) {
|
|
||||||
timeAvgMins -= Math.abs(timeAvgDiff);
|
|
||||||
} else {
|
|
||||||
timeAvgMins += Math.abs(timeAvgDiff);
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeAvg = timeAvgMins.toFixed(0);
|
|
||||||
const remainingTime = (remainingBlocks * timeAvgMins * 60 * 1000) + (now * 1000);
|
|
||||||
|
|
||||||
let colorAdjustments = '#ffffff66';
|
|
||||||
if (change > 0) {
|
|
||||||
colorAdjustments = '#3bcc49';
|
|
||||||
}
|
|
||||||
if (change < 0) {
|
|
||||||
colorAdjustments = '#dc3545';
|
|
||||||
}
|
|
||||||
|
|
||||||
let colorPreviousAdjustments = '#dc3545';
|
|
||||||
if (previousRetarget) {
|
|
||||||
if (previousRetarget >= 0) {
|
|
||||||
colorPreviousAdjustments = '#3bcc49';
|
|
||||||
}
|
|
||||||
if (previousRetarget === 0) {
|
|
||||||
colorPreviousAdjustments = '#ffffff66';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
colorPreviousAdjustments = '#ffffff66';
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
base: `${progress}%`,
|
|
||||||
change,
|
|
||||||
progress,
|
|
||||||
remainingBlocks,
|
|
||||||
timeAvg,
|
|
||||||
colorAdjustments,
|
|
||||||
colorPreviousAdjustments,
|
|
||||||
blocksInEpoch,
|
|
||||||
newDifficultyHeight,
|
|
||||||
remainingTime,
|
|
||||||
previousRetarget,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
this.mempoolBlocksData$ = this.stateService.mempoolBlocks$
|
this.mempoolBlocksData$ = this.stateService.mempoolBlocks$
|
||||||
.pipe(
|
.pipe(
|
||||||
map((mempoolBlocks) => {
|
map((mempoolBlocks) => {
|
||||||
|
|
Loading…
Add table
Reference in a new issue