Compare commits

...

38 Commits

Author SHA1 Message Date
wiz
5d360d4156 Merge pull request #1003 from mempool/simon/database-migration-feature
Automated database creation and migration
2021-12-23 21:50:10 +00:00
softsimon
91e30fbc3c Merge branch 'master' into simon/database-migration-feature
# Conflicts:
#	backend/src/index.ts
2021-12-24 00:26:33 +04:00
wiz
5b22e2a000 Merge pull request #1010 from mempool/simon/liquid-icons-api
Liquid icons api
2021-12-23 12:28:54 +00:00
softsimon
533653e54a Change Asset Icon API example to only show HTML 2021-12-23 15:35:17 +04:00
wiz
3dc0dc13ad Merge pull request #1038 from nymkappa/feature/increase-resolution-24h
Switch the 24h chart to 1 min data ticks
2021-12-22 18:33:32 +00:00
softsimon
e332789afc Bumping mempool.js to 2.3.0-dev1 and removing unused tsc build step 2021-12-22 19:33:10 +04:00
nymkappa
e4a9fd06b4 Switch the 24h chart to 1 min data ticks 2021-12-22 23:01:32 +09:00
softsimon
5845f2380e Adding sync external assets feature. 2021-12-21 02:00:50 +04:00
softsimon
c29311d831 Upgrading mempool-js with separated Liquid and Bisq 2021-12-20 23:48:26 +04:00
softsimon
252db109bc Adding icons.json to .gitignore 2021-12-20 04:01:40 +04:00
softsimon
b1c9334119 Changing API path and updating API Docs for asset icons. 2021-12-20 04:01:04 +04:00
wiz
ab04247726 Merge pull request #1033 from mempool/simon/extract-i18n
Extracting i18n string
2021-12-19 19:06:11 +00:00
softsimon
e94a85b989 Extracting i18n string 2021-12-19 22:57:31 +04:00
softsimon
a4569788f8 Liquid icons api 2021-12-19 22:09:49 +04:00
wiz
b455814e90 Merge pull request #1027 from hunicus/change-docs-layout
Revamp docs layout
2021-12-19 17:48:26 +00:00
softsimon
7afd0f3fe7 Merge pull request #1032 from mempool/wiz/update-about-page-links
Update links on About page
2021-12-19 21:36:10 +04:00
hunicus
a2a85469cf Streamline api-docs-navs conditionals 2021-12-19 12:29:52 -05:00
wiz
94488a6029 Merge pull request #1031 from mempool/wiz/update-issue-templates
Update GitHub issue templates to redirect support requests to chat
2021-12-19 17:02:57 +00:00
wiz
8e4829146a Rename About page section: Project Staff -> Project Members 2021-12-20 01:54:06 +09:00
wiz
08f185525c Update About page chat links: replace telegram with matrix 2021-12-20 01:52:58 +09:00
wiz
d6b00fe39e Update GitHub issue templates to redirect support requests to chat 2021-12-20 01:31:29 +09:00
softsimon
cec3baeaa4 Merge pull request #1024 from nymkappa/feature/blocks-mouse-scroll
User can drag the blockchain blocks horizontally with the mouse
2021-12-19 12:59:04 +04:00
nymkappa
6e59733cac User can drag the blockchain blocks horizontally with the mouse 2021-12-19 15:20:21 +09:00
hunicus
c5b705ede7 Adjust bisq cypress tests 2021-12-17 16:22:16 -05:00
hunicus
2819e24efe Remove unnecessary file change 2021-12-17 15:08:21 -05:00
hunicus
5f9bc4497a Customize mobile nav button appearance point
Since there are different numbers of topics across
bitcoin, liquid, bisq, faq, etc.
2021-12-17 14:42:21 -05:00
hunicus
086b14e816 Add various ux improvements for mobile doc menu 2021-12-17 11:42:54 -05:00
hunicus
958bfe6d25 Separate docs-nav into new component
Since same markup will be needed for desktop
and mobile menus.
2021-12-16 19:11:54 -05:00
hunicus
e01ab449cf Add skeleton for mobile docs nav 2021-12-16 18:44:39 -05:00
hunicus
9a18019d9d Add :before element for space before anchors
@angular-router's `scrollOffset` property seems to be
global, so it might mess up something else, and it also
wasn't taking effect when navigating directly to an
anchor anyway (i.e. from browser's address bar).
2021-12-16 13:20:30 -05:00
hunicus
5d8c970351 Update anchor links and add on-page links 2021-12-16 11:30:03 -05:00
hunicus
89fede9e48 Fix inconsistencies in api-docs markup 2021-12-16 09:54:07 -05:00
hunicus
f8a54784d0 Improve styling + switch section headings for tags 2021-12-16 09:54:07 -05:00
hunicus
010381aac4 Convert accordions to plain html 2021-12-16 08:46:51 -05:00
hunicus
1a8fd23b05 Add links and styling to fixed desktop docs nav 2021-12-16 00:04:47 -05:00
hunicus
3ae46e6ba1 Make desktop docs-nav sticky on scroll 2021-12-15 22:57:10 -05:00
hunicus
40f1949654 Create skeleton layout for desktop docs nav
This includes switching to a 2-column layout
(1 for nav and 1 for content) and moving footer
links to docs component so floats can be cleared
properly.
2021-12-15 13:17:37 -05:00
softsimon
2281116504 Automated database creation and migration
fixes #1002
2021-12-13 11:32:04 +04:00
38 changed files with 1931 additions and 1544 deletions

View File

@@ -1,7 +1,14 @@
---
name: 🐛 Bug Report
about: Report bugs or other issues to us on GitHub
---
<!-- <!--
SUPPORT REQUESTS: This is for reporting bugs in Mempool. SUPPORT REQUESTS:
If you have a support request, please join our Keybase group: This is for reporting bugs in Mempool, not for support requests.
If you have a support request, please join our Keybase or Matrix:
https://keybase.io/team/mempool https://keybase.io/team/mempool
https://matrix.to/#/#mempool:bitcoin.kyoto
--> -->
### Description ### Description
@@ -14,11 +21,11 @@
### Steps to reproduce ### Steps to reproduce
<!--if you can reliably reproduce the bug, list the steps here --> <!-- if you can reliably reproduce the bug, list the steps here -->
### Expected behaviour ### Expected behaviour
<!--description of the expected behavior --> <!-- description of the expected behavior -->
### Actual behaviour ### Actual behaviour
@@ -26,7 +33,7 @@
### Screenshots ### Screenshots
<!--Screenshots if gui related, drag and drop to add to the issue --> <!-- Screenshots if gui related, drag and drop to add to the issue -->
#### Device or machine #### Device or machine

View File

@@ -0,0 +1,28 @@
---
name: ✨ Feature Request
about: Request a feature or suggest other enhancements 💡
---
<!--
SUPPORT REQUESTS:
This is for requesting features in Mempool, not for support requests.
If you have a support request, please join our Keybase or Matrix:
https://keybase.io/team/mempool
https://matrix.to/#/#mempool:bitcoin.kyoto
-->
### Description
<!-- brief description of the feature request -->
### Problem to be solved
<!-- description of the the problem you're having -->
### Proposed solution
<!-- explain how you think we should solve the problem -->
#### Additional info
<!-- Additional information useful for implementing (e.g. docs, links, etc.) -->

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: 💬 Need help? Chat with us on Matrix
url: https://matrix.to/#/#mempool:bitcoin.kyoto
about: For support requests or general questions
- name: 💬 Need help? Chat with us on Keybase
url: https://keybase.io/team/mempool
about: For support requests or general questions

7
backend/.gitignore vendored
View File

@@ -1,7 +1,10 @@
# See http://help.github.com/ignore-files/ for more about ignoring files. # See http://help.github.com/ignore-files/ for more about ignoring files.
# production config # production config and external assets
mempool-config.json *.json
!mempool-config.sample.json
icons.json
# compiled output # compiled output
/dist /dist

View File

@@ -13,7 +13,8 @@
"INITIAL_BLOCKS_AMOUNT": 8, "INITIAL_BLOCKS_AMOUNT": 8,
"MEMPOOL_BLOCKS_AMOUNT": 8, "MEMPOOL_BLOCKS_AMOUNT": 8,
"PRICE_FEED_UPDATE_INTERVAL": 3600, "PRICE_FEED_UPDATE_INTERVAL": 3600,
"USE_SECOND_NODE_FOR_MINFEE": false "USE_SECOND_NODE_FOR_MINFEE": false,
"EXTERNAL_ASSETS": []
}, },
"CORE_RPC": { "CORE_RPC": {
"HOST": "127.0.0.1", "HOST": "127.0.0.1",

View File

@@ -0,0 +1,178 @@
import config from '../config';
import { DB } from '../database';
import logger from '../logger';
class DatabaseMigration {
private static currentVersion = 1;
private queryTimeout = 120000;
constructor() { }
public async $initializeOrMigrateDatabase(): Promise<void> {
if (!await this.$checkIfTableExists('statistics')) {
await this.$initializeDatabaseTables();
}
if (await this.$checkIfTableExists('state')) {
const databaseSchemaVersion = await this.$getSchemaVersionFromDatabase();
if (DatabaseMigration.currentVersion > databaseSchemaVersion) {
await this.$migrateTableSchemaFromVersion(databaseSchemaVersion);
}
} else {
await this.$migrateTableSchemaFromVersion(0);
}
}
private async $initializeDatabaseTables(): Promise<void> {
const connection = await DB.pool.getConnection();
for (const query of this.getInitializeTableQueries()) {
await connection.query<any>({ sql: query, timeout: this.queryTimeout });
}
connection.release();
logger.info(`Initial database tables have been created`);
}
private async $migrateTableSchemaFromVersion(version: number): Promise<void> {
const connection = await DB.pool.getConnection();
for (const query of this.getMigrationQueriesFromVersion(version)) {
await connection.query<any>({ sql: query, timeout: this.queryTimeout });
}
connection.release();
await this.$updateToLatestSchemaVersion();
logger.info(`Database schema have been migrated from version ${version} to ${DatabaseMigration.currentVersion} (latest version)`);
}
private async $getSchemaVersionFromDatabase(): Promise<number> {
const connection = await DB.pool.getConnection();
const query = `SELECT number FROM state WHERE name = 'schema_version';`;
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
connection.release();
return rows[0]['number'];
}
private async $updateToLatestSchemaVersion(): Promise<void> {
const connection = await DB.pool.getConnection();
const query = `UPDATE state SET number = ${DatabaseMigration.currentVersion} WHERE name = 'schema_version'`;
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
connection.release();
}
private async $checkIfTableExists(table: string): Promise<boolean> {
const connection = await DB.pool.getConnection();
const query = `SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '${config.DATABASE.DATABASE}' AND TABLE_NAME = '${table}'`;
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
connection.release();
return rows[0]['COUNT(*)'] === 1;
}
private getInitializeTableQueries(): string[] {
const queries: string[] = [];
queries.push(`CREATE TABLE IF NOT EXISTS statistics (
id int(11) NOT NULL,
added datetime NOT NULL,
unconfirmed_transactions int(11) UNSIGNED NOT NULL,
tx_per_second float UNSIGNED NOT NULL,
vbytes_per_second int(10) UNSIGNED NOT NULL,
mempool_byte_weight int(10) UNSIGNED NOT NULL,
fee_data longtext NOT NULL,
total_fee double UNSIGNED NOT NULL,
vsize_1 int(11) NOT NULL,
vsize_2 int(11) NOT NULL,
vsize_3 int(11) NOT NULL,
vsize_4 int(11) NOT NULL,
vsize_5 int(11) NOT NULL,
vsize_6 int(11) NOT NULL,
vsize_8 int(11) NOT NULL,
vsize_10 int(11) NOT NULL,
vsize_12 int(11) NOT NULL,
vsize_15 int(11) NOT NULL,
vsize_20 int(11) NOT NULL,
vsize_30 int(11) NOT NULL,
vsize_40 int(11) NOT NULL,
vsize_50 int(11) NOT NULL,
vsize_60 int(11) NOT NULL,
vsize_70 int(11) NOT NULL,
vsize_80 int(11) NOT NULL,
vsize_90 int(11) NOT NULL,
vsize_100 int(11) NOT NULL,
vsize_125 int(11) NOT NULL,
vsize_150 int(11) NOT NULL,
vsize_175 int(11) NOT NULL,
vsize_200 int(11) NOT NULL,
vsize_250 int(11) NOT NULL,
vsize_300 int(11) NOT NULL,
vsize_350 int(11) NOT NULL,
vsize_400 int(11) NOT NULL,
vsize_500 int(11) NOT NULL,
vsize_600 int(11) NOT NULL,
vsize_700 int(11) NOT NULL,
vsize_800 int(11) NOT NULL,
vsize_900 int(11) NOT NULL,
vsize_1000 int(11) NOT NULL,
vsize_1200 int(11) NOT NULL,
vsize_1400 int(11) NOT NULL,
vsize_1600 int(11) NOT NULL,
vsize_1800 int(11) NOT NULL,
vsize_2000 int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;`);
queries.push(`ALTER TABLE statistics ADD PRIMARY KEY (id);`);
queries.push(`ALTER TABLE statistics MODIFY id int(11) NOT NULL AUTO_INCREMENT;`);
return queries;
}
private getMigrationQueriesFromVersion(version: number): string[] {
const queries: string[] = [];
if (version < 1) {
if (config.MEMPOOL.NETWORK !== 'liquid') {
queries.push(`UPDATE statistics SET
vsize_1 = vsize_1 + vsize_2, vsize_2 = vsize_3,
vsize_3 = vsize_4, vsize_4 = vsize_5,
vsize_5 = vsize_6, vsize_6 = vsize_8,
vsize_8 = vsize_10, vsize_10 = vsize_12,
vsize_12 = vsize_15, vsize_15 = vsize_20,
vsize_20 = vsize_30, vsize_30 = vsize_40,
vsize_40 = vsize_50, vsize_50 = vsize_60,
vsize_60 = vsize_70, vsize_70 = vsize_80,
vsize_80 = vsize_90, vsize_90 = vsize_100,
vsize_100 = vsize_125, vsize_125 = vsize_150,
vsize_150 = vsize_175, vsize_175 = vsize_200,
vsize_200 = vsize_250, vsize_250 = vsize_300,
vsize_300 = vsize_350, vsize_350 = vsize_400,
vsize_400 = vsize_500, vsize_500 = vsize_600,
vsize_600 = vsize_700, vsize_700 = vsize_800,
vsize_800 = vsize_900, vsize_900 = vsize_1000,
vsize_1000 = vsize_1200, vsize_1200 = vsize_1400,
vsize_1400 = vsize_1800, vsize_1800 = vsize_2000, vsize_2000 = 0`);
}
queries.push(`CREATE TABLE IF NOT EXISTS elements_pegs (
block int(11) NOT NULL,
datetime int(11) NOT NULL,
amount bigint(20) NOT NULL,
txid varchar(65) NOT NULL,
txindex int(11) NOT NULL,
bitcoinaddress varchar(100) NOT NULL,
bitcointxid varchar(65) NOT NULL,
bitcoinindex int(11) NOT NULL,
final_tx int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;`);
queries.push(`CREATE TABLE IF NOT EXISTS state (
name varchar(25) NOT NULL,
number int(11) NULL,
string varchar(100) NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;`);
queries.push(`INSERT INTO state VALUES('schema_version', 0, NULL);`);
queries.push(`INSERT INTO state VALUES('last_elements_block', 0, NULL);`);
}
return queries;
}
}
export default new DatabaseMigration();

View File

@@ -18,12 +18,12 @@ class ElementsParser {
this.isRunning = true; this.isRunning = true;
const result = await bitcoinClient.getChainTips(); const result = await bitcoinClient.getChainTips();
const tip = result[0].height; const tip = result[0].height;
const latestBlock = await this.$getLatestBlockFromDatabase(); const latestBlockHeight = await this.$getLatestBlockHeightFromDatabase();
for (let height = latestBlock.block + 1; height <= tip; height++) { for (let height = latestBlockHeight + 1; height <= tip; height++) {
const blockHash: IBitcoinApi.ChainTips = await bitcoinClient.getBlockHash(height); const blockHash: IBitcoinApi.ChainTips = await bitcoinClient.getBlockHash(height);
const block: IBitcoinApi.Block = await bitcoinClient.getBlock(blockHash, 2); const block: IBitcoinApi.Block = await bitcoinClient.getBlock(blockHash, 2);
await this.$parseBlock(block); await this.$parseBlock(block);
await this.$saveLatestBlockToDatabase(block.height, block.time, block.hash); await this.$saveLatestBlockToDatabase(block.height);
} }
this.isRunning = false; this.isRunning = false;
} catch (e) { } catch (e) {
@@ -92,18 +92,18 @@ class ElementsParser {
logger.debug(`Saved L-BTC peg from block height #${height} with TXID ${txid}.`); logger.debug(`Saved L-BTC peg from block height #${height} with TXID ${txid}.`);
} }
protected async $getLatestBlockFromDatabase(): Promise<any> { protected async $getLatestBlockHeightFromDatabase(): Promise<number> {
const connection = await DB.pool.getConnection(); const connection = await DB.pool.getConnection();
const query = `SELECT block, datetime, block_hash FROM last_elements_block`; const query = `SELECT number FROM state WHERE name = 'last_elements_block'`;
const [rows] = await connection.query<any>(query); const [rows] = await connection.query<any>(query);
connection.release(); connection.release();
return rows[0]; return rows[0]['number'];
} }
protected async $saveLatestBlockToDatabase(blockHeight: number, datetime: number, blockHash: string) { protected async $saveLatestBlockToDatabase(blockHeight: number) {
const connection = await DB.pool.getConnection(); const connection = await DB.pool.getConnection();
const query = `UPDATE last_elements_block SET block = ?, datetime = ?, block_hash = ?`; const query = `UPDATE state SET number = ? WHERE name = 'last_elements_block'`;
await connection.query<any>(query, [blockHeight, datetime, blockHash]); await connection.query<any>(query, [blockHeight]);
connection.release(); connection.release();
} }
} }

View File

@@ -0,0 +1,39 @@
import * as fs from 'fs';
import config from '../../config';
import logger from '../../logger';
class Icons {
private static FILE_NAME = './icons.json';
private iconIds: string[] = [];
private icons: { [assetId: string]: string; } = {};
constructor() {}
public loadIcons() {
if (!fs.existsSync(Icons.FILE_NAME)) {
logger.warn(`${Icons.FILE_NAME} does not exist. No Liquid icons loaded.`);
return;
}
const cacheData = fs.readFileSync(Icons.FILE_NAME, 'utf8');
this.icons = JSON.parse(cacheData);
for (const i in this.icons) {
this.iconIds.push(i);
}
logger.debug(`Liquid icons has been loaded.`);
}
public getIconByAssetId(assetId: string): Buffer | undefined {
const icon = this.icons[assetId];
if (icon) {
return Buffer.from(icon, 'base64');
}
}
public getAllIconIds() {
return this.iconIds;
}
}
export default new Icons();

View File

@@ -394,7 +394,7 @@ class Statistics {
public async $list24H(): Promise<OptimizedStatistic[]> { public async $list24H(): Promise<OptimizedStatistic[]> {
try { try {
const connection = await DB.pool.getConnection(); const connection = await DB.pool.getConnection();
const query = this.getQueryForDaysAvg(120, '1 DAY'); // 2m interval const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics ORDER BY id DESC LIMIT 1440`;
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout }); const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
connection.release(); connection.release();
return this.mapStatisticToOptimizedStatistic(rows); return this.mapStatisticToOptimizedStatistic(rows);

View File

@@ -16,6 +16,7 @@ interface IConfig {
MEMPOOL_BLOCKS_AMOUNT: number; MEMPOOL_BLOCKS_AMOUNT: number;
PRICE_FEED_UPDATE_INTERVAL: number; PRICE_FEED_UPDATE_INTERVAL: number;
USE_SECOND_NODE_FOR_MINFEE: boolean; USE_SECOND_NODE_FOR_MINFEE: boolean;
EXTERNAL_ASSETS: string[];
}; };
ESPLORA: { ESPLORA: {
REST_API_URL: string; REST_API_URL: string;
@@ -78,6 +79,7 @@ const defaults: IConfig = {
'MEMPOOL_BLOCKS_AMOUNT': 8, 'MEMPOOL_BLOCKS_AMOUNT': 8,
'PRICE_FEED_UPDATE_INTERVAL': 3600, 'PRICE_FEED_UPDATE_INTERVAL': 3600,
'USE_SECOND_NODE_FOR_MINFEE': false, 'USE_SECOND_NODE_FOR_MINFEE': false,
'EXTERNAL_ASSETS': [],
}, },
'ESPLORA': { 'ESPLORA': {
'REST_API_URL': 'http://127.0.0.1:3000', 'REST_API_URL': 'http://127.0.0.1:3000',

View File

@@ -21,6 +21,9 @@ import backendInfo from './api/backend-info';
import loadingIndicators from './api/loading-indicators'; import loadingIndicators from './api/loading-indicators';
import mempool from './api/mempool'; import mempool from './api/mempool';
import elementsParser from './api/liquid/elements-parser'; import elementsParser from './api/liquid/elements-parser';
import databaseMigration from './api/database-migration';
import syncAssets from './sync-assets';
import icons from './api/liquid/icons';
class Server { class Server {
private wss: WebSocket.Server | undefined; private wss: WebSocket.Server | undefined;
@@ -77,16 +80,26 @@ class Server {
this.setUpWebsocketHandling(); this.setUpWebsocketHandling();
await syncAssets.syncAssets();
diskCache.loadMempoolCache(); diskCache.loadMempoolCache();
if (config.DATABASE.ENABLED) { if (config.DATABASE.ENABLED) {
await checkDbConnection(); await checkDbConnection();
try {
await databaseMigration.$initializeOrMigrateDatabase();
} catch (e) {
throw new Error(e instanceof Error ? e.message : 'Error');
}
} }
if (config.STATISTICS.ENABLED && config.DATABASE.ENABLED && cluster.isMaster) { if (config.STATISTICS.ENABLED && config.DATABASE.ENABLED && cluster.isMaster) {
statistics.startStatistics(); statistics.startStatistics();
} }
if (config.MEMPOOL.NETWORK === 'liquid') {
icons.loadIcons();
}
fiatConversion.startService(); fiatConversion.startService();
this.setUpHttpApiRoutes(); this.setUpHttpApiRoutes();
@@ -270,6 +283,13 @@ class Server {
; ;
} }
if (config.MEMPOOL.NETWORK === 'liquid') {
this.app
.get(config.MEMPOOL.API_URL_PREFIX + 'assets/icons', routes.getAllLiquidIcon)
.get(config.MEMPOOL.API_URL_PREFIX + 'asset/:assetId/icon', routes.getLiquidIcon)
;
}
if (config.MEMPOOL.NETWORK === 'liquid' && config.DATABASE.ENABLED) { if (config.MEMPOOL.NETWORK === 'liquid' && config.DATABASE.ENABLED) {
this.app this.app
.get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegs/month', routes.$getElementsPegsByMonth) .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegs/month', routes.$getElementsPegsByMonth)

View File

@@ -19,6 +19,7 @@ import loadingIndicators from './api/loading-indicators';
import { Common } from './api/common'; import { Common } from './api/common';
import bitcoinClient from './api/bitcoin/bitcoin-client'; import bitcoinClient from './api/bitcoin/bitcoin-client';
import elementsParser from './api/liquid/elements-parser'; import elementsParser from './api/liquid/elements-parser';
import icons from './api/liquid/icons';
class Routes { class Routes {
constructor() {} constructor() {}
@@ -807,6 +808,26 @@ class Routes {
: (e.message || 'Error')); : (e.message || 'Error'));
} }
} }
public getLiquidIcon(req: Request, res: Response) {
const result = icons.getIconByAssetId(req.params.assetId);
if (result) {
res.setHeader('content-type', 'image/png');
res.setHeader('content-length', result.length);
res.send(result);
} else {
res.status(404).send('Asset icon not found');
}
}
public getAllLiquidIcon(req: Request, res: Response) {
const result = icons.getAllIconIds();
if (result) {
res.json(result);
} else {
res.status(404).send('Asset icons not found');
}
}
} }
export default new Routes(); export default new Routes();

View File

@@ -0,0 +1,32 @@
import axios from 'axios';
import * as fs from 'fs';
const fsPromises = fs.promises;
import config from './config';
import logger from './logger';
const PATH = './';
class SyncAssets {
constructor() { }
public async syncAssets() {
for (const url of config.MEMPOOL.EXTERNAL_ASSETS) {
await this.downloadFile(url);
}
}
private async downloadFile(url: string) {
const fileName = url.split('/').slice(-1)[0];
logger.info(`Downloading external asset: ${fileName}...`);
try {
const response = await axios.get(url, {
responseType: 'stream', timeout: 30000
});
await fsPromises.writeFile(PATH + fileName, response.data);
} catch (e: any) {
throw new Error(`Failed to download external asset. ` + e);
}
}
}
export default new SyncAssets();

View File

@@ -59,9 +59,8 @@ describe('Bisq', () => {
cy.visit(`${basePath}`); cy.visit(`${basePath}`);
cy.waitForSkeletonGone(); cy.waitForSkeletonGone();
cy.get('li:nth-of-type(5) > a').click().then(() => { cy.get('li:nth-of-type(5) > a').click().then(() => {
cy.get('.card').should('have.length.at.least', 1); cy.get('.section-header').should('have.length.at.least', 1);
cy.get('.card').first().click(); cy.get('.endpoint-container').should('have.length.at.least', 1);
cy.get('.card-body');
}); });
}); });

View File

@@ -26,7 +26,7 @@
"@fortawesome/fontawesome-svg-core": "^1.2.35", "@fortawesome/fontawesome-svg-core": "^1.2.35",
"@fortawesome/free-solid-svg-icons": "^5.15.3", "@fortawesome/free-solid-svg-icons": "^5.15.3",
"@juggle/resize-observer": "^3.3.1", "@juggle/resize-observer": "^3.3.1",
"@mempool/mempool.js": "^2.2.4", "@mempool/mempool.js": "2.3.0-dev1",
"@ng-bootstrap/ng-bootstrap": "^10.0.0", "@ng-bootstrap/ng-bootstrap": "^10.0.0",
"@nguniversal/express-engine": "11.2.1", "@nguniversal/express-engine": "11.2.1",
"@types/qrcode": "1.4.1", "@types/qrcode": "1.4.1",
@@ -3230,12 +3230,40 @@
"integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw==" "integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw=="
}, },
"node_modules/@mempool/mempool.js": { "node_modules/@mempool/mempool.js": {
"version": "2.2.4", "version": "2.3.0-dev1",
"resolved": "https://registry.npmjs.org/@mempool/mempool.js/-/mempool.js-2.2.4.tgz", "resolved": "https://registry.npmjs.org/@mempool/mempool.js/-/mempool.js-2.3.0-dev1.tgz",
"integrity": "sha512-G9Ga2jHLfAuU/qXikRBtTecYr7BhLJH1WbIahefnGpgP48DCQaj1jizvuRZHhoElUvUT5flRt/O9kLjlbToqhw==", "integrity": "sha512-+UYGuG8qqdgrtC4J94hCs7+Dry8OprIixEarIC6rww1Nb5POz8n3NTDH8to1r0XLjPr+Du6/OX8fEN1QW94rNA==",
"dependencies": { "dependencies": {
"axios": "^0.21.1", "axios": "0.24.0",
"ws": "^7.4.3" "ws": "8.3.0"
}
},
"node_modules/@mempool/mempool.js/node_modules/axios": {
"version": "0.24.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
"dependencies": {
"follow-redirects": "^1.14.4"
}
},
"node_modules/@mempool/mempool.js/node_modules/ws": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.3.0.tgz",
"integrity": "sha512-Gs5EZtpqZzLvmIM59w4igITU57lrtYVFneaa434VROv4thzJyV6UjIL3D42lslWlI+D4KzLYnxSwtfuiO79sNw==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
} }
}, },
"node_modules/@ng-bootstrap/ng-bootstrap": { "node_modules/@ng-bootstrap/ng-bootstrap": {
@@ -4541,6 +4569,7 @@
"version": "0.21.4", "version": "0.21.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
"devOptional": true,
"dependencies": { "dependencies": {
"follow-redirects": "^1.14.0" "follow-redirects": "^1.14.0"
} }
@@ -7264,7 +7293,7 @@
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"devOptional": true, "dev": true,
"engines": { "engines": {
"node": ">=0.3.1" "node": ">=0.3.1"
} }
@@ -9122,7 +9151,7 @@
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
"devOptional": true, "dev": true,
"engines": { "engines": {
"node": ">=4" "node": ">=4"
} }
@@ -9131,7 +9160,7 @@
"version": "5.1.5", "version": "5.1.5",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
"devOptional": true, "dev": true,
"dependencies": { "dependencies": {
"ajv": "^6.12.3", "ajv": "^6.12.3",
"har-schema": "^2.0.0" "har-schema": "^2.0.0"
@@ -9416,7 +9445,7 @@
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
"devOptional": true, "dev": true,
"dependencies": { "dependencies": {
"assert-plus": "^1.0.0", "assert-plus": "^1.0.0",
"jsprim": "^1.2.2", "jsprim": "^1.2.2",
@@ -10385,7 +10414,7 @@
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
"devOptional": true "dev": true
}, },
"node_modules/json-schema-traverse": { "node_modules/json-schema-traverse": {
"version": "0.4.1", "version": "0.4.1",
@@ -10453,7 +10482,7 @@
"version": "1.4.2", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
"integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
"devOptional": true, "dev": true,
"dependencies": { "dependencies": {
"assert-plus": "1.0.0", "assert-plus": "1.0.0",
"extsprintf": "1.3.0", "extsprintf": "1.3.0",
@@ -17962,6 +17991,7 @@
"version": "7.4.6", "version": "7.4.6",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
"devOptional": true,
"engines": { "engines": {
"node": ">=8.3.0" "node": ">=8.3.0"
} }
@@ -20409,12 +20439,28 @@
"integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw==" "integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw=="
}, },
"@mempool/mempool.js": { "@mempool/mempool.js": {
"version": "2.2.4", "version": "2.3.0-dev1",
"resolved": "https://registry.npmjs.org/@mempool/mempool.js/-/mempool.js-2.2.4.tgz", "resolved": "https://registry.npmjs.org/@mempool/mempool.js/-/mempool.js-2.3.0-dev1.tgz",
"integrity": "sha512-G9Ga2jHLfAuU/qXikRBtTecYr7BhLJH1WbIahefnGpgP48DCQaj1jizvuRZHhoElUvUT5flRt/O9kLjlbToqhw==", "integrity": "sha512-+UYGuG8qqdgrtC4J94hCs7+Dry8OprIixEarIC6rww1Nb5POz8n3NTDH8to1r0XLjPr+Du6/OX8fEN1QW94rNA==",
"requires": { "requires": {
"axios": "^0.21.1", "axios": "0.24.0",
"ws": "^7.4.3" "ws": "8.3.0"
},
"dependencies": {
"axios": {
"version": "0.24.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
"requires": {
"follow-redirects": "^1.14.4"
}
},
"ws": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.3.0.tgz",
"integrity": "sha512-Gs5EZtpqZzLvmIM59w4igITU57lrtYVFneaa434VROv4thzJyV6UjIL3D42lslWlI+D4KzLYnxSwtfuiO79sNw==",
"requires": {}
}
} }
}, },
"@ng-bootstrap/ng-bootstrap": { "@ng-bootstrap/ng-bootstrap": {
@@ -21532,6 +21578,7 @@
"version": "0.21.4", "version": "0.21.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
"devOptional": true,
"requires": { "requires": {
"follow-redirects": "^1.14.0" "follow-redirects": "^1.14.0"
} }
@@ -23795,7 +23842,7 @@
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"devOptional": true "dev": true
}, },
"diffie-hellman": { "diffie-hellman": {
"version": "5.0.3", "version": "5.0.3",
@@ -25290,13 +25337,13 @@
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
"devOptional": true "dev": true
}, },
"har-validator": { "har-validator": {
"version": "5.1.5", "version": "5.1.5",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
"devOptional": true, "dev": true,
"requires": { "requires": {
"ajv": "^6.12.3", "ajv": "^6.12.3",
"har-schema": "^2.0.0" "har-schema": "^2.0.0"
@@ -25544,7 +25591,7 @@
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
"devOptional": true, "dev": true,
"requires": { "requires": {
"assert-plus": "^1.0.0", "assert-plus": "^1.0.0",
"jsprim": "^1.2.2", "jsprim": "^1.2.2",
@@ -26286,7 +26333,7 @@
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
"devOptional": true "dev": true
}, },
"json-schema-traverse": { "json-schema-traverse": {
"version": "0.4.1", "version": "0.4.1",
@@ -26339,7 +26386,7 @@
"version": "1.4.2", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
"integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
"devOptional": true, "dev": true,
"requires": { "requires": {
"assert-plus": "1.0.0", "assert-plus": "1.0.0",
"extsprintf": "1.3.0", "extsprintf": "1.3.0",
@@ -32158,7 +32205,8 @@
"ws": { "ws": {
"version": "7.4.6", "version": "7.4.6",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
"devOptional": true
}, },
"xhr2": { "xhr2": {
"version": "0.2.0", "version": "0.2.0",

View File

@@ -34,7 +34,10 @@
"sync-assets": "node sync-assets.js && rsync -av ./dist/mempool/browser/en-US/resources ./dist/mempool/browser/resources", "sync-assets": "node sync-assets.js && rsync -av ./dist/mempool/browser/en-US/resources ./dist/mempool/browser/resources",
"sync-assets-dev": "node sync-assets.js dev", "sync-assets-dev": "node sync-assets.js dev",
"generate-config": "node generate-config.js", "generate-config": "node generate-config.js",
"build-mempool.js": "tsc | browserify -p tinyify ./node_modules/@mempool/mempool.js/lib/index.js --standalone mempoolJS > ./dist/mempool/browser/en-US/mempool.js", "build-mempool.js": "npm run build-mempool-js && npm run build-mempool-liquid-js && npm run build-mempool-bisq-js",
"build-mempool-js": "browserify -p tinyify ./node_modules/@mempool/mempool.js/lib/index.js --standalone mempoolJS > ./dist/mempool/browser/en-US/mempool.js",
"build-mempool-bisq-js": "browserify -p tinyify ./node_modules/@mempool/mempool.js/lib/index-bisq.js --standalone bisqJS > ./dist/mempool/browser/en-US/bisq.js",
"build-mempool-liquid-js": "browserify -p tinyify ./node_modules/@mempool/mempool.js/lib/index-liquid.js --standalone liquidJS > ./dist/mempool/browser/en-US/liquid.js",
"test": "ng test", "test": "ng test",
"lint": "ng lint", "lint": "ng lint",
"e2e": "npm run generate-config && ng e2e", "e2e": "npm run generate-config && ng e2e",
@@ -70,7 +73,7 @@
"@fortawesome/fontawesome-svg-core": "^1.2.35", "@fortawesome/fontawesome-svg-core": "^1.2.35",
"@fortawesome/free-solid-svg-icons": "^5.15.3", "@fortawesome/free-solid-svg-icons": "^5.15.3",
"@juggle/resize-observer": "^3.3.1", "@juggle/resize-observer": "^3.3.1",
"@mempool/mempool.js": "^2.2.4", "@mempool/mempool.js": "2.3.0-dev1",
"@ng-bootstrap/ng-bootstrap": "^10.0.0", "@ng-bootstrap/ng-bootstrap": "^10.0.0",
"@nguniversal/express-engine": "11.2.1", "@nguniversal/express-engine": "11.2.1",
"@types/qrcode": "1.4.1", "@types/qrcode": "1.4.1",

View File

@@ -48,9 +48,10 @@ import { FeesBoxComponent } from './components/fees-box/fees-box.component';
import { DashboardComponent } from './dashboard/dashboard.component'; import { DashboardComponent } from './dashboard/dashboard.component';
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome'; import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, faChartArea, faCogs, faCubes, faDatabase, faExchangeAlt, faInfoCircle, import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, faChartArea, faCogs, faCubes, faDatabase, faExchangeAlt, faInfoCircle,
faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown, faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook } from '@fortawesome/free-solid-svg-icons'; faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown, faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl } from '@fortawesome/free-solid-svg-icons';
import { ApiDocsComponent } from './components/docs/api-docs.component'; import { ApiDocsComponent } from './components/docs/api-docs.component';
import { DocsComponent } from './components/docs/docs.component'; import { DocsComponent } from './components/docs/docs.component';
import { ApiDocsNavComponent } from './components/docs/api-docs-nav.component';
import { CodeTemplateComponent } from './components/docs/code-template.component'; import { CodeTemplateComponent } from './components/docs/code-template.component';
import { TermsOfServiceComponent } from './components/terms-of-service/terms-of-service.component'; import { TermsOfServiceComponent } from './components/terms-of-service/terms-of-service.component';
import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component'; import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component';
@@ -59,6 +60,7 @@ import { StorageService } from './services/storage.service';
import { HttpCacheInterceptor } from './services/http-cache.interceptor'; import { HttpCacheInterceptor } from './services/http-cache.interceptor';
import { SponsorComponent } from './components/sponsor/sponsor.component'; import { SponsorComponent } from './components/sponsor/sponsor.component';
import { PushTransactionComponent } from './components/push-transaction/push-transaction.component'; import { PushTransactionComponent } from './components/push-transaction/push-transaction.component';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
@NgModule({ @NgModule({
declarations: [ declarations: [
@@ -102,6 +104,7 @@ import { PushTransactionComponent } from './components/push-transaction/push-tra
SponsorComponent, SponsorComponent,
PushTransactionComponent, PushTransactionComponent,
DocsComponent, DocsComponent,
ApiDocsNavComponent,
], ],
imports: [ imports: [
BrowserModule.withServerTransition({ appId: 'serverApp' }), BrowserModule.withServerTransition({ appId: 'serverApp' }),
@@ -111,6 +114,7 @@ import { PushTransactionComponent } from './components/push-transaction/push-tra
BrowserAnimationsModule, BrowserAnimationsModule,
InfiniteScrollModule, InfiniteScrollModule,
NgbTypeaheadModule, NgbTypeaheadModule,
NgbModule,
FontAwesomeModule, FontAwesomeModule,
SharedModule, SharedModule,
NgxEchartsModule.forRoot({ NgxEchartsModule.forRoot({
@@ -161,5 +165,6 @@ export class AppModule {
library.addIcons(faAngleRight); library.addIcons(faAngleRight);
library.addIcons(faAngleLeft); library.addIcons(faAngleLeft);
library.addIcons(faBook); library.addIcons(faBook);
library.addIcons(faListUl);
} }
} }

View File

@@ -23,12 +23,12 @@
<a target="_blank" href="https://twitter.com/mempool"> <a target="_blank" href="https://twitter.com/mempool">
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="twitter" class="svg-inline--fa fa-twitter fa-w-16 fa-4x" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"></path></svg> <svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="twitter" class="svg-inline--fa fa-twitter fa-w-16 fa-4x" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"></path></svg>
</a> </a>
<a target="_blank" href="https://t.me/mempoolspace">
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="telegram-plane" class="svg-inline--fa fa-telegram-plane fa-w-14 fa-4x" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M446.7 98.6l-67.6 318.8c-5.1 22.5-18.4 28.1-37.3 17.5l-103-75.9-49.7 47.8c-5.5 5.5-10.1 10.1-20.7 10.1l7.4-104.9 190.9-172.5c8.3-7.4-1.8-11.5-12.9-4.1L117.8 284 16.2 252.2c-22.1-6.9-22.5-22.1 4.6-32.7L418.2 66.4c18.4-6.9 34.5 4.1 28.5 32.2z"></path></svg>
</a>
<a target="_blank" href="https://keybase.io/team/mempool"> <a target="_blank" href="https://keybase.io/team/mempool">
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="keybase" class="svg-inline--fa fa-keybase fa-w-14 fa-4x" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M286.17 419a18 18 0 1 0 18 18 18 18 0 0 0-18-18zm111.92-147.6c-9.5-14.62-39.37-52.45-87.26-73.71q-9.1-4.06-18.38-7.27a78.43 78.43 0 0 0-47.88-104.13c-12.41-4.1-23.33-6-32.41-5.77-.6-2-1.89-11 9.4-35L198.66 32l-5.48 7.56c-8.69 12.06-16.92 23.55-24.34 34.89a51 51 0 0 0-8.29-1.25c-41.53-2.45-39-2.33-41.06-2.33-50.61 0-50.75 52.12-50.75 45.88l-2.36 36.68c-1.61 27 19.75 50.21 47.63 51.85l8.93.54a214 214 0 0 0-46.29 35.54C14 304.66 14 374 14 429.77v33.64l23.32-29.8a148.6 148.6 0 0 0 14.56 37.56c5.78 10.13 14.87 9.45 19.64 7.33 4.21-1.87 10-6.92 3.75-20.11a178.29 178.29 0 0 1-15.76-53.13l46.82-59.83-24.66 74.11c58.23-42.4 157.38-61.76 236.25-38.59 34.2 10.05 67.45.69 84.74-23.84.72-1 1.2-2.16 1.85-3.22a156.09 156.09 0 0 1 2.8 28.43c0 23.3-3.69 52.93-14.88 81.64-2.52 6.46 1.76 14.5 8.6 15.74 7.42 1.57 15.33-3.1 18.37-11.15C429 443 434 414 434 382.32c0-38.58-13-77.46-35.91-110.92zM142.37 128.58l-15.7-.93-1.39 21.79 13.13.78a93 93 0 0 0 .32 19.57l-22.38-1.34a12.28 12.28 0 0 1-11.76-12.79L107 119c1-12.17 13.87-11.27 13.26-11.32l29.11 1.73a144.35 144.35 0 0 0-7 19.17zm148.42 172.18a10.51 10.51 0 0 1-14.35-1.39l-9.68-11.49-34.42 27a8.09 8.09 0 0 1-11.13-1.08l-15.78-18.64a7.38 7.38 0 0 1 1.34-10.34l34.57-27.18-14.14-16.74-17.09 13.45a7.75 7.75 0 0 1-10.59-1s-3.72-4.42-3.8-4.53a7.38 7.38 0 0 1 1.37-10.34L214 225.19s-18.51-22-18.6-22.14a9.56 9.56 0 0 1 1.74-13.42 10.38 10.38 0 0 1 14.3 1.37l81.09 96.32a9.58 9.58 0 0 1-1.74 13.44zM187.44 419a18 18 0 1 0 18 18 18 18 0 0 0-18-18z"></path></svg> <svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="keybase" class="svg-inline--fa fa-keybase fa-w-14 fa-4x" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M286.17 419a18 18 0 1 0 18 18 18 18 0 0 0-18-18zm111.92-147.6c-9.5-14.62-39.37-52.45-87.26-73.71q-9.1-4.06-18.38-7.27a78.43 78.43 0 0 0-47.88-104.13c-12.41-4.1-23.33-6-32.41-5.77-.6-2-1.89-11 9.4-35L198.66 32l-5.48 7.56c-8.69 12.06-16.92 23.55-24.34 34.89a51 51 0 0 0-8.29-1.25c-41.53-2.45-39-2.33-41.06-2.33-50.61 0-50.75 52.12-50.75 45.88l-2.36 36.68c-1.61 27 19.75 50.21 47.63 51.85l8.93.54a214 214 0 0 0-46.29 35.54C14 304.66 14 374 14 429.77v33.64l23.32-29.8a148.6 148.6 0 0 0 14.56 37.56c5.78 10.13 14.87 9.45 19.64 7.33 4.21-1.87 10-6.92 3.75-20.11a178.29 178.29 0 0 1-15.76-53.13l46.82-59.83-24.66 74.11c58.23-42.4 157.38-61.76 236.25-38.59 34.2 10.05 67.45.69 84.74-23.84.72-1 1.2-2.16 1.85-3.22a156.09 156.09 0 0 1 2.8 28.43c0 23.3-3.69 52.93-14.88 81.64-2.52 6.46 1.76 14.5 8.6 15.74 7.42 1.57 15.33-3.1 18.37-11.15C429 443 434 414 434 382.32c0-38.58-13-77.46-35.91-110.92zM142.37 128.58l-15.7-.93-1.39 21.79 13.13.78a93 93 0 0 0 .32 19.57l-22.38-1.34a12.28 12.28 0 0 1-11.76-12.79L107 119c1-12.17 13.87-11.27 13.26-11.32l29.11 1.73a144.35 144.35 0 0 0-7 19.17zm148.42 172.18a10.51 10.51 0 0 1-14.35-1.39l-9.68-11.49-34.42 27a8.09 8.09 0 0 1-11.13-1.08l-15.78-18.64a7.38 7.38 0 0 1 1.34-10.34l34.57-27.18-14.14-16.74-17.09 13.45a7.75 7.75 0 0 1-10.59-1s-3.72-4.42-3.8-4.53a7.38 7.38 0 0 1 1.37-10.34L214 225.19s-18.51-22-18.6-22.14a9.56 9.56 0 0 1 1.74-13.42 10.38 10.38 0 0 1 14.3 1.37l81.09 96.32a9.58 9.58 0 0 1-1.74 13.44zM187.44 419a18 18 0 1 0 18 18 18 18 0 0 0-18-18z"></path></svg>
</a> </a>
<a target="_blank" href="https://matrix.to/#/#mempool:bitcoin.kyoto">
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="matrix" class="svg-inline--fa fa-matrix fa-w-16 fa-4x" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1536 1792"><path fill="currentColor" d="M40.467 163.152v1465.696H145.92V1664H0V128h145.92v35.152zm450.757 464.64v74.14h2.069c19.79-28.356 43.717-50.215 71.483-65.575 27.765-15.656 59.963-23.336 96-23.336 34.56 0 66.165 6.795 94.818 20.086 28.652 13.293 50.216 37.22 65.28 70.893 16.246-23.926 38.4-45.194 66.166-63.507 27.766-18.314 60.848-27.472 98.954-27.472 28.948 0 55.828 3.545 80.64 10.635 24.812 7.088 45.785 18.314 63.508 33.968 17.722 15.656 31.31 35.742 41.354 60.85 9.747 25.107 14.768 55.236 14.768 90.683v366.573h-150.35V865.28c0-18.314-.59-35.741-2.068-51.987-1.476-16.247-5.316-30.426-11.52-42.24-6.499-12.112-15.656-21.563-28.062-28.653-12.405-7.088-29.242-10.634-50.214-10.634-21.268 0-38.4 4.135-51.397 12.112-12.997 8.27-23.336 18.608-30.72 31.901-7.386 12.997-12.407 27.765-14.77 44.602-2.363 16.542-3.84 33.379-3.84 50.216v305.133H692.971v-307.2c0-16.247-.294-32.197-1.18-48.149-.591-15.95-3.84-30.424-9.157-44.011-5.317-13.293-14.178-24.223-26.585-32.197-12.406-7.976-30.425-12.112-54.646-12.112-7.088 0-16.542 1.478-28.062 4.726-11.52 3.25-23.04 9.157-33.968 18.02-10.93 8.86-20.383 21.563-28.063 38.103-7.68 16.543-11.52 38.4-11.52 65.28v317.834H349.44V627.792zm1004.309 1001.056V163.152H1390.08V128H1536v1536h-145.92v-35.152z"/></svg>
</a>
</div> </div>
<div class="enterprise-sponsor"> <div class="enterprise-sponsor">
@@ -178,7 +178,7 @@
</div> </div>
<div class="maintainers" *ngIf="contributors.core.length"> <div class="maintainers" *ngIf="contributors.core.length">
<h3 i18n="about.project_staff">Project Staff</h3> <h3 i18n="about.project_members">Project Members</h3>
<div class="wrapper"> <div class="wrapper">
<ng-template ngFor let-contributor [ngForOf]="contributors.core"> <ng-template ngFor let-contributor [ngForOf]="contributors.core">
<a [href]="'https://github.com/' + contributor.name" target="_blank" [title]="contributor.name"> <a [href]="'https://github.com/' + contributor.name" target="_blank" [title]="contributor.name">

View File

@@ -1,7 +1,8 @@
<div class="blocks-container blockchain-blocks-container" *ngIf="(loadingBlocks$ | async) === false; else loadingBlocksTemplate"> <div class="blocks-container blockchain-blocks-container" *ngIf="(loadingBlocks$ | async) === false; else loadingBlocksTemplate">
<div *ngFor="let block of blocks; let i = index; trackBy: trackByBlocksFn" > <div *ngFor="let block of blocks; let i = index; trackBy: trackByBlocksFn" >
<div class="text-center bitcoin-block mined-block blockchain-blocks-{{ i }}" id="bitcoin-block-{{ block.height }}" [ngStyle]="blockStyles[i]" [class.blink-bg]="(specialBlocks[block.height] !== undefined)"> <div class="text-center bitcoin-block mined-block blockchain-blocks-{{ i }}" id="bitcoin-block-{{ block.height }}" [ngStyle]="blockStyles[i]" [class.blink-bg]="(specialBlocks[block.height] !== undefined)">
<a [routerLink]="['/block/' | relativeUrl, block.id]" [state]="{ data: { block: block } }" class="blockLink">&nbsp;</a> <a draggable="false" [routerLink]="['/block/' | relativeUrl, block.id]" [state]="{ data: { block: block } }"
class="blockLink" [ngClass]="{'disabled': (this.stateService.blockScrolling$ | async)}">&nbsp;</a>
<div class="block-height"> <div class="block-height">
<a [routerLink]="['/block/' | relativeUrl, block.id]" [state]="{ data: { block: block } }">{{ block.height }}</a> <a [routerLink]="['/block/' | relativeUrl, block.id]" [state]="{ data: { block: block } }">{{ block.height }}</a>
</div> </div>

View File

@@ -15,6 +15,10 @@
text-decoration: none; text-decoration: none;
} }
.blockLink.disabled {
pointer-events: none;
}
.mined-block { .mined-block {
position: absolute; position: absolute;
top: 0px; top: 0px;

View File

@@ -41,7 +41,7 @@ export class BlockchainBlocksComponent implements OnInit, OnDestroy {
}; };
constructor( constructor(
private stateService: StateService, public stateService: StateService,
private router: Router, private router: Router,
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
) { } ) { }

View File

@@ -18,6 +18,11 @@
.blockchain-wrapper { .blockchain-wrapper {
overflow: hidden; overflow: hidden;
height: 250px; height: 250px;
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+/Edge */
user-select: none; /* Standard */
} }
.position-container { .position-container {

View File

@@ -0,0 +1,76 @@
<ng-template [ngIf]="network.val !== 'bisq' && network.val !== 'liquid'">
<p>General</p>
<a [routerLink]="['./']" fragment="get-difficulty-adjustment" (click)="collapseItem.toggle()">GET Difficulty Adjustment</a>
</ng-template>
<ng-template [ngIf]="network.val === 'bisq'">
<p>Markets</p>
<a [routerLink]="['./']" fragment="get-market-currencies" (click)="collapseItem.toggle()">GET Market Currencies</a>
<a [routerLink]="['./']" fragment="get-market-depth" (click)="collapseItem.toggle()">GET Market Depth</a>
<a [routerLink]="['./']" fragment="get-market-hloc" (click)="collapseItem.toggle()">GET Market HLOC</a>
<a [routerLink]="['./']" fragment="get-markets" (click)="collapseItem.toggle()">GET Markets</a>
<a [routerLink]="['./']" fragment="get-market-offers" (click)="collapseItem.toggle()">GET Market Offers</a>
<a [routerLink]="['./']" fragment="get-market-ticker" (click)="collapseItem.toggle()">GET Market Ticker</a>
<a [routerLink]="['./']" fragment="get-market-trades" (click)="collapseItem.toggle()">GET Market Trades</a>
<a [routerLink]="['./']" fragment="get-market-volumes" (click)="collapseItem.toggle()">GET Market Volumes</a>
</ng-template>
<ng-template [ngIf]="network.val === 'bisq'">
<p>General</p>
<a [routerLink]="['./']" fragment="get-stats" (click)="collapseItem.toggle()">GET Stats</a>
</ng-template>
<p>Addresses</p>
<a [routerLink]="['./']" fragment="get-address" (click)="collapseItem.toggle()">GET Address</a>
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-address-transactions" (click)="collapseItem.toggle()">GET Address Transactions</a>
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-address-transactions-chain" (click)="collapseItem.toggle()">GET Address Transactions Chain</a>
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-address-transactions-mempool" (click)="collapseItem.toggle()">GET Address Transactions Mempool</a>
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-address-utxo" (click)="collapseItem.toggle()">GET Address UTXO</a>
<ng-template [ngIf]="network.val === 'liquid'">
<p>Assets</p>
<a [routerLink]="['./']" fragment="get-assets" (click)="collapseItem.toggle()">GET Assets</a>
<a [routerLink]="['./']" fragment="get-assets-icons" (click)="collapseItem.toggle()">GET Assets Icons</a>
<a [routerLink]="['./']" fragment="get-asset-transactions" (click)="collapseItem.toggle()">GET Asset Transactions</a>
<a [routerLink]="['./']" fragment="get-asset-supply" (click)="collapseItem.toggle()">GET Asset Supply</a>
<a [routerLink]="['./']" fragment="get-asset-icon" (click)="collapseItem.toggle()">GET Asset Icon</a>
</ng-template>
<p>Blocks</p>
<a [routerLink]="['./']" fragment="get-block" (click)="collapseItem.toggle()">GET Block</a>
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-header" (click)="collapseItem.toggle()">GET Block Header</a>
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-height" (click)="collapseItem.toggle()">GET Block Height</a>
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-raw" (click)="collapseItem.toggle()">GET Block Raw</a>
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-status" (click)="collapseItem.toggle()">GET Block Status</a>
<a [routerLink]="['./']" fragment="get-block-tip-height" (click)="collapseItem.toggle()">GET Block Tip Height</a>
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-tip-hash" (click)="collapseItem.toggle()">GET Block Tip Hash</a>
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-transaction-id" (click)="collapseItem.toggle()">GET Block Transaction ID</a>
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-transaction-ids" (click)="collapseItem.toggle()">GET Block Transaction IDs</a>
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-block-transactions" (click)="collapseItem.toggle()">GET Block Transactions</a>
<a [routerLink]="['./']" fragment="get-blocks" (click)="collapseItem.toggle()">GET Blocks</a>
<ng-template [ngIf]="network.val !== 'bisq'">
<p>Fees</p>
<a [routerLink]="['./']" fragment="get-mempool-blocks-fees" (click)="collapseItem.toggle()">GET Mempool Blocks Fees</a>
<a [routerLink]="['./']" fragment="get-recommended-fees" (click)="collapseItem.toggle()">GET Recommended Fees</a>
</ng-template>
<ng-template [ngIf]="network.val !== 'bisq'">
<p>Mempool</p>
<a [routerLink]="['./']" fragment="get-mempool" (click)="collapseItem.toggle()">GET Mempool</a>
<a [routerLink]="['./']" fragment="get-mempool-transaction-ids" (click)="collapseItem.toggle()">GET Mempool Transaction IDs</a>
<a [routerLink]="['./']" fragment="get-mempool-recent" (click)="collapseItem.toggle()">GET Mempool Recent</a>
</ng-template>
<p>Transactions</p>
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-cpfp" (click)="collapseItem.toggle()">GET Children Pay for Parent</a>
<a [routerLink]="['./']" fragment="get-transaction" (click)="collapseItem.toggle()">GET Transaction</a>
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-transaction-hex" (click)="collapseItem.toggle()">GET Transaction Hex</a>
<a *ngIf="network.val !== 'bisq' && network.val !== 'liquid'" [routerLink]="['./']" fragment="get-transaction-merkleblock-proof" (click)="collapseItem.toggle()">GET Transaction Merkleblock Proof</a>
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-transaction-merkle-proof" (click)="collapseItem.toggle()">GET Transaction Merkle Proof</a>
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-transaction-outspend" (click)="collapseItem.toggle()">GET Transaction Outspend</a>
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-transaction-outspends" (click)="collapseItem.toggle()">GET Transaction Outspends</a>
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-transaction-raw" (click)="collapseItem.toggle()">GET Transaction Raw</a>
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="get-transaction-status" (click)="collapseItem.toggle()">GET Transaction Status</a>
<a *ngIf="network.val === 'bisq'" [routerLink]="['./']" fragment="get-transactions" (click)="collapseItem.toggle()">GET Transactions</a>
<a *ngIf="network.val !== 'bisq'" [routerLink]="['./']" fragment="post-transaction" (click)="collapseItem.toggle()">POST Transaction</a>

View File

@@ -0,0 +1,17 @@
p {
color: #4a68b9;
font-weight: 700;
margin: 10px 0;
margin: 15px 0 10px 0;
}
p:first-child {
margin-top: 0
}
a {
color: #fff;
text-decoration: none;
display: block;
margin: 5px 0;
}

View File

@@ -0,0 +1,18 @@
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'app-api-docs-nav',
templateUrl: './api-docs-nav.component.html',
styleUrls: ['./api-docs-nav.component.scss']
})
export class ApiDocsNavComponent implements OnInit {
@Input() network: any;
@Input() collapseItem: any = { toggle: () => {} };
constructor() { }
ngOnInit(): void {
}
}

View File

@@ -3,18 +3,31 @@
<div id="restAPI" *ngIf="restTabActivated"> <div id="restAPI" *ngIf="restTabActivated">
<p>Reference for the {{ network.val === '' ? 'Bitcoin' : network.val.charAt(0).toUpperCase() + network.val.slice(1) }} <ng-container i18n="api-docs.title">API service</ng-container>.</p> <div id="doc-nav-desktop" class="hide-on-mobile" [ngClass]="desktopDocsNavPosition">
<app-api-docs-nav [network]="{ val: network$ | async }"></app-api-docs-nav>
</div>
<div class="doc-content">
<p class="hide-on-mobile no-bottom-space">Reference for the {{ network.val === '' ? 'Bitcoin' : network.val.charAt(0).toUpperCase() + network.val.slice(1) }} <ng-container i18n="api-docs.title">API service</ng-container>.</p>
<div id="doc-nav-mobile" class="hide-on-desktop" *ngIf="showFloatingDocsNav">
<button type="button" class="btn btn-outline-primary" (click)="collapse.toggle()" [attr.aria-expanded]="mobileMenuOpen" aria-controls="collapseExample"><fa-icon [icon]="['fas', 'list-ul']" [fixedWidth]="true"></fa-icon> {{ network.val === '' ? 'Bitcoin' : network.val.charAt(0).toUpperCase() + network.val.slice(1) }} REST API Overview</button>
<div #collapse="ngbCollapse" [(ngbCollapse)]="mobileMenuOpen">
<div class="card">
<div class="card-body">
<app-api-docs-nav [collapseItem]="collapse" [network]="{ val: network$ | async }"></app-api-docs-nav>
</div>
</div>
</div>
</div>
<div id="mobile-top-doc-nav" #mobileFixedApiNav class="hide-on-desktop"><app-api-docs-nav [network]="{ val: network$ | async }"></app-api-docs-nav></div>
<div class="api-category" *ngIf="network.val !== 'bisq' && network.val !== 'liquid'"> <div class="api-category" *ngIf="network.val !== 'bisq' && network.val !== 'liquid'">
<h4 i18n="api-docs.tab.general|API Docs tab for General">General</h4>
<ngb-accordion [closeOthers]="true" animated="true" type="dark">
<ngb-panel id="difficultyAdjustment" *ngIf="network.val !== 'liquid'"> <div class="endpoint-container" id="get-difficulty-adjustment">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-difficulty-adjustment">GET Difficulty Adjustment <span>General</span></a>
<span>GET Difficulty Adjustment</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="difficulty">
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.difficulty)" target="_blank">GET {{ baseNetworkUrl }}/api/v1/difficulty-adjustment</a> <a [href]="wrapUrl(network.val, code.difficulty)" target="_blank">GET {{ baseNetworkUrl }}/api/v1/difficulty-adjustment</a>
@@ -25,21 +38,13 @@
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.difficulty" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.difficulty" [network]="network.val" ></app-code-template>
</div> </div>
</ng-template>
</ngb-panel>
</ngb-accordion>
</div> </div>
<div class="api-category" *ngIf="network.val === 'bisq'"> <div class="api-category" *ngIf="network.val === 'bisq'">
<h4 i18n="Bisq All Markets">Markets</h4>
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
<ngb-panel id="bisqMarketsCurrencies"> <div class="endpoint-container" id="get-market-currencies">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-market-currencies">GET Market Currencies <span>Markets</span></a>
<span>GET Market Currencies</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.marketsCurrencies)" target="_blank">GET {{ baseNetworkUrl }}/api/currencies</a> <a [href]="wrapUrl(network.val, code.marketsCurrencies)" target="_blank">GET {{ baseNetworkUrl }}/api/currencies</a>
@@ -49,14 +54,10 @@
<div i18n>Provides list of available currencies for a given base currency. </div> <div i18n>Provides list of available currencies for a given base currency. </div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.marketsCurrencies" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.marketsCurrencies" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel id="marketDepth"> <div class="endpoint-container" id="get-market-depth">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-market-depth">GET Market Depth <span>Markets</span></a>
<span>GET Market Depth</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.marketDepth)" target="_blank">GET {{ baseNetworkUrl }}/api/depth?market=[:market]</a> <a [href]="wrapUrl(network.val, code.marketDepth)" target="_blank">GET {{ baseNetworkUrl }}/api/depth?market=[:market]</a>
@@ -66,14 +67,10 @@
<div i18n>Provides list of open offer prices for a single market.</div> <div i18n>Provides list of open offer prices for a single market.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.marketDepth" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.marketDepth" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel id="bisqMarketsHloc"> <div class="endpoint-container" id="get-market-hloc">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-market-hloc">GET Market HLOC <span>Markets</span></a>
<span>GET Market HLOC</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.marketHloc)" target="_blank">GET {{ baseNetworkUrl }}/api/hloc?market=[:market]</a> <a [href]="wrapUrl(network.val, code.marketHloc)" target="_blank">GET {{ baseNetworkUrl }}/api/hloc?market=[:market]</a>
@@ -83,14 +80,10 @@
<div i18n>Provides hi/low/open/close data for a given market. This can be used to generate a candlestick chart.</div> <div i18n>Provides hi/low/open/close data for a given market. This can be used to generate a candlestick chart.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.marketHloc" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.marketHloc" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel id="bisqMarketsMarkets"> <div class="endpoint-container" id="get-markets">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-markets">GET Markets <span>Markets</span></a>
<span>GET Markets</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.markets)" target="_blank">GET {{ baseNetworkUrl }}/api/markets</a> <a [href]="wrapUrl(network.val, code.markets)" target="_blank">GET {{ baseNetworkUrl }}/api/markets</a>
@@ -100,14 +93,10 @@
<div i18n>Provides list of available markets.</div> <div i18n>Provides list of available markets.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.markets" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.markets" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel id="bisqMarketsOffers"> <div class="endpoint-container" id="get-market-offers">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-market-offers">GET Market Offers <span>Markets</span></a>
<span>GET Market Offers</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.marketOffers)" target="_blank">GET {{ baseNetworkUrl }}/api/offers?market=[:market]</a> <a [href]="wrapUrl(network.val, code.marketOffers)" target="_blank">GET {{ baseNetworkUrl }}/api/offers?market=[:market]</a>
@@ -117,14 +106,10 @@
<div i18n>Provides list of open offer details for a single market.</div> <div i18n>Provides list of open offer details for a single market.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.marketOffers" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.marketOffers" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel id="bisqMarketsTicker"> <div class="endpoint-container" id="get-market-ticker">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-market-ticker">GET Market Ticker <span>Markets</span></a>
<span>GET Market Ticker</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.marketTicker)" target="_blank">GET {{ baseNetworkUrl }}/api/ticker?market=[:market]</a> <a [href]="wrapUrl(network.val, code.marketTicker)" target="_blank">GET {{ baseNetworkUrl }}/api/ticker?market=[:market]</a>
@@ -134,14 +119,10 @@
<div i18n>Provides 24 hour price ticker for single market or all markets</div> <div i18n>Provides 24 hour price ticker for single market or all markets</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.marketTicker" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.marketTicker" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel id="bisqMarketsTrades"> <div class="endpoint-container" id="get-market-trades">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-market-trades">GET Market Trades <span>Markets</span></a>
<span>GET Market Trades</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.marketTrades)" target="_blank">GET {{ baseNetworkUrl }}/api/trades?market=[:market]&limit=[:limit]</a> <a [href]="wrapUrl(network.val, code.marketTrades)" target="_blank">GET {{ baseNetworkUrl }}/api/trades?market=[:market]&limit=[:limit]</a>
@@ -151,14 +132,10 @@
<div i18n>Provides list of completed trades for a single market.</div> <div i18n>Provides list of completed trades for a single market.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.marketTrades" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.marketTrades" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel id="bisqMarketsVolumes"> <div class="endpoint-container" id="get-market-volumes">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-market-volumes">GET Market Volumes <span>Markets</span></a>
<span>GET Market Volumes</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.marketVolumes)" target="_blank">GET {{ baseNetworkUrl }}/api/volumes?basecurrency=[:basecurrency]</a> <a [href]="wrapUrl(network.val, code.marketVolumes)" target="_blank">GET {{ baseNetworkUrl }}/api/volumes?basecurrency=[:basecurrency]</a>
@@ -168,21 +145,14 @@
<div i18n>Provides periodic volume data in terms of base currency for one or all markets.</div> <div i18n>Provides periodic volume data in terms of base currency for one or all markets.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.marketVolumes" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.marketVolumes" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
</ngb-accordion>
</div> </div>
<div class="api-category" *ngIf="network.val === 'bisq'"> <div class="api-category" *ngIf="network.val === 'bisq'">
<h4 ngbNavLink i18n="api-docs.tab.bsq|API Docs tab for BSQ">General</h4>
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
<ngb-panel id="bisqStats"> <div class="endpoint-container" id="get-stats">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-stats">GET Stats <span>General</span></a>
<span>GET Stats</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.stats)" target="_blank'" target="_blank">GET {{ baseNetworkUrl }}/api/stats</a> <a [href]="wrapUrl(network.val, code.stats)" target="_blank'" target="_blank">GET {{ baseNetworkUrl }}/api/stats</a>
@@ -192,21 +162,14 @@
<div i18n>Returns statistics about all Bisq transactions.</div> <div i18n>Returns statistics about all Bisq transactions.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.stats" [network]="network.val"></app-code-template> <app-code-template [hostname]="hostname" [code]="code.stats" [network]="network.val"></app-code-template>
</ng-template> </div>
</ngb-panel>
</ngb-accordion>
</div> </div>
<div class="api-category"> <div class="api-category">
<h4 i18n="api-docs.tab.addresses|API Docs tab for Addresses">Addresses</h4>
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
<ngb-panel id="address"> <div class="endpoint-container" id="get-address">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-address">GET Address <span>Addresses</span></a>
<span>GET Address</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.address)" target="_blank">GET {{ baseNetworkUrl }}/api/address/:address</a> <a [href]="wrapUrl(network.val, code.address)" target="_blank">GET {{ baseNetworkUrl }}/api/address/:address</a>
@@ -216,14 +179,10 @@
<div 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>.</div> <div 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>.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.address" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.address" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val !== 'bisq'" id="addressTransactions"> <div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-address-transactions">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-address-transactions">GET Address Transactions <span>Addresses</span></a>
<span>GET Address Transactions</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.addressTransactions)" target="_blank">GET {{ baseNetworkUrl }}/api/address/:address/txs</a> <a [href]="wrapUrl(network.val, code.addressTransactions)" target="_blank">GET {{ baseNetworkUrl }}/api/address/:address/txs</a>
@@ -233,14 +192,10 @@
<div 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).</div> <div 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).</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.addressTransactions" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.addressTransactions" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val !== 'bisq'" id="addressTransactionsChain"> <div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-address-transactions-chain">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-address-transactions-chain">GET Address Transactions Chain <span>Addresses</span></a>
<span>GET Address Transactions Chain</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.addressTransactionsChain)" target="_blank">GET {{ baseNetworkUrl }}/api/address/:address/txs/chain</a> <a [href]="wrapUrl(network.val, code.addressTransactionsChain)" target="_blank">GET {{ baseNetworkUrl }}/api/address/:address/txs/chain</a>
@@ -250,14 +205,10 @@
<div 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.</div> <div 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.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.addressTransactionsChain" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.addressTransactionsChain" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val !== 'bisq'" id="addressTransactionsMempool"> <div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-address-transactions-mempool">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-address-transactions-mempool">GET Address Transactions Mempool <span>Addresses</span></a>
<span>GET Address Transactions Mempool</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.addressTransactionsMempool)" target="_blank">GET {{ baseNetworkUrl }}/api/address/:address/txs/mempool</a> <a [href]="wrapUrl(network.val, code.addressTransactionsMempool)" target="_blank">GET {{ baseNetworkUrl }}/api/address/:address/txs/mempool</a>
@@ -267,14 +218,10 @@
<div i18n>Get unconfirmed transaction history for the specified address/scripthash. Returns up to 50 transactions (no paging).</div> <div i18n>Get unconfirmed transaction history for the specified address/scripthash. Returns up to 50 transactions (no paging).</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.addressTransactionsMempool" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.addressTransactionsMempool" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val !== 'bisq'" id="addressUTXO"> <div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-address-utxo">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-address-utxo">GET Address UTXO <span>Addresses</span></a>
<span>GET Address UTXO</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.addressUTXO)" target="_blank">GET {{ baseNetworkUrl }}/api/address/:address/utxo</a> <a [href]="wrapUrl(network.val, code.addressUTXO)" target="_blank">GET {{ baseNetworkUrl }}/api/address/:address/utxo</a>
@@ -284,21 +231,14 @@
<div 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></div> <div 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></div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.addressUTXO" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.addressUTXO" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
</ngb-accordion>
</div> </div>
<div class="api-category" *ngIf="network.val === 'liquid'"> <div class="api-category" *ngIf="network.val === 'liquid'">
<h4 i18n="api-docs.tab.assets|API Docs tab for Assets">Assets</h4>
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
<ngb-panel id="assets"> <div class="endpoint-container" id="get-assets">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-assets">GET Assets <span>Assets</span></a>
<span>GET Assets</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.assets)" target="_blank">GET /liquid/api/asset/:asset_id</a> <a [href]="wrapUrl(network.val, code.assets)" target="_blank">GET /liquid/api/asset/:asset_id</a>
@@ -308,14 +248,10 @@
<div i18n>Returns information about a Liquid asset.</div> <div i18n>Returns information about a Liquid asset.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.assets" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.assets" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel id="assetTransactions"> <div class="endpoint-container" id="get-asset-transactions">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-asset-transactions">GET Asset Transactions <span>Assets</span></a>
<span>GET Asset Transactions</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<a [href]="wrapUrl(network.val, code.assetTransactions)" target="_blank">GET /liquid/api/asset/:asset_id/txs[/mempool|/chain]</a> <a [href]="wrapUrl(network.val, code.assetTransactions)" target="_blank">GET /liquid/api/asset/:asset_id/txs[/mempool|/chain]</a>
</div> </div>
@@ -324,14 +260,10 @@
<div 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.</div> <div 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.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.assetTransactions" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.assetTransactions" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel id="assetSupply"> <div class="endpoint-container" id="get-asset-supply">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-asset-supply">GET Asset Supply <span>Assets</span></a>
<span>GET Asset Supply</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.assetSupply)" target="_blank">GET /liquid/api/asset/:asset_id/supply[/decimal]</a> <a [href]="wrapUrl(network.val, code.assetSupply)" target="_blank">GET /liquid/api/asset/:asset_id/supply[/decimal]</a>
@@ -341,21 +273,40 @@
<div 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.</div> <div 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.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.assetSupply" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.assetSupply" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<div class="endpoint-container" id="get-assets-icons">
<a class="section-header" [routerLink]="['./']" fragment="get-assets-icons">GET Asset Icons <span>Assets</span></a>
<div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.assetIcons)" target="_blank">GET /liquid/api/v1/assets/icons</a>
</div>
<div class="description">
<div class="subtitle" i18n>Description</div>
<div>Get all the Asset IDs that has icons.</div>
</div>
<app-code-template [hostname]="hostname" [code]="code.assetIcons" [network]="network.val" ></app-code-template>
</div>
<div class="endpoint-container" id="get-asset-icon">
<a class="section-header" [routerLink]="['./']" fragment="get-asset-icon">GET Asset Icon <span>Assets</span></a>
<div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.assetIcon)" target="_blank">GET /liquid/api/v1/asset/:asset_id/icon</a>
</div>
<div class="description">
<div class="subtitle" i18n>Description</div>
<div>Get the icon of the specified asset.</div>
</div>
<app-code-template [hostname]="hostname" [code]="code.assetIcon" [network]="network.val" ></app-code-template>
</div>
</ngb-accordion>
</div> </div>
<div class="api-category"> <div class="api-category">
<h4 i18n="api-docs.tab.blocks|API Docs tab for Blocks">Blocks</h4>
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
<ngb-panel id="block"> <div class="endpoint-container" id="get-block">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-block">GET Block <span>Blocks</span></a>
<span>GET Block</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.block)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash</a> <a [href]="wrapUrl(network.val, code.block)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash</a>
@@ -365,14 +316,10 @@
<div 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>.</div> <div 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>.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.block" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.block" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val !== 'bisq'" id="blockHeader"> <div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-block-header">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-block-header">GET Block Header <span>Blocks</span></a>
<span>GET Block Header</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.blockHeader)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/header</a> <a [href]="wrapUrl(network.val, code.blockHeader)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/header</a>
@@ -382,14 +329,10 @@
<div i18n>Returns the hex-encoded block header.</div> <div i18n>Returns the hex-encoded block header.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.blockHeader" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.blockHeader" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val !== 'bisq'" id="blockHeight"> <div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-block-height">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-block-height">GET Block Height <span>Blocks</span></a>
<span>GET Block Height</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.blockHeader)" target="_blank">GET {{ baseNetworkUrl }}/api/block-height/:height</a> <a [href]="wrapUrl(network.val, code.blockHeader)" target="_blank">GET {{ baseNetworkUrl }}/api/block-height/:height</a>
@@ -399,15 +342,12 @@
<div i18n>Returns the hash of the block currently at <code>:height</code>.</div> <div i18n>Returns the hash of the block currently at <code>:height</code>.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.blockHeight" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.blockHeight" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val !== 'bisq'" id="blockRaw"> <div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-block-raw">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-block-raw">GET Block Raw <span>Blocks</span></a>
<span>GET Block Raw</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.blockRaw)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/raw</a> <a [href]="wrapUrl(network.val, code.blockRaw)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/raw</a>
</div> </div>
<div class="description"> <div class="description">
@@ -415,15 +355,10 @@
<div i18n>Returns the raw block representation in binary.</div> <div i18n>Returns the raw block representation in binary.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.blockRaw" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.blockRaw" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val !== 'bisq'" id="blockStatus"> <div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-block-status">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-block-status">GET Block Status <span>Blocks</span></a>
<span>GET Block Status</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="title">Get Block Status</div>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.blockStatus)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/status</a> <a [href]="wrapUrl(network.val, code.blockStatus)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/status</a>
@@ -433,14 +368,10 @@
<div 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).</div> <div 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).</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.blockStatus" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.blockStatus" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel id="blockTipHeight"> <div class="endpoint-container" id="get-block-tip-height">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-block-tip-height">GET Block Tip Height <span>Blocks</span></a>
<span>GET Block Tip Height</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.blockTipHeight)" target="_blank">GET {{ baseNetworkUrl }}/api/blocks/tip/height</a> <a [href]="wrapUrl(network.val, code.blockTipHeight)" target="_blank">GET {{ baseNetworkUrl }}/api/blocks/tip/height</a>
@@ -450,14 +381,10 @@
<div i18n>Returns the height of the last block.</div> <div i18n>Returns the height of the last block.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.blockTipHeight" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.blockTipHeight" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val !== 'bisq'" id="blockTipHash"> <div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-block-tip-hash">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-block-tip-hash">GET Block Tip Hash <span>Blocks</span></a>
<span>GET Block Tip Hash</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.blockTipHash)" target="_blank">GET {{ baseNetworkUrl }}/api/blocks/tip/hash</a> <a [href]="wrapUrl(network.val, code.blockTipHash)" target="_blank">GET {{ baseNetworkUrl }}/api/blocks/tip/hash</a>
@@ -467,14 +394,10 @@
<div i18n>Returns the hash of the last block.</div> <div i18n>Returns the hash of the last block.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.blockTipHash" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.blockTipHash" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val !== 'bisq'" id="blockTxId"> <div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-block-transaction-id">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-block-transaction-id">GET Block Transaction ID <span>Blocks</span></a>
<span>GET Block Transaction ID</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.blockTxId)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/txid/:index</a> <a [href]="wrapUrl(network.val, code.blockTxId)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/txid/:index</a>
@@ -484,14 +407,10 @@
<div i18n>Returns the transaction at index <code>:index</code> within the specified block.</div> <div i18n>Returns the transaction at index <code>:index</code> within the specified block.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.blockTxId" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.blockTxId" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val !== 'bisq'" id="blockTxIds"> <div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-block-transaction-ids">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-block-transaction-ids">GET Block Transaction IDs <span>Blocks</span></a>
<span>GET Block Transaction IDs</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.blockTxIds)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/txids</a> <a [href]="wrapUrl(network.val, code.blockTxIds)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/txids</a>
@@ -501,14 +420,10 @@
<div i18n>Returns a list of all txids in the block.</div> <div i18n>Returns a list of all txids in the block.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.blockTxIds" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.blockTxIds" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val !== 'bisq'" id="blockTxs"> <div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-block-transactions">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-block-transactions">GET Block Transactions <span>Blocks</span></a>
<span>GET Block Transactions</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.blockTxs)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/txs[/:start_index]</a> <a [href]="wrapUrl(network.val, code.blockTxs)" target="_blank">GET {{ baseNetworkUrl }}/api/block/:hash/txs[/:start_index]</a>
@@ -518,14 +433,10 @@
<div 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.</div> <div 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.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.blockTxs" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.blockTxs" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val !== 'bisq'" id="blocks"> <div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-blocks">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-blocks">GET Blocks <span>Blocks</span></a>
<span>GET Blocks</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.blocks)" target="_blank">GET {{ baseNetworkUrl }}/api/blocks[/:start_height]</a> <a [href]="wrapUrl(network.val, code.blocks)" target="_blank">GET {{ baseNetworkUrl }}/api/blocks[/:start_height]</a>
@@ -535,14 +446,10 @@
<div i18n>Returns the 10 newest blocks starting at the tip or at <code>:start_height</code> if specified.</div> <div i18n>Returns the 10 newest blocks starting at the tip or at <code>:start_height</code> if specified.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.blocks" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.blocks" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val === 'bisq'" id="blocks"> <div class="endpoint-container" *ngIf="network.val === 'bisq'" id="get-blocks">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-blocks">GET Blocks <span>Blocks</span></a>
<span>GET Blocks</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.blocksBisq)" target="_blank">GET {{ baseNetworkUrl }}/api/blocks/:index/:length</a> <a [href]="wrapUrl(network.val, code.blocksBisq)" target="_blank">GET {{ baseNetworkUrl }}/api/blocks/:index/:length</a>
@@ -552,21 +459,14 @@
<div i18n>Returns the 10 newest blocks starting at the tip or at <code>:start_height</code> if specified.</div> <div i18n>Returns the 10 newest blocks starting at the tip or at <code>:start_height</code> if specified.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.blocksBisq" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.blocksBisq" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
</ngb-accordion>
</div> </div>
<div class="api-category" *ngIf="network.val !== 'bisq'"> <div class="api-category" *ngIf="network.val !== 'bisq'">
<h4 i18n="api-docs.tab.fees|API Docs tab for Fees">Fees</h4>
<ngb-accordion [closeOthers]="true" animated="true" type="dark">
<ngb-panel id="feeMempoolBlocks"> <div class="endpoint-container" id="get-mempool-blocks-fees">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-mempool-blocks-fees">GET Mempool Blocks Fees <span>Fees</span></a>
<span>GET Mempool Blocks Fees</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.feeMempoolBlocks)" target="_blank">GET {{ baseNetworkUrl }}/api/v1/fees/mempool-blocks</a> <a [href]="wrapUrl(network.val, code.feeMempoolBlocks)" target="_blank">GET {{ baseNetworkUrl }}/api/v1/fees/mempool-blocks</a>
@@ -576,14 +476,10 @@
<div i18n="api-docs.fees.mempool-blocks|API Docs for /api/v1/fees/mempool-blocks">Returns current mempool as projected blocks.</div> <div i18n="api-docs.fees.mempool-blocks|API Docs for /api/v1/fees/mempool-blocks">Returns current mempool as projected blocks.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.feeMempoolBlocks" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.feeMempoolBlocks" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel id="feeRecommended"> <div class="endpoint-container" id="get-recommended-fees">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-recommended-fees">GET Recommended Fees <span>Fees</span></a>
<span>GET Recommended Fees</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.feeRecommended)" target="_blank">GET {{ baseNetworkUrl }}/api/v1/fees/recommended</a> <a [href]="wrapUrl(network.val, code.feeRecommended)" target="_blank">GET {{ baseNetworkUrl }}/api/v1/fees/recommended</a>
@@ -593,21 +489,14 @@
<div i18n="api-docs.fees.recommended|API Docs for /api/v1/fees/recommended">Returns our currently suggested fees for new transactions.</div> <div i18n="api-docs.fees.recommended|API Docs for /api/v1/fees/recommended">Returns our currently suggested fees for new transactions.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.feeRecommended" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.feeRecommended" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
</ngb-accordion>
</div> </div>
<div class="api-category" *ngIf="network.val !== 'bisq'"> <div class="api-category" *ngIf="network.val !== 'bisq'">
<h4 i18n="api-docs.tab.mempool|API Docs tab for Mempool">Mempool</h4>
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
<ngb-panel id="mempool"> <div class="endpoint-container" id="get-mempool">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-mempool">GET Mempool <span>Fees</span></a>
<span>GET Mempool</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.mempool)" target="_blank">GET {{ baseNetworkUrl }}/api/mempool</a> <a [href]="wrapUrl(network.val, code.mempool)" target="_blank">GET {{ baseNetworkUrl }}/api/mempool</a>
@@ -617,14 +506,10 @@
<div i18n="api-docs.mempool.mempool|API Docs for /api/mempool">Returns current mempool backlog statistics.</div> <div i18n="api-docs.mempool.mempool|API Docs for /api/mempool">Returns current mempool backlog statistics.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.mempool" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.mempool" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel id="mempoolTxs"> <div class="endpoint-container" id="get-mempool-transaction-ids">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-mempool-transaction-ids">GET Mempool Transactions IDs <span>Fees</span></a>
<span>GET Mempool Transactions IDs</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.mempoolTxs)" target="_blank">GET {{ baseNetworkUrl }}/api/mempool/txids</a> <a [href]="wrapUrl(network.val, code.mempoolTxs)" target="_blank">GET {{ baseNetworkUrl }}/api/mempool/txids</a>
@@ -634,14 +519,10 @@
<div 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.</div> <div 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.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.mempoolTxs" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.mempoolTxs" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel id="mempoolRecent"> <div class="endpoint-container" id="get-mempool-recent">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-mempool-recent">GET Mempool Recent <span>Fees</span></a>
<span>GET Mempool Recent</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.mempoolRecent)" target="_blank">GET {{ baseNetworkUrl }}/api/mempool/recent</a> <a [href]="wrapUrl(network.val, code.mempoolRecent)" target="_blank">GET {{ baseNetworkUrl }}/api/mempool/recent</a>
@@ -651,21 +532,14 @@
<div 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>.</div> <div 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>.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.mempoolRecent" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.mempoolRecent" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
</ngb-accordion>
</div> </div>
<div class="api-category"> <div class="api-category">
<h4 i18n="api-docs.tab.transactions|API Docs tab for Transactions">Transactions</h4>
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
<ngb-panel *ngIf="network.val !== 'bisq'" id="cpfp"> <div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-cpfp">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-cpfp">GET Children Pay for Parent <span>Transactions</span></a>
<span>GET Children Pay for Parent</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.transactionCpfp)" target="_blank">GET {{ baseNetworkUrl }}/api/v1/cpfp/:txid</a> <a [href]="wrapUrl(network.val, code.transactionCpfp)" target="_blank">GET {{ baseNetworkUrl }}/api/v1/cpfp/:txid</a>
@@ -675,15 +549,12 @@
<div i18n="api-docs.fees.cpfp|API Docs for /api/v1/fees/cpfp">Returns the ancestors and the best descendant fees for a transaction.</div> <div i18n="api-docs.fees.cpfp|API Docs for /api/v1/fees/cpfp">Returns the ancestors and the best descendant fees for a transaction.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.transactionCpfp" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.transactionCpfp" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel id="transaction"> <div class="endpoint-container" id="get-transaction">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-transaction">GET Transaction <span>Transactions</span></a>
<span>GET Transaction</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.transaction)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid</a> <a [href]="wrapUrl(network.val, code.transaction)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid</a>
</div> </div>
<div class="description"> <div class="description">
@@ -691,14 +562,10 @@
<div 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>.</div> <div 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>.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.transaction" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.transaction" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val !== 'bisq'" id="transactionHex"> <div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-transaction-hex">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-transaction-hex">GET Transaction Hex <span>Transactions</span></a>
<span>GET Transaction Hex</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.transactionHex)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/hex</a> <a [href]="wrapUrl(network.val, code.transactionHex)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/hex</a>
@@ -708,14 +575,10 @@
<div i18n>Returns a transaction serialized as hex.</div> <div i18n>Returns a transaction serialized as hex.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.transactionHex" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.transactionHex" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val !== 'bisq' && network.val !== 'liquid'" id="transactionMerkleBlockProof"> <div class="endpoint-container" *ngIf="network.val !== 'bisq' && network.val !== 'liquid'" id="get-transaction-merkleblock-proof">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-transaction-merkleblock-proof">GET Transaction Merkleblock Proof <span>Transactions</span></a>
<span>GET Transaction Merkleblock Proof</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.transactionMerkleBlockProof)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/merkleblock-proof</a> <a [href]="wrapUrl(network.val, code.transactionMerkleBlockProof)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/merkleblock-proof</a>
@@ -725,14 +588,10 @@
<div i18n>Returns a merkle inclusion proof for the transaction using <a href="https://bitcoin.org/en/glossary/merkle-block">bitcoind's merkleblock</a> format.</div> <div i18n>Returns a merkle inclusion proof for the transaction using <a href="https://bitcoin.org/en/glossary/merkle-block">bitcoind's merkleblock</a> format.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.transactionMerkleBlockProof" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.transactionMerkleBlockProof" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val !== 'bisq'" id="transactionMerkleProof"> <div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-transaction-merkle-proof">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-transaction-merkle-proof">GET Transaction Merkle Proof <span>Transactions</span></a>
<span>GET Transaction Merkle Proof</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.transactionMerkleProof)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/merkle-proof</a> <a [href]="wrapUrl(network.val, code.transactionMerkleProof)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/merkle-proof</a>
@@ -742,14 +601,10 @@
<div 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></div> <div 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></div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.transactionMerkleProof" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.transactionMerkleProof" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val !== 'bisq'" id="transactionOutspend"> <div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-transaction-outspend">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-transaction-outspend">GET Transaction Outspend <span>Transactions</span></a>
<span>GET Transaction Outspend</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.transactionOutspend)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/outspend/:vout</a> <a [href]="wrapUrl(network.val, code.transactionOutspend)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/outspend/:vout</a>
@@ -759,14 +614,10 @@
<div 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).</div> <div 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).</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.transactionOutspend" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.transactionOutspend" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val !== 'bisq'" id="transactionOutspends"> <div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-transaction-outspends">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-transaction-outspends">GET Transaction Outspends <span>Transactions</span></a>
<span>GET Transaction Outspends</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.transactionOutspends)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/outspends</a> <a [href]="wrapUrl(network.val, code.transactionOutspends)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/outspends</a>
@@ -776,14 +627,10 @@
<div i18n>Returns the spending status of all transaction outputs.</div> <div i18n>Returns the spending status of all transaction outputs.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.transactionOutspends" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.transactionOutspends" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val !== 'bisq'" id="transactionRaw"> <div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-transaction-raw">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-transaction-raw">GET Transaction Raw <span>Transactions</span></a>
<span>GET Transaction Raw</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.transactionRaw)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/raw</a> <a [href]="wrapUrl(network.val, code.transactionRaw)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/raw</a>
@@ -793,14 +640,10 @@
<div i18n>Returns a transaction as binary data.</div> <div i18n>Returns a transaction as binary data.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.transactionRaw" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.transactionRaw" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val !== 'bisq'" id="transactionStatus"> <div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="get-transaction-status">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-transaction-status">GET Transaction Status <span>Transactions</span></a>
<span>GET Transaction Status</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.transactionStatus)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/status</a> <a [href]="wrapUrl(network.val, code.transactionStatus)" target="_blank">GET {{ baseNetworkUrl }}/api/tx/:txid/status</a>
@@ -810,15 +653,10 @@
<div 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).</div> <div 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).</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.transactionStatus" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.transactionStatus" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val === 'bisq'" id="transactionsBisq"> <div class="endpoint-container" *ngIf="network.val === 'bisq'" id="get-transactions">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="get-transactions">GET Transactions <span>Transactions</span></a>
<span>GET Transactions</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="title">Get Mempool Txids</div>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<a [href]="wrapUrl(network.val, code.transactionsBisq)" target="_blank">GET {{ baseNetworkUrl }}/api/txs/:index/:length</a> <a [href]="wrapUrl(network.val, code.transactionsBisq)" target="_blank">GET {{ baseNetworkUrl }}/api/txs/:index/:length</a>
@@ -828,14 +666,10 @@
<div i18n>Returns :length of latest Bisq transactions, starting from :index.</div> <div i18n>Returns :length of latest Bisq transactions, starting from :index.</div>
</div> </div>
<app-code-template [hostname]="hostname" [code]="code.transactionsBisq" [network]="network.val" ></app-code-template> <app-code-template [hostname]="hostname" [code]="code.transactionsBisq" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
<ngb-panel *ngIf="network.val !== 'bisq'" id="postTransaction"> <div class="endpoint-container" *ngIf="network.val !== 'bisq'" id="post-transaction">
<ng-template ngbPanelTitle> <a class="section-header" [routerLink]="['./']" fragment="post-transaction">POST Transaction <span>Transactions</span></a>
<span>POST Transaction</span>
</ng-template>
<ng-template ngbPanelContent>
<div class="endpoint"> <div class="endpoint">
<div class="subtitle" i18n="Api docs endpoint">Endpoint</div> <div class="subtitle" i18n="Api docs endpoint">Endpoint</div>
<div>POST /api/tx</div> <div>POST /api/tx</div>
@@ -845,10 +679,9 @@
<div 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.</div> <div 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.</div>
</div> </div>
<app-code-template [method]="'post'" [hostname]="hostname" [code]="code.transactionPost" [network]="network.val" ></app-code-template> <app-code-template [method]="'post'" [hostname]="hostname" [code]="code.transactionPost" [network]="network.val" ></app-code-template>
</ng-template> </div>
</ngb-panel>
</ngb-accordion> </div>
</div> </div>
</div> </div>
@@ -868,13 +701,5 @@
</div> </div>
</div> </div>
<br>
<div class="text-center">
<a [routerLink]="['/terms-of-service']" i18n="shared.terms-of-service|Terms of Service">Terms of Service</a>
|
<a [routerLink]="['/privacy-policy']" i18n="shared.privacy-policy|Privacy Policy">Privacy Policy</a>
</div>
</div> </div>
</ng-container> </ng-container>

View File

@@ -22,6 +22,10 @@ li.nav-item {
} }
} }
.no-bottom-space {
margin-bottom: 0;
}
.nav-tabs .nav-link.active { .nav-tabs .nav-link.active {
border-bottom: 1px solid #fff; border-bottom: 1px solid #fff;
@media (min-width: 676px){ @media (min-width: 676px){
@@ -72,10 +76,131 @@ li.nav-item {
padding: 15px; padding: 15px;
} }
#restAPI .api-category { #doc-nav-desktop {
margin: 30px 0; width: 300px;
} }
.api-category h4 { #doc-nav-desktop.relative {
margin-bottom: 15px; float: left;
overflow: hidden;
}
#doc-nav-desktop.fixed {
float: unset;
position: fixed;
top: 20px;
overflow-y: auto;
height: calc(100vh - 50px);
scrollbar-color: #2d3348 #11131f;
scrollbar-width: thin;
}
::-webkit-scrollbar {
width: 3px;
}
::-webkit-scrollbar-track {
background: #11131f;
}
::-webkit-scrollbar-thumb {
background-color: #2d3348;
border-radius: 5px;
border: none;
}
.doc-content {
width: calc(100% - 330px);
float: right;
}
.endpoint-container:before {
display: block;
content: " ";
height: 1px;
margin-top: -1px;
visibility: hidden;
}
.endpoint-container .section-header {
display: block;
background-color: #2d3348;
color: #1bd8f4;
padding: 1rem 1.3rem 1rem 1.3rem;
font-weight: bold;
border-radius: 0.25rem;
margin: 20px 0 20px 0;
font-size: 24px;
position: relative;
}
.endpoint-container .section-header:hover {
text-decoration: none;
}
.endpoint-container .section-header span {
color: #fff;
background-color: #653b9c;
font-size: 12px;
text-transform: uppercase;
font-weight: 400;
padding: 8px 10px;
letter-spacing: 1px;
border-radius: 0.25rem;
font-family: monospace;
float: right;
}
#doc-nav-mobile {
position: fixed;
top: 20px;
width: calc(100% - 60px);
z-index: 100;
}
#doc-nav-mobile > div {
background-color: #2d3348;
z-index: 100;
border-radius: 0 0 0.5rem 0.5rem;
height: 55vh;
overflow-y: auto;
}
#doc-nav-mobile button {
width: 100%;
background-color: #105fb0;
color: #fff;
border-color: #105fb0;
border-radius: 0.5rem 0.5rem 0 0;
}
@media (max-width: 992px) {
.hide-on-mobile {
display: none;
}
.doc-content {
width: 100%;
}
.endpoint-container .section-header {
margin: 40px 0 70px 0;
}
.endpoint-container .section-header span {
float: none;
position: absolute;
top: unset;
left: 0;
bottom: -50px;
}
.endpoint-container:before {
height: 30px;
margin-top: -12px;
}
}
@media (min-width: 992px) {
.hide-on-desktop {
display: none;
}
} }

View File

@@ -1,4 +1,4 @@
import { Component, OnInit, Input } from '@angular/core'; import { Component, OnInit, Input, ViewChild, ElementRef } from '@angular/core';
import { Env, StateService } from 'src/app/services/state.service'; import { Env, StateService } from 'src/app/services/state.service';
import { Observable, merge, of } from 'rxjs'; import { Observable, merge, of } from 'rxjs';
import { SeoService } from 'src/app/services/seo.service'; import { SeoService } from 'src/app/services/seo.service';
@@ -17,12 +17,26 @@ export class ApiDocsComponent implements OnInit {
code: any; code: any;
baseNetworkUrl = ''; baseNetworkUrl = '';
@Input() restTabActivated: Boolean; @Input() restTabActivated: Boolean;
@ViewChild( "mobileFixedApiNav", { static: false } ) mobileFixedApiNav: ElementRef;
desktopDocsNavPosition = "relative";
showFloatingDocsNav = false;
mobileMenuOpen = true;
constructor( constructor(
private stateService: StateService, private stateService: StateService,
private seoService: SeoService, private seoService: SeoService,
) { } ) { }
ngAfterViewInit() {
const that = this;
setTimeout( () => {
window.addEventListener('scroll', function() {
that.desktopDocsNavPosition = ( window.pageYOffset > 182 ) ? "fixed" : "relative";
that.showFloatingDocsNav = ( window.pageYOffset > ( that.mobileFixedApiNav.nativeElement.offsetHeight + 188 ) ) ? true : false;
});
}, 1 );
}
ngOnInit(): void { ngOnInit(): void {
this.env = this.stateService.env; this.env = this.stateService.env;
this.seoService.setTitle($localize`:@@e351b40b3869a5c7d19c3d4918cb1ac7aaab95c4:API`); this.seoService.setTitle($localize`:@@e351b40b3869a5c7d19c3d4918cb1ac7aaab95c4:API`);
@@ -628,24 +642,6 @@ export class ApiDocsComponent implements OnInit {
console.log(asset); console.log(asset);
`, `,
}, },
codeSampleMainnet: {
esModule: [],
commonJS: [],
curl: [],
response: ''
},
codeSampleTestnet: {
esModule: [],
commonJS: [],
curl: [],
response: ''
},
codeSampleSignet: {
esModule: [],
commonJS: [],
curl: [],
response: ''
},
codeSampleLiquid: { codeSampleLiquid: {
esModule: [`6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`], esModule: [`6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`],
commonJS: [`6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`], commonJS: [`6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`],
@@ -679,6 +675,47 @@ export class ApiDocsComponent implements OnInit {
response: '' response: ''
}, },
}, },
assetIcons: {
codeTemplate: {
curl: `/api/v1/assets/icons`,
commonJS: `
const { %{0}: { assets } } = mempoolJS();
const assetsIcons = await assets.getAssetsIcons();
document.getElementById("result").textContent = JSON.stringify(assetsIcons, undefined, 2);
`,
esModule: `
const { %{0}: { assets } } = mempoolJS();
const assetsIcons = await assets.getAssetsIcons();
console.log(assetsIcons);
`,
},
codeSampleLiquid: {
esModule: [`6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`],
commonJS: [`6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`],
curl: [`6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`],
response: `[
"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d",
"ce091c998b83c78bb71a632313ba3760f1763d9cfcffae02258ffa9865a37bd2"
...
]`,
},
},
assetIcon: {
noWrap: true,
codeTemplate: {
curl: `/api/v1/asset/%{1}/icon`,
commonJS: `<img src="https://liquid.place/api/v1/asset/%{1}/icon">`,
},
codeSampleLiquid: {
esModule: [`6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`],
commonJS: [`6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`],
curl: [`6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`],
response: `PNG`,
},
},
assetTransactions: { assetTransactions: {
codeTemplate: { codeTemplate: {
curl: `/api/asset/%{1}/txs`, curl: `/api/asset/%{1}/txs`,

View File

@@ -164,6 +164,10 @@ init();`;
codeText = this.replaceJSPlaceholder(codeText, code.codeSampleBisq.esModule); codeText = this.replaceJSPlaceholder(codeText, code.codeSampleBisq.esModule);
} }
if (code.noWrap) {
return codeText;
}
let importText = `<script src="https://mempool.space/mempool.js"></script>`; let importText = `<script src="https://mempool.space/mempool.js"></script>`;
if (this.env.BASE_MODULE === 'bisq') { if (this.env.BASE_MODULE === 'bisq') {
importText = `<script src="https://bisq.markets/bisq.js"></script>`; importText = `<script src="https://bisq.markets/bisq.js"></script>`;

View File

@@ -27,5 +27,13 @@
<div id="main-tab-content" [ngbNavOutlet]="nav" class="mt-2"></div> <div id="main-tab-content" [ngbNavOutlet]="nav" class="mt-2"></div>
<br>
<div id="footer" class="text-center">
<a [routerLink]="['/terms-of-service']" i18n="shared.terms-of-service|Terms of Service">Terms of Service</a>
|
<a [routerLink]="['/privacy-policy']" i18n="shared.privacy-policy|Privacy Policy">Privacy Policy</a>
</div>
</div> </div>
</div> </div>

View File

@@ -1,4 +1,9 @@
#main-tab-content { #main-tab-content {
text-align: left; text-align: left;
padding-top: 10px; padding-top: 10px;
scroll-behavior: smooth;
}
#footer {
clear: both;
} }

View File

@@ -3,7 +3,8 @@
<div class="flashing"> <div class="flashing">
<ng-template ngFor let-projectedBlock [ngForOf]="mempoolBlocks$ | async" let-i="index" [ngForTrackBy]="trackByFn"> <ng-template ngFor let-projectedBlock [ngForOf]="mempoolBlocks$ | async" let-i="index" [ngForTrackBy]="trackByFn">
<div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]" [class.blink-bg]="projectedBlock.blink"> <div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]" [class.blink-bg]="projectedBlock.blink">
<a [routerLink]="['/mempool-block/' | relativeUrl, i]" class="blockLink">&nbsp;</a> <a draggable="false" [routerLink]="['/mempool-block/' | relativeUrl, i]"
class="blockLink" [ngClass]="{'disabled': (this.stateService.blockScrolling$ | async)}">&nbsp;</a>
<div class="block-body"> <div class="block-body">
<div class="fees"> <div class="fees">
~{{ projectedBlock.medianFee | number:feeRounding }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span> ~{{ projectedBlock.medianFee | number:feeRounding }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>

View File

@@ -117,6 +117,10 @@
z-index: 10; z-index: 10;
} }
.blockLink.disabled {
pointer-events: none;
}
.blockLink:hover { .blockLink:hover {
text-decoration: none; text-decoration: none;
} }

View File

@@ -8,8 +8,11 @@
<div *ngIf="countdown > 0" class="warning-label">{{ eventName }} in {{ countdown | number }} block{{ countdown === 1 ? '' : 's' }}!</div> <div *ngIf="countdown > 0" class="warning-label">{{ eventName }} in {{ countdown | number }} block{{ countdown === 1 ? '' : 's' }}!</div>
<div id="blockchain-container" dir="ltr"> <div id="blockchain-container" dir="ltr" #blockchainContainer
<app-blockchain></app-blockchain> (mousedown)="onMouseDown($event)"
(dragstart)="onDragStart($event)"
>
<app-blockchain></app-blockchain>
</div> </div>
<router-outlet></router-outlet> <router-outlet></router-outlet>

View File

@@ -1,8 +1,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core';
import { WebsocketService } from 'src/app/services/websocket.service'; import { WebsocketService } from 'src/app/services/websocket.service';
import { StateService } from 'src/app/services/state.service'; import { StateService } from 'src/app/services/state.service';
import { specialBlocks } from 'src/app/app.constants'; import { specialBlocks } from 'src/app/app.constants';
import { takeLast } from 'rxjs/operators';
@Component({ @Component({
selector: 'app-start', selector: 'app-start',
@@ -16,6 +15,9 @@ export class StartComponent implements OnInit {
countdown = 0; countdown = 0;
specialEvent = false; specialEvent = false;
eventName = ''; eventName = '';
mouseDragStartX: number;
blockchainScrollLeftInit: number;
@ViewChild('blockchainContainer') blockchainContainer: ElementRef;
constructor( constructor(
private websocketService: WebsocketService, private websocketService: WebsocketService,
@@ -50,4 +52,27 @@ export class StartComponent implements OnInit {
}); });
} }
onMouseDown(event: MouseEvent) {
this.mouseDragStartX = event.clientX;
this.blockchainScrollLeftInit = this.blockchainContainer.nativeElement.scrollLeft;
}
onDragStart(event: MouseEvent) { // Ignore Firefox annoying default drag behavior
event.preventDefault();
}
// We're catching the whole page event here because we still want to scroll blocks
// even if the mouse leave the blockchain blocks container. Same idea for mouseup below.
@HostListener('document:mousemove', ['$event'])
onMouseMove(event: MouseEvent): void {
if (this.mouseDragStartX != null) {
this.stateService.setBlockScrollingInProgress(true);
this.blockchainContainer.nativeElement.scrollLeft =
this.blockchainScrollLeftInit + this.mouseDragStartX - event.clientX
}
}
@HostListener('document:mouseup', [])
onMouseUp() {
this.mouseDragStartX = null;
this.stateService.setBlockScrollingInProgress(false);
}
} }

View File

@@ -89,6 +89,8 @@ export class StateService {
markBlock$ = new ReplaySubject<MarkBlockState>(); markBlock$ = new ReplaySubject<MarkBlockState>();
keyNavigation$ = new Subject<KeyboardEvent>(); keyNavigation$ = new Subject<KeyboardEvent>();
blockScrolling$: Subject<boolean> = new Subject<boolean>();
constructor( constructor(
@Inject(PLATFORM_ID) private platformId: any, @Inject(PLATFORM_ID) private platformId: any,
private router: Router, private router: Router,
@@ -176,4 +178,8 @@ export class StateService {
if (!prop) { return false; } if (!prop) { return false; }
return document[prop]; return document[prop];
} }
setBlockScrollingInProgress(value: boolean) {
this.blockScrolling$.next(value);
}
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,106 +0,0 @@
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";
CREATE TABLE `blocks` (
`height` int(11) NOT NULL,
`hash` varchar(65) NOT NULL,
`size` int(11) NOT NULL,
`weight` int(11) NOT NULL,
`minFee` int(11) NOT NULL,
`maxFee` int(11) NOT NULL,
`time` int(11) NOT NULL,
`fees` double NOT NULL,
`nTx` int(11) NOT NULL,
`medianFee` double NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `statistics` (
`id` int(11) NOT NULL,
`added` datetime NOT NULL,
`unconfirmed_transactions` int(11) UNSIGNED NOT NULL,
`tx_per_second` float UNSIGNED NOT NULL,
`vbytes_per_second` int(10) UNSIGNED NOT NULL,
`mempool_byte_weight` int(10) UNSIGNED NOT NULL,
`fee_data` longtext NOT NULL,
`total_fee` double UNSIGNED NOT NULL,
`vsize_1` int(11) NOT NULL,
`vsize_2` int(11) NOT NULL,
`vsize_3` int(11) NOT NULL,
`vsize_4` int(11) NOT NULL,
`vsize_5` int(11) NOT NULL,
`vsize_6` int(11) NOT NULL,
`vsize_8` int(11) NOT NULL,
`vsize_10` int(11) NOT NULL,
`vsize_12` int(11) NOT NULL,
`vsize_15` int(11) NOT NULL,
`vsize_20` int(11) NOT NULL,
`vsize_30` int(11) NOT NULL,
`vsize_40` int(11) NOT NULL,
`vsize_50` int(11) NOT NULL,
`vsize_60` int(11) NOT NULL,
`vsize_70` int(11) NOT NULL,
`vsize_80` int(11) NOT NULL,
`vsize_90` int(11) NOT NULL,
`vsize_100` int(11) NOT NULL,
`vsize_125` int(11) NOT NULL,
`vsize_150` int(11) NOT NULL,
`vsize_175` int(11) NOT NULL,
`vsize_200` int(11) NOT NULL,
`vsize_250` int(11) NOT NULL,
`vsize_300` int(11) NOT NULL,
`vsize_350` int(11) NOT NULL,
`vsize_400` int(11) NOT NULL,
`vsize_500` int(11) NOT NULL,
`vsize_600` int(11) NOT NULL,
`vsize_700` int(11) NOT NULL,
`vsize_800` int(11) NOT NULL,
`vsize_900` int(11) NOT NULL,
`vsize_1000` int(11) NOT NULL,
`vsize_1200` int(11) NOT NULL,
`vsize_1400` int(11) NOT NULL,
`vsize_1600` int(11) NOT NULL,
`vsize_1800` int(11) NOT NULL,
`vsize_2000` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `transactions` (
`blockheight` int(11) NOT NULL,
`txid` varchar(65) NOT NULL,
`fee` double NOT NULL,
`feePerVsize` double NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `blocks`
ADD PRIMARY KEY (`height`);
ALTER TABLE `statistics`
ADD PRIMARY KEY (`id`);
ALTER TABLE `transactions`
ADD PRIMARY KEY (`txid`),
ADD KEY `blockheight` (`blockheight`);
ALTER TABLE `statistics`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
CREATE TABLE `last_elements_block` (
`block` int(11) NOT NULL,
`datetime` int(11) NOT NULL,
`block_hash` varchar(65) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `last_elements_block` VALUES(0, 0, '');
CREATE TABLE `elements_pegs` (
`block` int(11) NOT NULL,
`datetime` int(11) NOT NULL,
`amount` bigint(20) NOT NULL,
`txid` varchar(65) NOT NULL,
`txindex` int(11) NOT NULL,
`bitcoinaddress` varchar(100) NOT NULL,
`bitcointxid` varchar(65) NOT NULL,
`bitcoinindex` int(11) NOT NULL,
`final_tx` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;