Merge branch 'master' into update_liquid_tests
This commit is contained in:
		
						commit
						af38ef8ee7
					
				@ -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) =>
 | 
				
			||||||
      .then((block: IBitcoinApi.Block) => {
 | 
					      this.bitcoindClient.getBlock(hash, 2)
 | 
				
			||||||
        return this.$convertTransaction(Object.assign(block.tx[0], {
 | 
					        .then((block: IBitcoinApi.Block) => {
 | 
				
			||||||
          confirmations: blocks.getCurrentBlockHeight() + 1,
 | 
					          return this.$convertTransaction(Object.assign(block.tx[0], {
 | 
				
			||||||
          blocktime: 1231006505 }), false);
 | 
					            confirmations: blocks.getCurrentBlockHeight() + 1,
 | 
				
			||||||
      });
 | 
					            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,28 +109,39 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  <ng-template [ngIf]="isLoadingAsset && !error">
 | 
					  <ng-template [ngIf]="isLoadingAsset && !error">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <ng-template #loadingTmpl>
 | 
					 | 
				
			||||||
      <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>
 | 
					 | 
				
			||||||
    </ng-template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <div class="box">
 | 
					    <div class="box">
 | 
				
			||||||
      <div class="row">
 | 
					      <div class="row">
 | 
				
			||||||
        <ng-container *ngTemplateOutlet="loadingTmpl"></ng-container>
 | 
					        <div class="col">
 | 
				
			||||||
        <ng-container *ngTemplateOutlet="loadingTmpl"></ng-container>
 | 
					          <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>
 | 
				
			||||||
 | 
					              <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>
 | 
				
			||||||
 | 
					          </table>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="w-100 d-block d-md-none"></div>
 | 
				
			||||||
 | 
					        <div class="col icon-holder">
 | 
				
			||||||
 | 
					          <fa-icon class="defaultIcon skeleton" [icon]="['fas', 'database']" [fixedWidth]="true" size="8x"></fa-icon>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
      </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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user