Implement i18n support in frontend using Angular + Transifex + NGINX
This PR adds basic i18n support into the mempool frontend, together with a smooth workflow for developers and translators to collaborate: * Using the existing @angular/localize module, developers add i18n metadata to any frontend strings their new features or changes modify * Using the new npm script `i18n-extract-from-source`, developers extract the i18n data from source code into `src/locale/messages.xlf` * After pushing the updated `src/locale/messages.xlf` to GitHub, the Transifex service will update its database from the new source data * Using the Transifex website UI, translators can work together to translate all the mempool frontend strings into their native languages * Using the new npm script `i18n-pull-from-transifex`, developers can pull in completed translations from Transifex, and commit them into git. This flow requires an API key from Transifex, which can be obtained at https://www.transifex.com/user/settings/api/ to be used with the python script installed by `pip install transifex-client` - after preparing these, run the npm script which will ask you for the API key the first time. When downloading is complete, you can test building the frontend, and if successful, commit the new strings files into git. This PR implements a new locale selector in the footer of the homepage dashboard, and includes WIP translations for the following languages: * Czech (cs) * German (de) * Japanese (ja) * Norwegian (nn) * Spanish (es) * Swedish (sv) * Ukrainian (uk) * Persian (fa) * Portugese (pt) * Turkish (tr) * Dutch (nl) * French (fr) * Chinese (zh) * Slovenian (sl) * Korean (ko) * Polish (pl) The user-agent's `Accept-Language` header is used to automatically detect their preferred language, which can be manually overriden by the pull-down selector, which saves their preference to a cookie, which is used by nginx to serve the correct HTML bundle to the user. Remaining tasks include adding i18n metadata for strings in the Bisq and Liquid frontend code, mouseover hover tooltip strings, hard-coded og metadata inside HTML templates, and many other places. This will be done in a separate PR. When upgrading to add i18n support, mempool instance operators must take care to install the new nginx.conf and nginx-mempool.conf files, and tweak for their specific site configuration. Fixes #81
This commit is contained in:
parent
f151eb81c8
commit
4658b47007
7
frontend/.tx/config
Normal file
7
frontend/.tx/config
Normal file
@ -0,0 +1,7 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
||||
[mempool.frontend-src-locale-messages-xlf--wiz-i18n]
|
||||
file_filter = frontend/src/locale/messages.<lang>.xlf
|
||||
source_lang = en-US
|
||||
type = XLIFF
|
@ -13,6 +13,66 @@
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"i18n": {
|
||||
"sourceLocale": {
|
||||
"code":"en-US",
|
||||
"baseHref":"/"
|
||||
},
|
||||
"locales": {
|
||||
"cs": {
|
||||
"translation": "src/locale/messages.cs.xlf",
|
||||
"baseHref": "/cs/"
|
||||
},
|
||||
"de": {
|
||||
"translation": "src/locale/messages.de.xlf",
|
||||
"baseHref": "/de/"
|
||||
},
|
||||
"es": {
|
||||
"translation": "src/locale/messages.es.xlf",
|
||||
"baseHref": "/es/"
|
||||
},
|
||||
"fa": {
|
||||
"translation": "src/locale/messages.fa.xlf",
|
||||
"baseHref": "/fa/"
|
||||
},
|
||||
"ja": {
|
||||
"translation": "src/locale/messages.ja.xlf",
|
||||
"baseHref": "/ja/"
|
||||
},
|
||||
"nl": {
|
||||
"translation": "src/locale/messages.nl.xlf",
|
||||
"baseHref": "/nl/"
|
||||
},
|
||||
"nn": {
|
||||
"translation": "src/locale/messages.nn.xlf",
|
||||
"baseHref": "/nn/"
|
||||
},
|
||||
"pt": {
|
||||
"translation": "src/locale/messages.pt.xlf",
|
||||
"baseHref": "/pt/"
|
||||
},
|
||||
"sv": {
|
||||
"translation": "src/locale/messages.sv.xlf",
|
||||
"baseHref": "/sv/"
|
||||
},
|
||||
"sl": {
|
||||
"translation": "src/locale/messages.sl.xlf",
|
||||
"baseHref": "/sl/"
|
||||
},
|
||||
"tr": {
|
||||
"translation": "src/locale/messages.tr.xlf",
|
||||
"baseHref": "/tr/"
|
||||
},
|
||||
"uk": {
|
||||
"translation": "src/locale/messages.uk.xlf",
|
||||
"baseHref": "/uk/"
|
||||
},
|
||||
"zh": {
|
||||
"translation": "src/locale/messages.zh.xlf",
|
||||
"baseHref": "/zh/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
@ -127,4 +187,4 @@
|
||||
}
|
||||
}},
|
||||
"defaultProject": "mempool"
|
||||
}
|
||||
}
|
||||
|
1
frontend/frontend
Symbolic link
1
frontend/frontend
Symbolic link
@ -0,0 +1 @@
|
||||
.
|
@ -22,9 +22,11 @@
|
||||
"scripts": {
|
||||
"ng": "./node_modules/@angular/cli/bin/ng",
|
||||
"tsc": "./node_modules/typescript/bin/tsc",
|
||||
"i18n-extract-from-source": "./node_modules/@angular/cli/bin/ng xi18n --ivy --out-file ./src/locale/messages.xlf",
|
||||
"i18n-pull-from-transifex": "tx pull -a --parallel --minimum-perc 1",
|
||||
"serve": "ng serve --proxy-config proxy.conf.json",
|
||||
"start": "npm run generate-config && npm run sync-assets-dev && ng serve --proxy-config proxy.conf.json",
|
||||
"build": "npm run generate-config && ng build --prod && npm run sync-assets",
|
||||
"build": "npm run generate-config && ng build --prod --localize && npm run sync-assets",
|
||||
"sync-assets": "node sync-assets.js",
|
||||
"sync-assets-dev": "node sync-assets.js dev",
|
||||
"generate-config": "node generate-config.js",
|
||||
|
@ -58,3 +58,58 @@ const browserWindow = window || {};
|
||||
// @ts-ignore
|
||||
const browserWindowEnv = browserWindow.__env || {};
|
||||
export const env: Env = Object.assign(defaultEnv, browserWindowEnv);
|
||||
|
||||
export interface Language {
|
||||
code: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const languages: Language[] = [
|
||||
// { code: 'ar', name: 'العربية' }, // Arabic
|
||||
// { code: 'bg', name: 'Български' }, // Bulgarian
|
||||
// { code: 'bs', name: 'Bosanski' }, // Bosnian
|
||||
// { code: 'ca', name: 'Català' }, // Catalan
|
||||
{ code: 'cs', name: 'Čeština' }, // Czech
|
||||
// { code: 'da', name: 'Dansk' }, // Danish
|
||||
{ code: 'de', name: 'Deutsch' }, // German
|
||||
// { code: 'et', name: 'Eesti' }, // Estonian
|
||||
// { code: 'el', name: 'Ελληνικά' }, // Greek
|
||||
{ code: 'en', name: 'English' }, // English
|
||||
{ code: 'es', name: 'Español' }, // Spanish
|
||||
// { code: 'eo', name: 'Esperanto' }, // Esperanto
|
||||
// { code: 'eu', name: 'Euskara' }, // Basque
|
||||
{ code: 'fa', name: 'فارسی' }, // Persian
|
||||
// { code: 'fr', name: 'Français' }, // French
|
||||
// { code: 'gl', name: 'Galego' }, // Galician
|
||||
// { code: 'ko', name: '한국어' }, // Korean
|
||||
// { code: 'hr', name: 'Hrvatski' }, // Croatian
|
||||
// { code: 'id', name: 'Bahasa Indonesia' },// Indonesian
|
||||
// { code: 'it', name: 'Italiano' }, // Italian
|
||||
// { code: 'he', name: 'עברית' }, // Hebrew
|
||||
// { code: 'ka', name: 'ქართული' }, // Georgian
|
||||
// { code: 'lv', name: 'Latviešu' }, // Latvian
|
||||
// { code: 'lt', name: 'Lietuvių' }, // Lithuanian
|
||||
// { code: 'hu', name: 'Magyar' }, // Hungarian
|
||||
// { code: 'mk', name: 'Македонски' }, // Macedonian
|
||||
// { code: 'ms', name: 'Bahasa Melayu' }, // Malay
|
||||
{ code: 'nl', name: 'Nederlands' }, // Dutch
|
||||
{ code: 'ja', name: '日本語' }, // Japanese
|
||||
// { code: 'nb', name: 'Norsk bokmål' }, // Norwegian Bokmål
|
||||
{ code: 'nn', name: 'Norsk' }, // Norwegian Nynorsk
|
||||
// { code: 'pl', name: 'Polski' }, // Polish
|
||||
{ code: 'pt', name: 'Português' }, // Portuguese
|
||||
// { code: 'pt-BR', name: 'Português (Brazil)' }, // Portuguese (Brazil)
|
||||
// { code: 'ro', name: 'Română' }, // Romanian
|
||||
// { code: 'ru', name: 'Русский' }, // Russian
|
||||
// { code: 'sk', name: 'Slovenčina' }, // Slovak
|
||||
{ code: 'sl', name: 'Slovenščina' }, // Slovenian
|
||||
// { code: 'sr', name: 'Српски / srpski' }, // Serbian
|
||||
// { code: 'sh', name: 'Srpskohrvatski / српскохрватски' },// Serbo-Croatian
|
||||
// { code: 'fi', name: 'Suomi' }, // Finnish
|
||||
{ code: 'sv', name: 'Svenska' }, // Swedish
|
||||
// { code: 'th', name: 'ไทย' }, // Thai
|
||||
{ code: 'tr', name: 'Türkçe' }, // Turkish
|
||||
{ code: 'uk', name: 'Українська' }, // Ukrainian
|
||||
// { code: 'vi', name: 'Tiếng Việt' }, // Vietnamese
|
||||
{ code: 'zh', name: '中文' }, // Chinese
|
||||
];
|
||||
|
@ -47,6 +47,7 @@ import { faAngleDoubleDown, faAngleDoubleUp, faAngleDown, faAngleUp, faBolt, faC
|
||||
faLink, faList, faSearch, faTachometerAlt, faThList, faTint, faTv } from '@fortawesome/free-solid-svg-icons';
|
||||
import { ApiDocsComponent } from './components/api-docs/api-docs.component';
|
||||
import { TermsOfServiceComponent } from './components/terms-of-service/terms-of-service.component';
|
||||
import { TranslationStringsComponent } from './components/translation-strings/translation-strings.component';
|
||||
import { StorageService } from './services/storage.service';
|
||||
|
||||
@NgModule({
|
||||
@ -82,6 +83,7 @@ import { StorageService } from './services/storage.service';
|
||||
DashboardComponent,
|
||||
ApiDocsComponent,
|
||||
TermsOfServiceComponent,
|
||||
TranslationStringsComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
@ -10,16 +10,16 @@
|
||||
|
||||
<br>
|
||||
|
||||
<h2>About the project</h2>
|
||||
<h2 i18n="about.about-the-project">About the project</h2>
|
||||
<div class="row row-cols-1">
|
||||
<div class="col col-md-6 offset-md-3">
|
||||
<p>The mempool open-source project aims to implement a high quality explorer and visualization website for the entire Bitcoin ecosystem, without distractions like altcoins, advertising, or third-party trackers.</p>
|
||||
<p i18n>The mempool open-source project aims to implement a high quality explorer and visualization website for the entire Bitcoin ecosystem, without distractions like altcoins, advertising, or third-party trackers.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<h2>Maintainers</h2>
|
||||
<h2 i18n="about.maintainers">Maintainers</h2>
|
||||
|
||||
<div class="container text-center">
|
||||
<div class="row row-cols-2">
|
||||
@ -29,7 +29,7 @@
|
||||
@softsimon_
|
||||
</a>
|
||||
<br>
|
||||
Development
|
||||
<span i18n="about.development">Development</span>
|
||||
</div>
|
||||
<div class="col col-md-2">
|
||||
<a href="https://twitter.com/wiz">
|
||||
@ -37,14 +37,14 @@
|
||||
@wiz
|
||||
</a>
|
||||
<br>
|
||||
Operations
|
||||
<span i18n="about.operations">Operations</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br><br>
|
||||
|
||||
<h2>Sponsors ❤️</h2>
|
||||
<h2 i18n="about.sponsors.withHeart">Sponsors ❤️</h2>
|
||||
|
||||
<div *ngIf="sponsors === null">
|
||||
<br>
|
||||
@ -60,9 +60,9 @@
|
||||
</ng-template>
|
||||
<br><br>
|
||||
|
||||
<button type="button" class="btn btn-primary" (click)="donationStatus = 2" [hidden]="donationStatus !== 1">Become a sponsor ❤️</button>
|
||||
<button type="button" class="btn btn-primary" (click)="donationStatus = 2" [hidden]="donationStatus !== 1" i18n="about.become-a-sponsor">Become a sponsor ❤️</button>
|
||||
<p *ngIf="donationStatus === 2 && !sponsorsEnabled">
|
||||
Navigate to <a href="https://mempool.space/about" target="_blank">https://mempool.space/about</a> to sponsor
|
||||
<span i18n="about.navigate-to">Navigate to</span> <a href="https://mempool.space/about" target="_blank">https://mempool.space/about</a> <span i18n="about.to-sponsor">to sponsor</span>
|
||||
</p>
|
||||
|
||||
<div style="max-width: 300px;" class="mx-auto" [hidden]="donationStatus !== 2 || !sponsorsEnabled">
|
||||
@ -79,17 +79,17 @@
|
||||
</div>
|
||||
<input formControlName="handle" class="form-control" type="text" placeholder="Twitter handle (Optional)">
|
||||
</div>
|
||||
<div class="required" *ngIf="donationForm.get('amount').hasError('required')">Amount required</div>
|
||||
<div class="required" *ngIf="donationForm.get('amount').hasError('min')">Minimum amount is 0.001 BTC</div>
|
||||
<div class="required" *ngIf="donationForm.get('amount').hasError('required')" i18n="about.sponsor.amount-required">Amount required</div>
|
||||
<div class="required" *ngIf="donationForm.get('amount').hasError('min')" i18n="about.sponsor.minimum-amount">Minimum amount is 0.001 BTC</div>
|
||||
<div class="input-group mt-4">
|
||||
<button class="btn btn-primary mx-auto" type="submit" [disabled]="donationForm.invalid">Request invoice</button>
|
||||
<button class="btn btn-primary mx-auto" type="submit" [disabled]="donationForm.invalid" i18n="about.sponsor.request-invoice">Request invoice</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<ng-template #lowAmount>
|
||||
<div class="input-group mb-4 text-small">
|
||||
If you donate 0.01 BTC or more, your profile photo will be added to the list of sponsors above :)
|
||||
<span i18n="about.sponsor.description">If you donate 0.01 BTC or more, your profile photo will be added to the list of sponsors above :)</span>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@ -167,13 +167,13 @@
|
||||
<p style="font-size: 12px;">{{ donationObj.amount }} BTC</p>
|
||||
</ng-template>
|
||||
|
||||
<p>Waiting for transaction... </p>
|
||||
<p i18n="about.sponsor.waiting-for-transaction">Waiting for transaction... </p>
|
||||
<div class="spinner-border text-light"></div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="donationStatus === 4" class="text-center">
|
||||
<h2>Donation confirmed!<br>Thank you!</h2>
|
||||
<p>If you specified a Twitter handle, the profile photo should now be visible on this page when you reload.</p>
|
||||
<h2><span i18n="about.sponsor.donation-confirmed">Donation confirmed!</span><br><span i18n="about.sponsor.thank-you">Thank you!</span></h2>
|
||||
<p i18n="about.sponsor.sponsor-completed">If you specified a Twitter handle, the profile photo should now be visible on this page when you reload.</p>
|
||||
</div>
|
||||
|
||||
<br><br><br><br>
|
||||
@ -203,7 +203,7 @@
|
||||
<br><br>
|
||||
|
||||
<div class="text-center">
|
||||
<a [routerLink]="['/terms-of-service']">Terms of Service</a>
|
||||
<a [routerLink]="['/terms-of-service']" i18n="shared.terms-of-service|Terms of Service">Terms of Service</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -1,2 +1,2 @@
|
||||
<span *ngIf="multisig" class="badge badge-pill badge-warning">multisig {{ multisigM }} of {{ multisigN }}</span>
|
||||
<span *ngIf="secondLayerClose" class="badge badge-pill badge-warning">Layer{{ network === 'liquid' ? '3' : '2' }} Peg-out</span>
|
||||
<span *ngIf="multisig" class="badge badge-pill badge-warning" i18n="address-labels.multisig">multisig {{ multisigM }} of {{ multisigN }}</span>
|
||||
<span *ngIf="secondLayerClose" class="badge badge-pill badge-warning" i18n="address-labels.upper-layer-peg-out">Layer{{ network === 'liquid' ? '3' : '2' }} Peg-out</span>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div class="container-xl">
|
||||
<h1 style="float: left;">Address</h1>
|
||||
<h1 style="float: left;" i18n="shared.address">Address</h1>
|
||||
<a [routerLink]="['/address/' | relativeUrl, addressString]" style="line-height: 56px; margin-left: 10px;">
|
||||
<span class="d-inline d-lg-none">{{ addressString | shortenString : 24 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ addressString }}</span>
|
||||
@ -17,15 +17,15 @@
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Total received</td>
|
||||
<td i18n="address.total-received">Total received</td>
|
||||
<td *ngIf="address.chain_stats.funded_txo_sum !== undefined; else confidentialTd"><app-amount [satoshis]="receieved" [noFiat]="true"></app-amount></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total sent</td>
|
||||
<td i18n="address.total-sent">Total sent</td>
|
||||
<td *ngIf="address.chain_stats.spent_txo_sum !== undefined; else confidentialTd"><app-amount [satoshis]="sent" [noFiat]="true"></app-amount></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Balance</td>
|
||||
<td i18n="address.balance">Balance</td>
|
||||
<td *ngIf="address.chain_stats.funded_txo_sum !== undefined; else confidentialTd"><app-amount [satoshis]="receieved - sent" [noFiat]="true"></app-amount> (<app-fiat [value]="receieved - sent"></app-fiat>)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -43,7 +43,7 @@
|
||||
|
||||
<br>
|
||||
|
||||
<h2><ng-template [ngIf]="transactions?.length">{{ (transactions?.length | number) || '?' }} of </ng-template>{{ txCount | number }} transactions</h2>
|
||||
<h2><ng-template [ngIf]="transactions?.length">{{ (transactions?.length | number) || '?' }} <span i18n="shared.of">of</span> </ng-template>{{ txCount | number }} <span i18n="shared.transactions">transactions</span></h2>
|
||||
|
||||
<app-transactions-list [transactions]="transactions" [showConfirmations]="true" (loadMore)="loadMore()"></app-transactions-list>
|
||||
|
||||
@ -98,7 +98,7 @@
|
||||
|
||||
<ng-template [ngIf]="error">
|
||||
<div class="text-center">
|
||||
Error loading address data.
|
||||
<span i18n="address.error.loading-address-data">Error loading address data.</span>
|
||||
<br>
|
||||
<i>{{ error.error }}</i>
|
||||
</div>
|
||||
@ -109,5 +109,5 @@
|
||||
<br>
|
||||
|
||||
<ng-template #confidentialTd>
|
||||
<td>Confidential</td>
|
||||
<td i18n="shared.confidential">Confidential</td>
|
||||
</ng-template>
|
||||
|
@ -3,7 +3,7 @@
|
||||
</ng-container>
|
||||
<ng-template #viewFiatVin>
|
||||
<ng-template [ngIf]="network === 'liquid' && (satoshis === undefined || satoshis === null)" [ngIfElse]="default">
|
||||
Confidential
|
||||
<span i18n="shared.confidential">Confidential</span>
|
||||
</ng-template>
|
||||
<ng-template #default>
|
||||
{{ satoshis / 100000000 | number : digitsInfo }}
|
||||
|
@ -6,17 +6,17 @@
|
||||
|
||||
<ul ngbNav #nav="ngbNav" [(activeId)]="active" class="nav-tabs">
|
||||
<li *ngIf="network.val !== 'bisq'" [ngbNavItem]="0">
|
||||
<a ngbNavLink>Websocket</a>
|
||||
<a ngbNavLink i18n="api-docs.tab.websocket|API Docs tab for Websocket">Websocket</a>
|
||||
<ng-template ngbNavContent>
|
||||
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th style="border-top: 0;">Endpoint</th>
|
||||
<th style="border-top: 0;">Description</th>
|
||||
<th style="border-top: 0;" i18n="api-docs.shared.endpoint|API Docs Endpoint">Endpoint</th>
|
||||
<th style="border-top: 0;" i18n="api-docs.shared.description|API Docs Description">Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap">wss://{{ hostname }}{{ network.val === '' ? '' : '/' + network.val }}/api/v1/ws</td>
|
||||
<td>Default push: <code>{{ '{' }} action: 'want', data: ['blocks', ...] {{ '}' }}</code> to express what you want pushed. Available: <code>blocks</code>, <code>mempool-block</code>, <code>live-2h-chart</code>, and <code>stats</code>.<br><br>Push transactions related to address: <code>{{ '{' }} 'track-address': '3PbJ...bF9B' {{ '}' }}</code> to receive all new transactions containing that address as input or output. Returns an array of transactions. <code>address-transactions</code> for new mempool transactions, and <code>block-transactions</code> for new block confirmed transactions.</td>
|
||||
<td i18n="api-docs.websocket.websocket">Default push: <code>{{ '{' }} action: 'want', data: ['blocks', ...] {{ '}' }}</code> to express what you want pushed. Available: <code>blocks</code>, <code>mempool-block</code>, <code>live-2h-chart</code>, and <code>stats</code>.<br><br>Push transactions related to address: <code>{{ '{' }} 'track-address': '3PbJ...bF9B' {{ '}' }}</code> to receive all new transactions containing that address as input or output. Returns an array of transactions. <code>address-transactions</code> for new mempool transactions, and <code>block-transactions</code> for new block confirmed transactions.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@ -24,21 +24,21 @@
|
||||
</li>
|
||||
|
||||
<li *ngIf="network.val !== 'bisq'" [ngbNavItem]="1">
|
||||
<a ngbNavLink>Fee Estimates</a>
|
||||
<a ngbNavLink i18n="api-docs.tab.fees|API Docs tab for Fees">Fees</a>
|
||||
<ng-template ngbNavContent>
|
||||
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th style="border-top: 0;">Endpoint</th>
|
||||
<th style="border-top: 0;">Description</th>
|
||||
<th style="border-top: 0;" i18n="api-docs.shared.endpoint|API Docs Endpoint">Endpoint</th>
|
||||
<th style="border-top: 0;" i18n="api-docs.shared.description|API Docs Description">Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/v1/fees/recommended" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/v1/fees/recommended</a></td>
|
||||
<td>Returns our currently suggested fees for new transactions.</td>
|
||||
<td i18n="api-docs.fees.recommended|API Docs for /api/v1/fees/recommended">Returns our currently suggested fees for new transactions.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/v1/fees/mempool-blocks" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/v1/fees/mempool-blocks</a></td>
|
||||
<td>Returns current mempool as projected blocks.</td>
|
||||
<td i18n="api-docs.fees.mempool-blocks|API Docs for /api/v1/fees/mempool-blocks">Returns current mempool as projected blocks.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@ -46,25 +46,25 @@
|
||||
</li>
|
||||
|
||||
<li *ngIf="network.val !== 'bisq'" [ngbNavItem]="2">
|
||||
<a ngbNavLink>Mempool</a>
|
||||
<a ngbNavLink i18n="api-docs.tab.mempool|API Docs tab for Mempool">Mempool</a>
|
||||
<ng-template ngbNavContent>
|
||||
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th style="border-top: 0;">Endpoint</th>
|
||||
<th style="border-top: 0;">Description</th>
|
||||
<th style="border-top: 0;" i18n="api-docs.shared.endpoint|API Docs Endpoint">Endpoint</th>
|
||||
<th style="border-top: 0;" i18n="api-docs.shared.description|API Docs Description">Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/mempool" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/mempool</a></td>
|
||||
<td>Returns current mempool backlog statistics.</td>
|
||||
<td i18n="api-docs.mempool.mempool|API Docs for /api/mempool">Returns current mempool backlog statistics.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/mempool/txids" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/mempool/txids</a></td>
|
||||
<td>Get the full list of txids in the mempool as an array. The order of the txids is arbitrary and does not match bitcoind.</td>
|
||||
<td i18n="api-docs.mempool.txids|API Docs for /api/mempool/txids">Get the full list of txids in the mempool as an array. The order of the txids is arbitrary and does not match bitcoind.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/mempool/recent" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/mempool/recent</a></td>
|
||||
<td>Get a list of the last 10 transactions to enter the mempool. Each transaction object contains simplified overview data, with the following fields: <code>txid</code>, <code>fee</code>, <code>vsize</code>, and <code>value</code>.</td>
|
||||
<td i18n="api-docs.mempool.recent|API Docs for /api/mempool/recent">Get a list of the last 10 transactions to enter the mempool. Each transaction object contains simplified overview data, with the following fields: <code>txid</code>, <code>fee</code>, <code>vsize</code>, and <code>value</code>.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@ -72,53 +72,53 @@
|
||||
</li>
|
||||
|
||||
<li *ngIf="network.val !== 'bisq'" [ngbNavItem]="3">
|
||||
<a ngbNavLink>Blocks</a>
|
||||
<a ngbNavLink i18n="api-docs.tab.blocks|API Docs tab for Blocks">Blocks</a>
|
||||
<ng-template ngbNavContent>
|
||||
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th style="border-top: 0;">Endpoint</th>
|
||||
<th style="border-top: 0;">Description</th>
|
||||
<th style="border-top: 0;" i18n="api-docs.shared.endpoint|API Docs Endpoint">Endpoint</th>
|
||||
<th style="border-top: 0;" i18n="api-docs.shared.description|API Docs Description">Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/block/000000000000000015dc777b3ff2611091336355d3f0ee9766a2cf3be8e4b1ce" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/block/:hash</a></td>
|
||||
<td>Returns details about a block. Available fields: <code>id</code>, <code>height</code>, <code>version</code>, <code>timestamp</code>, <code>bits</code>, <code>nonce</code>, <code>merkle_root</code>, <code>tx_count</code>, <code>size</code>, <code>weight</code>,<ng-container *ngIf="network.val === 'liquid'"> <code>proof</code>,</ng-container> and <code>previousblockhash</code>.</td>
|
||||
<td i18n>Returns details about a block. Available fields: <code>id</code>, <code>height</code>, <code>version</code>, <code>timestamp</code>, <code>bits</code>, <code>nonce</code>, <code>merkle_root</code>, <code>tx_count</code>, <code>size</code>, <code>weight</code>,<ng-container *ngIf="network.val === 'liquid'"> <code>proof</code>,</ng-container> and <code>previousblockhash</code>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/block/000000000000000015dc777b3ff2611091336355d3f0ee9766a2cf3be8e4b1ce/status" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/block/:hash/status</a></td>
|
||||
<td>Returns the confirmation status of a block. Available fields: <code>in_best_chain</code> (boolean, false for orphaned blocks), <code>next_best</code> (the hash of the next block, only available for blocks in the best chain).</td>
|
||||
<td i18n>Returns the confirmation status of a block. Available fields: <code>in_best_chain</code> (boolean, false for orphaned blocks), <code>next_best</code> (the hash of the next block, only available for blocks in the best chain).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/block/000000000000000015dc777b3ff2611091336355d3f0ee9766a2cf3be8e4b1ce/txs" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/block/:hash/txs[/:start_index]</a></td>
|
||||
<td>Returns a list of transactions in the block (up to 25 transactions beginning at <code>start_index</code>). Transactions returned here do not have the <code>status</code> field, since all the transactions share the same block and confirmation status.</td>
|
||||
<td i18n>Returns a list of transactions in the block (up to 25 transactions beginning at <code>start_index</code>). Transactions returned here do not have the <code>status</code> field, since all the transactions share the same block and confirmation status.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/block/000000000000000015dc777b3ff2611091336355d3f0ee9766a2cf3be8e4b1ce/txids" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/block/:hash/txids</a></td>
|
||||
<td>Returns a list of all txids in the block.</td>
|
||||
<td i18n>Returns a list of all txids in the block.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/block/000000000000000015dc777b3ff2611091336355d3f0ee9766a2cf3be8e4b1ce/txid/218" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/block/:hash/txid/:index</a></td>
|
||||
<td>Returns the transaction at index <code>:index</code> within the specified block.</td>
|
||||
<td i18n>Returns the transaction at index <code>:index</code> within the specified block.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/block/000000000000000015dc777b3ff2611091336355d3f0ee9766a2cf3be8e4b1ce/raw" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/block/:hash/txid/raw</a></td>
|
||||
<td>Returns the raw block representation in binary.</td>
|
||||
<td i18n>Returns the raw block representation in binary.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/block-height/0" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/block-height/:height</a></td>
|
||||
<td>Returns the hash of the block currently at <code>:height</code>.</td>
|
||||
<td i18n>Returns the hash of the block currently at <code>:height</code>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/blocks" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/blocks[/:start_height]</a></td>
|
||||
<td>Returns the 10 newest blocks starting at the tip or at <code>:start_height</code> if specified.</td>
|
||||
<td i18n>Returns the 10 newest blocks starting at the tip or at <code>:start_height</code> if specified.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/blocks/tip/height" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/blocks/tip/height</a></td>
|
||||
<td>Returns the height of the last block.</td>
|
||||
<td i18n>Returns the height of the last block.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/blocks/tip/hash" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/blocks/tip/hash</a></td>
|
||||
<td>Returns the hash of the last block.</td>
|
||||
<td i18n>Returns the hash of the last block.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@ -126,49 +126,49 @@
|
||||
</li>
|
||||
|
||||
<li *ngIf="network.val !== 'bisq'" [ngbNavItem]="4">
|
||||
<a ngbNavLink>Transactions</a>
|
||||
<a ngbNavLink i18n="api-docs.tab.transactions|API Docs tab for Transactions">Transactions</a>
|
||||
<ng-template ngbNavContent>
|
||||
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th style="border-top: 0;">Endpoint</th>
|
||||
<th style="border-top: 0;">Description</th>
|
||||
<th style="border-top: 0;" i18n="api-docs.shared.endpoint|API Docs Endpoint">Endpoint</th>
|
||||
<th style="border-top: 0;" i18n="api-docs.shared.description|API Docs Description">Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/tx/15e10745f15593a899cef391191bdd3d7c12412cc4696b7bcb669d0feadc8521" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/tx/:txid</a></td>
|
||||
<td>Returns details about a transaction. Available fields: <code>txid</code>, <code>version</code>, <code>locktime</code>, <code>size</code>, <code>weight</code>, <code>fee</code>, <code>vin</code>, <code>vout</code>, and <code>status</code>.</td>
|
||||
<td i18n>Returns details about a transaction. Available fields: <code>txid</code>, <code>version</code>, <code>locktime</code>, <code>size</code>, <code>weight</code>, <code>fee</code>, <code>vin</code>, <code>vout</code>, and <code>status</code>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/tx/15e10745f15593a899cef391191bdd3d7c12412cc4696b7bcb669d0feadc8521/status" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/tx/:txid/status</a></td>
|
||||
<td>Returns the confirmation status of a transaction. Available fields: <code>confirmed</code> (boolean), <code>block_height</code> (optional), and <code>block_hash</code> (optional).</td>
|
||||
<td i18n>Returns the confirmation status of a transaction. Available fields: <code>confirmed</code> (boolean), <code>block_height</code> (optional), and <code>block_hash</code> (optional).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/tx/15e10745f15593a899cef391191bdd3d7c12412cc4696b7bcb669d0feadc8521/hex" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/tx/:txid/hex</a></td>
|
||||
<td>Returns a transaction serialized as hex.</td>
|
||||
<td i18n>Returns a transaction serialized as hex.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/tx/15e10745f15593a899cef391191bdd3d7c12412cc4696b7bcb669d0feadc8521/raw" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/tx/:txid/raw</a></td>
|
||||
<td>Returns a transaction as binary data.</td>
|
||||
<td i18n>Returns a transaction as binary data.</td>
|
||||
</tr>
|
||||
<tr *ngIf="network.val !== 'liquid'">
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/tx/15e10745f15593a899cef391191bdd3d7c12412cc4696b7bcb669d0feadc8521/merkleblock-proof" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/tx/:txid/merkleblock-proof</a></td>
|
||||
<td>Returns a merkle inclusion proof for the transaction using <a href="https://bitcoin.org/en/glossary/merkle-block">bitcoind's merkleblock</a> format.</td>
|
||||
<td i18n>Returns a merkle inclusion proof for the transaction using <a href="https://bitcoin.org/en/glossary/merkle-block">bitcoind's merkleblock</a> format.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/tx/15e10745f15593a899cef391191bdd3d7c12412cc4696b7bcb669d0feadc8521/merkle-proof" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/tx/:txid/merkle-proof</a></td>
|
||||
<td>Returns a merkle inclusion proof for the transaction using <a href="https://electrumx.readthedocs.io/en/latest/protocol-methods.html#blockchain-transaction-get-merkle">Electrum's blockchain.transaction.get_merkle format.</a></td>
|
||||
<td i18n>Returns a merkle inclusion proof for the transaction using <a href="https://electrumx.readthedocs.io/en/latest/protocol-methods.html#blockchain-transaction-get-merkle">Electrum's blockchain.transaction.get_merkle format.</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/tx/15e10745f15593a899cef391191bdd3d7c12412cc4696b7bcb669d0feadc8521/outspend/3" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/tx/:txid/outspend/:vout</a></td>
|
||||
<td>Returns the spending status of a transaction output. Available fields: <code>spent</code> (boolean), <code>txid</code> (optional), <code>vin</code> (optional), and <code>status</code> (optional, the status of the spending tx).</td>
|
||||
<td i18n>Returns the spending status of a transaction output. Available fields: <code>spent</code> (boolean), <code>txid</code> (optional), <code>vin</code> (optional), and <code>status</code> (optional, the status of the spending tx).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/tx/15e10745f15593a899cef391191bdd3d7c12412cc4696b7bcb669d0feadc8521/outspends" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/tx/:txid/outspends</a></td>
|
||||
<td>Returns the spending status of all transaction outputs.</td>
|
||||
<td i18n>Returns the spending status of all transaction outputs.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap">POST {{ network.val === '' ? '' : '/' + network.val }}/api/tx</td>
|
||||
<td>Broadcast a raw transaction to the network. The transaction should be provided as hex in the request body. The <code>txid</code> will be returned on success.</td>
|
||||
<td i18n>Broadcast a raw transaction to the network. The transaction should be provided as hex in the request body. The <code>txid</code> will be returned on success.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@ -176,33 +176,33 @@
|
||||
</li>
|
||||
|
||||
<li *ngIf="network.val !== 'bisq'" [ngbNavItem]="5">
|
||||
<a ngbNavLink>Addresses</a>
|
||||
<a ngbNavLink i18n="api-docs.tab.addresses|API Docs tab for Addresses">Addresses</a>
|
||||
<ng-template ngbNavContent>
|
||||
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th style="border-top: 0;">Endpoint</th>
|
||||
<th style="border-top: 0;">Description</th>
|
||||
<th style="border-top: 0;" i18n="api-docs.shared.endpoint|API Docs Endpoint">Endpoint</th>
|
||||
<th style="border-top: 0;" i18n="api-docs.shared.description|API Docs Description">Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/address/1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/address/:address</a></td>
|
||||
<td>Returns details about an address. Available fields: <code>address</code>, <code>chain_stats</code>, and <code>mempool_stats</code>. {{ '{' }}chain,mempool{{ '}' }}_stats each contain an object with <code>tx_count</code>, <code>funded_txo_count</code>, <code>funded_txo_sum</code>, <code>spent_txo_count</code>, and <code>spent_txo_sum</code>.</td>
|
||||
<td i18n>Returns details about an address. Available fields: <code>address</code>, <code>chain_stats</code>, and <code>mempool_stats</code>. {{ '{' }}chain,mempool{{ '}' }}_stats each contain an object with <code>tx_count</code>, <code>funded_txo_count</code>, <code>funded_txo_sum</code>, <code>spent_txo_count</code>, and <code>spent_txo_sum</code>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/address/1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC/txs" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/address/:address/txs</a></td>
|
||||
<td>Get transaction history for the specified address/scripthash, sorted with newest first. Returns up to 50 mempool transactions plus the first 25 confirmed transactions. You can request more confirmed transactions using <code>:last_seen_txid</code> (see below).
|
||||
<td i18n>Get transaction history for the specified address/scripthash, sorted with newest first. Returns up to 50 mempool transactions plus the first 25 confirmed transactions. You can request more confirmed transactions using <code>:last_seen_txid</code> (see below).
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/address/1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC/txs/chain" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/address/:address/txs/chain</a></td>
|
||||
<td>Get confirmed transaction history for the specified address/scripthash, sorted with newest first. Returns 25 transactions per page. More can be requested by specifying the last txid seen by the previous query.</td>
|
||||
<td i18n>Get confirmed transaction history for the specified address/scripthash, sorted with newest first. Returns 25 transactions per page. More can be requested by specifying the last txid seen by the previous query.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/address/1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC/txs/mempool" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/address/:address/txs/mempool</a></td>
|
||||
<td>Get unconfirmed transaction history for the specified address/scripthash. Returns up to 50 transactions (no paging).</td>
|
||||
<td i18n>Get unconfirmed transaction history for the specified address/scripthash. Returns up to 50 transactions (no paging).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="{{ network.val === '' ? '' : '/' + network.val }}/api/address/1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC/utxo" target="_blank">GET {{ network.val === '' ? '' : '/' + network.val }}/api/address/:address/utxo</a></td>
|
||||
<td>Get the list of unspent transaction outputs associated with the address/scripthash. Available fields: <code>txid</code>, <code>vout</code>, <code>value</code>, and <code>status</code> (with the status of the funding tx).<ng-container *ngIf="network.val === 'liquid'">There is also a <code>valuecommitment</code> field that may appear in place of <code>value</code>, plus the following additional fields: <code>asset</code>/<code>assetcommitment</code>, <code>nonce</code>/<code>noncecommitment</code>, <code>surjection_proof</code>, and <code>range_proof</code>.</ng-container></td>
|
||||
<td i18n>Get the list of unspent transaction outputs associated with the address/scripthash. Available fields: <code>txid</code>, <code>vout</code>, <code>value</code>, and <code>status</code> (with the status of the funding tx).<ng-container *ngIf="network.val === 'liquid'">There is also a <code>valuecommitment</code> field that may appear in place of <code>value</code>, plus the following additional fields: <code>asset</code>/<code>assetcommitment</code>, <code>nonce</code>/<code>noncecommitment</code>, <code>surjection_proof</code>, and <code>range_proof</code>.</ng-container></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@ -210,66 +210,66 @@
|
||||
</li>
|
||||
|
||||
<li *ngIf="network.val === 'liquid'" [ngbNavItem]="6">
|
||||
<a ngbNavLink>Assets</a>
|
||||
<a ngbNavLink i18n="api-docs.tab.assets|API Docs tab for Assets">Assets</a>
|
||||
<ng-template ngbNavContent>
|
||||
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th style="border-top: 0;">Endpoint</th>
|
||||
<th style="border-top: 0;">Description</th>
|
||||
<th style="border-top: 0;" i18n="api-docs.shared.endpoint|API Docs Endpoint">Endpoint</th>
|
||||
<th style="border-top: 0;" i18n="api-docs.shared.description|API Docs Description">Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="/liquid/api/asset/6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d" target="_blank">GET /liquid/api/asset/:asset_id</a></td>
|
||||
<td>Returns information about a Liquid asset.</td>
|
||||
<td i18n>Returns information about a Liquid asset.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="/liquid/api/asset/4b5417ec5ab6112bedf539c3b4f5a806ed539542d8b717e1c4470aa3180edce5/txs" target="_blank">GET /liquid/api/asset/:asset_id/txs[/mempool|/chain]</a></td>
|
||||
<td>Returns transactions associated with the specified Liquid asset. For the network's native asset, returns a list of peg in, peg out, and burn transactions. For user-issued assets, returns a list of issuance, reissuance, and burn transactions. Does not include regular transactions transferring this asset.</td>
|
||||
<td i18n>Returns transactions associated with the specified Liquid asset. For the network's native asset, returns a list of peg in, peg out, and burn transactions. For user-issued assets, returns a list of issuance, reissuance, and burn transactions. Does not include regular transactions transferring this asset.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="/liquid/api/asset/4b5417ec5ab6112bedf539c3b4f5a806ed539542d8b717e1c4470aa3180edce5/supply" target="_blank">GET /liquid/api/asset/:asset_id/supply[/decimal]</a></td>
|
||||
<td>Get the current total supply of the specified asset. For the native asset (L-BTC), this is calculated as [chain,mempool]_stats.peg_in_amount - [chain,mempool]_stats.peg_out_amount - [chain,mempool]_stats.burned_amount. For issued assets, this is calculated as [chain,mempool]_stats.issued_amount - [chain,mempool]_stats.burned_amount. Not available for assets with blinded issuances. If /decimal is specified, returns the supply as a decimal according to the asset's divisibility. Otherwise, returned in base units.</td>
|
||||
<td i18n>Get the current total supply of the specified asset. For the native asset (L-BTC), this is calculated as [chain,mempool]_stats.peg_in_amount - [chain,mempool]_stats.peg_out_amount - [chain,mempool]_stats.burned_amount. For issued assets, this is calculated as [chain,mempool]_stats.issued_amount - [chain,mempool]_stats.burned_amount. Not available for assets with blinded issuances. If /decimal is specified, returns the supply as a decimal according to the asset's divisibility. Otherwise, returned in base units.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
<li *ngIf="network.val === 'bisq'" [ngbNavItem]="1">
|
||||
<a ngbNavLink>BSQ</a>
|
||||
<a ngbNavLink i18n="api-docs.tab.bsq|API Docs tab for BSQ">BSQ</a>
|
||||
<ng-template ngbNavContent>
|
||||
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th style="border-top: 0;">Endpoint</th>
|
||||
<th style="border-top: 0;">Description</th>
|
||||
<th style="border-top: 0;" i18n="api-docs.shared.endpoint|API Docs Endpoint">Endpoint</th>
|
||||
<th style="border-top: 0;" i18n="api-docs.shared.description|API Docs Description">Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="/bisq/api/stats" target="_blank">GET /bisq/api/stats</a></td>
|
||||
<td>Returns statistics about all Bisq transactions.</td>
|
||||
<td i18n>Returns statistics about all Bisq transactions.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="/bisq/api/tx/4b5417ec5ab6112bedf539c3b4f5a806ed539542d8b717e1c4470aa3180edce5" target="_blank">GET /bisq/api/tx/:txid</a></td>
|
||||
<td>Returns details about a Bisq transaction.</td>
|
||||
<td i18n>Returns details about a Bisq transaction.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="/bisq/api/txs/0/25" target="_blank">GET /bisq/api/txs/:index/:length</a></td>
|
||||
<td>Returns :length of latest Bisq transactions, starting from :index.</td>
|
||||
<td i18n>Returns :length of latest Bisq transactions, starting from :index.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="/bisq/api/block/000000000000000000079aa6bfa46eb8fc20474e8673d6e8a123b211236bf82d" target="_blank">GET /bisq/api/block/:hash</a></td>
|
||||
<td>Returns all Bisq transactions that exist in a Bitcoin block.</td>
|
||||
<td i18n>Returns all Bisq transactions that exist in a Bitcoin block.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="/bisq/api/blocks/0/25" target="_blank">GET /bisq/api/blocks/:index/:length</a></td>
|
||||
<td>Returns :length Bitcoin blocks that contain Bisq transactions, starting from :index.</td>
|
||||
<td i18n>Returns :length Bitcoin blocks that contain Bisq transactions, starting from :index.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="/bisq/api/blocks/tip/height" target="_blank">GET /bisq/api/blocks/tip/height</a></td>
|
||||
<td>Returns the most recently processed Bitcoin block height processed by Bisq.</td>
|
||||
<td i18n>Returns the most recently processed Bitcoin block height processed by Bisq.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap"><a href="/bisq/api/address/B1DgwRN92rdQ9xpEVCdXRfgeqGw9X4YtrZz" target="_blank">GET /bisq/api/address/:address</a></td>
|
||||
<td>Returns all Bisq transactions belonging to a Bitcoin address, with 'B' prefixed in front of the address.</td>
|
||||
<td i18n>Returns all Bisq transactions belonging to a Bitcoin address, with 'B' prefixed in front of the address.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</ng-template>
|
||||
@ -281,7 +281,7 @@
|
||||
<br>
|
||||
|
||||
<div class="text-center">
|
||||
<a [routerLink]="['/terms-of-service']">Terms of Service</a>
|
||||
<a [routerLink]="['/terms-of-service']" i18n="shared.terms-of-service|Terms of Service">Terms of Service</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -17,19 +17,19 @@
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td i18n="asset.name|Liquid Asset name">Name</td>
|
||||
<td>{{ assetContract[2] }} ({{ assetContract[1] }})</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Precision</td>
|
||||
<td i18n="asset.precision|Liquid Asset precision">Precision</td>
|
||||
<td>{{ assetContract[3] }}</td>
|
||||
</tr>
|
||||
<tr *ngIf="!isNativeAsset && assetContract[0]">
|
||||
<td>Issuer</td>
|
||||
<td i18n="asset.issuer|Liquid Asset issuer">Issuer</td>
|
||||
<td><a target="_blank" href="{{ 'http://' + assetContract[0] }}">{{ assetContract[0] }}</a></td>
|
||||
</tr>
|
||||
<tr *ngIf="!isNativeAsset">
|
||||
<td>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>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -40,27 +40,27 @@
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr *ngIf="isNativeAsset">
|
||||
<td>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>
|
||||
</tr>
|
||||
<tr *ngIf="isNativeAsset">
|
||||
<td>Pegged out</td>
|
||||
<td i18n="asset.pegged-out|Liquid Asset pegged-out amount">Pegged out</td>
|
||||
<td>{{ formatAmount(asset.chain_stats.peg_out_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
|
||||
</tr>
|
||||
<tr *ngIf="!isNativeAsset">
|
||||
<td>Issued amount</td>
|
||||
<td i18n="asset.issued-amount|Liquid Asset issued amount">Issued amount</td>
|
||||
<td *ngIf="!blindedIssuance; else confidentialTd">{{ formatAmount(asset.chain_stats.issued_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Burned amount</td>
|
||||
<td i18n="asset.burned-amount|Liquid Asset burned amount">Burned amount</td>
|
||||
<td *ngIf="!blindedIssuance; else confidentialTd">{{ formatAmount(asset.chain_stats.burned_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
|
||||
</tr>
|
||||
<tr *ngIf="!isNativeAsset">
|
||||
<td>Circulating amount</td>
|
||||
<td i18n="asset.circulating-amount|Liquid Asset circulating amount">Circulating amount</td>
|
||||
<td *ngIf="!blindedIssuance; else confidentialTd">{{ formatAmount(asset.chain_stats.issued_amount - asset.chain_stats.burned_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
|
||||
</tr>
|
||||
<tr *ngIf="isNativeAsset">
|
||||
<td>Circulating amount</td>
|
||||
<td i18n="asset.circulating-amount|Liquid Asset circulating amount">Circulating amount</td>
|
||||
<td>{{ formatAmount(asset.chain_stats.peg_in_amount - asset.chain_stats.burned_amount - asset.chain_stats.peg_out_amount, assetContract[3]) | number: '1.0-' + assetContract[3] }} {{ assetContract[1] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -129,7 +129,7 @@
|
||||
|
||||
<ng-template [ngIf]="error">
|
||||
<div class="text-center">
|
||||
Error loading asset data.
|
||||
<span i18n="asset.error.loading-asset-data">Error loading asset data.</span>
|
||||
<br>
|
||||
<i>{{ error.error }}</i>
|
||||
</div>
|
||||
@ -140,5 +140,5 @@
|
||||
<br>
|
||||
|
||||
<ng-template #confidentialTd>
|
||||
<td>Confidential</td>
|
||||
<td i18n="shared.confidential">Confidential</td>
|
||||
</ng-template>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div class="container-xl">
|
||||
|
||||
<div class="title-block">
|
||||
<h1 class="float-left"><ng-template [ngIf]="blockHeight === 0">Genesis </ng-template>Block <ng-template [ngIf]="blockHeight"><a [routerLink]="['/block/' | relativeUrl, blockHash]">{{ blockHeight }}</a></ng-template></h1>
|
||||
<h1 class="float-left"><ng-template [ngIf]="blockHeight === 0" i18n="block.genesis">Genesis </ng-template><ng-template [ngIf]="blockHeight" i18n="block.block">Block <a [routerLink]="['/block/' | relativeUrl, blockHash]">{{ blockHeight }}</a></ng-template></h1>
|
||||
<button [routerLink]="['/' | relativeUrl]" class="btn btn-sm float-right mr-2 mt-2">✕</button>
|
||||
</div>
|
||||
|
||||
@ -15,24 +15,24 @@
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="td-width">Hash</td>
|
||||
<td class="td-width" i18n="block.hash">Hash</td>
|
||||
<td><a [routerLink]="['/block/' | relativeUrl, block.id]" title="{{ block.id }}">{{ block.id | shortenString : 13 }}</a> <app-clipboard class="d-none d-sm-inline-block" [text]="block.id"></app-clipboard></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Timestamp</td>
|
||||
<td i18n="block.timestamp">Timestamp</td>
|
||||
<td>
|
||||
{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }}
|
||||
<div class="lg-inline">
|
||||
<i>(<app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since> ago)</i>
|
||||
<i>(<app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since>)</i>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Size</td>
|
||||
<td i18n="block.size">Size</td>
|
||||
<td>{{ block.size | bytes: 2 }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Weight</td>
|
||||
<td i18n="block.weight">Weight</td>
|
||||
<td>{{ block.weight | wuBytes: 2 }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -42,19 +42,19 @@
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr *ngIf="block.medianFee !== undefined">
|
||||
<td class="td-width">Median fee</td>
|
||||
<td>~{{ block.medianFee | number:'1.0-0' }} sat/vB (<app-fiat [value]="block.medianFee * 140" digitsInfo="1.2-2" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)</td>
|
||||
<td class="td-width" i18n="block.median-fee">Median fee</td>
|
||||
<td>~{{ block.medianFee | number:'1.0-0' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span> (<app-fiat [value]="block.medianFee * 140" digitsInfo="1.2-2" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)</td>
|
||||
</tr>
|
||||
<ng-template [ngIf]="fees !== undefined" [ngIfElse]="loadingFees">
|
||||
<tr>
|
||||
<td>Total fees</td>
|
||||
<td i18n="block.total-fees|Total fees in a block">Total fees</td>
|
||||
<td *ngIf="network !== 'liquid'; else liquidTotalFees"><app-amount [satoshis]="fees * 100000000" digitsInfo="1.2-2" [noFiat]="true"></app-amount> (<app-fiat [value]="fees * 100000000" digitsInfo="1.0-0"></app-fiat>)</td>
|
||||
<ng-template #liquidTotalFees>
|
||||
<td>{{ fees * 100000000 | number }} L-sat (<app-fiat [value]="fees * 100000000" digitsInfo="1.2-2"></app-fiat>)</td>
|
||||
</ng-template>
|
||||
</tr>
|
||||
<tr *ngIf="network !== 'liquid'">
|
||||
<td>Subsidy + fees:</td>
|
||||
<td i18n="block.subsidy-and-fees|Total subsidy and fees in a block">Subsidy + fees:</td>
|
||||
<td>
|
||||
<app-amount [satoshis]="(blockSubsidy + fees) * 100000000" digitsInfo="1.2-2" [noFiat]="true"></app-amount> (<app-fiat [value]="(blockSubsidy + fees) * 100000000" digitsInfo="1.0-0"></app-fiat>)
|
||||
</td>
|
||||
@ -62,16 +62,16 @@
|
||||
</ng-template>
|
||||
<ng-template #loadingFees>
|
||||
<tr>
|
||||
<td>Total fees</td>
|
||||
<td i18n="block.total-fees|Total fees in a block">Total fees</td>
|
||||
<td style="width: 75%;"><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
<tr *ngIf="network !== 'liquid'">
|
||||
<td>Subsidy + fees:</td>
|
||||
<td i18n="block.subsidy-and-fees|Total subsidy and fees in a block">Subsidy + fees:</td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<tr>
|
||||
<td>Miner</td>
|
||||
<td i18n="block.miner">Miner</td>
|
||||
<td><app-miner [coinbaseTransaction]="coinbaseTx"></app-miner></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -82,7 +82,7 @@
|
||||
|
||||
<br>
|
||||
|
||||
<h2 class="float-left">{{ block.tx_count | number }} transaction<ng-template [ngIf]="block.tx_count !== 1">s</ng-template></h2>
|
||||
<h2 class="float-left">{{ block.tx_count | number }} <ng-template [ngIf]="block.tx_count === 1" i18n="shared.transaction-count.singular">transaction</ng-template><ng-template [ngIf]="block.tx_count !== 1" i18n="shared.transaction-count.plural">transactions</ng-template></h2>
|
||||
|
||||
<ngb-pagination class="float-right" [collectionSize]="block.tx_count" [rotate]="true" [pageSize]="itemsPerPage" [(page)]="page" (pageChange)="pageChange(page)" [maxSize]="paginationMaxSize" [boundaryLinks]="true"></ngb-pagination>
|
||||
|
||||
@ -162,7 +162,7 @@
|
||||
|
||||
<ng-template [ngIf]="error">
|
||||
<div class="text-center">
|
||||
Error loading block data.
|
||||
<span i18n="block.error.loading-block-data">Error loading block data.</span>
|
||||
<br><br>
|
||||
<i>{{ error.error }}</i>
|
||||
</div>
|
||||
|
@ -7,14 +7,14 @@
|
||||
</div>
|
||||
<div class="block-body">
|
||||
<div class="fees">
|
||||
~{{ block.medianFee | number:'1.0-0' }} sat/vB
|
||||
~{{ block.medianFee | number:'1.0-0' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
|
||||
</div>
|
||||
<div class="fee-span">
|
||||
{{ block.feeRange[1] | number:'1.0-0' }} - {{ block.feeRange[block.feeRange.length - 1] | number:'1.0-0' }} sat/vB
|
||||
{{ block.feeRange[1] | number:'1.0-0' }} - {{ block.feeRange[block.feeRange.length - 1] | number:'1.0-0' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
|
||||
</div>
|
||||
<div class="block-size">{{ block.size | bytes: 2 }}</div>
|
||||
<div class="transaction-count">{{ block.tx_count | number }} transaction<ng-template [ngIf]="block.tx_count !== 1">s</ng-template></div>
|
||||
<div class="time-difference"><app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since> ago</div>
|
||||
<div class="transaction-count">{{ block.tx_count | number }} <ng-template [ngIf]="block.tx_count === 1" i18n="shared.transaction">transaction</ng-template><ng-template [ngIf]="block.tx_count !== 1" i18n="shared.transactions">transactions</ng-template></div>
|
||||
<div class="time-difference"><app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -8,7 +8,7 @@
|
||||
</div>
|
||||
<div *ngIf="(isLoading$ | async) === true" class="position-container loading">
|
||||
<div class="loading-block">
|
||||
<h3>Waiting for blocks...</h3>
|
||||
<h3 i18n="blockchain.waiting-for-blocks|Loading text">Waiting for blocks...</h3>
|
||||
<div class="spinner-border text-light mt-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2,4 +2,4 @@
|
||||
<button #btn class="btn btn-sm btn-link pt-0" style="line-height: 0.9;" [attr.data-clipboard-text]="text">
|
||||
<img src="./resources/clippy.svg" width="13">
|
||||
</button>
|
||||
</span>
|
||||
</span>
|
||||
|
@ -1,21 +1,21 @@
|
||||
<table style="width: 100%;">
|
||||
<tr *ngIf="(isLoadingWebSocket$ | async) === false && (feeEstimations$ | async) as feeEstimations; else loadingFees">
|
||||
<td class="d-none d-md-block">
|
||||
<h5 class="card-title">Low priority</h5>
|
||||
<h5 class="card-title" i18n="fees-box.low-priority">Low priority</h5>
|
||||
<p class="card-text">
|
||||
{{ feeEstimations.hourFee }} sat/vB (<app-fiat [value]="feeEstimations.hourFee * 140" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)
|
||||
{{ feeEstimations.hourFee }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span> (<app-fiat [value]="feeEstimations.hourFee * 140" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)
|
||||
</p>
|
||||
</td>
|
||||
<td>
|
||||
<h5 class="card-title">Medium priority</h5>
|
||||
<h5 class="card-title" i18n="fees-box.medium-priority">Medium priority</h5>
|
||||
<p class="card-text">
|
||||
{{ feeEstimations.halfHourFee }} sat/vB (<app-fiat [value]="feeEstimations.halfHourFee * 140" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)
|
||||
{{ feeEstimations.halfHourFee }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span> (<app-fiat [value]="feeEstimations.halfHourFee * 140" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)
|
||||
</p>
|
||||
</td>
|
||||
<td>
|
||||
<h5 class="card-title">High priority</h5>
|
||||
<h5 class="card-title" i18n="fees-box.high-priority">High priority</h5>
|
||||
<p class="card-text">
|
||||
{{ feeEstimations.fastestFee }} sat/vB (<app-fiat [value]="feeEstimations.fastestFee * 140" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)
|
||||
{{ feeEstimations.fastestFee }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span> (<app-fiat [value]="feeEstimations.fastestFee * 140" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
@ -24,15 +24,15 @@
|
||||
<ng-template #loadingFees>
|
||||
<tr>
|
||||
<td class="d-none d-md-block">
|
||||
<h5 class="card-title">Low priority</h5>
|
||||
<h5 class="card-title" i18n="fees-box.low-priority">Low priority</h5>
|
||||
<p class="card-text"><span class="skeleton-loader"></span></p>
|
||||
</td>
|
||||
<td>
|
||||
<h5 class="card-title">Medium priority</h5>
|
||||
<h5 class="card-title" i18n="fees-box.medium-priority">Medium priority</h5>
|
||||
<p class="card-text"><span class="skeleton-loader" style="width: 80%;"></span></p>
|
||||
</td>
|
||||
<td>
|
||||
<h5 class="card-title">High priority</h5>
|
||||
<h5 class="card-title" i18n="fees-box.high-priority">High priority</h5>
|
||||
<p class="card-text"><span class="skeleton-loader"></span></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -2,22 +2,22 @@
|
||||
<div class="container-xl">
|
||||
<div class="row text-center" *ngIf="mempoolInfoData$ | async as mempoolInfoData">
|
||||
<div class="col d-none d-sm-block">
|
||||
<span class="txPerSecond">Tx vBytes per second:</span>
|
||||
<span class="txPerSecond" i18n="footer.tx-vbytes-per-second">Tx vBytes per second:</span>
|
||||
<span *ngIf="mempoolInfoData.vBytesPerSecond === 0; else inSync">
|
||||
<span class="badge badge-pill badge-warning">Backend is synchronizing</span>
|
||||
<span class="badge badge-pill badge-warning" i18n="footer.backend-is-synchronizing">Backend is synchronizing</span>
|
||||
</span>
|
||||
<ng-template #inSync>
|
||||
<div class="progress sub-text">
|
||||
<div class="progress-bar {{ mempoolInfoData.progressClass }}" role="progressbar" [ngStyle]="{'width': mempoolInfoData.progressWidth}">{{ mempoolInfoData.vBytesPerSecond | ceil | number }} vBytes/s</div>
|
||||
<div class="progress-bar {{ mempoolInfoData.progressClass }}" role="progressbar" [ngStyle]="{'width': mempoolInfoData.progressWidth}">{{ mempoolInfoData.vBytesPerSecond | ceil | number }} <span i18n="shared.vbytes-per-second">vBytes/s</span></div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="unconfirmedTx">Unconfirmed<span class="extra-text"> transactions</span>:</span>
|
||||
<span class="unconfirmedTx"><span i18n="shared.unconfirmed">Unconfirmed</span> <span class="extra-text" i18n="shared.transactions">transactions</span>:</span>
|
||||
<div class="sub-text">{{ mempoolInfoData.memPoolInfo.size | number }}</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="mempoolSize">Mempool size:</span>
|
||||
<span class="mempoolSize" i18n="footer.mempool-size">Mempool size:</span>
|
||||
<div class="sub-text" *ngIf="(mempoolBlocksData$ | async) as mempoolBlocksData">{{ mempoolBlocksData.size | bytes }} ({{ mempoolBlocksData.blocks }} block<span [hidden]="mempoolBlocksData.blocks <= 1">s</span>)</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -6,17 +6,17 @@
|
||||
|
||||
<table class="table table-borderless" [alwaysCallback]="true" [fromRoot]="true" [infiniteScrollContainer]="'body'" infiniteScroll [infiniteScrollDistance]="1.5" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="loadMore()">
|
||||
<thead>
|
||||
<th style="width: 15%;">Height</th>
|
||||
<th class="d-none d-md-block" style="width: 20%;">Timestamp</th>
|
||||
<th style="width: 20%;">Mined</th>
|
||||
<th class="d-none d-lg-block" style="width: 15%;">Transactions</th>
|
||||
<th style="width: 20%;">Filled</th>
|
||||
<th style="width: 15%;" i18n="latest-blocks.height">Height</th>
|
||||
<th class="d-none d-md-block" style="width: 20%;" i18n="latest-blocks.timestamp">Timestamp</th>
|
||||
<th style="width: 20%;" i18n="latest-blocks.mined">Mined</th>
|
||||
<th class="d-none d-lg-block" style="width: 15%;" i18n="latest-blocks.transactions">Transactions</th>
|
||||
<th style="width: 20%;" i18n="latest-blocks.filled">Filled</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let block of blocks; let i= index; trackBy: trackByBlock">
|
||||
<td><a [routerLink]="['/block' | relativeUrl, block.id]" [state]="{ data: { block: block } }">{{ block.height }}</a></td>
|
||||
<td class="d-none d-md-block">{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }}</td>
|
||||
<td><app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since> ago</td>
|
||||
<td><app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since></td>
|
||||
<td class="d-none d-lg-block">{{ block.tx_count | number }}</td>
|
||||
<td>
|
||||
<div class="progress position-relative">
|
||||
|
@ -4,8 +4,8 @@
|
||||
<a class="navbar-brand" [routerLink]="['/' | relativeUrl]" style="position: relative;">
|
||||
<ng-container *ngIf="{ val: connectionState$ | async } as connectionState">
|
||||
<img src="./resources/mempool-logo.png" height="35" width="140" class="logo" [ngStyle]="{'opacity': connectionState.val === 2 ? 1 : 0.5 }">
|
||||
<div class="badge badge-warning connection-badge" *ngIf="connectionState.val === 0">Offline</div>
|
||||
<div class="badge badge-warning connection-badge" style="left: 0px;" *ngIf="connectionState.val === 1">Reconnecting...</div>
|
||||
<div class="badge badge-warning connection-badge" *ngIf="connectionState.val === 0" i18n="master-page.offline">Offline</div>
|
||||
<div class="badge badge-warning connection-badge" style="left: 0px;" *ngIf="connectionState.val === 1" i18n="master-page.reconnecting">Reconnecting...</div>
|
||||
</ng-container>
|
||||
</a>
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
<div ngbDropdownMenu>
|
||||
<button ngbDropdownItem class="mainnet" routerLink="/"><img src="./resources/bitcoin-logo.png" style="width: 30px;" class="mr-1"> Mainnet</button>
|
||||
<button ngbDropdownItem *ngIf="env.TESTNET_ENABLED" class="testnet" [class.active]="network.val === 'testnet'" routerLink="/testnet"><img src="./resources/testnet-logo.png" style="width: 30px;" class="mr-1"> Testnet</button>
|
||||
<h6 *ngIf="env.LIQUID_ENABLED || env.BISQ_ENABLED" class="dropdown-header">Layer 2 Networks</h6>
|
||||
<h6 *ngIf="env.LIQUID_ENABLED || env.BISQ_ENABLED" class="dropdown-header" i18n="master-page.layer2-networks-header">Layer 2 Networks</h6>
|
||||
<button ngbDropdownItem *ngIf="env.BISQ_ENABLED" class="mainnet" [class.active]="network.val === 'bisq'" routerLink="/bisq"><img src="./resources/bisq-logo.png" style="width: 30px;" class="mr-1"> Bisq</button>
|
||||
<button ngbDropdownItem *ngIf="env.LIQUID_ENABLED" class="liquid" [class.active]="network.val === 'liquid'" routerLink="/liquid"><img src="./resources/liquid-logo.png" style="width: 30px;" class="mr-1"> Liquid</button>
|
||||
</div>
|
||||
|
@ -13,23 +13,23 @@
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Median fee</td>
|
||||
<td i18n="mempool-block.median-fee">Median fee</td>
|
||||
<td>~{{ mempoolBlock.medianFee | number:'1.0-0' }} sat/vB (<app-fiat [value]="mempoolBlock.medianFee * 140" digitsInfo="1.2-2" ngbTooltip="Based on average native segwit transaction of 140 vBytes" placement="bottom"></app-fiat>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Fee span</td>
|
||||
<td i18n="mempool-block.fee-span">Fee span</td>
|
||||
<td><span class="yellow-color">{{ mempoolBlock.feeRange[0] | number:'1.0-0' }} - {{ mempoolBlock.feeRange[mempoolBlock.feeRange.length - 1] | number:'1.0-0' }} sat/vB</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total fees</td>
|
||||
<td i18n="mempool-block.total-fees">Total fees</td>
|
||||
<td><app-amount [satoshis]="mempoolBlock.totalFees" [digitsInfo]="'1.2-2'" [noFiat]="true"></app-amount> (<app-fiat [value]="mempoolBlock.totalFees" digitsInfo="1.0-0"></app-fiat>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Transactions</td>
|
||||
<td i18n="mempool-block.transactions">Transactions</td>
|
||||
<td>{{ mempoolBlock.nTx }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Filled</td>
|
||||
<td i18n="mempool-block.filled">Filled</td>
|
||||
<td>
|
||||
<div class="progress position-relative">
|
||||
<div class="progress-bar progress-mempool {{ (network$ | async) }}" role="progressbar" [ngStyle]="{'width': (mempoolBlock.blockVSize / 1000000) * 100 + '%' }"></div>
|
||||
@ -48,4 +48,4 @@
|
||||
|
||||
<br>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -5,23 +5,23 @@
|
||||
<a [routerLink]="['/mempool-block/' | relativeUrl, i]" class="blockLink"> </a>
|
||||
<div class="block-body">
|
||||
<div class="fees">
|
||||
~{{ projectedBlock.medianFee | number:'1.0-0' }} sat/vB
|
||||
~{{ projectedBlock.medianFee | number:'1.0-0' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
|
||||
</div>
|
||||
<div class="fee-span">
|
||||
{{ projectedBlock.feeRange[0] | number:'1.0-0' }} - {{ projectedBlock.feeRange[projectedBlock.feeRange.length - 1] | number:'1.0-0' }} sat/vB
|
||||
{{ projectedBlock.feeRange[0] | number:'1.0-0' }} - {{ projectedBlock.feeRange[projectedBlock.feeRange.length - 1] | number:'1.0-0' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
|
||||
</div>
|
||||
<div class="block-size">{{ projectedBlock.blockSize | bytes: 2 }}</div>
|
||||
<div class="transaction-count">{{ projectedBlock.nTx | number }} transaction<ng-template [ngIf]="projectedBlock.nTx !== 1">s</ng-template></div>
|
||||
<div class="transaction-count">{{ projectedBlock.nTx | number }} <ng-template [ngIf]="projectedBlock.nTx === 1" i18n="shared.transaction-count.singular">transaction</ng-template><ng-template [ngIf]="projectedBlock.nTx !== 1" i18n="shared.transaction-count.plural">transactions</ng-template></div>
|
||||
<div class="time-difference" *ngIf="projectedBlock.blockVSize <= 1000000; else mergedBlock">
|
||||
<ng-template [ngIf]="network === 'liquid'" [ngIfElse]="timeDiffMainnet">
|
||||
In < {{ 1 * i + 1 }} minute
|
||||
<span i18n="mempool-blocks.eta-of-next-block|Block Frequency">In</span> < {{ 1 * i + 1 }} <span i18n="shared.minute">minute</span>
|
||||
</ng-template>
|
||||
<ng-template #timeDiffMainnet>
|
||||
In ~{{ 10 * i + 10 }} minutes
|
||||
<span i18n="mempool-blocks.eta-of-next-block|Block Frequency">In</span> ~{{ 10 * i + 10 }} <span i18n="shared.minutes">minutes</span>
|
||||
</ng-template>
|
||||
</div>
|
||||
<ng-template #mergedBlock>
|
||||
<div class="time-difference"><b>({{ projectedBlock.blockVSize / 1000000 | ceil }} blocks)</b></div>
|
||||
<div class="time-difference"><b>({{ projectedBlock.blockVSize / 1000000 | ceil }} <span i18n="shared.blocks">blocks</span>)</b></div>
|
||||
</ng-template>
|
||||
</div>
|
||||
<span class="animated-border"></span>
|
||||
|
@ -7,6 +7,6 @@
|
||||
<a placement="bottom" [ngbTooltip]="title" [href]="url" target="_blank" class="badge badge-primary">{{ miner }}</a>
|
||||
</ng-template>
|
||||
<ng-template #unknownMiner>
|
||||
<span class="badge badge-secondary">Unknown</span>
|
||||
<span class="badge badge-secondary" i18n="miner.tag.unknown-miner">Unknown</span>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
|
@ -1,10 +1,10 @@
|
||||
<form [formGroup]="searchForm" (submit)="searchForm.valid && search()" novalidate>
|
||||
<div class="d-flex">
|
||||
<div class="search-box-container mr-2">
|
||||
<input #instance="ngbTypeahead" [ngbTypeahead]="typeaheadSearch" (selectItem)="itemSelected()" (focus)="focus$.next($any($event).target.value)" (click)="click$.next($any($event).target.value)" formControlName="searchText" type="text" class="form-control" placeholder="TXID, block height, hash or address">
|
||||
<input #instance="ngbTypeahead" [ngbTypeahead]="typeaheadSearch" (selectItem)="itemSelected()" (focus)="focus$.next($any($event).target.value)" (click)="click$.next($any($event).target.value)" formControlName="searchText" type="text" class="form-control" i18n-placeholder="search-form.searchbar-placeholder" placeholder="TXID, block height, hash or address">
|
||||
</div>
|
||||
<div>
|
||||
<button [disabled]="isSearching" type="submit" class="btn btn-block btn-primary"><fa-icon [icon]="['fas', 'search']" [fixedWidth]="true" title="Search"></fa-icon></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<div class="row">
|
||||
<div class="col-lg-12" *ngIf="loading">
|
||||
<div class="text-center">
|
||||
<h3>Loading graphs...</h3>
|
||||
<h3 i18n="statistics.loading-graphs">Loading graphs...</h3>
|
||||
<br>
|
||||
<div class="spinner-border text-light"></div>
|
||||
</div>
|
||||
@ -13,7 +13,7 @@
|
||||
|
||||
<div class="card mb-3" *ngIf="mempoolStats.length">
|
||||
<div class="card-header">
|
||||
<i class="fa fa-area-chart"></i> Mempool by vBytes (sat/vByte)
|
||||
<i class="fa fa-area-chart"></i> <span i18n="statistics.memory-by-vBytes">Mempool by vBytes (sat/vByte)</span>
|
||||
|
||||
<form [formGroup]="radioGroupForm" style="float: right;">
|
||||
<div class="spinner-border text-light bootstrap-spinner" *ngIf="spinnerLoading"></div>
|
||||
@ -54,7 +54,8 @@
|
||||
<div class="col-lg-12">
|
||||
<div class="card mb-3" *ngIf="mempoolTransactionsWeightPerSecondData">
|
||||
<div class="card-header">
|
||||
<i class="fa fa-area-chart"></i> Transaction vBytes per second (vB/s)</div>
|
||||
<i class="fa fa-area-chart"></i> <span i18n="statistics.transaction-vbytes-per-second">Transaction vBytes per second (vB/s)</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div style="height: 600px;">
|
||||
<app-chartist
|
||||
|
@ -18,4 +18,4 @@
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -16,17 +16,6 @@ export class TimeSinceComponent implements OnInit, OnChanges, OnDestroy {
|
||||
constructor(
|
||||
private ref: ChangeDetectorRef
|
||||
) {
|
||||
if (document.body.clientWidth < 768) {
|
||||
this.intervals = {
|
||||
year: 31536000,
|
||||
month: 2592000,
|
||||
week: 604800,
|
||||
day: 86400,
|
||||
hour: 3600,
|
||||
min: 60,
|
||||
sec: 1
|
||||
};
|
||||
} else {
|
||||
this.intervals = {
|
||||
year: 31536000,
|
||||
month: 2592000,
|
||||
@ -36,7 +25,6 @@ export class TimeSinceComponent implements OnInit, OnChanges, OnDestroy {
|
||||
minute: 60,
|
||||
second: 1
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
@ -58,7 +46,7 @@ export class TimeSinceComponent implements OnInit, OnChanges, OnDestroy {
|
||||
calculate() {
|
||||
const seconds = Math.floor((+new Date() - +new Date(this.time * 1000)) / 1000);
|
||||
if (seconds < 60) {
|
||||
return '< 1 minute';
|
||||
return $localize`:@@time-since.just-now:Just now`;
|
||||
}
|
||||
let counter;
|
||||
for (const i in this.intervals) {
|
||||
@ -66,9 +54,41 @@ export class TimeSinceComponent implements OnInit, OnChanges, OnDestroy {
|
||||
counter = Math.floor(seconds / this.intervals[i]);
|
||||
if (counter > 0) {
|
||||
if (counter === 1) {
|
||||
return counter + ' ' + i; // singular (1 day ago)
|
||||
switch (i) { // singular (1 day ago)
|
||||
case 'year': return $localize`:@@time-since.year.ago:${counter}:INTERPOLATION: year ago`; break;
|
||||
case 'month': return $localize`:@@time-since.month.ago:${counter}:INTERPOLATION: month ago`; break;
|
||||
case 'week': return $localize`:@@time-since.week.ago:${counter}:INTERPOLATION: week ago`; break;
|
||||
case 'day': return $localize`:@@time-since.day.ago:${counter}:INTERPOLATION: day ago`; break;
|
||||
case 'hour': return $localize`:@@time-since.hour.ago:${counter}:INTERPOLATION: hour ago`; break;
|
||||
case 'minute':
|
||||
if (document.body.clientWidth < 768) {
|
||||
return $localize`:@@time-since.min.ago:${counter}:INTERPOLATION: min ago`;
|
||||
}
|
||||
return $localize`:@@time-since.minute.ago:${counter}:INTERPOLATION: minute ago`;
|
||||
case 'second':
|
||||
if (document.body.clientWidth < 768) {
|
||||
return $localize`:@@time-since.sec.ago:${counter}:INTERPOLATION: sec ago`;
|
||||
}
|
||||
return $localize`:@@time-since.second.ago:${counter}:INTERPOLATION: second ago`;
|
||||
}
|
||||
} else {
|
||||
return counter + ' ' + i + 's'; // plural (2 days ago)
|
||||
switch (i) { // plural (2 days ago)
|
||||
case 'year': return $localize`:@@time-since.years.ago:${counter}:INTERPOLATION: years ago`; break;
|
||||
case 'month': return $localize`:@@time-since.months.ago:${counter}:INTERPOLATION: months ago`; break;
|
||||
case 'week': return $localize`:@@time-since.weeks.ago:${counter}:INTERPOLATION: weeks ago`; break;
|
||||
case 'day': return $localize`:@@time-since.days.ago:${counter}:INTERPOLATION: days ago`; break;
|
||||
case 'hour': return $localize`:@@time-since.hours.ago:${counter}:INTERPOLATION: hours ago`; break;
|
||||
case 'minute':
|
||||
if (document.body.clientWidth < 768) {
|
||||
return $localize`:@@time-since.mins.ago:${counter}:INTERPOLATION: mins ago`;
|
||||
}
|
||||
return $localize`:@@time-since.minutes.ago:${counter}:INTERPOLATION: minutes ago`;
|
||||
case 'second':
|
||||
if (document.body.clientWidth < 768) {
|
||||
return $localize`:@@time-since.secs.ago:${counter}:INTERPOLATION: secs ago`;
|
||||
}
|
||||
return $localize`:@@time-since.seconds.ago:${counter}:INTERPOLATION: seconds ago`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,20 +2,20 @@
|
||||
|
||||
<div class="title-block">
|
||||
<div *ngIf="rbfTransaction" class="alert alert-mempool" role="alert">
|
||||
This transaction has been replaced by:
|
||||
<span i18n="transaction.rbf.replacement|RBF replacement">This transaction has been replaced by:</span>
|
||||
<a class="alert-link" [routerLink]="['/tx/' | relativeUrl, rbfTransaction.txid]" [state]="{ data: rbfTransaction }">
|
||||
<span class="d-inline d-lg-none">{{ rbfTransaction.txid | shortenString : 24 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ rbfTransaction.txid }}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h1 class="float-left mr-3 mb-md-3">Transaction</h1>
|
||||
<h1 class="float-left mr-3 mb-md-3" i18n="shared.transaction">Transaction</h1>
|
||||
|
||||
<ng-template [ngIf]="tx?.status?.confirmed">
|
||||
<button *ngIf="latestBlock" type="button" class="btn btn-sm btn-success float-right mr-2 mt-1 mt-md-3">{{ latestBlock.height - tx.status.block_height + 1 }} confirmation<ng-container *ngIf="latestBlock.height - tx.status.block_height + 1 > 1">s</ng-container></button>
|
||||
<button *ngIf="latestBlock" type="button" class="btn btn-sm btn-success float-right mr-2 mt-1 mt-md-3">{{ latestBlock.height - tx.status.block_height + 1 }} <ng-container *ngIf="latestBlock.height - tx.status.block_height + 1 == 1" i18n="shared.confirmation-count.singular|Transaction singular confirmation count">confirmation</ng-container><ng-container *ngIf="latestBlock.height - tx.status.block_height + 1 > 1" i18n="shared.confirmation-count.plural|Transaction plural confirmation count">confirmations</ng-container></button>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="tx && !tx?.status.confirmed">
|
||||
<button type="button" class="btn btn-sm btn-danger float-right mr-2 mt-1 mt-md-3">Unconfirmed</button>
|
||||
<button type="button" class="btn btn-sm btn-danger float-right mr-2 mt-1 mt-md-3" i18n="transaction.unconfirmed|Transaction unconfirmed state">Unconfirmed</button>
|
||||
</ng-template>
|
||||
|
||||
<div>
|
||||
@ -39,28 +39,28 @@
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Timestamp</td>
|
||||
<td i18n="transaction.timestamp|Transaction Timestamp">Timestamp</td>
|
||||
<td>
|
||||
{{ tx.status.block_time * 1000 | date:'yyyy-MM-dd HH:mm' }}
|
||||
<div class="lg-inline">
|
||||
<i>(<app-time-since [time]="tx.status.block_time" [fastRender]="true"></app-time-since> ago)</i>
|
||||
<i>(<app-time-since [time]="tx.status.block_time" [fastRender]="true"></app-time-since>)</i>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr *ngIf="latestBlock && tx.status.block_height <= latestBlock.height - 8">
|
||||
<td class="td-width">Included in block</td>
|
||||
<td class="td-width" i18n="transaction.included-in-block|Transaction included in block">Included in block</td>
|
||||
<td>
|
||||
<a [routerLink]="['/block/' | relativeUrl, tx.status.block_hash]" [state]="{ data: { blockHeight: tx.status.block_height } }">{{ tx.status.block_height }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
<ng-template [ngIf]="transactionTime > 0">
|
||||
<tr>
|
||||
<td>Confirmed</td>
|
||||
<td>After <app-timespan [time]="tx.status.block_time - transactionTime"></app-timespan></td>
|
||||
<td i18n="transaction.confirmed|Transaction Confirmed state">Confirmed</td>
|
||||
<td><span i18n="transaction.confirmed.after|Transaction confirmed after">After</span> <app-timespan [time]="tx.status.block_time - transactionTime"></app-timespan></td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<tr *ngIf="network !== 'liquid'">
|
||||
<td class="td-width">Features</td>
|
||||
<td class="td-width" i18n="transaction.features|Transaction features">Features</td>
|
||||
<td>
|
||||
<app-tx-features [tx]="tx"></app-tx-features>
|
||||
</td>
|
||||
@ -72,13 +72,13 @@
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="td-width">Fee</td>
|
||||
<td class="td-width" i18n="transaction.fee|Transaction fee">Fee</td>
|
||||
<td>{{ tx.fee | number }} sat (<app-fiat [value]="tx.fee"></app-fiat>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Fee per vByte</td>
|
||||
<td i18n="transaction.fee-per-vbyte|Transaction fee">Fee per vByte</td>
|
||||
<td>
|
||||
{{ tx.fee / (tx.weight / 4) | number : '1.1-1' }} sat/vB
|
||||
{{ tx.fee / (tx.weight / 4) | number : '1.1-1' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
|
||||
|
||||
<app-tx-fee-rating *ngIf="tx.fee" [tx]="tx"></app-tx-fee-rating>
|
||||
</td>
|
||||
@ -106,35 +106,35 @@
|
||||
</tr>
|
||||
<ng-template #firstSeenTmpl>
|
||||
<tr>
|
||||
<td>First seen</td>
|
||||
<td><i><app-time-since [time]="transactionTime" [fastRender]="true"></app-time-since> ago</i></td>
|
||||
<td i18n="transaction.first-seen|Transaction first seen">First seen</td>
|
||||
<td><i><app-time-since [time]="transactionTime" [fastRender]="true"></app-time-since></i></td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
<tr>
|
||||
<td class="td-width">ETA</td>
|
||||
<td class="td-width" i18n="transaction.eta|Transaction ETA">ETA</td>
|
||||
<td>
|
||||
<ng-template [ngIf]="txInBlockIndex === undefined" [ngIfElse]="estimationTmpl">
|
||||
<span class="skeleton-loader"></span>
|
||||
</ng-template>
|
||||
<ng-template #estimationTmpl>
|
||||
<ng-template [ngIf]="txInBlockIndex >= 7" [ngIfElse]="belowBlockLimit">
|
||||
In several hours (or more)
|
||||
<span i18n="transaction.eta.in-several-hours|Transaction ETA in several hours or more">In several hours (or more)</span>
|
||||
</ng-template>
|
||||
<ng-template #belowBlockLimit>
|
||||
<ng-template [ngIf]="network === 'liquid'" [ngIfElse]="timeEstimateDefault">
|
||||
< {{ 1 * txInBlockIndex + 1 }} minutes <i>({{ txInBlockIndex + 1 }} block{{ txInBlockIndex > 0 ? 's' : '' }})</i>
|
||||
< {{ 1 * txInBlockIndex + 1 }} <span i18n="transaction.minutes|Transaction Minutes">minutes</span> <i>({{ txInBlockIndex + 1 }} <span i18n="transaction.eta.block|Transaction ETA (X blocks)">block</span>{{ txInBlockIndex > 0 ? 's' : '' }})</i>
|
||||
</ng-template>
|
||||
<ng-template #timeEstimateDefault>
|
||||
|
||||
~{{ 10 * txInBlockIndex + 10 }} minutes <i>({{ txInBlockIndex + 1 }} block{{ txInBlockIndex > 0 ? 's' : '' }})</i>
|
||||
~{{ 10 * txInBlockIndex + 10 }} <span i18n="transaction.minutes|Transaction Minutes">minutes</span> <i>({{ txInBlockIndex + 1 }} <span i18n="transaction.eta.block|Transaction ETA (X blocks)">block</span>{{ txInBlockIndex > 0 ? 's' : '' }})</i>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</td>
|
||||
</tr>
|
||||
<tr *ngIf="network !== 'liquid'">
|
||||
<td class="td-width">Features</td>
|
||||
<td class="td-width" i18n="transaction.features|Transaction Features">Features</td>
|
||||
<td>
|
||||
<app-tx-features [tx]="tx"></app-tx-features>
|
||||
</td>
|
||||
@ -146,12 +146,12 @@
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="td-width">Fee</td>
|
||||
<td>{{ tx.fee | number }} sat (<app-fiat [value]="tx.fee"></app-fiat>)</td>
|
||||
<td class="td-width" i18n="transaction.fee|Transaction Fee">Fee</td>
|
||||
<td>{{ tx.fee | number }} <span i18n="transaction.fee.sat|Transaction Fee sat">sat</span> (<app-fiat [value]="tx.fee"></app-fiat>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Fee per vByte</td>
|
||||
<td>{{ tx.fee / (tx.weight / 4) | number : '1.1-1' }} sat/vB</td>
|
||||
<td i18n="transaction.fee-per-vbyte|Transaction fee">Fee per vByte</td>
|
||||
<td>{{ tx.fee / (tx.weight / 4) | number : '1.1-1' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -162,24 +162,24 @@
|
||||
|
||||
<br>
|
||||
|
||||
<h2 class="float-left">Inputs & Outputs</h2>
|
||||
<h2 class="float-left" i18n="transaction.inputs-and-outputs|Transaction inputs and outputs">Inputs & Outputs</h2>
|
||||
|
||||
<button type="button" class="btn btn-outline-info btn-sm float-right mr-1 mt-0 mt-md-2" (click)="txList.toggleDetails()">Details</button>
|
||||
<button type="button" class="btn btn-outline-info btn-sm float-right mr-1 mt-0 mt-md-2" (click)="txList.toggleDetails()" i18n="transaction.details|Transaction Details">Details</button>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<app-transactions-list #txList [transactions]="[tx]" [transactionPage]="true"></app-transactions-list>
|
||||
|
||||
<h2>Details</h2>
|
||||
<h2 i18n="transaction.details">Details</h2>
|
||||
<div class="box">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Size</td>
|
||||
<td i18n="transaction.size|Transaction Size">Size</td>
|
||||
<td>{{ tx.size | bytes: 2 }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Weight</td>
|
||||
<td i18n="transaction.weight|Transaction Weight">Weight</td>
|
||||
<td>{{ tx.weight | wuBytes: 2 }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -263,8 +263,8 @@
|
||||
<ng-template [ngIf]="error">
|
||||
|
||||
<div class="text-center" *ngIf="waitingForTransaction; else errorTemplate">
|
||||
<h3>Transaction not found.</h3>
|
||||
<h5>Waiting for it to appear in the mempool...</h5>
|
||||
<h3 i18n="transaction.error.transaction-not-found">Transaction not found.</h3>
|
||||
<h5 i18n="transaction.error.waiting-for-it-to-appear">Waiting for it to appear in the mempool...</h5>
|
||||
<div class="spinner-border text-light mt-2"></div>
|
||||
</div>
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
<div class="float-right">
|
||||
<ng-template [ngIf]="tx.status.confirmed">{{ tx.status.block_time * 1000 | date:'yyyy-MM-dd HH:mm' }}</ng-template>
|
||||
<ng-template [ngIf]="!tx.status.confirmed && tx.firstSeen">
|
||||
<i><app-time-since [time]="tx.firstSeen" [fastRender]="true"></app-time-since> ago</i>
|
||||
<i><app-time-since [time]="tx.firstSeen" [fastRender]="true"></app-time-since></i>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
@ -36,9 +36,9 @@
|
||||
</td>
|
||||
<td>
|
||||
<div [ngSwitch]="true">
|
||||
<ng-container *ngSwitchCase="vin.is_coinbase"><a placement="bottom" [ngbTooltip]="vin.scriptsig | hex2ascii">Coinbase<ng-template [ngIf]="network !== 'liquid'"> (Newly Generated Coins)</ng-template></a><br><span class="badge badge-secondary scriptmessage longer">{{ vin.scriptsig | hex2ascii }}</span></ng-container>
|
||||
<ng-container *ngSwitchCase="vin.is_coinbase"><a placement="bottom" [ngbTooltip]="vin.scriptsig | hex2ascii"><span i18n="transactions-list.coinbase">Coinbase</span><ng-template [ngIf]="network !== 'liquid'"> <span i18n="transactions-list.newly-generated-coins">(Newly Generated Coins)</span></ng-template></a><br><span class="badge badge-secondary scriptmessage longer">{{ vin.scriptsig | hex2ascii }}</span></ng-container>
|
||||
<ng-container *ngSwitchCase="vin.is_pegin">
|
||||
Peg-in
|
||||
<span i18n="transactions-list.peg-in">Peg-in</span>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchDefault>
|
||||
<a [routerLink]="['/address/' | relativeUrl, vin.prevout.scriptpubkey_address]" title="{{ vin.prevout.scriptpubkey_address }}">
|
||||
@ -68,32 +68,32 @@
|
||||
<tbody>
|
||||
<ng-template [ngIf]="vin.scriptsig">
|
||||
<tr>
|
||||
<td>ScriptSig (ASM)</td>
|
||||
<td i18n="transactions-list.scriptsig.asm|ScriptSig (ASM)">ScriptSig (ASM)</td>
|
||||
<td [innerHTML]="vin.scriptsig_asm | asmStyler"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ScriptSig (HEX)</td>
|
||||
<td i18n="transactions-list.scriptsig.hex|ScriptSig (HEX)">ScriptSig (HEX)</td>
|
||||
<td>{{ vin.scriptsig }}</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<tr *ngIf="vin.witness">
|
||||
<td>Witness</td>
|
||||
<td i18n="transactions-list.witness">Witness</td>
|
||||
<td>{{ vin.witness.join(' ') }}</td>
|
||||
</tr>
|
||||
<tr *ngIf="vin.inner_redeemscript_asm">
|
||||
<td>P2SH redeem script</td>
|
||||
<td i18n="transactions-list.p2sh-redeem-script">P2SH redeem script</td>
|
||||
<td [innerHTML]="vin.inner_redeemscript_asm | asmStyler"></td>
|
||||
</tr>
|
||||
<tr *ngIf="vin.inner_witnessscript_asm">
|
||||
<td>P2WSH witness script</td>
|
||||
<td i18n="transactions-list.p2wsh-witness-script">P2WSH witness script</td>
|
||||
<td [innerHTML]="vin.inner_witnessscript_asm | asmStyler"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>nSequence</td>
|
||||
<td i18n="transactions-list.nsequence">nSequence</td>
|
||||
<td>{{ formatHex(vin.sequence) }}</td>
|
||||
</tr>
|
||||
<tr *ngIf="vin.prevout">
|
||||
<td>Previous output script</td>
|
||||
<td i18n="transactions-list.previous-output-script">Previous output script</td>
|
||||
<td [innerHTML]="vin.prevout.scriptpubkey_asm | asmStyler">{{ vin.prevout.scriptpubkey_type ? ('(' + vin.prevout.scriptpubkey_type + ')') : '' }}"</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -103,7 +103,7 @@
|
||||
</ng-template>
|
||||
<tr *ngIf="tx.vin.length > 10 && tx['@vinLimit']">
|
||||
<td colspan="3" class="text-center">
|
||||
<button class="btn btn-sm btn-primary mt-2" (click)="tx['@vinLimit'] = false;">Load all ({{ tx.vin.length - 10 }})</button>
|
||||
<button class="btn btn-sm btn-primary mt-2" (click)="tx['@vinLimit'] = false;"><span i18n="transactions-list.load-all">Load all</span> ({{ tx.vin.length - 10 }})</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -122,7 +122,8 @@
|
||||
</a>
|
||||
<ng-template #scriptpubkey_type>
|
||||
<ng-template [ngIf]="vout.pegout" [ngIfElse]="defaultscriptpubkey_type">
|
||||
Peg-out to <a [routerLink]="['/address/', vout.pegout.scriptpubkey_address]" title="{{ vout.pegout.scriptpubkey_address }}">
|
||||
<span i18n="transactions-list.peg-out-to">Peg-out to</span>
|
||||
<a [routerLink]="['/address/', vout.pegout.scriptpubkey_address]" title="{{ vout.pegout.scriptpubkey_address }}">
|
||||
<span class="d-block d-lg-none">{{ vout.pegout.scriptpubkey_address | shortenString : 16 }}</span>
|
||||
<span class="d-none d-lg-block">{{ vout.pegout.scriptpubkey_address | shortenString : 35 }}</span>
|
||||
</a>
|
||||
@ -160,19 +161,19 @@
|
||||
<table class="table table-striped table-borderless details-table mb-3">
|
||||
<tbody>
|
||||
<tr *ngIf="vout.scriptpubkey_type">
|
||||
<td>Type</td>
|
||||
<td i18n="transactions-list.vout.scriptpubkey-type">Type</td>
|
||||
<td>{{ vout.scriptpubkey_type.toUpperCase() }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>scriptPubKey (ASM)</td>
|
||||
<td i18n="transactions-list.scriptpubkey.asm|ScriptPubKey (ASM)">ScriptPubKey (ASM)</td>
|
||||
<td [innerHTML]="vout.scriptpubkey_asm | asmStyler"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>scriptPubKey (HEX)</td>
|
||||
<td i18n="transactions-list.scriptpubkey.hex|ScriptPubKey (HEX)">ScriptPubKey (HEX)</td>
|
||||
<td>{{ vout.scriptpubkey }}</td>
|
||||
</tr>
|
||||
<tr *ngIf="vout.scriptpubkey_type == 'op_return'">
|
||||
<td>OP_RETURN data</td>
|
||||
<td>OP_RETURN <span i18n="transactions-list.vout.scriptpubkey-type.data">data</span></td>
|
||||
<td>{{ vout.scriptpubkey_asm | hex2ascii }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -182,7 +183,7 @@
|
||||
</ng-template>
|
||||
<tr *ngIf="tx.vout.length > 10 && tx['@voutLimit']">
|
||||
<td colspan="3" class="text-center">
|
||||
<button class="btn btn-sm btn-primary mt-2" (click)="tx['@voutLimit'] = false;">Load all ({{ tx.vout.length - 10 }})</button>
|
||||
<button class="btn btn-sm btn-primary mt-2" (click)="tx['@voutLimit'] = false;"><span i18n="transactions-list.load-all">Load all</span> ({{ tx.vout.length - 10 }})</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -192,19 +193,19 @@
|
||||
|
||||
<div>
|
||||
<div class="float-left mt-2-5" *ngIf="!transactionPage && tx.fee">
|
||||
{{ tx.fee / (tx.weight / 4) | number : '1.1-1' }} sat/vB <span class="d-none d-sm-inline-block"> – {{ tx.fee | number }} sat (<app-fiat [value]="tx.fee"></app-fiat>)</span>
|
||||
{{ tx.fee / (tx.weight / 4) | number : '1.1-1' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span> <span class="d-none d-sm-inline-block"> – {{ tx.fee | number }} <span i18n="shared.sat|sat">sat</span> (<app-fiat [value]="tx.fee"></app-fiat>)</span>
|
||||
</div>
|
||||
|
||||
<div class="float-right">
|
||||
<span *ngIf="showConfirmations && latestBlock$ | async as latestBlock">
|
||||
<button *ngIf="tx.status.confirmed; else unconfirmedButton" type="button" class="btn btn-sm btn-success mt-2">{{ latestBlock.height - tx.status.block_height + 1 }} confirmation<ng-container *ngIf="latestBlock.height - tx.status.block_height + 1 > 1">s</ng-container></button>
|
||||
<button *ngIf="tx.status.confirmed; else unconfirmedButton" type="button" class="btn btn-sm btn-success mt-2">{{ latestBlock.height - tx.status.block_height + 1 }} <ng-container *ngIf="latestBlock.height - tx.status.block_height + 1 == 1" i18n="shared.confirmation-count.singular">confirmation</ng-container><ng-container *ngIf="latestBlock.height - tx.status.block_height + 1 > 1" i18n="shared.confirmation-count.plural">confirmations</ng-container></button>
|
||||
<ng-template #unconfirmedButton>
|
||||
<button type="button" class="btn btn-sm btn-danger mt-2">Unconfirmed</button>
|
||||
<button type="button" class="btn btn-sm btn-danger mt-2" i18n="transactions-list.unconfirmed">Unconfirmed</button>
|
||||
</ng-template>
|
||||
|
||||
</span>
|
||||
<button type="button" class="btn btn-sm btn-primary mt-2" (click)="switchCurrency()">
|
||||
<ng-template [ngIf]="network === 'liquid'" [ngIfElse]="defaultAmount">Confidential</ng-template>
|
||||
<ng-template [ngIf]="network === 'liquid'" [ngIfElse]="defaultAmount" i18n="shared.confidential">Confidential</ng-template>
|
||||
<ng-template #defaultAmount>
|
||||
<app-amount [satoshis]="getTotalTxOutput(tx)"></app-amount>
|
||||
</ng-template>
|
||||
|
@ -0,0 +1,22 @@
|
||||
<!-- this component is only used for the angular string extractor -->{{counter}}
|
||||
<div>
|
||||
<span i18n="@@time-since.just-now">Just now</span>
|
||||
<span i18n="@@time-since.sec.ago">{{counter}} sec ago</span>
|
||||
<span i18n="@@time-since.secs.ago">{{counter}} secs ago</span>
|
||||
<span i18n="@@time-since.second.ago">{{counter}} second ago</span>
|
||||
<span i18n="@@time-since.seconds.ago">{{counter}} seconds ago</span>
|
||||
<span i18n="@@time-since.min.ago">{{counter}} min ago</span>
|
||||
<span i18n="@@time-since.mins.ago">{{counter}} mins ago</span>
|
||||
<span i18n="@@time-since.minute.ago">{{counter}} minute ago</span>
|
||||
<span i18n="@@time-since.minutes.ago">{{counter}} minutes ago</span>
|
||||
<span i18n="@@time-since.hour.ago">{{counter}} hour ago</span>
|
||||
<span i18n="@@time-since.hours.ago">{{counter}} hours ago</span>
|
||||
<span i18n="@@time-since.day.ago">{{counter}} day ago</span>
|
||||
<span i18n="@@time-since.days.ago">{{counter}} days ago</span>
|
||||
<span i18n="@@time-since.week.ago">{{counter}} week ago</span>
|
||||
<span i18n="@@time-since.weeks.ago">{{counter}} weeks ago</span>
|
||||
<span i18n="@@time-since.month.ago">{{counter}} month ago</span>
|
||||
<span i18n="@@time-since.months.ago">{{counter}} months ago</span>
|
||||
<span i18n="@@time-since.year.ago">{{counter}} year ago</span>
|
||||
<span i18n="@@time-since.years.ago">{{counter}} years ago</span>
|
||||
</div>
|
@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-translation-strings',
|
||||
templateUrl: './translation-strings.component.html'
|
||||
})
|
||||
export class TranslationStringsComponent {
|
||||
counter: string;
|
||||
constructor() { }
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
<span *ngIf="segwitGains.realizedGains && !segwitGains.potentialBech32Gains; else segwitTwo" class="badge badge-success mr-1" ngbTooltip="This transaction saved {{ segwitGains.realizedGains * 100 | number: '1.0-0' }}% on fees by using native SegWit-Bech32" placement="bottom">SegWit</span>
|
||||
<span *ngIf="segwitGains.realizedGains && !segwitGains.potentialBech32Gains; else segwitTwo" class="badge badge-success mr-1" ngbTooltip="This transaction saved {{ segwitGains.realizedGains * 100 | number: '1.0-0' }}% on fees by using native SegWit-Bech32" placement="bottom" i18n="tx-features.tag.segwit|SegWit">SegWit</span>
|
||||
<ng-template #segwitTwo>
|
||||
<span *ngIf="segwitGains.realizedGains && segwitGains.potentialBech32Gains else potentialP2shGains" class="badge badge-warning mr-1" ngbTooltip="This transaction saved {{ segwitGains.realizedGains * 100 | number: '1.0-0' }}% on fees by using SegWit and could save {{ segwitGains.potentialBech32Gains * 100 | number : '1.0-0' }}% more by fully upgrading to native SegWit-Bech32" placement="bottom">SegWit</span>
|
||||
<span *ngIf="segwitGains.realizedGains && segwitGains.potentialBech32Gains else potentialP2shGains" class="badge badge-warning mr-1" ngbTooltip="This transaction saved {{ segwitGains.realizedGains * 100 | number: '1.0-0' }}% on fees by using SegWit and could save {{ segwitGains.potentialBech32Gains * 100 | number : '1.0-0' }}% more by fully upgrading to native SegWit-Bech32" placement="bottom" i18n="tx-features.tag.segwit|SegWit">SegWit</span>
|
||||
<ng-template #potentialP2shGains>
|
||||
<span *ngIf="segwitGains.potentialP2shGains" class="badge badge-danger mr-1" ngbTooltip="This transaction could save {{ segwitGains.potentialBech32Gains * 100 | number : '1.0-0' }}% on fees by upgrading to native SegWit-Bech32 or {{ segwitGains.potentialP2shGains * 100 | number: '1.0-0' }}% by upgrading to SegWit-P2SH" placement="bottom"><del>SegWit</del></span>
|
||||
<span *ngIf="segwitGains.potentialP2shGains" class="badge badge-danger mr-1" ngbTooltip="This transaction could save {{ segwitGains.potentialBech32Gains * 100 | number : '1.0-0' }}% on fees by upgrading to native SegWit-Bech32 or {{ segwitGains.potentialP2shGains * 100 | number: '1.0-0' }}% by upgrading to SegWit-P2SH" placement="bottom"><del i18n="tx-features.tag.segwit|SegWit">SegWit</del></span>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
<span *ngIf="isRbfTransaction" class="badge badge-success" ngbTooltip="This transaction support Replace-By-Fee (RBF) allowing fee bumping" placement="bottom">RBF</span>
|
||||
<span *ngIf="isRbfTransaction" class="badge badge-success" ngbTooltip="This transaction support Replace-By-Fee (RBF) allowing fee bumping" placement="bottom" i18n="tx-features.tag.rbf|RBF">RBF</span>
|
||||
|
@ -1,3 +1,3 @@
|
||||
<span *ngIf="feeRating === 1" class="badge badge-success">Optimal</span>
|
||||
<span *ngIf="feeRating === 2" class="badge badge-warning" title="Only ~{{ medianFeeNeeded }} sat/vB was needed to get into this block">Overpaid {{ overpaidTimes }}x</span>
|
||||
<span *ngIf="feeRating === 3" class="badge badge-danger" title="Only ~{{ medianFeeNeeded }} sat/vB was needed to get into this block">Overpaid {{ overpaidTimes }}x</span>
|
||||
<span *ngIf="feeRating === 1" class="badge badge-success" i18n="tx-fee-rating.optimal|TX Fee Rating is Optimal">Optimal</span>
|
||||
<span *ngIf="feeRating === 2" class="badge badge-warning" title="Only ~{{ medianFeeNeeded }} sat/vB was needed to get into this block" i18n="tx-fee-rating.overpaid.warning|TX Fee Rating is Warning">Overpaid {{ overpaidTimes }}x</span>
|
||||
<span *ngIf="feeRating === 3" class="badge badge-danger" title="Only ~{{ medianFeeNeeded }} sat/vB was needed to get into this block" i18n="tx-fee-rating.overpaid.danger|TX Fee Rating is Danger">Overpaid {{ overpaidTimes }}x</span>
|
||||
|
@ -72,18 +72,18 @@
|
||||
<div class="col mb-4">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Latest blocks</h5>
|
||||
<h5 class="card-title" i18n="dashboard.latest-blocks">Latest blocks</h5>
|
||||
<table class="table table-borderless text-left">
|
||||
<thead>
|
||||
<th style="width: 15%;">Height</th>
|
||||
<th style="width: 35%;">Mined</th>
|
||||
<th style="width: 20%;" class="d-none d-lg-table-cell">Txs</th>
|
||||
<th style="width: 30%;">Filled</th>
|
||||
<th style="width: 15%;" i18n="dashboard.latest-blocks.height">Height</th>
|
||||
<th style="width: 35%;" i18n="dashboard.latest-blocks.mined">Mined</th>
|
||||
<th style="width: 20%;" class="d-none d-lg-table-cell" i18n="dashboard.latest-blocks.transaction-count">TXs</th>
|
||||
<th style="width: 30%;" i18n="dashboard.latest-blocks.size">Size</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let block of blocks$ | async; let i = index; trackBy: trackByBlock">
|
||||
<td><a [routerLink]="['/block' | relativeUrl, block.id]" [state]="{ data: { block: block } }">{{ block.height }}</a></td>
|
||||
<td><app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since> ago</td>
|
||||
<td><app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since></td>
|
||||
<td class="d-none d-lg-table-cell">{{ block.tx_count | number }}</td>
|
||||
<td>
|
||||
<div class="progress position-relative">
|
||||
@ -94,27 +94,27 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="text-center"><a href="" [routerLink]="['/blocks' | relativeUrl]">View all »</a></div>
|
||||
<div class="text-center"><a href="" [routerLink]="['/blocks' | relativeUrl]" i18n="dashboard.view-all">View all »</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col mb-4">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Latest transactions</h5>
|
||||
<h5 class="card-title" i18n="dashboard.latest-transactions">Latest transactions</h5>
|
||||
<table class="table table-borderless text-left">
|
||||
<thead>
|
||||
<th style="width: 20%;">TXID</th>
|
||||
<th style="width: 35%;" class="text-right d-none d-lg-table-cell">Amount</th>
|
||||
<th *ngIf="(network$ | async) === ''" style="width: 20%;" class="text-right d-none d-lg-table-cell">USD</th>
|
||||
<th style="width: 25%;" class="text-right">Fee</th>
|
||||
<th style="width: 20%;" i18n="dashboard.latest-transactions.txid">TXID</th>
|
||||
<th style="width: 35%;" class="text-right d-none d-lg-table-cell" i18n="dashboard.latest-transactions.amount">Amount</th>
|
||||
<th *ngIf="(network$ | async) === ''" style="width: 20%;" class="text-right d-none d-lg-table-cell" i18n="dashboard.latest-transactions.USD">USD</th>
|
||||
<th style="width: 25%;" class="text-right" i18n="dashboard.latest-transactions.fee">Fee</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let transaction of transactions$ | async; let i = index;">
|
||||
<td><a [routerLink]="['/tx' | relativeUrl, transaction.txid]">{{ transaction.txid | shortenString : 10 }}</a></td>
|
||||
<td class="text-right d-none d-lg-table-cell"><app-amount [satoshis]="transaction.value" digitsInfo="1.8-8" [noFiat]="true"></app-amount></td>
|
||||
<td *ngIf="(network$ | async) === ''" class="text-right d-none d-lg-table-cell"><app-fiat [value]="transaction.value" digitsInfo="1.0-0"></app-fiat></td>
|
||||
<td class="text-right">{{ transaction.fee / (transaction.weight / 4) | number : '1.1-1' }} sat/vB</td>
|
||||
<td class="text-right">{{ transaction.fee / (transaction.weight / 4) | number : '1.1-1' }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -133,8 +133,14 @@
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div [formGroup]="languageForm" class="text-small text-center mt-4">
|
||||
<select formControlName="language" class="form-control form-control-secondary form-control-sm mx-auto" style="width: 130px;" (change)="changeLanguage()">
|
||||
<option *ngFor="let lang of languages" [value]="lang.code">{{ lang.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="text-small text-center mt-3">
|
||||
<a [routerLink]="['/terms-of-service']">Terms of Service</a>
|
||||
<a [routerLink]="['/terms-of-service']" i18n="shared.terms-of-service|Terms of Service">Terms of Service</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -153,15 +159,15 @@
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<td>
|
||||
<h5 class="card-title">Mempool size</h5>
|
||||
<h5 class="card-title" i18n="dashboard.mempool-size|Mempool size">Mempool size</h5>
|
||||
<p class="card-text" *ngIf="(mempoolBlocksData$ | async) as mempoolBlocksData; else loading">
|
||||
{{ mempoolBlocksData.size | bytes }} ({{ mempoolBlocksData.blocks }} block<span [hidden]="mempoolBlocksData.blocks <= 1">s</span>)
|
||||
{{ mempoolBlocksData.size | bytes }} ({{ mempoolBlocksData.blocks }} <span [hidden]="mempoolBlocksData.blocks == 1" i18n="dashboard.block">block</span><span [hidden]="mempoolBlocksData.blocks != 1">blocks</span>)
|
||||
</p>
|
||||
</td>
|
||||
<td>
|
||||
<h5 class="card-title">Unconfirmed</h5>
|
||||
<h5 class="card-title" i18n="dashboard.unconfirmed|Unconfirmed count">Unconfirmed</h5>
|
||||
<p class="card-text" *ngIf="mempoolInfoData.value; else loading">
|
||||
{{ mempoolInfoData.value.memPoolInfo.size | number }} TXs
|
||||
{{ mempoolInfoData.value.memPoolInfo.size | number }} <span i18n="dashboard.txs">TXs</span>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
@ -169,14 +175,14 @@
|
||||
</ng-template>
|
||||
|
||||
<ng-template #txPerSecond let-mempoolInfoData>
|
||||
<h5 class="card-title">Incoming transactions</h5>
|
||||
<h5 class="card-title" i18n="dashboard.incoming-transactions">Incoming transactions</h5>
|
||||
<ng-template [ngIf]="mempoolInfoData.value" [ngIfElse]="loading">
|
||||
<span *ngIf="mempoolInfoData.value.vBytesPerSecond === 0; else inSync">
|
||||
<span class="badge badge-pill badge-warning">Backend is synchronizing</span>
|
||||
<span class="badge badge-pill badge-warning" i18n="dashboard.backend-is-synchronizing">Backend is synchronizing</span>
|
||||
</span>
|
||||
<ng-template #inSync>
|
||||
<div class="progress sub-text" style="max-width: 250px;">
|
||||
<div class="progress-bar {{ mempoolInfoData.value.progressClass }}" style="padding: 4px;" role="progressbar" [ngStyle]="{'width': mempoolInfoData.value.progressWidth}">{{ mempoolInfoData.value.vBytesPerSecond | ceil | number }} vB/s</div>
|
||||
<div class="progress-bar {{ mempoolInfoData.value.progressClass }}" style="padding: 4px;" role="progressbar" [ngStyle]="{'width': mempoolInfoData.value.progressWidth}">{{ mempoolInfoData.value.vBytesPerSecond | ceil | number }} <ng-template i18n="shared.vbytes-per-second|vB/s">vB/s</ng-template></div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
@ -185,7 +191,7 @@
|
||||
<ng-template #difficultyEpoch>
|
||||
<div class="card text-center">
|
||||
<div class="card-body more-padding">
|
||||
<h5 class="card-title">Difficulty adjustment</h5>
|
||||
<h5 class="card-title" i18n="dashboard.difficulty-adjustment">Difficulty adjustment</h5>
|
||||
<div class="progress" *ngIf="(difficultyEpoch$ | async) as epochData; else loading">
|
||||
<div class="progress-bar" role="progressbar" style="width: 15%; background-color: #105fb0" [ngStyle]="{'width': epochData.base}"><ng-template [ngIf]="epochData.change > 0">+</ng-template>{{ epochData.change | number: '1.0-2' }}%</div>
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: 0%" [ngStyle]="{'width': epochData.green}"></div>
|
||||
@ -193,4 +199,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
|
@ -7,10 +7,12 @@ import { MempoolInfo, TransactionStripped } from '../interfaces/websocket.interf
|
||||
import { ApiService } from '../services/api.service';
|
||||
import { StateService } from '../services/state.service';
|
||||
import * as Chartist from '@mempool/chartist';
|
||||
import { formatDate } from '@angular/common';
|
||||
import { DOCUMENT, formatDate } from '@angular/common';
|
||||
import { WebsocketService } from '../services/websocket.service';
|
||||
import { SeoService } from '../services/seo.service';
|
||||
import { StorageService } from '../services/storage.service';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { languages, Language } from '../app.constants';
|
||||
|
||||
interface MempoolBlocksData {
|
||||
blocks: number;
|
||||
@ -55,6 +57,8 @@ export class DashboardComponent implements OnInit {
|
||||
mempoolTransactionsWeightPerSecondData: any;
|
||||
mempoolStats$: Observable<MempoolStatsData>;
|
||||
transactionsWeightPerSecondOptions: any;
|
||||
languageForm: FormGroup;
|
||||
languages: Language[];
|
||||
|
||||
constructor(
|
||||
@Inject(LOCALE_ID) private locale: string,
|
||||
@ -63,14 +67,22 @@ export class DashboardComponent implements OnInit {
|
||||
private websocketService: WebsocketService,
|
||||
private seoService: SeoService,
|
||||
private storageService: StorageService,
|
||||
private formBuilder: FormBuilder,
|
||||
@Inject(DOCUMENT) private document: Document
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.languages = languages;
|
||||
this.seoService.resetTitle();
|
||||
this.websocketService.want(['blocks', 'stats', 'mempool-blocks', 'live-2h-chart']);
|
||||
this.network$ = merge(of(''), this.stateService.networkChanged$);
|
||||
this.collapseLevel = this.storageService.getValue('dashboard-collapsed') || 'one';
|
||||
|
||||
this.languageForm = this.formBuilder.group({
|
||||
language: ['']
|
||||
});
|
||||
this.setLanguageFromUrl();
|
||||
|
||||
this.mempoolInfoData$ = combineLatest([
|
||||
this.stateService.mempoolInfo$,
|
||||
this.stateService.vbytesPerSecond$
|
||||
@ -232,4 +244,21 @@ export class DashboardComponent implements OnInit {
|
||||
}
|
||||
this.storageService.setValue('dashboard-collapsed', this.collapseLevel);
|
||||
}
|
||||
|
||||
setLanguageFromUrl() {
|
||||
const urlLanguage = this.document.location.pathname.split('/')[1];
|
||||
if (urlLanguage === '') {
|
||||
this.languageForm.get('language').setValue('en');
|
||||
} else if (this.languages.map((lang) => lang.code).indexOf(urlLanguage) > -1) {
|
||||
this.languageForm.get('language').setValue(urlLanguage);
|
||||
}
|
||||
}
|
||||
|
||||
changeLanguage() {
|
||||
const language = this.languageForm.get('language').value;
|
||||
this.document.location.href = (language === 'en' ? '/' : '/' + language);
|
||||
try {
|
||||
document.cookie = `lang=${language}; expires=Thu, 18 Dec 2050 12:00:00 UTC; path=/`;
|
||||
} catch (e) { }
|
||||
}
|
||||
}
|
||||
|
@ -33,8 +33,8 @@
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<div style="font-size: 36px">🚨 mempool requires javascript🚨</div>
|
||||
<div style="font-size: 20px"> You need to enable JS in your browser to use this website.</div>
|
||||
<div style="font-size: 36px" i18n="nojs.warning.1">🚨 mempool requires javascript🚨</div>
|
||||
<div style="font-size: 20px" i18n="nojs.warning.2"> You need to enable JS in your browser to use this website.</div>
|
||||
</noscript>
|
||||
<app-root></app-root>
|
||||
<script type="text/javascript">
|
||||
|
2224
frontend/src/locale/messages.cs.xlf
Normal file
2224
frontend/src/locale/messages.cs.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2224
frontend/src/locale/messages.de.xlf
Normal file
2224
frontend/src/locale/messages.de.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2016
frontend/src/locale/messages.en_US.xlf
Normal file
2016
frontend/src/locale/messages.en_US.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2224
frontend/src/locale/messages.es.xlf
Normal file
2224
frontend/src/locale/messages.es.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2217
frontend/src/locale/messages.fa.xlf
Normal file
2217
frontend/src/locale/messages.fa.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2134
frontend/src/locale/messages.fr.xlf
Normal file
2134
frontend/src/locale/messages.fr.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2224
frontend/src/locale/messages.ja.xlf
Normal file
2224
frontend/src/locale/messages.ja.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2067
frontend/src/locale/messages.ko.xlf
Normal file
2067
frontend/src/locale/messages.ko.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2154
frontend/src/locale/messages.nl.xlf
Normal file
2154
frontend/src/locale/messages.nl.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2224
frontend/src/locale/messages.nn.xlf
Normal file
2224
frontend/src/locale/messages.nn.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2046
frontend/src/locale/messages.pl.xlf
Normal file
2046
frontend/src/locale/messages.pl.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2187
frontend/src/locale/messages.pt.xlf
Normal file
2187
frontend/src/locale/messages.pt.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2203
frontend/src/locale/messages.pt_BR.xlf
Normal file
2203
frontend/src/locale/messages.pt_BR.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2113
frontend/src/locale/messages.sl.xlf
Normal file
2113
frontend/src/locale/messages.sl.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2226
frontend/src/locale/messages.sv.xlf
Normal file
2226
frontend/src/locale/messages.sv.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2167
frontend/src/locale/messages.tr.xlf
Normal file
2167
frontend/src/locale/messages.tr.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2224
frontend/src/locale/messages.uk.xlf
Normal file
2224
frontend/src/locale/messages.uk.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2017
frontend/src/locale/messages.xlf
Normal file
2017
frontend/src/locale/messages.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2116
frontend/src/locale/messages.zh.xlf
Normal file
2116
frontend/src/locale/messages.zh.xlf
Normal file
File diff suppressed because it is too large
Load Diff
@ -97,6 +97,15 @@ body {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.form-control.form-control-secondary {
|
||||
color: #fff;
|
||||
background-color: #2d3348;
|
||||
border: 1px solid #2d3348;
|
||||
}
|
||||
.form-control.form-control-secondary:focus {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.h2-match-table {
|
||||
padding-left: .65rem;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
var https = require('https');
|
||||
var fs = require('fs');
|
||||
|
||||
var PATH = 'dist/mempool/resources/';
|
||||
var PATH = 'dist/mempool/en-US/resources/';
|
||||
if (process.argv[2] && process.argv[2] === 'dev') {
|
||||
PATH = 'src/resources/';
|
||||
}
|
||||
|
195
production/nginx-mempool.conf
Normal file
195
production/nginx-mempool.conf
Normal file
@ -0,0 +1,195 @@
|
||||
root /mempool/public_html/mainnet/;
|
||||
|
||||
index index.html;
|
||||
|
||||
add_header Onion-Location http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion$request_uri;
|
||||
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
|
||||
|
||||
set $frameOptions "DENY";
|
||||
set $contentSecurityPolicy "frame-ancestors 'none'";
|
||||
if ($http_referer ~ ^https://mempool.space/)
|
||||
{
|
||||
set $frameOptions "ALLOW-FROM https://mempool.space";
|
||||
set $contentSecurityPolicy "frame-ancestors https://mempool.space";
|
||||
}
|
||||
if ($http_referer ~ ^https://mempool.ninja/)
|
||||
{
|
||||
set $frameOptions "ALLOW-FROM https://mempool.ninja";
|
||||
set $contentSecurityPolicy "frame-ancestors https://mempool.ninja";
|
||||
}
|
||||
if ($http_referer ~ ^https://node100.bitcoin.wiz.biz/)
|
||||
{
|
||||
set $frameOptions "ALLOW-FROM https://node100.bitcoin.wiz.biz";
|
||||
set $contentSecurityPolicy "frame-ancestors https://node100.bitcoin.wiz.biz";
|
||||
}
|
||||
if ($http_referer ~ ^https://wiz.biz/)
|
||||
{
|
||||
set $frameOptions "ALLOW-FROM https://wiz.biz";
|
||||
set $contentSecurityPolicy "frame-ancestors https://wiz.biz";
|
||||
}
|
||||
|
||||
add_header X-Frame-Options $frameOptions;
|
||||
add_header Content-Security-Policy $contentSecurityPolicy;
|
||||
|
||||
# fallback
|
||||
location / {
|
||||
#return 302 https://mempool.space/$request_uri;
|
||||
try_files /$lang/$uri /$lang/$uri/ $uri $uri/ /en-US/$uri /$lang/index.html /en-US/index.html =404;
|
||||
}
|
||||
|
||||
# location block using regex are matched in order
|
||||
|
||||
# used to rewrite resources from /<lang>/ to /en-US/
|
||||
location ~ ^/(ar|bg|bs|ca|cs|da|de|et|el|es|eo|eu|fa|fr|gl|ko|hr|id|it|he|ka|lv|lt|hu|mk|ms|nl|ja|no|nb|nn|pl|pt|pt-BR|ro|ru|sk|sl|sr|sh|fi|sv|th|tr|uk|vi|zh)/resources/ {
|
||||
rewrite ^/[a-zA-Z-]*/resources/(.*) /en-US/resources/$1;
|
||||
}
|
||||
# used for cookie override
|
||||
location ~ ^/(ar|bg|bs|ca|cs|da|de|et|el|es|eo|eu|fa|fr|gl|ko|hr|id|it|he|ka|lv|lt|hu|mk|ms|nl|ja|no|nb|nn|pl|pt|pt-BR|ro|ru|sk|sl|sr|sh|fi|sv|th|tr|uk|vi|zh)/ {
|
||||
try_files $uri $uri/ /$1/index.html =404;
|
||||
}
|
||||
|
||||
# add /sitemap for production SEO
|
||||
location /sitemap {
|
||||
try_files $uri =410;
|
||||
}
|
||||
# old /explorer redirect from v1 days
|
||||
location /explorer {
|
||||
rewrite /explorer/(.*) https://$host/$1 permanent;
|
||||
}
|
||||
|
||||
# static API docs
|
||||
location = /api {
|
||||
#return 302 https://mempool.space/$request_uri;
|
||||
try_files $uri $uri/ /en-US/index.html =404;
|
||||
}
|
||||
location = /api/ {
|
||||
#return 302 https://mempool.space/$request_uri;
|
||||
try_files $uri $uri/ /en-US/index.html =404;
|
||||
}
|
||||
location = /liquid/api {
|
||||
#return 302 https://mempool.space/$request_uri;
|
||||
try_files $uri $uri/ /en-US/index.html =404;
|
||||
}
|
||||
location = /liquid/api/ {
|
||||
#return 302 https://mempool.space/$request_uri;
|
||||
try_files $uri $uri/ /en-US/index.html =404;
|
||||
}
|
||||
location = /testnet/api {
|
||||
#return 302 https://mempool.space/$request_uri;
|
||||
try_files $uri $uri/ /en-US/index.html =404;
|
||||
}
|
||||
location = /testnet/api/ {
|
||||
#return 302 https://mempool.space/$request_uri;
|
||||
try_files $uri $uri/ /en-US/index.html =404;
|
||||
}
|
||||
location = /bisq/api {
|
||||
#return 302 https://mempool.space/$request_uri;
|
||||
try_files $uri $uri/ /en-US/index.html =404;
|
||||
}
|
||||
location = /bisq/api/ {
|
||||
#return 302 https://mempool.space/$request_uri;
|
||||
try_files $uri $uri/ /en-US/index.html =404;
|
||||
}
|
||||
|
||||
# mainnet API
|
||||
location /api/v1/donations {
|
||||
proxy_pass http://127.0.0.1:8999;
|
||||
# don't rate limit this API prefix
|
||||
}
|
||||
location /api/v1/donations/images {
|
||||
proxy_pass http://127.0.0.1:8999;
|
||||
proxy_cache cache;
|
||||
proxy_cache_valid 200 1d;
|
||||
}
|
||||
location /api/v1/ws {
|
||||
proxy_pass http://127.0.0.1:8999/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
}
|
||||
location /api/v1 {
|
||||
proxy_pass http://127.0.0.1:8999/api/v1;
|
||||
limit_req burst=50 nodelay zone=api;
|
||||
}
|
||||
location /api/ {
|
||||
proxy_pass http://[::1]:3000/;
|
||||
limit_req burst=50 nodelay zone=electrs;
|
||||
}
|
||||
|
||||
# liquid API
|
||||
location /liquid/api/v1/ws {
|
||||
proxy_pass http://127.0.0.1:8998/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
}
|
||||
location /liquid/api/v1 {
|
||||
proxy_pass http://127.0.0.1:8998/api/v1;
|
||||
limit_req burst=50 nodelay zone=api;
|
||||
}
|
||||
location /liquid/api/ {
|
||||
proxy_pass http://[::1]:3001/;
|
||||
limit_req burst=50 nodelay zone=electrs;
|
||||
}
|
||||
|
||||
# testnet API
|
||||
location /testnet/api/v1/ws {
|
||||
proxy_pass http://127.0.0.1:8997/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
}
|
||||
location /testnet/api/v1 {
|
||||
proxy_pass http://127.0.0.1:8997/api/v1;
|
||||
limit_req burst=50 nodelay zone=api;
|
||||
}
|
||||
location /testnet/api/ {
|
||||
proxy_pass http://[::1]:3002/;
|
||||
limit_req burst=50 nodelay zone=electrs;
|
||||
}
|
||||
|
||||
# bisq API
|
||||
location /bisq/api/v1/ws {
|
||||
proxy_pass http://127.0.0.1:8996/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
}
|
||||
location /bisq/api/v1/markets {
|
||||
proxy_pass http://127.0.0.1:8996/api/v1/bisq/markets;
|
||||
#limit_req burst=50 nodelay zone=api;
|
||||
}
|
||||
location /bisq/api/v1 {
|
||||
proxy_pass http://127.0.0.1:8996/api/v1;
|
||||
limit_req burst=50 nodelay zone=api;
|
||||
}
|
||||
location /bisq/api {
|
||||
proxy_pass http://127.0.0.1:8996/api/v1/bisq;
|
||||
limit_req burst=50 nodelay zone=api;
|
||||
}
|
||||
|
||||
# mainnet API
|
||||
location /ws {
|
||||
proxy_pass http://127.0.0.1:8999/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
}
|
||||
location /ws/mainnet {
|
||||
proxy_pass http://127.0.0.1:8999/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
}
|
||||
location /ws/liquid {
|
||||
proxy_pass http://127.0.0.1:8998/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
}
|
||||
location /ws/testnet {
|
||||
proxy_pass http://127.0.0.1:8997/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
user nobody;
|
||||
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
worker_processes auto;
|
||||
@ -38,10 +37,6 @@ http {
|
||||
# number of requests per connection, does not affect SPDY
|
||||
keepalive_requests 100;
|
||||
|
||||
types_hash_max_size 2048;
|
||||
|
||||
proxy_cache off;
|
||||
|
||||
# enable gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
@ -55,203 +50,107 @@ http {
|
||||
client_max_body_size 10m;
|
||||
|
||||
# proxy cache
|
||||
proxy_cache off;
|
||||
proxy_cache_path /var/cache/nginx keys_zone=cache:20m levels=1:2 inactive=600s max_size=500m;
|
||||
types_hash_max_size 2048;
|
||||
|
||||
# rate limit requests
|
||||
limit_req_zone $binary_remote_addr zone=api:5m rate=50r/m;
|
||||
limit_req_zone $binary_remote_addr zone=electrs:5m rate=1000r/m;
|
||||
limit_req_zone $binary_remote_addr zone=api:5m rate=200r/m;
|
||||
limit_req_zone $binary_remote_addr zone=electrs:5m rate=2000r/m;
|
||||
limit_req_status 429;
|
||||
|
||||
# rate limit connections
|
||||
limit_conn_zone $binary_remote_addr zone=websocket:10m;
|
||||
limit_conn_status 429;
|
||||
|
||||
server {
|
||||
listen 80 backlog=1024;
|
||||
listen [::]:80 backlog=1024;
|
||||
map $http_accept_language $header_lang {
|
||||
default en-US;
|
||||
~*^en-US en-US;
|
||||
~*^en en-US;
|
||||
~*^cs cs;
|
||||
~*^es es;
|
||||
~*^fa fa;
|
||||
~*^ja ja;
|
||||
~*^nl nl;
|
||||
~*^pl pl;
|
||||
~*^pt pt;
|
||||
~*^sv sv;
|
||||
~*^uk uk;
|
||||
}
|
||||
|
||||
server_name mempool.space;
|
||||
map $cookie_lang $lang {
|
||||
default $header_lang;
|
||||
~*^en-US en-US;
|
||||
~*^en en-US;
|
||||
~*^cs cs;
|
||||
~*^es es;
|
||||
~*^fa fa;
|
||||
~*^ja ja;
|
||||
~*^nl nl;
|
||||
~*^pl pl;
|
||||
~*^pt pt;
|
||||
~*^sv sv;
|
||||
~*^uk uk;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name mempool.space mempool.ninja bsq.ninja node100.bitcoin.wiz.biz;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 127.0.0.1:81 backlog=1024;
|
||||
listen [::]:443 ssl default http2 backlog=1024;
|
||||
listen 443 ssl http2;
|
||||
server_name bsq.ninja;
|
||||
ssl_certificate /usr/local/etc/letsencrypt/live/bsq.ninja/fullchain.pem;
|
||||
ssl_certificate_key /usr/local/etc/letsencrypt/live/bsq.ninja/privkey.pem;
|
||||
include /usr/local/etc/letsencrypt/options-ssl-nginx.conf;
|
||||
ssl_dhparam /usr/local/etc/letsencrypt/ssl-dhparams.pem;
|
||||
|
||||
set $redirect_uri https://mempool.space/bisq;
|
||||
if ($uri = /tx.html) {
|
||||
set $redirect_uri https://mempool.space/bisq/tx/$arg_tx;
|
||||
}
|
||||
if ($uri = /txo.html) {
|
||||
set $redirect_uri https://mempool.space/bisq/tx/$arg_txo;
|
||||
}
|
||||
if ($uri = /Address.html) {
|
||||
set $redirect_uri https://mempool.space/bisq/address/$arg_addr;
|
||||
}
|
||||
return 301 $redirect_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name node100.bitcoin.wiz.biz;
|
||||
ssl_certificate /usr/local/etc/letsencrypt/live/node100.bitcoin.wiz.biz/fullchain.pem;
|
||||
ssl_certificate_key /usr/local/etc/letsencrypt/live/node100.bitcoin.wiz.biz/privkey.pem;
|
||||
include /usr/local/etc/letsencrypt/options-ssl-nginx.conf;
|
||||
ssl_dhparam /usr/local/etc/letsencrypt/ssl-dhparams.pem;
|
||||
|
||||
include /usr/local/etc/nginx/nginx-mempool.conf;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name mempool.ninja;
|
||||
ssl_certificate /usr/local/etc/letsencrypt/live/mempool.ninja/fullchain.pem;
|
||||
ssl_certificate_key /usr/local/etc/letsencrypt/live/mempool.ninja/privkey.pem;
|
||||
include /usr/local/etc/letsencrypt/options-ssl-nginx.conf;
|
||||
ssl_dhparam /usr/local/etc/letsencrypt/ssl-dhparams.pem;
|
||||
|
||||
include /usr/local/etc/nginx/nginx-mempool.conf;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 127.0.0.1:81;
|
||||
listen 443 ssl default http2 backlog=1024;
|
||||
|
||||
server_name mempool.space;
|
||||
|
||||
ssl_certificate /usr/local/etc/letsencrypt/live/mempool.space/fullchain.pem;
|
||||
ssl_certificate_key /usr/local/etc/letsencrypt/live/mempool.space/privkey.pem;
|
||||
include /usr/local/etc/letsencrypt/options-ssl-nginx.conf;
|
||||
ssl_dhparam /usr/local/etc/letsencrypt/ssl-dhparams.pem;
|
||||
|
||||
root /mempool/public_html/mainnet/;
|
||||
index index.html;
|
||||
|
||||
# security headers
|
||||
|
||||
set $frameOptions "DENY";
|
||||
set $contentSecurityPolicy "frame-ancestors 'none'";
|
||||
if ($http_referer ~ ^https://mempool.space/)
|
||||
{
|
||||
set $frameOptions "ALLOW-FROM https://mempool.space";
|
||||
set $contentSecurityPolicy "frame-ancestors https://mempool.space";
|
||||
}
|
||||
if ($http_referer ~ ^https://wiz.biz/)
|
||||
{
|
||||
set $frameOptions "ALLOW-FROM https://wiz.biz";
|
||||
set $contentSecurityPolicy "frame-ancestors https://wiz.biz";
|
||||
}
|
||||
add_header X-Frame-Options $frameOptions;
|
||||
add_header Content-Security-Policy $contentSecurityPolicy;
|
||||
add_header Link "<https://mempool.space$request_uri>; rel=\"canonical\"";
|
||||
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
|
||||
#add_header Onion-Location http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion$request_uri;
|
||||
|
||||
# /
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html =404;
|
||||
}
|
||||
|
||||
# # /sitemap
|
||||
# location /sitemap {
|
||||
# try_files $uri =410;
|
||||
# }
|
||||
#
|
||||
# # /explorer
|
||||
# location /explorer {
|
||||
# rewrite /explorer/(.*) https://$host/$1 permanent;
|
||||
# }
|
||||
|
||||
# /api
|
||||
|
||||
location = /api {
|
||||
try_files $uri $uri/ /index.html =404;
|
||||
}
|
||||
location = /api/ {
|
||||
try_files $uri $uri/ /index.html =404;
|
||||
}
|
||||
location /api/v1/donations/images {
|
||||
# don't rate limit this URL prefix
|
||||
proxy_pass http://127.0.0.1:8999;
|
||||
proxy_cache cache;
|
||||
proxy_cache_valid 200 1d;
|
||||
}
|
||||
location /api/v1/ws {
|
||||
proxy_pass http://127.0.0.1:8999/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
limit_conn websocket 10;
|
||||
}
|
||||
location /api/v1 {
|
||||
proxy_pass http://127.0.0.1:8999/api/v1;
|
||||
limit_req burst=50 nodelay zone=api;
|
||||
}
|
||||
location /api/ {
|
||||
proxy_pass http://[::1]:3000/;
|
||||
limit_req burst=100 nodelay zone=electrs;
|
||||
}
|
||||
|
||||
# /mainnet/api
|
||||
|
||||
location = /mainnet/api {
|
||||
try_files $uri $uri/ /index.html =404;
|
||||
}
|
||||
location = /mainnet/api/ {
|
||||
try_files $uri $uri/ /index.html =404;
|
||||
}
|
||||
location /mainnet/api/v1/ws {
|
||||
proxy_pass http://127.0.0.1:8999/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
limit_conn websocket 10;
|
||||
}
|
||||
location /mainnet/api/v1 {
|
||||
proxy_pass http://127.0.0.1:8999/api/v1;
|
||||
limit_req burst=50 nodelay zone=api;
|
||||
}
|
||||
location /mainnet/api/ {
|
||||
proxy_pass http://[::1]:3000/;
|
||||
limit_req burst=100 nodelay zone=electrs;
|
||||
}
|
||||
|
||||
# /liquid/api
|
||||
|
||||
location = /liquid/api {
|
||||
try_files $uri $uri/ /index.html =404;
|
||||
}
|
||||
location = /liquid/api/ {
|
||||
try_files $uri $uri/ /index.html =404;
|
||||
}
|
||||
location /liquid/api/v1/ws {
|
||||
proxy_pass http://127.0.0.1:8998/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
limit_conn websocket 10;
|
||||
}
|
||||
location /liquid/api/v1 {
|
||||
proxy_pass http://127.0.0.1:8998/api/v1;
|
||||
limit_req burst=50 nodelay zone=api;
|
||||
}
|
||||
location /liquid/api/ {
|
||||
proxy_pass http://[::1]:3001/;
|
||||
limit_req burst=100 nodelay zone=electrs;
|
||||
}
|
||||
|
||||
# /testnet/api
|
||||
|
||||
location = /testnet/api {
|
||||
try_files $uri $uri/ /index.html =404;
|
||||
}
|
||||
location = /testnet/api/ {
|
||||
try_files $uri $uri/ /index.html =404;
|
||||
}
|
||||
location /testnet/api/v1/ws {
|
||||
proxy_pass http://127.0.0.1:8997/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
limit_conn websocket 10;
|
||||
}
|
||||
location /testnet/api/v1 {
|
||||
proxy_pass http://127.0.0.1:8997/api/v1;
|
||||
limit_req burst=50 nodelay zone=api;
|
||||
}
|
||||
location /testnet/api/ {
|
||||
proxy_pass http://[::1]:3002/;
|
||||
limit_req burst=100 nodelay zone=electrs;
|
||||
}
|
||||
|
||||
# /bisq
|
||||
|
||||
location = /bisq/api {
|
||||
try_files $uri $uri/ /index.html =404;
|
||||
}
|
||||
location = /bisq/api/ {
|
||||
try_files $uri $uri/ /index.html =404;
|
||||
}
|
||||
location /bisq/api/v1/ws {
|
||||
proxy_pass http://127.0.0.1:8996/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
}
|
||||
location /bisq/api/v1/markets {
|
||||
proxy_pass http://127.0.0.1:8996/api/v1/bisq/markets;
|
||||
#limit_req burst=50 nodelay zone=api;
|
||||
}
|
||||
location /bisq/api/v1 {
|
||||
proxy_pass http://127.0.0.1:8996/api/v1;
|
||||
limit_req burst=50 nodelay zone=api;
|
||||
}
|
||||
location /bisq/api {
|
||||
proxy_pass http://127.0.0.1:8996/api/v1/bisq;
|
||||
limit_req burst=50 nodelay zone=api;
|
||||
}
|
||||
include /usr/local/etc/nginx/nginx-mempool.conf;
|
||||
}
|
||||
}
|
||||
|
49
production/test-nginx
Executable file
49
production/test-nginx
Executable file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env zsh
|
||||
PROTO=https
|
||||
HOSTNAME=mempool.ninja
|
||||
URL_BASE=${PROTO}://${HOSTNAME}
|
||||
|
||||
curltest()
|
||||
{
|
||||
read output
|
||||
if [ "${output}" = "$1" ];then
|
||||
echo "PASS: |${output}|"
|
||||
else
|
||||
echo "FAIL: |${output}|"
|
||||
echo "WANT: |$1|"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Starting tests to ${URL_BASE}"
|
||||
|
||||
echo "Test locale for / with no header or cookie"
|
||||
curl -s ${URL_BASE}/ | grep '<html lang' | tr -d '\r\n' | curltest '<html lang="en-US">'
|
||||
|
||||
echo "Test locale for / with 'ja' lang header and no cookie"
|
||||
curl -s -H 'Accept-Language: ja' ${URL_BASE}/ | grep '<html lang' | tr -d '\r\n' | curltest '<html lang="ja">'
|
||||
|
||||
echo "Test locale for / with 'ja' lang header and 'en' lang cookie"
|
||||
curl -s -H 'Accept-Language: ja' --cookie 'lang=en' ${URL_BASE}/ | grep '<html lang' | tr -d '\r\n' | curltest '<html lang="en-US">'
|
||||
|
||||
echo "Test locale for / with 'ja' lang header and 'sv' lang cookie"
|
||||
curl -s -H 'Accept-Language: ja' --cookie 'lang=sv' ${URL_BASE}/ | grep '<html lang' | tr -d '\r\n' | curltest '<html lang="sv">'
|
||||
|
||||
echo "Test locale for / with 'ja' lang header and 'foo' lang cookie"
|
||||
curl -s -H 'Accept-Language: ja' --cookie 'lang=foo' ${URL_BASE}/ | grep '<html lang' | tr -d '\r\n' | curltest '<html lang="ja">'
|
||||
|
||||
echo "Test rewrite for /resources/pools.json with no header and no cookie"
|
||||
curl -s -i ${URL_BASE}/resources/pools.json | grep -i content-type | tr -d '\r\n' | curltest 'content-type: application/json'
|
||||
|
||||
echo "Test rewrite for /sv/resources/pools.json with no header and no cookie"
|
||||
curl -s -i ${URL_BASE}/sv/resources/pools.json | grep -i content-type | tr -d '\r\n' | curltest 'content-type: application/json'
|
||||
|
||||
echo "Test rewrite for /resources/pools.json with 'ja' lang header and no cookie"
|
||||
curl -s -i -H 'Accept-Language: ja' ${URL_BASE}/resources/pools.json | grep -i content-type | tr -d '\r\n' | curltest 'content-type: application/json'
|
||||
|
||||
echo "Test rewrite for /ja/resources/pools.json with 'ja' lang header and no cookie"
|
||||
curl -s -i -H 'Accept-Language: ja' ${URL_BASE}/ja/resources/pools.json | grep -i content-type | tr -d '\r\n' | curltest 'content-type: application/json'
|
||||
|
||||
#curl -s -i -H 'Accept-Language: sv' ${URL_BASE}/ja/resources/pools.json | grep -i content-type
|
||||
#curl -s -i -H 'Accept-Language: foo' --cookie 'lang=sv' ${URL_BASE}/ja/resources/pools.json | grep -i content-type
|
||||
#curl -s -i -H 'Accept-Language: foo' --cookie 'lang=sv' ${URL_BASE}/sv/resources/pools.json | grep -i content-type
|
Loading…
x
Reference in New Issue
Block a user