Compare commits
266 Commits
v2.2.2
...
v2.3.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
271726bf58 | ||
|
|
65e11c1aa1 | ||
|
|
0ccc992e4b | ||
|
|
52397cb341 | ||
|
|
f7a9b691a8 | ||
|
|
8f8c9c1fbc | ||
|
|
9e8ee4fedc | ||
|
|
30ffc0b56e | ||
|
|
053fb4d1b2 | ||
|
|
53cfda533d | ||
|
|
2d557195de | ||
|
|
ed9b46bae0 | ||
|
|
7c89dde5d4 | ||
|
|
87ca5b85d7 | ||
|
|
a2a43f4647 | ||
|
|
9ac77f7113 | ||
|
|
b6215dd54f | ||
|
|
69d5cfe98e | ||
|
|
73d9cd7ab7 | ||
|
|
84a6d29914 | ||
|
|
4633164f99 | ||
|
|
b22b63c9a2 | ||
|
|
40cd2a1ac3 | ||
|
|
e28f5cc403 | ||
|
|
1594fed853 | ||
|
|
41c61ef506 | ||
|
|
53b0bc0e48 | ||
|
|
e00234dfb9 | ||
|
|
8412e28073 | ||
|
|
10a110e682 | ||
|
|
1e40ec4fd0 | ||
|
|
9998a64fa5 | ||
|
|
d1e3c55a28 | ||
|
|
047220bd32 | ||
|
|
1ce05a3ac9 | ||
|
|
0271584b69 | ||
|
|
01da46daca | ||
|
|
73f558db6e | ||
|
|
feb8e35ec3 | ||
|
|
79e44479e9 | ||
|
|
9a39d3207f | ||
|
|
7e55e836fd | ||
|
|
571bf3fa64 | ||
|
|
0b1cf052a8 | ||
|
|
b301840bb8 | ||
|
|
a0e5a79ec4 | ||
|
|
8da89230c9 | ||
|
|
2408e81537 | ||
|
|
9377d80dbb | ||
|
|
8c200bc0e4 | ||
|
|
35ca8c846b | ||
|
|
4b1acfc77e | ||
|
|
8569523b89 | ||
|
|
9bef7da210 | ||
|
|
6f6b2a4355 | ||
|
|
783530b3de | ||
|
|
36777a8c5b | ||
|
|
638d6d896a | ||
|
|
2afb9d359f | ||
|
|
60a8d0ce6e | ||
|
|
e3aeb784ad | ||
|
|
8124274880 | ||
|
|
836f0f065d | ||
|
|
062fdb3658 | ||
|
|
7cfbd2c70d | ||
|
|
5b9ae2eaf5 | ||
|
|
d534c42c47 | ||
|
|
15a0644bd1 | ||
|
|
6ad4e655ea | ||
|
|
598920d6a9 | ||
|
|
d816e67ce4 | ||
|
|
662aafff57 | ||
|
|
cfec9345c8 | ||
|
|
9cb203f98a | ||
|
|
3bab50a43b | ||
|
|
0639ce9b07 | ||
|
|
34c9ca4197 | ||
|
|
14d015a904 | ||
|
|
38412753fe | ||
|
|
475f9344a0 | ||
|
|
d9cf6a2604 | ||
|
|
9eb159617f | ||
|
|
2dc930fad2 | ||
|
|
c478478f86 | ||
|
|
7a07f67be5 | ||
|
|
a3de75ebf4 | ||
|
|
46a2854f67 | ||
|
|
033f066abf | ||
|
|
97244c7b35 | ||
|
|
3d92369ed6 | ||
|
|
2bee46951c | ||
|
|
61ffcd0065 | ||
|
|
88e79c220f | ||
|
|
a2c21e9036 | ||
|
|
8a0316a562 | ||
|
|
055c1f2aa5 | ||
|
|
88e48df8a9 | ||
|
|
b81d296635 | ||
|
|
9f68f57d8a | ||
|
|
26a540a57c | ||
|
|
ad8398e3d4 | ||
|
|
0fabf8dbc3 | ||
|
|
cd63c6c0c1 | ||
|
|
c95f75254b | ||
|
|
9e4ad51b79 | ||
|
|
6e35102b3a | ||
|
|
d1e72c0cc0 | ||
|
|
1925023eb2 | ||
|
|
377eb0cae5 | ||
|
|
e33e4b1d71 | ||
|
|
b8f3c1f124 | ||
|
|
5333ec0b99 | ||
|
|
f761c5d966 | ||
|
|
3b6d07cace | ||
|
|
cbeeef3b2c | ||
|
|
5dab44e6c4 | ||
|
|
5139ffb4df | ||
|
|
06706be18f | ||
|
|
9264f3cf4f | ||
|
|
d6a9017267 | ||
|
|
bad75cfd4e | ||
|
|
68240e4f5c | ||
|
|
9d9ff6ed91 | ||
|
|
f19b84090a | ||
|
|
85c4574cbd | ||
|
|
e2c4e42c81 | ||
|
|
dd6c26b079 | ||
|
|
837992f7ea | ||
|
|
40914236d4 | ||
|
|
f52c36093e | ||
|
|
a9f4418e1a | ||
|
|
1a37c8b116 | ||
|
|
39d231bb3c | ||
|
|
4046c3176f | ||
|
|
c257fbfdcb | ||
|
|
1659be0d9f | ||
|
|
6f9762d50b | ||
|
|
9bf475bc97 | ||
|
|
e59a318cad | ||
|
|
57b64f64ad | ||
|
|
af3af5f099 | ||
|
|
fec603d5c5 | ||
|
|
ed2ebb1c70 | ||
|
|
14d2f8dd97 | ||
|
|
bf563cc195 | ||
|
|
f66e0a2c12 | ||
|
|
a43cd48795 | ||
|
|
44339daedf | ||
|
|
14b7b6427a | ||
|
|
a2e866d15a | ||
|
|
c2f288a861 | ||
|
|
e1c943d0a7 | ||
|
|
fa2d2e60b5 | ||
|
|
c919980652 | ||
|
|
b48389ae7d | ||
|
|
2bac7f9987 | ||
|
|
acf6fd9db5 | ||
|
|
74a9b65e81 | ||
|
|
822c840e54 | ||
|
|
6e93ef68fe | ||
|
|
3006deae6e | ||
|
|
740f5c2003 | ||
|
|
5c9d44e9eb | ||
|
|
88527b41e7 | ||
|
|
83cce0c3a7 | ||
|
|
e144d0c8e5 | ||
|
|
d72dbc1415 | ||
|
|
b857a7c37f | ||
|
|
c72c287b27 | ||
|
|
18e0a17d26 | ||
|
|
87eeef5d41 | ||
|
|
76a2fdeea7 | ||
|
|
792eb3727c | ||
|
|
2e0845847d | ||
|
|
8f774e55a8 | ||
|
|
28418640bb | ||
|
|
8aae5c1c9c | ||
|
|
92048964d1 | ||
|
|
b2140c2abe | ||
|
|
d0a8509194 | ||
|
|
aa0c3e6fed | ||
|
|
f0462114f3 | ||
|
|
9e0f9840aa | ||
|
|
d763c30f6a | ||
|
|
92b69657da | ||
|
|
d9ec0c1a36 | ||
|
|
4bf9f8b062 | ||
|
|
eefd8104bb | ||
|
|
16e807c4b0 | ||
|
|
461296e002 | ||
|
|
86c877c8e9 | ||
|
|
80fcceef73 | ||
|
|
b0e54818ae | ||
|
|
acbd7f0bde | ||
|
|
9a6efceb34 | ||
|
|
5ce7b55441 | ||
|
|
a3edaf17cc | ||
|
|
5695019216 | ||
|
|
7ab1ce8fc4 | ||
|
|
1f8ec2bd8e | ||
|
|
78b488466e | ||
|
|
66630743f6 | ||
|
|
ffa18bbe71 | ||
|
|
8cb1c5c88c | ||
|
|
bb07031362 | ||
|
|
31a0d44543 | ||
|
|
f90e19c767 | ||
|
|
800625d80e | ||
|
|
552540f510 | ||
|
|
bbee2dcb5b | ||
|
|
e4e338b05a | ||
|
|
061a55b236 | ||
|
|
9f0f9230fb | ||
|
|
40956c0a23 | ||
|
|
f29e35b325 | ||
|
|
d88efb8b0d | ||
|
|
b9489525c6 | ||
|
|
8ddcd298b0 | ||
|
|
69df6e4dcb | ||
|
|
f3c8e2134b | ||
|
|
0e25c52e67 | ||
|
|
60f41d3181 | ||
|
|
50c5244abf | ||
|
|
605c1a980c | ||
|
|
0d67bc36ee | ||
|
|
aa39bbd091 | ||
|
|
641d2ad028 | ||
|
|
d602b20f56 | ||
|
|
138f6e4e39 | ||
|
|
3e788ecbf9 | ||
|
|
2236c6d9a6 | ||
|
|
2a0a1b0213 | ||
|
|
e6e49fd5d6 | ||
|
|
f6d5f44469 | ||
|
|
51e09ff64f | ||
|
|
07ba2f6ecc | ||
|
|
bc42552bec | ||
|
|
c6b44a3be9 | ||
|
|
e1f4de0de3 | ||
|
|
d22dc0888a | ||
|
|
75fb27c690 | ||
|
|
401506a103 | ||
|
|
ab27ea28f0 | ||
|
|
6e579ce0b6 | ||
|
|
e7030cca32 | ||
|
|
2c496e9a50 | ||
|
|
014d6dee66 | ||
|
|
47ae306a75 | ||
|
|
8b8b06e6ab | ||
|
|
fa7a45421e | ||
|
|
d376ba1c61 | ||
|
|
388aa7fbe3 | ||
|
|
34695146ee | ||
|
|
9020c618f0 | ||
|
|
584d091d4e | ||
|
|
f434e50a2c | ||
|
|
1a7decb91d | ||
|
|
3574f8639e | ||
|
|
9b956ff88d | ||
|
|
1a98a14541 | ||
|
|
0088e58c14 | ||
|
|
3fad765269 | ||
|
|
2e122a9be1 | ||
|
|
2978d16148 | ||
|
|
fc58bcb3bc | ||
|
|
1696623e2f |
9
.github/workflows/cypress.yml
vendored
9
.github/workflows/cypress.yml
vendored
@@ -15,6 +15,12 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
- name: Setup node
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 16.10.0
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: frontend/package-lock.json
|
||||||
- name: ${{ matrix.browser }} browser tests (Mempool)
|
- name: ${{ matrix.browser }} browser tests (Mempool)
|
||||||
uses: cypress-io/github-action@v2
|
uses: cypress-io/github-action@v2
|
||||||
with:
|
with:
|
||||||
@@ -25,7 +31,6 @@ jobs:
|
|||||||
wait-on-timeout: 120
|
wait-on-timeout: 120
|
||||||
record: true
|
record: true
|
||||||
parallel: true
|
parallel: true
|
||||||
env: BASE_MODULE=mempool
|
|
||||||
group: Tests on ${{ matrix.browser }} (Mempool)
|
group: Tests on ${{ matrix.browser }} (Mempool)
|
||||||
browser: ${{ matrix.browser }}
|
browser: ${{ matrix.browser }}
|
||||||
ci-build-id: '${{ github.sha }}-${{ github.workflow }}-${{ github.event_name }}'
|
ci-build-id: '${{ github.sha }}-${{ github.workflow }}-${{ github.event_name }}'
|
||||||
@@ -46,7 +51,6 @@ jobs:
|
|||||||
record: true
|
record: true
|
||||||
parallel: true
|
parallel: true
|
||||||
spec: cypress/integration/liquid/liquid.spec.ts
|
spec: cypress/integration/liquid/liquid.spec.ts
|
||||||
env: BASE_MODULE=liquid
|
|
||||||
group: Tests on ${{ matrix.browser }} (Liquid)
|
group: Tests on ${{ matrix.browser }} (Liquid)
|
||||||
browser: ${{ matrix.browser }}
|
browser: ${{ matrix.browser }}
|
||||||
ci-build-id: '${{ github.sha }}-${{ github.workflow }}-${{ github.event_name }}'
|
ci-build-id: '${{ github.sha }}-${{ github.workflow }}-${{ github.event_name }}'
|
||||||
@@ -67,7 +71,6 @@ jobs:
|
|||||||
record: true
|
record: true
|
||||||
parallel: true
|
parallel: true
|
||||||
spec: cypress/integration/bisq/bisq.spec.ts
|
spec: cypress/integration/bisq/bisq.spec.ts
|
||||||
env: BASE_MODULE=bisq
|
|
||||||
group: Tests on ${{ matrix.browser }} (Bisq)
|
group: Tests on ${{ matrix.browser }} (Bisq)
|
||||||
browser: ${{ matrix.browser }}
|
browser: ${{ matrix.browser }}
|
||||||
ci-build-id: '${{ github.sha }}-${{ github.workflow }}-${{ github.event_name }}'
|
ci-build-id: '${{ github.sha }}-${{ github.workflow }}-${{ github.event_name }}'
|
||||||
|
|||||||
40
README.md
40
README.md
@@ -1,8 +1,8 @@
|
|||||||
# The Mempool Open Source Project
|
# The Mempool Open Source Project™
|
||||||
|
|
||||||
Mempool is the fully featured visualizer, explorer, and API service running on [mempool.space](https://mempool.space/), an open source project developed and operated for the benefit of the Bitcoin community, with a focus on the emerging transaction fee market to help our transition into a multi-layer ecosystem.
|
Mempool is the fully featured visualizer, explorer, and API service running on [mempool.space](https://mempool.space/), an open source project developed and operated for the benefit of the Bitcoin community, with a focus on the emerging transaction fee market to help our transition into a multi-layer ecosystem.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Installation Methods
|
## Installation Methods
|
||||||
|
|
||||||
@@ -20,11 +20,11 @@ The following instructions are for a manual installation on Linux or FreeBSD. Th
|
|||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
* Bitcoin Core (no pruning, txindex=1)
|
* [Bitcoin](https://github.com/bitcoin/bitcoin)
|
||||||
* Electrum Server (romanz/electrs)
|
* [Electrum](https://github.com/romanz/electrs)
|
||||||
* NodeJS (official stable LTS)
|
* [NodeJS](https://github.com/nodejs/node)
|
||||||
* MariaDB (default config)
|
* [MariaDB](https://github.com/mariadb/server)
|
||||||
* Nginx (use supplied nginx.conf and nginx-mempool.conf)
|
* [Nginx](https://github.com/nginx/nginx)
|
||||||
|
|
||||||
## Mempool
|
## Mempool
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ Clone the mempool repo, and checkout the latest release tag:
|
|||||||
Enable RPC and txindex in `bitcoin.conf`:
|
Enable RPC and txindex in `bitcoin.conf`:
|
||||||
```bash
|
```bash
|
||||||
rpcuser=mempool
|
rpcuser=mempool
|
||||||
rpcpassword=71b61986da5b03a5694d7c7d5165ece5
|
rpcpassword=mempool
|
||||||
txindex=1
|
txindex=1
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ Install MariaDB from OS package manager:
|
|||||||
|
|
||||||
# macOS
|
# macOS
|
||||||
brew install mariadb
|
brew install mariadb
|
||||||
brew services start mariadb
|
mysql.server start
|
||||||
```
|
```
|
||||||
|
|
||||||
Create database and grant privileges:
|
Create database and grant privileges:
|
||||||
@@ -80,7 +80,7 @@ Install mempool dependencies from npm and build the backend:
|
|||||||
```bash
|
```bash
|
||||||
# backend
|
# backend
|
||||||
cd backend
|
cd backend
|
||||||
npm install
|
npm install --prod
|
||||||
npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -96,18 +96,18 @@ Edit `mempool-config.json` to add your Bitcoin Core node RPC credentials:
|
|||||||
"MEMPOOL": {
|
"MEMPOOL": {
|
||||||
"NETWORK": "mainnet",
|
"NETWORK": "mainnet",
|
||||||
"BACKEND": "electrum",
|
"BACKEND": "electrum",
|
||||||
"HTTP_PORT": 8999,
|
"HTTP_PORT": 8999
|
||||||
"API_URL_PREFIX": "/api/v1/",
|
|
||||||
"POLL_RATE_MS": 2000
|
|
||||||
},
|
},
|
||||||
"CORE_RPC": {
|
"CORE_RPC": {
|
||||||
|
"HOST": "127.0.0.1",
|
||||||
|
"PORT": 8332,
|
||||||
"USERNAME": "mempool",
|
"USERNAME": "mempool",
|
||||||
"PASSWORD": "71b61986da5b03a5694d7c7d5165ece5"
|
"PASSWORD": "mempool"
|
||||||
},
|
},
|
||||||
"ELECTRUM": {
|
"ELECTRUM": {
|
||||||
"HOST": "127.0.0.1",
|
"HOST": "127.0.0.1",
|
||||||
"PORT": 50002,
|
"PORT": 50002,
|
||||||
"TLS_ENABLED": true,
|
"TLS_ENABLED": true
|
||||||
},
|
},
|
||||||
"DATABASE": {
|
"DATABASE": {
|
||||||
"ENABLED": true,
|
"ENABLED": true,
|
||||||
@@ -116,10 +116,6 @@ Edit `mempool-config.json` to add your Bitcoin Core node RPC credentials:
|
|||||||
"USERNAME": "mempool",
|
"USERNAME": "mempool",
|
||||||
"PASSWORD": "mempool",
|
"PASSWORD": "mempool",
|
||||||
"DATABASE": "mempool"
|
"DATABASE": "mempool"
|
||||||
},
|
|
||||||
"STATISTICS": {
|
|
||||||
"ENABLED": true,
|
|
||||||
"TX_PER_SECOND_SAMPLE_PERIOD": 150
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -160,14 +156,14 @@ Install mempool dependencies from npm and build the frontend static HTML/CSS/JS:
|
|||||||
```bash
|
```bash
|
||||||
# frontend
|
# frontend
|
||||||
cd frontend
|
cd frontend
|
||||||
npm install
|
npm install --prod
|
||||||
npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
Install the output into nginx webroot folder:
|
Install the output into nginx webroot folder:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo rsync -av --delete dist/mempool /var/www/
|
sudo rsync -av --delete dist/mempool/ /var/www/
|
||||||
```
|
```
|
||||||
|
|
||||||
## nginx + certbot
|
## nginx + certbot
|
||||||
@@ -176,7 +172,7 @@ Install the supplied nginx.conf and nginx-mempool.conf in /etc/nginx
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# install nginx and certbot
|
# install nginx and certbot
|
||||||
apt-get install -y nginx python-certbot-nginx
|
apt-get install -y nginx python3-certbot-nginx
|
||||||
|
|
||||||
# install the mempool configuration for nginx
|
# install the mempool configuration for nginx
|
||||||
cp nginx.conf nginx-mempool.conf /etc/nginx/
|
cp nginx.conf nginx-mempool.conf /etc/nginx/
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
"BLOCK_WEIGHT_UNITS": 4000000,
|
"BLOCK_WEIGHT_UNITS": 4000000,
|
||||||
"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
|
||||||
},
|
},
|
||||||
"CORE_RPC": {
|
"CORE_RPC": {
|
||||||
"HOST": "127.0.0.1",
|
"HOST": "127.0.0.1",
|
||||||
@@ -28,8 +29,7 @@
|
|||||||
"ESPLORA": {
|
"ESPLORA": {
|
||||||
"REST_API_URL": "http://127.0.0.1:3000"
|
"REST_API_URL": "http://127.0.0.1:3000"
|
||||||
},
|
},
|
||||||
"CORE_RPC_MINFEE": {
|
"SECOND_CORE_RPC": {
|
||||||
"ENABLED": false,
|
|
||||||
"HOST": "127.0.0.1",
|
"HOST": "127.0.0.1",
|
||||||
"PORT": 8332,
|
"PORT": 8332,
|
||||||
"USERNAME": "mempool",
|
"USERNAME": "mempool",
|
||||||
|
|||||||
523
backend/package-lock.json
generated
523
backend/package-lock.json
generated
@@ -1,32 +1,32 @@
|
|||||||
{
|
{
|
||||||
"name": "mempool-backend",
|
"name": "mempool-backend",
|
||||||
"version": "2.2.2",
|
"version": "2.3.0-dev",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "mempool-backend",
|
"name": "mempool-backend",
|
||||||
"version": "2.2.2",
|
"version": "2.3.0-dev",
|
||||||
"license": "GNU Affero General Public License v3.0",
|
"license": "GNU Affero General Public License v3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mempool/bitcoin": "^3.0.3",
|
"@mempool/bitcoin": "^3.0.3",
|
||||||
"@mempool/electrum-client": "^1.1.7",
|
"@mempool/electrum-client": "^1.1.7",
|
||||||
"axios": "^0.21.1",
|
"@types/ws": "8.2.2",
|
||||||
"bitcoinjs-lib": "^5.2.0",
|
"axios": "0.24.0",
|
||||||
|
"bitcoinjs-lib": "6.0.1",
|
||||||
"crypto-js": "^4.0.0",
|
"crypto-js": "^4.0.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"locutus": "^2.0.12",
|
"locutus": "^2.0.12",
|
||||||
"mysql2": "2.2.5",
|
"mysql2": "2.3.3",
|
||||||
"node-worker-threads-pool": "^1.4.3",
|
"node-worker-threads-pool": "^1.4.3",
|
||||||
"ws": "^7.4.6"
|
"typescript": "4.4.4",
|
||||||
|
"ws": "8.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/compression": "^1.0.1",
|
"@types/compression": "^1.0.1",
|
||||||
"@types/express": "^4.17.2",
|
"@types/express": "^4.17.2",
|
||||||
"@types/locutus": "^0.0.6",
|
"@types/locutus": "^0.0.6",
|
||||||
"@types/ws": "^7.4.4",
|
"tslint": "^6.1.0"
|
||||||
"tslint": "^6.1.0",
|
|
||||||
"typescript": "4.4.2"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
@@ -137,8 +137,7 @@
|
|||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "14.14.20",
|
"version": "14.14.20",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.20.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.20.tgz",
|
||||||
"integrity": "sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A==",
|
"integrity": "sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@types/qs": {
|
"node_modules/@types/qs": {
|
||||||
"version": "6.9.5",
|
"version": "6.9.5",
|
||||||
@@ -163,11 +162,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/ws": {
|
"node_modules/@types/ws": {
|
||||||
"version": "7.4.4",
|
"version": "8.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz",
|
||||||
"integrity": "sha512-d/7W23JAXPodQNbOZNXvl2K+bqAQrCMwlh/nuQsPSQk6Fq0opHoPrUw43aHsvSbIiQPr8Of2hkFbnz1XBFVyZQ==",
|
"integrity": "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
@@ -211,11 +208,11 @@
|
|||||||
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
"node_modules/axios": {
|
||||||
"version": "0.21.1",
|
"version": "0.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
|
||||||
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
|
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.10.0"
|
"follow-redirects": "^1.14.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
@@ -225,25 +222,17 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/base-x": {
|
"node_modules/base-x": {
|
||||||
"version": "3.0.8",
|
"version": "3.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz",
|
||||||
"integrity": "sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==",
|
"integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safe-buffer": "^5.0.1"
|
"safe-buffer": "^5.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/bech32": {
|
"node_modules/bech32": {
|
||||||
"version": "1.1.4",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz",
|
||||||
"integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ=="
|
"integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg=="
|
||||||
},
|
|
||||||
"node_modules/bindings": {
|
|
||||||
"version": "1.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
|
||||||
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"file-uri-to-path": "1.0.0"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"node_modules/bip174": {
|
"node_modules/bip174": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
@@ -253,71 +242,23 @@
|
|||||||
"node": ">=8.0.0"
|
"node": ">=8.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/bip32": {
|
|
||||||
"version": "2.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.6.tgz",
|
|
||||||
"integrity": "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/node": "10.12.18",
|
|
||||||
"bs58check": "^2.1.1",
|
|
||||||
"create-hash": "^1.2.0",
|
|
||||||
"create-hmac": "^1.1.7",
|
|
||||||
"tiny-secp256k1": "^1.1.3",
|
|
||||||
"typeforce": "^1.11.5",
|
|
||||||
"wif": "^2.0.6"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/bip32/node_modules/@types/node": {
|
|
||||||
"version": "10.12.18",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz",
|
|
||||||
"integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ=="
|
|
||||||
},
|
|
||||||
"node_modules/bip66": {
|
|
||||||
"version": "1.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz",
|
|
||||||
"integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=",
|
|
||||||
"dependencies": {
|
|
||||||
"safe-buffer": "^5.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/bitcoin-ops": {
|
|
||||||
"version": "1.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/bitcoin-ops/-/bitcoin-ops-1.4.1.tgz",
|
|
||||||
"integrity": "sha512-pef6gxZFztEhaE9RY9HmWVmiIHqCb2OyS4HPKkpc6CIiiOa3Qmuoylxc5P2EkU3w+5eTSifI9SEZC88idAIGow=="
|
|
||||||
},
|
|
||||||
"node_modules/bitcoinjs-lib": {
|
"node_modules/bitcoinjs-lib": {
|
||||||
"version": "5.2.0",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.0.1.tgz",
|
||||||
"integrity": "sha512-5DcLxGUDejgNBYcieMIUfjORtUeNWl828VWLHJGVKZCb4zIS1oOySTUr0LGmcqJBQgTBz3bGbRQla4FgrdQEIQ==",
|
"integrity": "sha512-x/7D4jDj/MMkmO6t3p2CSDXTqpwZ/jRsRiJDmaiXabrR9XRo7jwby8HRn7EyK1h24rKFFI7vI0ay4czl6bDOZQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bech32": "^1.1.2",
|
"bech32": "^2.0.0",
|
||||||
"bip174": "^2.0.1",
|
"bip174": "^2.0.1",
|
||||||
"bip32": "^2.0.4",
|
"bs58check": "^2.1.2",
|
||||||
"bip66": "^1.1.0",
|
|
||||||
"bitcoin-ops": "^1.4.0",
|
|
||||||
"bs58check": "^2.0.0",
|
|
||||||
"create-hash": "^1.1.0",
|
"create-hash": "^1.1.0",
|
||||||
"create-hmac": "^1.1.3",
|
|
||||||
"merkle-lib": "^2.0.10",
|
|
||||||
"pushdata-bitcoin": "^1.0.1",
|
|
||||||
"randombytes": "^2.0.1",
|
|
||||||
"tiny-secp256k1": "^1.1.1",
|
|
||||||
"typeforce": "^1.11.3",
|
"typeforce": "^1.11.3",
|
||||||
"varuint-bitcoin": "^1.0.4",
|
"varuint-bitcoin": "^1.1.2",
|
||||||
"wif": "^2.0.1"
|
"wif": "^2.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.0.0"
|
"node": ">=8.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/bn.js": {
|
|
||||||
"version": "4.11.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
|
|
||||||
"integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw=="
|
|
||||||
},
|
|
||||||
"node_modules/body-parser": {
|
"node_modules/body-parser": {
|
||||||
"version": "1.19.0",
|
"version": "1.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
||||||
@@ -348,11 +289,6 @@
|
|||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/brorand": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
|
|
||||||
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
|
|
||||||
},
|
|
||||||
"node_modules/bs58": {
|
"node_modules/bs58": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
|
||||||
@@ -487,19 +423,6 @@
|
|||||||
"sha.js": "^2.4.0"
|
"sha.js": "^2.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/create-hmac": {
|
|
||||||
"version": "1.1.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
|
|
||||||
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
|
|
||||||
"dependencies": {
|
|
||||||
"cipher-base": "^1.0.3",
|
|
||||||
"create-hash": "^1.1.0",
|
|
||||||
"inherits": "^2.0.1",
|
|
||||||
"ripemd160": "^2.0.0",
|
|
||||||
"safe-buffer": "^5.0.1",
|
|
||||||
"sha.js": "^2.4.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/crypto-js": {
|
"node_modules/crypto-js": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz",
|
||||||
@@ -514,9 +437,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/denque": {
|
"node_modules/denque": {
|
||||||
"version": "1.5.0",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz",
|
||||||
"integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==",
|
"integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
}
|
}
|
||||||
@@ -548,20 +471,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
||||||
},
|
},
|
||||||
"node_modules/elliptic": {
|
|
||||||
"version": "6.5.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
|
|
||||||
"integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"bn.js": "^4.11.9",
|
|
||||||
"brorand": "^1.1.0",
|
|
||||||
"hash.js": "^1.0.0",
|
|
||||||
"hmac-drbg": "^1.0.1",
|
|
||||||
"inherits": "^2.0.4",
|
|
||||||
"minimalistic-assert": "^1.0.1",
|
|
||||||
"minimalistic-crypto-utils": "^1.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/encodeurl": {
|
"node_modules/encodeurl": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||||
@@ -650,11 +559,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||||
},
|
},
|
||||||
"node_modules/file-uri-to-path": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
|
|
||||||
},
|
|
||||||
"node_modules/finalhandler": {
|
"node_modules/finalhandler": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
|
||||||
@@ -673,9 +577,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/follow-redirects": {
|
"node_modules/follow-redirects": {
|
||||||
"version": "1.13.1",
|
"version": "1.14.6",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz",
|
||||||
"integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==",
|
"integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
@@ -781,25 +685,6 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/hash.js": {
|
|
||||||
"version": "1.1.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
|
|
||||||
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
|
|
||||||
"dependencies": {
|
|
||||||
"inherits": "^2.0.3",
|
|
||||||
"minimalistic-assert": "^1.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/hmac-drbg": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
|
|
||||||
"dependencies": {
|
|
||||||
"hash.js": "^1.0.3",
|
|
||||||
"minimalistic-assert": "^1.0.0",
|
|
||||||
"minimalistic-crypto-utils": "^1.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/http-errors": {
|
"node_modules/http-errors": {
|
||||||
"version": "1.7.2",
|
"version": "1.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
|
||||||
@@ -937,11 +822,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||||
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
|
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
|
||||||
},
|
},
|
||||||
"node_modules/merkle-lib": {
|
|
||||||
"version": "2.0.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/merkle-lib/-/merkle-lib-2.0.10.tgz",
|
|
||||||
"integrity": "sha1-grjbrnXieneFOItz+ddyXQ9vMyY="
|
|
||||||
},
|
|
||||||
"node_modules/methods": {
|
"node_modules/methods": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||||
@@ -980,16 +860,6 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/minimalistic-assert": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
|
|
||||||
},
|
|
||||||
"node_modules/minimalistic-crypto-utils": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
|
|
||||||
},
|
|
||||||
"node_modules/minimatch": {
|
"node_modules/minimatch": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
@@ -1026,13 +896,13 @@
|
|||||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
},
|
},
|
||||||
"node_modules/mysql2": {
|
"node_modules/mysql2": {
|
||||||
"version": "2.2.5",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz",
|
||||||
"integrity": "sha512-XRqPNxcZTpmFdXbJqb+/CtYVLCx14x1RTeNMD4954L331APu75IC74GDqnZMEt1kwaXy6TySo55rF2F3YJS78g==",
|
"integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"denque": "^1.4.1",
|
"denque": "^2.0.1",
|
||||||
"generate-function": "^2.3.1",
|
"generate-function": "^2.3.1",
|
||||||
"iconv-lite": "^0.6.2",
|
"iconv-lite": "^0.6.3",
|
||||||
"long": "^4.0.0",
|
"long": "^4.0.0",
|
||||||
"lru-cache": "^6.0.0",
|
"lru-cache": "^6.0.0",
|
||||||
"named-placeholders": "^1.1.2",
|
"named-placeholders": "^1.1.2",
|
||||||
@@ -1044,9 +914,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mysql2/node_modules/iconv-lite": {
|
"node_modules/mysql2/node_modules/iconv-lite": {
|
||||||
"version": "0.6.2",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
"integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==",
|
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||||
},
|
},
|
||||||
@@ -1079,11 +949,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
||||||
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
|
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
|
||||||
},
|
},
|
||||||
"node_modules/nan": {
|
|
||||||
"version": "2.14.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
|
|
||||||
"integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ=="
|
|
||||||
},
|
|
||||||
"node_modules/negotiator": {
|
"node_modules/negotiator": {
|
||||||
"version": "0.6.2",
|
"version": "0.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||||
@@ -1135,9 +1000,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/path-parse": {
|
"node_modules/path-parse": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
|
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/path-to-regexp": {
|
"node_modules/path-to-regexp": {
|
||||||
@@ -1162,14 +1027,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
||||||
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
|
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
|
||||||
},
|
},
|
||||||
"node_modules/pushdata-bitcoin": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/pushdata-bitcoin/-/pushdata-bitcoin-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-FZMdPNlnreUiBvUjqnMxrvfUOvc=",
|
|
||||||
"dependencies": {
|
|
||||||
"bitcoin-ops": "^1.3.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/qs": {
|
"node_modules/qs": {
|
||||||
"version": "6.7.0",
|
"version": "6.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
||||||
@@ -1178,14 +1035,6 @@
|
|||||||
"node": ">=0.6"
|
"node": ">=0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/randombytes": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"safe-buffer": "^5.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/range-parser": {
|
"node_modules/range-parser": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||||
@@ -1382,22 +1231,6 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tiny-secp256k1": {
|
|
||||||
"version": "1.1.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz",
|
|
||||||
"integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==",
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"dependencies": {
|
|
||||||
"bindings": "^1.3.0",
|
|
||||||
"bn.js": "^4.11.8",
|
|
||||||
"create-hmac": "^1.1.7",
|
|
||||||
"elliptic": "^6.4.0",
|
|
||||||
"nan": "^2.13.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/toidentifier": {
|
"node_modules/toidentifier": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||||
@@ -1473,11 +1306,9 @@
|
|||||||
"integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
|
"integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "4.4.2",
|
"version": "4.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz",
|
||||||
"integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==",
|
"integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==",
|
||||||
"dev": true,
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -1538,11 +1369,11 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/ws": {
|
"node_modules/ws": {
|
||||||
"version": "7.4.6",
|
"version": "8.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.3.0.tgz",
|
||||||
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
|
"integrity": "sha512-Gs5EZtpqZzLvmIM59w4igITU57lrtYVFneaa434VROv4thzJyV6UjIL3D42lslWlI+D4KzLYnxSwtfuiO79sNw==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.3.0"
|
"node": ">=10.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"bufferutil": "^4.0.1",
|
"bufferutil": "^4.0.1",
|
||||||
@@ -1666,8 +1497,7 @@
|
|||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "14.14.20",
|
"version": "14.14.20",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.20.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.20.tgz",
|
||||||
"integrity": "sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A==",
|
"integrity": "sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"@types/qs": {
|
"@types/qs": {
|
||||||
"version": "6.9.5",
|
"version": "6.9.5",
|
||||||
@@ -1692,10 +1522,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/ws": {
|
"@types/ws": {
|
||||||
"version": "7.4.4",
|
"version": "8.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz",
|
||||||
"integrity": "sha512-d/7W23JAXPodQNbOZNXvl2K+bqAQrCMwlh/nuQsPSQk6Fq0opHoPrUw43aHsvSbIiQPr8Of2hkFbnz1XBFVyZQ==",
|
"integrity": "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
@@ -1733,11 +1562,11 @@
|
|||||||
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
||||||
},
|
},
|
||||||
"axios": {
|
"axios": {
|
||||||
"version": "0.21.1",
|
"version": "0.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
|
||||||
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
|
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"follow-redirects": "^1.10.0"
|
"follow-redirects": "^1.14.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
@@ -1747,92 +1576,37 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"base-x": {
|
"base-x": {
|
||||||
"version": "3.0.8",
|
"version": "3.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz",
|
||||||
"integrity": "sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==",
|
"integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "^5.0.1"
|
"safe-buffer": "^5.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bech32": {
|
"bech32": {
|
||||||
"version": "1.1.4",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz",
|
||||||
"integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ=="
|
"integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg=="
|
||||||
},
|
|
||||||
"bindings": {
|
|
||||||
"version": "1.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
|
||||||
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
|
|
||||||
"requires": {
|
|
||||||
"file-uri-to-path": "1.0.0"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"bip174": {
|
"bip174": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/bip174/-/bip174-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/bip174/-/bip174-2.0.1.tgz",
|
||||||
"integrity": "sha512-i3X26uKJOkDTAalYAp0Er+qGMDhrbbh2o93/xiPyAN2s25KrClSpe3VXo/7mNJoqA5qfko8rLS2l3RWZgYmjKQ=="
|
"integrity": "sha512-i3X26uKJOkDTAalYAp0Er+qGMDhrbbh2o93/xiPyAN2s25KrClSpe3VXo/7mNJoqA5qfko8rLS2l3RWZgYmjKQ=="
|
||||||
},
|
},
|
||||||
"bip32": {
|
|
||||||
"version": "2.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.6.tgz",
|
|
||||||
"integrity": "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==",
|
|
||||||
"requires": {
|
|
||||||
"@types/node": "10.12.18",
|
|
||||||
"bs58check": "^2.1.1",
|
|
||||||
"create-hash": "^1.2.0",
|
|
||||||
"create-hmac": "^1.1.7",
|
|
||||||
"tiny-secp256k1": "^1.1.3",
|
|
||||||
"typeforce": "^1.11.5",
|
|
||||||
"wif": "^2.0.6"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@types/node": {
|
|
||||||
"version": "10.12.18",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz",
|
|
||||||
"integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"bip66": {
|
|
||||||
"version": "1.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz",
|
|
||||||
"integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=",
|
|
||||||
"requires": {
|
|
||||||
"safe-buffer": "^5.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"bitcoin-ops": {
|
|
||||||
"version": "1.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/bitcoin-ops/-/bitcoin-ops-1.4.1.tgz",
|
|
||||||
"integrity": "sha512-pef6gxZFztEhaE9RY9HmWVmiIHqCb2OyS4HPKkpc6CIiiOa3Qmuoylxc5P2EkU3w+5eTSifI9SEZC88idAIGow=="
|
|
||||||
},
|
|
||||||
"bitcoinjs-lib": {
|
"bitcoinjs-lib": {
|
||||||
"version": "5.2.0",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.0.1.tgz",
|
||||||
"integrity": "sha512-5DcLxGUDejgNBYcieMIUfjORtUeNWl828VWLHJGVKZCb4zIS1oOySTUr0LGmcqJBQgTBz3bGbRQla4FgrdQEIQ==",
|
"integrity": "sha512-x/7D4jDj/MMkmO6t3p2CSDXTqpwZ/jRsRiJDmaiXabrR9XRo7jwby8HRn7EyK1h24rKFFI7vI0ay4czl6bDOZQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"bech32": "^1.1.2",
|
"bech32": "^2.0.0",
|
||||||
"bip174": "^2.0.1",
|
"bip174": "^2.0.1",
|
||||||
"bip32": "^2.0.4",
|
"bs58check": "^2.1.2",
|
||||||
"bip66": "^1.1.0",
|
|
||||||
"bitcoin-ops": "^1.4.0",
|
|
||||||
"bs58check": "^2.0.0",
|
|
||||||
"create-hash": "^1.1.0",
|
"create-hash": "^1.1.0",
|
||||||
"create-hmac": "^1.1.3",
|
|
||||||
"merkle-lib": "^2.0.10",
|
|
||||||
"pushdata-bitcoin": "^1.0.1",
|
|
||||||
"randombytes": "^2.0.1",
|
|
||||||
"tiny-secp256k1": "^1.1.1",
|
|
||||||
"typeforce": "^1.11.3",
|
"typeforce": "^1.11.3",
|
||||||
"varuint-bitcoin": "^1.0.4",
|
"varuint-bitcoin": "^1.1.2",
|
||||||
"wif": "^2.0.1"
|
"wif": "^2.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bn.js": {
|
|
||||||
"version": "4.11.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
|
|
||||||
"integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw=="
|
|
||||||
},
|
|
||||||
"body-parser": {
|
"body-parser": {
|
||||||
"version": "1.19.0",
|
"version": "1.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
||||||
@@ -1860,11 +1634,6 @@
|
|||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"brorand": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
|
|
||||||
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
|
|
||||||
},
|
|
||||||
"bs58": {
|
"bs58": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
|
||||||
@@ -1983,19 +1752,6 @@
|
|||||||
"sha.js": "^2.4.0"
|
"sha.js": "^2.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"create-hmac": {
|
|
||||||
"version": "1.1.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
|
|
||||||
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
|
|
||||||
"requires": {
|
|
||||||
"cipher-base": "^1.0.3",
|
|
||||||
"create-hash": "^1.1.0",
|
|
||||||
"inherits": "^2.0.1",
|
|
||||||
"ripemd160": "^2.0.0",
|
|
||||||
"safe-buffer": "^5.0.1",
|
|
||||||
"sha.js": "^2.4.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"crypto-js": {
|
"crypto-js": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz",
|
||||||
@@ -2010,9 +1766,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"denque": {
|
"denque": {
|
||||||
"version": "1.5.0",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz",
|
||||||
"integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ=="
|
"integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ=="
|
||||||
},
|
},
|
||||||
"depd": {
|
"depd": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
@@ -2035,20 +1791,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
||||||
},
|
},
|
||||||
"elliptic": {
|
|
||||||
"version": "6.5.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
|
|
||||||
"integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
|
|
||||||
"requires": {
|
|
||||||
"bn.js": "^4.11.9",
|
|
||||||
"brorand": "^1.1.0",
|
|
||||||
"hash.js": "^1.0.0",
|
|
||||||
"hmac-drbg": "^1.0.1",
|
|
||||||
"inherits": "^2.0.4",
|
|
||||||
"minimalistic-assert": "^1.0.1",
|
|
||||||
"minimalistic-crypto-utils": "^1.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"encodeurl": {
|
"encodeurl": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||||
@@ -2120,11 +1862,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"file-uri-to-path": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
|
|
||||||
},
|
|
||||||
"finalhandler": {
|
"finalhandler": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
|
||||||
@@ -2140,9 +1877,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"follow-redirects": {
|
"follow-redirects": {
|
||||||
"version": "1.13.1",
|
"version": "1.14.6",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz",
|
||||||
"integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg=="
|
"integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A=="
|
||||||
},
|
},
|
||||||
"forwarded": {
|
"forwarded": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
@@ -2213,25 +1950,6 @@
|
|||||||
"safe-buffer": "^5.2.0"
|
"safe-buffer": "^5.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hash.js": {
|
|
||||||
"version": "1.1.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
|
|
||||||
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
|
|
||||||
"requires": {
|
|
||||||
"inherits": "^2.0.3",
|
|
||||||
"minimalistic-assert": "^1.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hmac-drbg": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
|
|
||||||
"requires": {
|
|
||||||
"hash.js": "^1.0.3",
|
|
||||||
"minimalistic-assert": "^1.0.0",
|
|
||||||
"minimalistic-crypto-utils": "^1.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"http-errors": {
|
"http-errors": {
|
||||||
"version": "1.7.2",
|
"version": "1.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
|
||||||
@@ -2347,11 +2065,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||||
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
|
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
|
||||||
},
|
},
|
||||||
"merkle-lib": {
|
|
||||||
"version": "2.0.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/merkle-lib/-/merkle-lib-2.0.10.tgz",
|
|
||||||
"integrity": "sha1-grjbrnXieneFOItz+ddyXQ9vMyY="
|
|
||||||
},
|
|
||||||
"methods": {
|
"methods": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||||
@@ -2375,16 +2088,6 @@
|
|||||||
"mime-db": "1.45.0"
|
"mime-db": "1.45.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimalistic-assert": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
|
|
||||||
},
|
|
||||||
"minimalistic-crypto-utils": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
|
|
||||||
},
|
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
@@ -2415,13 +2118,13 @@
|
|||||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
},
|
},
|
||||||
"mysql2": {
|
"mysql2": {
|
||||||
"version": "2.2.5",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz",
|
||||||
"integrity": "sha512-XRqPNxcZTpmFdXbJqb+/CtYVLCx14x1RTeNMD4954L331APu75IC74GDqnZMEt1kwaXy6TySo55rF2F3YJS78g==",
|
"integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"denque": "^1.4.1",
|
"denque": "^2.0.1",
|
||||||
"generate-function": "^2.3.1",
|
"generate-function": "^2.3.1",
|
||||||
"iconv-lite": "^0.6.2",
|
"iconv-lite": "^0.6.3",
|
||||||
"long": "^4.0.0",
|
"long": "^4.0.0",
|
||||||
"lru-cache": "^6.0.0",
|
"lru-cache": "^6.0.0",
|
||||||
"named-placeholders": "^1.1.2",
|
"named-placeholders": "^1.1.2",
|
||||||
@@ -2430,9 +2133,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"iconv-lite": {
|
"iconv-lite": {
|
||||||
"version": "0.6.2",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
"integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==",
|
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||||
}
|
}
|
||||||
@@ -2463,11 +2166,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nan": {
|
|
||||||
"version": "2.14.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
|
|
||||||
"integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ=="
|
|
||||||
},
|
|
||||||
"negotiator": {
|
"negotiator": {
|
||||||
"version": "0.6.2",
|
"version": "0.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||||
@@ -2507,9 +2205,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"path-parse": {
|
"path-parse": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
|
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"path-to-regexp": {
|
"path-to-regexp": {
|
||||||
@@ -2531,27 +2229,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
||||||
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
|
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
|
||||||
},
|
},
|
||||||
"pushdata-bitcoin": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/pushdata-bitcoin/-/pushdata-bitcoin-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-FZMdPNlnreUiBvUjqnMxrvfUOvc=",
|
|
||||||
"requires": {
|
|
||||||
"bitcoin-ops": "^1.3.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"qs": {
|
"qs": {
|
||||||
"version": "6.7.0",
|
"version": "6.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
||||||
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
|
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
|
||||||
},
|
},
|
||||||
"randombytes": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
|
|
||||||
"requires": {
|
|
||||||
"safe-buffer": "^5.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"range-parser": {
|
"range-parser": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||||
@@ -2703,18 +2385,6 @@
|
|||||||
"has-flag": "^3.0.0"
|
"has-flag": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tiny-secp256k1": {
|
|
||||||
"version": "1.1.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz",
|
|
||||||
"integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==",
|
|
||||||
"requires": {
|
|
||||||
"bindings": "^1.3.0",
|
|
||||||
"bn.js": "^4.11.8",
|
|
||||||
"create-hmac": "^1.1.7",
|
|
||||||
"elliptic": "^6.4.0",
|
|
||||||
"nan": "^2.13.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"toidentifier": {
|
"toidentifier": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||||
@@ -2771,10 +2441,9 @@
|
|||||||
"integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
|
"integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
|
||||||
},
|
},
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "4.4.2",
|
"version": "4.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz",
|
||||||
"integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==",
|
"integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"unpipe": {
|
"unpipe": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@@ -2819,9 +2488,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "7.4.6",
|
"version": "8.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.3.0.tgz",
|
||||||
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
|
"integrity": "sha512-Gs5EZtpqZzLvmIM59w4igITU57lrtYVFneaa434VROv4thzJyV6UjIL3D42lslWlI+D4KzLYnxSwtfuiO79sNw==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"yallist": {
|
"yallist": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mempool-backend",
|
"name": "mempool-backend",
|
||||||
"version": "2.2.2",
|
"version": "2.3.0-dev",
|
||||||
"description": "Bitcoin mempool visualizer and blockchain explorer backend",
|
"description": "Bitcoin mempool visualizer and blockchain explorer backend",
|
||||||
"license": "GNU Affero General Public License v3.0",
|
"license": "GNU Affero General Public License v3.0",
|
||||||
"homepage": "https://mempool.space",
|
"homepage": "https://mempool.space",
|
||||||
@@ -30,21 +30,21 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mempool/bitcoin": "^3.0.3",
|
"@mempool/bitcoin": "^3.0.3",
|
||||||
"@mempool/electrum-client": "^1.1.7",
|
"@mempool/electrum-client": "^1.1.7",
|
||||||
"axios": "^0.21.1",
|
"@types/ws": "8.2.2",
|
||||||
"bitcoinjs-lib": "^5.2.0",
|
"axios": "0.24.0",
|
||||||
|
"bitcoinjs-lib": "6.0.1",
|
||||||
"crypto-js": "^4.0.0",
|
"crypto-js": "^4.0.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"locutus": "^2.0.12",
|
"locutus": "^2.0.12",
|
||||||
"mysql2": "2.2.5",
|
"mysql2": "2.3.3",
|
||||||
"node-worker-threads-pool": "^1.4.3",
|
"node-worker-threads-pool": "^1.4.3",
|
||||||
"ws": "^7.4.6"
|
"typescript": "4.4.4",
|
||||||
|
"ws": "8.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/compression": "^1.0.1",
|
"@types/compression": "^1.0.1",
|
||||||
"@types/express": "^4.17.2",
|
"@types/express": "^4.17.2",
|
||||||
"@types/locutus": "^0.0.6",
|
"@types/locutus": "^0.0.6",
|
||||||
"@types/ws": "^7.4.4",
|
"tslint": "^6.1.0"
|
||||||
"tslint": "^6.1.0",
|
|
||||||
"typescript": "4.4.2"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ interface BisqScriptPubKey {
|
|||||||
addresses: string[];
|
addresses: string[];
|
||||||
asm: string;
|
asm: string;
|
||||||
hex: string;
|
hex: string;
|
||||||
reqSigs: number;
|
reqSigs?: number;
|
||||||
type: string;
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,4 +11,12 @@ export interface AbstractBitcoinApi {
|
|||||||
$getAddress(address: string): Promise<IEsploraApi.Address>;
|
$getAddress(address: string): Promise<IEsploraApi.Address>;
|
||||||
$getAddressTransactions(address: string, lastSeenTxId: string): Promise<IEsploraApi.Transaction[]>;
|
$getAddressTransactions(address: string, lastSeenTxId: string): Promise<IEsploraApi.Transaction[]>;
|
||||||
$getAddressPrefix(prefix: string): string[];
|
$getAddressPrefix(prefix: string): string[];
|
||||||
|
$sendRawTransaction(rawTransaction: string): Promise<string>;
|
||||||
|
}
|
||||||
|
export interface BitcoinRpcCredentials {
|
||||||
|
host: string;
|
||||||
|
port: number;
|
||||||
|
user: string;
|
||||||
|
pass: string;
|
||||||
|
timeout: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,16 +3,17 @@ import { AbstractBitcoinApi } from './bitcoin-api-abstract-factory';
|
|||||||
import EsploraApi from './esplora-api';
|
import EsploraApi from './esplora-api';
|
||||||
import BitcoinApi from './bitcoin-api';
|
import BitcoinApi from './bitcoin-api';
|
||||||
import ElectrumApi from './electrum-api';
|
import ElectrumApi from './electrum-api';
|
||||||
|
import bitcoinClient from './bitcoin-client';
|
||||||
|
|
||||||
function bitcoinApiFactory(): AbstractBitcoinApi {
|
function bitcoinApiFactory(): AbstractBitcoinApi {
|
||||||
switch (config.MEMPOOL.BACKEND) {
|
switch (config.MEMPOOL.BACKEND) {
|
||||||
case 'esplora':
|
case 'esplora':
|
||||||
return new EsploraApi();
|
return new EsploraApi();
|
||||||
case 'electrum':
|
case 'electrum':
|
||||||
return new ElectrumApi();
|
return new ElectrumApi(bitcoinClient);
|
||||||
case 'none':
|
case 'none':
|
||||||
default:
|
default:
|
||||||
return new BitcoinApi();
|
return new BitcoinApi(bitcoinClient);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export namespace IBitcoinApi {
|
|||||||
time: number; // (numeric) Same as blocktime
|
time: number; // (numeric) Same as blocktime
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Vin {
|
export interface Vin {
|
||||||
txid?: string; // (string) The transaction id
|
txid?: string; // (string) The transaction id
|
||||||
vout?: number; // (string)
|
vout?: number; // (string)
|
||||||
scriptSig?: { // (json object) The script
|
scriptSig?: { // (json object) The script
|
||||||
@@ -82,17 +82,22 @@ export namespace IBitcoinApi {
|
|||||||
sequence: number; // (numeric) The script sequence number
|
sequence: number; // (numeric) The script sequence number
|
||||||
txinwitness?: string[]; // (string) hex-encoded witness data
|
txinwitness?: string[]; // (string) hex-encoded witness data
|
||||||
coinbase?: string;
|
coinbase?: string;
|
||||||
|
is_pegin?: boolean; // (boolean) Elements peg-in
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Vout {
|
export interface Vout {
|
||||||
value: number; // (numeric) The value in BTC
|
value: number; // (numeric) The value in BTC
|
||||||
n: number; // (numeric) index
|
n: number; // (numeric) index
|
||||||
|
asset?: string; // (string) Elements asset id
|
||||||
scriptPubKey: { // (json object)
|
scriptPubKey: { // (json object)
|
||||||
asm: string; // (string) the asm
|
asm: string; // (string) the asm
|
||||||
hex: string; // (string) the hex
|
hex: string; // (string) the hex
|
||||||
reqSigs: number; // (numeric) The required sigs
|
reqSigs?: number; // (numeric) The required sigs
|
||||||
type: string; // (string) The type, eg 'pubkeyhash'
|
type: string; // (string) The type, eg 'pubkeyhash'
|
||||||
addresses: string[] // (string) bitcoin address
|
address?: string; // (string) bitcoin address
|
||||||
|
addresses?: string[]; // (string) bitcoin addresses
|
||||||
|
pegout_chain?: string; // (string) Elements peg-out chain
|
||||||
|
pegout_addresses?: string[]; // (string) Elements peg-out addresses
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import config from '../../config';
|
|
||||||
import * as bitcoin from '@mempool/bitcoin';
|
|
||||||
import * as bitcoinjs from 'bitcoinjs-lib';
|
import * as bitcoinjs from 'bitcoinjs-lib';
|
||||||
import { AbstractBitcoinApi } from './bitcoin-api-abstract-factory';
|
import { AbstractBitcoinApi } from './bitcoin-api-abstract-factory';
|
||||||
import { IBitcoinApi } from './bitcoin-api.interface';
|
import { IBitcoinApi } from './bitcoin-api.interface';
|
||||||
@@ -10,16 +8,10 @@ import { TransactionExtended } from '../../mempool.interfaces';
|
|||||||
|
|
||||||
class BitcoinApi implements AbstractBitcoinApi {
|
class BitcoinApi implements AbstractBitcoinApi {
|
||||||
private rawMempoolCache: IBitcoinApi.RawMempool | null = null;
|
private rawMempoolCache: IBitcoinApi.RawMempool | null = null;
|
||||||
private bitcoindClient: any;
|
protected bitcoindClient: any;
|
||||||
|
|
||||||
constructor() {
|
constructor(bitcoinClient: any) {
|
||||||
this.bitcoindClient = new bitcoin.Client({
|
this.bitcoindClient = bitcoinClient;
|
||||||
host: config.CORE_RPC.HOST,
|
|
||||||
port: config.CORE_RPC.PORT,
|
|
||||||
user: config.CORE_RPC.USERNAME,
|
|
||||||
pass: config.CORE_RPC.PASSWORD,
|
|
||||||
timeout: 60000,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$getRawTransaction(txId: string, skipConversion = false, addPrevout = false): Promise<IEsploraApi.Transaction> {
|
$getRawTransaction(txId: string, skipConversion = false, addPrevout = false): Promise<IEsploraApi.Transaction> {
|
||||||
@@ -106,6 +98,10 @@ class BitcoinApi implements AbstractBitcoinApi {
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$sendRawTransaction(rawTransaction: string): Promise<string> {
|
||||||
|
return this.bitcoindClient.sendRawTransaction(rawTransaction);
|
||||||
|
}
|
||||||
|
|
||||||
protected async $convertTransaction(transaction: IBitcoinApi.Transaction, addPrevout: boolean): Promise<IEsploraApi.Transaction> {
|
protected async $convertTransaction(transaction: IBitcoinApi.Transaction, addPrevout: boolean): Promise<IEsploraApi.Transaction> {
|
||||||
let esploraTransaction: IEsploraApi.Transaction = {
|
let esploraTransaction: IEsploraApi.Transaction = {
|
||||||
txid: transaction.txid,
|
txid: transaction.txid,
|
||||||
@@ -123,7 +119,8 @@ class BitcoinApi implements AbstractBitcoinApi {
|
|||||||
return {
|
return {
|
||||||
value: vout.value * 100000000,
|
value: vout.value * 100000000,
|
||||||
scriptpubkey: vout.scriptPubKey.hex,
|
scriptpubkey: vout.scriptPubKey.hex,
|
||||||
scriptpubkey_address: vout.scriptPubKey && vout.scriptPubKey.addresses ? vout.scriptPubKey.addresses[0] : '',
|
scriptpubkey_address: vout.scriptPubKey && vout.scriptPubKey.address ? vout.scriptPubKey.address
|
||||||
|
: vout.scriptPubKey.addresses ? vout.scriptPubKey.addresses[0] : '',
|
||||||
scriptpubkey_asm: vout.scriptPubKey.asm ? this.convertScriptSigAsm(vout.scriptPubKey.asm) : '',
|
scriptpubkey_asm: vout.scriptPubKey.asm ? this.convertScriptSigAsm(vout.scriptPubKey.asm) : '',
|
||||||
scriptpubkey_type: this.translateScriptPubKeyType(vout.scriptPubKey.type),
|
scriptpubkey_type: this.translateScriptPubKeyType(vout.scriptPubKey.type),
|
||||||
};
|
};
|
||||||
@@ -302,6 +299,11 @@ class BitcoinApi implements AbstractBitcoinApi {
|
|||||||
const witnessScript = vin.witness[vin.witness.length - 1];
|
const witnessScript = vin.witness[vin.witness.length - 1];
|
||||||
vin.inner_witnessscript_asm = this.convertScriptSigAsm(bitcoinjs.script.toASM(Buffer.from(witnessScript, 'hex')));
|
vin.inner_witnessscript_asm = this.convertScriptSigAsm(bitcoinjs.script.toASM(Buffer.from(witnessScript, 'hex')));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vin.prevout.scriptpubkey_type === 'v1_p2tr' && vin.witness && vin.witness.length > 1) {
|
||||||
|
const witnessScript = vin.witness[vin.witness.length - 2];
|
||||||
|
vin.inner_witnessscript_asm = this.convertScriptSigAsm(bitcoinjs.script.toASM(Buffer.from(witnessScript, 'hex')));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
import config from '../../config';
|
|
||||||
import * as bitcoin from '@mempool/bitcoin';
|
|
||||||
import { IBitcoinApi } from './bitcoin-api.interface';
|
|
||||||
|
|
||||||
class BitcoinBaseApi {
|
|
||||||
bitcoindClient: any;
|
|
||||||
bitcoindClientMempoolInfo: any;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.bitcoindClient = new bitcoin.Client({
|
|
||||||
host: config.CORE_RPC.HOST,
|
|
||||||
port: config.CORE_RPC.PORT,
|
|
||||||
user: config.CORE_RPC.USERNAME,
|
|
||||||
pass: config.CORE_RPC.PASSWORD,
|
|
||||||
timeout: 60000,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (config.CORE_RPC_MINFEE.ENABLED) {
|
|
||||||
this.bitcoindClientMempoolInfo = new bitcoin.Client({
|
|
||||||
host: config.CORE_RPC_MINFEE.HOST,
|
|
||||||
port: config.CORE_RPC_MINFEE.PORT,
|
|
||||||
user: config.CORE_RPC_MINFEE.USERNAME,
|
|
||||||
pass: config.CORE_RPC_MINFEE.PASSWORD,
|
|
||||||
timeout: 60000,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$getMempoolInfo(): Promise<IBitcoinApi.MempoolInfo> {
|
|
||||||
if (config.CORE_RPC_MINFEE.ENABLED) {
|
|
||||||
return Promise.all([
|
|
||||||
this.bitcoindClient.getMempoolInfo(),
|
|
||||||
this.bitcoindClientMempoolInfo.getMempoolInfo()
|
|
||||||
]).then(([mempoolInfo, secondMempoolInfo]) => {
|
|
||||||
mempoolInfo.maxmempool = secondMempoolInfo.maxmempool;
|
|
||||||
mempoolInfo.mempoolminfee = secondMempoolInfo.mempoolminfee;
|
|
||||||
mempoolInfo.minrelaytxfee = secondMempoolInfo.minrelaytxfee;
|
|
||||||
return mempoolInfo;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return this.bitcoindClient.getMempoolInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
$getBlockchainInfo(): Promise<IBitcoinApi.BlockchainInfo> {
|
|
||||||
return this.bitcoindClient.getBlockchainInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
$validateAddress(address: string): Promise<IBitcoinApi.AddressInformation> {
|
|
||||||
return this.bitcoindClient.validateAddress(address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default new BitcoinBaseApi();
|
|
||||||
13
backend/src/api/bitcoin/bitcoin-client.ts
Normal file
13
backend/src/api/bitcoin/bitcoin-client.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import config from '../../config';
|
||||||
|
import * as bitcoin from '@mempool/bitcoin';
|
||||||
|
import { BitcoinRpcCredentials } from './bitcoin-api-abstract-factory';
|
||||||
|
|
||||||
|
const nodeRpcCredentials: BitcoinRpcCredentials = {
|
||||||
|
host: config.CORE_RPC.HOST,
|
||||||
|
port: config.CORE_RPC.PORT,
|
||||||
|
user: config.CORE_RPC.USERNAME,
|
||||||
|
pass: config.CORE_RPC.PASSWORD,
|
||||||
|
timeout: 60000,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default new bitcoin.Client(nodeRpcCredentials);
|
||||||
13
backend/src/api/bitcoin/bitcoin-second-client.ts
Normal file
13
backend/src/api/bitcoin/bitcoin-second-client.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import config from '../../config';
|
||||||
|
import * as bitcoin from '@mempool/bitcoin';
|
||||||
|
import { BitcoinRpcCredentials } from './bitcoin-api-abstract-factory';
|
||||||
|
|
||||||
|
const nodeRpcCredentials: BitcoinRpcCredentials = {
|
||||||
|
host: config.SECOND_CORE_RPC.HOST,
|
||||||
|
port: config.SECOND_CORE_RPC.PORT,
|
||||||
|
user: config.SECOND_CORE_RPC.USERNAME,
|
||||||
|
pass: config.SECOND_CORE_RPC.PASSWORD,
|
||||||
|
timeout: 60000,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default new bitcoin.Client(nodeRpcCredentials);
|
||||||
@@ -1,23 +1,20 @@
|
|||||||
import config from '../../config';
|
import config from '../../config';
|
||||||
import { AbstractBitcoinApi } from './bitcoin-api-abstract-factory';
|
import { AbstractBitcoinApi } from './bitcoin-api-abstract-factory';
|
||||||
import { IBitcoinApi } from './bitcoin-api.interface';
|
|
||||||
import { IEsploraApi } from './esplora-api.interface';
|
import { IEsploraApi } from './esplora-api.interface';
|
||||||
import { IElectrumApi } from './electrum-api.interface';
|
import { IElectrumApi } from './electrum-api.interface';
|
||||||
import BitcoinApi from './bitcoin-api';
|
import BitcoinApi from './bitcoin-api';
|
||||||
import mempool from '../mempool';
|
|
||||||
import logger from '../../logger';
|
import logger from '../../logger';
|
||||||
import * as ElectrumClient from '@mempool/electrum-client';
|
import * as ElectrumClient from '@mempool/electrum-client';
|
||||||
import * as sha256 from 'crypto-js/sha256';
|
import * as sha256 from 'crypto-js/sha256';
|
||||||
import * as hexEnc from 'crypto-js/enc-hex';
|
import * as hexEnc from 'crypto-js/enc-hex';
|
||||||
import loadingIndicators from '../loading-indicators';
|
import loadingIndicators from '../loading-indicators';
|
||||||
import memoryCache from '../memory-cache';
|
import memoryCache from '../memory-cache';
|
||||||
import bitcoinBaseApi from './bitcoin-base.api';
|
|
||||||
|
|
||||||
class BitcoindElectrsApi extends BitcoinApi implements AbstractBitcoinApi {
|
class BitcoindElectrsApi extends BitcoinApi implements AbstractBitcoinApi {
|
||||||
private electrumClient: any;
|
private electrumClient: any;
|
||||||
|
|
||||||
constructor() {
|
constructor(bitcoinClient: any) {
|
||||||
super();
|
super(bitcoinClient);
|
||||||
|
|
||||||
const electrumConfig = { client: 'mempool-v2', version: '1.4' };
|
const electrumConfig = { client: 'mempool-v2', version: '1.4' };
|
||||||
const electrumPersistencePolicy = { retryPeriod: 10000, maxRetry: 1000, callback: null };
|
const electrumPersistencePolicy = { retryPeriod: 10000, maxRetry: 1000, callback: null };
|
||||||
@@ -45,7 +42,7 @@ class BitcoindElectrsApi extends BitcoinApi implements AbstractBitcoinApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async $getAddress(address: string): Promise<IEsploraApi.Address> {
|
async $getAddress(address: string): Promise<IEsploraApi.Address> {
|
||||||
const addressInfo = await bitcoinBaseApi.$validateAddress(address);
|
const addressInfo = await this.bitcoindClient.validateAddress(address);
|
||||||
if (!addressInfo || !addressInfo.isvalid) {
|
if (!addressInfo || !addressInfo.isvalid) {
|
||||||
return ({
|
return ({
|
||||||
'address': address,
|
'address': address,
|
||||||
@@ -90,16 +87,13 @@ class BitcoindElectrsApi extends BitcoinApi implements AbstractBitcoinApi {
|
|||||||
},
|
},
|
||||||
'electrum': true,
|
'electrum': true,
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e: any) {
|
||||||
if (e === 'failed to get confirmed status') {
|
throw new Error(typeof e === 'string' ? e : e && e.message || e);
|
||||||
e = 'The number of transactions on this address exceeds the Electrum server limit';
|
|
||||||
}
|
|
||||||
throw new Error(typeof e === 'string' ? e : 'Error');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async $getAddressTransactions(address: string, lastSeenTxId: string): Promise<IEsploraApi.Transaction[]> {
|
async $getAddressTransactions(address: string, lastSeenTxId: string): Promise<IEsploraApi.Transaction[]> {
|
||||||
const addressInfo = await bitcoinBaseApi.$validateAddress(address);
|
const addressInfo = await this.bitcoindClient.validateAddress(address);
|
||||||
if (!addressInfo || !addressInfo.isvalid) {
|
if (!addressInfo || !addressInfo.isvalid) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -127,12 +121,9 @@ class BitcoindElectrsApi extends BitcoinApi implements AbstractBitcoinApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return transactions;
|
return transactions;
|
||||||
} catch (e) {
|
} catch (e: any) {
|
||||||
loadingIndicators.setProgress('address-' + address, 100);
|
loadingIndicators.setProgress('address-' + address, 100);
|
||||||
if (e === 'failed to get confirmed status') {
|
throw new Error(typeof e === 'string' ? e : e && e.message || e);
|
||||||
e = 'The number of transactions on this address exceeds the Electrum server limit';
|
|
||||||
}
|
|
||||||
throw new Error(typeof e === 'string' ? e : 'Error');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ class ElectrsApi implements AbstractBitcoinApi {
|
|||||||
$getAddressPrefix(prefix: string): string[] {
|
$getAddressPrefix(prefix: string): string[] {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$sendRawTransaction(rawTransaction: string): Promise<string> {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ElectrsApi;
|
export default ElectrsApi;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { BlockExtended, TransactionExtended } from '../mempool.interfaces';
|
|||||||
import { Common } from './common';
|
import { Common } from './common';
|
||||||
import diskCache from './disk-cache';
|
import diskCache from './disk-cache';
|
||||||
import transactionUtils from './transaction-utils';
|
import transactionUtils from './transaction-utils';
|
||||||
import bitcoinBaseApi from './bitcoin/bitcoin-base.api';
|
import bitcoinClient from './bitcoin/bitcoin-client';
|
||||||
|
|
||||||
class Blocks {
|
class Blocks {
|
||||||
private blocks: BlockExtended[] = [];
|
private blocks: BlockExtended[] = [];
|
||||||
@@ -45,7 +45,7 @@ class Blocks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.lastDifficultyAdjustmentTime) {
|
if (!this.lastDifficultyAdjustmentTime) {
|
||||||
const blockchainInfo = await bitcoinBaseApi.$getBlockchainInfo();
|
const blockchainInfo = await bitcoinClient.getBlockchainInfo();
|
||||||
if (blockchainInfo.blocks === blockchainInfo.headers) {
|
if (blockchainInfo.blocks === blockchainInfo.headers) {
|
||||||
const heightDiff = blockHeightTip % 2016;
|
const heightDiff = blockHeightTip % 2016;
|
||||||
const blockHash = await bitcoinApi.$getBlockHash(blockHeightTip - heightDiff);
|
const blockHash = await bitcoinApi.$getBlockHash(blockHeightTip - heightDiff);
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { CpfpInfo, TransactionExtended, TransactionStripped } from '../mempool.interfaces';
|
import { CpfpInfo, TransactionExtended, TransactionStripped } from '../mempool.interfaces';
|
||||||
import config from '../config';
|
import config from '../config';
|
||||||
export class Common {
|
export class Common {
|
||||||
|
static nativeAssetId = '6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d';
|
||||||
|
|
||||||
static median(numbers: number[]) {
|
static median(numbers: number[]) {
|
||||||
let medianNr = 0;
|
let medianNr = 0;
|
||||||
const numsLen = numbers.length;
|
const numsLen = numbers.length;
|
||||||
|
|||||||
111
backend/src/api/liquid/elements-parser.ts
Normal file
111
backend/src/api/liquid/elements-parser.ts
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import { IBitcoinApi } from '../bitcoin/bitcoin-api.interface';
|
||||||
|
import bitcoinClient from '../bitcoin/bitcoin-client';
|
||||||
|
import bitcoinSecondClient from '../bitcoin/bitcoin-second-client';
|
||||||
|
import { Common } from '../common';
|
||||||
|
import { DB } from '../../database';
|
||||||
|
import logger from '../../logger';
|
||||||
|
|
||||||
|
class ElementsParser {
|
||||||
|
private isRunning = false;
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
public async $parse() {
|
||||||
|
if (this.isRunning) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.isRunning = true;
|
||||||
|
const result = await bitcoinClient.getChainTips();
|
||||||
|
const tip = result[0].height;
|
||||||
|
const latestBlock = await this.$getLatestBlockFromDatabase();
|
||||||
|
for (let height = latestBlock.block + 1; height <= tip; height++) {
|
||||||
|
const blockHash: IBitcoinApi.ChainTips = await bitcoinClient.getBlockHash(height);
|
||||||
|
const block: IBitcoinApi.Block = await bitcoinClient.getBlock(blockHash, 2);
|
||||||
|
await this.$parseBlock(block);
|
||||||
|
await this.$saveLatestBlockToDatabase(block.height, block.time, block.hash);
|
||||||
|
}
|
||||||
|
this.isRunning = false;
|
||||||
|
} catch (e) {
|
||||||
|
this.isRunning = false;
|
||||||
|
throw new Error(e instanceof Error ? e.message : 'Error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async $getPegDataByMonth(): Promise<any> {
|
||||||
|
const connection = await DB.pool.getConnection();
|
||||||
|
const query = `SELECT SUM(amount) AS amount, DATE_FORMAT(FROM_UNIXTIME(datetime), '%Y-%m-01') AS date FROM elements_pegs GROUP BY DATE_FORMAT(FROM_UNIXTIME(datetime), '%Y%m')`;
|
||||||
|
const [rows] = await connection.query<any>(query);
|
||||||
|
connection.release();
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async $parseBlock(block: IBitcoinApi.Block) {
|
||||||
|
for (const tx of block.tx) {
|
||||||
|
await this.$parseInputs(tx, block);
|
||||||
|
await this.$parseOutputs(tx, block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async $parseInputs(tx: IBitcoinApi.Transaction, block: IBitcoinApi.Block) {
|
||||||
|
for (const [index, input] of tx.vin.entries()) {
|
||||||
|
if (input.is_pegin) {
|
||||||
|
await this.$parsePegIn(input, index, tx.txid, block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async $parsePegIn(input: IBitcoinApi.Vin, vindex: number, txid: string, block: IBitcoinApi.Block) {
|
||||||
|
const bitcoinTx: IBitcoinApi.Transaction = await bitcoinSecondClient.getRawTransaction(input.txid, true);
|
||||||
|
const prevout = bitcoinTx.vout[input.vout || 0];
|
||||||
|
const outputAddress = prevout.scriptPubKey.address || (prevout.scriptPubKey.addresses && prevout.scriptPubKey.addresses[0]) || '';
|
||||||
|
await this.$savePegToDatabase(block.height, block.time, prevout.value * 100000000, txid, vindex,
|
||||||
|
outputAddress, bitcoinTx.txid, prevout.n, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async $parseOutputs(tx: IBitcoinApi.Transaction, block: IBitcoinApi.Block) {
|
||||||
|
for (const output of tx.vout) {
|
||||||
|
if (output.scriptPubKey.pegout_chain) {
|
||||||
|
await this.$savePegToDatabase(block.height, block.time, 0 - output.value * 100000000, tx.txid, output.n,
|
||||||
|
(output.scriptPubKey.pegout_addresses && output.scriptPubKey.pegout_addresses[0] || ''), '', 0, 0);
|
||||||
|
}
|
||||||
|
if (!output.scriptPubKey.pegout_chain && output.scriptPubKey.type === 'nulldata'
|
||||||
|
&& output.value && output.value > 0 && output.asset && output.asset === Common.nativeAssetId) {
|
||||||
|
await this.$savePegToDatabase(block.height, block.time, 0 - output.value * 100000000, tx.txid, output.n,
|
||||||
|
(output.scriptPubKey.pegout_addresses && output.scriptPubKey.pegout_addresses[0] || ''), '', 0, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async $savePegToDatabase(height: number, blockTime: number, amount: number, txid: string,
|
||||||
|
txindex: number, bitcoinaddress: string, bitcointxid: string, bitcoinindex: number, final_tx: number): Promise<void> {
|
||||||
|
const connection = await DB.pool.getConnection();
|
||||||
|
const query = `INSERT INTO elements_pegs(
|
||||||
|
block, datetime, amount, txid, txindex, bitcoinaddress, bitcointxid, bitcoinindex, final_tx
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`;
|
||||||
|
|
||||||
|
const params: (string | number)[] = [
|
||||||
|
height, blockTime, amount, txid, txindex, bitcoinaddress, bitcointxid, bitcoinindex, final_tx
|
||||||
|
];
|
||||||
|
await connection.query(query, params);
|
||||||
|
connection.release();
|
||||||
|
logger.debug(`Saved L-BTC peg from block height #${height} with TXID ${txid}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async $getLatestBlockFromDatabase(): Promise<any> {
|
||||||
|
const connection = await DB.pool.getConnection();
|
||||||
|
const query = `SELECT block, datetime, block_hash FROM last_elements_block`;
|
||||||
|
const [rows] = await connection.query<any>(query);
|
||||||
|
connection.release();
|
||||||
|
return rows[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async $saveLatestBlockToDatabase(blockHeight: number, datetime: number, blockHash: string) {
|
||||||
|
const connection = await DB.pool.getConnection();
|
||||||
|
const query = `UPDATE last_elements_block SET block = ?, datetime = ?, block_hash = ?`;
|
||||||
|
await connection.query<any>(query, [blockHeight, datetime, blockHash]);
|
||||||
|
connection.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new ElementsParser();
|
||||||
@@ -75,7 +75,8 @@ class MempoolBlocks {
|
|||||||
let blockSize = 0;
|
let blockSize = 0;
|
||||||
let transactions: TransactionExtended[] = [];
|
let transactions: TransactionExtended[] = [];
|
||||||
transactionsSorted.forEach((tx) => {
|
transactionsSorted.forEach((tx) => {
|
||||||
if (blockWeight + tx.weight <= config.MEMPOOL.BLOCK_WEIGHT_UNITS || mempoolBlocks.length === config.MEMPOOL.MEMPOOL_BLOCKS_AMOUNT) {
|
if (blockWeight + tx.weight <= config.MEMPOOL.BLOCK_WEIGHT_UNITS
|
||||||
|
|| mempoolBlocks.length === config.MEMPOOL.MEMPOOL_BLOCKS_AMOUNT - 1) {
|
||||||
blockWeight += tx.weight;
|
blockWeight += tx.weight;
|
||||||
blockSize += tx.size;
|
blockSize += tx.size;
|
||||||
transactions.push(tx);
|
transactions.push(tx);
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ import logger from '../logger';
|
|||||||
import { Common } from './common';
|
import { Common } from './common';
|
||||||
import transactionUtils from './transaction-utils';
|
import transactionUtils from './transaction-utils';
|
||||||
import { IBitcoinApi } from './bitcoin/bitcoin-api.interface';
|
import { IBitcoinApi } from './bitcoin/bitcoin-api.interface';
|
||||||
import bitcoinBaseApi from './bitcoin/bitcoin-base.api';
|
|
||||||
import loadingIndicators from './loading-indicators';
|
import loadingIndicators from './loading-indicators';
|
||||||
|
import bitcoinClient from './bitcoin/bitcoin-client';
|
||||||
|
import bitcoinSecondClient from './bitcoin/bitcoin-second-client';
|
||||||
|
|
||||||
class Mempool {
|
class Mempool {
|
||||||
private static WEBSOCKET_REFRESH_RATE_MS = 10000;
|
private static WEBSOCKET_REFRESH_RATE_MS = 10000;
|
||||||
@@ -61,7 +62,7 @@ class Mempool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async $updateMemPoolInfo() {
|
public async $updateMemPoolInfo() {
|
||||||
this.mempoolInfo = await bitcoinBaseApi.$getMempoolInfo();
|
this.mempoolInfo = await this.$getMempoolInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public getMempoolInfo(): IBitcoinApi.MempoolInfo {
|
public getMempoolInfo(): IBitcoinApi.MempoolInfo {
|
||||||
@@ -205,6 +206,21 @@ class Mempool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private $getMempoolInfo() {
|
||||||
|
if (config.MEMPOOL.USE_SECOND_NODE_FOR_MINFEE) {
|
||||||
|
return Promise.all([
|
||||||
|
bitcoinClient.getMempoolInfo(),
|
||||||
|
bitcoinSecondClient.getMempoolInfo()
|
||||||
|
]).then(([mempoolInfo, secondMempoolInfo]) => {
|
||||||
|
mempoolInfo.maxmempool = secondMempoolInfo.maxmempool;
|
||||||
|
mempoolInfo.mempoolminfee = secondMempoolInfo.mempoolminfee;
|
||||||
|
mempoolInfo.minrelaytxfee = secondMempoolInfo.minrelaytxfee;
|
||||||
|
return mempoolInfo;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return bitcoinClient.getMempoolInfo();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new Mempool();
|
export default new Mempool();
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ import { DB } from '../database';
|
|||||||
import logger from '../logger';
|
import logger from '../logger';
|
||||||
|
|
||||||
import { Statistic, TransactionExtended, OptimizedStatistic } from '../mempool.interfaces';
|
import { Statistic, TransactionExtended, OptimizedStatistic } from '../mempool.interfaces';
|
||||||
|
import config from '../config';
|
||||||
|
|
||||||
class Statistics {
|
class Statistics {
|
||||||
protected intervalTimer: NodeJS.Timer | undefined;
|
protected intervalTimer: NodeJS.Timer | undefined;
|
||||||
protected newStatisticsEntryCallback: ((stats: OptimizedStatistic) => void) | undefined;
|
protected newStatisticsEntryCallback: ((stats: OptimizedStatistic) => void) | undefined;
|
||||||
protected queryTimeout = 120000;
|
protected queryTimeout = 120000;
|
||||||
protected cache: { [date: string]: OptimizedStatistic[] } = {
|
protected cache: { [date: string]: OptimizedStatistic[] } = {
|
||||||
'24h': [], '1w': [], '1m': [], '3m': [], '6m': [], '1y': [],
|
'24h': [], '1w': [], '1m': [], '3m': [], '6m': [], '1y': [], '2y': [], '3y': []
|
||||||
};
|
};
|
||||||
|
|
||||||
public setNewStatisticsEntryCallback(fn: (stats: OptimizedStatistic) => void) {
|
public setNewStatisticsEntryCallback(fn: (stats: OptimizedStatistic) => void) {
|
||||||
@@ -48,6 +49,8 @@ class Statistics {
|
|||||||
this.cache['3m'] = await this.$list3M();
|
this.cache['3m'] = await this.$list3M();
|
||||||
this.cache['6m'] = await this.$list6M();
|
this.cache['6m'] = await this.$list6M();
|
||||||
this.cache['1y'] = await this.$list1Y();
|
this.cache['1y'] = await this.$list1Y();
|
||||||
|
this.cache['2y'] = await this.$list2Y();
|
||||||
|
this.cache['3y'] = await this.$list3Y();
|
||||||
logger.debug('Statistics cache created');
|
logger.debug('Statistics cache created');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,10 +85,15 @@ class Statistics {
|
|||||||
250, 300, 350, 400, 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000];
|
250, 300, 350, 400, 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000];
|
||||||
|
|
||||||
const weightVsizeFees: { [feePerWU: number]: number } = {};
|
const weightVsizeFees: { [feePerWU: number]: number } = {};
|
||||||
|
const lastItem = logFees.length - 1;
|
||||||
|
|
||||||
memPoolArray.forEach((transaction) => {
|
memPoolArray.forEach((transaction) => {
|
||||||
for (let i = 0; i < logFees.length; i++) {
|
for (let i = 0; i < logFees.length; i++) {
|
||||||
if ((logFees[i] === 2000 && transaction.effectiveFeePerVsize >= 2000) || transaction.effectiveFeePerVsize <= logFees[i]) {
|
if (
|
||||||
|
(config.MEMPOOL.NETWORK === 'liquid' && (i === lastItem || transaction.effectiveFeePerVsize * 10 < logFees[i + 1]))
|
||||||
|
||
|
||||||
|
(config.MEMPOOL.NETWORK !== 'liquid' && (i === lastItem || transaction.effectiveFeePerVsize < logFees[i + 1]))
|
||||||
|
) {
|
||||||
if (weightVsizeFees[logFees[i]]) {
|
if (weightVsizeFees[logFees[i]]) {
|
||||||
weightVsizeFees[logFees[i]] += transaction.vsize;
|
weightVsizeFees[logFees[i]] += transaction.vsize;
|
||||||
} else {
|
} else {
|
||||||
@@ -403,10 +411,37 @@ class Statistics {
|
|||||||
connection.release();
|
connection.release();
|
||||||
return this.mapStatisticToOptimizedStatistic(rows);
|
return this.mapStatisticToOptimizedStatistic(rows);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('$list6M() error' + (e instanceof Error ? e.message : e));
|
logger.err('$list1Y() error' + (e instanceof Error ? e.message : e));
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async $list2Y(): Promise<OptimizedStatistic[]> {
|
||||||
|
try {
|
||||||
|
const connection = await DB.pool.getConnection();
|
||||||
|
const query = this.getQueryForDays(120960);
|
||||||
|
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||||
|
connection.release();
|
||||||
|
return this.mapStatisticToOptimizedStatistic(rows);
|
||||||
|
} catch (e) {
|
||||||
|
logger.err('$list2Y() error' + (e instanceof Error ? e.message : e));
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async $list3Y(): Promise<OptimizedStatistic[]> {
|
||||||
|
try {
|
||||||
|
const connection = await DB.pool.getConnection();
|
||||||
|
const query = this.getQueryForDays(181440);
|
||||||
|
const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout });
|
||||||
|
connection.release();
|
||||||
|
return this.mapStatisticToOptimizedStatistic(rows);
|
||||||
|
} catch (e) {
|
||||||
|
logger.err('$list3Y() error' + (e instanceof Error ? e.message : e));
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private mapStatisticToOptimizedStatistic(statistic: Statistic[]): OptimizedStatistic[] {
|
private mapStatisticToOptimizedStatistic(statistic: Statistic[]): OptimizedStatistic[] {
|
||||||
return statistic.map((s) => {
|
return statistic.map((s) => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import transactionUtils from './transaction-utils';
|
|||||||
|
|
||||||
class WebsocketHandler {
|
class WebsocketHandler {
|
||||||
private wss: WebSocket.Server | undefined;
|
private wss: WebSocket.Server | undefined;
|
||||||
private nativeAssetId = '6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d';
|
|
||||||
private extraInitProperties = {};
|
private extraInitProperties = {};
|
||||||
|
|
||||||
constructor() { }
|
constructor() { }
|
||||||
@@ -308,7 +307,7 @@ class WebsocketHandler {
|
|||||||
|
|
||||||
newTransactions.forEach((tx) => {
|
newTransactions.forEach((tx) => {
|
||||||
|
|
||||||
if (client['track-asset'] === this.nativeAssetId) {
|
if (client['track-asset'] === Common.nativeAssetId) {
|
||||||
if (tx.vin.some((vin) => !!vin.is_pegin)) {
|
if (tx.vin.some((vin) => !!vin.is_pegin)) {
|
||||||
foundTransactions.push(tx);
|
foundTransactions.push(tx);
|
||||||
return;
|
return;
|
||||||
@@ -439,7 +438,7 @@ class WebsocketHandler {
|
|||||||
const foundTransactions: TransactionExtended[] = [];
|
const foundTransactions: TransactionExtended[] = [];
|
||||||
|
|
||||||
transactions.forEach((tx) => {
|
transactions.forEach((tx) => {
|
||||||
if (client['track-asset'] === this.nativeAssetId) {
|
if (client['track-asset'] === Common.nativeAssetId) {
|
||||||
if (tx.vin && tx.vin.some((vin) => !!vin.is_pegin)) {
|
if (tx.vin && tx.vin.some((vin) => !!vin.is_pegin)) {
|
||||||
foundTransactions.push(tx);
|
foundTransactions.push(tx);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ interface IConfig {
|
|||||||
INITIAL_BLOCKS_AMOUNT: number;
|
INITIAL_BLOCKS_AMOUNT: number;
|
||||||
MEMPOOL_BLOCKS_AMOUNT: number;
|
MEMPOOL_BLOCKS_AMOUNT: number;
|
||||||
PRICE_FEED_UPDATE_INTERVAL: number;
|
PRICE_FEED_UPDATE_INTERVAL: number;
|
||||||
|
USE_SECOND_NODE_FOR_MINFEE: boolean;
|
||||||
};
|
};
|
||||||
ESPLORA: {
|
ESPLORA: {
|
||||||
REST_API_URL: string;
|
REST_API_URL: string;
|
||||||
@@ -30,8 +31,7 @@ interface IConfig {
|
|||||||
USERNAME: string;
|
USERNAME: string;
|
||||||
PASSWORD: string;
|
PASSWORD: string;
|
||||||
};
|
};
|
||||||
CORE_RPC_MINFEE: {
|
SECOND_CORE_RPC: {
|
||||||
ENABLED: boolean;
|
|
||||||
HOST: string;
|
HOST: string;
|
||||||
PORT: number;
|
PORT: number;
|
||||||
USERNAME: string;
|
USERNAME: string;
|
||||||
@@ -77,6 +77,7 @@ const defaults: IConfig = {
|
|||||||
'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,
|
||||||
},
|
},
|
||||||
'ESPLORA': {
|
'ESPLORA': {
|
||||||
'REST_API_URL': 'http://127.0.0.1:3000',
|
'REST_API_URL': 'http://127.0.0.1:3000',
|
||||||
@@ -92,8 +93,7 @@ const defaults: IConfig = {
|
|||||||
'USERNAME': 'mempool',
|
'USERNAME': 'mempool',
|
||||||
'PASSWORD': 'mempool'
|
'PASSWORD': 'mempool'
|
||||||
},
|
},
|
||||||
'CORE_RPC_MINFEE': {
|
'SECOND_CORE_RPC': {
|
||||||
'ENABLED': false,
|
|
||||||
'HOST': '127.0.0.1',
|
'HOST': '127.0.0.1',
|
||||||
'PORT': 8332,
|
'PORT': 8332,
|
||||||
'USERNAME': 'mempool',
|
'USERNAME': 'mempool',
|
||||||
@@ -129,7 +129,7 @@ class Config implements IConfig {
|
|||||||
ESPLORA: IConfig['ESPLORA'];
|
ESPLORA: IConfig['ESPLORA'];
|
||||||
ELECTRUM: IConfig['ELECTRUM'];
|
ELECTRUM: IConfig['ELECTRUM'];
|
||||||
CORE_RPC: IConfig['CORE_RPC'];
|
CORE_RPC: IConfig['CORE_RPC'];
|
||||||
CORE_RPC_MINFEE: IConfig['CORE_RPC_MINFEE'];
|
SECOND_CORE_RPC: IConfig['SECOND_CORE_RPC'];
|
||||||
DATABASE: IConfig['DATABASE'];
|
DATABASE: IConfig['DATABASE'];
|
||||||
SYSLOG: IConfig['SYSLOG'];
|
SYSLOG: IConfig['SYSLOG'];
|
||||||
STATISTICS: IConfig['STATISTICS'];
|
STATISTICS: IConfig['STATISTICS'];
|
||||||
@@ -141,7 +141,7 @@ class Config implements IConfig {
|
|||||||
this.ESPLORA = configs.ESPLORA;
|
this.ESPLORA = configs.ESPLORA;
|
||||||
this.ELECTRUM = configs.ELECTRUM;
|
this.ELECTRUM = configs.ELECTRUM;
|
||||||
this.CORE_RPC = configs.CORE_RPC;
|
this.CORE_RPC = configs.CORE_RPC;
|
||||||
this.CORE_RPC_MINFEE = configs.CORE_RPC_MINFEE;
|
this.SECOND_CORE_RPC = configs.SECOND_CORE_RPC;
|
||||||
this.DATABASE = configs.DATABASE;
|
this.DATABASE = configs.DATABASE;
|
||||||
this.SYSLOG = configs.SYSLOG;
|
this.SYSLOG = configs.SYSLOG;
|
||||||
this.STATISTICS = configs.STATISTICS;
|
this.STATISTICS = configs.STATISTICS;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import logger from './logger';
|
|||||||
import backendInfo from './api/backend-info';
|
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';
|
||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
private wss: WebSocket.Server | undefined;
|
private wss: WebSocket.Server | undefined;
|
||||||
@@ -68,7 +69,8 @@ class Server {
|
|||||||
next();
|
next();
|
||||||
})
|
})
|
||||||
.use(express.urlencoded({ extended: true }))
|
.use(express.urlencoded({ extended: true }))
|
||||||
.use(express.json());
|
.use(express.text())
|
||||||
|
;
|
||||||
|
|
||||||
this.server = http.createServer(this.app);
|
this.server = http.createServer(this.app);
|
||||||
this.wss = new WebSocket.Server({ server: this.server });
|
this.wss = new WebSocket.Server({ server: this.server });
|
||||||
@@ -112,7 +114,7 @@ class Server {
|
|||||||
await memPool.$updateMemPoolInfo();
|
await memPool.$updateMemPoolInfo();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = `updateMempoolInfo: ${(e instanceof Error ? e.message : e)}`;
|
const msg = `updateMempoolInfo: ${(e instanceof Error ? e.message : e)}`;
|
||||||
if (config.CORE_RPC_MINFEE.ENABLED) {
|
if (config.MEMPOOL.USE_SECOND_NODE_FOR_MINFEE) {
|
||||||
logger.warn(msg);
|
logger.warn(msg);
|
||||||
} else {
|
} else {
|
||||||
logger.debug(msg);
|
logger.debug(msg);
|
||||||
@@ -141,6 +143,15 @@ class Server {
|
|||||||
if (this.wss) {
|
if (this.wss) {
|
||||||
websocketHandler.setWebsocketServer(this.wss);
|
websocketHandler.setWebsocketServer(this.wss);
|
||||||
}
|
}
|
||||||
|
if (config.MEMPOOL.NETWORK === 'liquid' && config.DATABASE.ENABLED) {
|
||||||
|
blocks.setNewBlockCallback(async () => {
|
||||||
|
try {
|
||||||
|
await elementsParser.$parse();
|
||||||
|
} catch (e) {
|
||||||
|
logger.warn('Elements parsing error: ' + (e instanceof Error ? e.message : e));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
websocketHandler.setupConnectionHandling();
|
websocketHandler.setupConnectionHandling();
|
||||||
statistics.setNewStatisticsEntryCallback(websocketHandler.handleNewStatistic.bind(websocketHandler));
|
statistics.setNewStatisticsEntryCallback(websocketHandler.handleNewStatistic.bind(websocketHandler));
|
||||||
blocks.setNewBlockCallback(websocketHandler.handleNewBlock.bind(websocketHandler));
|
blocks.setNewBlockCallback(websocketHandler.handleNewBlock.bind(websocketHandler));
|
||||||
@@ -159,6 +170,7 @@ class Server {
|
|||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'backend-info', routes.getBackendInfo)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'backend-info', routes.getBackendInfo)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'init-data', routes.getInitData)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'init-data', routes.getInitData)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'validate-address/:address', routes.validateAddress)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'validate-address/:address', routes.validateAddress)
|
||||||
|
.post(config.MEMPOOL.API_URL_PREFIX + 'tx/push', routes.$postTransactionForm)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'donations', async (req, res) => {
|
.get(config.MEMPOOL.API_URL_PREFIX + 'donations', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('https://mempool.space/api/v1/donations', { responseType: 'stream', timeout: 10000 });
|
const response = await axios.get('https://mempool.space/api/v1/donations', { responseType: 'stream', timeout: 10000 });
|
||||||
@@ -206,6 +218,8 @@ class Server {
|
|||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'statistics/3m', routes.get3MStatistics.bind(routes))
|
.get(config.MEMPOOL.API_URL_PREFIX + 'statistics/3m', routes.get3MStatistics.bind(routes))
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'statistics/6m', routes.get6MStatistics.bind(routes))
|
.get(config.MEMPOOL.API_URL_PREFIX + 'statistics/6m', routes.get6MStatistics.bind(routes))
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'statistics/1y', routes.get1YStatistics.bind(routes))
|
.get(config.MEMPOOL.API_URL_PREFIX + 'statistics/1y', routes.get1YStatistics.bind(routes))
|
||||||
|
.get(config.MEMPOOL.API_URL_PREFIX + 'statistics/2y', routes.get2YStatistics.bind(routes))
|
||||||
|
.get(config.MEMPOOL.API_URL_PREFIX + 'statistics/3y', routes.get3YStatistics.bind(routes))
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,6 +250,7 @@ class Server {
|
|||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'mempool/txids', routes.getMempoolTxIds)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'mempool/txids', routes.getMempoolTxIds)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'mempool/recent', routes.getRecentMempoolTransactions)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'mempool/recent', routes.getRecentMempoolTransactions)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId', routes.getTransaction)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId', routes.getTransaction)
|
||||||
|
.post(config.MEMPOOL.API_URL_PREFIX + 'tx', routes.$postTransaction)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/hex', routes.getRawTransaction)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/hex', routes.getRawTransaction)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/status', routes.getTransactionStatus)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/status', routes.getTransactionStatus)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/outspends', routes.getTransactionOutspends)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/outspends', routes.getTransactionOutspends)
|
||||||
@@ -254,6 +269,12 @@ class Server {
|
|||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'address-prefix/:prefix', routes.getAddressPrefix)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'address-prefix/:prefix', routes.getAddressPrefix)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.MEMPOOL.NETWORK === 'liquid' && config.DATABASE.ENABLED) {
|
||||||
|
this.app
|
||||||
|
.get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegs/month', routes.$getElementsPegsByMonth)
|
||||||
|
;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ import transactionUtils from './api/transaction-utils';
|
|||||||
import blocks from './api/blocks';
|
import blocks from './api/blocks';
|
||||||
import loadingIndicators from './api/loading-indicators';
|
import loadingIndicators from './api/loading-indicators';
|
||||||
import { Common } from './api/common';
|
import { Common } from './api/common';
|
||||||
import bitcoinBaseApi from './api/bitcoin/bitcoin-base.api';
|
import bitcoinClient from './api/bitcoin/bitcoin-client';
|
||||||
|
import elementsParser from './api/liquid/elements-parser';
|
||||||
|
|
||||||
class Routes {
|
class Routes {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
@@ -51,6 +52,14 @@ class Routes {
|
|||||||
res.json(statistics.getCache()['1y']);
|
res.json(statistics.getCache()['1y']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get2YStatistics(req: Request, res: Response) {
|
||||||
|
res.json(statistics.getCache()['2y']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get3YStatistics(req: Request, res: Response) {
|
||||||
|
res.json(statistics.getCache()['3y']);
|
||||||
|
}
|
||||||
|
|
||||||
public getInitData(req: Request, res: Response) {
|
public getInitData(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const result = websocketHandler.getInitData();
|
const result = websocketHandler.getInitData();
|
||||||
@@ -612,7 +621,7 @@ class Routes {
|
|||||||
const addressData = await bitcoinApi.$getAddress(req.params.address);
|
const addressData = await bitcoinApi.$getAddress(req.params.address);
|
||||||
res.json(addressData);
|
res.json(addressData);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error && e.message && e.message.indexOf('exceeds') > 0) {
|
if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) {
|
||||||
return res.status(413).send(e instanceof Error ? e.message : e);
|
return res.status(413).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
res.status(500).send(e instanceof Error ? e.message : e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
@@ -629,7 +638,7 @@ class Routes {
|
|||||||
const transactions = await bitcoinApi.$getAddressTransactions(req.params.address, req.params.txId);
|
const transactions = await bitcoinApi.$getAddressTransactions(req.params.address, req.params.txId);
|
||||||
res.json(transactions);
|
res.json(transactions);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error && e.message && e.message.indexOf('exceeds') > 0) {
|
if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) {
|
||||||
return res.status(413).send(e instanceof Error ? e.message : e);
|
return res.status(413).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
res.status(500).send(e instanceof Error ? e.message : e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
@@ -690,7 +699,7 @@ class Routes {
|
|||||||
|
|
||||||
public async validateAddress(req: Request, res: Response) {
|
public async validateAddress(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const result = await bitcoinBaseApi.$validateAddress(req.params.address);
|
const result = await bitcoinClient.validateAddress(req.params.address);
|
||||||
res.json(result);
|
res.json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e instanceof Error ? e.message : e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
@@ -715,14 +724,16 @@ class Routes {
|
|||||||
const nextRetargetHeight = blockHeight + remainingBlocks;
|
const nextRetargetHeight = blockHeight + remainingBlocks;
|
||||||
|
|
||||||
let difficultyChange = 0;
|
let difficultyChange = 0;
|
||||||
if (blocksInEpoch > 0) {
|
if (remainingBlocks < 1870) {
|
||||||
difficultyChange = (600 / (diff / blocksInEpoch ) - 1) * 100;
|
if (blocksInEpoch > 0) {
|
||||||
}
|
difficultyChange = (600 / (diff / blocksInEpoch ) - 1) * 100;
|
||||||
if (difficultyChange > 300) {
|
}
|
||||||
difficultyChange = 300;
|
if (difficultyChange > 300) {
|
||||||
}
|
difficultyChange = 300;
|
||||||
if (difficultyChange < -75) {
|
}
|
||||||
difficultyChange = -75;
|
if (difficultyChange < -75) {
|
||||||
|
difficultyChange = -75;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeAvgDiff = difficultyChange * 0.1;
|
const timeAvgDiff = difficultyChange * 0.1;
|
||||||
@@ -754,6 +765,48 @@ class Routes {
|
|||||||
res.status(500).send(e instanceof Error ? e.message : e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async $getElementsPegsByMonth(req: Request, res: Response) {
|
||||||
|
try {
|
||||||
|
const pegs = await elementsParser.$getPegDataByMonth();
|
||||||
|
res.json(pegs);
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async $postTransaction(req: Request, res: Response) {
|
||||||
|
res.setHeader('content-type', 'text/plain');
|
||||||
|
try {
|
||||||
|
let rawTx;
|
||||||
|
if (typeof req.body === 'object') {
|
||||||
|
rawTx = Object.keys(req.body)[0];
|
||||||
|
} else {
|
||||||
|
rawTx = req.body;
|
||||||
|
}
|
||||||
|
const txIdResult = await bitcoinApi.$sendRawTransaction(rawTx);
|
||||||
|
res.send(txIdResult);
|
||||||
|
} catch (e: any) {
|
||||||
|
res.status(400).send(e.message && e.code ? 'sendrawtransaction RPC error: ' + JSON.stringify({ code: e.code, message: e.message })
|
||||||
|
: (e.message || 'Error'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async $postTransactionForm(req: Request, res: Response) {
|
||||||
|
res.setHeader('content-type', 'text/plain');
|
||||||
|
const matches = /tx=([a-z0-9]+)/.exec(req.body);
|
||||||
|
let txHex = '';
|
||||||
|
if (matches && matches[1]) {
|
||||||
|
txHex = matches[1];
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const txIdResult = await bitcoinClient.sendRawTransaction(txHex);
|
||||||
|
res.send(txIdResult);
|
||||||
|
} catch (e: any) {
|
||||||
|
res.status(400).send(e.message && e.code ? 'sendrawtransaction RPC error: ' + JSON.stringify({ code: e.code, message: e.message })
|
||||||
|
: (e.message || 'Error'));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new Routes();
|
export default new Routes();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"target": "esnext",
|
"target": "esnext",
|
||||||
"lib": ["es2019"],
|
"lib": ["es2019", "dom"],
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:12-buster-slim AS builder
|
FROM node:16.10.0-buster-slim AS builder
|
||||||
|
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
COPY . .
|
COPY . .
|
||||||
@@ -8,7 +8,7 @@ RUN apt-get install -y build-essential python3 pkg-config
|
|||||||
RUN npm install
|
RUN npm install
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
FROM node:12-buster-slim
|
FROM node:16.10.0-buster-slim
|
||||||
|
|
||||||
WORKDIR /backend
|
WORKDIR /backend
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:12-buster-slim AS builder
|
FROM node:16.10.0-buster-slim AS builder
|
||||||
|
|
||||||
ARG commitHash
|
ARG commitHash
|
||||||
ENV DOCKER_COMMIT_HASH=${commitHash}
|
ENV DOCKER_COMMIT_HASH=${commitHash}
|
||||||
|
|||||||
1
frontend/.gitignore
vendored
1
frontend/.gitignore
vendored
@@ -34,6 +34,7 @@ speed-measure-plugin.json
|
|||||||
.history/*
|
.history/*
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
|
/.angular/cache
|
||||||
/.sass-cache
|
/.sass-cache
|
||||||
/connect.lock
|
/connect.lock
|
||||||
/coverage
|
/coverage
|
||||||
|
|||||||
@@ -1,11 +1,50 @@
|
|||||||
# mempool-frontend
|
# mempool-frontend
|
||||||
|
|
||||||
## Transifex Project
|
## Contributing
|
||||||
|
|
||||||
|
This package is used for the https://mempool.space, https://liquid.network and https://bisq.markets websites - there are npm scripts to setup all three, which effectively change how BASE_MODULE is configured:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ npm run config:defaults:mempool
|
||||||
|
$ npm run config:defaults:liquid
|
||||||
|
$ npm run config:defaults:bisq
|
||||||
|
```
|
||||||
|
|
||||||
|
Changes that affect the frontend codebase only can be done using the production backend so you don't need to spin up the entire Mempool infrastructure. This is very convenient in case you want to quickly improve the UI, fix typos or implement new features that don't require any backend changes.
|
||||||
|
|
||||||
|
Make your changes, install the project dependencies and run the frontend server as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ npm install
|
||||||
|
$ npm run serve:local-prod
|
||||||
|
```
|
||||||
|
|
||||||
|
The frontend will be available at http://localhost:4200/ and all API requests will be proxied to the production server at https://mempool.space
|
||||||
|
|
||||||
|
After making your changes, you can run our end-to-end automation suite and check for possible regressions:
|
||||||
|
|
||||||
|
Headless:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ npm run config:defaults:mempool && npm run cypress:run
|
||||||
|
```
|
||||||
|
|
||||||
|
Interactive:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ npm run config:defaults:mempool && npm run cypress:open
|
||||||
|
```
|
||||||
|
|
||||||
|
This will open the Cypress test runner, where you can select any of the test files to run.
|
||||||
|
|
||||||
|
If all tests are green, submit your PR and it will be reviewed by someone on the team as soon as possible.
|
||||||
|
|
||||||
|
## Translations: Transifex Project
|
||||||
|
|
||||||
The mempool frontend strings are localized into 20+ locales:
|
The mempool frontend strings are localized into 20+ locales:
|
||||||
https://www.transifex.com/mempool/mempool/dashboard/
|
https://www.transifex.com/mempool/mempool/dashboard/
|
||||||
|
|
||||||
## Translators
|
### Translators
|
||||||
|
|
||||||
* Arabic @baro0k
|
* Arabic @baro0k
|
||||||
* Czech @pixelmade2
|
* Czech @pixelmade2
|
||||||
@@ -27,8 +66,11 @@ https://www.transifex.com/mempool/mempool/dashboard/
|
|||||||
* Slovenian @thepkbadger
|
* Slovenian @thepkbadger
|
||||||
* Finnish @bio_bitcoin
|
* Finnish @bio_bitcoin
|
||||||
* Swedish @softsimon_
|
* Swedish @softsimon_
|
||||||
|
* Thai @Gusb3ll
|
||||||
* Turkish @stackmore
|
* Turkish @stackmore
|
||||||
* Ukrainian @volbil
|
* Ukrainian @volbil
|
||||||
* Vietnamese @bitcoin_vietnam
|
* Vietnamese @bitcoin_vietnam
|
||||||
* Chinese @wdljt
|
* Chinese @wdljt
|
||||||
* Russian @TonyCrusoe @Bitconan
|
* Russian @TonyCrusoe @Bitconan
|
||||||
|
* Romanian @mirceavesa
|
||||||
|
* Macedonian @SkechBoy
|
||||||
|
|||||||
@@ -94,6 +94,10 @@
|
|||||||
"translation": "src/locale/messages.sv.xlf",
|
"translation": "src/locale/messages.sv.xlf",
|
||||||
"baseHref": "/sv/"
|
"baseHref": "/sv/"
|
||||||
},
|
},
|
||||||
|
"th": {
|
||||||
|
"translation": "src/locale/messages.th.xlf",
|
||||||
|
"baseHref": "/th/"
|
||||||
|
},
|
||||||
"tr": {
|
"tr": {
|
||||||
"translation": "src/locale/messages.tr.xlf",
|
"translation": "src/locale/messages.tr.xlf",
|
||||||
"baseHref": "/tr/"
|
"baseHref": "/tr/"
|
||||||
@@ -114,10 +118,18 @@
|
|||||||
"translation": "src/locale/messages.hu.xlf",
|
"translation": "src/locale/messages.hu.xlf",
|
||||||
"baseHref": "/hu/"
|
"baseHref": "/hu/"
|
||||||
},
|
},
|
||||||
|
"mk": {
|
||||||
|
"translation": "src/locale/messages.mk.xlf",
|
||||||
|
"baseHref": "/mk/"
|
||||||
|
},
|
||||||
"zh": {
|
"zh": {
|
||||||
"translation": "src/locale/messages.zh.xlf",
|
"translation": "src/locale/messages.zh.xlf",
|
||||||
"baseHref": "/zh/"
|
"baseHref": "/zh/"
|
||||||
},
|
},
|
||||||
|
"ro": {
|
||||||
|
"translation": "src/locale/messages.ro.xlf",
|
||||||
|
"baseHref": "/ro/"
|
||||||
|
},
|
||||||
"ru": {
|
"ru": {
|
||||||
"translation": "src/locale/messages.ru.xlf",
|
"translation": "src/locale/messages.ru.xlf",
|
||||||
"baseHref": "/ru/"
|
"baseHref": "/ru/"
|
||||||
@@ -137,7 +149,6 @@
|
|||||||
"main": "src/main.ts",
|
"main": "src/main.ts",
|
||||||
"polyfills": "src/polyfills.ts",
|
"polyfills": "src/polyfills.ts",
|
||||||
"tsConfig": "tsconfig.app.json",
|
"tsConfig": "tsconfig.app.json",
|
||||||
"aot": true,
|
|
||||||
"assets": [
|
"assets": [
|
||||||
"src/favicon.ico",
|
"src/favicon.ico",
|
||||||
"src/resources",
|
"src/resources",
|
||||||
@@ -149,7 +160,13 @@
|
|||||||
],
|
],
|
||||||
"scripts": [
|
"scripts": [
|
||||||
"generated-config.js"
|
"generated-config.js"
|
||||||
]
|
],
|
||||||
|
"vendorChunk": true,
|
||||||
|
"extractLicenses": false,
|
||||||
|
"buildOptimizer": false,
|
||||||
|
"sourceMap": true,
|
||||||
|
"optimization": false,
|
||||||
|
"namedChunks": true
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
@@ -159,7 +176,14 @@
|
|||||||
"with": "src/environments/environment.prod.ts"
|
"with": "src/environments/environment.prod.ts"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"optimization": true,
|
"optimization": {
|
||||||
|
"scripts": true,
|
||||||
|
"styles": {
|
||||||
|
"minify": true,
|
||||||
|
"inlineCritical": false
|
||||||
|
},
|
||||||
|
"fonts": true
|
||||||
|
},
|
||||||
"outputHashing": "all",
|
"outputHashing": "all",
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"namedChunks": false,
|
"namedChunks": false,
|
||||||
@@ -178,7 +202,8 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"defaultConfiguration": ""
|
||||||
},
|
},
|
||||||
"serve": {
|
"serve": {
|
||||||
"builder": "@angular-devkit/build-angular:dev-server",
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
@@ -190,11 +215,11 @@
|
|||||||
"browserTarget": "mempool:build:production"
|
"browserTarget": "mempool:build:production"
|
||||||
},
|
},
|
||||||
"local": {
|
"local": {
|
||||||
"proxyConfig": "proxy.conf.json",
|
"proxyConfig": "proxy.conf.local.js",
|
||||||
"verbose": true
|
"verbose": true
|
||||||
},
|
},
|
||||||
"staging": {
|
"staging": {
|
||||||
"proxyConfig": "proxy.stg.conf.json",
|
"proxyConfig": "proxy.conf.js",
|
||||||
"disableHostCheck": true,
|
"disableHostCheck": true,
|
||||||
"host": "0.0.0.0",
|
"host": "0.0.0.0",
|
||||||
"verbose": true
|
"verbose": true
|
||||||
@@ -230,20 +255,6 @@
|
|||||||
"scripts": []
|
"scripts": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lint": {
|
|
||||||
"builder": "@angular-devkit/build-angular:tslint",
|
|
||||||
"options": {
|
|
||||||
"tsConfig": [
|
|
||||||
"tsconfig.app.json",
|
|
||||||
"tsconfig.spec.json",
|
|
||||||
"tsconfig.server.json",
|
|
||||||
"cypress/tsconfig.json"
|
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"**/node_modules/**"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"e2e": {
|
"e2e": {
|
||||||
"builder": "@cypress/schematic:cypress",
|
"builder": "@cypress/schematic:cypress",
|
||||||
"options": {
|
"options": {
|
||||||
@@ -262,7 +273,9 @@
|
|||||||
"options": {
|
"options": {
|
||||||
"outputPath": "dist/mempool/server",
|
"outputPath": "dist/mempool/server",
|
||||||
"main": "server.ts",
|
"main": "server.ts",
|
||||||
"tsConfig": "tsconfig.server.json"
|
"tsConfig": "tsconfig.server.json",
|
||||||
|
"sourceMap": true,
|
||||||
|
"optimization": false
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
@@ -277,7 +290,8 @@
|
|||||||
"localize": true,
|
"localize": true,
|
||||||
"optimization": true
|
"optimization": true
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"defaultConfiguration": ""
|
||||||
},
|
},
|
||||||
"serve-ssr": {
|
"serve-ssr": {
|
||||||
"builder": "@nguniversal/builders:ssr-dev-server",
|
"builder": "@nguniversal/builders:ssr-dev-server",
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
describe('Bisq', () => {
|
describe('Bisq', () => {
|
||||||
let baseModule;
|
const baseModule = Cypress.env("BASE_MODULE");
|
||||||
|
const basePath = (baseModule === 'bisq') ? '' : '/bisq';
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
baseModule = (Cypress.env('BASE_MODULE') && Cypress.env('BASE_MODULE') === 'bisq') ? '' : '/bisq';
|
|
||||||
|
|
||||||
cy.intercept('/sockjs-node/info*').as('socket');
|
cy.intercept('/sockjs-node/info*').as('socket');
|
||||||
cy.intercept('/bisq/api/markets/hloc?market=btc_usd&interval=day').as('hloc');
|
cy.intercept('/bisq/api/markets/hloc?market=btc_usd&interval=day').as('hloc');
|
||||||
cy.intercept('/bisq/api/markets/ticker').as('ticker');
|
cy.intercept('/bisq/api/markets/ticker').as('ticker');
|
||||||
@@ -23,15 +23,15 @@ describe('Bisq', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Cypress.env("BASE_MODULE") === '' || Cypress.env("BASE_MODULE") !== 'liquid') {
|
if (baseModule === 'mempool' || baseModule === 'bisq') {
|
||||||
|
|
||||||
it('loads the dashboard', () => {
|
it('loads the dashboard', () => {
|
||||||
cy.visit(`${baseModule}`);
|
cy.visit(`${basePath}`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loads the transactions screen', () => {
|
it('loads the transactions screen', () => {
|
||||||
cy.visit(`${baseModule}`);
|
cy.visit(`${basePath}`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('li:nth-of-type(2) > a').click().then(() => {
|
cy.get('li:nth-of-type(2) > a').click().then(() => {
|
||||||
cy.get('.table > tr').should('have.length', 50);
|
cy.get('.table > tr').should('have.length', 50);
|
||||||
@@ -39,7 +39,7 @@ describe('Bisq', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('loads the blocks screen', () => {
|
it('loads the blocks screen', () => {
|
||||||
cy.visit(`${baseModule}`);
|
cy.visit(`${basePath}`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('li:nth-of-type(3) > a').click().then(() => {
|
cy.get('li:nth-of-type(3) > a').click().then(() => {
|
||||||
cy.wait('@blocks');
|
cy.wait('@blocks');
|
||||||
@@ -48,7 +48,7 @@ describe('Bisq', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('loads the stats screen', () => {
|
it('loads the stats screen', () => {
|
||||||
cy.visit(`${baseModule}`);
|
cy.visit(`${basePath}`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('li:nth-of-type(4) > a').click().then(() => {
|
cy.get('li:nth-of-type(4) > a').click().then(() => {
|
||||||
cy.wait('@stats');
|
cy.wait('@stats');
|
||||||
@@ -56,7 +56,7 @@ describe('Bisq', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('loads the api screen', () => {
|
it('loads the api screen', () => {
|
||||||
cy.visit(`${baseModule}`);
|
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('.card').should('have.length.at.least', 1);
|
||||||
@@ -67,7 +67,7 @@ describe('Bisq', () => {
|
|||||||
|
|
||||||
it('shows blocks pagination with 5 pages (desktop)', () => {
|
it('shows blocks pagination with 5 pages (desktop)', () => {
|
||||||
cy.viewport(760, 800);
|
cy.viewport(760, 800);
|
||||||
cy.visit(`${baseModule}/blocks`);
|
cy.visit(`${basePath}/blocks`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('tbody tr').should('have.length', 10);
|
cy.get('tbody tr').should('have.length', 10);
|
||||||
// 5 pages + 4 buttons = 9 buttons
|
// 5 pages + 4 buttons = 9 buttons
|
||||||
@@ -76,13 +76,13 @@ describe('Bisq', () => {
|
|||||||
|
|
||||||
it('shows blocks pagination with 3 pages (mobile)', () => {
|
it('shows blocks pagination with 3 pages (mobile)', () => {
|
||||||
cy.viewport(669, 800);
|
cy.viewport(669, 800);
|
||||||
cy.visit(`${baseModule}/blocks`);
|
cy.visit(`${basePath}/blocks`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('tbody tr').should('have.length', 10);
|
cy.get('tbody tr').should('have.length', 10);
|
||||||
// 3 pages + 4 buttons = 7 buttons
|
// 3 pages + 4 buttons = 7 buttons
|
||||||
cy.get('.pagination-container ul.pagination').first().children().should('have.length', 7);
|
cy.get('.pagination-container ul.pagination').first().children().should('have.length', 7);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
it.skip("Tests cannot be run on the selected BASE_MODULE");
|
it.skip(`Tests cannot be run on the selected BASE_MODULE ${baseModule}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
describe('Liquid', () => {
|
describe('Liquid', () => {
|
||||||
let baseModule;
|
const baseModule = Cypress.env("BASE_MODULE");
|
||||||
beforeEach(() => {
|
const basePath = (baseModule === 'liquid') ? '' : '/liquid';
|
||||||
baseModule = (Cypress.env('BASE_MODULE') && Cypress.env('BASE_MODULE') === 'liquid') ? '' : '/liquid';
|
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
cy.intercept('/liquid/api/block/**').as('block');
|
cy.intercept('/liquid/api/block/**').as('block');
|
||||||
cy.intercept('/liquid/api/blocks/').as('blocks');
|
cy.intercept('/liquid/api/blocks/').as('blocks');
|
||||||
cy.intercept('/liquid/api/tx/**/outspends').as('outspends');
|
cy.intercept('/liquid/api/tx/**/outspends').as('outspends');
|
||||||
@@ -16,30 +16,36 @@ describe('Liquid', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Cypress.env("BASE_MODULE") === '' || Cypress.env("BASE_MODULE") !== 'bisq') {
|
if (baseModule === 'mempool' || baseModule === 'liquid') {
|
||||||
|
|
||||||
|
it('check first mempool block after skeleton loads', () => {
|
||||||
|
cy.visit(`${basePath}`);
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('#mempool-block-0 > .blockLink').should('exist');
|
||||||
|
});
|
||||||
|
|
||||||
it('loads the dashboard', () => {
|
it('loads the dashboard', () => {
|
||||||
cy.visit(`${baseModule}`);
|
cy.visit(`${basePath}`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loads the blocks page', () => {
|
it('loads the blocks page', () => {
|
||||||
cy.visit(`${baseModule}/blocks`);
|
cy.visit(`${basePath}/blocks`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loads a specific block page', () => {
|
it('loads a specific block page', () => {
|
||||||
cy.visit(`${baseModule}/block/7e1369a23a5ab861e7bdede2aadcccae4ea873ffd9caf11c7c5541eb5bcdff54`);
|
cy.visit(`${basePath}/block/7e1369a23a5ab861e7bdede2aadcccae4ea873ffd9caf11c7c5541eb5bcdff54`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loads the graphs page', () => {
|
it('loads the graphs page', () => {
|
||||||
cy.visit(`${baseModule}/graphs`);
|
cy.visit(`${basePath}/graphs`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loads the tv page - desktop', () => {
|
it('loads the tv page - desktop', () => {
|
||||||
cy.visit(`${baseModule}`);
|
cy.visit(`${basePath}`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('li:nth-of-type(3) > a').click().then(() => {
|
cy.get('li:nth-of-type(3) > a').click().then(() => {
|
||||||
cy.wait(1000);
|
cy.wait(1000);
|
||||||
@@ -47,7 +53,7 @@ describe('Liquid', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('loads the graphs page - mobile', () => {
|
it('loads the graphs page - mobile', () => {
|
||||||
cy.visit(`${baseModule}`)
|
cy.visit(`${basePath}`)
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('li:nth-of-type(3) > a').click().then(() => {
|
cy.get('li:nth-of-type(3) > a').click().then(() => {
|
||||||
cy.viewport('iphone-6');
|
cy.viewport('iphone-6');
|
||||||
@@ -58,13 +64,13 @@ describe('Liquid', () => {
|
|||||||
|
|
||||||
describe('assets', () => {
|
describe('assets', () => {
|
||||||
it('shows the assets screen', () => {
|
it('shows the assets screen', () => {
|
||||||
cy.visit(`${baseModule}/assets`);
|
cy.visit(`${basePath}/assets`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('table tr').should('have.length.at.least', 5);
|
cy.get('table tr').should('have.length.at.least', 5);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows searching assets', () => {
|
it('allows searching assets', () => {
|
||||||
cy.visit(`${baseModule}/assets`);
|
cy.visit(`${basePath}/assets`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('.container-xl input').click().type('Liquid Bitcoin').then(() => {
|
cy.get('.container-xl input').click().type('Liquid Bitcoin').then(() => {
|
||||||
cy.get('table tr').should('have.length', 1);
|
cy.get('table tr').should('have.length', 1);
|
||||||
@@ -72,7 +78,7 @@ describe('Liquid', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('shows a specific asset ID', () => {
|
it('shows a specific asset ID', () => {
|
||||||
cy.visit(`${baseModule}/assets`);
|
cy.visit(`${basePath}/assets`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('.container-xl input').click().type('Liquid AUD').then(() => {
|
cy.get('.container-xl input').click().type('Liquid AUD').then(() => {
|
||||||
cy.get('table tr td:nth-of-type(1) a').click();
|
cy.get('table tr td:nth-of-type(1) a').click();
|
||||||
@@ -84,27 +90,27 @@ describe('Liquid', () => {
|
|||||||
describe('unblinded TX', () => {
|
describe('unblinded TX', () => {
|
||||||
|
|
||||||
it('should not show an unblinding error message for regular txs', () => {
|
it('should not show an unblinding error message for regular txs', () => {
|
||||||
cy.visit(`${baseModule}/tx/82a479043ec3841e0d3f829afc8df4f0e2bbd675a13f013ea611b2fde0027d45`);
|
cy.visit(`${basePath}/tx/82a479043ec3841e0d3f829afc8df4f0e2bbd675a13f013ea611b2fde0027d45`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('.error-unblinded' ).should('not.exist');
|
cy.get('.error-unblinded' ).should('not.exist');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('show unblinded TX', () => {
|
it('show unblinded TX', () => {
|
||||||
cy.visit(`${baseModule}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc,2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3a`);
|
cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc,2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3a`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('#table-tx-vin tr').should('have.class', 'assetBox');
|
cy.get('#table-tx-vin tr').should('have.class', 'assetBox');
|
||||||
cy.get('#table-tx-vout tr').should('have.class', 'assetBox');
|
cy.get('#table-tx-vout tr').should('have.class', 'assetBox');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('show empty unblinded TX', () => {
|
it('show empty unblinded TX', () => {
|
||||||
cy.visit(`${baseModule}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=`);
|
cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('#table-tx-vin tr').should('have.class', '');
|
cy.get('#table-tx-vin tr').should('have.class', '');
|
||||||
cy.get('#table-tx-vout tr').should('have.class', '');
|
cy.get('#table-tx-vout tr').should('have.class', '');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('show invalid unblinded TX hex', () => {
|
it('show invalid unblinded TX hex', () => {
|
||||||
cy.visit(`${baseModule}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=123`);
|
cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=123`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('#table-tx-vin tr').should('have.class', '');
|
cy.get('#table-tx-vin tr').should('have.class', '');
|
||||||
cy.get('#table-tx-vout tr').should('have.class', '');
|
cy.get('#table-tx-vout tr').should('have.class', '');
|
||||||
@@ -112,36 +118,36 @@ describe('Liquid', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('show first unblinded vout', () => {
|
it('show first unblinded vout', () => {
|
||||||
cy.visit(`${baseModule}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc`);
|
cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('#table-tx-vout tr:first-child()').should('have.class', 'assetBox');
|
cy.get('#table-tx-vout tr:first-child()').should('have.class', 'assetBox');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('show second unblinded vout', () => {
|
it('show second unblinded vout', () => {
|
||||||
cy.visit(`${baseModule}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3a`);
|
cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3a`);
|
||||||
cy.get('#table-tx-vout tr').should('have.class', 'assetBox');
|
cy.get('#table-tx-vout tr').should('have.class', 'assetBox');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('show invalid error unblinded TX', () => {
|
it('show invalid error unblinded TX', () => {
|
||||||
cy.visit(`${baseModule}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc,2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3c`);
|
cy.visit(`${basePath}/tx/f2f41c0850e8e7e3f1af233161fd596662e67c11ef10ed15943884186fbb7f46#blinded=100000,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,0ab9f70650f16b1db8dfada05237f7d0d65191c3a13183da8a2ddddfbde9a2ad,fd98b2edc5530d76acd553f206a431f4c1fab27e10e290ad719582af878e98fc,2364760,6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d,90c7a43b15b905bca045ca42a01271cfe71d2efe3133f4197792c24505cb32ed,12eb5959d9293b8842e7dd8bc9aa9639fd3fd031c5de3ba911adeca94eb57a3c`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('#table-tx-vout tr').should('have.class', 'assetBox');
|
cy.get('#table-tx-vout tr').should('have.class', 'assetBox');
|
||||||
cy.get('.error-unblinded' ).contains('Error: Invalid blinding data.');
|
cy.get('.error-unblinded' ).contains('Error: Invalid blinding data.');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows asset peg in/out and burn transactions', () => {
|
it('shows asset peg in/out and burn transactions', () => {
|
||||||
cy.visit(`${baseModule}/asset/6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`);
|
cy.visit(`${basePath}/asset/6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('#table-tx-vout tr').not('.assetBox');
|
cy.get('#table-tx-vout tr').not('.assetBox');
|
||||||
cy.get('#table-tx-vin tr').not('.assetBox');
|
cy.get('#table-tx-vin tr').not('.assetBox');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prevents regressing issue #644', () => {
|
it('prevents regressing issue #644', () => {
|
||||||
cy.visit(`${baseModule}/tx/393b890966f305e7c440fcfb12a13f51a7a9011cc59ff5f14f6f93214261bd82`);
|
cy.visit(`${basePath}/tx/393b890966f305e7c440fcfb12a13f51a7a9011cc59ff5f14f6f93214261bd82`);
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
it.skip("Tests cannot be run on the selected BASE_MODULE");
|
it.skip(`Tests cannot be run on the selected BASE_MODULE ${baseModule}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { emitMempoolInfo, dropWebSocket } from "../../support/websocket";
|
import { emitMempoolInfo, dropWebSocket } from "../../support/websocket";
|
||||||
|
|
||||||
|
const baseModule = Cypress.env("BASE_MODULE");
|
||||||
|
|
||||||
describe('Mainnet', () => {
|
describe('Mainnet', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
//cy.intercept('/sockjs-node/info*').as('socket');
|
//cy.intercept('/sockjs-node/info*').as('socket');
|
||||||
@@ -20,7 +22,13 @@ describe('Mainnet', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Cypress.env("BASE_MODULE") === '' || Cypress.env("BASE_MODULE") === 'mempool') {
|
if (baseModule === 'mempool') {
|
||||||
|
|
||||||
|
it('check first mempool block after skeleton loads', () => {
|
||||||
|
cy.visit('/');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('#mempool-block-0 > .blockLink').should('exist');
|
||||||
|
});
|
||||||
|
|
||||||
it('loads the status screen', () => {
|
it('loads the status screen', () => {
|
||||||
cy.visit('/status');
|
cy.visit('/status');
|
||||||
@@ -61,6 +69,22 @@ describe('Mainnet', () => {
|
|||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('check op_return tx tooltip', () => {
|
||||||
|
cy.visit('/block/00000000000000000003c5f542bed265319c6cf64238cf1f1bb9bca3ebf686d2');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('tbody > :nth-child(2) > :nth-child(1) > a').first().trigger('onmouseover');
|
||||||
|
cy.get('tbody > :nth-child(2) > :nth-child(1) > a').first().trigger('mouseenter');
|
||||||
|
cy.get('.tooltip-inner').should('be.visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('check op_return coinbase tooltip', () => {
|
||||||
|
cy.visit('/block/00000000000000000003c5f542bed265319c6cf64238cf1f1bb9bca3ebf686d2');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('div > a > .badge').first().trigger('onmouseover');
|
||||||
|
cy.get('div > a > .badge').first().trigger('mouseenter');
|
||||||
|
cy.get('.tooltip-inner').should('be.visible');
|
||||||
|
});
|
||||||
|
|
||||||
describe('search', () => {
|
describe('search', () => {
|
||||||
it('allows searching for partial Bitcoin addresses', () => {
|
it('allows searching for partial Bitcoin addresses', () => {
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
@@ -87,18 +111,33 @@ describe('Mainnet', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
['BC1PQYQSZQ', 'bc1PqYqSzQ'].forEach((searchTerm) => {
|
['BC1PQYQSZQ', 'bc1PqYqSzQ'].forEach((searchTerm) => {
|
||||||
it(`allows searching for partial case insensitive bc1 addresses: ${searchTerm}`, () => {
|
it(`allows searching for partial case insensitive bech32m addresses: ${searchTerm}`, () => {
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
cy.get('.search-box-container > .form-control').type(searchTerm).then(() => {
|
cy.get('.search-box-container > .form-control').type(searchTerm).then(() => {
|
||||||
cy.get('ngb-typeahead-window button.dropdown-item').should('have.length', 1);
|
cy.get('ngb-typeahead-window button.dropdown-item').should('have.length', 1);
|
||||||
cy.get('ngb-typeahead-window button.dropdown-item.active').click().then(() => {
|
cy.get('ngb-typeahead-window button.dropdown-item.active').click().then(() => {
|
||||||
cy.url().should('include', '/address/bc1pqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs3wf0qm');
|
cy.url().should('include', '/address/bc1pqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsyjer9e');
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('.text-center').should('not.have.text', 'Invalid Bitcoin address');
|
cy.get('.text-center').should('not.have.text', 'Invalid Bitcoin address');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
['BC1Q000375VXCU', 'bC1q000375vXcU'].forEach((searchTerm) => {
|
||||||
|
it(`allows searching for partial case insensitive bech32 addresses: ${searchTerm}`, () => {
|
||||||
|
cy.visit('/');
|
||||||
|
cy.get('.search-box-container > .form-control').type(searchTerm).then(() => {
|
||||||
|
cy.get('ngb-typeahead-window button.dropdown-item').should('have.length', 1);
|
||||||
|
cy.get('ngb-typeahead-window button.dropdown-item.active').click().then(() => {
|
||||||
|
cy.url().should('include', '/address/bc1q000375vxcuf5v04lmwy22vy2thvhqkxghgq7dy');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('.text-center').should('not.have.text', 'Invalid Bitcoin address');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('blocks navigation', () => {
|
describe('blocks navigation', () => {
|
||||||
@@ -232,7 +271,7 @@ describe('Mainnet', () => {
|
|||||||
cy.changeNetwork("bisq");
|
cy.changeNetwork("bisq");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loads the dashboard with the skeleton blocks', () => {
|
it.skip('loads the dashboard with the skeleton blocks', () => {
|
||||||
cy.mockMempoolSocket();
|
cy.mockMempoolSocket();
|
||||||
cy.visit("/");
|
cy.visit("/");
|
||||||
cy.get(':nth-child(1) > #bitcoin-block-0').should('be.visible');
|
cy.get(':nth-child(1) > #bitcoin-block-0').should('be.visible');
|
||||||
@@ -269,6 +308,33 @@ describe('Mainnet', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('graphs page', () => {
|
||||||
|
it('check buttons - mobile', () => {
|
||||||
|
cy.viewport('iphone-6');
|
||||||
|
cy.visit('/graphs');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('.small-buttons > :nth-child(2)').should('be.visible');
|
||||||
|
cy.get('#dropdownFees').should('be.visible');
|
||||||
|
cy.get('.btn-group').should('be.visible');
|
||||||
|
});
|
||||||
|
it('check buttons - tablet', () => {
|
||||||
|
cy.viewport('ipad-2');
|
||||||
|
cy.visit('/graphs');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('.small-buttons > :nth-child(2)').should('be.visible');
|
||||||
|
cy.get('#dropdownFees').should('be.visible');
|
||||||
|
cy.get('.btn-group').should('be.visible');
|
||||||
|
});
|
||||||
|
it('check buttons - desktop', () => {
|
||||||
|
cy.viewport('macbook-16');
|
||||||
|
cy.visit('/graphs');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('.small-buttons > :nth-child(2)').should('be.visible');
|
||||||
|
cy.get('#dropdownFees').should('be.visible');
|
||||||
|
cy.get('.btn-group').should('be.visible');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('loads the tv screen - desktop', () => {
|
it('loads the tv screen - desktop', () => {
|
||||||
cy.viewport('macbook-16');
|
cy.viewport('macbook-16');
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
@@ -286,7 +352,7 @@ describe('Mainnet', () => {
|
|||||||
cy.visit('/tv');
|
cy.visit('/tv');
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
cy.get('.chart-holder');
|
cy.get('.chart-holder');
|
||||||
cy.get('.blockchain-wrapper').should('be.visible');
|
cy.get('.blockchain-wrapper').should('not.visible');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loads the api screen', () => {
|
it('loads the api screen', () => {
|
||||||
@@ -363,6 +429,6 @@ describe('Mainnet', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
it.skip("Tests cannot be run on the selected BASE_MODULE");
|
it.skip(`Tests cannot be run on the selected BASE_MODULE ${baseModule}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { emitMempoolInfo } from "../../support/websocket";
|
import { emitMempoolInfo } from "../../support/websocket";
|
||||||
|
|
||||||
|
const baseModule = Cypress.env("BASE_MODULE");
|
||||||
|
|
||||||
describe('Signet', () => {
|
describe('Signet', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.intercept('/api/block-height/*').as('block-height');
|
cy.intercept('/api/block-height/*').as('block-height');
|
||||||
@@ -9,13 +11,19 @@ describe('Signet', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
if (Cypress.env("BASE_MODULE") === '' || Cypress.env("BASE_MODULE") === 'mempool') {
|
if (baseModule === 'mempool') {
|
||||||
it('loads the dashboard', () => {
|
it('loads the dashboard', () => {
|
||||||
cy.visit('/signet');
|
cy.visit('/signet');
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loads the dashboard with the skeleton blocks', () => {
|
it('check first mempool block after skeleton loads', () => {
|
||||||
|
cy.visit('/');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('#mempool-block-0 > .blockLink').should('exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.skip('loads the dashboard with the skeleton blocks', () => {
|
||||||
cy.mockMempoolSocket();
|
cy.mockMempoolSocket();
|
||||||
cy.visit("/signet");
|
cy.visit("/signet");
|
||||||
cy.get(':nth-child(1) > #bitcoin-block-0').should('be.visible');
|
cy.get(':nth-child(1) > #bitcoin-block-0').should('be.visible');
|
||||||
@@ -126,6 +134,6 @@ describe('Signet', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
it.skip("Tests cannot be run on the selected BASE_MODULE");
|
it.skip(`Tests cannot be run on the selected BASE_MODULE ${baseModule}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { confirmAddress, emitMempoolInfo, sendWsMock, showNewTx, startTrackingAddress } from "../../support/websocket";
|
import { confirmAddress, emitMempoolInfo, sendWsMock, showNewTx, startTrackingAddress } from "../../support/websocket";
|
||||||
|
|
||||||
|
const baseModule = Cypress.env("BASE_MODULE");
|
||||||
|
|
||||||
describe('Testnet', () => {
|
describe('Testnet', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.intercept('/api/block-height/*').as('block-height');
|
cy.intercept('/api/block-height/*').as('block-height');
|
||||||
@@ -8,14 +10,20 @@ describe('Testnet', () => {
|
|||||||
cy.intercept('/api/tx/*/outspends').as('tx-outspends');
|
cy.intercept('/api/tx/*/outspends').as('tx-outspends');
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Cypress.env("BASE_MODULE") === '' || Cypress.env("BASE_MODULE") === 'mempool') {
|
if (baseModule === 'mempool') {
|
||||||
|
|
||||||
it('loads the dashboard', () => {
|
it('loads the dashboard', () => {
|
||||||
cy.visit('/testnet');
|
cy.visit('/testnet');
|
||||||
cy.waitForSkeletonGone();
|
cy.waitForSkeletonGone();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loads the dashboard with the skeleton blocks', () => {
|
it('check first mempool block after skeleton loads', () => {
|
||||||
|
cy.visit('/');
|
||||||
|
cy.waitForSkeletonGone();
|
||||||
|
cy.get('#mempool-block-0 > .blockLink').should('exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.skip('loads the dashboard with the skeleton blocks', () => {
|
||||||
cy.mockMempoolSocket();
|
cy.mockMempoolSocket();
|
||||||
cy.visit("/testnet");
|
cy.visit("/testnet");
|
||||||
cy.get(':nth-child(1) > #bitcoin-block-0').should('be.visible');
|
cy.get(':nth-child(1) > #bitcoin-block-0').should('be.visible');
|
||||||
@@ -123,6 +131,6 @@ describe('Testnet', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
it.skip("Tests cannot be run on the selected BASE_MODULE");
|
it.skip(`Tests cannot be run on the selected BASE_MODULE ${baseModule}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1 +1,13 @@
|
|||||||
module.exports = (on, config) => {}
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const CONFIG_FILE = 'mempool-frontend-config.json';
|
||||||
|
|
||||||
|
module.exports = (on, config) => {
|
||||||
|
if (fs.existsSync(CONFIG_FILE)) {
|
||||||
|
let contents = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
||||||
|
config.env.BASE_MODULE = contents.BASE_MODULE ? contents.BASE_MODULE : 'mempool';
|
||||||
|
} else {
|
||||||
|
config.env.BASE_MODULE = 'mempool';
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|||||||
25012
frontend/package-lock.json
generated
25012
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mempool-frontend",
|
"name": "mempool-frontend",
|
||||||
"version": "2.2.2",
|
"version": "2.3.0-dev",
|
||||||
"description": "Bitcoin mempool visualizer and blockchain explorer backend",
|
"description": "Bitcoin mempool visualizer and blockchain explorer backend",
|
||||||
"license": "GNU Affero General Public License v3.0",
|
"license": "GNU Affero General Public License v3.0",
|
||||||
"homepage": "https://mempool.space",
|
"homepage": "https://mempool.space",
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "./node_modules/@angular/cli/bin/ng",
|
"ng": "./node_modules/@angular/cli/bin/ng",
|
||||||
"tsc": "./node_modules/typescript/bin/tsc",
|
"tsc": "./node_modules/typescript/bin/tsc",
|
||||||
"i18n-extract-from-source": "./node_modules/@angular/cli/bin/ng extract-i18n --ivy --out-file ./src/locale/messages.xlf",
|
"i18n-extract-from-source": "./node_modules/@angular/cli/bin/ng extract-i18n --out-file ./src/locale/messages.xlf",
|
||||||
"i18n-pull-from-transifex": "tx pull -a --parallel --minimum-perc 1 --force",
|
"i18n-pull-from-transifex": "tx pull -a --parallel --minimum-perc 1 --force",
|
||||||
"serve": "npm run generate-config && ng serve -c local",
|
"serve": "npm run generate-config && ng serve -c local",
|
||||||
"serve:stg": "npm run generate-config && ng serve -c staging",
|
"serve:stg": "npm run generate-config && ng serve -c staging",
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
"start": "npm run generate-config && npm run sync-assets-dev && ng serve -c local",
|
"start": "npm run generate-config && npm run sync-assets-dev && ng serve -c local",
|
||||||
"start:stg": "npm run generate-config && npm run sync-assets-dev && ng serve -c staging",
|
"start:stg": "npm run generate-config && npm run sync-assets-dev && ng serve -c staging",
|
||||||
"start:local-prod": "npm run generate-config && npm run sync-assets-dev && ng serve -c local-prod",
|
"start:local-prod": "npm run generate-config && npm run sync-assets-dev && ng serve -c local-prod",
|
||||||
"build": "npm run generate-config && ng build --prod --localize && npm run sync-assets && npm run build-mempool.js",
|
"build": "npm run generate-config && ng build --configuration production --localize && npm run sync-assets && npm run build-mempool.js",
|
||||||
"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",
|
||||||
@@ -53,34 +53,38 @@
|
|||||||
"cypress:run:ci": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-prod 4200 cypress:run:record"
|
"cypress:run:ci": "node update-config.js TESTNET_ENABLED=true SIGNET_ENABLED=true LIQUID_ENABLED=true BISQ_ENABLED=true ITEMS_PER_PAGE=25 && npm run generate-config && start-server-and-test serve:local-prod 4200 cypress:run:record"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "~11.2.8",
|
"@angular-devkit/build-angular": "^13.0.4",
|
||||||
"@angular/common": "~11.2.8",
|
"@angular/animations": "~13.0.3",
|
||||||
"@angular/compiler": "~11.2.8",
|
"@angular/cli": "~13.0.4",
|
||||||
"@angular/core": "~11.2.8",
|
"@angular/common": "~13.0.3",
|
||||||
"@angular/forms": "~11.2.8",
|
"@angular/compiler": "~13.0.3",
|
||||||
"@angular/localize": "^11.2.8",
|
"@angular/core": "~13.0.3",
|
||||||
"@angular/platform-browser": "~11.2.8",
|
"@angular/forms": "~13.0.3",
|
||||||
"@angular/platform-browser-dynamic": "~11.2.8",
|
"@angular/localize": "^13.0.3",
|
||||||
"@angular/platform-server": "~11.2.8",
|
"@angular/platform-browser": "~13.0.3",
|
||||||
"@angular/router": "~11.2.8",
|
"@angular/platform-browser-dynamic": "~13.0.3",
|
||||||
|
"@angular/platform-server": "~13.0.3",
|
||||||
|
"@angular/router": "~13.0.3",
|
||||||
"@fortawesome/angular-fontawesome": "^0.8.2",
|
"@fortawesome/angular-fontawesome": "^0.8.2",
|
||||||
"@fortawesome/fontawesome-common-types": "^0.2.35",
|
"@fortawesome/fontawesome-common-types": "^0.2.35",
|
||||||
"@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",
|
||||||
"@mempool/chartist": "^0.11.4",
|
"@juggle/resize-observer": "^3.3.1",
|
||||||
"@mempool/mempool.js": "^2.2.4",
|
"@mempool/mempool.js": "^2.2.4",
|
||||||
"@ng-bootstrap/ng-bootstrap": "^7.0.0",
|
"@ng-bootstrap/ng-bootstrap": "^10.0.0",
|
||||||
"@nguniversal/express-engine": "11.2.1",
|
"@nguniversal/express-engine": "11.2.1",
|
||||||
"@types/qrcode": "^1.3.4",
|
"@types/qrcode": "1.4.1",
|
||||||
"bootstrap": "4.5.0",
|
"bootstrap": "4.5.0",
|
||||||
"browserify": "^17.0.0",
|
"browserify": "^17.0.0",
|
||||||
"clipboard": "^2.0.4",
|
"clipboard": "^2.0.4",
|
||||||
"domino": "^2.1.6",
|
"domino": "^2.1.6",
|
||||||
|
"echarts": "^5.1.2",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"lightweight-charts": "^3.3.0",
|
"lightweight-charts": "^3.3.0",
|
||||||
"ngx-bootrap-multiselect": "^2.0.0",
|
"ngx-bootrap-multiselect": "^2.0.0",
|
||||||
|
"ngx-echarts": "^7.0.1",
|
||||||
"ngx-infinite-scroll": "^10.0.1",
|
"ngx-infinite-scroll": "^10.0.1",
|
||||||
"qrcode": "^1.4.4",
|
"qrcode": "1.5.0",
|
||||||
"rxjs": "^6.6.7",
|
"rxjs": "^6.6.7",
|
||||||
"tinyify": "^3.0.0",
|
"tinyify": "^3.0.0",
|
||||||
"tlite": "^0.1.9",
|
"tlite": "^0.1.9",
|
||||||
@@ -88,10 +92,8 @@
|
|||||||
"zone.js": "~0.11.4"
|
"zone.js": "~0.11.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "^0.1102.7",
|
"@angular/compiler-cli": "~13.0.3",
|
||||||
"@angular/cli": "~11.2.7",
|
"@angular/language-service": "~13.0.3",
|
||||||
"@angular/compiler-cli": "~11.2.8",
|
|
||||||
"@angular/language-service": "~11.2.8",
|
|
||||||
"@nguniversal/builders": "^11.2.1",
|
"@nguniversal/builders": "^11.2.1",
|
||||||
"@types/express": "^4.17.0",
|
"@types/express": "^4.17.0",
|
||||||
"@types/jasmine": "~3.6.0",
|
"@types/jasmine": "~3.6.0",
|
||||||
@@ -101,21 +103,21 @@
|
|||||||
"http-proxy-middleware": "^1.0.5",
|
"http-proxy-middleware": "^1.0.5",
|
||||||
"jasmine-core": "~3.6.0",
|
"jasmine-core": "~3.6.0",
|
||||||
"jasmine-spec-reporter": "~5.0.0",
|
"jasmine-spec-reporter": "~5.0.0",
|
||||||
"karma": "~6.1.0",
|
"karma": "~6.3.4",
|
||||||
"karma-chrome-launcher": "~3.1.0",
|
"karma-chrome-launcher": "~3.1.0",
|
||||||
"karma-coverage": "~2.0.3",
|
"karma-coverage": "~2.0.3",
|
||||||
"karma-jasmine": "~4.0.0",
|
"karma-jasmine": "~4.0.0",
|
||||||
"karma-jasmine-html-reporter": "^1.5.0",
|
"karma-jasmine-html-reporter": "^1.5.0",
|
||||||
"ts-node": "~8.3.0",
|
"ts-node": "~8.3.0",
|
||||||
"tslint": "~6.1.0",
|
"tslint": "~6.1.0",
|
||||||
"typescript": "~4.1.5"
|
"typescript": "~4.4.4"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@cypress/schematic": "^1.3.0",
|
"@cypress/schematic": "^1.3.0",
|
||||||
"cypress": "^8.3.1",
|
"cypress": "^9.1.1",
|
||||||
"cypress-fail-on-console-error": "^2.1.0",
|
"cypress-fail-on-console-error": "^2.1.3",
|
||||||
"cypress-wait-until": "^1.7.1",
|
"cypress-wait-until": "^1.7.1",
|
||||||
"mock-socket": "^9.0.3",
|
"mock-socket": "^9.0.3",
|
||||||
"start-server-and-test": "^1.12.6"
|
"start-server-and-test": "^1.12.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,6 @@ try {
|
|||||||
throw new Error(e);
|
throw new Error(e);
|
||||||
} else {
|
} else {
|
||||||
console.log(`${CONFIG_FILE_NAME} file not found, using default config`);
|
console.log(`${CONFIG_FILE_NAME} file not found, using default config`);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,4 +60,20 @@ PROXY_CONFIG = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (configContent && configContent.BASE_MODULE == "liquid") {
|
||||||
|
PROXY_CONFIG.push({
|
||||||
|
context: ['/resources/pools.json', '/resources/assets.json', '/resources/assets.minimal.json'],
|
||||||
|
target: "https://liquid.network",
|
||||||
|
secure: false,
|
||||||
|
changeOrigin: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
PROXY_CONFIG.push({
|
||||||
|
context: ['/resources/pools.json', '/resources/assets.json', '/resources/assets.minimal.json'],
|
||||||
|
target: "https://mempool.space",
|
||||||
|
secure: false,
|
||||||
|
changeOrigin: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = PROXY_CONFIG;
|
module.exports = PROXY_CONFIG;
|
||||||
|
|||||||
@@ -1,114 +0,0 @@
|
|||||||
{
|
|
||||||
"/api/v1": {
|
|
||||||
"target": "http://localhost:8999/",
|
|
||||||
"secure": false
|
|
||||||
},
|
|
||||||
"/api/v1/ws": {
|
|
||||||
"target": "http://localhost:8999/",
|
|
||||||
"secure": false,
|
|
||||||
"ws": true
|
|
||||||
},
|
|
||||||
"/api/": {
|
|
||||||
"target": "http://localhost:8999/",
|
|
||||||
"secure": false,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/api/": "/api/v1/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/testnet/api/v1": {
|
|
||||||
"target": "http://localhost:8999/",
|
|
||||||
"secure": false,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/testnet/api/v1": "/api/v1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/testnet/api/v1/ws": {
|
|
||||||
"target": "http://localhost:8999/",
|
|
||||||
"secure": false,
|
|
||||||
"ws": true,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/testnet/api": "/api/v1/ws"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/testnet/api/": {
|
|
||||||
"target": "http://localhost:50001/",
|
|
||||||
"secure": false,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/testnet/api": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/signet/api/v1": {
|
|
||||||
"target": "http://localhost:8999/",
|
|
||||||
"secure": false,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/signet/api/v1": "/api/v1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/signet/api/v1/ws": {
|
|
||||||
"target": "http://localhost:8999/",
|
|
||||||
"secure": false,
|
|
||||||
"ws": true,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/signet/api": "/api/v1/ws"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/signet/api/": {
|
|
||||||
"target": "http://localhost:50001/",
|
|
||||||
"secure": false,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/signet/api": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/liquid/api/v1/ws": {
|
|
||||||
"target": "http://localhost:8999/",
|
|
||||||
"secure": false,
|
|
||||||
"ws": true,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/liquid/api": "/api/v1/ws"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/liquid/api/v1/": {
|
|
||||||
"target": "http://localhost:8999/",
|
|
||||||
"secure": false,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/liquid/api/": "/api/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/liquid/api/": {
|
|
||||||
"target": "http://localhost:8999/",
|
|
||||||
"secure": false,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/liquid/api/": "/api/v1/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/bisq/api/": {
|
|
||||||
"target": "http://localhost:8999/",
|
|
||||||
"secure": false,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/bisq/api/": "/api/v1/bisq/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/bisq/api/v1/ws": {
|
|
||||||
"target": "http://localhost:8999/",
|
|
||||||
"secure": false,
|
|
||||||
"ws": true,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/bisq/api": "/api/v1/ws"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/resources/assets.minimal.json": {
|
|
||||||
"target": "https://mempool.space",
|
|
||||||
"secure": false,
|
|
||||||
"changeOrigin": true
|
|
||||||
},
|
|
||||||
"/resources/assets.json": {
|
|
||||||
"target": "https://mempool.space",
|
|
||||||
"secure": false,
|
|
||||||
"changeOrigin": true
|
|
||||||
},
|
|
||||||
"/resources/pools.json": {
|
|
||||||
"target": "https://mempool.space",
|
|
||||||
"secure": false,
|
|
||||||
"changeOrigin": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
73
frontend/proxy.conf.local.js
Normal file
73
frontend/proxy.conf.local.js
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
let PROXY_CONFIG = require('./proxy.conf.js');
|
||||||
|
const BACKEND_CONFIG_FILE_NAME = '../backend/mempool-config.json';
|
||||||
|
const FRONTEND_CONFIG_FILE_NAME = 'mempool-frontend-config.json';
|
||||||
|
|
||||||
|
let backendConfigContent;
|
||||||
|
let frontendConfigContent;
|
||||||
|
|
||||||
|
// Read frontend config
|
||||||
|
try {
|
||||||
|
const rawConfig = fs.readFileSync(FRONTEND_CONFIG_FILE_NAME);
|
||||||
|
frontendConfigContent = JSON.parse(rawConfig);
|
||||||
|
console.log(`${FRONTEND_CONFIG_FILE_NAME} file found, using provided config`);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
if (e.code !== 'ENOENT') {
|
||||||
|
throw new Error(e);
|
||||||
|
} else {
|
||||||
|
console.log(`${FRONTEND_CONFIG_FILE_NAME} file not found, using default config`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read backend config
|
||||||
|
try {
|
||||||
|
const rawConfig = fs.readFileSync(BACKEND_CONFIG_FILE_NAME);
|
||||||
|
backendConfigContent = JSON.parse(rawConfig);
|
||||||
|
console.log(`${BACKEND_CONFIG_FILE_NAME} file found, using provided config`);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
if (e.code !== 'ENOENT') {
|
||||||
|
throw new Error(e);
|
||||||
|
} else {
|
||||||
|
console.log(`${BACKEND_CONFIG_FILE_NAME} file not found, using default config`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the "/api/**" entry from the default proxy config
|
||||||
|
let localDevContext = PROXY_CONFIG[0].context
|
||||||
|
|
||||||
|
localDevContext.splice(PROXY_CONFIG[0].context.indexOf('/api/**'), 1);
|
||||||
|
|
||||||
|
PROXY_CONFIG[0].context = localDevContext;
|
||||||
|
|
||||||
|
// Change all targets to localhost
|
||||||
|
PROXY_CONFIG.map(conf => conf.target = "http://localhost:8999");
|
||||||
|
|
||||||
|
// Add rules for local backend
|
||||||
|
if (backendConfigContent) {
|
||||||
|
PROXY_CONFIG.push({
|
||||||
|
context: ['/api/address/**', '/api/tx/**', '/api/block/**', '/api/blocks/**'],
|
||||||
|
target: `http://localhost:8999`,
|
||||||
|
secure: false,
|
||||||
|
changeOrigin: true,
|
||||||
|
proxyTimeout: 30000,
|
||||||
|
pathRewrite: {
|
||||||
|
"^/api/": "/api/v1/"
|
||||||
|
},
|
||||||
|
});
|
||||||
|
PROXY_CONFIG.push({
|
||||||
|
context: ['/api/v1/**'],
|
||||||
|
target: `http://localhost:8999`,
|
||||||
|
secure: false,
|
||||||
|
changeOrigin: true,
|
||||||
|
proxyTimeout: 30000
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(PROXY_CONFIG);
|
||||||
|
|
||||||
|
module.exports = PROXY_CONFIG;
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
{
|
|
||||||
"/api/v1/ws": {
|
|
||||||
"target": "https://mempool.space",
|
|
||||||
"secure": false,
|
|
||||||
"ws": true
|
|
||||||
},
|
|
||||||
"/api": {
|
|
||||||
"target": "https://mempool.space",
|
|
||||||
"secure": false,
|
|
||||||
"changeOrigin": true,
|
|
||||||
"logLevel": "debug",
|
|
||||||
"timeout": 3600000
|
|
||||||
},
|
|
||||||
"/testnet/api/v1/ws": {
|
|
||||||
"target": "https://mempool.space/testnet",
|
|
||||||
"secure": false,
|
|
||||||
"ws": true,
|
|
||||||
"loglevel": "debug",
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/testnet/api": "/api/v1/ws"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/testnet/api": {
|
|
||||||
"target": "https://mempool.space",
|
|
||||||
"secure": true,
|
|
||||||
"changeOrigin": true,
|
|
||||||
"loglevel": "debug",
|
|
||||||
"pathRewrite": {
|
|
||||||
"/testnet/api": "/testnet/api"
|
|
||||||
},
|
|
||||||
"timeout": 3600000
|
|
||||||
},
|
|
||||||
"/signet/api/v1/ws": {
|
|
||||||
"target": "https://mempool.space/signet",
|
|
||||||
"secure": false,
|
|
||||||
"ws": true,
|
|
||||||
"loglevel": "debug",
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/signet/api": "/api/v1/ws"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/signet/api": {
|
|
||||||
"target": "https://mempool.space",
|
|
||||||
"secure": true,
|
|
||||||
"changeOrigin": true,
|
|
||||||
"loglevel": "debug",
|
|
||||||
"pathRewrite": {
|
|
||||||
"/signet/api": "/signet/api"
|
|
||||||
},
|
|
||||||
"timeout": 3600000
|
|
||||||
},
|
|
||||||
|
|
||||||
"/bisq/api/v1/ws": {
|
|
||||||
"target": "https://mempool.space/bisq",
|
|
||||||
"secure": false,
|
|
||||||
"ws": true,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/bisq/api": "/api/v1/ws"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/bisq/api": {
|
|
||||||
"target": "https://mempool.space/bisq",
|
|
||||||
"secure": false,
|
|
||||||
"changeOrigin": true,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/bisq/api/": "/api/v1/bisq/"
|
|
||||||
},
|
|
||||||
"timeout": 3600000
|
|
||||||
},
|
|
||||||
"/liquid/api/v1/ws": {
|
|
||||||
"target": "https://mempool.space",
|
|
||||||
"secure": false,
|
|
||||||
"ws": true
|
|
||||||
},
|
|
||||||
"/liquid/api": {
|
|
||||||
"target": "https://mempool.space",
|
|
||||||
"secure": false,
|
|
||||||
"changeOrigin": true,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/liquid/api/": "/liquid/api/"
|
|
||||||
},
|
|
||||||
"timeout": 3600000
|
|
||||||
},
|
|
||||||
"/resources/assets.minimal.json": {
|
|
||||||
"target": "https://mempool.space",
|
|
||||||
"secure": false,
|
|
||||||
"changeOrigin": true
|
|
||||||
},
|
|
||||||
"/resources/assets.json": {
|
|
||||||
"target": "https://mempool.space",
|
|
||||||
"secure": false,
|
|
||||||
"changeOrigin": true
|
|
||||||
},
|
|
||||||
"/resources/pools.json": {
|
|
||||||
"target": "https://mempool.space",
|
|
||||||
"secure": false,
|
|
||||||
"changeOrigin": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
{
|
|
||||||
"/api/v1/ws": {
|
|
||||||
"target": "https://mempool.ninja",
|
|
||||||
"secure": false,
|
|
||||||
"ws": true
|
|
||||||
},
|
|
||||||
"/api/*": {
|
|
||||||
"target": "https://mempool.ninja",
|
|
||||||
"secure": false,
|
|
||||||
"changeOrigin": true,
|
|
||||||
"logLevel": "debug",
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/api": "https://mempool.ninja/api"
|
|
||||||
},
|
|
||||||
"timeout": 3600000
|
|
||||||
},
|
|
||||||
"/testnet/api/v1/ws": {
|
|
||||||
"target": "https://mempool.ninja/testnet",
|
|
||||||
"secure": false,
|
|
||||||
"ws": true,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/testnet/api": "/api/v1/ws"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/testnet/api/v1/*": {
|
|
||||||
"target": "https://mempool.ninja/testnet",
|
|
||||||
"secure": false,
|
|
||||||
"changeOrigin": true,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/testnet/api/v1": "/api/v1"
|
|
||||||
},
|
|
||||||
"timeout": 3600000
|
|
||||||
},
|
|
||||||
"/signet/api/v1/ws": {
|
|
||||||
"target": "https://mempool.ninja/signet",
|
|
||||||
"secure": false,
|
|
||||||
"ws": true,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/signet/api": "/api/v1/ws"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/signet/api/v1/*": {
|
|
||||||
"target": "https://mempool.ninja/signet",
|
|
||||||
"secure": false,
|
|
||||||
"changeOrigin": true,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/signet/api/v1": "/api/v1"
|
|
||||||
},
|
|
||||||
"timeout": 3600000
|
|
||||||
},
|
|
||||||
"/bisq/api/v1/ws": {
|
|
||||||
"target": "https://mempool.ninja/bisq",
|
|
||||||
"secure": false,
|
|
||||||
"ws": true,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/bisq/api": "/api/v1/ws"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/bisq/api/*": {
|
|
||||||
"target": "https://mempool.ninja/bisq",
|
|
||||||
"secure": false,
|
|
||||||
"changeOrigin": true,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/bisq/api/": "/api/v1/bisq/"
|
|
||||||
},
|
|
||||||
"timeout": 3600000
|
|
||||||
},
|
|
||||||
"/liquid/api/v1/ws": {
|
|
||||||
"target": "https://mempool.ninja/liquid",
|
|
||||||
"secure": false,
|
|
||||||
"ws": true,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/liquid/api": "/api/v1/ws"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/liquid/api/*": {
|
|
||||||
"target": "https://mempool.ninja/liquid",
|
|
||||||
"secure": false,
|
|
||||||
"changeOrigin": true,
|
|
||||||
"pathRewrite": {
|
|
||||||
"^/liquid/api/": "/api/liquid/"
|
|
||||||
},
|
|
||||||
"timeout": 3600000
|
|
||||||
},
|
|
||||||
"/resources/assets.minimal.json": {
|
|
||||||
"target": "https://mempool.space",
|
|
||||||
"secure": false,
|
|
||||||
"changeOrigin": true
|
|
||||||
},
|
|
||||||
"/resources/assets.json": {
|
|
||||||
"target": "https://mempool.space",
|
|
||||||
"secure": false,
|
|
||||||
"changeOrigin": true
|
|
||||||
},
|
|
||||||
"/resources/pools.json": {
|
|
||||||
"target": "https://mempool.space",
|
|
||||||
"secure": false,
|
|
||||||
"changeOrigin": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'zone.js/dist/zone-node';
|
import 'zone.js/node';
|
||||||
import './generated-config';
|
import './generated-config';
|
||||||
|
|
||||||
import * as domino from 'domino';
|
import * as domino from 'domino';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'zone.js/dist/zone-node';
|
import 'zone.js/node';
|
||||||
import './generated-config';
|
import './generated-config';
|
||||||
|
|
||||||
import { ngExpressEngine } from '@nguniversal/express-engine';
|
import { ngExpressEngine } from '@nguniversal/express-engine';
|
||||||
|
|||||||
@@ -21,12 +21,17 @@ import { TrademarkPolicyComponent } from './components/trademark-policy/trademar
|
|||||||
import { BisqMasterPageComponent } from './components/bisq-master-page/bisq-master-page.component';
|
import { BisqMasterPageComponent } from './components/bisq-master-page/bisq-master-page.component';
|
||||||
import { SponsorComponent } from './components/sponsor/sponsor.component';
|
import { SponsorComponent } from './components/sponsor/sponsor.component';
|
||||||
import { LiquidMasterPageComponent } from './components/liquid-master-page/liquid-master-page.component';
|
import { LiquidMasterPageComponent } from './components/liquid-master-page/liquid-master-page.component';
|
||||||
|
import { PushTransactionComponent } from './components/push-transaction/push-transaction.component';
|
||||||
|
|
||||||
let routes: Routes = [
|
let routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: MasterPageComponent,
|
component: MasterPageComponent,
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
path: 'tx/push',
|
||||||
|
component: PushTransactionComponent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: StartComponent,
|
component: StartComponent,
|
||||||
@@ -95,6 +100,10 @@ let routes: Routes = [
|
|||||||
path: '',
|
path: '',
|
||||||
component: MasterPageComponent,
|
component: MasterPageComponent,
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
path: 'tx/push',
|
||||||
|
component: PushTransactionComponent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: StartComponent,
|
component: StartComponent,
|
||||||
@@ -164,6 +173,10 @@ let routes: Routes = [
|
|||||||
path: '',
|
path: '',
|
||||||
component: MasterPageComponent,
|
component: MasterPageComponent,
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
path: 'tx/push',
|
||||||
|
component: PushTransactionComponent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: StartComponent,
|
component: StartComponent,
|
||||||
@@ -226,6 +239,10 @@ let routes: Routes = [
|
|||||||
path: '',
|
path: '',
|
||||||
component: MasterPageComponent,
|
component: MasterPageComponent,
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
path: 'tx/push',
|
||||||
|
component: PushTransactionComponent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: StartComponent,
|
component: StartComponent,
|
||||||
@@ -325,6 +342,10 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
|
|||||||
path: '',
|
path: '',
|
||||||
component: DashboardComponent
|
component: DashboardComponent
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'tx/push',
|
||||||
|
component: PushTransactionComponent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'tx/:id',
|
path: 'tx/:id',
|
||||||
component: TransactionComponent
|
component: TransactionComponent
|
||||||
|
|||||||
@@ -31,6 +31,46 @@ export const mempoolFeeColors = [
|
|||||||
'b9254b',
|
'b9254b',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const chartColors = [
|
||||||
|
"#D81B60",
|
||||||
|
"#8E24AA",
|
||||||
|
"#5E35B1",
|
||||||
|
"#3949AB",
|
||||||
|
"#1E88E5",
|
||||||
|
"#039BE5",
|
||||||
|
"#00ACC1",
|
||||||
|
"#00897B",
|
||||||
|
"#43A047",
|
||||||
|
"#7CB342",
|
||||||
|
"#C0CA33",
|
||||||
|
"#FDD835",
|
||||||
|
"#FFB300",
|
||||||
|
"#FB8C00",
|
||||||
|
"#F4511E",
|
||||||
|
"#6D4C41",
|
||||||
|
"#757575",
|
||||||
|
"#546E7A",
|
||||||
|
"#b71c1c",
|
||||||
|
"#880E4F",
|
||||||
|
"#4A148C",
|
||||||
|
"#311B92",
|
||||||
|
"#1A237E",
|
||||||
|
"#0D47A1",
|
||||||
|
"#01579B",
|
||||||
|
"#006064",
|
||||||
|
"#004D40",
|
||||||
|
"#1B5E20",
|
||||||
|
"#33691E",
|
||||||
|
"#827717",
|
||||||
|
"#F57F17",
|
||||||
|
"#FF6F00",
|
||||||
|
"#E65100",
|
||||||
|
"#BF360C",
|
||||||
|
"#3E2723",
|
||||||
|
"#212121",
|
||||||
|
"#263238",
|
||||||
|
];
|
||||||
|
|
||||||
export const feeLevels = [1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 175, 200,
|
export const feeLevels = [1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 175, 200,
|
||||||
250, 300, 350, 400, 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000];
|
250, 300, 350, 400, 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000];
|
||||||
|
|
||||||
@@ -66,7 +106,7 @@ export const languages: Language[] = [
|
|||||||
// { code: 'lv', name: 'Latviešu' }, // Latvian
|
// { code: 'lv', name: 'Latviešu' }, // Latvian
|
||||||
// { code: 'lt', name: 'Lietuvių' }, // Lithuanian
|
// { code: 'lt', name: 'Lietuvių' }, // Lithuanian
|
||||||
{ code: 'hu', name: 'Magyar' }, // Hungarian
|
{ code: 'hu', name: 'Magyar' }, // Hungarian
|
||||||
// { code: 'mk', name: 'Македонски' }, // Macedonian
|
{ code: 'mk', name: 'Македонски' }, // Macedonian
|
||||||
// { code: 'ms', name: 'Bahasa Melayu' }, // Malay
|
// { code: 'ms', name: 'Bahasa Melayu' }, // Malay
|
||||||
{ code: 'nl', name: 'Nederlands' }, // Dutch
|
{ code: 'nl', name: 'Nederlands' }, // Dutch
|
||||||
{ code: 'ja', name: '日本語' }, // Japanese
|
{ code: 'ja', name: '日本語' }, // Japanese
|
||||||
@@ -75,7 +115,7 @@ export const languages: Language[] = [
|
|||||||
{ code: 'pl', name: 'Polski' }, // Polish
|
{ code: 'pl', name: 'Polski' }, // Polish
|
||||||
{ code: 'pt', name: 'Português' }, // Portuguese
|
{ code: 'pt', name: 'Português' }, // Portuguese
|
||||||
// { code: 'pt-BR', name: 'Português (Brazil)' }, // Portuguese (Brazil)
|
// { code: 'pt-BR', name: 'Português (Brazil)' }, // Portuguese (Brazil)
|
||||||
// { code: 'ro', name: 'Română' }, // Romanian
|
{ code: 'ro', name: 'Română' }, // Romanian
|
||||||
{ code: 'ru', name: 'Русский' }, // Russian
|
{ code: 'ru', name: 'Русский' }, // Russian
|
||||||
// { code: 'sk', name: 'Slovenčina' }, // Slovak
|
// { code: 'sk', name: 'Slovenčina' }, // Slovak
|
||||||
{ code: 'sl', name: 'Slovenščina' }, // Slovenian
|
{ code: 'sl', name: 'Slovenščina' }, // Slovenian
|
||||||
@@ -83,9 +123,20 @@ export const languages: Language[] = [
|
|||||||
// { code: 'sh', name: 'Srpskohrvatski / српскохрватски' },// Serbo-Croatian
|
// { code: 'sh', name: 'Srpskohrvatski / српскохрватски' },// Serbo-Croatian
|
||||||
{ code: 'fi', name: 'Suomi' }, // Finnish
|
{ code: 'fi', name: 'Suomi' }, // Finnish
|
||||||
{ code: 'sv', name: 'Svenska' }, // Swedish
|
{ code: 'sv', name: 'Svenska' }, // Swedish
|
||||||
// { code: 'th', name: 'ไทย' }, // Thai
|
{ code: 'th', name: 'ไทย' }, // Thai
|
||||||
{ code: 'tr', name: 'Türkçe' }, // Turkish
|
{ code: 'tr', name: 'Türkçe' }, // Turkish
|
||||||
{ code: 'uk', name: 'Українська' }, // Ukrainian
|
{ code: 'uk', name: 'Українська' }, // Ukrainian
|
||||||
{ code: 'vi', name: 'Tiếng Việt' }, // Vietnamese
|
{ code: 'vi', name: 'Tiếng Việt' }, // Vietnamese
|
||||||
{ code: 'zh', name: '中文' }, // Chinese
|
{ code: 'zh', name: '中文' }, // Chinese
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const specialBlocks = {
|
||||||
|
'709632': {
|
||||||
|
labelEvent: 'Taproot 🌱 activation',
|
||||||
|
labelEventCompleted: 'Taproot 🌱 has been activated!',
|
||||||
|
},
|
||||||
|
'840000': {
|
||||||
|
labelEvent: 'Halving 🥳',
|
||||||
|
labelEventCompleted: 'Block Subsidy has halved to 3.125 BTC per block',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { NgModule } from '@angular/core';
|
|||||||
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
||||||
|
import { NgxEchartsModule } from 'ngx-echarts';
|
||||||
|
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
import { AppComponent } from './components/app/app.component';
|
import { AppComponent } from './components/app/app.component';
|
||||||
@@ -26,16 +27,17 @@ import { LiquidMasterPageComponent } from './components/liquid-master-page/liqui
|
|||||||
import { AboutComponent } from './components/about/about.component';
|
import { AboutComponent } from './components/about/about.component';
|
||||||
import { TelevisionComponent } from './components/television/television.component';
|
import { TelevisionComponent } from './components/television/television.component';
|
||||||
import { StatisticsComponent } from './components/statistics/statistics.component';
|
import { StatisticsComponent } from './components/statistics/statistics.component';
|
||||||
import { ChartistComponent } from './components/statistics/chartist.component';
|
|
||||||
import { BlockchainBlocksComponent } from './components/blockchain-blocks/blockchain-blocks.component';
|
import { BlockchainBlocksComponent } from './components/blockchain-blocks/blockchain-blocks.component';
|
||||||
import { BlockchainComponent } from './components/blockchain/blockchain.component';
|
import { BlockchainComponent } from './components/blockchain/blockchain.component';
|
||||||
import { FooterComponent } from './components/footer/footer.component';
|
import { FooterComponent } from './components/footer/footer.component';
|
||||||
import { AudioService } from './services/audio.service';
|
import { AudioService } from './services/audio.service';
|
||||||
import { MempoolBlockComponent } from './components/mempool-block/mempool-block.component';
|
import { MempoolBlockComponent } from './components/mempool-block/mempool-block.component';
|
||||||
import { FeeDistributionGraphComponent } from './components/fee-distribution-graph/fee-distribution-graph.component';
|
import { FeeDistributionGraphComponent } from './components/fee-distribution-graph/fee-distribution-graph.component';
|
||||||
|
import { IncomingTransactionsGraphComponent } from './components/incoming-transactions-graph/incoming-transactions-graph.component';
|
||||||
import { TimeSpanComponent } from './components/time-span/time-span.component';
|
import { TimeSpanComponent } from './components/time-span/time-span.component';
|
||||||
import { SeoService } from './services/seo.service';
|
import { SeoService } from './services/seo.service';
|
||||||
import { MempoolGraphComponent } from './components/mempool-graph/mempool-graph.component';
|
import { MempoolGraphComponent } from './components/mempool-graph/mempool-graph.component';
|
||||||
|
import { LbtcPegsGraphComponent } from './components/lbtc-pegs-graph/lbtc-pegs-graph.component';
|
||||||
import { AssetComponent } from './components/asset/asset.component';
|
import { AssetComponent } from './components/asset/asset.component';
|
||||||
import { AssetsComponent } from './assets/assets.component';
|
import { AssetsComponent } from './assets/assets.component';
|
||||||
import { StatusViewComponent } from './components/status-view/status-view.component';
|
import { StatusViewComponent } from './components/status-view/status-view.component';
|
||||||
@@ -45,7 +47,7 @@ import { NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
|
|||||||
import { FeesBoxComponent } from './components/fees-box/fees-box.component';
|
import { FeesBoxComponent } from './components/fees-box/fees-box.component';
|
||||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||||
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
|
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
|
||||||
import { 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 } from '@fortawesome/free-solid-svg-icons';
|
faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown, faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { ApiDocsComponent } from './components/api-docs/api-docs.component';
|
import { ApiDocsComponent } from './components/api-docs/api-docs.component';
|
||||||
import { CodeTemplateComponent } from './components/api-docs/code-template.component';
|
import { CodeTemplateComponent } from './components/api-docs/code-template.component';
|
||||||
@@ -55,6 +57,7 @@ import { TrademarkPolicyComponent } from './components/trademark-policy/trademar
|
|||||||
import { StorageService } from './services/storage.service';
|
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';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@@ -78,11 +81,12 @@ import { SponsorComponent } from './components/sponsor/sponsor.component';
|
|||||||
TimeSpanComponent,
|
TimeSpanComponent,
|
||||||
AddressLabelsComponent,
|
AddressLabelsComponent,
|
||||||
MempoolBlocksComponent,
|
MempoolBlocksComponent,
|
||||||
ChartistComponent,
|
|
||||||
FooterComponent,
|
FooterComponent,
|
||||||
MempoolBlockComponent,
|
MempoolBlockComponent,
|
||||||
FeeDistributionGraphComponent,
|
FeeDistributionGraphComponent,
|
||||||
|
IncomingTransactionsGraphComponent,
|
||||||
MempoolGraphComponent,
|
MempoolGraphComponent,
|
||||||
|
LbtcPegsGraphComponent,
|
||||||
AssetComponent,
|
AssetComponent,
|
||||||
AssetsComponent,
|
AssetsComponent,
|
||||||
MinerComponent,
|
MinerComponent,
|
||||||
@@ -95,6 +99,7 @@ import { SponsorComponent } from './components/sponsor/sponsor.component';
|
|||||||
PrivacyPolicyComponent,
|
PrivacyPolicyComponent,
|
||||||
TrademarkPolicyComponent,
|
TrademarkPolicyComponent,
|
||||||
SponsorComponent,
|
SponsorComponent,
|
||||||
|
PushTransactionComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule.withServerTransition({ appId: 'serverApp' }),
|
BrowserModule.withServerTransition({ appId: 'serverApp' }),
|
||||||
@@ -106,6 +111,9 @@ import { SponsorComponent } from './components/sponsor/sponsor.component';
|
|||||||
NgbTypeaheadModule,
|
NgbTypeaheadModule,
|
||||||
FontAwesomeModule,
|
FontAwesomeModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
|
NgxEchartsModule.forRoot({
|
||||||
|
echarts: () => import('echarts')
|
||||||
|
})
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
ElectrsApiService,
|
ElectrsApiService,
|
||||||
@@ -134,6 +142,7 @@ export class AppModule {
|
|||||||
library.addIcons(faLink);
|
library.addIcons(faLink);
|
||||||
library.addIcons(faBolt);
|
library.addIcons(faBolt);
|
||||||
library.addIcons(faTint);
|
library.addIcons(faTint);
|
||||||
|
library.addIcons(faFilter);
|
||||||
library.addIcons(faAngleDown);
|
library.addIcons(faAngleDown);
|
||||||
library.addIcons(faAngleUp);
|
library.addIcons(faAngleUp);
|
||||||
library.addIcons(faExchangeAlt);
|
library.addIcons(faExchangeAlt);
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<div class="container-xl">
|
<div class="container-xl">
|
||||||
<h1 style="float: left;" i18n="Registered assets page header">Registered assets</h1>
|
<div class="title-asset">
|
||||||
<br>
|
<h1 i18n="Registered assets page header">Registered assets</h1>
|
||||||
|
</div>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
|
|
||||||
<form [formGroup]="searchForm" class="form-inline">
|
<form [formGroup]="searchForm" class="form-inline">
|
||||||
<div class="input-group m-2">
|
<div class="input-group mb-2">
|
||||||
<input style="width: 250px;" formControlName="searchText" type="text" class="form-control" i18n-placeholder="Search Assets Placeholder Text" placeholder="Search asset">
|
<input style="width: 250px;" formControlName="searchText" type="text" class="form-control" i18n-placeholder="Search Assets Placeholder Text" placeholder="Search asset">
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button [disabled]="!searchForm.get('searchText')?.value.length" class="btn btn-secondary" type="button" (click)="searchForm.get('searchText')?.setValue('');" autocomplete="off" i18n="Search Clear Button">Clear</button>
|
<button [disabled]="!searchForm.get('searchText')?.value.length" class="btn btn-secondary" type="button" (click)="searchForm.get('searchText')?.setValue('');" autocomplete="off" i18n="Search Clear Button">Clear</button>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
<td>{{ asset.ticker }}</td>
|
<td>{{ asset.ticker }}</td>
|
||||||
<td class="d-none d-md-block">{{ asset.entity && asset.entity.domain }}</td>
|
<td class="d-none d-md-block">{{ asset.entity && asset.entity.domain }}</td>
|
||||||
<td><a [routerLink]="['/asset/' | relativeUrl, asset.asset_id]">{{ asset.asset_id | shortenString : 13 }}</a> <app-clipboard class="d-none d-sm-inline-block" [text]="asset.asset_id"></app-clipboard></td>
|
<td><a [routerLink]="['/asset/' | relativeUrl, asset.asset_id]">{{ asset.asset_id | shortenString : 13 }}</a> <app-clipboard class="d-none d-sm-inline-block" [text]="asset.asset_id"></app-clipboard></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
<td><span class="skeleton-loader"></span></td>
|
<td><span class="skeleton-loader"></span></td>
|
||||||
<td class="d-none d-md-block"><span class="skeleton-loader"></span></td>
|
<td class="d-none d-md-block"><span class="skeleton-loader"></span></td>
|
||||||
<td><span class="skeleton-loader"></span></td>
|
<td><span class="skeleton-loader"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
@@ -68,4 +68,4 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|||||||
@@ -3,4 +3,11 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
.title-asset {
|
||||||
|
h1 {
|
||||||
|
line-height: 1;
|
||||||
|
margin: 0px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
<span style="float: left;" class="d-none d-md-block">{{ tx.id }}</span>
|
<span style="float: left;" class="d-none d-md-block">{{ tx.id }}</span>
|
||||||
</a>
|
</a>
|
||||||
<div class="float-right">
|
<div class="float-right">
|
||||||
{{ tx.time | date:'yyyy-MM-dd HH:mm' }}
|
‎{{ tx.time | date:'yyyy-MM-dd HH:mm' }}
|
||||||
</div>
|
</div>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
<div class="container-xl">
|
<div class="container-xl">
|
||||||
|
|
||||||
<div class="title-block">
|
<div class="title-block">
|
||||||
<h1><ng-template [ngIf]="blockHeight" i18n="block.block">Block <a [routerLink]="['/block/' | relativeUrl, blockHash]">{{ blockHeight }}</a></ng-template></h1>
|
<h1><ng-template [ngIf]="blockHeight" i18n="shared.block-title">Block <ng-container *ngTemplateOutlet="blockTemplateContent"></ng-container></ng-template></h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ng-template #blockTemplateContent><a [routerLink]="['/block/' | relativeUrl, blockHash]">{{ blockHeight }}</a></ng-template>
|
||||||
|
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
|
|
||||||
<ng-template [ngIf]="!isLoading && !error">
|
<ng-template [ngIf]="!isLoading && !error">
|
||||||
@@ -20,7 +22,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td i18n="transaction.timestamp|Transaction Timestamp">Timestamp</td>
|
<td i18n="transaction.timestamp|Transaction Timestamp">Timestamp</td>
|
||||||
<td>
|
<td>
|
||||||
{{ block.time | date:'yyyy-MM-dd HH:mm' }}
|
‎{{ block.time | date:'yyyy-MM-dd HH:mm' }}
|
||||||
<div class="lg-inline">
|
<div class="lg-inline">
|
||||||
<i class="symbol">(<app-time-since [time]="block.time / 1000" [fastRender]="true"></app-time-since>)</i>
|
<i class="symbol">(<app-time-since [time]="block.time / 1000" [fastRender]="true"></app-time-since>)</i>
|
||||||
</div>
|
</div>
|
||||||
@@ -58,7 +60,7 @@
|
|||||||
<span style="float: left;" class="d-none d-md-block">{{ tx.id }}</span>
|
<span style="float: left;" class="d-none d-md-block">{{ tx.id }}</span>
|
||||||
</a>
|
</a>
|
||||||
<div class="float-right">
|
<div class="float-right">
|
||||||
{{ tx.time | date:'yyyy-MM-dd HH:mm' }}
|
‎{{ tx.time | date:'yyyy-MM-dd HH:mm' }}
|
||||||
</div>
|
</div>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<tbody *ngIf="(trades$ | async) as trades; else loadingTmpl">
|
<tbody *ngIf="(trades$ | async) as trades; else loadingTmpl">
|
||||||
<tr *ngFor="let trade of trades;">
|
<tr *ngFor="let trade of trades;">
|
||||||
<td>
|
<td>
|
||||||
{{ trade.trade_date | date:'yyyy-MM-dd HH:mm' }}
|
‎{{ trade.trade_date | date:'yyyy-MM-dd HH:mm' }}
|
||||||
</td>
|
</td>
|
||||||
<td *ngIf="view === 'all'">
|
<td *ngIf="view === 'all'">
|
||||||
<ng-container *ngIf="(trade._market || market).rtype === 'fiat'; else priceCrypto"><span class="green-color">{{ trade.price | currency: (trade._market || market).rsymbol }}</span></ng-container>
|
<ng-container *ngIf="(trade._market || market).rtype === 'fiat'; else priceCrypto"><span class="green-color">{{ trade.price | currency: (trade._market || market).rsymbol }}</span></ng-container>
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
<div class="container-xl">
|
<div class="container-xl">
|
||||||
|
|
||||||
<div class="title-block">
|
|
||||||
<ng-template [ngIf]="!isLoading && !error">
|
<ng-template [ngIf]="!isLoading && !error">
|
||||||
|
<div class="title-block">
|
||||||
<div>
|
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<h1 i18n="shared.transaction">Transaction</h1>
|
<h1 i18n="shared.transaction">Transaction</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tx-link">
|
<span class="tx-link float-left">
|
||||||
<a [routerLink]="['/bisq-tx' | relativeUrl, bisqTx.id]">
|
<a [routerLink]="['/bisq-tx' | relativeUrl, bisqTx.id]">
|
||||||
<span class="d-inline d-lg-none">{{ bisqTx.id | shortenString : 24 }}</span>
|
<span class="d-inline d-lg-none">{{ bisqTx.id | shortenString : 24 }}</span>
|
||||||
<span class="d-none d-lg-inline">{{ bisqTx.id }}</span>
|
<span class="d-none d-lg-inline">{{ bisqTx.id }}</span>
|
||||||
</a>
|
</a>
|
||||||
<app-clipboard [text]="bisqTx.id"></app-clipboard>
|
<app-clipboard [text]="bisqTx.id"></app-clipboard>
|
||||||
</div>
|
</span>
|
||||||
|
<span class="grow"></span>
|
||||||
<div class="container-buttons">
|
<div class="container-buttons">
|
||||||
<button *ngIf="(latestBlock$ | async) as latestBlock" type="button" class="btn btn-sm btn-success float-right">
|
<button *ngIf="(latestBlock$ | async) as latestBlock" type="button" class="btn btn-sm btn-success float-right">
|
||||||
<ng-container *ngTemplateOutlet="latestBlock.height - bisqTx.blockHeight + 1 == 1 ? confirmationSingular : confirmationPlural; context: {$implicit: latestBlock.height - bisqTx.blockHeight + 1}"></ng-container>
|
<ng-container *ngTemplateOutlet="latestBlock.height - bisqTx.blockHeight + 1 == 1 ? confirmationSingular : confirmationPlural; context: {$implicit: latestBlock.height - bisqTx.blockHeight + 1}"></ng-container>
|
||||||
@@ -22,6 +21,8 @@
|
|||||||
<ng-template #confirmationPlural let-i i18n="shared.confirmation-count.plural|Transaction plural confirmation count">{{ i }} confirmations</ng-template>
|
<ng-template #confirmationPlural let-i i18n="shared.confirmation-count.plural|Transaction plural confirmation count">{{ i }} confirmations</ng-template>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
|
|
||||||
<div class="box transaction-container">
|
<div class="box transaction-container">
|
||||||
@@ -32,7 +33,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td i18n="transaction.timestamp|Transaction Timestamp">Timestamp</td>
|
<td i18n="transaction.timestamp|Transaction Timestamp">Timestamp</td>
|
||||||
<td>
|
<td>
|
||||||
{{ bisqTx.time | date:'yyyy-MM-dd HH:mm' }}
|
‎{{ bisqTx.time | date:'yyyy-MM-dd HH:mm' }}
|
||||||
<div class="lg-inline">
|
<div class="lg-inline">
|
||||||
<i class="symbol">(<app-time-since [time]="bisqTx.time / 1000" [fastRender]="true"></app-time-since>)</i>
|
<i class="symbol">(<app-time-since [time]="bisqTx.time / 1000" [fastRender]="true"></app-time-since>)</i>
|
||||||
</div>
|
</div>
|
||||||
@@ -84,25 +85,33 @@
|
|||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<h2 i18n="transaction.details">Details</h2>
|
<div class="title">
|
||||||
|
<h2 i18n="transaction.details">Details</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
<app-bisq-transaction-details [tx]="bisqTx"></app-bisq-transaction-details>
|
<app-bisq-transaction-details [tx]="bisqTx"></app-bisq-transaction-details>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<h2 i18n="transaction.inputs-and-outputs|Transaction inputs and outputs">Inputs & Outputs</h2>
|
<div class="title">
|
||||||
|
<h2 i18n="transaction.inputs-and-outputs|Transaction inputs and outputs">Inputs & Outputs</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
<app-bisq-transfers [tx]="bisqTx"></app-bisq-transfers>
|
<app-bisq-transfers [tx]="bisqTx"></app-bisq-transfers>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
</div>
|
</ng-template>
|
||||||
</ng-template>
|
|
||||||
|
|
||||||
<ng-template [ngIf="isLoading && !error">
|
<ng-template [ngIf]="isLoading && !error">
|
||||||
|
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
|
|
||||||
|
<div class="title-block">
|
||||||
|
<div class="title">
|
||||||
|
<h1 i18n="shared.transaction">Transaction</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm">
|
<div class="col-sm">
|
||||||
@@ -112,6 +121,14 @@
|
|||||||
<td class="td-width"><span class="skeleton-loader"></span></td>
|
<td class="td-width"><span class="skeleton-loader"></span></td>
|
||||||
<td><span class="skeleton-loader"></span></td>
|
<td><span class="skeleton-loader"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="td-width"><span class="skeleton-loader"></span></td>
|
||||||
|
<td><span class="skeleton-loader"></span></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="td-width"><span class="skeleton-loader"></span></td>
|
||||||
|
<td><span class="skeleton-loader"></span></td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -122,6 +139,10 @@
|
|||||||
<td class="td-width"><span class="skeleton-loader"></span></td>
|
<td class="td-width"><span class="skeleton-loader"></span></td>
|
||||||
<td><span class="skeleton-loader"></span></td>
|
<td><span class="skeleton-loader"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="td-width"><span class="skeleton-loader"></span></td>
|
||||||
|
<td><span class="skeleton-loader"></span></td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -130,7 +151,10 @@
|
|||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<h2 i18n="transaction.details">Details</h2>
|
<div class="title">
|
||||||
|
<h2 i18n="transaction.details">Details</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<table class="table table-borderless table-striped">
|
<table class="table table-borderless table-striped">
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -151,18 +175,30 @@
|
|||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<h2 i18n="transaction.inputs-and-outputs|Transaction inputs and outputs">Inputs & Outputs</h2>
|
<div class="title">
|
||||||
|
<h2 i18n="transaction.inputs-and-outputs|Transaction inputs and outputs">Inputs & Outputs</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<table class="table table-borderless table-striped">
|
<div class="col-sm">
|
||||||
<tbody>
|
<table class="table table-borderless table-striped">
|
||||||
<tr>
|
<tbody>
|
||||||
<td><span class="skeleton-loader"></span></td>
|
<tr>
|
||||||
<td><span class="skeleton-loader"></span></td>
|
<td><span class="skeleton-loader"></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
<table class="table table-borderless table-striped">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><span class="skeleton-loader"></span></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -178,4 +214,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef, OnDestroy } from '@angular/core';
|
||||||
import { BisqTransaction, BisqOutput } from '../bisq.interfaces';
|
import { BisqTransaction, BisqOutput } from '../bisq.interfaces';
|
||||||
|
|
||||||
import { merge, Observable } from 'rxjs';
|
import { Observable, Subscription } from 'rxjs';
|
||||||
import { switchMap, map, tap, filter } from 'rxjs/operators';
|
import { switchMap, map, tap } from 'rxjs/operators';
|
||||||
import { BisqApiService } from '../bisq-api.service';
|
import { BisqApiService } from '../bisq-api.service';
|
||||||
import { SeoService } from 'src/app/services/seo.service';
|
import { SeoService } from 'src/app/services/seo.service';
|
||||||
import { FormGroup, FormBuilder } from '@angular/forms';
|
import { FormGroup, FormBuilder } from '@angular/forms';
|
||||||
@@ -16,7 +16,7 @@ import { WebsocketService } from 'src/app/services/websocket.service';
|
|||||||
styleUrls: ['./bisq-transactions.component.scss'],
|
styleUrls: ['./bisq-transactions.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
})
|
})
|
||||||
export class BisqTransactionsComponent implements OnInit {
|
export class BisqTransactionsComponent implements OnInit, OnDestroy {
|
||||||
transactions$: Observable<[BisqTransaction[], number]>;
|
transactions$: Observable<[BisqTransaction[], number]>;
|
||||||
page = 1;
|
page = 1;
|
||||||
itemsPerPage = 50;
|
itemsPerPage = 50;
|
||||||
@@ -25,6 +25,7 @@ export class BisqTransactionsComponent implements OnInit {
|
|||||||
loadingItems: number[];
|
loadingItems: number[];
|
||||||
radioGroupForm: FormGroup;
|
radioGroupForm: FormGroup;
|
||||||
types: string[] = [];
|
types: string[] = [];
|
||||||
|
radioGroupSubscription: Subscription;
|
||||||
|
|
||||||
txTypeOptions: IMultiSelectOption[] = [
|
txTypeOptions: IMultiSelectOption[] = [
|
||||||
{ id: 1, name: $localize`Asset listing fee` },
|
{ id: 1, name: $localize`Asset listing fee` },
|
||||||
@@ -90,52 +91,39 @@ export class BisqTransactionsComponent implements OnInit {
|
|||||||
this.paginationMaxSize = 3;
|
this.paginationMaxSize = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.transactions$ = merge(
|
this.transactions$ = this.route.queryParams
|
||||||
this.route.queryParams
|
|
||||||
.pipe(
|
|
||||||
filter((queryParams) => {
|
|
||||||
const newPage = parseInt(queryParams.page, 10);
|
|
||||||
const types = queryParams.types;
|
|
||||||
if (newPage !== this.page || types !== this.types.map((type) => this.txTypes.indexOf(type) + 1).join(',')) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}),
|
|
||||||
tap((queryParams) => {
|
|
||||||
if (queryParams.page) {
|
|
||||||
const newPage = parseInt(queryParams.page, 10);
|
|
||||||
this.page = newPage;
|
|
||||||
} else {
|
|
||||||
this.page = 1;
|
|
||||||
}
|
|
||||||
if (queryParams.types) {
|
|
||||||
const types = queryParams.types.split(',').map((str: string) => parseInt(str, 10));
|
|
||||||
this.types = types.map((id: number) => this.txTypes[id - 1]);
|
|
||||||
this.radioGroupForm.get('txTypes').setValue(types, { emitEvent: false });
|
|
||||||
} else {
|
|
||||||
this.types = [];
|
|
||||||
this.radioGroupForm.get('txTypes').setValue(this.txTypesDefaultChecked, { emitEvent: false });
|
|
||||||
}
|
|
||||||
this.cd.markForCheck();
|
|
||||||
})
|
|
||||||
),
|
|
||||||
this.radioGroupForm.valueChanges
|
|
||||||
.pipe(
|
|
||||||
tap((data) => {
|
|
||||||
this.types = data.txTypes.map((id: number) => this.txTypes[id - 1]);
|
|
||||||
if (this.types.length === this.txTypes.length) {
|
|
||||||
this.types = [];
|
|
||||||
}
|
|
||||||
this.page = 1;
|
|
||||||
this.typesChanged(data.txTypes);
|
|
||||||
this.cd.markForCheck();
|
|
||||||
})
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.pipe(
|
.pipe(
|
||||||
|
tap((queryParams) => {
|
||||||
|
if (queryParams.page) {
|
||||||
|
const newPage = parseInt(queryParams.page, 10);
|
||||||
|
this.page = newPage;
|
||||||
|
} else {
|
||||||
|
this.page = 1;
|
||||||
|
}
|
||||||
|
if (queryParams.types) {
|
||||||
|
const types = queryParams.types.split(',').map((str: string) => parseInt(str, 10));
|
||||||
|
this.types = types.map((id: number) => this.txTypes[id - 1]);
|
||||||
|
this.radioGroupForm.get('txTypes').setValue(types, { emitEvent: false });
|
||||||
|
} else {
|
||||||
|
this.types = [];
|
||||||
|
this.radioGroupForm.get('txTypes').setValue([], { emitEvent: false });
|
||||||
|
}
|
||||||
|
this.cd.markForCheck();
|
||||||
|
}),
|
||||||
switchMap(() => this.bisqApiService.listTransactions$((this.page - 1) * this.itemsPerPage, this.itemsPerPage, this.types)),
|
switchMap(() => this.bisqApiService.listTransactions$((this.page - 1) * this.itemsPerPage, this.itemsPerPage, this.types)),
|
||||||
map((response) => [response.body, parseInt(response.headers.get('x-total-count'), 10)])
|
map((response) => [response.body, parseInt(response.headers.get('x-total-count'), 10)])
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.radioGroupSubscription = this.radioGroupForm.valueChanges
|
||||||
|
.subscribe((data) => {
|
||||||
|
this.types = data.txTypes.map((id: number) => this.txTypes[id - 1]);
|
||||||
|
if (this.types.length === this.txTypes.length) {
|
||||||
|
this.types = [];
|
||||||
|
}
|
||||||
|
this.page = 1;
|
||||||
|
this.typesChanged(data.txTypes);
|
||||||
|
this.cd.markForCheck();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pageChange(page: number) {
|
pageChange(page: number) {
|
||||||
@@ -144,8 +132,6 @@ export class BisqTransactionsComponent implements OnInit {
|
|||||||
queryParams: { page: page },
|
queryParams: { page: page },
|
||||||
queryParamsHandling: 'merge',
|
queryParamsHandling: 'merge',
|
||||||
});
|
});
|
||||||
// trigger queryParams change
|
|
||||||
this.page = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typesChanged(types: number[]) {
|
typesChanged(types: number[]) {
|
||||||
@@ -172,4 +158,8 @@ export class BisqTransactionsComponent implements OnInit {
|
|||||||
onResize(event: any) {
|
onResize(event: any) {
|
||||||
this.paginationMaxSize = event.target.innerWidth < 670 ? 3 : 5;
|
this.paginationMaxSize = event.target.innerWidth < 670 ? 3 : 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.radioGroupSubscription.unsubscribe();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ interface BisqScriptPubKey {
|
|||||||
addresses: string[];
|
addresses: string[];
|
||||||
asm: string;
|
asm: string;
|
||||||
hex: string;
|
hex: string;
|
||||||
reqSigs: number;
|
reqSigs?: number;
|
||||||
type: string;
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { BisqDashboardComponent } from './bisq-dashboard/bisq-dashboard.componen
|
|||||||
import { BisqMarketComponent } from './bisq-market/bisq-market.component';
|
import { BisqMarketComponent } from './bisq-market/bisq-market.component';
|
||||||
import { BisqMainDashboardComponent } from './bisq-main-dashboard/bisq-main-dashboard.component';
|
import { BisqMainDashboardComponent } from './bisq-main-dashboard/bisq-main-dashboard.component';
|
||||||
import { TermsOfServiceComponent } from '../components/terms-of-service/terms-of-service.component';
|
import { TermsOfServiceComponent } from '../components/terms-of-service/terms-of-service.component';
|
||||||
|
import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
@@ -30,6 +31,10 @@ const routes: Routes = [
|
|||||||
path: 'market/:pair',
|
path: 'market/:pair',
|
||||||
component: BisqMarketComponent,
|
component: BisqMarketComponent,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'tx/push',
|
||||||
|
component: PushTransactionComponent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'tx/:id',
|
path: 'tx/:id',
|
||||||
component: BisqTransactionComponent
|
component: BisqTransactionComponent
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export function calcSegwitFeeGains(tx: Transaction) {
|
|||||||
const isP2sh = vin.prevout.scriptpubkey_type === 'p2sh';
|
const isP2sh = vin.prevout.scriptpubkey_type === 'p2sh';
|
||||||
const isP2wsh = vin.prevout.scriptpubkey_type === 'v0_p2wsh';
|
const isP2wsh = vin.prevout.scriptpubkey_type === 'v0_p2wsh';
|
||||||
const isP2wpkh = vin.prevout.scriptpubkey_type === 'v0_p2wpkh';
|
const isP2wpkh = vin.prevout.scriptpubkey_type === 'v0_p2wpkh';
|
||||||
|
const isP2tr = vin.prevout.scriptpubkey_type === 'v1_p2tr';
|
||||||
|
|
||||||
const op = vin.scriptsig ? vin.scriptsig_asm.split(' ')[0] : null;
|
const op = vin.scriptsig ? vin.scriptsig_asm.split(' ')[0] : null;
|
||||||
const isP2sh2Wpkh = isP2sh && !!vin.witness && op === 'OP_PUSHBYTES_22';
|
const isP2sh2Wpkh = isP2sh && !!vin.witness && op === 'OP_PUSHBYTES_22';
|
||||||
@@ -25,6 +26,7 @@ export function calcSegwitFeeGains(tx: Transaction) {
|
|||||||
// Native Segwit - P2WPKH/P2WSH (Bech32)
|
// Native Segwit - P2WPKH/P2WSH (Bech32)
|
||||||
case isP2wpkh:
|
case isP2wpkh:
|
||||||
case isP2wsh:
|
case isP2wsh:
|
||||||
|
case isP2tr:
|
||||||
// maximal gains: the scriptSig is moved entirely to the witness part
|
// maximal gains: the scriptSig is moved entirely to the witness part
|
||||||
realizedGains += witnessSize(vin) * 3;
|
realizedGains += witnessSize(vin) * 3;
|
||||||
// XXX P2WSH output creation is more expensive, should we take this into consideration?
|
// XXX P2WSH output creation is more expensive, should we take this into consideration?
|
||||||
|
|||||||
@@ -34,9 +34,9 @@
|
|||||||
<div class="enterprise-sponsor">
|
<div class="enterprise-sponsor">
|
||||||
<h3 i18n="about.sponsors.enterprise.withRocket">Enterprise Sponsors 🚀</h3>
|
<h3 i18n="about.sponsors.enterprise.withRocket">Enterprise Sponsors 🚀</h3>
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<a href="https://squarecrypto.org/" target="_blank" title="Square Crypto">
|
<a href="https://spiral.xyz/" target="_blank" title="Spiral">
|
||||||
<img class="image" src="/resources/profile/sqcrypto.svg" />
|
<img class="image" src="/resources/profile/spiral.svg" />
|
||||||
<span>Square</span>
|
<span>Spiral</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://gemini.com/" target="_blank" title="Gemini">
|
<a href="https://gemini.com/" target="_blank" title="Gemini">
|
||||||
<img class="image" src="/resources/profile/gemini.svg" />
|
<img class="image" src="/resources/profile/gemini.svg" />
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
<img class="image" src="/resources/profile/foundry.svg" />
|
<img class="image" src="/resources/profile/foundry.svg" />
|
||||||
<span>Foundry</span>
|
<span>Foundry</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://unchained-capital.com/" target="_blank" title="Unchained">
|
<a href="https://unchained.com/" target="_blank" title="Unchained">
|
||||||
<img class="image" src="/resources/profile/unchained.svg" />
|
<img class="image" src="/resources/profile/unchained.svg" />
|
||||||
<span>Unchained</span>
|
<span>Unchained</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -126,6 +126,10 @@
|
|||||||
<img class="image" src="/resources/profile/blixt.png" />
|
<img class="image" src="/resources/profile/blixt.png" />
|
||||||
<span>Blixt</span>
|
<span>Blixt</span>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="https://github.com/ZeusLN/zeus" target="_blank" title="Zeus">
|
||||||
|
<img class="image" src="/resources/profile/zeus.png" />
|
||||||
|
<span>Zeus</span>
|
||||||
|
</a>
|
||||||
<a href="https://github.com/vulpemventures/marina" target="_blank" title="Marina Wallet">
|
<a href="https://github.com/vulpemventures/marina" target="_blank" title="Marina Wallet">
|
||||||
<img class="image" src="/resources/profile/marina.svg" />
|
<img class="image" src="/resources/profile/marina.svg" />
|
||||||
<span>Marina</span>
|
<span>Marina</span>
|
||||||
@@ -138,10 +142,6 @@
|
|||||||
<img class="image" src="/resources/profile/blw.png" />
|
<img class="image" src="/resources/profile/blw.png" />
|
||||||
<span>BLW</span>
|
<span>BLW</span>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/pxsocs/warden" target="_blank" title="WARden Portfolio">
|
|
||||||
<img class="image" src="/resources/profile/warden.jpg" />
|
|
||||||
<span>WARden</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -159,20 +159,32 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="contributors">
|
<ng-container *ngIf="allContributors$ | async as contributors else loadingSponsors">
|
||||||
<h3 i18n="about.contributors">Project Contributors</h3>
|
<div class="contributors">
|
||||||
<div class="wrapper">
|
<h3 i18n="about.contributors">Project Contributors</h3>
|
||||||
<ng-container *ngIf="contributors$ | async as contributors; else loadingSponsors">
|
<div class="wrapper">
|
||||||
<ng-template ngFor let-contributor [ngForOf]="contributors">
|
<ng-template ngFor let-contributor [ngForOf]="contributors.regular">
|
||||||
<a [href]="'https://github.com/' + contributor.name" target="_blank" [title]="contributor.name">
|
<a [href]="'https://github.com/' + contributor.name" target="_blank" [title]="contributor.name">
|
||||||
<img class="image" [src]="'/api/v1/contributors/images/' + contributor.id" />
|
<img class="image" [src]="'/api/v1/contributors/images/' + contributor.id" />
|
||||||
<span>{{ contributor.name }}</span>
|
<span>{{ contributor.name }}</span>
|
||||||
</a>
|
</a>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ng-container>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
<div class="maintainers" *ngIf="contributors.core.length">
|
||||||
|
<h3 i18n="about.project_staff">Project Staff</h3>
|
||||||
|
<div class="wrapper">
|
||||||
|
<ng-template ngFor let-contributor [ngForOf]="contributors.core">
|
||||||
|
<a [href]="'https://github.com/' + contributor.name" target="_blank" [title]="contributor.name">
|
||||||
|
<img class="image" [src]="'/api/v1/contributors/images/' + contributor.id" />
|
||||||
|
<span>{{ contributor.name }}</span>
|
||||||
|
</a>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<div class="maintainers">
|
<div class="maintainers">
|
||||||
<h3 i18n="about.maintainers">Project Maintainers</h3>
|
<h3 i18n="about.maintainers">Project Maintainers</h3>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { Observable } from 'rxjs';
|
|||||||
import { ApiService } from 'src/app/services/api.service';
|
import { ApiService } from 'src/app/services/api.service';
|
||||||
import { IBackendInfo } from 'src/app/interfaces/websocket.interface';
|
import { IBackendInfo } from 'src/app/interfaces/websocket.interface';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-about',
|
selector: 'app-about',
|
||||||
@@ -16,7 +17,7 @@ import { Router } from '@angular/router';
|
|||||||
export class AboutComponent implements OnInit {
|
export class AboutComponent implements OnInit {
|
||||||
backendInfo$: Observable<IBackendInfo>;
|
backendInfo$: Observable<IBackendInfo>;
|
||||||
sponsors$: Observable<any>;
|
sponsors$: Observable<any>;
|
||||||
contributors$: Observable<any>;
|
allContributors$: Observable<any>;
|
||||||
frontendGitCommitHash = this.stateService.env.GIT_COMMIT_HASH;
|
frontendGitCommitHash = this.stateService.env.GIT_COMMIT_HASH;
|
||||||
packetJsonVersion = this.stateService.env.PACKAGE_JSON_VERSION;
|
packetJsonVersion = this.stateService.env.PACKAGE_JSON_VERSION;
|
||||||
officialMempoolSpace = this.stateService.env.OFFICIAL_MEMPOOL_SPACE;
|
officialMempoolSpace = this.stateService.env.OFFICIAL_MEMPOOL_SPACE;
|
||||||
@@ -37,7 +38,14 @@ export class AboutComponent implements OnInit {
|
|||||||
this.websocketService.want(['blocks']);
|
this.websocketService.want(['blocks']);
|
||||||
|
|
||||||
this.sponsors$ = this.apiService.getDonation$();
|
this.sponsors$ = this.apiService.getDonation$();
|
||||||
this.contributors$ = this.apiService.getContributor$();
|
this.allContributors$ = this.apiService.getContributor$().pipe(
|
||||||
|
map((contributors) => {
|
||||||
|
return {
|
||||||
|
regular: contributors.filter((user) => !user.core_constributor),
|
||||||
|
core: contributors.filter((user) => user.core_constributor),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
sponsor() {
|
sponsor() {
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
<div class="container-xl">
|
<div class="container-xl">
|
||||||
<h1 i18n="shared.address">Address</h1>
|
<div class="title-address">
|
||||||
<div class="tx-link">
|
<h1 i18n="shared.address">Address</h1>
|
||||||
<a [routerLink]="['/address/' | relativeUrl, addressString]" >
|
<div class="tx-link">
|
||||||
<span class="d-inline d-lg-none">{{ addressString | shortenString : 18 }}</span>
|
<a [routerLink]="['/address/' | relativeUrl, addressString]" >
|
||||||
<span class="d-none d-lg-inline">{{ addressString }}</span>
|
<span class="d-inline d-lg-none">{{ addressString | shortenString : 18 }}</span>
|
||||||
</a>
|
<span class="d-none d-lg-inline">{{ addressString }}</span>
|
||||||
<app-clipboard [text]="addressString"></app-clipboard>
|
</a>
|
||||||
|
<app-clipboard [text]="addressString"></app-clipboard>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
|
||||||
|
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
<ng-template [ngIf]="!address.electrum">
|
<ng-template [ngIf]="!address.electrum">
|
||||||
<tr>
|
<tr>
|
||||||
<td i18n="address.total-received">Total received</td>
|
<td i18n="address.total-received">Total received</td>
|
||||||
<td *ngIf="address.chain_stats.funded_txo_sum !== undefined; else confidentialTd"><app-amount [satoshis]="receieved" [noFiat]="true"></app-amount></td>
|
<td *ngIf="address.chain_stats.funded_txo_sum !== undefined; else confidentialTd"><app-amount [satoshis]="received" [noFiat]="true"></app-amount></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td i18n="address.total-sent">Total sent</td>
|
<td i18n="address.total-sent">Total sent</td>
|
||||||
@@ -34,7 +35,7 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
<tr>
|
<tr>
|
||||||
<td i18n="address.balance">Balance</td>
|
<td i18n="address.balance">Balance</td>
|
||||||
<td *ngIf="address.chain_stats.funded_txo_sum !== undefined; else confidentialTd"><app-amount [satoshis]="receieved - sent" [noFiat]="true"></app-amount> <span class="fiat"><app-fiat [value]="receieved - sent"></app-fiat></span></td>
|
<td *ngIf="address.chain_stats.funded_txo_sum !== undefined; else confidentialTd"><app-amount [satoshis]="received - sent" [noFiat]="true"></app-amount> <span class="fiat"><app-fiat [value]="received - sent"></app-fiat></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -50,12 +51,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
<div class="title-tx">
|
||||||
<h2>
|
<h2>
|
||||||
<ng-template [ngIf]="!transactions?.length"> </ng-template>
|
<ng-template [ngIf]="!transactions?.length"> </ng-template>
|
||||||
<ng-template i18n="X of X Address Transaction" [ngIf]="transactions?.length === 1">{{ (transactions?.length | number) || '?' }} of {{ txCount | number }} transaction</ng-template>
|
<ng-template i18n="X of X Address Transaction" [ngIf]="transactions?.length === 1">{{ (transactions?.length | number) || '?' }} of {{ txCount | number }} transaction</ng-template>
|
||||||
<ng-template i18n="X of X Address Transactions (Plural)" [ngIf]="transactions?.length > 1">{{ (transactions?.length | number) || '?' }} of {{ txCount | number }} transactions</ng-template>
|
<ng-template i18n="X of X Address Transactions (Plural)" [ngIf]="transactions?.length > 1">{{ (transactions?.length | number) || '?' }} of {{ txCount | number }} transactions</ng-template>
|
||||||
</h2>
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
<app-transactions-list [transactions]="transactions" [showConfirmations]="true" (loadMore)="loadMore()"></app-transactions-list>
|
<app-transactions-list [transactions]="transactions" [showConfirmations]="true" (loadMore)="loadMore()"></app-transactions-list>
|
||||||
|
|
||||||
@@ -85,7 +87,7 @@
|
|||||||
|
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template [ngIf]="retryLoadmore">
|
<ng-template [ngIf]="retryLoadMore">
|
||||||
<br>
|
<br>
|
||||||
<button type="button" class="btn btn-outline-info btn-sm" (click)="loadMore()"><fa-icon [icon]="['fas', 'redo-alt']" [fixedWidth]="true"></fa-icon></button>
|
<button type="button" class="btn btn-outline-info btn-sm" (click)="loadMore()"><fa-icon [icon]="['fas', 'redo-alt']" [fixedWidth]="true"></fa-icon></button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
@@ -114,7 +116,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="w-100 d-block d-md-none"></div>
|
<div class="w-100 d-block d-md-none"></div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -122,10 +124,11 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template [ngIf]="error">
|
<ng-template [ngIf]="error">
|
||||||
|
<br>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<span i18n="address.error.loading-address-data">Error loading address data.</span>
|
<span i18n="address.error.loading-address-data">Error loading address data.</span>
|
||||||
<br>
|
<br>
|
||||||
<ng-template #displayServerError><i>{{ error.error }}</i></ng-template>
|
<ng-template #displayServerError><i class="small">({{ error.error }})</i></ng-template>
|
||||||
<ng-template [ngIf]="error.status === 413 || error.status === 405" [ngIfElse]="displayServerError">
|
<ng-template [ngIf]="error.status === 413 || error.status === 405" [ngIfElse]="displayServerError">
|
||||||
<ng-container i18n="Electrum server limit exceeded error">
|
<ng-container i18n="Electrum server limit exceeded error">
|
||||||
<i>The number of transactions on this address exceeds the Electrum server limit</i>
|
<i>The number of transactions on this address exceeds the Electrum server limit</i>
|
||||||
@@ -136,6 +139,8 @@
|
|||||||
<a href="https://mempool.space/address/{{ addressString }}" target="_blank">https://mempool.space/address/{{ addressString }}</a>
|
<a href="https://mempool.space/address/{{ addressString }}" target="_blank">https://mempool.space/address/{{ addressString }}</a>
|
||||||
<br>
|
<br>
|
||||||
<a href="http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/address/{{ addressString }}" target="_blank">http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/address/{{ addressString }}</a>
|
<a href="http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/address/{{ addressString }}" target="_blank">http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion/address/{{ addressString }}</a>
|
||||||
|
<br><br>
|
||||||
|
<i class="small">({{ error.error }})</i>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|||||||
@@ -35,17 +35,22 @@
|
|||||||
h1 {
|
h1 {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
|
margin-right: 10px;
|
||||||
|
font-size: 1.9rem;
|
||||||
@media (min-width: 576px) {
|
@media (min-width: 576px) {
|
||||||
|
font-size: 2rem;
|
||||||
float: left;
|
float: left;
|
||||||
margin-right: 10px;
|
}
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
font-size: 2.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.address-link {
|
.address-link {
|
||||||
line-height: 56px;
|
line-height: 56px;
|
||||||
margin-left: 0px;
|
margin-left: 0px;
|
||||||
top: -2px;
|
top: -2px;
|
||||||
position: relative;
|
position: relative;
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
line-height: 69px;
|
line-height: 69px;
|
||||||
}
|
}
|
||||||
@@ -69,10 +74,20 @@ h1 {
|
|||||||
|
|
||||||
.tx-link {
|
.tx-link {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
height: 100%;
|
||||||
top: 14px;
|
top: 9px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@media (min-width: 576px) {
|
||||||
|
top: 11px;
|
||||||
|
}
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
top: 20px;
|
top: 17px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.title-tx {
|
||||||
|
h2 {
|
||||||
|
line-height: 1;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export class AddressComponent implements OnInit, OnDestroy {
|
|||||||
isLoadingAddress = true;
|
isLoadingAddress = true;
|
||||||
transactions: Transaction[];
|
transactions: Transaction[];
|
||||||
isLoadingTransactions = true;
|
isLoadingTransactions = true;
|
||||||
retryLoadmore = false;
|
retryLoadMore = false;
|
||||||
error: any;
|
error: any;
|
||||||
mainSubscription: Subscription;
|
mainSubscription: Subscription;
|
||||||
addressLoadingStatus$: Observable<number>;
|
addressLoadingStatus$: Observable<number>;
|
||||||
@@ -33,7 +33,7 @@ export class AddressComponent implements OnInit, OnDestroy {
|
|||||||
totalConfirmedTxCount = 0;
|
totalConfirmedTxCount = 0;
|
||||||
loadedConfirmedTxCount = 0;
|
loadedConfirmedTxCount = 0;
|
||||||
txCount = 0;
|
txCount = 0;
|
||||||
receieved = 0;
|
received = 0;
|
||||||
sent = 0;
|
sent = 0;
|
||||||
|
|
||||||
private tempTransactions: Transaction[];
|
private tempTransactions: Transaction[];
|
||||||
@@ -142,7 +142,13 @@ export class AddressComponent implements OnInit, OnDestroy {
|
|||||||
this.tempTransactions[this.timeTxIndexes[index]].firstSeen = time;
|
this.tempTransactions[this.timeTxIndexes[index]].firstSeen = time;
|
||||||
});
|
});
|
||||||
this.tempTransactions.sort((a, b) => {
|
this.tempTransactions.sort((a, b) => {
|
||||||
return b.status.block_time - a.status.block_time || b.firstSeen - a.firstSeen;
|
if (b.status.confirmed) {
|
||||||
|
if (b.status.block_height === a.status.block_height) {
|
||||||
|
return b.status.block_time - a.status.block_time;
|
||||||
|
}
|
||||||
|
return b.status.block_height - a.status.block_height;
|
||||||
|
}
|
||||||
|
return b.firstSeen - a.firstSeen;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.transactions = this.tempTransactions;
|
this.transactions = this.tempTransactions;
|
||||||
@@ -177,7 +183,7 @@ export class AddressComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
transaction.vout.forEach((vout) => {
|
transaction.vout.forEach((vout) => {
|
||||||
if (vout.scriptpubkey_address === this.address.address) {
|
if (vout.scriptpubkey_address === this.address.address) {
|
||||||
this.receieved += vout.value;
|
this.received += vout.value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -200,7 +206,7 @@ export class AddressComponent implements OnInit, OnDestroy {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.isLoadingTransactions = true;
|
this.isLoadingTransactions = true;
|
||||||
this.retryLoadmore = false;
|
this.retryLoadMore = false;
|
||||||
this.electrsApiService.getAddressTransactionsFromHash$(this.address.address, this.lastTransactionTxId)
|
this.electrsApiService.getAddressTransactionsFromHash$(this.address.address, this.lastTransactionTxId)
|
||||||
.subscribe((transactions: Transaction[]) => {
|
.subscribe((transactions: Transaction[]) => {
|
||||||
this.lastTransactionTxId = transactions[transactions.length - 1].txid;
|
this.lastTransactionTxId = transactions[transactions.length - 1].txid;
|
||||||
@@ -210,12 +216,12 @@ export class AddressComponent implements OnInit, OnDestroy {
|
|||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
this.isLoadingTransactions = false;
|
this.isLoadingTransactions = false;
|
||||||
this.retryLoadmore = true;
|
this.retryLoadMore = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updateChainStats() {
|
updateChainStats() {
|
||||||
this.receieved = this.address.chain_stats.funded_txo_sum + this.address.mempool_stats.funded_txo_sum;
|
this.received = this.address.chain_stats.funded_txo_sum + this.address.mempool_stats.funded_txo_sum;
|
||||||
this.sent = this.address.chain_stats.spent_txo_sum + this.address.mempool_stats.spent_txo_sum;
|
this.sent = this.address.chain_stats.spent_txo_sum + this.address.mempool_stats.spent_txo_sum;
|
||||||
this.txCount = this.address.chain_stats.tx_count + this.address.mempool_stats.tx_count;
|
this.txCount = this.address.chain_stats.tx_count + this.address.mempool_stats.tx_count;
|
||||||
this.totalConfirmedTxCount = this.address.chain_stats.tx_count;
|
this.totalConfirmedTxCount = this.address.chain_stats.tx_count;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<ng-container *ngIf="{ val: network$ | async } as network">
|
<ng-container *ngIf="{ val: network$ | async } as network">
|
||||||
<div class="container-xl">
|
<div class="container-xl text-left">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<h2>{{ network.val === '' ? 'Bitcoin' : network.val.charAt(0).toUpperCase() + network.val.slice(1) }} <ng-container i18n="api-docs.title">API Service</ng-container></h2>
|
<h2>{{ network.val === '' ? 'Bitcoin' : network.val.charAt(0).toUpperCase() + network.val.slice(1) }} <ng-container i18n="api-docs.title">API Service</ng-container></h2>
|
||||||
</div>
|
</div>
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li *ngIf="network.val === 'bisq'" [ngbNavItem]="0">
|
<li *ngIf="network.val === 'bisq'" [ngbNavItem]="0">
|
||||||
<a ngbNavLink i18n="api-docs.tab.bsq|API Docs tab for BSQ">Markets</a>
|
<a ngbNavLink i18n="Bisq All Markets">Markets</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
|
<ngb-accordion [closeOthers]="true" animated="true" type="dark" >
|
||||||
|
|
||||||
@@ -698,7 +698,7 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template ngbPanelContent>
|
<ng-template ngbPanelContent>
|
||||||
<div class="endpoint">
|
<div class="endpoint">
|
||||||
<a [href]="wrapUrl(network.val, code.mempoolRecent)" 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">
|
||||||
<div class="subtitle" i18n>Description</div>
|
<div class="subtitle" i18n>Description</div>
|
||||||
|
|||||||
@@ -40,18 +40,6 @@ export class ApiDocsComponent implements OnInit {
|
|||||||
|
|
||||||
this.hostname = `${document.location.protocol}//${this.hostname}`;
|
this.hostname = `${document.location.protocol}//${this.hostname}`;
|
||||||
|
|
||||||
if (document.location.hostname === 'localhost') {
|
|
||||||
if (this.env.BASE_MODULE === 'bisq') {
|
|
||||||
this.hostname = `https://bisq.markets`;
|
|
||||||
}
|
|
||||||
if (this.env.BASE_MODULE === 'liquid') {
|
|
||||||
this.hostname = `https://liquid.network`;
|
|
||||||
}
|
|
||||||
if (this.env.BASE_MODULE === 'mempool') {
|
|
||||||
this.hostname = `https://mempool.space`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.code = {
|
this.code = {
|
||||||
address: {
|
address: {
|
||||||
codeTemplate: {
|
codeTemplate: {
|
||||||
@@ -73,17 +61,17 @@ export class ApiDocsComponent implements OnInit {
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
codeSampleMainnet: {
|
codeSampleMainnet: {
|
||||||
esModule: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
esModule: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
commonJS: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
commonJS: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
curl: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
curl: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
response: `{
|
response: `{
|
||||||
address: "1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC",
|
address: "1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv",
|
||||||
chain_stats: {
|
chain_stats: {
|
||||||
funded_txo_count: 765,
|
funded_txo_count: 5,
|
||||||
funded_txo_sum: 87749875807,
|
funded_txo_sum: 15007599040,
|
||||||
spent_txo_count: 765,
|
spent_txo_count: 5,
|
||||||
spent_txo_sum: 87749875807,
|
spent_txo_sum: 15007599040,
|
||||||
tx_count: 875
|
tx_count: 7
|
||||||
},
|
},
|
||||||
mempool_stats: {
|
mempool_stats: {
|
||||||
funded_txo_count: 0,
|
funded_txo_count: 0,
|
||||||
@@ -117,17 +105,17 @@ export class ApiDocsComponent implements OnInit {
|
|||||||
}`
|
}`
|
||||||
},
|
},
|
||||||
codeSampleSignet: {
|
codeSampleSignet: {
|
||||||
esModule: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
esModule: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
commonJS: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
commonJS: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
curl: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
curl: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
response: `{
|
response: `{
|
||||||
address: "1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC",
|
address: "1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv",
|
||||||
chain_stats: {
|
chain_stats: {
|
||||||
funded_txo_count: 765,
|
funded_txo_count: 5,
|
||||||
funded_txo_sum: 87749875807,
|
funded_txo_sum: 15007599040,
|
||||||
spent_txo_count: 765,
|
spent_txo_count: 5,
|
||||||
spent_txo_sum: 87749875807,
|
spent_txo_sum: 15007599040,
|
||||||
tx_count: 875
|
tx_count: 7
|
||||||
},
|
},
|
||||||
mempool_stats: {
|
mempool_stats: {
|
||||||
funded_txo_count: 0,
|
funded_txo_count: 0,
|
||||||
@@ -199,24 +187,24 @@ export class ApiDocsComponent implements OnInit {
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
codeSampleMainnet: {
|
codeSampleMainnet: {
|
||||||
esModule: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
esModule: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
commonJS: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
commonJS: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
curl: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
curl: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
response: `[
|
response: `[
|
||||||
{
|
{
|
||||||
txid: "f39fbfd2482ac8a7174fe27caddd66aec05eec0d0e988ddf0de2136a416394c4",
|
txid: "dba43fd04b7ae3df8e5b596f2e7fab247c58629d622e3a5213f03a5a09684430",
|
||||||
version: 2,
|
version: 1,
|
||||||
locktime: 0,
|
locktime: 0,
|
||||||
vin: [ [Object] ],
|
vin: [ [Object] ],
|
||||||
vout: [ [Object], [Object] ],
|
vout: [ [Object], [Object] ],
|
||||||
size: 251,
|
size: 255,
|
||||||
weight: 1004,
|
weight: 1020,
|
||||||
fee: 8212,
|
fee: 10000,
|
||||||
status: {
|
status: {
|
||||||
confirmed: true,
|
confirmed: true,
|
||||||
block_height: 684536,
|
block_height: 326148,
|
||||||
block_hash: "00000000000000000008df08f428ca4e8251ba9171d9060b056f1f94d4fefbc7",
|
block_hash: "00000000000000001e4118adcfbb02364bc13c41c210d8811e4f39aeb3687e36",
|
||||||
block_time: 1621687336
|
block_time: 1413798020
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
...
|
...
|
||||||
@@ -247,17 +235,17 @@ export class ApiDocsComponent implements OnInit {
|
|||||||
]`
|
]`
|
||||||
},
|
},
|
||||||
codeSampleSignet: {
|
codeSampleSignet: {
|
||||||
esModule: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
esModule: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
commonJS: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
commonJS: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
curl: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
curl: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
response: `{
|
response: `{
|
||||||
address: "1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC",
|
address: "1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv",
|
||||||
chain_stats: {
|
chain_stats: {
|
||||||
funded_txo_count: 765,
|
funded_txo_count: 5,
|
||||||
funded_txo_sum: 87749875807,
|
funded_txo_sum: 15007599040,
|
||||||
spent_txo_count: 765,
|
spent_txo_count: 5,
|
||||||
spent_txo_sum: 87749875807,
|
spent_txo_sum: 15007599040,
|
||||||
tx_count: 875
|
tx_count: 7
|
||||||
},
|
},
|
||||||
mempool_stats: {
|
mempool_stats: {
|
||||||
funded_txo_count: 0,
|
funded_txo_count: 0,
|
||||||
@@ -319,9 +307,9 @@ export class ApiDocsComponent implements OnInit {
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
codeSampleMainnet: {
|
codeSampleMainnet: {
|
||||||
esModule: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
esModule: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
commonJS: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
commonJS: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
curl: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
curl: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
response: `[
|
response: `[
|
||||||
{
|
{
|
||||||
txid: "c4e53c2e37f4fac759fdb0d8380e4d49e6c7211233ae276a44ce7074a1d6d168",
|
txid: "c4e53c2e37f4fac759fdb0d8380e4d49e6c7211233ae276a44ce7074a1d6d168",
|
||||||
@@ -367,11 +355,11 @@ export class ApiDocsComponent implements OnInit {
|
|||||||
],`
|
],`
|
||||||
},
|
},
|
||||||
codeSampleSignet: {
|
codeSampleSignet: {
|
||||||
esModule: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
esModule: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
commonJS: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
commonJS: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
curl: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
curl: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
response: `{
|
response: `{
|
||||||
address: "1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC",
|
address: "1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv",
|
||||||
chain_stats: {
|
chain_stats: {
|
||||||
funded_txo_count: 765,
|
funded_txo_count: 765,
|
||||||
funded_txo_sum: 87749875807,
|
funded_txo_sum: 87749875807,
|
||||||
@@ -439,9 +427,9 @@ export class ApiDocsComponent implements OnInit {
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
codeSampleMainnet: {
|
codeSampleMainnet: {
|
||||||
esModule: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
esModule: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
commonJS: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
commonJS: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
curl: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
curl: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
response: `[
|
response: `[
|
||||||
{
|
{
|
||||||
txid: "16cd9bbc6b62313a22d16671fa559aec6bf581df8b5853d37775c84b0fddfa90",
|
txid: "16cd9bbc6b62313a22d16671fa559aec6bf581df8b5853d37775c84b0fddfa90",
|
||||||
@@ -475,9 +463,9 @@ export class ApiDocsComponent implements OnInit {
|
|||||||
]`
|
]`
|
||||||
},
|
},
|
||||||
codeSampleSignet: {
|
codeSampleSignet: {
|
||||||
esModule: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
esModule: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
commonJS: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
commonJS: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
curl: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
curl: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
response: `[
|
response: `[
|
||||||
{
|
{
|
||||||
txid: "16cd9bbc6b62313a22d16671fa559aec6bf581df8b5853d37775c84b0fddfa90",
|
txid: "16cd9bbc6b62313a22d16671fa559aec6bf581df8b5853d37775c84b0fddfa90",
|
||||||
@@ -575,9 +563,9 @@ export class ApiDocsComponent implements OnInit {
|
|||||||
]`
|
]`
|
||||||
},
|
},
|
||||||
codeSampleSignet: {
|
codeSampleSignet: {
|
||||||
esModule: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
esModule: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
commonJS: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
commonJS: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
curl: [`1wizSAYSbuyXbt9d8JV8ytm5acqq2TorC`],
|
curl: [`1wiz18xYmhRX6xStj2b9t1rwWX4GKUgpv`],
|
||||||
response: `[
|
response: `[
|
||||||
{
|
{
|
||||||
txid: "e58b47f657b496a083ad9a4fb10c744d5e993028efd9cfba149885334d98bdf5",
|
txid: "e58b47f657b496a083ad9a4fb10c744d5e993028efd9cfba149885334d98bdf5",
|
||||||
@@ -2063,7 +2051,7 @@ export class ApiDocsComponent implements OnInit {
|
|||||||
},
|
},
|
||||||
transactionCpfp: {
|
transactionCpfp: {
|
||||||
codeTemplate: {
|
codeTemplate: {
|
||||||
curl: `/api/fees/cpfp/%{1}`,
|
curl: `/api/v1/cpfp/%{1}`,
|
||||||
commonJS: `
|
commonJS: `
|
||||||
const { %{0}: { fees } } = mempoolJS();
|
const { %{0}: { fees } } = mempoolJS();
|
||||||
|
|
||||||
@@ -3428,8 +3416,8 @@ export class ApiDocsComponent implements OnInit {
|
|||||||
|
|
||||||
ws.addEventListener('message', function incoming({data}) {
|
ws.addEventListener('message', function incoming({data}) {
|
||||||
const res = JSON.parse(data.toString());
|
const res = JSON.parse(data.toString());
|
||||||
if (res.blocks) {
|
if (res.block) {
|
||||||
document.getElementById("result-blocks").textContent = JSON.stringify(res.blocks, undefined, 2);
|
document.getElementById("result-blocks").textContent = JSON.stringify(res.block, undefined, 2);
|
||||||
}
|
}
|
||||||
if (res.mempoolInfo) {
|
if (res.mempoolInfo) {
|
||||||
document.getElementById("result-mempool-info").textContent = JSON.stringify(res.mempoolInfo, undefined, 2);
|
document.getElementById("result-mempool-info").textContent = JSON.stringify(res.mempoolInfo, undefined, 2);
|
||||||
@@ -3437,8 +3425,8 @@ export class ApiDocsComponent implements OnInit {
|
|||||||
if (res.transactions) {
|
if (res.transactions) {
|
||||||
document.getElementById("result-transactions").textContent = JSON.stringify(res.transactions, undefined, 2);
|
document.getElementById("result-transactions").textContent = JSON.stringify(res.transactions, undefined, 2);
|
||||||
}
|
}
|
||||||
if (res.mempoolBlocks) {
|
if (res["mempool-blocks"]) {
|
||||||
document.getElementById("result-mempool-blocks").textContent = JSON.stringify(res.mempoolBlocks, undefined, 2);
|
document.getElementById("result-mempool-blocks").textContent = JSON.stringify(res["mempool-blocks"], undefined, 2);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
@@ -3451,8 +3439,8 @@ export class ApiDocsComponent implements OnInit {
|
|||||||
|
|
||||||
ws.on("message", function incoming(data) {
|
ws.on("message", function incoming(data) {
|
||||||
const res = JSON.parse(data.toString());
|
const res = JSON.parse(data.toString());
|
||||||
if (res.blocks) {
|
if (res.block) {
|
||||||
console.log(res.blocks);
|
console.log(res.block);
|
||||||
}
|
}
|
||||||
if (res.mempoolInfo) {
|
if (res.mempoolInfo) {
|
||||||
console.log(res.mempoolInfo);
|
console.log(res.mempoolInfo);
|
||||||
@@ -3460,8 +3448,8 @@ export class ApiDocsComponent implements OnInit {
|
|||||||
if (res.transactions) {
|
if (res.transactions) {
|
||||||
console.log(res.transactions);
|
console.log(res.transactions);
|
||||||
}
|
}
|
||||||
if (res.mempoolBlocks) {
|
if (res["mempool-blocks"]) {
|
||||||
console.log(res.mempoolBlocks);
|
console.log(res["mempool-blocks"]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
|
|||||||
@@ -174,10 +174,10 @@ init();`;
|
|||||||
|
|
||||||
let resultHtml = '<pre id="result"></pre>';
|
let resultHtml = '<pre id="result"></pre>';
|
||||||
if (this.method === 'websocket') {
|
if (this.method === 'websocket') {
|
||||||
resultHtml = `<pre id="result-blocks"></pre>
|
resultHtml = `<h2>Blocks</h2><pre id="result-blocks">Waiting for data</pre><br>
|
||||||
<pre id="result-mempool-info"></pre>
|
<h2>Mempool Info</h2><pre id="result-mempool-info">Waiting for data</pre><br>
|
||||||
<pre id="result-transactions"></pre>
|
<h2>Transactions</h2><pre id="result-transactions">Waiting for data</pre><br>
|
||||||
<pre id="result-mempool-blocks"></pre>`;
|
<h2>Mempool Blocks</h2><pre id="result-mempool-blocks">Waiting for data</pre><br>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<!DOCTYPE html>
|
return `<!DOCTYPE html>
|
||||||
@@ -284,13 +284,13 @@ yarn add @mempool/liquid.js`;
|
|||||||
if (this.env.BASE_MODULE === 'mempool') {
|
if (this.env.BASE_MODULE === 'mempool') {
|
||||||
if (this.network === 'main' || this.network === '') {
|
if (this.network === 'main' || this.network === '') {
|
||||||
if (this.method === 'post') {
|
if (this.method === 'post') {
|
||||||
return `curl POST -sSLd "${text}"`;
|
return `curl -X POST -sSLd "${text}"`;
|
||||||
}
|
}
|
||||||
return `curl -sSL "${this.hostname}${text}"`;
|
return `curl -sSL "${this.hostname}${text}"`;
|
||||||
}
|
}
|
||||||
if (this.method === 'post') {
|
if (this.method === 'post') {
|
||||||
text = text.replace('/api', `/${this.network}/api`);
|
text = text.replace('/api', `/${this.network}/api`);
|
||||||
return `curl POST -sSLd "${text}"`;
|
return `curl -X POST -sSLd "${text}"`;
|
||||||
}
|
}
|
||||||
return `curl -sSL "${this.hostname}/${this.network}${text}"`;
|
return `curl -sSL "${this.hostname}/${this.network}${text}"`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,13 @@ export class AppComponent implements OnInit {
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.router.events.subscribe((val) => {
|
this.router.events.subscribe((val) => {
|
||||||
if (val instanceof NavigationEnd) {
|
if (val instanceof NavigationEnd) {
|
||||||
this.link.setAttribute('href', 'https://mempool.space' + (this.location.path() === '/' ? '' : this.location.path()));
|
let domain = 'mempool.space';
|
||||||
|
if (this.stateService.env.BASE_MODULE === 'liquid') {
|
||||||
|
domain = 'liquid.network';
|
||||||
|
} else if (this.stateService.env.BASE_MODULE === 'bisq') {
|
||||||
|
domain = 'bisq.markets';
|
||||||
|
}
|
||||||
|
this.link.setAttribute('href', 'https://' + domain + this.location.path());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
<div class="container-xl">
|
<div class="container-xl">
|
||||||
<h1 style="float: left;" i18n="asset|Liquid Asset page title">Asset</h1>
|
<div class="title-asset">
|
||||||
<a [routerLink]="['/asset/' | relativeUrl, assetString]" style="line-height: 56px; margin-left: 10px;">
|
<h1 i18n="asset|Liquid Asset page title">Asset</h1>
|
||||||
<span class="d-inline d-lg-none">{{ assetString | shortenString : 24 }}</span>
|
<div class="tx-link">
|
||||||
<span class="d-none d-lg-inline">{{ assetString }}</span>
|
<a [routerLink]="['/asset/' | relativeUrl, assetString]">
|
||||||
</a>
|
<span class="d-inline d-lg-none">{{ assetString | shortenString : 24 }}</span>
|
||||||
<app-clipboard [text]="assetString"></app-clipboard>
|
<span class="d-none d-lg-inline">{{ assetString }}</span>
|
||||||
<br>
|
</a>
|
||||||
|
<app-clipboard [text]="assetString"></app-clipboard>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
|
|
||||||
@@ -72,12 +75,14 @@
|
|||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<h2>
|
<div class="title-tx">
|
||||||
<ng-template [ngIf]="transactions?.length" i18n="asset.M_of_N">{{ (transactions?.length | number) || '?' }} of {{ txCount | number }} </ng-template>
|
<h2>
|
||||||
<ng-template [ngIf]="isNativeAsset" [ngIfElse]="defaultAsset" i18n="Liquid native asset transactions title">Peg In/Out and Burn Transactions</ng-template>
|
<ng-template [ngIf]="transactions?.length" i18n="asset.M_of_N">{{ (transactions?.length | number) || '?' }} of {{ txCount | number }} </ng-template>
|
||||||
<ng-template #defaultAsset i18n="Default asset transactions title">Issuance and Burn Transactions</ng-template>
|
<ng-template [ngIf]="isNativeAsset" [ngIfElse]="defaultAsset" i18n="Liquid native asset transactions title">Peg In/Out and Burn Transactions</ng-template>
|
||||||
</h2>
|
<ng-template #defaultAsset i18n="Default asset transactions title">Issuance and Burn Transactions</ng-template>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
<app-transactions-list [transactions]="transactions" [showConfirmations]="true" (loadMore)="loadMore()"></app-transactions-list>
|
<app-transactions-list [transactions]="transactions" [showConfirmations]="true" (loadMore)="loadMore()"></app-transactions-list>
|
||||||
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
|
|||||||
@@ -20,4 +20,33 @@
|
|||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
margin-right: 0px;
|
margin-right: 0px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
margin-right: 15px;
|
||||||
|
@media (min-width: 576px) {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tx-link {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
top: 9px;
|
||||||
|
position: relative;
|
||||||
|
@media (min-width: 576px) {
|
||||||
|
top: 11px;
|
||||||
|
}
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
top: 17px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.title-tx {
|
||||||
|
h2 {
|
||||||
|
line-height: 1;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -136,7 +136,13 @@ export class AssetComponent implements OnInit, OnDestroy {
|
|||||||
this.tempTransactions[this.timeTxIndexes[index]].firstSeen = time;
|
this.tempTransactions[this.timeTxIndexes[index]].firstSeen = time;
|
||||||
});
|
});
|
||||||
this.tempTransactions.sort((a, b) => {
|
this.tempTransactions.sort((a, b) => {
|
||||||
return b.status.block_time - a.status.block_time || b.firstSeen - a.firstSeen;
|
if (b.status.confirmed) {
|
||||||
|
if (b.status.block_height === a.status.block_height) {
|
||||||
|
return b.status.block_time - a.status.block_time;
|
||||||
|
}
|
||||||
|
return b.status.block_height - a.status.block_height;
|
||||||
|
}
|
||||||
|
return b.firstSeen - a.firstSeen;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.transactions = this.tempTransactions;
|
this.transactions = this.tempTransactions;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
<div class="title-block" id="block">
|
<div class="title-block" id="block">
|
||||||
<h1>
|
<h1>
|
||||||
<ng-template [ngIf]="blockHeight === 0" i18n="block.genesis">Genesis
|
<ng-template [ngIf]="blockHeight === 0"><ng-container i18n="@@2303359202781425764">Genesis</ng-container>
|
||||||
<div class="next-previous-blocks">
|
<span class="next-previous-blocks">
|
||||||
<a *ngIf="showNextBlocklink" [routerLink]="['/block/' | relativeUrl, nextBlockHeight]" (click)="navigateToNextBlock()" i18n-ngbTooltip="Next Block" ngbTooltip="Next Block" placement="bottom">
|
<a *ngIf="showNextBlocklink" [routerLink]="['/block/' | relativeUrl, nextBlockHeight]" (click)="navigateToNextBlock()" i18n-ngbTooltip="Next Block" ngbTooltip="Next Block" placement="bottom">
|
||||||
<fa-icon [icon]="['fas', 'angle-left']" [fixedWidth]="true"></fa-icon>
|
<fa-icon [icon]="['fas', 'angle-left']" [fixedWidth]="true"></fa-icon>
|
||||||
</a>
|
</a>
|
||||||
@@ -11,10 +11,11 @@
|
|||||||
<span placement="bottom" class="disable">
|
<span placement="bottom" class="disable">
|
||||||
<fa-icon [icon]="['fas', 'angle-right']" [fixedWidth]="true"></fa-icon>
|
<fa-icon [icon]="['fas', 'angle-right']" [fixedWidth]="true"></fa-icon>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</span>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template [ngIf]="blockHeight" i18n="block.block"> Block
|
<ng-template [ngIf]="blockHeight" i18n="shared.block-title">Block <ng-container *ngTemplateOutlet="blockTemplateContent"></ng-container></ng-template>
|
||||||
<div class="next-previous-blocks">
|
<ng-template #blockTemplateContent>
|
||||||
|
<span class="next-previous-blocks">
|
||||||
<a *ngIf="showNextBlocklink" [routerLink]="['/block/' | relativeUrl, nextBlockHeight]" (click)="navigateToNextBlock()" i18n-ngbTooltip="Next Block" ngbTooltip="Next Block" placement="bottom">
|
<a *ngIf="showNextBlocklink" [routerLink]="['/block/' | relativeUrl, nextBlockHeight]" (click)="navigateToNextBlock()" i18n-ngbTooltip="Next Block" ngbTooltip="Next Block" placement="bottom">
|
||||||
<fa-icon [icon]="['fas', 'angle-left']" [fixedWidth]="true"></fa-icon>
|
<fa-icon [icon]="['fas', 'angle-left']" [fixedWidth]="true"></fa-icon>
|
||||||
</a>
|
</a>
|
||||||
@@ -28,7 +29,7 @@
|
|||||||
<span *ngIf="!showPreviousBlocklink" placement="bottom" class="disable">
|
<span *ngIf="!showPreviousBlocklink" placement="bottom" class="disable">
|
||||||
<fa-icon [icon]="['fas', 'angle-right']" [fixedWidth]="true"></fa-icon>
|
<fa-icon [icon]="['fas', 'angle-right']" [fixedWidth]="true"></fa-icon>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</span>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
@@ -53,7 +54,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td i18n="block.timestamp">Timestamp</td>
|
<td i18n="block.timestamp">Timestamp</td>
|
||||||
<td>
|
<td>
|
||||||
{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }}
|
‎{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }}
|
||||||
<div class="lg-inline">
|
<div class="lg-inline">
|
||||||
<i class="symbol">(<app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since>)</i>
|
<i class="symbol">(<app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since>)</i>
|
||||||
</div>
|
</div>
|
||||||
@@ -61,11 +62,11 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td i18n="block.size">Size</td>
|
<td i18n="block.size">Size</td>
|
||||||
<td [innerHTML]="block.size | bytes: 2"></td>
|
<td [innerHTML]="'‎' + (block.size | bytes: 2)"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td i18n="block.weight">Weight</td>
|
<td i18n="block.weight">Weight</td>
|
||||||
<td [innerHTML]="block.weight | wuBytes: 2"></td>
|
<td [innerHTML]="'‎' + (block.weight | wuBytes: 2)"></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -168,21 +169,21 @@
|
|||||||
<ng-template #transactionsPlural let-i i18n="shared.transaction-count.plural">{{ i }} transactions</ng-template>
|
<ng-template #transactionsPlural let-i i18n="shared.transaction-count.plural">{{ i }} transactions</ng-template>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<ngb-pagination class="pagination-container" [collectionSize]="block.tx_count" [rotate]="true" [pageSize]="itemsPerPage" [(page)]="page" (pageChange)="pageChange(page, blockTxTitle)" [maxSize]="paginationMaxSize" [boundaryLinks]="true" [ellipses]="false"></ngb-pagination>
|
<ngb-pagination class="pagination-container float-right" [collectionSize]="block.tx_count" [rotate]="true" [pageSize]="itemsPerPage" [(page)]="page" (pageChange)="pageChange(page, blockTxTitle)" [maxSize]="paginationMaxSize" [boundaryLinks]="true" [ellipses]="false"></ngb-pagination>
|
||||||
</div>
|
</div>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
|
|
||||||
<app-transactions-list [transactions]="transactions"></app-transactions-list>
|
<app-transactions-list [transactions]="transactions"></app-transactions-list>
|
||||||
|
|
||||||
<ng-template [ngIf]="isLoadingTransactions">
|
<ng-template [ngIf]="isLoadingTransactions">
|
||||||
<div class="text-center mb-4 mt-3">
|
<div class="text-center mb-4" class="tx-skeleton">
|
||||||
|
|
||||||
<div class="header-bg box" style="padding: 10px; margin-bottom: 10px;">
|
<div class="header-bg box">
|
||||||
<span class="skeleton-loader"></span>
|
<span class="skeleton-loader"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="header-bg box">
|
<div class="header-bg box">
|
||||||
<div class="row" style="height: 107px;">
|
<div class="row">
|
||||||
<div class="col-sm">
|
<div class="col-sm">
|
||||||
<span class="skeleton-loader"></span>
|
<span class="skeleton-loader"></span>
|
||||||
</div>
|
</div>
|
||||||
@@ -199,7 +200,7 @@
|
|||||||
<div class="progress-bar progress-darklight" role="progressbar" [ngStyle]="{'width': txsLoadingStatus + '%' }"></div>
|
<div class="progress-bar progress-darklight" role="progressbar" [ngStyle]="{'width': txsLoadingStatus + '%' }"></div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ngb-pagination class="pagination-container float-right" [collectionSize]="block.tx_count" [rotate]="true" [pageSize]="itemsPerPage" [(page)]="page" (pageChange)="pageChange(page, blockTxTitle)" [maxSize]="paginationMaxSize" [boundaryLinks]="true" [ellipses]="false"></ngb-pagination>
|
<ngb-pagination class="pagination-container float-right" [collectionSize]="block.tx_count" [rotate]="true" [pageSize]="itemsPerPage" [(page)]="page" (pageChange)="pageChange(page, blockTxTitle)" [maxSize]="paginationMaxSize" [boundaryLinks]="true" [ellipses]="false"></ngb-pagination>
|
||||||
@@ -207,7 +208,7 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template [ngIf]="isLoadingBlock && !error">
|
<ng-template [ngIf]="isLoadingBlock && !error">
|
||||||
|
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm">
|
<div class="col-sm">
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.fiat {
|
.fiat {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@@ -40,10 +40,7 @@
|
|||||||
h1 {
|
h1 {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
@media (min-width: 576px) {
|
line-height: 1;
|
||||||
float: left;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
a {
|
a {
|
||||||
&:hover, &:focus{
|
&:hover, &:focus{
|
||||||
text-decoration: none;;
|
text-decoration: none;;
|
||||||
@@ -87,32 +84,23 @@ h1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.block-tx-title {
|
.block-tx-title {
|
||||||
padding-top: 10px;
|
display: flex;
|
||||||
display: block;
|
justify-content: space-between;
|
||||||
text-align: right;
|
flex-direction: column;
|
||||||
margin-top: -30px;
|
margin-top: -15px;
|
||||||
|
position: relative;
|
||||||
@media (min-width: 550px) {
|
@media (min-width: 550px) {
|
||||||
margin-top: 0px;
|
margin-top: 1rem;
|
||||||
padding-top: 10px;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
h2 {
|
h2 {
|
||||||
display: inline-block;
|
line-height: 1;
|
||||||
float: left;
|
|
||||||
line-height: 1.6;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-bottom: -15px;
|
|
||||||
padding-right: 10px;
|
|
||||||
padding-top: 15px;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
top: -22px;
|
padding-bottom: 10px;
|
||||||
width: auto;
|
|
||||||
@media (min-width: 550px) {
|
@media (min-width: 550px) {
|
||||||
padding-top: 0px;
|
padding-bottom: 0px;
|
||||||
top: 0px;
|
align-self: end;
|
||||||
}
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
padding-top: 5px;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,22 +110,41 @@ h1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.next-previous-blocks {
|
.next-previous-blocks {
|
||||||
font-size: 36px;
|
font-size: 28px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: bottom;
|
@media (min-width: 768px) {
|
||||||
|
font-size: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: #1ad8f4;
|
color: #1ad8f4;
|
||||||
&:hover, &:focus {
|
&:hover, &:focus {
|
||||||
color: #09a3ba;
|
color: #09a3ba;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
// transform: scale(1.2);
|
|
||||||
// transition: 150ms all ease-in-out;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.disable {
|
.disable {
|
||||||
font-size: 36px;
|
font-size: 28px;
|
||||||
color: #393e5c73;
|
color: #393e5c73;
|
||||||
}
|
@media (min-width: 768px) {
|
||||||
|
font-size: 36px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tx-skeleton {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
.header-bg {
|
||||||
|
&:first-child {
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
&:nth-child(2) {
|
||||||
|
.row {
|
||||||
|
height: 107px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<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]">
|
<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"> </a>
|
<a [routerLink]="['/block/' | relativeUrl, block.id]" [state]="{ data: { block: block } }" class="blockLink"> </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>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
<div class="fee-span">
|
<div class="fee-span">
|
||||||
{{ block.feeRange[1] | number:feeRounding }} - {{ block.feeRange[block.feeRange.length - 1] | number:feeRounding }} <ng-container i18n="shared.sat-vbyte|sat/vB">sat/vB</ng-container>
|
{{ block.feeRange[1] | number:feeRounding }} - {{ block.feeRange[block.feeRange.length - 1] | number:feeRounding }} <ng-container i18n="shared.sat-vbyte|sat/vB">sat/vB</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<div class="block-size" [innerHTML]="block.size | bytes: 2">‎</div>
|
<div class="block-size" [innerHTML]="'‎' + (block.size | bytes: 2)"></div>
|
||||||
<div class="transaction-count">
|
<div class="transaction-count">
|
||||||
<ng-container *ngTemplateOutlet="block.tx_count === 1 ? transactionsSingular : transactionsPlural; context: {$implicit: block.tx_count | number}"></ng-container>
|
<ng-container *ngTemplateOutlet="block.tx_count === 1 ? transactionsSingular : transactionsPlural; context: {$implicit: block.tx_count | number}"></ng-container>
|
||||||
<ng-template #transactionsSingular let-i i18n="shared.transaction-count.singular">{{ i }} transaction</ng-template>
|
<ng-template #transactionsSingular let-i i18n="shared.transaction-count.singular">{{ i }} transaction</ng-template>
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
<div [hidden]="!arrowVisible" id="arrow-up" [style.transition]="transition" [ngStyle]="{'left': arrowLeftPx + 'px' }"></div>
|
<div [hidden]="!arrowVisible" id="arrow-up" [style.transition]="transition" [ngStyle]="{'left': arrowLeftPx + 'px' }"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-template #loadingBlocksTemplate >
|
<ng-template #loadingBlocksTemplate>
|
||||||
<div class="blocks-container">
|
<div class="blocks-container">
|
||||||
<div class="flashing">
|
<div class="flashing">
|
||||||
<div *ngFor="let block of emptyBlocks; let i = index; trackBy: trackByBlocksFn" >
|
<div *ngFor="let block of emptyBlocks; let i = index; trackBy: trackByBlocksFn" >
|
||||||
|
|||||||
@@ -111,7 +111,7 @@
|
|||||||
|
|
||||||
.flashing {
|
.flashing {
|
||||||
animation: opacityPulse 2s ease-out;
|
animation: opacityPulse 2s ease-out;
|
||||||
animation-iteration-count: infinite;
|
animation-iteration-count: infinite;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,4 +119,4 @@
|
|||||||
0% {opacity: 0.7;}
|
0% {opacity: 0.7;}
|
||||||
50% {opacity: 1.0;}
|
50% {opacity: 1.0;}
|
||||||
100% {opacity: 0.7;}
|
100% {opacity: 0.7;}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, Input } from '@angular/core';
|
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
||||||
import { Observable, Subscription } from 'rxjs';
|
import { Observable, Subscription } from 'rxjs';
|
||||||
import { Block } from 'src/app/interfaces/electrs.interface';
|
import { Block } from 'src/app/interfaces/electrs.interface';
|
||||||
import { StateService } from 'src/app/services/state.service';
|
import { StateService } from 'src/app/services/state.service';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
import { specialBlocks } from 'src/app/app.constants';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-blockchain-blocks',
|
selector: 'app-blockchain-blocks',
|
||||||
@@ -11,7 +12,7 @@ import { Router } from '@angular/router';
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class BlockchainBlocksComponent implements OnInit, OnDestroy {
|
export class BlockchainBlocksComponent implements OnInit, OnDestroy {
|
||||||
|
specialBlocks = specialBlocks;
|
||||||
network = '';
|
network = '';
|
||||||
blocks: Block[] = [];
|
blocks: Block[] = [];
|
||||||
emptyBlocks: Block[] = this.mountEmptyBlocks();
|
emptyBlocks: Block[] = this.mountEmptyBlocks();
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
<div style="height: 225px;" *ngIf="mempoolVsizeFeesData; else loadingFees">
|
<div class="fee-distribution-chart" *ngIf="mempoolVsizeFeesOptions; else loadingFees">
|
||||||
<app-chartist
|
<div echarts [initOpts]="mempoolVsizeFeesInitOptions" [options]="mempoolVsizeFeesOptions"></div>
|
||||||
[data]="mempoolVsizeFeesData"
|
|
||||||
[type]="'Line'"
|
|
||||||
[options]="mempoolVsizeFeesOptions">
|
|
||||||
</app-chartist>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-template #loadingFees>
|
<ng-template #loadingFees>
|
||||||
|
|||||||
@@ -1,70 +1,83 @@
|
|||||||
import { Component, Input, OnChanges, ChangeDetectionStrategy } from '@angular/core';
|
import { OnChanges } from '@angular/core';
|
||||||
import * as Chartist from '@mempool/chartist';
|
import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-fee-distribution-graph',
|
selector: 'app-fee-distribution-graph',
|
||||||
templateUrl: './fee-distribution-graph.component.html',
|
templateUrl: './fee-distribution-graph.component.html',
|
||||||
styleUrls: ['./fee-distribution-graph.component.scss'],
|
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class FeeDistributionGraphComponent implements OnChanges {
|
export class FeeDistributionGraphComponent implements OnInit, OnChanges {
|
||||||
@Input() feeRange;
|
@Input() data: any;
|
||||||
|
@Input() height: number | string = 210;
|
||||||
|
@Input() top: number | string = 20;
|
||||||
|
@Input() right: number | string = 22;
|
||||||
|
@Input() left: number | string = 30;
|
||||||
|
|
||||||
mempoolVsizeFeesData: any;
|
|
||||||
mempoolVsizeFeesOptions: any;
|
mempoolVsizeFeesOptions: any;
|
||||||
|
mempoolVsizeFeesInitOptions = {
|
||||||
|
renderer: 'svg'
|
||||||
|
};
|
||||||
|
|
||||||
feeLevels = [1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 175, 200,
|
constructor() { }
|
||||||
250, 300, 350, 400, 500];
|
|
||||||
|
|
||||||
constructor(
|
ngOnInit() {
|
||||||
) { }
|
this.mountChart();
|
||||||
|
|
||||||
ngOnChanges() {
|
|
||||||
this.mempoolVsizeFeesOptions = {
|
|
||||||
showArea: true,
|
|
||||||
showLine: true,
|
|
||||||
fullWidth: true,
|
|
||||||
showPoint: true,
|
|
||||||
low: 0,
|
|
||||||
axisY: {
|
|
||||||
showLabel: false,
|
|
||||||
offset: 0
|
|
||||||
},
|
|
||||||
axisX: {
|
|
||||||
showGrid: true,
|
|
||||||
showLabel: false,
|
|
||||||
offset: 0
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
Chartist.plugins.ctPointLabels({
|
|
||||||
textAnchor: 'middle',
|
|
||||||
labelInterpolationFnc: (value) => Math.round(value)
|
|
||||||
})
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
const fees = this.feeRange;
|
|
||||||
const series = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < this.feeLevels.length; i++) {
|
|
||||||
let total = 0;
|
|
||||||
// for (let j = 0; j < fees.length; j++) {
|
|
||||||
for (const fee of fees) {
|
|
||||||
if (i === this.feeLevels.length - 1) {
|
|
||||||
if (fee >= this.feeLevels[i]) {
|
|
||||||
total += 1;
|
|
||||||
}
|
|
||||||
} else if (fee >= this.feeLevels[i] && fee < this.feeLevels[i + 1]) {
|
|
||||||
total += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
series.push(total);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mempoolVsizeFeesData = {
|
|
||||||
series: [fees],
|
|
||||||
labels: fees.map((d, i) => i)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnChanges() {
|
||||||
|
this.mountChart();
|
||||||
|
}
|
||||||
|
|
||||||
|
mountChart() {
|
||||||
|
this.mempoolVsizeFeesOptions = {
|
||||||
|
grid: {
|
||||||
|
height: '210',
|
||||||
|
right: '20',
|
||||||
|
top: '22',
|
||||||
|
left: '30',
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
type: 'dotted',
|
||||||
|
color: '#ffffff66',
|
||||||
|
opacity: 0.25,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
data: this.data,
|
||||||
|
type: 'line',
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'top',
|
||||||
|
color: '#ffffff',
|
||||||
|
textShadowBlur: 0,
|
||||||
|
formatter: (label: any) => {
|
||||||
|
return Math.floor(label.data);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
smooth: true,
|
||||||
|
lineStyle: {
|
||||||
|
color: '#D81B60',
|
||||||
|
width: 4,
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
color: '#b71c1c',
|
||||||
|
borderWidth: 10,
|
||||||
|
borderMiterLimit: 10,
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: '#D81B60',
|
||||||
|
opacity: 1,
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<div class="echarts" echarts [initOpts]="mempoolStatsChartInitOption" [options]="mempoolStatsChartOption" (chartRendered)="rendered()"></div>
|
||||||
|
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
||||||
|
<div class="spinner-border text-light"></div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
import { Component, Input, Inject, LOCALE_ID, ChangeDetectionStrategy, OnInit } from '@angular/core';
|
||||||
|
import { formatDate } from '@angular/common';
|
||||||
|
import { EChartsOption } from 'echarts';
|
||||||
|
import { OnChanges } from '@angular/core';
|
||||||
|
import { StorageService } from 'src/app/services/storage.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-incoming-transactions-graph',
|
||||||
|
templateUrl: './incoming-transactions-graph.component.html',
|
||||||
|
styles: [`
|
||||||
|
.loadingGraphs {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: calc(50% - 16px);
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
`],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class IncomingTransactionsGraphComponent implements OnInit, OnChanges {
|
||||||
|
@Input() data: any;
|
||||||
|
@Input() theme: string;
|
||||||
|
@Input() height: number | string = '200';
|
||||||
|
@Input() right: number | string = '10';
|
||||||
|
@Input() top: number | string = '20';
|
||||||
|
@Input() left: number | string = '0';
|
||||||
|
@Input() template: ('widget' | 'advanced') = 'widget';
|
||||||
|
|
||||||
|
isLoading = true;
|
||||||
|
mempoolStatsChartOption: EChartsOption = {};
|
||||||
|
mempoolStatsChartInitOption = {
|
||||||
|
renderer: 'svg'
|
||||||
|
};
|
||||||
|
windowPreference: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(LOCALE_ID) private locale: string,
|
||||||
|
private storageService: StorageService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.isLoading = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(): void {
|
||||||
|
if (!this.data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.windowPreference = this.storageService.getValue('graphWindowPreference');
|
||||||
|
this.mountChart();
|
||||||
|
}
|
||||||
|
|
||||||
|
rendered() {
|
||||||
|
if (!this.data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isLoading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mountChart(): void {
|
||||||
|
this.mempoolStatsChartOption = {
|
||||||
|
grid: {
|
||||||
|
height: this.height,
|
||||||
|
right: this.right,
|
||||||
|
top: this.top,
|
||||||
|
left: this.left,
|
||||||
|
},
|
||||||
|
animation: false,
|
||||||
|
dataZoom: [{
|
||||||
|
type: 'inside',
|
||||||
|
realtime: true,
|
||||||
|
zoomOnMouseWheel: (this.template === 'advanced') ? true : false,
|
||||||
|
maxSpan: 100,
|
||||||
|
minSpan: 10,
|
||||||
|
}, {
|
||||||
|
show: (this.template === 'advanced') ? true : false,
|
||||||
|
type: 'slider',
|
||||||
|
brushSelect: false,
|
||||||
|
realtime: true,
|
||||||
|
selectedDataBackground: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#fff',
|
||||||
|
opacity: 0.45,
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
position: (pos, params, el, elRect, size) => {
|
||||||
|
const obj = { top: -20 };
|
||||||
|
obj[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 80;
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
|
extraCssText: `width: ${(['2h', '24h'].includes(this.windowPreference) || this.template === 'widget') ? '125px' : '135px'};
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;`,
|
||||||
|
axisPointer: {
|
||||||
|
type: 'line',
|
||||||
|
},
|
||||||
|
formatter: (params: any) => {
|
||||||
|
const colorSpan = (color: string) => `<span class="indicator" style="background-color: ` + color + `"></span>`;
|
||||||
|
let itemFormatted = '<div class="title">' + params[0].axisValue + '</div>';
|
||||||
|
params.map((item: any, index: number) => {
|
||||||
|
if (index < 26) {
|
||||||
|
itemFormatted += `<div class="item">
|
||||||
|
<div class="indicator-container">${colorSpan(item.color)}</div>
|
||||||
|
<div class="grow"></div>
|
||||||
|
<div class="value">${item.value} <span class="symbol">vB/s</span></div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return `<div class="tx-wrapper-tooltip-chart ${(this.template === 'advanced') ? 'tx-wrapper-tooltip-chart-advanced' : ''}">${itemFormatted}</div>`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
axisLabel: {
|
||||||
|
align: 'center',
|
||||||
|
fontSize: 11,
|
||||||
|
lineHeight: 12
|
||||||
|
},
|
||||||
|
data: this.data.labels.map((value: any) => `${formatDate(value, 'M/d', this.locale)}\n${formatDate(value, 'H:mm', this.locale)}`),
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
axisLabel: {
|
||||||
|
fontSize: 11,
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
type: 'dotted',
|
||||||
|
color: '#ffffff66',
|
||||||
|
opacity: 0.25,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: this.data.series[0],
|
||||||
|
type: 'line',
|
||||||
|
smooth: false,
|
||||||
|
showSymbol: false,
|
||||||
|
lineStyle: {
|
||||||
|
width: 3,
|
||||||
|
},
|
||||||
|
markLine: {
|
||||||
|
silent: true,
|
||||||
|
symbol: 'none',
|
||||||
|
lineStyle: {
|
||||||
|
color: '#fff',
|
||||||
|
opacity: 1,
|
||||||
|
width: 2,
|
||||||
|
},
|
||||||
|
data: [{
|
||||||
|
yAxis: 1667,
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
color: '#ffffff',
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
visualMap: {
|
||||||
|
show: false,
|
||||||
|
top: 50,
|
||||||
|
right: 10,
|
||||||
|
pieces: [{
|
||||||
|
gt: 0,
|
||||||
|
lte: 1667,
|
||||||
|
color: '#7CB342'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gt: 1667,
|
||||||
|
lte: 2000,
|
||||||
|
color: '#FDD835'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gt: 2000,
|
||||||
|
lte: 2500,
|
||||||
|
color: '#FFB300'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gt: 2500,
|
||||||
|
lte: 3000,
|
||||||
|
color: '#FB8C00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gt: 3000,
|
||||||
|
lte: 3500,
|
||||||
|
color: '#F4511E'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gt: 3500,
|
||||||
|
color: '#D81B60'
|
||||||
|
}],
|
||||||
|
outOfRange: {
|
||||||
|
color: '#999'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let block of blocks; let i= index; trackBy: trackByBlock">
|
<tr *ngFor="let block of blocks; let i= index; trackBy: trackByBlock">
|
||||||
<td><a [routerLink]="['/block' | relativeUrl, block.id]" [state]="{ data: { block: block } }">{{ block.height }}</a></td>
|
<td><a [routerLink]="['/block' | relativeUrl, block.id]" [state]="{ data: { block: block } }">{{ block.height }}</a></td>
|
||||||
<td class="d-none d-md-block">{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }}</td>
|
<td class="d-none d-md-block">‎{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }}</td>
|
||||||
<td><app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since></td>
|
<td><app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since></td>
|
||||||
<td class="d-none d-lg-block">{{ block.tx_count | number }}</td>
|
<td class="d-none d-lg-block">{{ block.tx_count | number }}</td>
|
||||||
<td>
|
<td>
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<div class="echarts" echarts [initOpts]="pegsChartInitOption" [options]="pegsChartOptions" (chartRendered)="rendered()"></div>
|
||||||
|
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
||||||
|
<div class="spinner-border text-light"></div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
import { Component, Inject, LOCALE_ID, ChangeDetectionStrategy, Input, OnChanges, OnInit } from '@angular/core';
|
||||||
|
import { formatDate, formatNumber } from '@angular/common';
|
||||||
|
import { EChartsOption } from 'echarts';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-lbtc-pegs-graph',
|
||||||
|
styles: [`
|
||||||
|
::ng-deep .tx-wrapper-tooltip-chart { width: 135px; }
|
||||||
|
.loadingGraphs {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: calc(50% - 16px);
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
`],
|
||||||
|
templateUrl: './lbtc-pegs-graph.component.html',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class LbtcPegsGraphComponent implements OnInit, OnChanges {
|
||||||
|
@Input() data: any;
|
||||||
|
pegsChartOptions: EChartsOption;
|
||||||
|
|
||||||
|
height: number | string = '200';
|
||||||
|
right: number | string = '10';
|
||||||
|
top: number | string = '20';
|
||||||
|
left: number | string = '50';
|
||||||
|
template: ('widget' | 'advanced') = 'widget';
|
||||||
|
isLoading = true;
|
||||||
|
|
||||||
|
pegsChartOption: EChartsOption = {};
|
||||||
|
pegsChartInitOption = {
|
||||||
|
renderer: 'svg'
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(LOCALE_ID) private locale: string,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.isLoading = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges() {
|
||||||
|
if (!this.data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.pegsChartOptions = this.createChartOptions(this.data.series, this.data.labels);
|
||||||
|
}
|
||||||
|
|
||||||
|
rendered() {
|
||||||
|
if (!this.data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isLoading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
createChartOptions(series: number[], labels: string[]): EChartsOption {
|
||||||
|
return {
|
||||||
|
grid: {
|
||||||
|
height: this.height,
|
||||||
|
right: this.right,
|
||||||
|
top: this.top,
|
||||||
|
left: this.left,
|
||||||
|
},
|
||||||
|
animation: false,
|
||||||
|
dataZoom: [{
|
||||||
|
type: 'inside',
|
||||||
|
realtime: true,
|
||||||
|
zoomOnMouseWheel: (this.template === 'advanced') ? true : false,
|
||||||
|
maxSpan: 100,
|
||||||
|
minSpan: 10,
|
||||||
|
}, {
|
||||||
|
show: (this.template === 'advanced') ? true : false,
|
||||||
|
type: 'slider',
|
||||||
|
brushSelect: false,
|
||||||
|
realtime: true,
|
||||||
|
selectedDataBackground: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#fff',
|
||||||
|
opacity: 0.45,
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
position: (pos, params, el, elRect, size) => {
|
||||||
|
const obj = { top: -20 };
|
||||||
|
obj[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 80;
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
|
extraCssText: `width: ${(this.template === 'widget') ? '125px' : '135px'};
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;`,
|
||||||
|
axisPointer: {
|
||||||
|
type: 'line',
|
||||||
|
},
|
||||||
|
formatter: (params: any) => {
|
||||||
|
const colorSpan = (color: string) => `<span class="indicator" style="background-color: #116761;"></span>`;
|
||||||
|
let itemFormatted = '<div class="title">' + params[0].axisValue + '</div>';
|
||||||
|
params.map((item: any, index: number) => {
|
||||||
|
if (index < 26) {
|
||||||
|
itemFormatted += `<div class="item">
|
||||||
|
<div class="indicator-container">${colorSpan(item.color)}</div>
|
||||||
|
<div class="grow"></div>
|
||||||
|
<div class="value">${formatNumber(item.value, this.locale, '1.2-2')} <span class="symbol">L-BTC</span></div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return `<div class="tx-wrapper-tooltip-chart ${(this.template === 'advanced') ? 'tx-wrapper-tooltip-chart-advanced' : ''}">${itemFormatted}</div>`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
axisLabel: {
|
||||||
|
align: 'center',
|
||||||
|
fontSize: 11,
|
||||||
|
lineHeight: 12
|
||||||
|
},
|
||||||
|
boundaryGap: false,
|
||||||
|
data: labels.map((value: any) => `${formatDate(value, 'MMM\ny', this.locale)}`),
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
axisLabel: {
|
||||||
|
fontSize: 11,
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
type: 'dotted',
|
||||||
|
color: '#ffffff66',
|
||||||
|
opacity: 0.25,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: series,
|
||||||
|
type: 'line',
|
||||||
|
stack: 'total',
|
||||||
|
smooth: false,
|
||||||
|
showSymbol: false,
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.2,
|
||||||
|
color: '#116761',
|
||||||
|
},
|
||||||
|
lineStyle: {
|
||||||
|
width: 3,
|
||||||
|
color: '#116761',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -72,6 +72,14 @@ li.nav-item {
|
|||||||
width: 60%;
|
width: 60%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
.dropdown {
|
||||||
|
.dropdown-toggle {
|
||||||
|
width: 62px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width: 576px) {
|
@media (min-width: 576px) {
|
||||||
.navbar-brand {
|
.navbar-brand {
|
||||||
width: 140px;
|
width: 140px;
|
||||||
@@ -132,4 +140,4 @@ nav {
|
|||||||
}
|
}
|
||||||
.navbar-dark .navbar-nav .nav-link {
|
.navbar-dark .navbar-nav .nav-link {
|
||||||
color: #f1f1f1;
|
color: #f1f1f1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class="container-xl" *ngIf="mempoolBlock$ | async as mempoolBlock">
|
<div class="container-xl" *ngIf="mempoolBlock$ | async as mempoolBlock">
|
||||||
|
|
||||||
<div class="title-block">
|
<div class="title-block">
|
||||||
<h1 class="float-left">{{ ordinal$ | async }}</h1>
|
<h1>{{ ordinal$ | async }}</h1>
|
||||||
<button [routerLink]="['/' | relativeUrl]" class="btn btn-sm float-right">✕</button>
|
<button [routerLink]="['/' | relativeUrl]" class="btn btn-sm float-right">✕</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm">
|
<div class="col-md">
|
||||||
<table class="table table-borderless table-striped">
|
<table class="table table-borderless table-striped">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -40,8 +40,8 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm">
|
<div class="col-md chart-container">
|
||||||
<app-fee-distribution-graph [feeRange]="mempoolBlock.feeRange"></app-fee-distribution-graph>
|
<app-fee-distribution-graph [data]="mempoolBlock.feeRange" ></app-fee-distribution-graph>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,20 +4,10 @@
|
|||||||
top: 5px;
|
top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-block {
|
|
||||||
color: #FFF;
|
|
||||||
padding-top: 20px;
|
|
||||||
padding-bottom: 3px;
|
|
||||||
border-top: 3px solid #FFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fiat {
|
.fiat {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
display: block;
|
display: inline-block;
|
||||||
@media (min-width: 992px) {
|
margin-left: 10px;
|
||||||
display: inline-block;
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.table {
|
.table {
|
||||||
@@ -34,8 +24,15 @@
|
|||||||
h1 {
|
h1 {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
|
line-height: 1;
|
||||||
@media (min-width: 576px) {
|
@media (min-width: 576px) {
|
||||||
float: left;
|
float: left;
|
||||||
margin-right: 10px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chart-container{
|
||||||
|
margin: 20px auto;
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<ng-container *ngIf="(loadingBlocks$ | async) === false; else loadingBlocks">
|
<ng-container *ngIf="(loadingBlocks$ | async) === false; else loadingBlocks">
|
||||||
<div class="mempool-blocks-container" *ngIf="(timeAvg$ | async) as timeAvg;">
|
<div class="mempool-blocks-container" *ngIf="(timeAvg$ | async) as timeAvg;">
|
||||||
<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]">
|
<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"> </a>
|
<a [routerLink]="['/mempool-block/' | relativeUrl, i]" class="blockLink"> </a>
|
||||||
<div class="block-body">
|
<div class="block-body">
|
||||||
<div class="fees">
|
<div class="fees">
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
<div class="fee-span">
|
<div class="fee-span">
|
||||||
{{ projectedBlock.feeRange[0] | number:feeRounding }} - {{ projectedBlock.feeRange[projectedBlock.feeRange.length - 1] | number:feeRounding }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
|
{{ projectedBlock.feeRange[0] | number:feeRounding }} - {{ projectedBlock.feeRange[projectedBlock.feeRange.length - 1] | number:feeRounding }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="block-size" [innerHTML]="projectedBlock.blockSize | bytes: 2">‎</div>
|
<div class="block-size" [innerHTML]="'‎' + (projectedBlock.blockSize | bytes: 2)"></div>
|
||||||
<div class="transaction-count">
|
<div class="transaction-count">
|
||||||
<ng-container *ngTemplateOutlet="projectedBlock.nTx === 1 ? transactionsSingular : transactionsPlural; context: {$implicit: projectedBlock.nTx | number}"></ng-container>
|
<ng-container *ngTemplateOutlet="projectedBlock.nTx === 1 ? transactionsSingular : transactionsPlural; context: {$implicit: projectedBlock.nTx | number}"></ng-container>
|
||||||
<ng-template #transactionsSingular let-i i18n="shared.transaction-count.singular">{{ i }} transaction</ng-template>
|
<ng-template #transactionsSingular let-i i18n="shared.transaction-count.singular">{{ i }} transaction</ng-template>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
.flashing {
|
.flashing {
|
||||||
animation: opacityPulse 2s ease-out;
|
animation: opacityPulse 2s ease-out;
|
||||||
animation-iteration-count: infinite;
|
animation-iteration-count: infinite;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.bitcoin-block::after {
|
.bitcoin-block::after {
|
||||||
content: '';
|
content: '';
|
||||||
width: 125px;
|
width: 125px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
position:absolute;
|
position:absolute;
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
left: -20px;
|
left: -20px;
|
||||||
background-color: #232838;
|
background-color: #232838;
|
||||||
transform:skew(40deg);
|
transform:skew(40deg);
|
||||||
transform-origin:top;
|
transform-origin:top;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bitcoin-block::before {
|
.bitcoin-block::before {
|
||||||
@@ -78,18 +78,18 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: -12px;
|
top: -12px;
|
||||||
left: -20px;
|
left: -20px;
|
||||||
background-color: #191c27;
|
background-color: #191c27;
|
||||||
|
|
||||||
transform: skewY(50deg);
|
transform: skewY(50deg);
|
||||||
transform-origin: top;
|
transform-origin: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mempool-block.bitcoin-block::after {
|
.mempool-block.bitcoin-block::after {
|
||||||
background-color: #403834;
|
background-color: #403834;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mempool-block.bitcoin-block::before {
|
.mempool-block.bitcoin-block::before {
|
||||||
background-color: #2d2825;
|
background-color: #2d2825;
|
||||||
}
|
}
|
||||||
|
|
||||||
.black-background {
|
.black-background {
|
||||||
@@ -102,8 +102,8 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
right: 75px;
|
right: 75px;
|
||||||
top: 140px;
|
top: 140px;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
border-left: 35px solid transparent;
|
border-left: 35px solid transparent;
|
||||||
border-right: 35px solid transparent;
|
border-right: 35px solid transparent;
|
||||||
border-bottom: 35px solid #FFF;
|
border-bottom: 35px solid #FFF;
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ import { Subscription, Observable, fromEvent, merge, of, combineLatest, timer }
|
|||||||
import { MempoolBlock } from 'src/app/interfaces/websocket.interface';
|
import { MempoolBlock } from 'src/app/interfaces/websocket.interface';
|
||||||
import { StateService } from 'src/app/services/state.service';
|
import { StateService } from 'src/app/services/state.service';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { take, map, switchMap, share } from 'rxjs/operators';
|
import { take, map, switchMap } from 'rxjs/operators';
|
||||||
import { feeLevels, mempoolFeeColors } from 'src/app/app.constants';
|
import { feeLevels, mempoolFeeColors } from 'src/app/app.constants';
|
||||||
|
import { specialBlocks } from 'src/app/app.constants';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-mempool-blocks',
|
selector: 'app-mempool-blocks',
|
||||||
@@ -13,12 +14,13 @@ import { feeLevels, mempoolFeeColors } from 'src/app/app.constants';
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
||||||
|
specialBlocks = specialBlocks;
|
||||||
mempoolBlocks: MempoolBlock[] = [];
|
mempoolBlocks: MempoolBlock[] = [];
|
||||||
mempoolEmptyBlocks: MempoolBlock[] = this.mountEmptyBlocks();
|
mempoolEmptyBlocks: MempoolBlock[] = this.mountEmptyBlocks();
|
||||||
mempoolBlocks$: Observable<MempoolBlock[]>;
|
mempoolBlocks$: Observable<MempoolBlock[]>;
|
||||||
timeAvg$: Observable<number>;
|
timeAvg$: Observable<number>;
|
||||||
loadingBlocks$: Observable<boolean>;
|
loadingBlocks$: Observable<boolean>;
|
||||||
|
blocksSubscription: Subscription;
|
||||||
|
|
||||||
mempoolBlocksFull: MempoolBlock[] = [];
|
mempoolBlocksFull: MempoolBlock[] = [];
|
||||||
mempoolBlockStyles = [];
|
mempoolBlockStyles = [];
|
||||||
@@ -74,26 +76,36 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
|||||||
fromEvent(window, 'resize')
|
fromEvent(window, 'resize')
|
||||||
)
|
)
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap(() => this.stateService.mempoolBlocks$),
|
switchMap(() => combineLatest([
|
||||||
map((blocks) => {
|
this.stateService.blocks$.pipe(map(([block]) => block)),
|
||||||
if (!blocks.length) {
|
this.stateService.mempoolBlocks$
|
||||||
return [{ index: 0, blockSize: 0, blockVSize: 0, feeRange: [0, 0], medianFee: 0, nTx: 0, totalFees: 0 }];
|
.pipe(
|
||||||
}
|
map((mempoolBlocks) => {
|
||||||
return blocks;
|
if (!mempoolBlocks.length) {
|
||||||
}),
|
return [{ index: 0, blockSize: 0, blockVSize: 0, feeRange: [0, 0], medianFee: 0, nTx: 0, totalFees: 0 }];
|
||||||
map((blocks) => {
|
}
|
||||||
blocks.forEach((block, i) => {
|
return mempoolBlocks;
|
||||||
block.index = this.blockIndex + i;
|
}),
|
||||||
});
|
)
|
||||||
const stringifiedBlocks = JSON.stringify(blocks);
|
])),
|
||||||
this.mempoolBlocksFull = JSON.parse(stringifiedBlocks);
|
map(([lastBlock, mempoolBlocks]) => {
|
||||||
this.mempoolBlocks = this.reduceMempoolBlocksToFitScreen(JSON.parse(stringifiedBlocks));
|
mempoolBlocks.forEach((block, i) => {
|
||||||
this.updateMempoolBlockStyles();
|
block.index = this.blockIndex + i;
|
||||||
this.calculateTransactionPosition();
|
block.height = lastBlock.height + i + 1;
|
||||||
return this.mempoolBlocks;
|
if (this.stateService.network === '') {
|
||||||
})
|
block.blink = specialBlocks[block.height] ? true : false;
|
||||||
);
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const stringifiedBlocks = JSON.stringify(mempoolBlocks);
|
||||||
|
this.mempoolBlocksFull = JSON.parse(stringifiedBlocks);
|
||||||
|
this.mempoolBlocks = this.reduceMempoolBlocksToFitScreen(JSON.parse(stringifiedBlocks));
|
||||||
|
|
||||||
|
this.updateMempoolBlockStyles();
|
||||||
|
this.calculateTransactionPosition();
|
||||||
|
return this.mempoolBlocks;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
this.timeAvg$ = timer(0, 1000)
|
this.timeAvg$ = timer(0, 1000)
|
||||||
.pipe(
|
.pipe(
|
||||||
@@ -118,7 +130,7 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
|||||||
} else {
|
} else {
|
||||||
timeAvgMins += Math.abs(timeAvgDiff);
|
timeAvgMins += Math.abs(timeAvgDiff);
|
||||||
}
|
}
|
||||||
|
|
||||||
return timeAvgMins * 60 * 1000;
|
return timeAvgMins * 60 * 1000;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -184,7 +196,8 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reduceMempoolBlocksToFitScreen(blocks: MempoolBlock[]): MempoolBlock[] {
|
reduceMempoolBlocksToFitScreen(blocks: MempoolBlock[]): MempoolBlock[] {
|
||||||
const blocksAmount = Math.max(2, Math.floor(window.innerWidth / 2 / (this.blockWidth + this.blockPadding)));
|
const innerWidth = this.stateService.env.BASE_MODULE !== 'liquid' && window.innerWidth <= 767.98 ? window.innerWidth : window.innerWidth / 2;
|
||||||
|
const blocksAmount = Math.min(this.stateService.env.MEMPOOL_BLOCKS_AMOUNT, Math.floor(innerWidth / (this.blockWidth + this.blockPadding)));
|
||||||
while (blocks.length > blocksAmount) {
|
while (blocks.length > blocksAmount) {
|
||||||
const block = blocks.pop();
|
const block = blocks.pop();
|
||||||
const lastBlock = blocks[blocks.length - 1];
|
const lastBlock = blocks[blocks.length - 1];
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
<app-chartist
|
<div echarts class="echarts" (chartInit)="onChartReady($event)" (chartRendered)="rendered()" [initOpts]="mempoolVsizeFeesInitOptions" [options]="mempoolVsizeFeesOptions"></div>
|
||||||
*ngIf="mempoolVsizeFeesData"
|
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
||||||
[data]="mempoolVsizeFeesData"
|
<div class="spinner-border text-light"></div>
|
||||||
[type]="'Line'"
|
</div>
|
||||||
[options]="mempoolVsizeFeesOptions">
|
|
||||||
</app-chartist>
|
|
||||||
@@ -1,145 +1,375 @@
|
|||||||
import { Component, OnInit, Input, Inject, LOCALE_ID, ChangeDetectionStrategy, OnChanges } from '@angular/core';
|
import { Component, OnInit, Input, Inject, LOCALE_ID, ChangeDetectionStrategy, OnChanges } from '@angular/core';
|
||||||
import { formatDate } from '@angular/common';
|
import { formatDate } from '@angular/common';
|
||||||
import { VbytesPipe } from 'src/app/shared/pipes/bytes-pipe/vbytes.pipe';
|
import { VbytesPipe } from 'src/app/shared/pipes/bytes-pipe/vbytes.pipe';
|
||||||
import * as Chartist from '@mempool/chartist';
|
import { formatNumber } from "@angular/common";
|
||||||
|
|
||||||
import { OptimizedMempoolStats } from 'src/app/interfaces/node-api.interface';
|
import { OptimizedMempoolStats } from 'src/app/interfaces/node-api.interface';
|
||||||
import { StateService } from 'src/app/services/state.service';
|
import { StateService } from 'src/app/services/state.service';
|
||||||
import { StorageService } from 'src/app/services/storage.service';
|
import { StorageService } from 'src/app/services/storage.service';
|
||||||
|
import { EChartsOption } from 'echarts';
|
||||||
|
import { feeLevels, chartColors } from 'src/app/app.constants';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-mempool-graph',
|
selector: 'app-mempool-graph',
|
||||||
templateUrl: './mempool-graph.component.html',
|
templateUrl: './mempool-graph.component.html',
|
||||||
|
styles: [`
|
||||||
|
.loadingGraphs {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: calc(50% - 16px);
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
`],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class MempoolGraphComponent implements OnInit, OnChanges {
|
export class MempoolGraphComponent implements OnInit, OnChanges {
|
||||||
@Input() data;
|
@Input() data: any[];
|
||||||
@Input() dateSpan = '2h';
|
@Input() limitFee = 350;
|
||||||
@Input() showLegend = true;
|
@Input() limitFilterFee = 1;
|
||||||
@Input() offsetX = 40;
|
@Input() height: number | string = 200;
|
||||||
@Input() small = false;
|
@Input() top: number | string = 20;
|
||||||
|
@Input() right: number | string = 10;
|
||||||
|
@Input() left: number | string = 75;
|
||||||
|
@Input() template: ('widget' | 'advanced') = 'widget';
|
||||||
|
@Input() showZoom = true;
|
||||||
|
|
||||||
mempoolVsizeFeesOptions: any;
|
isLoading = true;
|
||||||
mempoolVsizeFeesData: any;
|
mempoolVsizeFeesData: any;
|
||||||
|
mempoolVsizeFeesOptions: EChartsOption;
|
||||||
isMobile = window.innerWidth <= 767.98;
|
mempoolVsizeFeesInitOptions = {
|
||||||
|
renderer: 'svg',
|
||||||
|
};
|
||||||
|
windowPreference: string;
|
||||||
|
hoverIndexSerie = 0;
|
||||||
|
feeLimitIndex: number;
|
||||||
|
feeLevelsOrdered = [];
|
||||||
|
chartColorsOrdered = chartColors;
|
||||||
inverted: boolean;
|
inverted: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private vbytesPipe: VbytesPipe,
|
private vbytesPipe: VbytesPipe,
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
@Inject(LOCALE_ID) private locale: string,
|
|
||||||
private storageService: StorageService,
|
private storageService: StorageService,
|
||||||
|
@Inject(LOCALE_ID) private locale: string,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
let labelHops = !this.showLegend ? 48 : 24;
|
this.isLoading = true;
|
||||||
if (this.small) {
|
this.inverted = this.storageService.getValue('inverted-graph') === 'true';
|
||||||
labelHops = labelHops / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isMobile) {
|
|
||||||
labelHops = 96;
|
|
||||||
}
|
|
||||||
|
|
||||||
const labelInterpolationFnc = (value: any, index: any) => {
|
|
||||||
switch (this.dateSpan) {
|
|
||||||
case '2h':
|
|
||||||
case '24h':
|
|
||||||
value = formatDate(value, 'HH:mm', this.locale);
|
|
||||||
break;
|
|
||||||
case '1w':
|
|
||||||
value = formatDate(value, 'dd/MM HH:mm', this.locale);
|
|
||||||
break;
|
|
||||||
case '1m':
|
|
||||||
case '3m':
|
|
||||||
case '6m':
|
|
||||||
case '1y':
|
|
||||||
value = formatDate(value, 'dd/MM', this.locale);
|
|
||||||
}
|
|
||||||
return index % labelHops === 0 ? value : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.mempoolVsizeFeesOptions = {
|
|
||||||
showArea: true,
|
|
||||||
showLine: false,
|
|
||||||
fullWidth: true,
|
|
||||||
showPoint: false,
|
|
||||||
stackedLine: !this.inverted,
|
|
||||||
low: 0,
|
|
||||||
axisX: {
|
|
||||||
labelInterpolationFnc: labelInterpolationFnc,
|
|
||||||
offset: this.offsetX,
|
|
||||||
},
|
|
||||||
axisY: {
|
|
||||||
labelInterpolationFnc: (value: number): any => this.vbytesPipe.transform(value, 2, 'vB', 'MvB', true),
|
|
||||||
offset: this.showLegend ? 160 : 60,
|
|
||||||
},
|
|
||||||
plugins: this.inverted ? [Chartist.plugins.ctTargetLine({ value: this.stateService.blockVSize })] : []
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.showLegend) {
|
|
||||||
const legendNames: string[] = [1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 175, 200,
|
|
||||||
250, 300, 350, 400].map((sat, i, arr) => {
|
|
||||||
if (sat === 400) {
|
|
||||||
return '350+';
|
|
||||||
}
|
|
||||||
if (i === 0) {
|
|
||||||
return '0 - 1';
|
|
||||||
}
|
|
||||||
return arr[i - 1] + ' - ' + sat;
|
|
||||||
});
|
|
||||||
// Only Liquid has lower than 1 sat/vb transactions
|
|
||||||
if (this.stateService.network !== 'liquid') {
|
|
||||||
legendNames.shift();
|
|
||||||
}
|
|
||||||
this.mempoolVsizeFeesOptions.plugins.push(
|
|
||||||
Chartist.plugins.legend({ legendNames: legendNames })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
this.inverted = this.storageService.getValue('inverted-graph') === 'true';
|
if (!this.data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.windowPreference = this.storageService.getValue('graphWindowPreference');
|
||||||
this.mempoolVsizeFeesData = this.handleNewMempoolData(this.data.concat([]));
|
this.mempoolVsizeFeesData = this.handleNewMempoolData(this.data.concat([]));
|
||||||
|
this.mountFeeChart();
|
||||||
|
}
|
||||||
|
|
||||||
|
rendered() {
|
||||||
|
if (!this.data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isLoading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onChartReady(myChart: any) {
|
||||||
|
myChart.getZr().on('mousemove', (e: any) => {
|
||||||
|
if (e.target !== undefined &&
|
||||||
|
e.target.parent !== undefined &&
|
||||||
|
e.target.parent.parent !== null &&
|
||||||
|
e.target.parent.parent.__ecComponentInfo !== undefined) {
|
||||||
|
this.hoverIndexSerie = e.target.parent.parent.__ecComponentInfo.index;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNewMempoolData(mempoolStats: OptimizedMempoolStats[]) {
|
handleNewMempoolData(mempoolStats: OptimizedMempoolStats[]) {
|
||||||
mempoolStats.reverse();
|
mempoolStats.reverse();
|
||||||
const labels = mempoolStats.map(stats => stats.added);
|
const labels = mempoolStats.map(stats => stats.added);
|
||||||
|
const finalArrayVByte = this.generateArray(mempoolStats);
|
||||||
const finalArrayVbyte = this.generateArray(mempoolStats);
|
|
||||||
|
|
||||||
// Only Liquid has lower than 1 sat/vb transactions
|
|
||||||
if (this.stateService.network !== 'liquid') {
|
|
||||||
finalArrayVbyte.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
labels: labels,
|
labels: labels,
|
||||||
series: finalArrayVbyte
|
series: finalArrayVByte
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
generateArray(mempoolStats: OptimizedMempoolStats[]) {
|
generateArray(mempoolStats: OptimizedMempoolStats[]) {
|
||||||
const finalArray: number[][] = [];
|
const finalArray: number[][] = [];
|
||||||
let feesArray: number[] = [];
|
let feesArray: number[] = [];
|
||||||
|
let limitFeesTemplate = this.template === 'advanced' ? 26 : 20;
|
||||||
for (let index = 37; index > -1; index--) {
|
for (let index = limitFeesTemplate; index > -1; index--) {
|
||||||
feesArray = [];
|
feesArray = [];
|
||||||
mempoolStats.forEach((stats) => {
|
mempoolStats.forEach((stats) => {
|
||||||
const theFee = stats.vsizes[index].toString();
|
feesArray.push(stats.vsizes[index] ? stats.vsizes[index] : 0);
|
||||||
if (theFee) {
|
|
||||||
feesArray.push(parseInt(theFee, 10));
|
|
||||||
} else {
|
|
||||||
feesArray.push(0);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
if (this.inverted && finalArray.length) {
|
|
||||||
feesArray = feesArray.map((value, i) => value + finalArray[finalArray.length - 1][i]);
|
|
||||||
}
|
|
||||||
finalArray.push(feesArray);
|
finalArray.push(feesArray);
|
||||||
}
|
}
|
||||||
finalArray.reverse();
|
finalArray.reverse();
|
||||||
return finalArray;
|
return finalArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mountFeeChart() {
|
||||||
|
this.orderLevels();
|
||||||
|
const { labels, series } = this.mempoolVsizeFeesData;
|
||||||
|
|
||||||
|
const seriesGraph = [];
|
||||||
|
const newColors = [];
|
||||||
|
for (let index = 0; index < series.length; index++) {
|
||||||
|
const value = series[index];
|
||||||
|
if (index >= this.feeLimitIndex) {
|
||||||
|
newColors.push(this.chartColorsOrdered[index]);
|
||||||
|
seriesGraph.push({
|
||||||
|
name: this.feeLevelsOrdered[index],
|
||||||
|
type: 'line',
|
||||||
|
stack: 'fees',
|
||||||
|
smooth: false,
|
||||||
|
markPoint: {
|
||||||
|
symbol: 'rect',
|
||||||
|
},
|
||||||
|
lineStyle: {
|
||||||
|
width: 0,
|
||||||
|
opacity: 0,
|
||||||
|
},
|
||||||
|
symbol: 'none',
|
||||||
|
emphasis: {
|
||||||
|
focus: 'none',
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.85,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
markLine: {
|
||||||
|
silent: true,
|
||||||
|
symbol: 'none',
|
||||||
|
lineStyle: {
|
||||||
|
color: '#fff',
|
||||||
|
opacity: 1,
|
||||||
|
width: this.inverted ? 2 : 0,
|
||||||
|
},
|
||||||
|
data: [{
|
||||||
|
yAxis: '1000000',
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
color: '#ffffff',
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: this.chartColorsOrdered[index],
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
data: value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mempoolVsizeFeesOptions = {
|
||||||
|
series: this.inverted ? [...seriesGraph].reverse() : seriesGraph,
|
||||||
|
hover: true,
|
||||||
|
color: this.inverted ? [...newColors].reverse() : newColors,
|
||||||
|
tooltip: {
|
||||||
|
show: (window.innerWidth >= 768) ? true : false,
|
||||||
|
trigger: 'axis',
|
||||||
|
alwaysShowContent: false,
|
||||||
|
position: (pos, params, el, elRect, size) => {
|
||||||
|
const positions = { top: (this.template === 'advanced') ? 0 : -30 };
|
||||||
|
positions[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 60;
|
||||||
|
return positions;
|
||||||
|
},
|
||||||
|
extraCssText: `width: ${(this.template === 'advanced') ? '275px' : '200px'};
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;`,
|
||||||
|
axisPointer: {
|
||||||
|
type: 'line',
|
||||||
|
},
|
||||||
|
formatter: (params: any) => {
|
||||||
|
const { totalValue, totalValueArray } = this.getTotalValues(params);
|
||||||
|
const itemFormatted = [];
|
||||||
|
let totalParcial = 0;
|
||||||
|
let progressPercentageText = '';
|
||||||
|
const items = this.inverted ? [...params].reverse() : params;
|
||||||
|
items.map((item: any, index: number) => {
|
||||||
|
totalParcial += item.value;
|
||||||
|
const progressPercentage = (item.value / totalValue) * 100;
|
||||||
|
const progressPercentageSum = (totalValueArray[index] / totalValue) * 100;
|
||||||
|
let activeItemClass = '';
|
||||||
|
let hoverActive = 0;
|
||||||
|
if (this.inverted) {
|
||||||
|
hoverActive = Math.abs(this.feeLevelsOrdered.length - item.seriesIndex - this.feeLevelsOrdered.length);
|
||||||
|
} else {
|
||||||
|
hoverActive = item.seriesIndex;
|
||||||
|
}
|
||||||
|
if (this.hoverIndexSerie === hoverActive) {
|
||||||
|
progressPercentageText = `<div class="total-parcial-active">
|
||||||
|
<span class="progress-percentage">
|
||||||
|
${formatNumber(progressPercentage, this.locale, '1.2-2')}
|
||||||
|
<span class="symbol">%</span>
|
||||||
|
</span>
|
||||||
|
<span class="total-parcial-vbytes">
|
||||||
|
${this.vbytesPipe.transform(totalParcial, 2, 'vB', 'MvB', false)}
|
||||||
|
</span>
|
||||||
|
<div class="total-percentage-bar">
|
||||||
|
<span class="total-percentage-bar-background">
|
||||||
|
<span style="
|
||||||
|
width: ${progressPercentage}%;
|
||||||
|
background: ${item.color}
|
||||||
|
"></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
activeItemClass = 'active';
|
||||||
|
}
|
||||||
|
itemFormatted.push(`<tr class="item ${activeItemClass}">
|
||||||
|
<td class="indicator-container">
|
||||||
|
<span class="indicator" style="
|
||||||
|
background-color: ${item.color}
|
||||||
|
"></span>
|
||||||
|
<span>
|
||||||
|
${item.seriesName}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="total-progress-sum">
|
||||||
|
<span>
|
||||||
|
${this.vbytesPipe.transform(item.value, 2, 'vB', 'MvB', false)}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="total-progress-sum">
|
||||||
|
<span>
|
||||||
|
${this.vbytesPipe.transform(totalValueArray[index], 2, 'vB', 'MvB', false)}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="total-progress-sum-bar">
|
||||||
|
<span class="total-percentage-bar-background">
|
||||||
|
<span style="
|
||||||
|
width: ${progressPercentageSum.toFixed(2)}%;
|
||||||
|
background-color: ${this.chartColorsOrdered[3]}
|
||||||
|
"></span>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>`);
|
||||||
|
});
|
||||||
|
const classActive = (this.template === 'advanced') ? 'fees-wrapper-tooltip-chart-advanced' : '';
|
||||||
|
const titleRange = $localize`Range`;
|
||||||
|
const titleSize = $localize`:@@7faaaa08f56427999f3be41df1093ce4089bbd75:Size`;
|
||||||
|
const titleSum = $localize`Sum`;
|
||||||
|
return `<div class="fees-wrapper-tooltip-chart ${classActive}">
|
||||||
|
<div class="title">
|
||||||
|
${params[0].axisValue}
|
||||||
|
<span class="total-value">
|
||||||
|
${this.vbytesPipe.transform(totalValue, 2, 'vB', 'MvB', false)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>${titleRange}</th>
|
||||||
|
<th>${titleSize}</th>
|
||||||
|
<th>${titleSum}</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
${this.inverted ? itemFormatted.join('') : itemFormatted.reverse().join('')}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<span class="total-value">
|
||||||
|
${progressPercentageText}
|
||||||
|
</span>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dataZoom: [{
|
||||||
|
type: 'inside',
|
||||||
|
realtime: true,
|
||||||
|
zoomOnMouseWheel: (this.template === 'advanced') ? true : false,
|
||||||
|
maxSpan: 100,
|
||||||
|
minSpan: 10,
|
||||||
|
}, {
|
||||||
|
show: (this.template === 'advanced' && this.showZoom) ? true : false,
|
||||||
|
type: 'slider',
|
||||||
|
brushSelect: false,
|
||||||
|
realtime: true,
|
||||||
|
bottom: 0,
|
||||||
|
selectedDataBackground: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#fff',
|
||||||
|
opacity: 0.45,
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
animation: false,
|
||||||
|
grid: {
|
||||||
|
height: this.height,
|
||||||
|
right: this.right,
|
||||||
|
top: this.top,
|
||||||
|
left: this.left,
|
||||||
|
},
|
||||||
|
xAxis: [
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
axisLine: { onZero: true },
|
||||||
|
axisLabel: {
|
||||||
|
align: 'center',
|
||||||
|
fontSize: 11,
|
||||||
|
lineHeight: 12,
|
||||||
|
},
|
||||||
|
data: labels.map((value: any) => `${formatDate(value, 'M/d', this.locale)}\n${formatDate(value, 'H:mm', this.locale)}`),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
axisLine: { onZero: false },
|
||||||
|
axisLabel: {
|
||||||
|
fontSize: 11,
|
||||||
|
formatter: (value: number) => (`${this.vbytesPipe.transform(value, 2, 'vB', 'MvB', true)}`),
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
type: 'dotted',
|
||||||
|
color: '#ffffff66',
|
||||||
|
opacity: 0.25,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getTotalValues = (values: any) => {
|
||||||
|
let totalValueTemp = 0;
|
||||||
|
const totalValueArray = [];
|
||||||
|
const valuesInverted = this.inverted ? values : [...values].reverse();
|
||||||
|
for (const item of valuesInverted) {
|
||||||
|
totalValueTemp += item.value;
|
||||||
|
totalValueArray.push(totalValueTemp);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
totalValue: totalValueTemp,
|
||||||
|
totalValueArray: totalValueArray.reverse(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
orderLevels() {
|
||||||
|
this.feeLevelsOrdered = [];
|
||||||
|
for (let i = 0; i < feeLevels.length; i++) {
|
||||||
|
if (feeLevels[i] === this.limitFilterFee) {
|
||||||
|
this.feeLimitIndex = i;
|
||||||
|
}
|
||||||
|
if (feeLevels[i] <= this.limitFee) {
|
||||||
|
if (this.stateService.network === 'liquid') {
|
||||||
|
this.feeLevelsOrdered.push(`${(feeLevels[i] / 10).toFixed(1)} - ${(feeLevels[i + 1] / 10).toFixed(1)}`);
|
||||||
|
} else {
|
||||||
|
this.feeLevelsOrdered.push(`${feeLevels[i]} - ${feeLevels[i + 1]}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.chartColorsOrdered = chartColors.slice(0, this.feeLevelsOrdered.length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,40 +5,53 @@
|
|||||||
<br><br>
|
<br><br>
|
||||||
|
|
||||||
<h2>Privacy Policy</h2>
|
<h2>Privacy Policy</h2>
|
||||||
<h6>Updated: August 2, 2021</h6>
|
<h6>Updated: November 18, 2021</h6>
|
||||||
|
|
||||||
<br><br>
|
<br><br>
|
||||||
|
|
||||||
<div class="text-left">
|
<div class="text-left">
|
||||||
|
|
||||||
|
<p *ngIf="officialMempoolSpace">The <a href="https://mempool.space/">mempool.space</a> website, the <a href="https://liquid.network/">liquid.network</a> website, the <a href="https://bisq.markets/">bisq.markets</a> website, their associated API services, and related network and server infrastructure (collectively, the "Website") are operated by Mempool Space K.K. in Japan ("Mempool", "We", or "Us") and self-hosted from <a href="https://wq.apnic.net/static/search.html?query=AS142052">AS142052</a>.</p>
|
||||||
|
|
||||||
|
<p *ngIf="!officialMempoolSpace">This website and its API service (collectively, the "Website") are operated by a member of the Bitcoin community ("We" or "Us"). Mempool Space K.K. in Japan ("Mempool") has no affiliation with the operator of this Website, and does not sponsor or endorse the information provided herein.</p>
|
||||||
|
|
||||||
|
<h5>By accessing this Website, you agree to the following Privacy Policy:</h5>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
<h4>TRUSTED THIRD PARTIES ARE SECURITY HOLES</h4>
|
<h4>TRUSTED THIRD PARTIES ARE SECURITY HOLES</h4>
|
||||||
|
|
||||||
<ul>
|
<p>Out of respect for the Bitcoin community, this website does not use any third-party analytics, third-party trackers, or third-party cookies, and we do not share any private user data with third-parties. Additionally, to mitigate the risk of surveillance by malicious third-parties, we self-host this website on our own hardware and network infrastructure, so there are no "hosting companies" or "cloud providers" involved with the operation of this website.</p>
|
||||||
<li>We do not use any third-party analytics, trackers, or cookies.</li>
|
|
||||||
<li>We do not share any private user data with third-parties.</li>
|
<br>
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h4>TRUSTED FIRST PARTIES ARE ALSO SECURITY HOLES</h4>
|
<h4>TRUSTED FIRST PARTIES ARE ALSO SECURITY HOLES</h4>
|
||||||
|
|
||||||
<ul>
|
<p>Out of respect for the Bitcoin community, this website does not use any first-party cookies, except to store your preferred language setting (if any). However, we do use minimal first-party analytics and logging as needed for the operation of this website, as follows:</p>
|
||||||
<li>Your IP address may be collected in our webserver logs used for sysadmin purposes.</li>
|
|
||||||
<li>Your IP address may be collected in our self-hosted statistics matomo app.</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h4>DON'T TRUST US, PROTECT YOUR PRIVACY</h4>
|
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>Use a Tor Browser or a privacy VPN service to hide your IP address from us.</li>
|
|
||||||
<li>Use a self-hosted instance of The Mempool Open Source Project™ on your own hardware.</li>
|
<li>We use basic webserver logging (nginx) for sysadmin purposes, which collects your IP address along with the requests you make. These logs are deleted after 10 days, and we do not share this data with any third-party. To conceal your IP address from our webserver logs, we recommend that you use Tor Browser with our Tor v3 hidden service onion hostname.</li>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<li>We use a self-hosted statistics application (matomo) for analytics purposes, which collects your IP address along with the requests you make. Our matomo instance is configured to respect your privacy by redacting your IP address and other methods, and we do not share this data with any third-party. To conceal your activity from our analytics, we recommend that you use a privacy protecting browser extension that blocks matomo from being loaded.</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h4>IF YOU DONATE TO MEMPOOL.SPACE</h4>
|
<br>
|
||||||
|
|
||||||
<ul>
|
<h4>TRUST YOUR OWN SELF-HOSTED MEMPOOL EXPLORER</h4>
|
||||||
<li>Your payment information, together with your Twitter identity (if provided) will be collected.</li>
|
|
||||||
<li>We display sponsor profiles publicly on <a href="https://mempool.space/about">mempool.space/about</a>.</li>
|
<p>For maximum privacy, we recommend that you use your own self-hosted instance of The Mempool Open Source Project™ on your own hardware. You can easily install your own self-hosted instance of this website on a Raspberry Pi using a one-click installation method maintained by various Bitcoin fullnode distributions such as Umbrel, RaspiBlitz, MyNode, and RoninDojo. See our project's GitHub page for more details about self-hosting this website.</p>
|
||||||
<li>Thank you :)</li>
|
|
||||||
</ul>
|
<br>
|
||||||
|
|
||||||
|
<h4>DONATING TO MEMPOOL.SPACE</h4>
|
||||||
|
|
||||||
|
<p>If you donate to mempool.space, your payment information and your Twitter identity (if provided) will be collected in a database, which may be used to publicly display the sponsor profiles on <a href="https://mempool.space/about">mempool.space/about</a>. Thank you for supporting The Mempool Open Source Project.</p>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
<p>EOF</p>
|
<p>EOF</p>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<div class="container-xl">
|
||||||
|
<h1 i18n="shared.broadcast-transaction|Broadcast Transaction">Broadcast Transaction</h1>
|
||||||
|
|
||||||
|
<form [formGroup]="pushTxForm" (submit)="pushTxForm.valid && postTx()" novalidate>
|
||||||
|
<div class="mb-3">
|
||||||
|
<textarea formControlName="txHash" class="form-control" rows="5" i18n-placeholder="transaction.hex" placeholder="Transaction Hex"></textarea>
|
||||||
|
</div>
|
||||||
|
<button [disabled]="isLoading" type="submit" class="btn btn-primary mr-2" i18n="shared.broadcast-transaction|Broadcast Transaction">Broadcast Transaction</button>
|
||||||
|
<p class="red-color d-inline">{{ error }}</p> <a *ngIf="txId" [routerLink]="['/tx/' | relativeUrl, txId]">{{ txId }}</a>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { ApiService } from 'src/app/services/api.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-push-transaction',
|
||||||
|
templateUrl: './push-transaction.component.html',
|
||||||
|
styleUrls: ['./push-transaction.component.scss']
|
||||||
|
})
|
||||||
|
export class PushTransactionComponent implements OnInit {
|
||||||
|
pushTxForm: FormGroup;
|
||||||
|
error: string = '';
|
||||||
|
txId: string = '';
|
||||||
|
isLoading = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private apiService: ApiService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.pushTxForm = this.formBuilder.group({
|
||||||
|
txHash: ['', Validators.required],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
postTx() {
|
||||||
|
this.isLoading = true;
|
||||||
|
this.error = '';
|
||||||
|
this.txId = '';
|
||||||
|
this.apiService.postTransaction$(this.pushTxForm.get('txHash').value)
|
||||||
|
.subscribe((result) => {
|
||||||
|
this.isLoading = false;
|
||||||
|
this.txId = result;
|
||||||
|
this.pushTxForm.reset();
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
if (typeof error.error === 'string') {
|
||||||
|
const matchText = error.error.match('"message":"(.*?)"');
|
||||||
|
this.error = matchText && matchText[1] || error.error;
|
||||||
|
} else if (error.message) {
|
||||||
|
this.error = error.message;
|
||||||
|
}
|
||||||
|
this.isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Component, Input, AfterViewInit, OnDestroy, ViewChild, ElementRef } from '@angular/core';
|
import { Component, Input, AfterViewInit, ViewChild, ElementRef } from '@angular/core';
|
||||||
import * as QRCode from 'qrcode/build/qrcode.js';
|
import * as QRCode from 'qrcode';
|
||||||
import { StateService } from 'src/app/services/state.service';
|
import { StateService } from 'src/app/services/state.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -23,7 +23,7 @@ export class QrcodeComponent implements AfterViewInit {
|
|||||||
if (!this.stateService.isBrowser) {
|
if (!this.stateService.isBrowser) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const opts = {
|
const opts: QRCode.QRCodeRenderersOptions = {
|
||||||
errorCorrectionLevel: 'H',
|
errorCorrectionLevel: 'H',
|
||||||
margin: 0,
|
margin: 0,
|
||||||
color: {
|
color: {
|
||||||
@@ -31,7 +31,6 @@ export class QrcodeComponent implements AfterViewInit {
|
|||||||
light: '#fff'
|
light: '#fff'
|
||||||
},
|
},
|
||||||
width: this.size,
|
width: this.size,
|
||||||
height: this.size,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!this.data) {
|
if (!this.data) {
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ form {
|
|||||||
@media (min-width: 576px) {
|
@media (min-width: 576px) {
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
@media (min-width: 992px) {
|
@media (min-width: 992px) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-block {
|
.btn-block {
|
||||||
width: 58.55px;
|
width: 62px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-box-container {
|
.search-box-container {
|
||||||
@@ -37,4 +37,4 @@ form {
|
|||||||
.btn {
|
.btn {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export class SearchFormComponent implements OnInit {
|
|||||||
|
|
||||||
regexAddress = /^([a-km-zA-HJ-NP-Z1-9]{26,35}|[a-km-zA-HJ-NP-Z1-9]{80}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,100}|[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100})$/;
|
regexAddress = /^([a-km-zA-HJ-NP-Z1-9]{26,35}|[a-km-zA-HJ-NP-Z1-9]{80}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,100}|[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100})$/;
|
||||||
regexBlockhash = /^[0]{8}[a-fA-F0-9]{56}$/;
|
regexBlockhash = /^[0]{8}[a-fA-F0-9]{56}$/;
|
||||||
regexTransaction = /^[a-fA-F0-9]{64}$/;
|
regexTransaction = /^([a-fA-F0-9]{64}):?(\d+)?$/;
|
||||||
regexBlockheight = /^[0-9]+$/;
|
regexBlockheight = /^[0-9]+$/;
|
||||||
|
|
||||||
@ViewChild('instance', {static: true}) instance: NgbTypeahead;
|
@ViewChild('instance', {static: true}) instance: NgbTypeahead;
|
||||||
@@ -100,17 +100,23 @@ export class SearchFormComponent implements OnInit {
|
|||||||
} else if (this.regexBlockhash.test(searchText) || this.regexBlockheight.test(searchText)) {
|
} else if (this.regexBlockhash.test(searchText) || this.regexBlockheight.test(searchText)) {
|
||||||
this.navigate('/block/', searchText);
|
this.navigate('/block/', searchText);
|
||||||
} else if (this.regexTransaction.test(searchText)) {
|
} else if (this.regexTransaction.test(searchText)) {
|
||||||
|
const matches = this.regexTransaction.exec(searchText);
|
||||||
if (this.network === 'liquid') {
|
if (this.network === 'liquid') {
|
||||||
if (this.assets[searchText]) {
|
if (this.assets[matches[1]]) {
|
||||||
this.navigate('/asset/', searchText);
|
this.navigate('/asset/', matches[1]);
|
||||||
}
|
}
|
||||||
this.electrsApiService.getAsset$(searchText)
|
this.electrsApiService.getAsset$(matches[1])
|
||||||
.subscribe(
|
.subscribe(
|
||||||
() => { this.navigate('/asset/', searchText); },
|
() => { this.navigate('/asset/', matches[1]); },
|
||||||
() => { this.navigate('/tx/', searchText); }
|
() => {
|
||||||
|
this.electrsApiService.getBlock$(matches[1])
|
||||||
|
.subscribe(
|
||||||
|
(block) => { this.navigate('/block/', matches[1], { state: { data: { block } } }); },
|
||||||
|
() => { this.navigate('/tx/', matches[0]); });
|
||||||
|
}
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.navigate('/tx/', searchText);
|
this.navigate('/tx/', matches[0]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.isSearching = false;
|
this.isSearching = false;
|
||||||
@@ -118,8 +124,8 @@ export class SearchFormComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
navigate(url: string, searchText: string) {
|
navigate(url: string, searchText: string, extras?: any) {
|
||||||
this.router.navigate([(this.network && this.stateService.env.BASE_MODULE === 'mempool' ? '/' + this.network : '') + url, searchText]);
|
this.router.navigate([(this.network && this.stateService.env.BASE_MODULE === 'mempool' ? '/' + this.network : '') + url, searchText], extras);
|
||||||
this.searchTriggered.emit();
|
this.searchTriggered.emit();
|
||||||
this.searchForm.setValue({
|
this.searchForm.setValue({
|
||||||
searchText: '',
|
searchText: '',
|
||||||
|
|||||||
@@ -1,3 +1,13 @@
|
|||||||
|
<ng-container *ngIf="specialEvent">
|
||||||
|
<div class="pyro">
|
||||||
|
<div class="before"></div>
|
||||||
|
<div class="after"></div>
|
||||||
|
</div>
|
||||||
|
<div class="warning-label">{{ eventName }}</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<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">
|
||||||
<app-blockchain></app-blockchain>
|
<app-blockchain></app-blockchain>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user